Merge pull request #3 from DaniGTA/master

Adding german metadata support
This commit is contained in:
Luke 2017-11-12 14:42:30 -05:00 committed by GitHub
commit 8cfe6d27a0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
47 changed files with 2729 additions and 266 deletions

Binary file not shown.

View File

@ -12,7 +12,7 @@ namespace MediaBrowser.Plugins.Anime
private static readonly Task Completed = Task.FromResult(true);
private readonly Queue<TaskCompletionSource<bool>> _waiters = new Queue<TaskCompletionSource<bool>>();
private int _currentCount;
public AsyncSemaphore(int initialCount)
{
if (initialCount < 0) throw new ArgumentOutOfRangeException("initialCount");
@ -66,7 +66,7 @@ namespace MediaBrowser.Plugins.Anime
Task wait = _semaphore.WaitAsync();
return wait.IsCompleted
? _releaser
: wait.ContinueWith((_, state) => new Releaser((AsyncLock) state),
: wait.ContinueWith((_, state) => new Releaser((AsyncLock)state),
this, CancellationToken.None,
TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
}

View File

@ -1,7 +1,4 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using MediaBrowser.Model.Plugins;
using MediaBrowser.Model.Plugins;
namespace MediaBrowser.Plugins.Anime.Configuration
{
@ -21,6 +18,11 @@ namespace MediaBrowser.Plugins.Anime.Configuration
/// Use titles in Japanese romaji.
/// </summary>
JapaneseRomaji,
/// <summary>
/// Use titles in German.
/// </summary>
German,
}
public class PluginConfiguration
@ -32,6 +34,8 @@ namespace MediaBrowser.Plugins.Anime.Configuration
public int MaxGenres { get; set; }
public bool AddAnimeGenre { get; set; }
public bool UseAnidbOrderingWithSeasons { get; set; }
public string MyAnimeList_API_Name { get; set; }
public string MyAnimeList_API_Pw { get; set; }
public PluginConfiguration()
{
@ -41,6 +45,8 @@ namespace MediaBrowser.Plugins.Anime.Configuration
MaxGenres = 5;
AddAnimeGenre = true;
UseAnidbOrderingWithSeasons = false;
MyAnimeList_API_Name = "";
MyAnimeList_API_Pw = "";
}
}
}
}

View File

@ -18,6 +18,7 @@
<option id="optLanguageLocalized" value="Localized">Localized</option>
<option id="optLanguageRomaji" value="JapaneseRomaji">Romaji</option>
<option id="optLanguageJapanese" value="Japanese">Japanese</option>
<option id="optLanguageGerman" value="German">German</option>
</select>
</li>
<li>
@ -36,13 +37,25 @@
<label for="chkMaxGenres">
Max Genres [0: unlimited]
</label>
<input id="chkMaxGenres" name="chkMaxGenres" type="number" min="0" value="maxGenres"/>
<input id="chkMaxGenres" name="chkMaxGenres" type="number" min="0" value="maxGenres" />
</li>
<li>
<label for="chkMyAnimeList_API_Name">
MyAnimeList Name. -not rly needed
</label>
<input id="chkMyAnimeList_API_Name" name="chkMyAnimeList_API_Name" type="text" value="" disabled/>
</li>
<li>
<label for="chkMyAnimeList_API_Pw">
MyAnimeList Pw. -not rly needed
</label>
<input id="chkMyAnimeList_API_Pw" name="chkMyAnimeList_API_Pw" type="password" value="" disabled/>
</li>
<li>
<label for="chkAnidbSeasonOne">
Use AniDB Odering with Seasons
</label>
<input id="chkAnidbSeasonOne" name="chkAnidbSeasonOne" type="checkbox" value="anidbSeasonOne"/>
<input id="chkAnidbSeasonOne" name="chkAnidbSeasonOne" type="checkbox" value="anidbSeasonOne" />
</li>
<li>
<button type="submit" data-theme="b">Save</button>
@ -52,12 +65,12 @@
</form>
</div>
</div>
<script type="text/javascript">
var AnimeConfigurationPage =
{
pluginUniqueId: "1d0dddf7-1877-4473-8d7b-03f7dac1e559",
virtualFolders: [],
physicalFolders: [],
@ -70,10 +83,12 @@
$('#titleLanguage', page).val(config.TitlePreference).change();
$('#chkAutomaticUpdates', page).checked(config.AllowAutomaticMetadataUpdates).checkboxradio("refresh");
$('#chkTidyGenres', page).checked(config.TidyGenreList).checkboxradio("refresh");
$('#chkMyAnimeList_API_Name', page, val(config.MyAnimeList_API_Name)).change();
$('#chkMyAnimeList_API_Pw', page, val(config.MyAnimeList_API_Pw)).change();
$('#chkMaxGenres', page).val(config.MaxGenres).change();
$('#chkMoveExcessGenresToTags', page).checked(config.MoveExcessGenresToTags).checkboxradio("refresh");
$('#chkAnidbSeasonOne', page).checked(config.UseAnidbOrderingWithSeasons).checkboxradio("refresh");
Dashboard.hideLoadingMsg();
});
},
@ -89,9 +104,12 @@
config.AllowAutomaticMetadataUpdates = $('#chkAutomaticUpdates', page).prop('checked');
config.TidyGenreList = $('#chkTidyGenres').prop('checked');
config.MaxGenres = $('#chkMaxGenres').val();
config.MaxGenres = $('#chkNames').val();
config.MyAnimeList_API_Name = $('#chkMyAnimeList_API_Name').val();
config.MyAnimeList_API_Pw= $('#chkMyAnimeList_API_Pw').val();
config.MoveExcessGenresToTags = $('#chkMoveExcessGenresToTags').prop('checked');
config.UseAnidbOrderingWithSeasons = $('#chkAnidbSeasonOne').prop('checked');
ApiClient.updatePluginConfiguration(AnimeConfigurationPage.pluginUniqueId, config).then(function (result) {
Dashboard.processPluginConfigurationUpdateResult(result);
});

View File

@ -13,4 +13,4 @@ namespace MediaBrowser.Plugins.Anime
return default(T);
}
}
}
}

View File

@ -6,6 +6,10 @@
<FileVersion>1.2.1.1</FileVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|netstandard2.0|AnyCPU'">
<OutputPath>bin\</OutputPath>
</PropertyGroup>
<ItemGroup>
<None Remove="Configuration\configPage.html" />
</ItemGroup>

View File

@ -1,13 +1,12 @@
using System;
using System.Collections.Generic;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Plugins;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Plugins;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Plugins.Anime.Configuration;
using MediaBrowser.Plugins.Anime.Providers.AniDB.Converter;
using MediaBrowser.Plugins.Anime.Providers.AniDB.Identity;
using System;
using System.Collections.Generic;
namespace MediaBrowser.Plugins.Anime
{
@ -41,9 +40,10 @@ namespace MediaBrowser.Plugins.Anime
}
private Guid _id = new Guid("1d0dddf7-1877-4473-8d7b-03f7dac1e559");
public override Guid Id
{
get { return _id; }
}
}
}
}

View File

@ -1,9 +1,6 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using AnimeLists;
using AnimeLists;
using MediaBrowser.Common.Configuration;
using System.IO;
namespace MediaBrowser.Plugins.Anime.Providers.AniDB.Converter
{
@ -23,4 +20,4 @@ namespace MediaBrowser.Plugins.Anime.Providers.AniDB.Converter
Mapper = new Mapper(animelist);
}
}
}
}

View File

@ -1,8 +1,3 @@
using System.Threading.Tasks;
using AnimeLists;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Plugins.Anime.Configuration;
namespace MediaBrowser.Plugins.Anime.Providers.AniDB.Converter
{
//public class AnidbTvdbEpisodeConverter : IItemIdentityConverter<EpisodeInfo>
@ -29,7 +24,7 @@ namespace MediaBrowser.Plugins.Anime.Providers.AniDB.Converter
// }
// }
// var overrideTvdb = string.IsNullOrEmpty(tvdb)
// var overrideTvdb = string.IsNullOrEmpty(tvdb)
// || info.ParentIndexNumber == null
// || (info.ParentIndexNumber < 2 && Plugin.Instance.Configuration.UseAnidbOrderingWithSeasons);

View File

@ -1,7 +1,3 @@
using System.Threading.Tasks;
using AnimeLists;
using MediaBrowser.Controller.Providers;
namespace MediaBrowser.Plugins.Anime.Providers.AniDB.Converter
{
//public class AnidbTvdbSeasonConverter : IItemIdentityConverter<SeasonInfo>
@ -39,7 +35,7 @@ namespace MediaBrowser.Plugins.Anime.Providers.AniDB.Converter
// }
// }
// }
// return false;
// }

View File

@ -1,12 +1,12 @@
using System;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Model.Logging;
using MediaBrowser.Plugins.Anime.Providers.AniDB.Metadata;
using System;
using System.IO;
using System.IO.Compression;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Model.Logging;
using MediaBrowser.Plugins.Anime.Providers.AniDB.Metadata;
namespace MediaBrowser.Plugins.Anime.Providers.AniDB.Identity
{
@ -22,11 +22,13 @@ namespace MediaBrowser.Plugins.Anime.Providers.AniDB.Identity
private readonly IApplicationPaths _paths;
private readonly ILogger _logger;
public static string s_paths;
public AniDbTitleDownloader(ILogger logger, IApplicationPaths paths)
{
_logger = logger;
_paths = paths;
s_paths = GetDataPath(paths);
}
/// <summary>
@ -39,6 +41,23 @@ namespace MediaBrowser.Plugins.Anime.Providers.AniDB.Identity
return Path.Combine(applicationPaths.CachePath, "anidb");
}
/// <summary>
/// Load XML static| Too prevent EXCEPTIONS
/// </summary>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public static async Task Load_static(CancellationToken cancellationToken)
{
var titlesFile = TitlesFilePath_;
var titlesFileInfo = new FileInfo(titlesFile);
// download titles if we do not already have them, or have not updated for a week
if (!titlesFileInfo.Exists || (DateTime.UtcNow - titlesFileInfo.LastWriteTimeUtc).TotalDays > 7)
{
await DownloadTitles_static(titlesFile).ConfigureAwait(false);
}
}
public async Task Load(CancellationToken cancellationToken)
{
var titlesFile = TitlesFilePath;
@ -72,14 +91,46 @@ namespace MediaBrowser.Plugins.Anime.Providers.AniDB.Identity
}
}
/// <summary>
/// static|Downloads an xml file from AniDB which contains all of the titles for every anime, and their IDs,
/// and saves it to disk.
/// </summary>
/// <param name="titlesFile"></param>
/// <returns></returns>
private static async Task DownloadTitles_static(string titlesFile)
{
var client = new WebClient();
await AniDbSeriesProvider.RequestLimiter.Tick();
using (var stream = await client.OpenReadTaskAsync(TitlesUrl))
using (var unzipped = new GZipStream(stream, CompressionMode.Decompress))
using (var writer = File.Open(titlesFile, FileMode.Create, FileAccess.Write))
{
await unzipped.CopyToAsync(writer).ConfigureAwait(false);
}
}
public string TitlesFilePath
{
get
{
var data = GetDataPath(_paths);
Directory.CreateDirectory(data);
Directory.CreateDirectory(s_paths);
return Path.Combine(data, "titles.xml");
return Path.Combine(s_paths, "titles.xml");
}
}
/// <summary>
/// Get the FilePath
/// </summary>
public static string TitlesFilePath_
{
get
{
Directory.CreateDirectory(s_paths);
return Path.Combine(s_paths, "titles.xml");
}
}
}

View File

@ -1,4 +1,5 @@
using System;
using MediaBrowser.Model.Logging;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@ -6,7 +7,6 @@ using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
using MediaBrowser.Model.Logging;
namespace MediaBrowser.Plugins.Anime.Providers.AniDB.Identity
{
@ -16,7 +16,7 @@ namespace MediaBrowser.Plugins.Anime.Providers.AniDB.Identity
/// </summary>
public class AniDbTitleMatcher : IAniDbTitleMatcher
{
private enum TitleType
public enum TitleType
{
Main = 0,
Official = 1,
@ -24,9 +24,10 @@ namespace MediaBrowser.Plugins.Anime.Providers.AniDB.Identity
Synonym = 3
}
private struct TitleInfo
public struct TitleInfo
{
public string AniDbId { get; set; }
public string Title { get; set; }
public TitleType Type { get; set; }
}
@ -35,13 +36,13 @@ namespace MediaBrowser.Plugins.Anime.Providers.AniDB.Identity
/// Gets or sets the global <see cref="IAniDbTitleMatcher"/> instance.
/// </summary>
public static IAniDbTitleMatcher DefaultInstance { get; set; }
private readonly ILogger _logger;
private readonly IAniDbTitleDownloader _downloader;
public readonly IAniDbTitleDownloader _downloader;
private readonly AsyncLock _lock;
private Dictionary<string, TitleInfo> _titles;
public static Dictionary<string, TitleInfo> _titles;
/// <summary>
/// Creates a new instance of the AniDbTitleMatcher class.
/// </summary>
@ -58,7 +59,7 @@ namespace MediaBrowser.Plugins.Anime.Providers.AniDB.Identity
{
return FindSeries(title, CancellationToken.None);
}
public async Task<string> FindSeries(string title, CancellationToken cancellationToken)
{
using (await _lock.LockAsync())
@ -75,7 +76,7 @@ namespace MediaBrowser.Plugins.Anime.Providers.AniDB.Identity
private string LookupAniDbId(string title)
{
TitleInfo info;
if (_titles.TryGetValue(title, out info))
if (_titles.TryGetValue(title, out info))
{
return info.AniDbId;
}
@ -83,8 +84,21 @@ namespace MediaBrowser.Plugins.Anime.Providers.AniDB.Identity
return null;
}
const string Remove = "\"'!`?";
const string Spacers = "/,.:;\\(){}[]+-_=*"; // (there are not actually two - in the they are different char codes)
public static TitleInfo GetTitleInfos(string title)
{
TitleInfo info;
if (!string.IsNullOrEmpty(title))
{
if (_titles.TryGetValue(title, out info))
{
return info;
}
}
return new TitleInfo();
}
private const string Remove = "\"'!`?";
private const string Spacers = "/,.:;\\(){}[]+-_=*"; // (there are not actually two - in the they are different char codes)
internal static string GetComparableName(string name)
{
@ -95,7 +109,7 @@ namespace MediaBrowser.Plugins.Anime.Providers.AniDB.Identity
{
if (c >= 0x2B0 && c <= 0x0333)
{
// skip char modifier and diacritics
// skip char modifier and diacritics
}
else if (Remove.IndexOf(c) > -1)
{
@ -133,7 +147,7 @@ namespace MediaBrowser.Plugins.Anime.Providers.AniDB.Identity
{
get { return _titles != null; }
}
private async Task Load(CancellationToken cancellationToken)
{
if (_titles == null)
@ -155,7 +169,7 @@ namespace MediaBrowser.Plugins.Anime.Providers.AniDB.Identity
_logger.ErrorException("Failed to load AniDB titles", e);
}
}
private Task ReadTitlesFile()
{
return Task.Run(() =>
@ -187,16 +201,18 @@ namespace MediaBrowser.Plugins.Anime.Providers.AniDB.Identity
reader.MoveToAttribute("aid");
aid = reader.Value;
break;
case "title":
var title = reader.ReadElementContentAsString();
if (!string.IsNullOrEmpty(aid) && !string.IsNullOrEmpty(title))
if (!string.IsNullOrEmpty(aid) && !string.IsNullOrEmpty(title))
{
var type = ParseType(reader.GetAttribute("type"));
TitleInfo currentTitleInfo;
if (!_titles.TryGetValue(title, out currentTitleInfo) || (int)currentTitleInfo.Type < (int)type)
{
_titles[title] = new TitleInfo {AniDbId = aid, Type = type};
_titles[title] = new TitleInfo { AniDbId = aid, Type = type, Title = title };
}
}
break;
@ -208,7 +224,7 @@ namespace MediaBrowser.Plugins.Anime.Providers.AniDB.Identity
var comparable = (from pair in _titles
let comp = GetComparableName(pair.Key)
where !_titles.ContainsKey(comp)
select new {Title = comp, Id = pair.Value})
select new { Title = comp, Id = pair.Value })
.ToArray();
foreach (var pair in comparable)
@ -220,13 +236,17 @@ namespace MediaBrowser.Plugins.Anime.Providers.AniDB.Identity
private TitleType ParseType(string type)
{
switch (type) {
switch (type)
{
case "main":
return TitleType.Main;
case "official":
return TitleType.Official;
case "short":
return TitleType.Short;
case "syn":
return TitleType.Synonym;
}

View File

@ -1,8 +1,3 @@
using System.Threading.Tasks;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Plugins.Anime.Configuration;
using MediaBrowser.Plugins.Anime.Providers.AniDB.Converter;
namespace MediaBrowser.Plugins.Anime.Providers.AniDB.Identity
{
//public class AnidbEpisodeIdentityProvider : IItemIdentityProvider<EpisodeInfo>

View File

@ -1,8 +1,4 @@
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.Providers;
namespace MediaBrowser.Plugins.Anime.Providers.AniDB.Identity
namespace MediaBrowser.Plugins.Anime.Providers.AniDB.Identity
{
//public class AnidbSeriesIdentityProvider : IItemIdentityProvider<SeriesInfo>
//{
@ -19,4 +15,4 @@ namespace MediaBrowser.Plugins.Anime.Providers.AniDB.Identity
// }
// }
//}
}
}

View File

@ -1,18 +1,16 @@
using System;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Providers;
using MediaBrowser.Plugins.Anime.Providers.AniDB.Converter;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Providers;
using MediaBrowser.Plugins.Anime.Configuration;
using MediaBrowser.Plugins.Anime.Providers.AniDB.Converter;
using MediaBrowser.Plugins.Anime.Providers.AniDB.Identity;
namespace MediaBrowser.Plugins.Anime.Providers.AniDB.Metadata
{
@ -177,6 +175,7 @@ namespace MediaBrowser.Plugins.Anime.Providers.AniDB.Metadata
}
break;
case "title":
var language = reader.GetAttribute("xml:lang");
var name = reader.ReadElementContentAsString();
@ -238,6 +237,7 @@ namespace MediaBrowser.Plugins.Anime.Providers.AniDB.Metadata
}
break;
case "airdate":
var airdate = reader.ReadElementContentAsString();
if (!string.IsNullOrEmpty(airdate))
@ -248,6 +248,7 @@ namespace MediaBrowser.Plugins.Anime.Providers.AniDB.Metadata
}
break;
case "rating":
int count;
float rating;
@ -258,6 +259,7 @@ namespace MediaBrowser.Plugins.Anime.Providers.AniDB.Metadata
}
break;
case "title":
var language = reader.GetAttribute("xml:lang");
var name = reader.ReadElementContentAsString();

View File

@ -18,4 +18,4 @@ namespace MediaBrowser.Plugins.Anime.Providers.AniDB.Metadata
Cast = new List<AniDbPersonInfo>();
}
}
}
}

View File

@ -1,14 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Providers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Plugins.Anime.Providers.AniDB.Metadata
{

View File

@ -1,14 +1,14 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Providers;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Plugins.Anime.Providers.AniDB.Metadata
{
@ -33,7 +33,7 @@ namespace MediaBrowser.Plugins.Anime.Providers.AniDB.Metadata
public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasMetadata item, CancellationToken cancellationToken)
{
var season = (Season) item;
var season = (Season)item;
var series = season.Series;
var seriesId = series.ProviderIds.GetOrDefault(ProviderNames.AniDb);
@ -45,7 +45,7 @@ namespace MediaBrowser.Plugins.Anime.Providers.AniDB.Metadata
public IEnumerable<ImageType> GetSupportedImages(IHasMetadata item)
{
return new[] {ImageType.Primary};
return new[] { ImageType.Primary };
}
public string Name => "AniDB";

View File

@ -1,12 +1,12 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Providers;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Plugins.Anime.Providers.AniDB.Metadata
{

View File

@ -1,16 +1,16 @@
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Providers;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
namespace MediaBrowser.Plugins.Anime.Providers.AniDB.Metadata
{
@ -34,7 +34,6 @@ namespace MediaBrowser.Plugins.Anime.Providers.AniDB.Metadata
CancellationToken = cancellationToken,
Url = url,
ResourcePool = AniDbSeriesProvider.ResourcePool
}).ConfigureAwait(false);
}

View File

@ -1,4 +1,13 @@
using System;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Providers;
using MediaBrowser.Plugins.Anime.Configuration;
using MediaBrowser.Plugins.Anime.Providers.AniDB.Identity;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
@ -11,15 +20,6 @@ using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Serialization;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Providers;
using MediaBrowser.Plugins.Anime.Configuration;
using MediaBrowser.Plugins.Anime.Providers.AniDB.Identity;
namespace MediaBrowser.Plugins.Anime.Providers.AniDB.Metadata
{
@ -28,10 +28,12 @@ namespace MediaBrowser.Plugins.Anime.Providers.AniDB.Metadata
private const string SeriesDataFile = "series.xml";
private const string SeriesQueryUrl = "http://api.anidb.net:9001/httpapi?request=anime&client={0}&clientver=1&protover=1&aid={1}";
private const string ClientName = "mediabrowser";
// AniDB has very low request rate limits, a minimum of 2 seconds between requests, and an average of 4 seconds between requests
public static readonly SemaphoreSlim ResourcePool = new SemaphoreSlim(1, 1);
public static readonly RateLimiter RequestLimiter = new RateLimiter(TimeSpan.FromSeconds(3), TimeSpan.FromSeconds(5), TimeSpan.FromMinutes(5));
private static readonly int[] IgnoredCategoryIds = {6, 22, 23, 60, 128, 129, 185, 216, 242, 255, 268, 269, 289};
private static readonly int[] IgnoredCategoryIds = { 6, 22, 23, 60, 128, 129, 185, 216, 242, 255, 268, 269, 289 };
private static readonly Regex AniDbUrlRegex = new Regex(@"http://anidb.net/\w+ \[(?<name>[^\]]*)\]");
private readonly IApplicationPaths _appPaths;
private readonly IHttpClient _httpClient;
@ -62,8 +64,11 @@ namespace MediaBrowser.Plugins.Anime.Providers.AniDB.Metadata
var result = new MetadataResult<Series>();
var aid = info.ProviderIds.GetOrDefault(ProviderNames.AniDb);
if (string.IsNullOrEmpty(aid))
aid = await TitleMatcher.FindSeries(info.Name, cancellationToken).ConfigureAwait(false);
if (string.IsNullOrEmpty(aid) && !string.IsNullOrEmpty(info.Name))
{
aid = Equals_check.Fast_xml_search(info.Name, info.Name, true);
aid = Equals_check.Fast_xml_search(Equals_check.clear_name(info.Name), Equals_check.clear_name(info.Name), true);
}
if (!string.IsNullOrEmpty(aid))
{
@ -106,7 +111,12 @@ namespace MediaBrowser.Plugins.Anime.Providers.AniDB.Metadata
public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
{
throw new NotImplementedException();
return _httpClient.GetResponse(new HttpRequestOptions
{
CancellationToken = cancellationToken,
Url = url,
ResourcePool = ResourcePool
});
}
public static async Task<string> GetSeriesData(IApplicationPaths appPaths, IHttpClient httpClient, string seriesId, CancellationToken cancellationToken)
@ -120,7 +130,7 @@ namespace MediaBrowser.Plugins.Anime.Providers.AniDB.Metadata
{
await DownloadSeriesData(seriesId, seriesDataPath, appPaths.CachePath, httpClient, cancellationToken).ConfigureAwait(false);
}
return seriesDataPath;
}
@ -165,6 +175,7 @@ namespace MediaBrowser.Plugins.Anime.Providers.AniDB.Metadata
}
break;
case "enddate":
var endDate = reader.ReadElementContentAsString();
@ -179,6 +190,7 @@ namespace MediaBrowser.Plugins.Anime.Providers.AniDB.Metadata
}
break;
case "titles":
using (var subtree = reader.ReadSubtree())
{
@ -190,6 +202,7 @@ namespace MediaBrowser.Plugins.Anime.Providers.AniDB.Metadata
}
break;
case "creators":
using (var subtree = reader.ReadSubtree())
{
@ -197,10 +210,12 @@ namespace MediaBrowser.Plugins.Anime.Providers.AniDB.Metadata
}
break;
case "description":
series.Overview = ReplaceLineFeedWithNewLine(StripAniDbLinks(reader.ReadElementContentAsString()));
break;
case "ratings":
using (var subtree = reader.ReadSubtree())
{
@ -208,6 +223,7 @@ namespace MediaBrowser.Plugins.Anime.Providers.AniDB.Metadata
}
break;
case "resources":
using (var subtree = reader.ReadSubtree())
{
@ -215,6 +231,7 @@ namespace MediaBrowser.Plugins.Anime.Providers.AniDB.Metadata
}
break;
case "characters":
using (var subtree = reader.ReadSubtree())
{
@ -222,12 +239,14 @@ namespace MediaBrowser.Plugins.Anime.Providers.AniDB.Metadata
}
break;
case "tags":
using (var subtree = reader.ReadSubtree())
{
}
break;
case "categories":
using (var subtree = reader.ReadSubtree())
{
@ -235,6 +254,7 @@ namespace MediaBrowser.Plugins.Anime.Providers.AniDB.Metadata
}
break;
case "episodes":
using (var subtree = reader.ReadSubtree())
{
@ -311,7 +331,7 @@ namespace MediaBrowser.Plugins.Anime.Providers.AniDB.Metadata
if (categorySubtree.NodeType == XmlNodeType.Element && categorySubtree.Name == "name")
{
var name = categorySubtree.ReadElementContentAsString();
genres.Add(new GenreInfo {Name = name, Weight = weight});
genres.Add(new GenreInfo { Name = name, Weight = weight });
}
}
}
@ -351,10 +371,11 @@ namespace MediaBrowser.Plugins.Anime.Providers.AniDB.Metadata
{
var firstId = ids.OrderBy(i => i).First().ToString(CultureInfo.InvariantCulture);
series.ProviderIds.Add(ProviderNames.MyAnimeList, firstId);
// series.ProviderIds.Add(ProviderNames.AniList, firstId);
// series.ProviderIds.Add(ProviderNames.AniList, firstId);
}
break;
case "4":
while (reader.Read())
{
@ -412,6 +433,7 @@ namespace MediaBrowser.Plugins.Anime.Providers.AniDB.Metadata
case "name":
role = reader.ReadElementContentAsString();
break;
case "seiyuu":
name = reader.ReadElementContentAsString();
break;
@ -440,7 +462,7 @@ namespace MediaBrowser.Plugins.Anime.Providers.AniDB.Metadata
CultureInfo.InvariantCulture,
out rating))
{
series.CommunityRating = (float) Math.Round(rating, 1);
series.CommunityRating = (float)Math.Round(rating, 1);
}
}
}
@ -637,7 +659,7 @@ namespace MediaBrowser.Plugins.Anime.Providers.AniDB.Metadata
}
}
var serializer = new XmlSerializer(typeof (AniDbPersonInfo));
var serializer = new XmlSerializer(typeof(AniDbPersonInfo));
foreach (var person in cast)
{
var path = GetCastPath(person.Name, cachePath);

View File

@ -1,16 +1,18 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Net;
using MediaBrowser.Common.Net;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Serialization;
using System;
using System.IO;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Plugins.Anime.Providers.AniList
{
public class AniListApiClient
{
public static readonly SemaphoreSlim ResourcePool = new SemaphoreSlim(1, 1);
public class AccessToken
{
public string access_token { get; set; }
@ -56,18 +58,21 @@ namespace MediaBrowser.Plugins.Anime.Providers.AniList
try
{
var json = "";
string urlWithToken = url;
if (url.Contains("?"))
urlWithToken += $"&access_token={_accessToken}";
else
urlWithToken += $"?access_token={_accessToken}";
using (var stream = await _http.Get(urlWithToken, CancellationToken.None).ConfigureAwait(false))
using (var reader = new StreamReader(stream))
var _webRequest = WebRequest.Create(@"" + Uri.EscapeUriString(urlWithToken));
using (var _response = _webRequest.GetResponse())
using (var _content = _response.GetResponseStream())
using (var _reader = new StreamReader(_content))
{
var json = reader.ReadToEnd().Trim();
return _jsonSerializer.DeserializeFromString<T>(json);
json = _reader.ReadToEnd().Trim();
}
return _jsonSerializer.DeserializeFromString<T>(json);
}
catch
{
@ -102,4 +107,4 @@ namespace MediaBrowser.Plugins.Anime.Providers.AniList
}
}
}
}
}

View File

@ -1,13 +1,4 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Plugins.Anime.Providers.AniDB.Identity;
namespace MediaBrowser.Plugins.Anime.Providers.AniList
namespace MediaBrowser.Plugins.Anime.Providers.AniList
{
//public class AniListSeriesIdentityProvider : IItemIdentityProvider<SeriesInfo>
//{
@ -38,7 +29,7 @@ namespace MediaBrowser.Plugins.Anime.Providers.AniList
// if (first == null)
// return;
// info.ProviderIds.Remove(ProviderNames.AniList);
// info.ProviderIds.Add(ProviderNames.AniList, first.id.ToString());
// }

View File

@ -1,11 +1,4 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.TV;
@ -16,7 +9,12 @@ using MediaBrowser.Model.Providers;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Plugins.Anime.Configuration;
using MediaBrowser.Plugins.Anime.Providers.AniDB.Identity;
using MediaBrowser.Plugins.Anime.Providers.AniDB.Metadata;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Plugins.Anime.Providers.AniList
{
@ -24,14 +22,18 @@ namespace MediaBrowser.Plugins.Anime.Providers.AniList
{
private readonly IApplicationPaths _paths;
private readonly AniListApiClient _api;
private readonly IHttpClient _httpClient;
private readonly ILogger _log;
public static readonly SemaphoreSlim ResourcePool = new SemaphoreSlim(1, 1);
public int Order => -2;
public string Name => "AniList";
public AniListSeriesProvider(IHttpClient http, IApplicationPaths paths, ILogManager logManager, IJsonSerializer jsonSerializer)
{
_httpClient = http;
_paths = paths;
_api = new AniListApiClient(http, logManager, jsonSerializer);
_log = logManager.GetLogger("AniList");
}
public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(SeriesInfo searchInfo, CancellationToken cancellationToken)
@ -93,8 +95,59 @@ namespace MediaBrowser.Plugins.Anime.Providers.AniList
var result = new MetadataResult<Series>();
var aid = info.ProviderIds.GetOrDefault(ProviderNames.AniList);
if (string.IsNullOrEmpty(aid))
return result;
if (string.IsNullOrEmpty(aid) && !string.IsNullOrEmpty(info.Name))
{
var search = await _api.Search(info.Name);
foreach (var a in search)
{
if (string.IsNullOrEmpty(aid))
{
if (Equals_check.Compare_strings(a.title_english, info.Name))
aid = a.id.ToString();
if (Equals_check.Compare_strings(a.title_japanese, info.Name))
aid = a.id.ToString();
if (Equals_check.Compare_strings(a.title_romaji, info.Name))
aid = a.id.ToString();
_log.Log(LogSeverity.Info, a.title_romaji + "vs" + info.Name);
}
}
if (string.IsNullOrEmpty(aid))
{
var cleaned = AniDbTitleMatcher.GetComparableName(Equals_check.clear_name(info.Name));
if (String.Compare(cleaned, info.Name, StringComparison.OrdinalIgnoreCase) != 0)
{
search = await _api.Search(cleaned);
foreach (var b in search)
{
if (Equals_check.Compare_strings(b.title_english, info.Name))
aid = b.id.ToString();
if (Equals_check.Compare_strings(b.title_japanese, info.Name))
aid = b.id.ToString();
if (Equals_check.Compare_strings(b.title_romaji, info.Name))
aid = b.id.ToString();
}
}
}
if (string.IsNullOrEmpty(aid))
{
search = await _api.Search(Equals_check.clear_name(info.Name));
foreach (var b in search)
{
if (Equals_check.Compare_strings(b.title_english, info.Name))
aid = b.id.ToString();
if (Equals_check.Compare_strings(b.title_japanese, info.Name))
aid = b.id.ToString();
if (Equals_check.Compare_strings(b.title_romaji, info.Name))
aid = b.id.ToString();
}
}
}
if (!string.IsNullOrEmpty(aid))
{
@ -119,9 +172,10 @@ namespace MediaBrowser.Plugins.Anime.Providers.AniList
if (anime.genres != null)
{
foreach (var genre in anime.genres)
if(!string.IsNullOrEmpty(genre))
result.Item.AddGenre(genre);
GenreHelper.CleanupGenres(result.Item);
}
@ -130,6 +184,14 @@ namespace MediaBrowser.Plugins.Anime.Providers.AniList
if (!string.IsNullOrEmpty(anime.image_url_banner))
StoreImageUrl(aid, anime.image_url_banner, "banner");
if (string.IsNullOrEmpty(result.Item.Name))
{
if (!string.IsNullOrEmpty(anime.title_romaji))
{
result.Item.Name = anime.title_romaji;
}
}
}
return result;
@ -155,88 +217,92 @@ namespace MediaBrowser.Plugins.Anime.Providers.AniList
File.WriteAllText(path, url);
}
public static string GetSeriesImage(IApplicationPaths paths, string series, string type)
public static async Task<string> GetSeriesImage(IApplicationPaths paths, string series, string type)
{
var path = Path.Combine(paths.CachePath, "anilist", type, series + ".txt");
if (File.Exists(path))
return File.ReadAllText(path);
return await Task.Run(() => File.ReadAllText(path));
return null;
}
public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
}
public class AniListSeriesImageProvider : IRemoteImageProvider
{
private readonly IHttpClient _httpClient;
private readonly IApplicationPaths _appPaths;
public AniListSeriesImageProvider(IHttpClient httpClient, IApplicationPaths appPaths)
{
_httpClient = httpClient;
_appPaths = appPaths;
}
public string Name => "AniList";
public bool Supports(IHasMetadata item) => item is Series || item is Season;
public IEnumerable<ImageType> GetSupportedImages(IHasMetadata item)
{
return new[] {ImageType.Primary, ImageType.Banner};
}
public Task<IEnumerable<RemoteImageInfo>> GetImages(IHasMetadata item, CancellationToken cancellationToken)
{
var seriesId = item.GetProviderId(ProviderNames.AniList);
return GetImages(seriesId, cancellationToken);
}
public async Task<IEnumerable<RemoteImageInfo>> GetImages(string aid, CancellationToken cancellationToken)
{
var list = new List<RemoteImageInfo>();
if (!string.IsNullOrEmpty(aid))
{
var primary = AniListSeriesProvider.GetSeriesImage(_appPaths, aid, "image");
if (!string.IsNullOrEmpty(primary))
{
list.Add(new RemoteImageInfo
{
ProviderName = Name,
Type = ImageType.Primary,
Url = primary
});
}
var banner = AniListSeriesProvider.GetSeriesImage(_appPaths, aid, "banner");
if (!string.IsNullOrEmpty(banner))
{
list.Add(new RemoteImageInfo
{
ProviderName = Name,
Type = ImageType.Banner,
Url = banner
});
}
}
return list;
}
public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
{
return _httpClient.GetResponse(new HttpRequestOptions
{
CancellationToken = cancellationToken,
Url = url,
ResourcePool = AniDbSeriesProvider.ResourcePool
ResourcePool = ResourcePool
});
}
public class AniListSeriesImageProvider : IRemoteImageProvider
{
private readonly IHttpClient _httpClient;
private readonly IApplicationPaths _appPaths;
public AniListSeriesImageProvider(IHttpClient httpClient, IApplicationPaths appPaths)
{
_httpClient = httpClient;
_appPaths = appPaths;
}
public string Name => "AniList";
public bool Supports(IHasMetadata item) => item is Series || item is Season;
public IEnumerable<ImageType> GetSupportedImages(IHasMetadata item)
{
return new[] { ImageType.Primary, ImageType.Banner };
}
public Task<IEnumerable<RemoteImageInfo>> GetImages(IHasMetadata item, CancellationToken cancellationToken)
{
var seriesId = item.GetProviderId(ProviderNames.AniList);
return GetImages(seriesId, cancellationToken);
}
public async Task<IEnumerable<RemoteImageInfo>> GetImages(string aid, CancellationToken cancellationToken)
{
var list = new List<RemoteImageInfo>();
if (!string.IsNullOrEmpty(aid))
{
var primary = await GetSeriesImage(_appPaths, aid, "image");
if (!string.IsNullOrEmpty(primary))
{
list.Add(new RemoteImageInfo
{
ProviderName = Name,
Type = ImageType.Primary,
Url = primary
});
}
var banner = await GetSeriesImage(_appPaths, aid, "banner");
if (!string.IsNullOrEmpty(banner))
{
list.Add(new RemoteImageInfo
{
ProviderName = Name,
Type = ImageType.Banner,
Url = banner
});
}
}
return list;
}
public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
{
return _httpClient.GetResponse(new HttpRequestOptions
{
CancellationToken = cancellationToken,
Url = url,
ResourcePool = ResourcePool
});
}
}
}
}
}

View File

@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
namespace MediaBrowser.Plugins.Anime.Providers.AniList
{

View File

@ -0,0 +1,134 @@
/*using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Providers;
using MediaBrowser.Plugins.Anime.Providers.AniDB.Converter;
namespace MediaBrowser.Plugins.Anime.Providers.AniSearch
{
/// <summary>
/// The <see cref="AniSearchEpisodeProvider" /> class provides episode metadata from AniSearch.
/// </summary>
public class AniSearchEpisodeProvider : IRemoteMetadataProvider<Episode, EpisodeInfo>
{
private readonly IServerConfigurationManager _configurationManager;
private readonly IHttpClient _httpClient;
/// <summary>
/// Creates a new instance of the <see cref="AniDbEpisodeProvider" /> class.
/// </summary>
/// <param name="configurationManager">The configuration manager.</param>
/// <param name="httpClient">The HTTP client.</param>
public AniSearchEpisodeProvider(IServerConfigurationManager configurationManager, IHttpClient httpClient)
{
_configurationManager = configurationManager;
_httpClient = httpClient;
}
public async Task<MetadataResult<Episode>> GetMetadata(EpisodeInfo info, CancellationToken cancellationToken)
{
var result = new MetadataResult<Episode>();
cancellationToken.ThrowIfCancellationRequested();
var anisearchId = info.ProviderIds.GetOrDefault(ProviderNames.AniSearch);
if (string.IsNullOrEmpty(anisearchId))
return result;
var id = AnidbEpisodeIdentity.Parse(anisearchId);
if (id == null)
return result;
result.Item = new Episode
{
IndexNumber = info.IndexNumber,
ParentIndexNumber = info.ParentIndexNumber
};
string url= "https://www.anisearch.de/anime/"+id+"/episodes";
string web_content = await api.WebRequestAPI(url);
result.HasMetadata = true;
if (id.Value.EpisodeNumberEnd != null && id.Value.EpisodeNumberEnd > id.Value.EpisodeNumber)
{
for (var i = id.Value.EpisodeNumber + 1; i <= id.Value.EpisodeNumberEnd; i++)
{
string episode = await api.One_line_regex(new System.Text.RegularExpressions.Regex("<span itemprop=\"name\" lang=\"de\" class=\"bold\">" + @"(.*?)<"), web_content, 1, i);
if(episode == "")
{
}
else
{
}
}
}
return result;
}
public string Name => "AniSearch";
public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(EpisodeInfo searchInfo, CancellationToken cancellationToken)
{
var list = new List<RemoteSearchResult>();
var id = AnidbEpisodeIdentity.Parse(searchInfo.ProviderIds.GetOrDefault(ProviderNames.AniSearch));
if (id == null)
{
//var episodeIdentifier = new AnidbEpisodeIdentityProvider();
//await episodeIdentifier.Identify(searchInfo);
//var converter = new AnidbTvdbEpisodeConverter();
//await converter.Convert(searchInfo);
//id = AnidbEpisodeIdentity.Parse(searchInfo.ProviderIds.GetOrDefault(ProviderNames.AniDb));
}
if (id == null)
return list;
try
{
var metadataResult = await GetMetadata(searchInfo, cancellationToken).ConfigureAwait(false);
if (metadataResult.HasMetadata)
{
var item = metadataResult.Item;
list.Add(new RemoteSearchResult
{
IndexNumber = item.IndexNumber,
Name = item.Name,
ParentIndexNumber = item.ParentIndexNumber,
PremiereDate = item.PremiereDate,
ProductionYear = item.ProductionYear,
ProviderIds = item.ProviderIds,
SearchProviderName = Name,
IndexNumberEnd = item.IndexNumberEnd
});
}
}
catch (FileNotFoundException)
{
// Don't fail the provider because this will just keep on going and going.
}
catch (DirectoryNotFoundException)
{
// Don't fail the provider because this will just keep on going and going.
}
return list;
}
public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
}
}
*/

View File

@ -0,0 +1,29 @@
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
namespace MediaBrowser.Plugins.Anime.Providers.AniSearch
{
public class AniSearchExternalId : IExternalId
{
public bool Supports(IHasProviderIds item)
{
return item is Series;
}
public string Name
{
get { return "AniSearch"; }
}
public string Key
{
get { return ProviderNames.AniSearch; }
}
public string UrlFormatString
{
get { return "http://www.anisearch.de/anime/{0}"; }
}
}
}

View File

@ -0,0 +1,162 @@
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Providers;
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Plugins.Anime.Providers.AniSearch
{
public class AniSearchSeriesProvider : IRemoteMetadataProvider<Series, SeriesInfo>, IHasOrder
{
private readonly IHttpClient _httpClient;
private readonly IApplicationPaths _paths;
private readonly ILogger _log;
public int Order => -3;
public string Name => "AniSearch";
public static readonly SemaphoreSlim ResourcePool = new SemaphoreSlim(1, 1);
public AniSearchSeriesProvider(IApplicationPaths appPaths, IHttpClient httpClient, ILogManager logManager)
{
_log = logManager.GetLogger("AniSearch");
_httpClient = httpClient;
_paths = appPaths;
}
public async Task<MetadataResult<Series>> GetMetadata(SeriesInfo info, CancellationToken cancellationToken)
{
var result = new MetadataResult<Series>();
var aid = info.ProviderIds.GetOrDefault(ProviderNames.AniSearch);
if (string.IsNullOrEmpty(aid))
{
_log.Info("Start AniSearch... Searching(" + info.Name + ")");
aid = await api.FindSeries(info.Name, cancellationToken);
}
if (!string.IsNullOrEmpty(aid))
{
string WebContent = await api.WebRequestAPI(api.AniSearch_anime_link + aid);
result.Item = new Series();
result.HasMetadata = true;
result.Item.ProviderIds.Add(ProviderNames.AniSearch, aid);
result.Item.Overview = await api.Get_Overview(WebContent);
try
{
//AniSearch has a max rating of 5
result.Item.CommunityRating = (float.Parse(await api.Get_Rating(WebContent), System.Globalization.CultureInfo.InvariantCulture) * 2);
}
catch (Exception) { }
foreach (var genre in await api.Get_Genre(WebContent))
result.Item.AddGenre(genre);
GenreHelper.CleanupGenres(result.Item);
StoreImageUrl(aid, await api.Get_ImageUrl(WebContent), "image");
}
return result;
}
public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(SeriesInfo searchInfo, CancellationToken cancellationToken)
{
var results = new Dictionary<string, RemoteSearchResult>();
var aid = searchInfo.ProviderIds.GetOrDefault(ProviderNames.AniSearch);
if (!string.IsNullOrEmpty(aid))
{
if (!results.ContainsKey(aid))
results.Add(aid, await api.GetAnime(aid));
}
if (!string.IsNullOrEmpty(searchInfo.Name))
{
List<string> ids = await api.Search_GetSeries_list(searchInfo.Name, cancellationToken);
foreach (string a in ids)
{
results.Add(a, await api.GetAnime(a));
}
}
return results.Values;
}
private void StoreImageUrl(string series, string url, string type)
{
var path = Path.Combine(_paths.CachePath, "anisearch", type, series + ".txt");
var directory = Path.GetDirectoryName(path);
Directory.CreateDirectory(directory);
File.WriteAllText(path, url);
}
public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
{
return _httpClient.GetResponse(new HttpRequestOptions
{
CancellationToken = cancellationToken,
Url = url,
ResourcePool = ResourcePool
});
}
}
public class AniSearchSeriesImageProvider : IRemoteImageProvider
{
private readonly IHttpClient _httpClient;
private readonly IApplicationPaths _appPaths;
public AniSearchSeriesImageProvider(IHttpClient httpClient, IApplicationPaths appPaths)
{
_httpClient = httpClient;
_appPaths = appPaths;
}
public string Name => "AniSearch";
public bool Supports(IHasMetadata item) => item is Series || item is Season;
public IEnumerable<ImageType> GetSupportedImages(IHasMetadata item)
{
return new[] { ImageType.Primary };
}
public Task<IEnumerable<RemoteImageInfo>> GetImages(IHasMetadata item, CancellationToken cancellationToken)
{
var seriesId = item.GetProviderId(ProviderNames.AniSearch);
return GetImages(seriesId, cancellationToken);
}
public async Task<IEnumerable<RemoteImageInfo>> GetImages(string aid, CancellationToken cancellationToken)
{
var list = new List<RemoteImageInfo>();
if (!string.IsNullOrEmpty(aid))
{
var primary = await api.Get_ImageUrl(await api.WebRequestAPI(api.AniSearch_anime_link + aid));
list.Add(new RemoteImageInfo
{
ProviderName = Name,
Type = ImageType.Primary,
Url = primary
});
}
return list;
}
public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
{
return _httpClient.GetResponse(new HttpRequestOptions
{
CancellationToken = cancellationToken,
Url = url,
ResourcePool = AniSearchSeriesProvider.ResourcePool
});
}
}
}

View File

@ -0,0 +1,306 @@
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Providers;
using MediaBrowser.Plugins.Anime.Configuration;
using System;
using System.Collections.Generic;
using System.Net;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Plugins.Anime.Providers.AniSearch
{
/// <summary>
/// API for http://anisearch.de a german anime database
/// 🛈 Anisearch does not have an API interface to work with
/// </summary>
internal class api
{
public static List<string> anime_search_names = new List<string>();
public static List<string> anime_search_ids = new List<string>();
public static string SearchLink = "https://www.anisearch.de/anime/index/?char=all&page=1&text={0}&smode=2&sort=title&order=asc&view=2&title=de,en,fr,it,pl,ru,es,tr&titlex=1,2&hentai=yes";
public static string AniSearch_anime_link = "https://www.anisearch.de/anime/";
/// <summary>
/// API call to get the anime with the id
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public static async Task<RemoteSearchResult> GetAnime(string id)
{
string WebContent = await WebRequestAPI(AniSearch_anime_link + id);
var result = new RemoteSearchResult
{
Name = await SelectName(WebContent, Plugin.Instance.Configuration.TitlePreference, "en")
};
result.SearchProviderName = await One_line_regex(new Regex("\"" + "Japanisch" + "\"" + @"> <strong>(.*?)<\/"), WebContent);
result.ImageUrl = await Get_ImageUrl(WebContent);
result.SetProviderId(ProviderNames.AniSearch, id);
result.Overview = await Get_Overview(WebContent);
return result;
}
/// <summary>
/// API call to select the lang
/// </summary>
/// <param name="WebContent"></param>
/// <param name="preference"></param>
/// <param name="language"></param>
/// <returns></returns>
private static async Task<string> SelectName(string WebContent, TitlePreferenceType preference, string language)
{
if (preference == TitlePreferenceType.Localized && language == "en")
return await Get_title("en", WebContent);
if (preference == TitlePreferenceType.Localized && language == "de")
return await Get_title("de", WebContent);
if (preference == TitlePreferenceType.Localized && language == "ger")
return await Get_title("de", WebContent);
if (preference == TitlePreferenceType.Japanese)
return await Get_title("jap", WebContent);
return await Get_title("jap_r", WebContent);
}
/// <summary>
/// API call to get the title with the right lang
/// </summary>
/// <param name="lang"></param>
/// <param name="WebContent"></param>
/// <returns></returns>
public static async Task<string> Get_title(string lang, string WebContent)
{
switch (lang)
{
case "en":
return await One_line_regex(new Regex("\"" + "Englisch" + "\"" + @"> <strong>(.*?)<\/"), WebContent);
case "de":
return await One_line_regex(new Regex("\"" + "Deutsch" + "\"" + @"> <strong>(.*?)<\/"), WebContent);
case "jap":
return await One_line_regex(new Regex("<div class=\"grey\">" + @"(.*?)<\/"), await One_line_regex(new Regex("\"" + "Englisch" + "\"" + @"> <strong>(.*?)<\/div"), WebContent));
//Default is jap_r
default:
return await One_line_regex(new Regex("\"" + "Japanisch" + "\"" + @"> <strong>(.*?)<\/"), WebContent);
}
}
/// <summary>
/// API call to get the genre of the anime
/// </summary>
/// <param name="WebContent"></param>
/// <returns></returns>
public static async Task<List<string>> Get_Genre(string WebContent)
{
List<string> result = new List<string>();
string Genres = await One_line_regex(new Regex("<ul class=\"cloud\">" + @"(.*?)<\/ul>"), WebContent);
int x = 0;
string AniSearch_Genre = null;
while (AniSearch_Genre != "")
{
AniSearch_Genre = await One_line_regex(new Regex(@"<li>(.*?)<\/li>"), Genres, 0, x);
AniSearch_Genre = await One_line_regex(new Regex("\">" + @"(.*?)<\/a>"), AniSearch_Genre);
if (AniSearch_Genre != "")
{
result.Add(AniSearch_Genre);
}
x++;
}
return result;
}
/// <summary>
/// API call to get the img url
/// </summary>
/// <param name="WebContent"></param>
/// <returns></returns>
public static async Task<string> Get_ImageUrl(string WebContent)
{
return await One_line_regex(new Regex("<img itemprop=\"image\" src=\"" + @"(.*?)" + "\""), WebContent);
}
/// <summary>
/// API call too get the rating
/// </summary>
/// <param name="WebContent"></param>
/// <returns></returns>
public static async Task<string> Get_Rating(string WebContent)
{
return await One_line_regex(new Regex("<span itemprop=\"ratingValue\">" + @"(.*?)" + @"<\/span>"), WebContent);
}
/// <summary>
/// API call to get the description
/// </summary>
/// <param name="WebContent"></param>
/// <returns></returns>
public static async Task<string> Get_Overview(string WebContent)
{
return Regex.Replace(await One_line_regex(new Regex("<span itemprop=\"description\" lang=\"de\" id=\"desc-de\" class=\"desc-zz textblock\">" + @"(.*?)<\/span>"), WebContent), "<.*?>", String.Empty);
}
/// <summary>
/// API call to search a title and return the right one back
/// </summary>
/// <param name="title"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public static async Task<string> Search_GetSeries(string title, CancellationToken cancellationToken)
{
anime_search_names.Clear();
anime_search_ids.Clear();
string result = null;
string result_text = null;
string WebContent = await WebRequestAPI(string.Format(SearchLink, title));
int x = 0;
while (result_text != "")
{
result_text = await One_line_regex(new Regex("<th scope=\"row\" class=\"showpop\" data-width=\"200\"" + @".*?>(.*)<\/th>"), WebContent, 1, x);
if (result_text != "")
{
//get id
int _x = 0;
string a_name = null;
while (a_name != "")
{
try
{
string id = await One_line_regex(new Regex(@"anime\/(.*?),"), result_text);
a_name = Regex.Replace(await One_line_regex(new Regex(@"((<a|<d).*?>)(.*?)(<\/a>|<\/div>)"), result_text, 3, _x), "<.*?>", String.Empty);
if (a_name != "")
{
if (await Task.Run(() => Equals_check.Compare_strings(a_name, title)))
{
return id;
}
int n;
if (Int32.TryParse(id, out n))
{
anime_search_names.Add(a_name);
anime_search_ids.Add(id);
}
}
}
catch (Exception) { }
_x++;
}
}
x++;
}
return result;
}
/// <summary>
/// API call to search a title and return a list back
/// </summary>
/// <param name="title"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public static async Task<List<string>> Search_GetSeries_list(string title, CancellationToken cancellationToken)
{
List<string> result = new List<string>();
string result_text = null;
string WebContent = await WebRequestAPI(string.Format(SearchLink, title));
int x = 0;
while (result_text != "")
{
result_text = await One_line_regex(new Regex("<th scope=\"row\" class=\"showpop\" data-width=\"200\"" + @".*?>(.*)<\/th>"), WebContent, 1, x);
if (result_text != "")
{
//get id
int _x = 0;
string a_name = null;
while (a_name != "")
{
string id = await One_line_regex(new Regex(@"anime\/(.*?),"), result_text);
a_name = Regex.Replace(await One_line_regex(new Regex(@"((<a|<d).*?>)(.*?)(<\/a>|<\/div>)"), result_text, 3, _x), "<.*?>", String.Empty);
if (a_name != "")
{
if (Equals_check.Compare_strings(a_name, title))
{
result.Add(id);
return result;
}
int n;
if (Int32.TryParse(id, out n))
{
result.Add(id);
}
}
_x++;
}
}
x++;
}
return result;
}
/// <summary>
/// SEARCH Title
/// </summary>
public static async Task<string> FindSeries(string title, CancellationToken cancellationToken)
{
string aid = await Search_GetSeries(title, cancellationToken);
if (!string.IsNullOrEmpty(aid))
{
return aid;
}
else
{
int x = 0;
foreach (string a_name in anime_search_names)
{
if (Equals_check.Compare_strings(a_name, title))
{
return anime_search_ids[x];
}
x++;
}
}
aid = await Search_GetSeries(Equals_check.clear_name(title), cancellationToken);
if (!string.IsNullOrEmpty(aid))
{
return aid;
}
return null;
}
/// <summary>
/// Simple regex
/// </summary>
public static async Task<string> One_line_regex(Regex regex, string match, int group = 1, int match_int = 0)
{
Regex _regex = regex;
int x = 0;
MatchCollection matches = await Task.Run(() => regex.Matches(match));
foreach (Match _match in matches)
{
if (x == match_int)
{
return await Task.Run(() => _match.Groups[group].Value.ToString());
}
x++;
}
return "";
}
/// <summary>
/// GET website content from the link
/// </summary>
public static async Task<string> WebRequestAPI(string link)
{
string _strContent = "";
using (WebClient client = new WebClient())
{
Task<string> async_content = client.DownloadStringTaskAsync(link);
_strContent = await async_content;
}
return _strContent;
}
}
}

View File

@ -33,7 +33,7 @@
// public async Task Run(Series series, CancellationToken cancellationToken)
// {
// await RemoveObsoleteSeasons(series).ConfigureAwait(false);
//
//
// var hasNewSeasons = await AddDummySeasonFolders(series, cancellationToken).ConfigureAwait(false);
//
// if (hasNewSeasons)
@ -115,7 +115,7 @@
// };
//
// season.SetParent(series);
//
//
// await series.AddChild(season, cancellationToken).ConfigureAwait(false);
//
// await season.RefreshMetadata(new MetadataRefreshOptions(), cancellationToken).ConfigureAwait(false);
@ -179,4 +179,4 @@
// return hasChanges;
// }
// }
//}
//}

View File

@ -1,8 +1,8 @@
using System;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Plugins.Anime.Configuration;
using System;
using System.Collections.Generic;
using System.Linq;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Plugins.Anime.Configuration;
namespace MediaBrowser.Plugins.Anime.Providers
{
@ -49,19 +49,159 @@ namespace MediaBrowser.Plugins.Anime.Providers
{"Vampire", "Supernatural"},
{"Yaoi", "Adult"},
{"Yuri", "Adult"},
{"Zombie", "Supernatural"}
{"Zombie", "Supernatural"},
//AniSearch Genre
{"Geister­geschichten", "Geister­geschichten"},
{"Romanze", "Romance"},
{"Alltagsdrama", "Slice of Life"},
{"Alltagsleben", "Slice of Life"},
{"Psychodrama", "Psycho"},
{"Actiondrama", "Action"},
{"Nonsense-Komödie", "Comedy"},
{"Magie", "Fantasy"},
{"Abenteuer", "Adventure"},
{"Komödie", "Comedy"},
{"Erotik", "Adult"},
{"Historisch", "Period & Historical"},
//Proxer
{"Slice_of_Life", "Slice of Life"},
};
private static readonly string[] GenresAsTags =
{
"Hentai",
"Space",
"Vampire",
"Weltraum",
"Yaoi",
"Yuri",
"Zombie",
"Demons",
"Witch"
"Witch",
//AniSearchTags
"Krieg",
"Militär",
"Satire",
"Übermäßige Gewaltdarstellung",
"Monster",
"Zeitgenössische Fantasy",
"Dialogwitz",
"Romantische Komödie",
"Slapstick",
"Alternative Welt",
"4-panel",
"CG-Anime",
"Episodisch",
"Moe",
"Parodie",
"Splatter",
"Tragödie",
"Verworrene Handlung",
//Themen
"Erwachsenwerden",
"Gender Bender",
"Ältere Frau, jüngerer Mann",
"Älterer Mann, jüngere Frau",
//Schule (School)
"Grundschule",
"Kindergarten",
"Klubs",
"Mittelschule",
"Oberschule",
"Schule",
"Universität",
//Zeit (Time)
"Altes Asien",
"Frühe Neuzeit",
"Gegenwart",
"industrialisierung",
"Meiji-Ära",
"Mittelalter",
"Weltkriege",
//Fantasy
"Dunkle Fantasy",
"Epische Fantasy",
"Zeitgenössische Fantasy",
//Ort
"Alternative Welt",
"In einem Raumschiff",
"Weltraum",
//Setting
"Cyberpunk",
"Endzeit",
"Space Opera",
//Hauptfigur
"Charakterschache Heldin",
"Charakterschacher Held",
"Charakterstarke Heldin",
"Charakterstarker Held",
"Gedächtnisverlust",
"Stoische Heldin",
"Stoischer Held",
"Widerwillige Heldin",
"Widerwilliger Held",
//Figuren
"Diva",
"Genie",
"Schul-Delinquent",
"Tomboy",
"Tsundere",
"Yandere",
//Kampf (fight)
"Bionische Kräfte",
"Martial Arts",
"PSI-Kräfte",
"Real Robots",
"Super Robots",
"Schusswaffen",
"Schwerter & co",
//Sports (Sport)
"Baseball",
"Boxen",
"Denk- und Glücksspiele",
"Football",
"Fußball",
"Kampfsport",
"Rennsport",
"Tennis",
//Kunst (Art)
"Anime & Film",
"Malerei",
"Manga & Doujinshi",
"Musik",
"Theater",
//Tätigkeit
"Band",
"Detektiv",
"Dieb",
"Essenszubereitung",
"Idol",
"Kopfgeldjäger",
"Ninja",
"Polizist",
"Ritter",
"Samurai",
"Solosänger",
//Wesen
"Außerirdische",
"Cyborgs",
"Dämonen",
"Elfen",
"Geister",
"Hexen",
"Himmlische Wesen",
"Kamis",
"Kemonomimi",
"Monster",
"Roboter & Androiden",
"Tiermenschen",
"Vampire",
"Youkai",
"Zombie",
//Proxer
"Virtual Reality",
"Game",
"Survival",
"Fanservice",
"Schlauer Protagonist",
};
private static readonly Dictionary<string, string> IgnoreIfPresent = new Dictionary<string, string>
@ -90,7 +230,7 @@ namespace MediaBrowser.Plugins.Anime.Providers
max = Math.Max(max - 1, 0);
}
if (config.MaxGenres > 0)
{
series.Genres = series.Genres.Take(max).ToList();

View File

@ -525,4 +525,4 @@
// return airDate;
// }
// }
//}
//}

View File

@ -0,0 +1,29 @@
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
namespace MediaBrowser.Plugins.Anime.Providers.MyAnimeList
{
public class MyAnimeListrExternalId : IExternalId
{
public bool Supports(IHasProviderIds item)
{
return item is Series;
}
public string Name
{
get { return "MyAnimeList"; }
}
public string Key
{
get { return ProviderNames.MyAnimeList; }
}
public string UrlFormatString
{
get { return "https://myanimelist.net/anime/{0}"; }
}
}
}

View File

@ -0,0 +1,169 @@
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Providers;
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Plugins.Anime.Providers.MyAnimeList
{
public class MyAnimeListSeriesProvider : IRemoteMetadataProvider<Series, SeriesInfo>, IHasOrder
{
private readonly ILogger _log;
private readonly IHttpClient _httpClient;
private readonly IApplicationPaths _paths;
public static readonly SemaphoreSlim ResourcePool = new SemaphoreSlim(1, 1);
public static string provider_name = ProviderNames.MyAnimeList;
public int Order => -5;
public string Name => "MyAnimeList";
public MyAnimeListSeriesProvider(IApplicationPaths appPaths, IHttpClient httpClient, ILogManager logManager)
{
_log = logManager.GetLogger("MyAnimeList");
_httpClient = httpClient;
_paths = appPaths;
}
public async Task<MetadataResult<Series>> GetMetadata(SeriesInfo info, CancellationToken cancellationToken)
{
var result = new MetadataResult<Series>();
var aid = info.ProviderIds.GetOrDefault(provider_name);
if (string.IsNullOrEmpty(aid))
{
_log.Info("Start MyAnimeList... Searching(" + info.Name + ")");
aid = await api.FindSeries(info.Name, cancellationToken);
}
if (!string.IsNullOrEmpty(aid))
{
string WebContent = await api.WebRequestAPI(api.anime_link + aid);
result.Item = new Series();
result.HasMetadata = true;
result.Item.ProviderIds.Add(provider_name, aid);
result.Item.Overview = await api.Get_OverviewAsync(WebContent);
result.ResultLanguage = "eng";
try
{
result.Item.CommunityRating = float.Parse(await api.Get_RatingAsync(WebContent), System.Globalization.CultureInfo.InvariantCulture);
}
catch (Exception) { }
foreach (var genre in await api.Get_GenreAsync(WebContent))
{
if (!string.IsNullOrEmpty(genre))
{
result.Item.AddGenre(genre);
}
}
GenreHelper.CleanupGenres(result.Item);
StoreImageUrl(aid, await api.Get_ImageUrlAsync(WebContent), "image");
}
return result;
}
public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(SeriesInfo searchInfo, CancellationToken cancellationToken)
{
var results = new Dictionary<string, RemoteSearchResult>();
var aid = searchInfo.ProviderIds.GetOrDefault(provider_name);
if (!string.IsNullOrEmpty(aid))
{
if (!results.ContainsKey(aid))
results.Add(aid, await api.GetAnime(aid, cancellationToken));
}
if (!string.IsNullOrEmpty(searchInfo.Name))
{
List<string> ids = await api.Search_GetSeries_list(searchInfo.Name, cancellationToken);
foreach (string a in ids)
{
results.Add(a, await api.GetAnime(a, cancellationToken));
}
}
return results.Values;
}
private void StoreImageUrl(string series, string url, string type)
{
var path = Path.Combine(_paths.CachePath, "myanimelist", type, series + ".txt");
var directory = Path.GetDirectoryName(path);
Directory.CreateDirectory(directory);
File.WriteAllText(path, url);
}
public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
{
return _httpClient.GetResponse(new HttpRequestOptions
{
CancellationToken = cancellationToken,
Url = url,
ResourcePool = ResourcePool
});
}
}
public class MyAnimeListSeriesImageProvider : IRemoteImageProvider
{
private readonly IHttpClient _httpClient;
private readonly IApplicationPaths _appPaths;
public static readonly SemaphoreSlim ResourcePool = new SemaphoreSlim(1, 1);
public MyAnimeListSeriesImageProvider(IHttpClient httpClient, IApplicationPaths appPaths)
{
_httpClient = httpClient;
_appPaths = appPaths;
}
public string Name => "MyAnimeList";
public bool Supports(IHasMetadata item) => item is Series || item is Season;
public IEnumerable<ImageType> GetSupportedImages(IHasMetadata item)
{
return new[] { ImageType.Primary };
}
public Task<IEnumerable<RemoteImageInfo>> GetImages(IHasMetadata item, CancellationToken cancellationToken)
{
var seriesId = item.GetProviderId(MyAnimeListSeriesProvider.provider_name);
return GetImages(seriesId, cancellationToken);
}
public async Task<IEnumerable<RemoteImageInfo>> GetImages(string aid, CancellationToken cancellationToken)
{
var list = new List<RemoteImageInfo>();
if (!string.IsNullOrEmpty(aid))
{
var primary = await api.Get_ImageUrlAsync(await api.WebRequestAPI(api.anime_link + aid));
list.Add(new RemoteImageInfo
{
ProviderName = Name,
Type = ImageType.Primary,
Url = primary
});
}
return list;
}
public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
{
return _httpClient.GetResponse(new HttpRequestOptions
{
CancellationToken = cancellationToken,
Url = url,
ResourcePool = MyAnimeListSeriesProvider.ResourcePool
});
}
}
}

View File

@ -0,0 +1,352 @@
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Providers;
using MediaBrowser.Plugins.Anime.Configuration;
using System;
using System.Collections.Generic;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Plugins.Anime.Providers.MyAnimeList
{
/// <summary>
/// This API use the WebContent of MyAnimelist and the API of MyAnimelist
/// </summary>
internal class api
{
public static List<string> anime_search_names = new List<string>();
public static List<string> anime_search_ids = new List<string>();
//Use API too search
public static string SearchLink = "https://myanimelist.net/api/anime/search.xml?q={0}";
//No API funktion exist too get anime
public static string anime_link = "https://myanimelist.net/anime/info/";
/// <summary>
/// WebContent API call to get a anime with id
/// </summary>
/// <param name="id"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public static async Task<RemoteSearchResult> GetAnime(string id, CancellationToken cancellationToken)
{
string WebContent = await WebRequestAPI(anime_link + id);
var result = new RemoteSearchResult
{
Name = await SelectName(WebContent, Plugin.Instance.Configuration.TitlePreference, "en", cancellationToken)
};
result.SearchProviderName = WebUtility.HtmlDecode(await one_line_regex(new Regex("<span itemprop=\"name\">" + @"(.*?)<"), WebContent));
result.ImageUrl = await Get_ImageUrlAsync(WebContent);
result.SetProviderId(MyAnimeListSeriesProvider.provider_name, id);
result.Overview = await Get_OverviewAsync(WebContent);
return result;
}
/// <summary>
/// WebContent API call to select a prefence title
/// </summary>
/// <param name="WebContent"></param>
/// <param name="preference"></param>
/// <param name="language"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
private static async Task<string> SelectName(string WebContent, TitlePreferenceType preference, string language, CancellationToken cancellationToken)
{
if (preference == TitlePreferenceType.Localized && language == "en")
return await Get_title("en", WebContent);
if (preference == TitlePreferenceType.Localized && language == "de")
return await Get_title("de", WebContent);
if (preference == TitlePreferenceType.Localized && language == "ger")
return await Get_title("de", WebContent);
if (preference == TitlePreferenceType.Japanese)
return await Get_title("jap", WebContent);
return await Get_title("jap_r", WebContent);
}
/// <summary>
/// WebContent API call get a specific title
/// </summary>
/// <param name="lang"></param>
/// <param name="WebContent"></param>
/// <returns></returns>
public static async Task<string> Get_title(string lang, string WebContent)
{
switch (lang)
{
case "en":
return WebUtility.HtmlDecode(await one_line_regex(new Regex(@">([\S\s]*?)<"), await one_line_regex(new Regex(@"English:<\/span>(?s)(.*?)<"), WebContent)));
case "jap":
return WebUtility.HtmlDecode(await one_line_regex(new Regex(@">([\S\s]*?)<"), await one_line_regex(new Regex(@"Japanese:<\/span>(?s)(.*?)<"), WebContent)));
//Default is jap_r
default:
return WebUtility.HtmlDecode(await one_line_regex(new Regex("<span itemprop=\"name\">" + @"(.*?)<"), WebContent));
}
}
/// <summary>
/// WebContent API call get genre
/// </summary>
/// <param name="WebContent"></param>
/// <returns></returns>
public static async Task<List<string>> Get_GenreAsync(string WebContent)
{
try
{
List<string> result = new List<string>();
string Genres = await one_line_regex(new Regex(@"\.setTargeting\(" + "\"genres\"" + @", \[(.*?)\])"), WebContent);
int x = 1;
Genres = Genres.Replace("\"", "");
foreach (string Genre in Genres.Split(','))
{
if (!string.IsNullOrEmpty(Genre))
{
result.Add(Genre);
}
x++;
}
return result;
}
catch (Exception)
{
List<string> test = new List<string>();
test.Add("");
return test;
}
}
/// <summary>
/// WebContent API call get rating
/// </summary>
/// <param name="WebContent"></param>
public static async Task<string> Get_RatingAsync(string WebContent)
{
return await one_line_regex(new Regex("<span itemprop=\"ratingValue\">" + @"(.*?)<"), WebContent);
}
/// <summary>
/// WebContent API call to get the imgurl
/// </summary>
/// <param name="WebContent"></param>
/// <returns></returns>
public static async Task<string> Get_ImageUrlAsync(string WebContent)
{
return await one_line_regex(new Regex("src=\"" + @"(?s)(.*?)" + "\""), await one_line_regex(new Regex(" < div style=\"text - align: center; \">" + @"(?s)(.*?)alt="), WebContent));
}
/// <summary>
/// WebContent API call to get the description
/// </summary>
/// <param name="WebContent"></param>
/// <returns></returns>
public static async Task<string> Get_OverviewAsync(string WebContent)
{
return System.Net.WebUtility.HtmlDecode(await one_line_regex(new Regex("\"og: description\" content=\"" + @"(.*?)" + "\">"), WebContent));
}
/// <summary>
/// MyAnimeListAPI call to search the right series
/// </summary>
/// <param name="title"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public static async Task<string> Search_GetSeries(string title, CancellationToken cancellationToken)
{
anime_search_names.Clear();
anime_search_ids.Clear();
string result = null;
string result_text = null;
string WebContent = await WebRequestAPI(string.Format(SearchLink, Uri.EscapeUriString(title)), Plugin.Instance.Configuration.MyAnimeList_API_Name, Plugin.Instance.Configuration.MyAnimeList_API_Pw);
int x = 0;
while (result_text != "")
{
result_text = await one_line_regex(new Regex(@"<entry>(.*?)<\/entry>"), WebContent, 1, x);
if (result_text != "")
{
//get id
string id = await one_line_regex(new Regex(@"<id>(.*?)<\/id>"), result_text);
string a_name = await one_line_regex(new Regex(@"<title>(.*?)<\/title>"), result_text);
string b_name = await one_line_regex(new Regex(@"<english>(.*?)<\/english>"), result_text);
string c_name = await one_line_regex(new Regex(@"<synonyms>(.*?)<\/synonyms>"), result_text);
if (Equals_check.Compare_strings(a_name, title))
{
result = id;
return result;
}
if (Equals_check.Compare_strings(b_name, title))
{
result = id;
return result;
}
foreach (string d_name in c_name.Split(';'))
{
if (Equals_check.Compare_strings(d_name, title))
{
result = id;
return result;
}
}
int n;
if (Int32.TryParse(id, out n))
{
anime_search_names.Add(a_name);
anime_search_ids.Add(id);
}
}
x++;
}
return result;
}
/// <summary>
/// MyAnimeListAPI call to search the series and return a list
/// </summary>
/// <param name="title"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public static async Task<List<string>> Search_GetSeries_list(string title, CancellationToken cancellationToken)
{
List<string> result = new List<string>();
string result_text = null;
string WebContent = await WebRequestAPI(string.Format(SearchLink, Uri.EscapeUriString(title)), Plugin.Instance.Configuration.MyAnimeList_API_Name, Plugin.Instance.Configuration.MyAnimeList_API_Pw);
int x = 0;
while (result_text != "")
{
result_text = await one_line_regex(new Regex(@"<entry>(.*?)<\/entry>"), WebContent, 1, x);
if (result_text != "")
{
//get id
string id = await one_line_regex(new Regex(@"<id>(.*?)<\/id>"), result_text);
string a_name = await one_line_regex(new Regex(@"<title>(.*?)<\/title>"), result_text);
string b_name = await one_line_regex(new Regex(@"<english>(.*?)<\/english>"), result_text);
string c_name = await one_line_regex(new Regex(@"<synonyms>(.*?)<\/synonyms>"), result_text);
if (Equals_check.Compare_strings(a_name, title))
{
result.Add(id);
return result;
}
if (Equals_check.Compare_strings(b_name, title))
{
result.Add(id);
return result;
}
foreach (string d_name in c_name.Split(';'))
{
if (Equals_check.Compare_strings(d_name, title))
{
result.Add(id);
return result;
}
}
int n;
if (Int32.TryParse(id, out n))
{
result.Add(id);
}
}
x++;
}
return result;
}
/// <summary>
/// MyAnimeListAPI call to find a series
/// </summary>
/// <param name="title"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public static async Task<string> FindSeries(string title, CancellationToken cancellationToken)
{
string aid = await Search_GetSeries(title, cancellationToken);
if (!string.IsNullOrEmpty(aid))
{
return aid;
}
else
{
int x = 0;
foreach (string a_name in anime_search_names)
{
if (Equals_check.Compare_strings(a_name, title))
{
return anime_search_ids[x];
}
x++;
}
}
aid = await Search_GetSeries(Equals_check.clear_name(title), cancellationToken);
if (!string.IsNullOrEmpty(aid))
{
return aid;
}
return null;
}
/// <summary>
/// simple regex
/// </summary>
/// <param name="regex"></param>
/// <param name="match"></param>
/// <param name="group"></param>
/// <param name="match_int"></param>
/// <returns></returns>
public static async Task<string> one_line_regex(Regex regex, string match, int group = 1, int match_int = 0)
{
Regex _regex = regex;
int x = 0;
MatchCollection matches = await Task.Run(() => regex.Matches(match));
foreach (Match _match in matches)
{
if (x == match_int)
{
return await Task.Run(() => _match.Groups[group].Value.ToString());
}
x++;
}
return "";
}
/// <summary>
/// A WebRequestAPI too handle the Webcontent and the API of MyAnimeList
/// </summary>
/// <param name="link"></param>
/// <param name="name"></param>
/// <param name="pw"></param>
/// <returns></returns>
public static async Task<string> WebRequestAPI(string link, string name = null, string pw = null)
{
try
{
string encoded = await Task.Run(() => Convert.ToBase64String(Encoding.GetEncoding("ISO-8859-1").GetBytes(name + ":" + pw)));
string _strContent;
using (WebClient client = new WebClient())
{
if (!await Task.Run(() => string.IsNullOrEmpty(name)) && !await Task.Run(() => string.IsNullOrEmpty(pw)))
{
client.Headers.Add("Authorization", "Basic " + encoded);
}
Task<string> async_content = client.DownloadStringTaskAsync(link);
_strContent = await async_content;
}
return _strContent;
}
catch (WebException)
{
return "";
}
}
}
}

View File

@ -5,5 +5,7 @@
public const string AniDb = "AniDB";
public const string MyAnimeList = "MyAnimeList";
public const string AniList = "AniList";
public const string AniSearch = "AniSearch";
public const string Proxer = "Proxer";
}
}
}

View File

@ -0,0 +1,29 @@
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
namespace MediaBrowser.Plugins.Anime.Providers.Proxer
{
public class ProxerExternalId : IExternalId
{
public bool Supports(IHasProviderIds item)
{
return item is Series;
}
public string Name
{
get { return "Proxer"; }
}
public string Key
{
get { return ProviderNames.Proxer; }
}
public string UrlFormatString
{
get { return "https://proxer.me/info/{0}"; }
}
}
}

View File

@ -0,0 +1,163 @@
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Providers;
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Plugins.Anime.Providers.Proxer
{
public class ProxerSeriesProvider : IRemoteMetadataProvider<Series, SeriesInfo>, IHasOrder
{
private readonly ILogger _log;
private readonly IHttpClient _httpClient;
private readonly IApplicationPaths _paths;
public static string provider_name = ProviderNames.Proxer;
public static readonly SemaphoreSlim ResourcePool = new SemaphoreSlim(1, 1);
public int Order => -4;
public string Name => "Proxer";
public ProxerSeriesProvider(IApplicationPaths appPaths, IHttpClient httpClient, ILogManager logManager)
{
_log = logManager.GetLogger("Proxer");
_httpClient = httpClient;
_paths = appPaths;
}
public async Task<MetadataResult<Series>> GetMetadata(SeriesInfo info, CancellationToken cancellationToken)
{
var result = new MetadataResult<Series>();
var aid = info.ProviderIds.GetOrDefault(provider_name);
if (string.IsNullOrEmpty(aid))
{
_log.Info("Start Proxer... Searching(" + info.Name + ")");
aid = await api.FindSeries(info.Name, cancellationToken);
}
if (!string.IsNullOrEmpty(aid))
{
string WebContent = await api.WebRequestAPI(api.Proxer_anime_link + aid);
result.Item = new Series();
result.HasMetadata = true;
result.Item.ProviderIds.Add(provider_name, aid);
result.Item.Overview = await api.Get_Overview(WebContent);
result.ResultLanguage = "ger";
try
{
result.Item.CommunityRating = float.Parse(await api.Get_Rating(WebContent), System.Globalization.CultureInfo.InvariantCulture);
}
catch (Exception) { }
foreach (var genre in await api.Get_Genre(WebContent))
result.Item.AddGenre(genre);
GenreHelper.CleanupGenres(result.Item);
StoreImageUrl(aid, await api.Get_ImageUrl(WebContent), "image");
}
return result;
}
public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(SeriesInfo searchInfo, CancellationToken cancellationToken)
{
var results = new Dictionary<string, RemoteSearchResult>();
var aid = searchInfo.ProviderIds.GetOrDefault(provider_name);
if (!string.IsNullOrEmpty(aid))
{
if (!results.ContainsKey(aid))
results.Add(aid, await api.GetAnime(aid));
}
if (!string.IsNullOrEmpty(searchInfo.Name))
{
List<string> ids = await api.Search_GetSeries_list(searchInfo.Name, cancellationToken);
foreach (string a in ids)
{
results.Add(a, await api.GetAnime(a));
}
}
return results.Values;
}
private void StoreImageUrl(string series, string url, string type)
{
var path = Path.Combine(_paths.CachePath, "proxer", type, series + ".txt");
var directory = Path.GetDirectoryName(path);
Directory.CreateDirectory(directory);
File.WriteAllText(path, url);
}
public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
{
return _httpClient.GetResponse(new HttpRequestOptions
{
CancellationToken = cancellationToken,
Url = url,
ResourcePool = ResourcePool
});
}
}
public class ProxerSeriesImageProvider : IRemoteImageProvider
{
private readonly IHttpClient _httpClient;
private readonly IApplicationPaths _appPaths;
public ProxerSeriesImageProvider(IHttpClient httpClient, IApplicationPaths appPaths)
{
_httpClient = httpClient;
_appPaths = appPaths;
}
public string Name => "Proxer";
public bool Supports(IHasMetadata item) => item is Series || item is Season;
public IEnumerable<ImageType> GetSupportedImages(IHasMetadata item)
{
return new[] { ImageType.Primary };
}
public Task<IEnumerable<RemoteImageInfo>> GetImages(IHasMetadata item, CancellationToken cancellationToken)
{
var seriesId = item.GetProviderId(ProxerSeriesProvider.provider_name);
return GetImages(seriesId, cancellationToken);
}
public async Task<IEnumerable<RemoteImageInfo>> GetImages(string aid, CancellationToken cancellationToken)
{
var list = new List<RemoteImageInfo>();
if (!string.IsNullOrEmpty(aid))
{
var primary = api.Get_ImageUrl(await api.WebRequestAPI(api.Proxer_anime_link + aid));
list.Add(new RemoteImageInfo
{
ProviderName = Name,
Type = ImageType.Primary,
Url = await primary
});
}
return list;
}
public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
{
return _httpClient.GetResponse(new HttpRequestOptions
{
CancellationToken = cancellationToken,
Url = url,
ResourcePool = ProxerSeriesProvider.ResourcePool
});
}
}
}

View File

@ -0,0 +1,311 @@
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Providers;
using MediaBrowser.Plugins.Anime.Configuration;
using System;
using System.Collections.Generic;
using System.Net;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Plugins.Anime.Providers.Proxer
{
/// <summary>
/// API for http://proxer.me/ german anime database.
/// 🛈 Proxer does not have an API interface to work with
/// </summary>
internal class api
{
public static List<string> anime_search_names = new List<string>();
public static List<string> anime_search_ids = new List<string>();
public static string SearchLink = "http://proxer.me/search?s=search&name={0}&typ=all-anime&tags=&notags=#top";
public static string Proxer_anime_link = "http://proxer.me/info/";
/// <summary>
/// API call to get a anime with the id
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public static async Task<RemoteSearchResult> GetAnime(string id)
{
string WebContent = await WebRequestAPI(Proxer_anime_link + id);
var result = new RemoteSearchResult
{
Name = await SelectName(WebContent, Plugin.Instance.Configuration.TitlePreference, "en")
};
result.SearchProviderName = await one_line_regex(new Regex(@">([\S\s]*?)<"), await one_line_regex(new Regex(@"<td><b>Original Titel<\/b><\/td>([\S\s]*?)\/td>"), WebContent));
result.ImageUrl = await Get_ImageUrl(WebContent);
result.SetProviderId(ProxerSeriesProvider.provider_name, id);
result.Overview = await Get_Overview(WebContent);
return result;
}
/// <summary>
/// Get the right name lang
/// </summary>
/// <param name="WebContent"></param>
/// <param name="preference"></param>
/// <param name="language"></param>
/// <returns></returns>
private static async Task<string> SelectName(string WebContent, TitlePreferenceType preference, string language)
{
if (preference == TitlePreferenceType.Localized && language == "en")
return await Get_title("en", WebContent);
if (preference == TitlePreferenceType.Localized && language == "de")
return await Get_title("de", WebContent);
if (preference == TitlePreferenceType.Localized && language == "ger")
return await Get_title("de", WebContent);
if (preference == TitlePreferenceType.Japanese)
return await Get_title("jap", WebContent);
return await Get_title("jap_r", WebContent);
}
/// <summary>
/// API call to get the name in the called lang
/// </summary>
/// <param name="lang"></param>
/// <param name="WebContent"></param>
/// <returns></returns>
public static async Task<string> Get_title(string lang, string WebContent)
{
switch (lang)
{
case "en":
return await one_line_regex(new Regex(@">([\S\s]*?)<"), await one_line_regex(new Regex(@"<td><b>Englischer Titel<\/b><\/td>([\S\s]*?)\/td>"), WebContent));
case "de":
return await one_line_regex(new Regex(@">([\S\s]*?)<"), await one_line_regex(new Regex(@"<td><b>Deutscher Titel<\/b><\/td>([\S\s]*?)\/td>"), WebContent));
case "jap":
return await one_line_regex(new Regex(@">([\S\s]*?)<"), await one_line_regex(new Regex(@"<td><b>Japanischer Titel<\/b><\/td>([\S\s]*?)\/td>"), WebContent));
//Default is jap_r
default:
return await one_line_regex(new Regex(@">([\S\s]*?)<"), await one_line_regex(new Regex(@"<td><b>Original Titel<\/b><\/td>([\S\s]*?)\/td>"), WebContent));
}
}
/// <summary>
/// API call to get the genres of the anime
/// </summary>
/// <param name="WebContent"></param>
/// <returns></returns>
public static async Task<List<string>> Get_Genre(string WebContent)
{
List<string> result = new List<string>();
string Genres = await one_line_regex(new Regex(@"<b>Genre<\/b>((?:.*?\r?\n?)*)<\/tr>"), WebContent);
int x = 1;
string Proxer_Genre = null;
while (Proxer_Genre != "")
{
Proxer_Genre = await one_line_regex(new Regex("\">" + @"((?:.*?\r?\n?)*)<"), Genres, 1, x);
if (Proxer_Genre != "")
{
result.Add(Proxer_Genre);
}
x++;
}
return result;
}
/// <summary>
/// API call to get the ratings of the anime
/// </summary>
/// <param name="WebContent"></param>
/// <returns></returns>
public static async Task<string> Get_Rating(string WebContent)
{
return await one_line_regex(new Regex("<span class=\"average\">" + @"(.*?)<"), WebContent);
}
/// <summary>
/// API call to get the ImageUrl if the anime
/// </summary>
/// <param name="WebContent"></param>
/// <returns></returns>
public static async Task<string> Get_ImageUrl(string WebContent)
{
return "http://" + await one_line_regex(new Regex("<img src=\"" + @"\/\/((?:.*?\r?\n?)*)" + "\""), WebContent);
}
/// <summary>
/// API call to get the description of the anime
/// </summary>
/// <param name="WebContent"></param>
/// <returns></returns>
public static async Task<string> Get_Overview(string WebContent)
{
return await one_line_regex(new Regex(@"Beschreibung:<\/b><br>((?:.*?\r?\n?)*)<\/td>"), WebContent);
}
/// <summary>
/// Search a title and return the right one back
/// </summary>
/// <param name="title"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public static async Task<string> Search_GetSeries(string title, CancellationToken cancellationToken,bool bettersearchresults=false)
{
anime_search_names.Clear();
anime_search_ids.Clear();
string result = null;
string result_text = null;
string WebContent = "";
if (bettersearchresults)
{
WebContent = await WebRequestAPI(string.Format(SearchLink, Uri.EscapeUriString(Equals_check.Half_string(title, 4,60))));
}
else
{
WebContent = await WebRequestAPI(string.Format(SearchLink, Uri.EscapeUriString(title)));
}
int x = 0;
while (result_text != "")
{
result_text = await one_line_regex(new Regex("<tr align=\"" + @"left(.*?)tr>"), WebContent, 1, x);
if (result_text != "")
{
//get id
string id = await one_line_regex(new Regex("class=\"entry" + @"(.*?)" + "\">"), result_text);
string a_name = await one_line_regex(new Regex("#top\">" + @"(.*?)</a>"), result_text);
if (Equals_check.Compare_strings(a_name, title))
{
result = id;
return result;
}
int n;
if (Int32.TryParse(id, out n))
{
anime_search_names.Add(a_name);
anime_search_ids.Add(id);
}
}
x++;
}
return result;
}
/// <summary>
/// Search a title and return a list back
/// </summary>
/// <param name="title"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public static async Task<List<string>> Search_GetSeries_list(string title, CancellationToken cancellationToken)
{
List<string> result = new List<string>();
string result_text = null;
string WebContent = await WebRequestAPI(string.Format(SearchLink, Uri.EscapeUriString(title)));
int x = 0;
while (result_text != "")
{
result_text = await one_line_regex(new Regex("<tr align=\"" + @"left(.*?)tr>"), WebContent, 1, x);
if (result_text != "")
{
//get id
string id = await one_line_regex(new Regex("class=\"entry" + @"(.*?)" + "\">"), result_text);
string a_name = await one_line_regex(new Regex("#top\">" + @"(.*?)</a>"), result_text);
if (Equals_check.Compare_strings(a_name, title))
{
result.Add(id);
return result;
}
int n;
if (Int32.TryParse(id, out n))
{
result.Add(id);
}
}
x++;
}
return result;
}
/// <summary>
/// API call too find a series
/// </summary>
/// <param name="title"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public static async Task<string> FindSeries(string title, CancellationToken cancellationToken)
{
string aid = await Search_GetSeries(title, cancellationToken);
if (!string.IsNullOrEmpty(aid))
{
return aid;
}
else
{
int x = 0;
foreach (string a_name in anime_search_names)
{
if (Equals_check.Compare_strings(a_name, title))
{
return anime_search_ids[x];
}
x++;
}
}
aid = await Search_GetSeries(Equals_check.clear_name(title), cancellationToken,true);
if (!string.IsNullOrEmpty(aid))
{
return aid;
}
aid = await Search_GetSeries(Equals_check.clear_name_step2(title), cancellationToken,true);
if (!string.IsNullOrEmpty(aid))
{
return aid;
}
return null;
}
/// <summary>
/// Simple async regex call
/// </summary>
/// <param name="regex"></param>
/// <param name="match"></param>
/// <param name="group"></param>
/// <param name="match_int"></param>
/// <returns></returns>
public static async Task<string> one_line_regex(Regex regex, string match, int group = 1, int match_int = 0)
{
Regex _regex = regex;
int x = 0;
MatchCollection matches = await Task.Run(() => regex.Matches(match));
foreach (Match _match in matches)
{
if (x == match_int)
{
return await Task.Run(() => _match.Groups[group].Value.ToString());
}
x++;
}
return "";
}
/// <summary>
/// Need too get the Webcontent for some API calls.
/// </summary>
/// <param name="link"></param>
/// <returns></returns>
public static async Task<string> WebRequestAPI(string link)
{
string _strContent = "";
using (WebClient client = new WebClient())
{
client.Headers.Add(HttpRequestHeader.Cookie, "Adult=1");
Task<string> async_content = client.DownloadStringTaskAsync(link);
_strContent = await async_content;
}
return _strContent;
}
}
}

View File

@ -140,4 +140,4 @@
// }
// }
//
//}
//}

View File

@ -0,0 +1,465 @@
using MediaBrowser.Model.Logging;
using MediaBrowser.Plugins.Anime.Providers.AniDB.Identity;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Xml.Linq;
namespace MediaBrowser.Plugins.Anime.Providers
{
internal class Equals_check
{
public readonly ILogger _logger;
public Equals_check(ILogger logger)
{
_logger = logger;
}
/// <summary>
/// Clear name
/// </summary>
/// <param name="a"></param>
/// <returns></returns>
public static string clear_name(string a)
{
try
{
a = a.Trim().Replace(one_line_regex(new Regex(@"(?s) \(.*?\)"), a.Trim(), 0), "");
}
catch (Exception)
{ }
a = a.Replace(".", " ");
a = a.Replace("-", " ");
a = a.Replace("`", "");
a = a.Replace("'", "");
a = a.Replace("&", "and");
try
{
a = a.Replace(one_line_regex(new Regex(@"(?s)(S[0-9]+)"), a.Trim()), one_line_regex(new Regex(@"(?s)S([0-9]+)"), a.Trim()));
}
catch (Exception)
{
}
return a;
}
/// <summary>
/// Clear name heavy.
/// Example: Text & Text to Text and Text
/// </summary>
/// <param name="a"></param>
/// <returns></returns>
public static string clear_name_step2(string a)
{
try
{
a = a.Trim().Replace(one_line_regex(new Regex(@"(?s) \(.*?\)"), a.Trim(), 0), "");
}
catch (Exception)
{ }
a = a.Replace(".", " ");
a = a.Replace("-", " ");
a = a.Replace("`", "");
a = a.Replace("'", "");
a = a.Replace("&", "and");
a = a.Replace(":", "");
a = a.Replace("␣", "");
a = a.Replace("2wei", "zwei");
return a;
}
/// <summary>
/// If a and b match it return true
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <returns></returns>
public static bool Compare_strings(string a, string b)
{
if (!string.IsNullOrEmpty(a) && !string.IsNullOrEmpty(b))
{
if (simple_compare(a, b))
return true;
if (Fast_xml_search(a, b))
return true;
return false;
}
return false;
}
/// <summary>
/// Cut p(%) away from the string
/// </summary>
/// <param name="string_"></param>
/// <param name="min_lenght"></param>
/// <param name="p"></param>
/// <returns></returns>
public static string Half_string(string string_, int min_lenght = 0, int p = 50)
{
decimal length = 0;
if ((int)((decimal)string_.Length - (((decimal)string_.Length / 100m) * (decimal)p)) > min_lenght)
{
length = (decimal)string_.Length - (((decimal)string_.Length / 100m) * (decimal)p);
}
else
{
if (string_.Length < min_lenght)
{
length = string_.Length;
}
else
{
length = min_lenght;
}
}
return string_.Substring(0, (int)length);
}
/// <summary>
/// simple regex
/// </summary>
/// <param name="regex"></param>
/// <param name="match"></param>
/// <param name="group"></param>
/// <param name="match_int"></param>
/// <returns></returns>
public static string one_line_regex(Regex regex, string match, int group = 1, int match_int = 0)
{
Regex _regex = regex;
int x = 0;
foreach (Match _match in regex.Matches(match))
{
if (x == match_int)
{
return _match.Groups[group].Value.ToString();
}
x++;
}
return "";
}
/// <summary>
///Return true if a and b match return false if not
///It loads the titles.xml on exceptions
/// </summary>
private static bool Fast_xml_search(string a, string b, bool return_AniDBid = false, bool retry = false)
{
//Get AID aid=\"([s\S].*)\">
try
{
List<string> pre_aid = new List<string>();
string xml = File.ReadAllText(get_anidb_xml_file());
int x = 0;
string s1 = "-";
string s2 = "-";
while (!string.IsNullOrEmpty(s1) && !string.IsNullOrEmpty(s2))
{
s1 = one_line_regex(new Regex("<anime aid=" + "\"" + @"(\d+)" + "\"" + @">(?>[^<>]+|<(?!\/anime>)[^<>]*>)*?" + Regex.Escape(Half_string(a, 4))), xml, 1, x);
if (s1 != "")
{
pre_aid.Add(s1);
}
s2 = one_line_regex(new Regex("<anime aid=" + "\"" + @"(\d+)" + "\"" + @">(?>[^<>]+|<(?!\/anime>)[^<>]*>)*?" + Regex.Escape(Half_string(b, 4))), xml, 1, x);
if (s1 != "")
{
if (s1 != s2)
{
pre_aid.Add(s2);
}
}
x++;
}
foreach (string _aid in pre_aid)
{
XElement doc = XElement.Parse("<?xml version=\"1.0\" encoding=\"UTF - 8\"?>" + "<animetitles>" + one_line_regex(new Regex("<anime aid=\"" + _aid + "\">" + @"(?s)(.*?)<\/anime>"), xml, 0) + "</animetitles>");
var a_ = from page in doc.Elements("anime")
where _aid == page.Attribute("aid").Value
select page;
if (simple_compare(a_.Elements("title"), b) && simple_compare(a_.Elements("title"), a))
{
return true;
}
}
return false;
}
catch (Exception)
{
if (retry)
{
return false;
}
else
{
Task.Run(() => AniDbTitleDownloader.Load_static(new System.Threading.CancellationToken()));
return Fast_xml_search(a, b, false, true);
}
}
}
/// <summary>
/// Return the AniDB ID if a and b match
/// </summary>
public static string Fast_xml_search(string a, string b, bool return_AniDBid, int x_ = 0)
{
//Get AID aid=\"([s\S].*)\">
try
{
List<string> pre_aid = new List<string>();
string xml = File.ReadAllText(get_anidb_xml_file());
int x = 0;
string s1 = "-";
string s2 = "-";
while (!string.IsNullOrEmpty(s1) && !string.IsNullOrEmpty(s2))
{
s1 = one_line_regex(new Regex("<anime aid=" + "\"" + @"(\d+)" + "\"" + @">(?>[^<>]+|<(?!\/anime>)[^<>]*>)*?" + Regex.Escape(Half_string(a, 4))), xml, 1, x);
if (s1 != "")
{
pre_aid.Add(s1);
}
s2 = one_line_regex(new Regex("<anime aid=" + "\"" + @"(\d+)" + "\"" + @">(?>[^<>]+|<(?!\/anime>)[^<>]*>)*?" + Regex.Escape(Half_string(b, 4))), xml, 1, x);
if (s1 != "")
{
if (s1 != s2)
{
pre_aid.Add(s2);
}
}
x++;
}
foreach (string _aid in pre_aid)
{
XElement doc = XElement.Parse("<?xml version=\"1.0\" encoding=\"UTF - 8\"?>" + "<animetitles>" + one_line_regex(new Regex("<anime aid=\"" + _aid + "\">" + @"(?s)(.*?)<\/anime>"), xml, 0) + "</animetitles>");
var a_ = from page in doc.Elements("anime")
where _aid == page.Attribute("aid").Value
select page;
if (simple_compare(a_.Elements("title"), b) && simple_compare(a_.Elements("title"), a))
{
return _aid;
}
}
return "";
}
catch (Exception)
{
if (x_ == 1)
{
return "";
}
else
{
Task.Run(() => AniDbTitleDownloader.Load_static(new System.Threading.CancellationToken()));
return Fast_xml_search(a, b, true, 1);
}
}
}
/// <summary>
/// get file Path from anidb xml file
/// </summary>
/// <returns></returns>
private static string get_anidb_xml_file()
{
return AniDbTitleDownloader.TitlesFilePath_;
}
/// <summary>
/// Compare 2 Strings, and it just works
/// SeriesA S2 == SeriesA Second Season | True;
/// </summary>
private static bool simple_compare(string a, string b, bool fastmode = false)
{
if (fastmode)
{
if (a[0] == b[0])
{
}
else
{
return false;
}
}
if (Core_compare(a, b))
return true;
if (Core_compare(b, a))
return true;
return false;
}
/// <summary>
/// Compare 2 Strings, and it just works
/// </summary>
private static bool Core_compare(string a, string b)
{
if (a == b)
return true;
a = a.ToLower().Replace(" ", "").Trim().Replace(".", "");
b = b.ToLower().Replace(" ", "").Trim().Replace(".", "");
if (clear_name(a) == clear_name(b))
return true;
if (clear_name_step2(a) == clear_name_step2(b))
return true;
if (a.Replace("-", " ") == b.Replace("-", " "))
return true;
if (a.Replace(" 2", ":secondseason") == b.Replace(" 2", ":secondseason"))
return true;
if (a.Replace("2", "secondseason") == b.Replace("2", "secondseason"))
return true;
if (convert_symbols_too_numbers(a, "I") == convert_symbols_too_numbers(b, "I"))
return true;
if (convert_symbols_too_numbers(a, "!") == convert_symbols_too_numbers(b, "!"))
return true;
if (a.Replace("ndseason", "") == b.Replace("ndseason", ""))
return true;
if (a.Replace("ndseason", "") == b)
return true;
if (one_line_regex(new Regex(@"((.*)s([0 - 9]))"), a, 2) + one_line_regex(new Regex(@"((.*)s([0 - 9]))"), a, 3) == one_line_regex(new Regex(@"((.*)s([0 - 9]))"), b, 2) + one_line_regex(new Regex(@"((.*)s([0 - 9]))"), b, 3))
if (!string.IsNullOrEmpty(one_line_regex(new Regex(@"((.*)s([0 - 9]))"), a, 2) + one_line_regex(new Regex(@"((.*)s([0 - 9]))"), a, 3)))
return true;
if (one_line_regex(new Regex(@"((.*)s([0 - 9]))"), a, 2) + one_line_regex(new Regex(@"((.*)s([0 - 9]))"), a, 3) == b)
if (!string.IsNullOrEmpty(one_line_regex(new Regex(@"((.*)s([0 - 9]))"), a, 2) + one_line_regex(new Regex(@"((.*)s([0 - 9]))"), a, 3)))
return true;
if (a.Replace("rdseason", "") == b.Replace("rdseason", ""))
return true;
if (a.Replace("rdseason", "") == b)
return true;
try
{
if (a.Replace("2", "secondseason").Replace(one_line_regex(new Regex(@"(?s)\(.*?\)"), a, 0), "") == b.Replace("2", "secondseason").Replace(one_line_regex(new Regex(@"(?s)\(.*?\)"), b, 0), ""))
return true;
}
catch (Exception)
{
}
try
{
if (a.Replace("2", "secondseason").Replace(one_line_regex(new Regex(@"(?s)\(.*?\)"), a, 0), "") == b)
return true;
}
catch (Exception)
{
}
try
{
if (a.Replace(" 2", ":secondseason").Replace(one_line_regex(new Regex(@"(?s)\(.*?\)"), a, 0), "") == b.Replace(" 2", ":secondseason").Replace(one_line_regex(new Regex(@"(?s)\(.*?\)"), b, 0), ""))
return true;
}
catch (Exception)
{
}
try
{
if (a.Replace(" 2", ":secondseason").Replace(one_line_regex(new Regex(@"(?s)\(.*?\)"), a, 0), "") == b)
return true;
}
catch (Exception)
{
}
try
{
if (a.Replace(one_line_regex(new Regex(@"(?s)\(.*?\)"), a, 0), "") == b.Replace(one_line_regex(new Regex(@"(?s)\(.*?\)"), b, 0), ""))
return true;
}
catch (Exception)
{
}
try
{
if (a.Replace(one_line_regex(new Regex(@"(?s)\(.*?\)"), a, 0), "") == b)
return true;
}
catch (Exception)
{
}
try
{
if (b.Replace(one_line_regex(new Regex(@"(?s)\(.*?\)"), b, 0), "").Replace(" 2", ": second Season") == a)
return true;
}
catch (Exception)
{
}
try
{
if (a.Replace(" 2ndseason", ":secondseason") + " vs " + b == a)
return true;
}
catch (Exception)
{
}
try
{
if (a.Replace(one_line_regex(new Regex(@"(?s)\(.*?\)"), a, 0), "").Replace(" 2", ":secondseason") == b)
return true;
}
catch (Exception)
{
}
return false;
}
/// <summary>
/// Example: Convert II to 2
/// </summary>
/// <param name="input"></param>
/// <param name="symbol"></param>
/// <returns></returns>
private static string convert_symbols_too_numbers(string input, string symbol)
{
try
{
string regex_c = "_";
int x = 0;
int highest_number = 0;
while (!string.IsNullOrEmpty(regex_c))
{
regex_c = one_line_regex(new Regex(@"(" + symbol + @"+)"), input.ToLower().Trim(), 1, x).Trim();
if (highest_number < regex_c.Count())
highest_number = regex_c.Count();
x++;
}
x = 0;
string output = "";
while (x != highest_number)
{
output = output + symbol;
x++;
}
output = input.Replace(output, highest_number.ToString());
if (string.IsNullOrEmpty(output))
{
output = input;
}
return output;
}
catch (Exception)
{
return input;
}
}
/// <summary>
/// Simple Compare a XElemtent with a string
/// </summary>
/// <param name="a_"></param>
/// <param name="b"></param>
/// <returns></returns>
private static bool simple_compare(IEnumerable<XElement> a_, string b)
{
foreach (XElement a in a_)
{
if (simple_compare(a.Value, b, true))
return true;
}
return false;
}
}
}

View File

@ -38,7 +38,7 @@ namespace MediaBrowser.Plugins.Anime
_targetInterval = targetInterval;
_timeWindowDuration = timeWindow;
_maxAllowedInWindow = (int) (timeWindow.Ticks/targetInterval.Ticks);
_maxAllowedInWindow = (int)(timeWindow.Ticks / targetInterval.Ticks);
_lastTake = DateTime.Now - minimumInterval;
}
@ -74,10 +74,10 @@ namespace MediaBrowser.Plugins.Anime
DateTime now = DateTime.Now;
TimeSpan minWait = (_lastTake + _minimumInterval) - now;
float load = (float) _window.Count/_maxAllowedInWindow;
float load = (float)_window.Count / _maxAllowedInWindow;
float waitTicks = minWait.Ticks + (_targetInterval.Ticks - minWait.Ticks)*load;
return new TimeSpan((long) waitTicks);
float waitTicks = minWait.Ticks + (_targetInterval.Ticks - minWait.Ticks) * load;
return new TimeSpan((long)waitTicks);
}
private void FlushExpiredRecords()

View File

@ -11,7 +11,7 @@ namespace AnimeLists
/// <remarks />
[XmlArray("mapping-list")]
[XmlArrayItem("mapping", typeof (AnimelistMapping), IsNullable = false)]
[XmlArrayItem("mapping", typeof(AnimelistMapping), IsNullable = false)]
public AnimelistMapping[] Mappinglist { get; set; }
/// <remarks />
@ -26,6 +26,18 @@ namespace AnimeLists
[XmlAttribute("anidbid")]
public string AnidbId { get; set; }
/// <remarks />
[XmlAttribute("anisearch")]
public string AniSearchId { get; set; }
/// <remarks />
[XmlAttribute("proxer")]
public string ProxerId { get; set; }
/// <remarks />
[XmlAttribute("myanimelist")]
public string MyAnimeListId { get; set; }
/// <remarks />
[XmlAttribute("tvdbid")]
public string TvdbId { get; set; }

View File

@ -7,11 +7,11 @@ namespace AnimeLists
public class AnimelistSupplementalinfo
{
/// <remarks />
[XmlElement("credits", typeof (string))]
[XmlElement("director", typeof (string))]
[XmlElement("fanart", typeof (AnimelistSupplementalinfoFanart))]
[XmlElement("genre", typeof (string))]
[XmlElement("studio", typeof (string))]
[XmlElement("credits", typeof(string))]
[XmlElement("director", typeof(string))]
[XmlElement("fanart", typeof(AnimelistSupplementalinfoFanart))]
[XmlElement("genre", typeof(string))]
[XmlElement("studio", typeof(string))]
[XmlChoiceIdentifier("ItemsElementName")]
public object[] Items { get; set; }

View File

@ -1,7 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
@ -87,4 +85,4 @@ namespace AnimeLists
}
}
}
}
}

View File

@ -31,7 +31,7 @@ namespace AnimeLists
}
}
XmlSerializer serializer = new XmlSerializer(typeof (Animelist));
XmlSerializer serializer = new XmlSerializer(typeof(Animelist));
using (var stream = File.OpenRead(_temp))
return serializer.Deserialize(stream) as Animelist;

View File

@ -39,7 +39,7 @@ namespace AnimeLists
List<AnimelistAnime> animeList;
if (!_tvdbMappings.TryGetValue(tvdb.Series, out animeList))
return null;
// look for exact mapping in mapping list
foreach (var anime in animeList.Where(x => x.Mappinglist != null))
{
@ -61,9 +61,9 @@ namespace AnimeLists
}
var seasonMatch = animeList
.Select(x => new {Season = Parse(x.DefaultTvdbSeason), Match = x})
.Select(x => new { Season = Parse(x.DefaultTvdbSeason), Match = x })
.Where(x => x.Season == tvdb.Season)
.Select(x => new {Offset = x.Match.EpisodeOffsetSpecified ? x.Match.EpisodeOffset : 0, x.Match})
.Select(x => new { Offset = x.Match.EpisodeOffsetSpecified ? x.Match.EpisodeOffset : 0, x.Match })
.Where(x => x.Offset <= tvdb.Index)
.OrderByDescending(x => x.Offset)
.FirstOrDefault();
@ -101,7 +101,7 @@ namespace AnimeLists
return null;
}
public TvdbEpisode ToTvdb(AnidbEpisode anidb)
{
AnimelistAnime anime;