mirror of
https://github.com/jellyfin/jellyfin.git
synced 2026-05-13 04:06:31 +01:00
Merge pull request #16472 from IDisposable/feature/season-provider-id-from-path
Parse provider IDs from season and episode folder/file names
This commit is contained in:
@@ -1,8 +1,10 @@
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Emby.Naming.Common;
|
||||
using Emby.Server.Implementations.Library;
|
||||
using Jellyfin.Data.Enums;
|
||||
using MediaBrowser.Controller.Entities.TV;
|
||||
using MediaBrowser.Controller.Library;
|
||||
@@ -81,10 +83,34 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
|
||||
episode.ParentIndexNumber = 1;
|
||||
}
|
||||
|
||||
SetProviderIdFromPath(episode, args.Path);
|
||||
|
||||
return episode;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets provider ids from the episode file name.
|
||||
/// </summary>
|
||||
/// <param name="item">The episode.</param>
|
||||
/// <param name="path">The episode file path.</param>
|
||||
private static void SetProviderIdFromPath(Episode item, string path)
|
||||
{
|
||||
var justName = Path.GetFileNameWithoutExtension(path.AsSpan());
|
||||
|
||||
var imdbId = justName.GetAttributeValue("imdbid");
|
||||
item.TrySetProviderId(MetadataProvider.Imdb, imdbId);
|
||||
|
||||
var tvdbId = justName.GetAttributeValue("tvdbid");
|
||||
item.TrySetProviderId(MetadataProvider.Tvdb, tvdbId);
|
||||
|
||||
var tvmazeId = justName.GetAttributeValue("tvmazeid");
|
||||
item.TrySetProviderId(MetadataProvider.TvMaze, tvmazeId);
|
||||
|
||||
var tmdbId = justName.GetAttributeValue("tmdbid");
|
||||
item.TrySetProviderId(MetadataProvider.Tmdb, tmdbId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Emby.Naming.Common;
|
||||
using Emby.Naming.TV;
|
||||
using Emby.Server.Implementations.Library;
|
||||
using MediaBrowser.Controller.Entities.TV;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.Globalization;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
@@ -101,10 +104,31 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
|
||||
args.LibraryOptions.PreferredMetadataLanguage);
|
||||
}
|
||||
|
||||
SetProviderIdFromPath(season, path);
|
||||
|
||||
return season;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets provider ids from the season folder name.
|
||||
/// </summary>
|
||||
/// <param name="item">The season.</param>
|
||||
/// <param name="path">The season folder path.</param>
|
||||
private static void SetProviderIdFromPath(Season item, string path)
|
||||
{
|
||||
var justName = Path.GetFileName(path.AsSpan());
|
||||
|
||||
var tvdbId = justName.GetAttributeValue("tvdbid");
|
||||
item.TrySetProviderId(MetadataProvider.Tvdb, tvdbId);
|
||||
|
||||
var tvmazeId = justName.GetAttributeValue("tvmazeid");
|
||||
item.TrySetProviderId(MetadataProvider.TvMaze, tvmazeId);
|
||||
|
||||
var tmdbId = justName.GetAttributeValue("tmdbid");
|
||||
item.TrySetProviderId(MetadataProvider.Tmdb, tmdbId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.TV;
|
||||
using MediaBrowser.Controller.Providers;
|
||||
using MediaBrowser.Model.Entities;
|
||||
|
||||
@@ -17,6 +18,18 @@ public class ImdbExternalUrlProvider : IExternalUrlProvider
|
||||
public IEnumerable<string> GetExternalUrls(BaseItem item)
|
||||
{
|
||||
var baseUrl = "https://www.imdb.com/";
|
||||
|
||||
if (item is Season season)
|
||||
{
|
||||
if (season.Series?.TryGetProviderId(MetadataProvider.Imdb, out var seriesImdbId) == true
|
||||
&& season.IndexNumber.HasValue)
|
||||
{
|
||||
yield return baseUrl + $"title/{seriesImdbId}/episodes/?season={season.IndexNumber.Value}";
|
||||
}
|
||||
|
||||
yield break;
|
||||
}
|
||||
|
||||
if (item.TryGetProviderId(MetadataProvider.Imdb, out var externalId))
|
||||
{
|
||||
if (item is Person)
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
using MediaBrowser.Controller.Entities.TV;
|
||||
using MediaBrowser.Controller.Providers;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.Providers;
|
||||
|
||||
namespace MediaBrowser.Providers.Plugins.Tmdb.TV
|
||||
{
|
||||
/// <summary>
|
||||
/// External id for a TMDb episode.
|
||||
/// </summary>
|
||||
public class TmdbEpisodeExternalId : IExternalId
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public string ProviderName => TmdbUtils.ProviderName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Key => MetadataProvider.Tmdb.ToString();
|
||||
|
||||
/// <inheritdoc />
|
||||
public ExternalIdMediaType? Type => ExternalIdMediaType.Episode;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Supports(IHasProviderIds item) => item is Episode;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using MediaBrowser.Controller.Entities.TV;
|
||||
using MediaBrowser.Controller.Providers;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.Providers;
|
||||
|
||||
namespace MediaBrowser.Providers.Plugins.Tmdb.TV
|
||||
{
|
||||
/// <summary>
|
||||
/// External id for a TMDb season.
|
||||
/// </summary>
|
||||
public class TmdbSeasonExternalId : IExternalId
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public string ProviderName => TmdbUtils.ProviderName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Key => MetadataProvider.Tmdb.ToString();
|
||||
|
||||
/// <inheritdoc />
|
||||
public ExternalIdMediaType? Type => ExternalIdMediaType.Season;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Supports(IHasProviderIds item) => item is Season;
|
||||
}
|
||||
}
|
||||
@@ -20,9 +20,6 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV
|
||||
public ExternalIdMediaType? Type => ExternalIdMediaType.Series;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Supports(IHasProviderIds item)
|
||||
{
|
||||
return item is Series;
|
||||
}
|
||||
public bool Supports(IHasProviderIds item) => item is Series;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.Audio;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Providers.Plugins.AudioDb;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Providers.Tests.ExternalId
|
||||
{
|
||||
public sealed class AudioDbExternalUrlProviderTests
|
||||
{
|
||||
private readonly AudioDbAlbumExternalUrlProvider _albumProvider = new();
|
||||
private readonly AudioDbArtistExternalUrlProvider _artistProvider = new();
|
||||
|
||||
[Fact]
|
||||
public void GetExternalUrls_MusicAlbumWithAudioDbAlbumId_ReturnsCorrectUrl()
|
||||
{
|
||||
var album = new MusicAlbum();
|
||||
album.SetProviderId(MetadataProvider.AudioDbAlbum, "12345");
|
||||
|
||||
var urls = _albumProvider.GetExternalUrls(album);
|
||||
|
||||
Assert.Contains("https://www.theaudiodb.com/album/12345", urls);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetExternalUrls_MusicAlbumWithNoAudioDbAlbumId_ReturnsNoUrl()
|
||||
{
|
||||
var album = new MusicAlbum();
|
||||
|
||||
var urls = _albumProvider.GetExternalUrls(album);
|
||||
|
||||
Assert.Empty(urls);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetExternalUrls_NonAlbumWithAudioDbAlbumId_ReturnsNoUrl()
|
||||
{
|
||||
var artist = new MusicArtist();
|
||||
artist.SetProviderId(MetadataProvider.AudioDbAlbum, "12345");
|
||||
|
||||
var urls = _albumProvider.GetExternalUrls(artist);
|
||||
|
||||
Assert.Empty(urls);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetExternalUrls_MusicArtistWithAudioDbArtistId_ReturnsCorrectUrl()
|
||||
{
|
||||
var artist = new MusicArtist();
|
||||
artist.SetProviderId(MetadataProvider.AudioDbArtist, "67890");
|
||||
|
||||
var urls = _artistProvider.GetExternalUrls(artist);
|
||||
|
||||
Assert.Contains("https://www.theaudiodb.com/artist/67890", urls);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetExternalUrls_PersonWithAudioDbArtistId_ReturnsCorrectUrl()
|
||||
{
|
||||
var person = new Person();
|
||||
person.SetProviderId(MetadataProvider.AudioDbArtist, "67890");
|
||||
|
||||
var urls = _artistProvider.GetExternalUrls(person);
|
||||
|
||||
Assert.Contains("https://www.theaudiodb.com/artist/67890", urls);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetExternalUrls_MusicArtistWithNoAudioDbArtistId_ReturnsNoUrl()
|
||||
{
|
||||
var artist = new MusicArtist();
|
||||
|
||||
var urls = _artistProvider.GetExternalUrls(artist);
|
||||
|
||||
Assert.Empty(urls);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetExternalUrls_NonArtistWithAudioDbArtistId_ReturnsNoUrl()
|
||||
{
|
||||
var album = new MusicAlbum();
|
||||
album.SetProviderId(MetadataProvider.AudioDbArtist, "67890");
|
||||
|
||||
var urls = _artistProvider.GetExternalUrls(album);
|
||||
|
||||
Assert.Empty(urls);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.TV;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Providers.Plugins.ComicVine;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Providers.Tests.ExternalId
|
||||
{
|
||||
public sealed class ComicVineExternalUrlProviderTests
|
||||
{
|
||||
private readonly ComicVineExternalUrlProvider _provider = new();
|
||||
|
||||
[Fact]
|
||||
public void GetExternalUrls_PersonWithComicVineId_ReturnsCorrectUrl()
|
||||
{
|
||||
var person = new Person();
|
||||
person.SetProviderId("ComicVine", "person/4005-1234");
|
||||
|
||||
var urls = _provider.GetExternalUrls(person);
|
||||
|
||||
Assert.Contains("https://comicvine.gamespot.com/person/4005-1234", urls);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetExternalUrls_BookWithComicVineId_ReturnsCorrectUrl()
|
||||
{
|
||||
var book = new Book();
|
||||
book.SetProviderId("ComicVine", "issue/4000-5678");
|
||||
|
||||
var urls = _provider.GetExternalUrls(book);
|
||||
|
||||
Assert.Contains("https://comicvine.gamespot.com/issue/4000-5678", urls);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetExternalUrls_PersonWithNoComicVineId_ReturnsNoUrl()
|
||||
{
|
||||
var person = new Person();
|
||||
|
||||
var urls = _provider.GetExternalUrls(person);
|
||||
|
||||
Assert.Empty(urls);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetExternalUrls_NonSupportedItemWithComicVineId_ReturnsNoUrl()
|
||||
{
|
||||
var series = new Series();
|
||||
series.SetProviderId("ComicVine", "volume/4050-9999");
|
||||
|
||||
var urls = _provider.GetExternalUrls(series);
|
||||
|
||||
Assert.Empty(urls);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.TV;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Providers.Plugins.GoogleBooks;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Providers.Tests.ExternalId
|
||||
{
|
||||
public sealed class GoogleBooksExternalUrlProviderTests
|
||||
{
|
||||
private readonly GoogleBooksExternalUrlProvider _provider = new();
|
||||
|
||||
[Fact]
|
||||
public void GetExternalUrls_BookWithGoogleBooksId_ReturnsCorrectUrl()
|
||||
{
|
||||
var book = new Book();
|
||||
book.SetProviderId("GoogleBooks", "buc0AAAAMAAJ");
|
||||
|
||||
var urls = _provider.GetExternalUrls(book);
|
||||
|
||||
Assert.Contains("https://books.google.com/books?id=buc0AAAAMAAJ", urls);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetExternalUrls_BookWithNoGoogleBooksId_ReturnsNoUrl()
|
||||
{
|
||||
var book = new Book();
|
||||
|
||||
var urls = _provider.GetExternalUrls(book);
|
||||
|
||||
Assert.Empty(urls);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetExternalUrls_NonBookWithGoogleBooksId_ReturnsNoUrl()
|
||||
{
|
||||
var series = new Series();
|
||||
series.SetProviderId("GoogleBooks", "buc0AAAAMAAJ");
|
||||
|
||||
var urls = _provider.GetExternalUrls(series);
|
||||
|
||||
Assert.Empty(urls);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
using System;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.Movies;
|
||||
using MediaBrowser.Controller.Entities.TV;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Providers.Movies;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Providers.Tests.ExternalId
|
||||
{
|
||||
// put tests that mock the static LibraryManager in the same collection to avoid test interference
|
||||
[Collection("LibraryManagerTests")]
|
||||
public sealed class ImdbExternalUrlProviderTests : IDisposable
|
||||
{
|
||||
private readonly ImdbExternalUrlProvider _provider = new();
|
||||
private readonly Mock<ILibraryManager> _libraryManagerMock = new();
|
||||
private readonly ILibraryManager? _previousLibraryManager;
|
||||
|
||||
public ImdbExternalUrlProviderTests()
|
||||
{
|
||||
_previousLibraryManager = BaseItem.LibraryManager;
|
||||
BaseItem.LibraryManager = _libraryManagerMock.Object;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
BaseItem.LibraryManager = _previousLibraryManager;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetExternalUrls_MovieWithImdbId_ReturnsCorrectUrl()
|
||||
{
|
||||
var movie = new Movie();
|
||||
movie.SetProviderId(MetadataProvider.Imdb, "tt1234567");
|
||||
|
||||
var urls = _provider.GetExternalUrls(movie);
|
||||
|
||||
Assert.Contains("https://www.imdb.com/title/tt1234567", urls);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetExternalUrls_SeriesWithImdbId_ReturnsCorrectUrl()
|
||||
{
|
||||
var series = new Series();
|
||||
series.SetProviderId(MetadataProvider.Imdb, "tt7654321");
|
||||
|
||||
var urls = _provider.GetExternalUrls(series);
|
||||
|
||||
Assert.Contains("https://www.imdb.com/title/tt7654321", urls);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetExternalUrls_EpisodeWithImdbId_ReturnsCorrectUrl()
|
||||
{
|
||||
var episode = new Episode();
|
||||
episode.SetProviderId(MetadataProvider.Imdb, "tt9999999");
|
||||
|
||||
var urls = _provider.GetExternalUrls(episode);
|
||||
|
||||
Assert.Contains("https://www.imdb.com/title/tt9999999", urls);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetExternalUrls_SeasonWithSeriesImdbId_ReturnsSeasonEpisodesUrl()
|
||||
{
|
||||
var series = new Series { Id = Guid.NewGuid() };
|
||||
series.SetProviderId(MetadataProvider.Imdb, "tt1234567");
|
||||
|
||||
var season = new Season { IndexNumber = 2, SeriesId = series.Id };
|
||||
_libraryManagerMock.Setup(m => m.GetItemById(series.Id)).Returns(series);
|
||||
|
||||
var urls = _provider.GetExternalUrls(season);
|
||||
|
||||
Assert.Contains("https://www.imdb.com/title/tt1234567/episodes/?season=2", urls);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetExternalUrls_SeasonWithNoSeriesImdbId_ReturnsNoUrl()
|
||||
{
|
||||
var series = new Series { Id = Guid.NewGuid() };
|
||||
var season = new Season { IndexNumber = 1, SeriesId = series.Id };
|
||||
_libraryManagerMock.Setup(m => m.GetItemById(series.Id)).Returns(series);
|
||||
|
||||
var urls = _provider.GetExternalUrls(season);
|
||||
|
||||
Assert.Empty(urls);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetExternalUrls_SeasonWithNoIndexNumber_ReturnsNoUrl()
|
||||
{
|
||||
var series = new Series { Id = Guid.NewGuid() };
|
||||
series.SetProviderId(MetadataProvider.Imdb, "tt1234567");
|
||||
var season = new Season { IndexNumber = null, SeriesId = series.Id };
|
||||
_libraryManagerMock.Setup(m => m.GetItemById(series.Id)).Returns(series);
|
||||
|
||||
var urls = _provider.GetExternalUrls(season);
|
||||
|
||||
Assert.Empty(urls);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetExternalUrls_SeasonWithUnknownSeriesId_ReturnsNoUrl()
|
||||
{
|
||||
var season = new Season { IndexNumber = 1, SeriesId = Guid.NewGuid() };
|
||||
_libraryManagerMock.Setup(m => m.GetItemById(It.IsAny<Guid>())).Returns((BaseItem?)null);
|
||||
|
||||
var urls = _provider.GetExternalUrls(season);
|
||||
|
||||
Assert.Empty(urls);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetExternalUrls_ItemWithNoImdbId_ReturnsNoUrl()
|
||||
{
|
||||
var movie = new Movie();
|
||||
|
||||
var urls = _provider.GetExternalUrls(movie);
|
||||
|
||||
Assert.Empty(urls);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.TV;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Providers.Books.Isbn;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Providers.Tests.ExternalId
|
||||
{
|
||||
public sealed class IsbnExternalUrlProviderTests
|
||||
{
|
||||
private readonly IsbnExternalUrlProvider _provider = new();
|
||||
|
||||
[Fact]
|
||||
public void GetExternalUrls_BookWithIsbnId_ReturnsCorrectUrl()
|
||||
{
|
||||
var book = new Book();
|
||||
book.SetProviderId("ISBN", "9780306406157");
|
||||
|
||||
var urls = _provider.GetExternalUrls(book);
|
||||
|
||||
Assert.Contains("https://search.worldcat.org/search?q=bn:9780306406157", urls);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetExternalUrls_BookWithNoIsbnId_ReturnsNoUrl()
|
||||
{
|
||||
var book = new Book();
|
||||
|
||||
var urls = _provider.GetExternalUrls(book);
|
||||
|
||||
Assert.Empty(urls);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetExternalUrls_NonBookWithIsbnId_ReturnsNoUrl()
|
||||
{
|
||||
var series = new Series();
|
||||
series.SetProviderId("ISBN", "9780306406157");
|
||||
|
||||
var urls = _provider.GetExternalUrls(series);
|
||||
|
||||
Assert.Empty(urls);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using MediaBrowser.Common;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.Audio;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.Serialization;
|
||||
using MediaBrowser.Providers.Plugins.MusicBrainz;
|
||||
using MediaBrowser.Providers.Plugins.MusicBrainz.Configuration;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Providers.Tests.ExternalId
|
||||
{
|
||||
public sealed class MusicBrainzExternalUrlProviderTests : IDisposable
|
||||
{
|
||||
private static readonly PropertyInfo _instanceProperty =
|
||||
typeof(Plugin).GetProperty("Instance", BindingFlags.Public | BindingFlags.Static)!;
|
||||
|
||||
private static readonly MethodInfo _instanceSetter =
|
||||
_instanceProperty.GetSetMethod(nonPublic: true)!;
|
||||
|
||||
private readonly Plugin? _previousPlugin;
|
||||
|
||||
public MusicBrainzExternalUrlProviderTests()
|
||||
{
|
||||
_previousPlugin = Plugin.Instance;
|
||||
|
||||
var appPathsMock = new Mock<IApplicationPaths>();
|
||||
appPathsMock.Setup(p => p.PluginsPath).Returns(System.IO.Path.GetTempPath());
|
||||
appPathsMock.Setup(p => p.PluginConfigurationsPath).Returns(System.IO.Path.GetTempPath());
|
||||
|
||||
var xmlSerializerMock = new Mock<IXmlSerializer>();
|
||||
xmlSerializerMock
|
||||
.Setup(s => s.DeserializeFromFile(typeof(PluginConfiguration), It.IsAny<string>()))
|
||||
.Returns(new PluginConfiguration());
|
||||
|
||||
var appHostMock = new Mock<IApplicationHost>();
|
||||
appHostMock.Setup(h => h.Name).Returns("Jellyfin");
|
||||
appHostMock.Setup(h => h.ApplicationVersionString).Returns("1.0.0");
|
||||
appHostMock.Setup(h => h.ApplicationUserAgentAddress).Returns("localhost");
|
||||
|
||||
_ = new Plugin(appPathsMock.Object, xmlSerializerMock.Object, appHostMock.Object);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_instanceSetter.Invoke(null, new object?[] { _previousPlugin });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetExternalUrls_MusicAlbumWithMusicBrainzAlbumId_ReturnsCorrectUrl()
|
||||
{
|
||||
var album = new MusicAlbum();
|
||||
album.SetProviderId(MetadataProvider.MusicBrainzAlbum, "a1b2c3d4-e5f6-7890-abcd-ef1234567890");
|
||||
|
||||
var urls = new MusicBrainzAlbumExternalUrlProvider().GetExternalUrls(album);
|
||||
|
||||
Assert.Contains(PluginConfiguration.DefaultServer + "/release/a1b2c3d4-e5f6-7890-abcd-ef1234567890", urls);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetExternalUrls_MusicAlbumWithNoMusicBrainzAlbumId_ReturnsNoUrl()
|
||||
{
|
||||
var album = new MusicAlbum();
|
||||
|
||||
var urls = new MusicBrainzAlbumExternalUrlProvider().GetExternalUrls(album);
|
||||
|
||||
Assert.Empty(urls);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetExternalUrls_NonAlbumWithMusicBrainzAlbumId_ReturnsNoUrl()
|
||||
{
|
||||
var artist = new MusicArtist();
|
||||
artist.SetProviderId(MetadataProvider.MusicBrainzAlbum, "a1b2c3d4-e5f6-7890-abcd-ef1234567890");
|
||||
|
||||
var urls = new MusicBrainzAlbumExternalUrlProvider().GetExternalUrls(artist);
|
||||
|
||||
Assert.Empty(urls);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetExternalUrls_MusicAlbumWithMusicBrainzAlbumArtistId_ReturnsCorrectUrl()
|
||||
{
|
||||
var album = new MusicAlbum();
|
||||
album.SetProviderId(MetadataProvider.MusicBrainzAlbumArtist, "a1b2c3d4-e5f6-7890-abcd-ef1234567890");
|
||||
|
||||
var urls = new MusicBrainzAlbumArtistExternalUrlProvider().GetExternalUrls(album);
|
||||
|
||||
Assert.Contains(PluginConfiguration.DefaultServer + "/artist/a1b2c3d4-e5f6-7890-abcd-ef1234567890", urls);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetExternalUrls_MusicAlbumWithNoMusicBrainzAlbumArtistId_ReturnsNoUrl()
|
||||
{
|
||||
var album = new MusicAlbum();
|
||||
|
||||
var urls = new MusicBrainzAlbumArtistExternalUrlProvider().GetExternalUrls(album);
|
||||
|
||||
Assert.Empty(urls);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetExternalUrls_MusicArtistWithMusicBrainzArtistId_ReturnsCorrectUrl()
|
||||
{
|
||||
var artist = new MusicArtist();
|
||||
artist.SetProviderId(MetadataProvider.MusicBrainzArtist, "a1b2c3d4-e5f6-7890-abcd-ef1234567890");
|
||||
|
||||
var urls = new MusicBrainzArtistExternalUrlProvider().GetExternalUrls(artist);
|
||||
|
||||
Assert.Contains(PluginConfiguration.DefaultServer + "/artist/a1b2c3d4-e5f6-7890-abcd-ef1234567890", urls);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetExternalUrls_PersonWithMusicBrainzArtistId_ReturnsCorrectUrl()
|
||||
{
|
||||
var person = new Person();
|
||||
person.SetProviderId(MetadataProvider.MusicBrainzArtist, "a1b2c3d4-e5f6-7890-abcd-ef1234567890");
|
||||
|
||||
var urls = new MusicBrainzArtistExternalUrlProvider().GetExternalUrls(person);
|
||||
|
||||
Assert.Contains(PluginConfiguration.DefaultServer + "/artist/a1b2c3d4-e5f6-7890-abcd-ef1234567890", urls);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetExternalUrls_MusicArtistWithNoMusicBrainzArtistId_ReturnsNoUrl()
|
||||
{
|
||||
var artist = new MusicArtist();
|
||||
|
||||
var urls = new MusicBrainzArtistExternalUrlProvider().GetExternalUrls(artist);
|
||||
|
||||
Assert.Empty(urls);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetExternalUrls_NonArtistWithMusicBrainzArtistId_ReturnsNoUrl()
|
||||
{
|
||||
var album = new MusicAlbum();
|
||||
album.SetProviderId(MetadataProvider.MusicBrainzArtist, "a1b2c3d4-e5f6-7890-abcd-ef1234567890");
|
||||
|
||||
var urls = new MusicBrainzArtistExternalUrlProvider().GetExternalUrls(album);
|
||||
|
||||
Assert.Empty(urls);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetExternalUrls_MusicAlbumWithMusicBrainzReleaseGroupId_ReturnsCorrectUrl()
|
||||
{
|
||||
var album = new MusicAlbum();
|
||||
album.SetProviderId(MetadataProvider.MusicBrainzReleaseGroup, "a1b2c3d4-e5f6-7890-abcd-ef1234567890");
|
||||
|
||||
var urls = new MusicBrainzReleaseGroupExternalUrlProvider().GetExternalUrls(album);
|
||||
|
||||
Assert.Contains(PluginConfiguration.DefaultServer + "/release-group/a1b2c3d4-e5f6-7890-abcd-ef1234567890", urls);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetExternalUrls_MusicAlbumWithNoMusicBrainzReleaseGroupId_ReturnsNoUrl()
|
||||
{
|
||||
var album = new MusicAlbum();
|
||||
|
||||
var urls = new MusicBrainzReleaseGroupExternalUrlProvider().GetExternalUrls(album);
|
||||
|
||||
Assert.Empty(urls);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetExternalUrls_AudioWithMusicBrainzTrackId_ReturnsCorrectUrl()
|
||||
{
|
||||
var audio = new Audio();
|
||||
audio.SetProviderId(MetadataProvider.MusicBrainzTrack, "a1b2c3d4-e5f6-7890-abcd-ef1234567890");
|
||||
|
||||
var urls = new MusicBrainzTrackExternalUrlProvider().GetExternalUrls(audio);
|
||||
|
||||
Assert.Contains(PluginConfiguration.DefaultServer + "/track/a1b2c3d4-e5f6-7890-abcd-ef1234567890", urls);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetExternalUrls_AudioWithNoMusicBrainzTrackId_ReturnsNoUrl()
|
||||
{
|
||||
var audio = new Audio();
|
||||
|
||||
var urls = new MusicBrainzTrackExternalUrlProvider().GetExternalUrls(audio);
|
||||
|
||||
Assert.Empty(urls);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetExternalUrls_NonAudioWithMusicBrainzTrackId_ReturnsNoUrl()
|
||||
{
|
||||
var album = new MusicAlbum();
|
||||
album.SetProviderId(MetadataProvider.MusicBrainzTrack, "a1b2c3d4-e5f6-7890-abcd-ef1234567890");
|
||||
|
||||
var urls = new MusicBrainzTrackExternalUrlProvider().GetExternalUrls(album);
|
||||
|
||||
Assert.Empty(urls);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
using System;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.Movies;
|
||||
using MediaBrowser.Controller.Entities.TV;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Providers.Plugins.Tmdb;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Providers.Tests.ExternalId
|
||||
{
|
||||
// put tests that mock the static LibraryManager in the same collection to avoid test interference
|
||||
[Collection("LibraryManagerTests")]
|
||||
public sealed class TmdbExternalUrlProviderTests : IDisposable
|
||||
{
|
||||
private readonly TmdbExternalUrlProvider _provider = new();
|
||||
private readonly Mock<ILibraryManager> _libraryManagerMock = new();
|
||||
private readonly ILibraryManager? _previousLibraryManager;
|
||||
|
||||
public TmdbExternalUrlProviderTests()
|
||||
{
|
||||
_previousLibraryManager = BaseItem.LibraryManager;
|
||||
BaseItem.LibraryManager = _libraryManagerMock.Object;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
BaseItem.LibraryManager = _previousLibraryManager;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetExternalUrls_SeriesWithTmdbId_ReturnsCorrectUrl()
|
||||
{
|
||||
var series = new Series();
|
||||
series.SetProviderId(MetadataProvider.Tmdb, "1399");
|
||||
|
||||
var urls = _provider.GetExternalUrls(series);
|
||||
|
||||
Assert.Contains(TmdbUtils.BaseTmdbUrl + "tv/1399", urls);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetExternalUrls_SeriesWithNoTmdbId_ReturnsNoUrl()
|
||||
{
|
||||
var series = new Series();
|
||||
|
||||
var urls = _provider.GetExternalUrls(series);
|
||||
|
||||
Assert.Empty(urls);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetExternalUrls_SeasonWithSeriesTmdbId_ReturnsCorrectUrl()
|
||||
{
|
||||
var series = new Series { Id = Guid.NewGuid() };
|
||||
series.SetProviderId(MetadataProvider.Tmdb, "1399");
|
||||
|
||||
var season = new Season { IndexNumber = 3, SeriesId = series.Id };
|
||||
_libraryManagerMock.Setup(m => m.GetItemById(series.Id)).Returns(series);
|
||||
|
||||
var urls = _provider.GetExternalUrls(season);
|
||||
|
||||
Assert.Contains(TmdbUtils.BaseTmdbUrl + "tv/1399/season/3", urls);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetExternalUrls_SeasonWithNoSeriesTmdbId_ReturnsNoUrl()
|
||||
{
|
||||
var series = new Series { Id = Guid.NewGuid() };
|
||||
var season = new Season { IndexNumber = 1, SeriesId = series.Id };
|
||||
_libraryManagerMock.Setup(m => m.GetItemById(series.Id)).Returns(series);
|
||||
|
||||
var urls = _provider.GetExternalUrls(season);
|
||||
|
||||
Assert.Empty(urls);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetExternalUrls_SeasonWithNoIndexNumber_ReturnsNoUrl()
|
||||
{
|
||||
var series = new Series { Id = Guid.NewGuid() };
|
||||
series.SetProviderId(MetadataProvider.Tmdb, "1399");
|
||||
var season = new Season { IndexNumber = null, SeriesId = series.Id };
|
||||
_libraryManagerMock.Setup(m => m.GetItemById(series.Id)).Returns(series);
|
||||
|
||||
var urls = _provider.GetExternalUrls(season);
|
||||
|
||||
Assert.Empty(urls);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetExternalUrls_EpisodeWithSeriesTmdbId_ReturnsCorrectUrl()
|
||||
{
|
||||
var series = new Series { Id = Guid.NewGuid() };
|
||||
series.SetProviderId(MetadataProvider.Tmdb, "1399");
|
||||
|
||||
var season = new Season { Id = Guid.NewGuid(), IndexNumber = 2, SeriesId = series.Id };
|
||||
|
||||
var episode = new Episode
|
||||
{
|
||||
IndexNumber = 5,
|
||||
SeasonId = season.Id,
|
||||
SeriesId = series.Id
|
||||
};
|
||||
|
||||
_libraryManagerMock.Setup(m => m.GetItemById(series.Id)).Returns(series);
|
||||
_libraryManagerMock.Setup(m => m.GetItemById(season.Id)).Returns(season);
|
||||
|
||||
var urls = _provider.GetExternalUrls(episode);
|
||||
|
||||
Assert.Contains(TmdbUtils.BaseTmdbUrl + "tv/1399/season/2/episode/5", urls);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetExternalUrls_EpisodeWithNoSeriesTmdbId_ReturnsNoUrl()
|
||||
{
|
||||
var series = new Series { Id = Guid.NewGuid() };
|
||||
var season = new Season { Id = Guid.NewGuid(), IndexNumber = 1, SeriesId = series.Id };
|
||||
var episode = new Episode { IndexNumber = 1, SeasonId = season.Id, SeriesId = series.Id };
|
||||
|
||||
_libraryManagerMock.Setup(m => m.GetItemById(series.Id)).Returns(series);
|
||||
_libraryManagerMock.Setup(m => m.GetItemById(season.Id)).Returns(season);
|
||||
|
||||
var urls = _provider.GetExternalUrls(episode);
|
||||
|
||||
Assert.Empty(urls);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetExternalUrls_MovieWithTmdbId_ReturnsCorrectUrl()
|
||||
{
|
||||
var movie = new Movie();
|
||||
movie.SetProviderId(MetadataProvider.Tmdb, "550");
|
||||
|
||||
var urls = _provider.GetExternalUrls(movie);
|
||||
|
||||
Assert.Contains(TmdbUtils.BaseTmdbUrl + "movie/550", urls);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetExternalUrls_MovieWithNoTmdbId_ReturnsNoUrl()
|
||||
{
|
||||
var movie = new Movie();
|
||||
|
||||
var urls = _provider.GetExternalUrls(movie);
|
||||
|
||||
Assert.Empty(urls);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetExternalUrls_PersonWithTmdbId_ReturnsCorrectUrl()
|
||||
{
|
||||
var person = new Person();
|
||||
person.SetProviderId(MetadataProvider.Tmdb, "6384");
|
||||
|
||||
var urls = _provider.GetExternalUrls(person);
|
||||
|
||||
Assert.Contains(TmdbUtils.BaseTmdbUrl + "person/6384", urls);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetExternalUrls_PersonWithNoTmdbId_ReturnsNoUrl()
|
||||
{
|
||||
var person = new Person();
|
||||
|
||||
var urls = _provider.GetExternalUrls(person);
|
||||
|
||||
Assert.Empty(urls);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetExternalUrls_BoxSetWithTmdbId_ReturnsCorrectUrl()
|
||||
{
|
||||
var boxSet = new BoxSet();
|
||||
boxSet.SetProviderId(MetadataProvider.Tmdb, "10");
|
||||
|
||||
var urls = _provider.GetExternalUrls(boxSet);
|
||||
|
||||
Assert.Contains(TmdbUtils.BaseTmdbUrl + "collection/10", urls);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetExternalUrls_BoxSetWithNoTmdbId_ReturnsNoUrl()
|
||||
{
|
||||
var boxSet = new BoxSet();
|
||||
|
||||
var urls = _provider.GetExternalUrls(boxSet);
|
||||
|
||||
Assert.Empty(urls);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using MediaBrowser.Controller.Entities.TV;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Providers.TV;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Providers.Tests.ExternalId
|
||||
{
|
||||
public sealed class Zap2ItExternalUrlProviderTests
|
||||
{
|
||||
private readonly Zap2ItExternalUrlProvider _provider = new();
|
||||
|
||||
[Fact]
|
||||
public void GetExternalUrls_ItemWithZap2ItId_ReturnsCorrectUrl()
|
||||
{
|
||||
var series = new Series();
|
||||
series.SetProviderId(MetadataProvider.Zap2It, "EP012345678901");
|
||||
|
||||
var urls = _provider.GetExternalUrls(series);
|
||||
|
||||
Assert.Contains("http://tvlistings.zap2it.com/overview.html?programSeriesId=EP012345678901", urls);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetExternalUrls_ItemWithNoZap2ItId_ReturnsNoUrl()
|
||||
{
|
||||
var series = new Series();
|
||||
|
||||
var urls = _provider.GetExternalUrls(series);
|
||||
|
||||
Assert.Empty(urls);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -61,6 +61,94 @@ namespace Jellyfin.Server.Implementations.Tests.Library
|
||||
Assert.NotNull(episodeResolver.Resolve(itemResolveArgs));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("/media/Show/Season 01/Show S01E01 [tvdbid=12345].mkv", MetadataProvider.Tvdb, "12345")]
|
||||
[InlineData("/media/Show/Season 01/Show S01E01 [tvdbid-12345].mkv", MetadataProvider.Tvdb, "12345")]
|
||||
[InlineData("/media/Show/Season 01/Show S01E01 (tvdbid=12345).mkv", MetadataProvider.Tvdb, "12345")]
|
||||
[InlineData("/media/Show/Season 02/Show S02E03 [tvmazeid=67890].mkv", MetadataProvider.TvMaze, "67890")]
|
||||
[InlineData("/media/Show/Season 02/Show S02E03 [tvmazeid-67890].mkv", MetadataProvider.TvMaze, "67890")]
|
||||
[InlineData("/media/Show/Season 03/Show S03E04 [tmdbid=99999].mkv", MetadataProvider.Tmdb, "99999")]
|
||||
[InlineData("/media/Show/Season 03/Show S03E04 [tmdbid-99999].mkv", MetadataProvider.Tmdb, "99999")]
|
||||
[InlineData("/media/Show/Season 04/Show S04E05 [imdbid=tt1234567].mkv", MetadataProvider.Imdb, "tt1234567")]
|
||||
[InlineData("/media/Show/Season 04/Show S04E05 [imdbid-tt1234567].mkv", MetadataProvider.Imdb, "tt1234567")]
|
||||
public void Resolve_EpisodeFileWithProviderId_SetsProviderId(string path, MetadataProvider provider, string expectedId)
|
||||
{
|
||||
var series = new Series { Name = "Show" };
|
||||
var episodeResolver = new EpisodeResolverMock(Mock.Of<ILogger<EpisodeResolver>>(), _namingOptions, Mock.Of<IDirectoryService>());
|
||||
var itemResolveArgs = new ItemResolveArgs(
|
||||
Mock.Of<IServerApplicationPaths>(),
|
||||
null)
|
||||
{
|
||||
Parent = series,
|
||||
CollectionType = CollectionType.tvshows,
|
||||
FileInfo = new FileSystemMetadata
|
||||
{
|
||||
FullName = path,
|
||||
IsDirectory = false
|
||||
}
|
||||
};
|
||||
|
||||
var episode = episodeResolver.Resolve(itemResolveArgs);
|
||||
|
||||
Assert.NotNull(episode);
|
||||
Assert.True(episode.TryGetProviderId(provider, out var actualId));
|
||||
Assert.Equal(expectedId, actualId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Resolve_EpisodeFileWithProviderIdsOnAllLevels_OnlyUsesEpisodeLevelId()
|
||||
{
|
||||
// Series folder has tvdbid=11111, season folder has tvdbid=22222, episode file has tvdbid=33333.
|
||||
// The episode should only pick up its own ID, not the series- or season-level ones.
|
||||
var series = new Series { Name = "Show" };
|
||||
var episodeResolver = new EpisodeResolverMock(Mock.Of<ILogger<EpisodeResolver>>(), _namingOptions, Mock.Of<IDirectoryService>());
|
||||
var itemResolveArgs = new ItemResolveArgs(
|
||||
Mock.Of<IServerApplicationPaths>(),
|
||||
null)
|
||||
{
|
||||
Parent = series,
|
||||
CollectionType = CollectionType.tvshows,
|
||||
FileInfo = new FileSystemMetadata
|
||||
{
|
||||
FullName = "/media/Show [tvdbid=11111]/Season 01 [tvdbid=22222]/Show S01E01 [tvdbid=33333].mkv",
|
||||
IsDirectory = false
|
||||
}
|
||||
};
|
||||
|
||||
var episode = episodeResolver.Resolve(itemResolveArgs);
|
||||
|
||||
Assert.NotNull(episode);
|
||||
Assert.True(episode.TryGetProviderId(MetadataProvider.Tvdb, out var tvdbId));
|
||||
Assert.Equal("33333", tvdbId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Resolve_EpisodeFileWithMultipleProviderIds_SetsAll()
|
||||
{
|
||||
var series = new Series { Name = "Show" };
|
||||
var episodeResolver = new EpisodeResolverMock(Mock.Of<ILogger<EpisodeResolver>>(), _namingOptions, Mock.Of<IDirectoryService>());
|
||||
var itemResolveArgs = new ItemResolveArgs(
|
||||
Mock.Of<IServerApplicationPaths>(),
|
||||
null)
|
||||
{
|
||||
Parent = series,
|
||||
CollectionType = CollectionType.tvshows,
|
||||
FileInfo = new FileSystemMetadata
|
||||
{
|
||||
FullName = "/media/Show/Season 01/Show S01E01 [tvdbid=12345][tmdbid=99999].mkv",
|
||||
IsDirectory = false
|
||||
}
|
||||
};
|
||||
|
||||
var episode = episodeResolver.Resolve(itemResolveArgs);
|
||||
|
||||
Assert.NotNull(episode);
|
||||
Assert.True(episode.TryGetProviderId(MetadataProvider.Tvdb, out var tvdbId));
|
||||
Assert.Equal("12345", tvdbId);
|
||||
Assert.True(episode.TryGetProviderId(MetadataProvider.Tmdb, out var tmdbId));
|
||||
Assert.Equal("99999", tmdbId);
|
||||
}
|
||||
|
||||
private sealed class EpisodeResolverMock : EpisodeResolver
|
||||
{
|
||||
public EpisodeResolverMock(ILogger<EpisodeResolver> logger, NamingOptions namingOptions, IDirectoryService directoryService) : base(logger, namingOptions, directoryService)
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
using Emby.Naming.Common;
|
||||
using Emby.Server.Implementations.Library.Resolvers.TV;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Entities.TV;
|
||||
using MediaBrowser.Model.Configuration;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.Globalization;
|
||||
using MediaBrowser.Model.IO;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Server.Implementations.Tests.Library
|
||||
{
|
||||
public class SeasonResolverTests
|
||||
{
|
||||
private static readonly NamingOptions _namingOptions = new();
|
||||
private readonly SeasonResolver _resolver;
|
||||
|
||||
public SeasonResolverTests()
|
||||
{
|
||||
var localizationMock = new Mock<ILocalizationManager>();
|
||||
localizationMock
|
||||
.Setup(l => l.GetLocalizedString(It.IsAny<string>()))
|
||||
.Returns("Season {0}");
|
||||
|
||||
_resolver = new SeasonResolver(
|
||||
_namingOptions,
|
||||
localizationMock.Object,
|
||||
Mock.Of<ILogger<SeasonResolver>>());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("/media/Show/Season 01 [tvdbid=12345]", MetadataProvider.Tvdb, "12345")]
|
||||
[InlineData("/media/Show/Season 01 [tvdbid-12345]", MetadataProvider.Tvdb, "12345")]
|
||||
[InlineData("/media/Show/Season 01 (tvdbid=12345)", MetadataProvider.Tvdb, "12345")]
|
||||
[InlineData("/media/Show/Season 02 [tvmazeid=67890]", MetadataProvider.TvMaze, "67890")]
|
||||
[InlineData("/media/Show/Season 02 [tvmazeid-67890]", MetadataProvider.TvMaze, "67890")]
|
||||
[InlineData("/media/Show/Season 03 [tmdbid=99999]", MetadataProvider.Tmdb, "99999")]
|
||||
[InlineData("/media/Show/Season 03 [tmdbid-99999]", MetadataProvider.Tmdb, "99999")]
|
||||
public void Resolve_SeasonFolderWithProviderId_SetsProviderId(string path, MetadataProvider provider, string expectedId)
|
||||
{
|
||||
var series = new Series { Path = "/media/Show" };
|
||||
|
||||
var args = new MediaBrowser.Controller.Library.ItemResolveArgs(
|
||||
Mock.Of<IServerApplicationPaths>(),
|
||||
null)
|
||||
{
|
||||
Parent = series,
|
||||
LibraryOptions = new LibraryOptions(),
|
||||
FileInfo = new FileSystemMetadata
|
||||
{
|
||||
FullName = path,
|
||||
IsDirectory = true
|
||||
}
|
||||
};
|
||||
|
||||
var season = _resolver.Resolve(args);
|
||||
|
||||
Assert.NotNull(season);
|
||||
Assert.True(season.TryGetProviderId(provider, out var actualId));
|
||||
Assert.Equal(expectedId, actualId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Resolve_SeasonFolderWithMultipleProviderIds_SetsAll()
|
||||
{
|
||||
var series = new Series { Path = "/media/Show" };
|
||||
|
||||
var args = new MediaBrowser.Controller.Library.ItemResolveArgs(
|
||||
Mock.Of<IServerApplicationPaths>(),
|
||||
null)
|
||||
{
|
||||
Parent = series,
|
||||
LibraryOptions = new LibraryOptions(),
|
||||
FileInfo = new FileSystemMetadata
|
||||
{
|
||||
FullName = "/media/Show/Season 01 [tvdbid=12345][tmdbid=99999]",
|
||||
IsDirectory = true
|
||||
}
|
||||
};
|
||||
|
||||
var season = _resolver.Resolve(args);
|
||||
|
||||
Assert.NotNull(season);
|
||||
Assert.True(season.TryGetProviderId(MetadataProvider.Tvdb, out var tvdbId));
|
||||
Assert.Equal("12345", tvdbId);
|
||||
Assert.True(season.TryGetProviderId(MetadataProvider.Tmdb, out var tmdbId));
|
||||
Assert.Equal("99999", tmdbId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Resolve_SeasonFolderWithSeriesProviderIdInParentPath_DoesNotInheritSeriesId()
|
||||
{
|
||||
// Series folder has tvdbid=11111, season folder has tvdbid=22222.
|
||||
// The season should only pick up its own ID, not the series-level one.
|
||||
var series = new Series { Path = "/media/Show [tvdbid=11111]" };
|
||||
|
||||
var args = new MediaBrowser.Controller.Library.ItemResolveArgs(
|
||||
Mock.Of<IServerApplicationPaths>(),
|
||||
null)
|
||||
{
|
||||
Parent = series,
|
||||
LibraryOptions = new LibraryOptions(),
|
||||
FileInfo = new FileSystemMetadata
|
||||
{
|
||||
FullName = "/media/Show [tvdbid=11111]/Season 01 [tvdbid=22222]",
|
||||
IsDirectory = true
|
||||
}
|
||||
};
|
||||
|
||||
var season = _resolver.Resolve(args);
|
||||
|
||||
Assert.NotNull(season);
|
||||
Assert.True(season.TryGetProviderId(MetadataProvider.Tvdb, out var tvdbId));
|
||||
Assert.Equal("22222", tvdbId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Resolve_SeasonFolderWithNoProviderId_HasNoProviderIds()
|
||||
{
|
||||
var series = new Series { Path = "/media/Show" };
|
||||
|
||||
var args = new MediaBrowser.Controller.Library.ItemResolveArgs(
|
||||
Mock.Of<IServerApplicationPaths>(),
|
||||
null)
|
||||
{
|
||||
Parent = series,
|
||||
LibraryOptions = new LibraryOptions(),
|
||||
FileInfo = new FileSystemMetadata
|
||||
{
|
||||
FullName = "/media/Show/Season 01",
|
||||
IsDirectory = true
|
||||
}
|
||||
};
|
||||
|
||||
var season = _resolver.Resolve(args);
|
||||
|
||||
Assert.NotNull(season);
|
||||
Assert.False(season.TryGetProviderId(MetadataProvider.Tvdb, out _));
|
||||
Assert.False(season.TryGetProviderId(MetadataProvider.TvMaze, out _));
|
||||
Assert.False(season.TryGetProviderId(MetadataProvider.Tmdb, out _));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
using Emby.Naming.Common;
|
||||
using Emby.Server.Implementations.Library.Resolvers.TV;
|
||||
using Jellyfin.Data.Enums;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Entities.TV;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.IO;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Server.Implementations.Tests.Library
|
||||
{
|
||||
public class SeriesResolverTests
|
||||
{
|
||||
private static readonly NamingOptions _namingOptions = new();
|
||||
private readonly SeriesResolver _resolver;
|
||||
private readonly Mock<ILibraryManager> _libraryManagerMock;
|
||||
|
||||
public SeriesResolverTests()
|
||||
{
|
||||
_libraryManagerMock = new Mock<ILibraryManager>();
|
||||
// Return null so that configuredContentType != CollectionType.tvshows, allowing series resolution.
|
||||
_libraryManagerMock
|
||||
.Setup(m => m.GetConfiguredContentType(It.IsAny<string>()))
|
||||
.Returns((CollectionType?)null);
|
||||
|
||||
_resolver = new SeriesResolver(Mock.Of<ILogger<SeriesResolver>>(), _namingOptions);
|
||||
}
|
||||
|
||||
private MediaBrowser.Controller.Library.ItemResolveArgs MakeTvArgs(string path) =>
|
||||
new(Mock.Of<IServerApplicationPaths>(), _libraryManagerMock.Object)
|
||||
{
|
||||
CollectionType = CollectionType.tvshows,
|
||||
FileSystemChildren = [],
|
||||
FileInfo = new FileSystemMetadata
|
||||
{
|
||||
FullName = path,
|
||||
IsDirectory = true
|
||||
}
|
||||
};
|
||||
|
||||
[Theory]
|
||||
[InlineData("/media/Show [tvdbid=12345]", MetadataProvider.Tvdb, "12345")]
|
||||
[InlineData("/media/Show [tvdbid-12345]", MetadataProvider.Tvdb, "12345")]
|
||||
[InlineData("/media/Show (tvdbid=12345)", MetadataProvider.Tvdb, "12345")]
|
||||
[InlineData("/media/Show [tvmazeid=67890]", MetadataProvider.TvMaze, "67890")]
|
||||
[InlineData("/media/Show [tvmazeid-67890]", MetadataProvider.TvMaze, "67890")]
|
||||
[InlineData("/media/Show [tmdbid=99999]", MetadataProvider.Tmdb, "99999")]
|
||||
[InlineData("/media/Show [tmdbid-99999]", MetadataProvider.Tmdb, "99999")]
|
||||
[InlineData("/media/Show [imdbid=tt1234567]", MetadataProvider.Imdb, "tt1234567")]
|
||||
[InlineData("/media/Show [imdbid-tt1234567]", MetadataProvider.Imdb, "tt1234567")]
|
||||
public void ResolvePath_SeriesFolderWithProviderId_SetsProviderId(string path, MetadataProvider provider, string expectedId)
|
||||
{
|
||||
var series = _resolver.ResolvePath(MakeTvArgs(path)) as Series;
|
||||
|
||||
Assert.NotNull(series);
|
||||
Assert.True(series.TryGetProviderId(provider, out var actualId));
|
||||
Assert.Equal(expectedId, actualId);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("/media/Show [anidbid=11111]", "AniDB", "11111")]
|
||||
[InlineData("/media/Show [anilistid=22222]", "AniList", "22222")]
|
||||
[InlineData("/media/Show [anisearchid=33333]", "AniSearch", "33333")]
|
||||
public void ResolvePath_SeriesFolderWithAniProviderId_SetsProviderId(string path, string providerKey, string expectedId)
|
||||
{
|
||||
var series = _resolver.ResolvePath(MakeTvArgs(path)) as Series;
|
||||
|
||||
Assert.NotNull(series);
|
||||
Assert.True(series.TryGetProviderId(providerKey, out var actualId));
|
||||
Assert.Equal(expectedId, actualId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ResolvePath_SeriesFolderWithMultipleProviderIds_SetsAll()
|
||||
{
|
||||
var series = _resolver.ResolvePath(MakeTvArgs("/media/Show [tvdbid=12345][tmdbid=99999]")) as Series;
|
||||
|
||||
Assert.NotNull(series);
|
||||
Assert.True(series.TryGetProviderId(MetadataProvider.Tvdb, out var tvdbId));
|
||||
Assert.Equal("12345", tvdbId);
|
||||
Assert.True(series.TryGetProviderId(MetadataProvider.Tmdb, out var tmdbId));
|
||||
Assert.Equal("99999", tmdbId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ResolvePath_SeriesFolderWithNoProviderId_HasNoProviderIds()
|
||||
{
|
||||
var series = _resolver.ResolvePath(MakeTvArgs("/media/Show")) as Series;
|
||||
|
||||
Assert.NotNull(series);
|
||||
Assert.False(series.TryGetProviderId(MetadataProvider.Tvdb, out _));
|
||||
Assert.False(series.TryGetProviderId(MetadataProvider.TvMaze, out _));
|
||||
Assert.False(series.TryGetProviderId(MetadataProvider.Tmdb, out _));
|
||||
Assert.False(series.TryGetProviderId(MetadataProvider.Imdb, out _));
|
||||
Assert.False(series.TryGetProviderId("AniDB", out _));
|
||||
Assert.False(series.TryGetProviderId("AniList", out _));
|
||||
Assert.False(series.TryGetProviderId("AniSearch", out _));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ResolvePath_SeriesFolderNotInTvShowsCollection_DoesNotResolve()
|
||||
{
|
||||
// Without CollectionType.tvshows, a plain folder with no tvshow.nfo and
|
||||
// no season/episode children should not resolve as a Series.
|
||||
var args = new MediaBrowser.Controller.Library.ItemResolveArgs(
|
||||
Mock.Of<IServerApplicationPaths>(),
|
||||
_libraryManagerMock.Object)
|
||||
{
|
||||
CollectionType = null,
|
||||
FileSystemChildren = [],
|
||||
FileInfo = new FileSystemMetadata
|
||||
{
|
||||
FullName = "/media/Show [tvdbid=12345]",
|
||||
IsDirectory = true
|
||||
}
|
||||
};
|
||||
|
||||
Assert.Null(_resolver.ResolvePath(args));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user