mirror of
https://github.com/jellyfin/jellyfin.git
synced 2024-11-17 19:08:53 -07:00
Merge remote-tracking branch 'main/master' into FixMessageCommand
This commit is contained in:
commit
3820671724
@ -4,6 +4,7 @@ using System;
|
|||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
using MediaBrowser.Common.Providers;
|
||||||
|
|
||||||
namespace Emby.Server.Implementations.Library
|
namespace Emby.Server.Implementations.Library
|
||||||
{
|
{
|
||||||
@ -43,8 +44,8 @@ namespace Emby.Server.Implementations.Library
|
|||||||
// for imdbid we also accept pattern matching
|
// for imdbid we also accept pattern matching
|
||||||
if (string.Equals(attribute, "imdbid", StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(attribute, "imdbid", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
var m = Regex.Match(str, "tt([0-9]{7,8})", RegexOptions.IgnoreCase);
|
var match = ProviderIdParsers.TryFindImdbId(str, out var imdbId);
|
||||||
return m.Success ? m.Value : null;
|
return match ? imdbId.ToString() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
125
MediaBrowser.Common/Providers/ProviderIdParsers.cs
Normal file
125
MediaBrowser.Common/Providers/ProviderIdParsers.cs
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
#nullable enable
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
|
namespace MediaBrowser.Common.Providers
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Parsers for provider ids.
|
||||||
|
/// </summary>
|
||||||
|
public static class ProviderIdParsers
|
||||||
|
{
|
||||||
|
private const int ImdbMinNumbers = 7;
|
||||||
|
private const int ImdbMaxNumbers = 8;
|
||||||
|
private const string ImdbPrefix = "tt";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parses an IMDb id from a string.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="text">The text to parse.</param>
|
||||||
|
/// <param name="imdbId">The parsed IMDb id.</param>
|
||||||
|
/// <returns>True if parsing was successful, false otherwise.</returns>
|
||||||
|
public static bool TryFindImdbId(ReadOnlySpan<char> text, [NotNullWhen(true)] out ReadOnlySpan<char> imdbId)
|
||||||
|
{
|
||||||
|
// imdb id is at least 9 chars (tt + 7 numbers)
|
||||||
|
while (text.Length >= 2 + ImdbMinNumbers)
|
||||||
|
{
|
||||||
|
var ttPos = text.IndexOf(ImdbPrefix);
|
||||||
|
if (ttPos == -1)
|
||||||
|
{
|
||||||
|
imdbId = default;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
text = text.Slice(ttPos);
|
||||||
|
var i = 2;
|
||||||
|
var limit = Math.Min(text.Length, ImdbMaxNumbers + 2);
|
||||||
|
for (; i < limit; i++)
|
||||||
|
{
|
||||||
|
var c = text[i];
|
||||||
|
if (!IsDigit(c))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// skip if more than 8 digits + 2 chars for tt
|
||||||
|
if (i <= ImdbMaxNumbers + 2 && i >= ImdbMinNumbers + 2)
|
||||||
|
{
|
||||||
|
imdbId = text.Slice(0, i);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
text = text.Slice(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
imdbId = default;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parses an TMDb id from a movie url.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="text">The text with the url to parse.</param>
|
||||||
|
/// <param name="tmdbId">The parsed TMDb id.</param>
|
||||||
|
/// <returns>True if parsing was successful, false otherwise.</returns>
|
||||||
|
public static bool TryFindTmdbMovieId(ReadOnlySpan<char> text, [NotNullWhen(true)] out ReadOnlySpan<char> tmdbId)
|
||||||
|
=> TryFindProviderId(text, "themoviedb.org/movie/", out tmdbId);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parses an TMDb id from a series url.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="text">The text with the url to parse.</param>
|
||||||
|
/// <param name="tmdbId">The parsed TMDb id.</param>
|
||||||
|
/// <returns>True if parsing was successful, false otherwise.</returns>
|
||||||
|
public static bool TryFindTmdbSeriesId(ReadOnlySpan<char> text, [NotNullWhen(true)] out ReadOnlySpan<char> tmdbId)
|
||||||
|
=> TryFindProviderId(text, "themoviedb.org/tv/", out tmdbId);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parses an TVDb id from a url.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="text">The text with the url to parse.</param>
|
||||||
|
/// <param name="tvdbId">The parsed TVDb id.</param>
|
||||||
|
/// <returns>True if parsing was successful, false otherwise.</returns>
|
||||||
|
public static bool TryFindTvdbId(ReadOnlySpan<char> text, [NotNullWhen(true)] out ReadOnlySpan<char> tvdbId)
|
||||||
|
=> TryFindProviderId(text, "thetvdb.com/?tab=series&id=", out tvdbId);
|
||||||
|
|
||||||
|
private static bool TryFindProviderId(ReadOnlySpan<char> text, ReadOnlySpan<char> searchString, [NotNullWhen(true)] out ReadOnlySpan<char> providerId)
|
||||||
|
{
|
||||||
|
var searchPos = text.IndexOf(searchString);
|
||||||
|
if (searchPos == -1)
|
||||||
|
{
|
||||||
|
providerId = default;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
text = text.Slice(searchPos + searchString.Length);
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
for (; i < text.Length; i++)
|
||||||
|
{
|
||||||
|
var c = text[i];
|
||||||
|
|
||||||
|
if (!IsDigit(c))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i >= 1)
|
||||||
|
{
|
||||||
|
providerId = text.Slice(0, i);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
providerId = default;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsDigit(char c)
|
||||||
|
{
|
||||||
|
return c >= '0' && c <= '9';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -6,11 +6,12 @@ using System.Globalization;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
using MediaBrowser.Common.Configuration;
|
using MediaBrowser.Common.Configuration;
|
||||||
|
using MediaBrowser.Common.Providers;
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
|
using MediaBrowser.Controller.Entities.Movies;
|
||||||
using MediaBrowser.Controller.Entities.TV;
|
using MediaBrowser.Controller.Entities.TV;
|
||||||
using MediaBrowser.Controller.Library;
|
using MediaBrowser.Controller.Library;
|
||||||
using MediaBrowser.Controller.Providers;
|
using MediaBrowser.Controller.Providers;
|
||||||
@ -67,8 +68,6 @@ namespace MediaBrowser.XbmcMetadata.Parsers
|
|||||||
|
|
||||||
protected virtual bool SupportsUrlAfterClosingXmlTag => false;
|
protected virtual bool SupportsUrlAfterClosingXmlTag => false;
|
||||||
|
|
||||||
protected virtual string MovieDbParserSearchString => "themoviedb.org/movie/";
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fetches metadata for an item from one xml file.
|
/// Fetches metadata for an item from one xml file.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -185,8 +184,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// If the file is just an Imdb url, handle that
|
// If the file is just provider urls, handle that
|
||||||
|
|
||||||
ParseProviderLinks(item.Item, xml);
|
ParseProviderLinks(item.Item, xml);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@ -225,50 +223,29 @@ namespace MediaBrowser.XbmcMetadata.Parsers
|
|||||||
|
|
||||||
protected void ParseProviderLinks(T item, string xml)
|
protected void ParseProviderLinks(T item, string xml)
|
||||||
{
|
{
|
||||||
// Look for a match for the Regex pattern "tt" followed by 7 or 8 digits
|
if (ProviderIdParsers.TryFindImdbId(xml, out var imdbId))
|
||||||
var m = Regex.Match(xml, "tt([0-9]{7,8})", RegexOptions.IgnoreCase);
|
|
||||||
if (m.Success)
|
|
||||||
{
|
{
|
||||||
item.SetProviderId(MetadataProvider.Imdb, m.Value);
|
item.SetProviderId(MetadataProvider.Imdb, imdbId.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Support Tmdb
|
if (item is Movie)
|
||||||
// https://www.themoviedb.org/movie/30287-fallo
|
|
||||||
var srch = MovieDbParserSearchString;
|
|
||||||
var index = xml.IndexOf(srch, StringComparison.OrdinalIgnoreCase);
|
|
||||||
|
|
||||||
if (index != -1)
|
|
||||||
{
|
{
|
||||||
var tmdbId = xml.AsSpan().Slice(index + srch.Length).TrimEnd('/');
|
if (ProviderIdParsers.TryFindTmdbMovieId(xml, out var tmdbId))
|
||||||
index = tmdbId.IndexOf('-');
|
|
||||||
if (index != -1)
|
|
||||||
{
|
{
|
||||||
tmdbId = tmdbId.Slice(0, index);
|
item.SetProviderId(MetadataProvider.Tmdb, tmdbId.ToString());
|
||||||
}
|
|
||||||
|
|
||||||
if (!tmdbId.IsEmpty
|
|
||||||
&& !tmdbId.IsWhiteSpace()
|
|
||||||
&& int.TryParse(tmdbId, NumberStyles.Integer, CultureInfo.InvariantCulture, out var value))
|
|
||||||
{
|
|
||||||
item.SetProviderId(MetadataProvider.Tmdb, value.ToString(UsCulture));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item is Series)
|
if (item is Series)
|
||||||
{
|
{
|
||||||
srch = "thetvdb.com/?tab=series&id=";
|
if (ProviderIdParsers.TryFindTmdbSeriesId(xml, out var tmdbId))
|
||||||
|
|
||||||
index = xml.IndexOf(srch, StringComparison.OrdinalIgnoreCase);
|
|
||||||
|
|
||||||
if (index != -1)
|
|
||||||
{
|
{
|
||||||
var tvdbId = xml.AsSpan().Slice(index + srch.Length).TrimEnd('/');
|
item.SetProviderId(MetadataProvider.Tmdb, tmdbId.ToString());
|
||||||
if (!tvdbId.IsEmpty
|
}
|
||||||
&& !tvdbId.IsWhiteSpace()
|
|
||||||
&& int.TryParse(tvdbId, NumberStyles.Integer, CultureInfo.InvariantCulture, out var value))
|
if (ProviderIdParsers.TryFindTvdbId(xml, out var tvdbId))
|
||||||
{
|
{
|
||||||
item.SetProviderId(MetadataProvider.Tvdb, value.ToString(UsCulture));
|
item.SetProviderId(MetadataProvider.Tvdb, tvdbId.ToString());
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,12 +49,19 @@ namespace MediaBrowser.XbmcMetadata.Parsers
|
|||||||
{
|
{
|
||||||
case "id":
|
case "id":
|
||||||
{
|
{
|
||||||
|
// get ids from attributes
|
||||||
string? imdbId = reader.GetAttribute("IMDB");
|
string? imdbId = reader.GetAttribute("IMDB");
|
||||||
string? tmdbId = reader.GetAttribute("TMDB");
|
string? tmdbId = reader.GetAttribute("TMDB");
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(imdbId))
|
// read id from content
|
||||||
|
var contentId = reader.ReadElementContentAsString();
|
||||||
|
if (contentId.Contains("tt", StringComparison.Ordinal) && string.IsNullOrEmpty(imdbId))
|
||||||
{
|
{
|
||||||
imdbId = reader.ReadElementContentAsString();
|
imdbId = contentId;
|
||||||
|
}
|
||||||
|
else if (string.IsNullOrEmpty(tmdbId))
|
||||||
|
{
|
||||||
|
tmdbId = contentId;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(imdbId))
|
if (!string.IsNullOrWhiteSpace(imdbId))
|
||||||
|
@ -37,9 +37,6 @@ namespace MediaBrowser.XbmcMetadata.Parsers
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override bool SupportsUrlAfterClosingXmlTag => true;
|
protected override bool SupportsUrlAfterClosingXmlTag => true;
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override string MovieDbParserSearchString => "themoviedb.org/tv/";
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override void FetchDataFromXmlNode(XmlReader reader, MetadataResult<Series> itemResult)
|
protected override void FetchDataFromXmlNode(XmlReader reader, MetadataResult<Series> itemResult)
|
||||||
{
|
{
|
||||||
|
@ -0,0 +1,85 @@
|
|||||||
|
using System;
|
||||||
|
using MediaBrowser.Common.Providers;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Jellyfin.Common.Tests.Providers
|
||||||
|
{
|
||||||
|
public class ProviderIdParserTests
|
||||||
|
{
|
||||||
|
[Theory]
|
||||||
|
[InlineData("tt1234567", "tt1234567")]
|
||||||
|
[InlineData("tt12345678", "tt12345678")]
|
||||||
|
[InlineData("https://www.imdb.com/title/tt1234567", "tt1234567")]
|
||||||
|
[InlineData("https://www.imdb.com/title/tt12345678", "tt12345678")]
|
||||||
|
[InlineData(@"multiline\nhttps://www.imdb.com/title/tt1234567", "tt1234567")]
|
||||||
|
[InlineData(@"multiline\nhttps://www.imdb.com/title/tt12345678", "tt12345678")]
|
||||||
|
[InlineData("tt1234567tt7654321", "tt1234567")]
|
||||||
|
[InlineData("tt12345678tt7654321", "tt12345678")]
|
||||||
|
[InlineData("tt123456789", "tt12345678")]
|
||||||
|
public void FindImdbId_Valid_Success(string text, string expected)
|
||||||
|
{
|
||||||
|
Assert.True(ProviderIdParsers.TryFindImdbId(text, out ReadOnlySpan<char> parsedId));
|
||||||
|
Assert.Equal(expected, parsedId.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("tt123456")]
|
||||||
|
[InlineData("https://www.imdb.com/title/tt123456")]
|
||||||
|
[InlineData("Jellyfin")]
|
||||||
|
public void FindImdbId_Invalid_Success(string text)
|
||||||
|
{
|
||||||
|
Assert.False(ProviderIdParsers.TryFindImdbId(text, out _));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("https://www.themoviedb.org/movie/30287-fallo", "30287")]
|
||||||
|
[InlineData("themoviedb.org/movie/30287", "30287")]
|
||||||
|
public void FindTmdbMovieId_Valid_Success(string text, string expected)
|
||||||
|
{
|
||||||
|
Assert.True(ProviderIdParsers.TryFindTmdbMovieId(text, out ReadOnlySpan<char> parsedId));
|
||||||
|
Assert.Equal(expected, parsedId.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("https://www.themoviedb.org/movie/fallo-30287")]
|
||||||
|
[InlineData("https://www.themoviedb.org/tv/1668-friends")]
|
||||||
|
public void FindTmdbMovieId_Invalid_Success(string text)
|
||||||
|
{
|
||||||
|
Assert.False(ProviderIdParsers.TryFindTmdbMovieId(text, out _));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("https://www.themoviedb.org/tv/1668-friends", "1668")]
|
||||||
|
[InlineData("themoviedb.org/tv/1668", "1668")]
|
||||||
|
public void FindTmdbSeriesId_Valid_Success(string text, string expected)
|
||||||
|
{
|
||||||
|
Assert.True(ProviderIdParsers.TryFindTmdbSeriesId(text, out ReadOnlySpan<char> parsedId));
|
||||||
|
Assert.Equal(expected, parsedId.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("https://www.themoviedb.org/tv/friends-1668")]
|
||||||
|
[InlineData("https://www.themoviedb.org/movie/30287-fallo")]
|
||||||
|
public void FindTmdbSeriesId_Invalid_Success(string text)
|
||||||
|
{
|
||||||
|
Assert.False(ProviderIdParsers.TryFindTmdbSeriesId(text, out _));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("https://www.thetvdb.com/?tab=series&id=121361", "121361")]
|
||||||
|
[InlineData("thetvdb.com/?tab=series&id=121361", "121361")]
|
||||||
|
public void FindTvdbId_Valid_Success(string text, string expected)
|
||||||
|
{
|
||||||
|
Assert.True(ProviderIdParsers.TryFindTvdbId(text, out ReadOnlySpan<char> parsedId));
|
||||||
|
Assert.Equal(expected, parsedId.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("thetvdb.com/?tab=series&id=Jellyfin121361")]
|
||||||
|
[InlineData("https://www.themoviedb.org/tv/1668-friends")]
|
||||||
|
public void FindTvdbId_Invalid_Success(string text)
|
||||||
|
{
|
||||||
|
Assert.False(ProviderIdParsers.TryFindTvdbId(text, out _));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -203,6 +203,21 @@ namespace Jellyfin.XbmcMetadata.Tests.Parsers
|
|||||||
Assert.Equal(id, item.ProviderIds[provider]);
|
Assert.Equal(id, item.ProviderIds[provider]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Parse_RadarrUrlFile_Success()
|
||||||
|
{
|
||||||
|
var result = new MetadataResult<Video>()
|
||||||
|
{
|
||||||
|
Item = new Movie()
|
||||||
|
};
|
||||||
|
|
||||||
|
_parser.Fetch(result, "Test Data/Radarr.nfo", CancellationToken.None);
|
||||||
|
var item = (Movie)result.Item;
|
||||||
|
|
||||||
|
Assert.Equal("583689", item.ProviderIds[MetadataProvider.Tmdb.ToString()]);
|
||||||
|
Assert.Equal("tt4154796", item.ProviderIds[MetadataProvider.Imdb.ToString()]);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Fetch_WithNullItem_ThrowsArgumentException()
|
public void Fetch_WithNullItem_ThrowsArgumentException()
|
||||||
{
|
{
|
||||||
|
@ -93,7 +93,8 @@
|
|||||||
</fanart>
|
</fanart>
|
||||||
<mpaa>Australia:M</mpaa>
|
<mpaa>Australia:M</mpaa>
|
||||||
<id>tt0974015</id>
|
<id>tt0974015</id>
|
||||||
<uniqueid type="imdb" default="true">tt0974015</uniqueid>
|
<uniqueid type="imdb">tt0974015</uniqueid>
|
||||||
|
<uniqueid type="tmdb">141052</uniqueid>
|
||||||
<genre>Action</genre>
|
<genre>Action</genre>
|
||||||
<genre>Adventure</genre>
|
<genre>Adventure</genre>
|
||||||
<genre>Fantasy</genre>
|
<genre>Fantasy</genre>
|
||||||
|
2
tests/Jellyfin.XbmcMetadata.Tests/Test Data/Radarr.nfo
Normal file
2
tests/Jellyfin.XbmcMetadata.Tests/Test Data/Radarr.nfo
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
https://www.themoviedb.org/movie/583689
|
||||||
|
https://www.imdb.com/title/tt4154796
|
Loading…
Reference in New Issue
Block a user