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;
}