public IEnumerable<string> PopulatePages(Doujin doujin) { if (!int.TryParse(doujin.SourceId, out var intId)) yield break; var data = _serializer.Deserialize<InternalDoujinData>(doujin.Data); if (data.ImageNames == null || data.Extensions == null) yield break; for (var i = 0; i < data.ImageNames.Length; i++) { var name = data.ImageNames[i]; string extension; switch (data.Extensions[i]) { case '.': extension = ""; break; case 'p': extension = ".png"; break; case 'J': extension = ".jpeg"; break; case 'g': extension = ".gif"; break; default: extension = ".jpg"; break; } yield return Hitomi.Image(intId, name + extension); } }
public async Task<DoujinInfo> GetAsync(string id, CancellationToken cancellationToken = default) { if (!int.TryParse(id, out var intId)) return null; HtmlNode root; // load html page using (var response = await _http.SendAsync( new HttpRequestMessage { Method = HttpMethod.Get, RequestUri = new Uri(Hitomi.Gallery(intId)) }, cancellationToken)) { if (!response.IsSuccessStatusCode) return null; using (var reader = new StringReader(await response.Content.ReadAsStringAsync())) { var doc = new HtmlDocument(); doc.Load(reader); root = doc.DocumentNode; } } // filter out anime var type = Sanitize(root.SelectSingleNode(Hitomi.XPath.Type)); if (type != null && type.Equals("anime", StringComparison.OrdinalIgnoreCase)) { _logger.LogInformation($"Skipping '{id}' because it is of type 'anime'."); return null; } var prettyName = Sanitize(root.SelectSingleNode(Hitomi.XPath.Name)); // replace stuff in brackets with nothing prettyName = _bracketsRegex.Replace(prettyName, ""); var originalName = prettyName; // parse names with two parts var pipeIndex = prettyName.IndexOf('|'); if (pipeIndex != -1) { prettyName = prettyName.Substring(0, pipeIndex).Trim(); originalName = originalName.Substring(pipeIndex + 1).Trim(); } // parse language var languageHref = root.SelectSingleNode(Hitomi.XPath.Language)?.Attributes["href"]?.Value; var language = languageHref == null ? null : _languageHrefRegex.Match(languageHref).Groups["language"].Value; var doujin = new DoujinInfo { PrettyName = prettyName, OriginalName = originalName, UploadTime = DateTime.Parse(Sanitize(root.SelectSingleNode(Hitomi.XPath.Date))).ToUniversalTime(), Source = this, SourceId = id, Artist = Sanitize(root.SelectSingleNode(Hitomi.XPath.Artists))?.ToLowerInvariant(), Group = Sanitize(root.SelectSingleNode(Hitomi.XPath.Groups))?.ToLowerInvariant(), Language = language?.ToLowerInvariant(), Parody = ConvertSeries(Sanitize(root.SelectSingleNode(Hitomi.XPath.Series)))?.ToLowerInvariant(), Characters = root.SelectNodes(Hitomi.XPath.Characters)?.Select(n => Sanitize(n)?.ToLowerInvariant()), Tags = root.SelectNodes(Hitomi.XPath.Tags) ?.Select(n => ConvertTag(Sanitize(n)?.ToLowerInvariant())) }; // parse images using (var response = await _http.SendAsync( new HttpRequestMessage { Method = HttpMethod.Get, RequestUri = new Uri(Hitomi.GalleryInfo(intId)) }, cancellationToken)) { if (!response.IsSuccessStatusCode) return null; using (var textReader = new StringReader(await response.Content.ReadAsStringAsync())) using (var jsonReader = new JsonTextReader(textReader)) { // discard javascript bit and start at json while ((char) textReader.Peek() != '[') textReader.Read(); var images = _serializer.Deserialize<ImageInfo[]>(jsonReader); var extensionsCombined = new string(images.Select(i => { var ext = Path.GetExtension(i.Name); switch (ext) { case "": return '.'; case ".jpg": return 'j'; case ".jpeg": return 'J'; case ".png": return 'p'; case ".gif": return 'g'; default: throw new NotSupportedException( $"Unknown image format '{ext}'."); } }) .ToArray()); doujin.PageCount = images.Length; doujin.Data = _serializer.Serialize(new InternalDoujinData { ImageNames = images.Select(i => Path.GetFileNameWithoutExtension(i.Name)).ToArray(), Extensions = extensionsCombined }); } } return doujin; }