Skip to content
This repository has been archived by the owner on Mar 3, 2023. It is now read-only.

Commit

Permalink
Add search function to Fanza
Browse files Browse the repository at this point in the history
  • Loading branch information
erri120 committed Jan 4, 2022
1 parent 41c536a commit c513a47
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 3 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ The following settings can be adjusted in _Add-ons..._ -> _Extensions settings_

**Usage**:

This Metadata Provider expects a link to the game on Fanza (eg: `https://www.dmm.co.jp/dc/doujin/-/detail/=/cid=d_216826/`) as the name of the game.
This Metadata Provider expects a link to the game on Fanza (eg: `https://www.dmm.co.jp/dc/doujin/-/detail/=/cid=d_216826/`) as the name of the game. If you do not provide a link the Plugin will search for the game on the site and let you choose from the results.

**Settings**:

Expand Down
17 changes: 17 additions & 0 deletions src/FanzaMetadata.Test/FanzaTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,23 @@ public async Task TestScrapGamePage(string id)
Assert.NotEmpty(res.PreviewImages!);
}

[Fact]
public async Task TestScrapSearchPage()
{
var file = Path.Combine("files", "search.html");
Assert.True(File.Exists(file));

var handler = new Mock<HttpMessageHandler>();
handler
.SetupAnyRequest()
.ReturnsResponse(File.ReadAllBytes(file));

var scrapper = new Scrapper(new XunitLogger<Scrapper>(_testOutputHelper), handler.Object);
var res = await scrapper.ScrapSearchPage("ゴブリンの巣穴");

Assert.NotEmpty(res);
}

[Theory]
[InlineData("216826", "https://www.dmm.co.jp/dc/doujin/-/detail/=/cid=d_216826/")]
[InlineData("200809", "https://www.dmm.co.jp/dc/doujin/-/detail/=/cid=d_200809/?dmmref=ListRanking&i3_ref=list&i3_ord=5")]
Expand Down
65 changes: 63 additions & 2 deletions src/FanzaMetadata/FanzaMetadataProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public FanzaMetadataProvider(IPlayniteAPI playniteAPI, Settings settings, Metada
private ScrapperResult? _result;
private bool _didRun;

private static string? GetIdFromLink(string link)
public static string? GetIdFromLink(string link)
{
if (!link.StartsWith(Scrapper.GameBaseUrl, StringComparison.OrdinalIgnoreCase)) return null;

Expand Down Expand Up @@ -90,7 +90,68 @@ public static Scrapper SetupScrapper()
var id = GetIdFromGame(Game);
if (id is null)
{
throw new NotImplementedException();
if (string.IsNullOrWhiteSpace(Game.Name))
{
_logger.LogError("Unable to get Id from Game and Name is null or whitespace!");
_didRun = true;
return null;
}

if (IsBackgroundDownload)
{
// background download so we just choose the first item

var searchTask = scrapper.ScrapSearchPage(Game.Name, args.CancelToken);
searchTask.Wait(args.CancelToken);

var searchResult = searchTask.Result;
if (searchResult is null || !searchResult.Any())
{
_logger.LogError("Search return nothing for {Name}, make sure you are logged in!", Game.Name);
_didRun = true;
return null;
}

id = searchResult.First().Id;
}
else
{
var item = _playniteAPI.Dialogs.ChooseItemWithSearch(
new List<GenericItemOption>(),
searchString =>
{
var searchTask = scrapper.ScrapSearchPage(searchString, args.CancelToken);
searchTask.Wait(args.CancelToken);

var searchResult = searchTask.Result;
if (searchResult is null || !searchResult.Any())
{
_logger.LogError("Search return nothing, make sure you are logged in!");
_didRun = true;
return null;
}

var items = searchResult
.Select(x => new GenericItemOption(x.Name, $"{Scrapper.GameBaseUrl}{x.Id}"))
.ToList();

return items;
}, Game.Name, "Search Fanza");

if (item is null)
{
_didRun = true;
return null;
}

var link = item.Description;
id = GetIdFromLink(link ?? string.Empty);

if (id is null)
{
throw new NotImplementedException();
}
}
}

var task = scrapper.ScrapGamePage(id, args.CancelToken);
Expand Down
33 changes: 33 additions & 0 deletions src/FanzaMetadata/Scrapper.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net.Http;
Expand All @@ -18,6 +19,7 @@ public class Scrapper

public const string GameBaseUrl = "https://www.dmm.co.jp/dc/doujin/-/detail/=/cid=d_";
public const string IconUrlFormat = "https://doujin-assets.dmm.co.jp/digital/game/d_{0}/d_{0}pt.jpg";
public const string SearchBaseUrl = "https://www.dmm.co.jp/search/=/searchstr=";

public Scrapper(ILogger<Scrapper> logger, HttpMessageHandler messageHandler)
{
Expand Down Expand Up @@ -182,4 +184,35 @@ public async Task<ScrapperResult> ScrapGamePage(string id, CancellationToken can

return result;
}

public async Task<List<SearchResult>> ScrapSearchPage(string term, CancellationToken cancellationToken = default)
{
var url = SearchBaseUrl + term;

var context = BrowsingContext.New(_configuration);
var document = await context.OpenAsync(url, cancellationToken);

var anchorElements = document.GetElementsByClassName("tmb")
.Where(elem => elem.TagName.Equals(TagNames.P, StringComparison.OrdinalIgnoreCase))
.Select(elem => elem.Children.FirstOrDefault(x => x.TagName.Equals(TagNames.A, StringComparison.OrdinalIgnoreCase)))
.Cast<IHtmlAnchorElement>();

var results = new List<SearchResult>();

foreach (var anchorElement in anchorElements)
{
var id = FanzaMetadataProvider.GetIdFromLink(anchorElement.Href);
if (id is null) continue;

var txtElement = anchorElement.GetElementsByClassName("txt").FirstOrDefault();
if (txtElement is null) continue;

var name = txtElement.Text().Trim();
var searchResult = new SearchResult(name, id);

results.Add(searchResult);
}

return results;
}
}
14 changes: 14 additions & 0 deletions src/FanzaMetadata/ScrapperResult.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;

namespace FanzaMetadata;

Expand All @@ -25,3 +26,16 @@ public class ScrapperResult

public string? IconUrl { get; set; }
}

[DebuggerDisplay("{Name} ({Id})")]
public class SearchResult
{
public readonly string Name;
public readonly string Id;

public SearchResult(string name, string id)
{
Name = name;
Id = id;
}
}

0 comments on commit c513a47

Please sign in to comment.