Merge branch 'master' into unharden-for-lxc

This commit is contained in:
Cody Robibero 2021-12-24 02:01:06 +00:00 committed by GitHub
commit cecfdeeec3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
189 changed files with 1826 additions and 2364 deletions

1
.copr Symbolic link
View File

@ -0,0 +1 @@
fedora

View File

@ -1 +0,0 @@
../fedora/Makefile

View File

@ -18,11 +18,8 @@ using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Controller.Playlists;
using MediaBrowser.Controller.TV; using MediaBrowser.Controller.TV;
using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Dto; using MediaBrowser.Model.Dto;
@ -30,12 +27,7 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Querying; using MediaBrowser.Model.Querying;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Book = MediaBrowser.Controller.Entities.Book;
using Episode = MediaBrowser.Controller.Entities.TV.Episode;
using Genre = MediaBrowser.Controller.Entities.Genre; using Genre = MediaBrowser.Controller.Entities.Genre;
using Movie = MediaBrowser.Controller.Entities.Movies.Movie;
using MusicAlbum = MediaBrowser.Controller.Entities.Audio.MusicAlbum;
using Series = MediaBrowser.Controller.Entities.TV.Series;
namespace Emby.Dlna.ContentDirectory namespace Emby.Dlna.ContentDirectory
{ {
@ -539,7 +531,7 @@ namespace Emby.Dlna.ContentDirectory
User = user, User = user,
Recursive = true, Recursive = true,
IsMissing = false, IsMissing = false,
ExcludeItemTypes = new[] { nameof(Book) }, ExcludeItemTypes = new[] { BaseItemKind.Book },
IsFolder = isFolder, IsFolder = isFolder,
MediaTypes = mediaTypes, MediaTypes = mediaTypes,
DtoOptions = GetDtoOptions() DtoOptions = GetDtoOptions()
@ -619,7 +611,7 @@ namespace Emby.Dlna.ContentDirectory
Limit = limit, Limit = limit,
StartIndex = startIndex, StartIndex = startIndex,
IsVirtualItem = false, IsVirtualItem = false,
ExcludeItemTypes = new[] { nameof(Book) }, ExcludeItemTypes = new[] { BaseItemKind.Book },
IsPlaceHolder = false, IsPlaceHolder = false,
DtoOptions = GetDtoOptions(), DtoOptions = GetDtoOptions(),
OrderBy = GetOrderBy(sort, folder.IsPreSorted) OrderBy = GetOrderBy(sort, folder.IsPreSorted)
@ -644,7 +636,7 @@ namespace Emby.Dlna.ContentDirectory
{ {
StartIndex = startIndex, StartIndex = startIndex,
Limit = limit, Limit = limit,
IncludeItemTypes = new[] { nameof(LiveTvChannel) }, IncludeItemTypes = new[] { BaseItemKind.LiveTvChannel },
OrderBy = GetOrderBy(sort, false) OrderBy = GetOrderBy(sort, false)
}; };
@ -675,23 +667,23 @@ namespace Emby.Dlna.ContentDirectory
switch (stubType) switch (stubType)
{ {
case StubType.Latest: case StubType.Latest:
return GetLatest(item, query, nameof(Audio)); return GetLatest(item, query, BaseItemKind.Audio);
case StubType.Playlists: case StubType.Playlists:
return GetMusicPlaylists(query); return GetMusicPlaylists(query);
case StubType.Albums: case StubType.Albums:
return GetChildrenOfItem(item, query, nameof(MusicAlbum)); return GetChildrenOfItem(item, query, BaseItemKind.MusicAlbum);
case StubType.Artists: case StubType.Artists:
return GetMusicArtists(item, query); return GetMusicArtists(item, query);
case StubType.AlbumArtists: case StubType.AlbumArtists:
return GetMusicAlbumArtists(item, query); return GetMusicAlbumArtists(item, query);
case StubType.FavoriteAlbums: case StubType.FavoriteAlbums:
return GetChildrenOfItem(item, query, nameof(MusicAlbum), true); return GetChildrenOfItem(item, query, BaseItemKind.MusicAlbum, true);
case StubType.FavoriteArtists: case StubType.FavoriteArtists:
return GetFavoriteArtists(item, query); return GetFavoriteArtists(item, query);
case StubType.FavoriteSongs: case StubType.FavoriteSongs:
return GetChildrenOfItem(item, query, nameof(Audio), true); return GetChildrenOfItem(item, query, BaseItemKind.Audio, true);
case StubType.Songs: case StubType.Songs:
return GetChildrenOfItem(item, query, nameof(Audio)); return GetChildrenOfItem(item, query, BaseItemKind.Audio);
case StubType.Genres: case StubType.Genres:
return GetMusicGenres(item, query); return GetMusicGenres(item, query);
} }
@ -746,13 +738,13 @@ namespace Emby.Dlna.ContentDirectory
case StubType.ContinueWatching: case StubType.ContinueWatching:
return GetMovieContinueWatching(item, query); return GetMovieContinueWatching(item, query);
case StubType.Latest: case StubType.Latest:
return GetLatest(item, query, nameof(Movie)); return GetLatest(item, query, BaseItemKind.Movie);
case StubType.Movies: case StubType.Movies:
return GetChildrenOfItem(item, query, nameof(Movie)); return GetChildrenOfItem(item, query, BaseItemKind.Movie);
case StubType.Collections: case StubType.Collections:
return GetMovieCollections(query); return GetMovieCollections(query);
case StubType.Favorites: case StubType.Favorites:
return GetChildrenOfItem(item, query, nameof(Movie), true); return GetChildrenOfItem(item, query, BaseItemKind.Movie, true);
case StubType.Genres: case StubType.Genres:
return GetGenres(item, query); return GetGenres(item, query);
} }
@ -831,13 +823,13 @@ namespace Emby.Dlna.ContentDirectory
case StubType.NextUp: case StubType.NextUp:
return GetNextUp(item, query); return GetNextUp(item, query);
case StubType.Latest: case StubType.Latest:
return GetLatest(item, query, nameof(Episode)); return GetLatest(item, query, BaseItemKind.Episode);
case StubType.Series: case StubType.Series:
return GetChildrenOfItem(item, query, nameof(Series)); return GetChildrenOfItem(item, query, BaseItemKind.Series);
case StubType.FavoriteSeries: case StubType.FavoriteSeries:
return GetChildrenOfItem(item, query, nameof(Series), true); return GetChildrenOfItem(item, query, BaseItemKind.Series, true);
case StubType.FavoriteEpisodes: case StubType.FavoriteEpisodes:
return GetChildrenOfItem(item, query, nameof(Episode), true); return GetChildrenOfItem(item, query, BaseItemKind.Episode, true);
case StubType.Genres: case StubType.Genres:
return GetGenres(item, query); return GetGenres(item, query);
} }
@ -898,7 +890,7 @@ namespace Emby.Dlna.ContentDirectory
private QueryResult<ServerItem> GetMovieCollections(InternalItemsQuery query) private QueryResult<ServerItem> GetMovieCollections(InternalItemsQuery query)
{ {
query.Recursive = true; query.Recursive = true;
query.IncludeItemTypes = new[] { nameof(BoxSet) }; query.IncludeItemTypes = new[] { BaseItemKind.BoxSet };
var result = _libraryManager.GetItemsResult(query); var result = _libraryManager.GetItemsResult(query);
@ -913,7 +905,7 @@ namespace Emby.Dlna.ContentDirectory
/// <param name="itemType">The item type.</param> /// <param name="itemType">The item type.</param>
/// <param name="isFavorite">A value indicating whether to only fetch favorite items.</param> /// <param name="isFavorite">A value indicating whether to only fetch favorite items.</param>
/// <returns>The <see cref="QueryResult{ServerItem}"/>.</returns> /// <returns>The <see cref="QueryResult{ServerItem}"/>.</returns>
private QueryResult<ServerItem> GetChildrenOfItem(BaseItem parent, InternalItemsQuery query, string itemType, bool isFavorite = false) private QueryResult<ServerItem> GetChildrenOfItem(BaseItem parent, InternalItemsQuery query, BaseItemKind itemType, bool isFavorite = false)
{ {
query.Recursive = true; query.Recursive = true;
query.Parent = parent; query.Parent = parent;
@ -1013,7 +1005,7 @@ namespace Emby.Dlna.ContentDirectory
private QueryResult<ServerItem> GetMusicPlaylists(InternalItemsQuery query) private QueryResult<ServerItem> GetMusicPlaylists(InternalItemsQuery query)
{ {
query.Parent = null; query.Parent = null;
query.IncludeItemTypes = new[] { nameof(Playlist) }; query.IncludeItemTypes = new[] { BaseItemKind.Playlist };
query.Recursive = true; query.Recursive = true;
var result = _libraryManager.GetItemsResult(query); var result = _libraryManager.GetItemsResult(query);
@ -1052,7 +1044,7 @@ namespace Emby.Dlna.ContentDirectory
/// <param name="query">The <see cref="InternalItemsQuery"/>.</param> /// <param name="query">The <see cref="InternalItemsQuery"/>.</param>
/// <param name="itemType">The item type.</param> /// <param name="itemType">The item type.</param>
/// <returns>The <see cref="QueryResult{ServerItem}"/>.</returns> /// <returns>The <see cref="QueryResult{ServerItem}"/>.</returns>
private QueryResult<ServerItem> GetLatest(BaseItem parent, InternalItemsQuery query, string itemType) private QueryResult<ServerItem> GetLatest(BaseItem parent, InternalItemsQuery query, BaseItemKind itemType)
{ {
query.OrderBy = Array.Empty<(string, SortOrder)>(); query.OrderBy = Array.Empty<(string, SortOrder)>();
@ -1086,7 +1078,7 @@ namespace Emby.Dlna.ContentDirectory
{ {
Recursive = true, Recursive = true,
ArtistIds = new[] { item.Id }, ArtistIds = new[] { item.Id },
IncludeItemTypes = new[] { nameof(MusicAlbum) }, IncludeItemTypes = new[] { BaseItemKind.MusicAlbum },
Limit = limit, Limit = limit,
StartIndex = startIndex, StartIndex = startIndex,
DtoOptions = GetDtoOptions(), DtoOptions = GetDtoOptions(),
@ -1115,8 +1107,8 @@ namespace Emby.Dlna.ContentDirectory
GenreIds = new[] { item.Id }, GenreIds = new[] { item.Id },
IncludeItemTypes = new[] IncludeItemTypes = new[]
{ {
nameof(Movie), BaseItemKind.Movie,
nameof(Series) BaseItemKind.Series
}, },
Limit = limit, Limit = limit,
StartIndex = startIndex, StartIndex = startIndex,
@ -1144,7 +1136,7 @@ namespace Emby.Dlna.ContentDirectory
{ {
Recursive = true, Recursive = true,
GenreIds = new[] { item.Id }, GenreIds = new[] { item.Id },
IncludeItemTypes = new[] { nameof(MusicAlbum) }, IncludeItemTypes = new[] { BaseItemKind.MusicAlbum },
Limit = limit, Limit = limit,
StartIndex = startIndex, StartIndex = startIndex,
DtoOptions = GetDtoOptions(), DtoOptions = GetDtoOptions(),

View File

@ -5,7 +5,6 @@ using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Text;
using System.Text.Json; using System.Text.Json;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;

View File

@ -14,6 +14,7 @@ namespace Emby.Naming.AudioBook
public class AudioBookListResolver public class AudioBookListResolver
{ {
private readonly NamingOptions _options; private readonly NamingOptions _options;
private readonly AudioBookResolver _audioBookResolver;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="AudioBookListResolver"/> class. /// Initializes a new instance of the <see cref="AudioBookListResolver"/> class.
@ -22,6 +23,7 @@ namespace Emby.Naming.AudioBook
public AudioBookListResolver(NamingOptions options) public AudioBookListResolver(NamingOptions options)
{ {
_options = options; _options = options;
_audioBookResolver = new AudioBookResolver(_options);
} }
/// <summary> /// <summary>
@ -31,21 +33,19 @@ namespace Emby.Naming.AudioBook
/// <returns>Returns IEnumerable of <see cref="AudioBookInfo"/>.</returns> /// <returns>Returns IEnumerable of <see cref="AudioBookInfo"/>.</returns>
public IEnumerable<AudioBookInfo> Resolve(IEnumerable<FileSystemMetadata> files) public IEnumerable<AudioBookInfo> Resolve(IEnumerable<FileSystemMetadata> files)
{ {
var audioBookResolver = new AudioBookResolver(_options);
// File with empty fullname will be sorted out here. // File with empty fullname will be sorted out here.
var audiobookFileInfos = files var audiobookFileInfos = files
.Select(i => audioBookResolver.Resolve(i.FullName)) .Select(i => _audioBookResolver.Resolve(i.FullName))
.OfType<AudioBookFileInfo>() .OfType<AudioBookFileInfo>()
.ToList(); .ToList();
var stackResult = new StackResolver(_options) var stackResult = StackResolver.ResolveAudioBooks(audiobookFileInfos);
.ResolveAudioBooks(audiobookFileInfos);
foreach (var stack in stackResult) foreach (var stack in stackResult)
{ {
var stackFiles = stack.Files var stackFiles = stack.Files
.Select(i => audioBookResolver.Resolve(i)) .Select(i => _audioBookResolver.Resolve(i))
.OfType<AudioBookFileInfo>() .OfType<AudioBookFileInfo>()
.ToList(); .ToList();

View File

@ -1,7 +1,7 @@
using System; using System;
using System.IO; using System.IO;
using System.Linq;
using Emby.Naming.Common; using Emby.Naming.Common;
using Jellyfin.Extensions;
namespace Emby.Naming.AudioBook namespace Emby.Naming.AudioBook
{ {
@ -37,7 +37,7 @@ namespace Emby.Naming.AudioBook
var extension = Path.GetExtension(path); var extension = Path.GetExtension(path);
// Check supported extensions // Check supported extensions
if (!_options.AudioFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase)) if (!_options.AudioFileExtensions.Contains(extension, StringComparison.OrdinalIgnoreCase))
{ {
return null; return null;
} }

View File

@ -1,6 +1,7 @@
#pragma warning disable CA1819 #pragma warning disable CA1819
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using Emby.Naming.Video; using Emby.Naming.Video;
@ -124,11 +125,11 @@ namespace Emby.Naming.Common
token: "DSR") token: "DSR")
}; };
VideoFileStackingExpressions = new[] VideoFileStackingRules = new[]
{ {
"(?<title>.*?)(?<volume>[ _.-]*(?:cd|dvd|p(?:ar)?t|dis[ck])[ _.-]*[0-9]+)(?<ignore>.*?)(?<extension>\\.[^.]+)$", new FileStackRule(@"^(?<filename>.*?)(?:(?<=[\]\)\}])|[ _.-]+)[\(\[]?(?<parttype>cd|dvd|part|pt|dis[ck])[ _.-]*(?<number>[0-9]+)[\)\]]?(?:\.[^.]+)?$", true),
"(?<title>.*?)(?<volume>[ _.-]*(?:cd|dvd|p(?:ar)?t|dis[ck])[ _.-]*[a-d])(?<ignore>.*?)(?<extension>\\.[^.]+)$", new FileStackRule(@"^(?<filename>.*?)(?:(?<=[\]\)\}])|[ _.-]+)[\(\[]?(?<parttype>cd|dvd|part|pt|dis[ck])[ _.-]*(?<number>[a-d])[\)\]]?(?:\.[^.]+)?$", false),
"(?<title>.*?)(?<volume>[ ._-]*[a-d])(?<ignore>.*?)(?<extension>\\.[^.]+)$" new FileStackRule(@"^(?<filename>.*?)(?:(?<=[\]\)\}])|[ _.-]?)(?<number>[a-d])(?:\.[^.]+)?$", false)
}; };
CleanDateTimes = new[] CleanDateTimes = new[]
@ -403,6 +404,12 @@ namespace Emby.Naming.Common
VideoExtraRules = new[] VideoExtraRules = new[]
{ {
new ExtraRule(
ExtraType.Trailer,
ExtraRuleType.DirectoryName,
"trailers",
MediaType.Video),
new ExtraRule( new ExtraRule(
ExtraType.Trailer, ExtraType.Trailer,
ExtraRuleType.Filename, ExtraRuleType.Filename,
@ -469,6 +476,12 @@ namespace Emby.Naming.Common
"theme", "theme",
MediaType.Audio), MediaType.Audio),
new ExtraRule(
ExtraType.ThemeSong,
ExtraRuleType.DirectoryName,
"theme-music",
MediaType.Audio),
new ExtraRule( new ExtraRule(
ExtraType.Scene, ExtraType.Scene,
ExtraRuleType.Suffix, ExtraRuleType.Suffix,
@ -563,7 +576,7 @@ namespace Emby.Naming.Common
ExtraType.Unknown, ExtraType.Unknown,
ExtraRuleType.DirectoryName, ExtraRuleType.DirectoryName,
"extras", "extras",
MediaType.Video), MediaType.Video)
}; };
Format3DRules = new[] Format3DRules = new[]
@ -675,9 +688,29 @@ namespace Emby.Naming.Common
.Distinct(StringComparer.OrdinalIgnoreCase) .Distinct(StringComparer.OrdinalIgnoreCase)
.ToArray(); .ToArray();
AllExtrasTypesFolderNames = new Dictionary<string, ExtraType>(StringComparer.OrdinalIgnoreCase)
{
["trailers"] = ExtraType.Trailer,
["theme-music"] = ExtraType.ThemeSong,
["backdrops"] = ExtraType.ThemeVideo,
["extras"] = ExtraType.Unknown,
["behind the scenes"] = ExtraType.BehindTheScenes,
["deleted scenes"] = ExtraType.DeletedScene,
["interviews"] = ExtraType.Interview,
["scenes"] = ExtraType.Scene,
["samples"] = ExtraType.Sample,
["shorts"] = ExtraType.Clip,
["featurettes"] = ExtraType.Clip
};
Compile(); Compile();
} }
/// <summary>
/// Gets or sets the folder name to extra types mapping.
/// </summary>
public Dictionary<string, ExtraType> AllExtrasTypesFolderNames { get; set; }
/// <summary> /// <summary>
/// Gets or sets list of audio file extensions. /// Gets or sets list of audio file extensions.
/// </summary> /// </summary>
@ -759,9 +792,9 @@ namespace Emby.Naming.Common
public Format3DRule[] Format3DRules { get; set; } public Format3DRule[] Format3DRules { get; set; }
/// <summary> /// <summary>
/// Gets or sets list of raw video file-stacking expressions strings. /// Gets the file stacking rules.
/// </summary> /// </summary>
public string[] VideoFileStackingExpressions { get; set; } public FileStackRule[] VideoFileStackingRules { get; }
/// <summary> /// <summary>
/// Gets or sets list of raw clean DateTimes regular expressions strings. /// Gets or sets list of raw clean DateTimes regular expressions strings.
@ -783,11 +816,6 @@ namespace Emby.Naming.Common
/// </summary> /// </summary>
public ExtraRule[] VideoExtraRules { get; set; } public ExtraRule[] VideoExtraRules { get; set; }
/// <summary>
/// Gets list of video file-stack regular expressions.
/// </summary>
public Regex[] VideoFileStackingRegexes { get; private set; } = Array.Empty<Regex>();
/// <summary> /// <summary>
/// Gets list of clean datetime regular expressions. /// Gets list of clean datetime regular expressions.
/// </summary> /// </summary>
@ -813,7 +841,6 @@ namespace Emby.Naming.Common
/// </summary> /// </summary>
public void Compile() public void Compile()
{ {
VideoFileStackingRegexes = VideoFileStackingExpressions.Select(Compile).ToArray();
CleanDateTimeRegexes = CleanDateTimes.Select(Compile).ToArray(); CleanDateTimeRegexes = CleanDateTimes.Select(Compile).ToArray();
CleanStringRegexes = CleanStrings.Select(Compile).ToArray(); CleanStringRegexes = CleanStrings.Select(Compile).ToArray();
EpisodeWithoutSeasonRegexes = EpisodeWithoutSeasonExpressions.Select(Compile).ToArray(); EpisodeWithoutSeasonRegexes = EpisodeWithoutSeasonExpressions.Select(Compile).ToArray();

View File

@ -2,6 +2,7 @@ using System;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using Emby.Naming.Common; using Emby.Naming.Common;
using Jellyfin.Extensions;
namespace Emby.Naming.Subtitles namespace Emby.Naming.Subtitles
{ {
@ -34,7 +35,7 @@ namespace Emby.Naming.Subtitles
} }
var extension = Path.GetExtension(path); var extension = Path.GetExtension(path);
if (!_options.SubtitleFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase)) if (!_options.SubtitleFileExtensions.Contains(extension, StringComparison.OrdinalIgnoreCase))
{ {
return null; return null;
} }
@ -42,11 +43,11 @@ namespace Emby.Naming.Subtitles
var flags = GetFlags(path); var flags = GetFlags(path);
var info = new SubtitleInfo( var info = new SubtitleInfo(
path, path,
_options.SubtitleDefaultFlags.Any(i => flags.Contains(i, StringComparer.OrdinalIgnoreCase)), _options.SubtitleDefaultFlags.Any(i => flags.Contains(i, StringComparison.OrdinalIgnoreCase)),
_options.SubtitleForcedFlags.Any(i => flags.Contains(i, StringComparer.OrdinalIgnoreCase))); _options.SubtitleForcedFlags.Any(i => flags.Contains(i, StringComparison.OrdinalIgnoreCase)));
var parts = flags.Where(i => !_options.SubtitleDefaultFlags.Contains(i, StringComparer.OrdinalIgnoreCase) var parts = flags.Where(i => !_options.SubtitleDefaultFlags.Contains(i, StringComparison.OrdinalIgnoreCase)
&& !_options.SubtitleForcedFlags.Contains(i, StringComparer.OrdinalIgnoreCase)) && !_options.SubtitleForcedFlags.Contains(i, StringComparison.OrdinalIgnoreCase))
.ToList(); .ToList();
// Should have a name, language and file extension // Should have a name, language and file extension

View File

@ -1,8 +1,8 @@
using System; using System;
using System.IO; using System.IO;
using System.Linq;
using Emby.Naming.Common; using Emby.Naming.Common;
using Emby.Naming.Video; using Emby.Naming.Video;
using Jellyfin.Extensions;
namespace Emby.Naming.TV namespace Emby.Naming.TV
{ {
@ -48,7 +48,7 @@ namespace Emby.Naming.TV
{ {
var extension = Path.GetExtension(path); var extension = Path.GetExtension(path);
// Check supported extensions // Check supported extensions
if (!_options.VideoFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase)) if (!_options.VideoFileExtensions.Contains(extension, StringComparison.OrdinalIgnoreCase))
{ {
// It's not supported. Check stub extensions // It's not supported. Check stub extensions
if (!StubResolver.TryResolveFile(path, _options, out stubType)) if (!StubResolver.TryResolveFile(path, _options, out stubType))

View File

@ -1,4 +1,3 @@
using System.Globalization;
using Emby.Naming.Common; using Emby.Naming.Common;
namespace Emby.Naming.TV namespace Emby.Naming.TV

View File

@ -1,4 +1,3 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;

View File

@ -1,5 +1,7 @@
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using Emby.Naming.Audio; using Emby.Naming.Audio;
using Emby.Naming.Common; using Emby.Naming.Common;
@ -9,46 +11,28 @@ namespace Emby.Naming.Video
/// <summary> /// <summary>
/// Resolve if file is extra for video. /// Resolve if file is extra for video.
/// </summary> /// </summary>
public class ExtraResolver public static class ExtraResolver
{ {
private static readonly char[] _digits = new[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }; private static readonly char[] _digits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
private readonly NamingOptions _options;
/// <summary>
/// Initializes a new instance of the <see cref="ExtraResolver"/> class.
/// </summary>
/// <param name="options"><see cref="NamingOptions"/> object containing VideoExtraRules and passed to <see cref="AudioFileParser"/> and <see cref="VideoResolver"/>.</param>
public ExtraResolver(NamingOptions options)
{
_options = options;
}
/// <summary> /// <summary>
/// Attempts to resolve if file is extra. /// Attempts to resolve if file is extra.
/// </summary> /// </summary>
/// <param name="path">Path to file.</param> /// <param name="path">Path to file.</param>
/// <param name="namingOptions">The naming options.</param>
/// <returns>Returns <see cref="ExtraResult"/> object.</returns> /// <returns>Returns <see cref="ExtraResult"/> object.</returns>
public ExtraResult GetExtraInfo(string path) public static ExtraResult GetExtraInfo(string path, NamingOptions namingOptions)
{ {
var result = new ExtraResult(); var result = new ExtraResult();
for (var i = 0; i < _options.VideoExtraRules.Length; i++) for (var i = 0; i < namingOptions.VideoExtraRules.Length; i++)
{ {
var rule = _options.VideoExtraRules[i]; var rule = namingOptions.VideoExtraRules[i];
if (rule.MediaType == MediaType.Audio) if ((rule.MediaType == MediaType.Audio && !AudioFileParser.IsAudioFile(path, namingOptions))
{ || (rule.MediaType == MediaType.Video && !VideoResolver.IsVideoFile(path, namingOptions)))
if (!AudioFileParser.IsAudioFile(path, _options))
{ {
continue; continue;
} }
}
else if (rule.MediaType == MediaType.Video)
{
if (!VideoResolver.IsVideoFile(path, _options))
{
continue;
}
}
var pathSpan = path.AsSpan(); var pathSpan = path.AsSpan();
if (rule.RuleType == ExtraRuleType.Filename) if (rule.RuleType == ExtraRuleType.Filename)
@ -76,9 +60,9 @@ namespace Emby.Naming.Video
{ {
var filename = Path.GetFileName(path); var filename = Path.GetFileName(path);
var regex = new Regex(rule.Token, RegexOptions.IgnoreCase); var isMatch = Regex.IsMatch(filename, rule.Token, RegexOptions.IgnoreCase | RegexOptions.Compiled);
if (regex.IsMatch(filename)) if (isMatch)
{ {
result.ExtraType = rule.ExtraType; result.ExtraType = rule.ExtraType;
result.Rule = rule; result.Rule = rule;
@ -102,5 +86,66 @@ namespace Emby.Naming.Video
return result; return result;
} }
/// <summary>
/// Finds extras matching the video info.
/// </summary>
/// <param name="files">The list of file video infos.</param>
/// <param name="videoInfo">The video to compare against.</param>
/// <param name="videoFlagDelimiters">The video flag delimiters.</param>
/// <returns>A list of video extras for [videoInfo].</returns>
public static IReadOnlyList<VideoFileInfo> GetExtras(IReadOnlyList<VideoInfo> files, VideoFileInfo videoInfo, ReadOnlySpan<char> videoFlagDelimiters)
{
var parentDir = videoInfo.IsDirectory ? videoInfo.Path : Path.GetDirectoryName(videoInfo.Path.AsSpan());
var trimmedFileNameWithoutExtension = TrimFilenameDelimiters(videoInfo.FileNameWithoutExtension, videoFlagDelimiters);
var trimmedVideoInfoName = TrimFilenameDelimiters(videoInfo.Name, videoFlagDelimiters);
var result = new List<VideoFileInfo>();
for (var pos = files.Count - 1; pos >= 0; pos--)
{
var current = files[pos];
// ignore non-extras and multi-file (can this happen?)
if (current.ExtraType == null || current.Files.Count > 1)
{
continue;
}
var currentFile = current.Files[0];
var trimmedCurrentFileName = TrimFilenameDelimiters(currentFile.Name, videoFlagDelimiters);
// first check filenames
bool isValid = StartsWith(trimmedCurrentFileName, trimmedFileNameWithoutExtension)
|| (StartsWith(trimmedCurrentFileName, trimmedVideoInfoName) && currentFile.Year == videoInfo.Year);
// then by directory
if (!isValid)
{
// When the extra rule type is DirectoryName we must go one level higher to get the "real" dir name
var currentParentDir = currentFile.ExtraRule?.RuleType == ExtraRuleType.DirectoryName
? Path.GetDirectoryName(Path.GetDirectoryName(currentFile.Path.AsSpan()))
: Path.GetDirectoryName(currentFile.Path.AsSpan());
isValid = !currentParentDir.IsEmpty && !parentDir.IsEmpty && currentParentDir.Equals(parentDir, StringComparison.OrdinalIgnoreCase);
}
if (isValid)
{
result.Add(currentFile);
}
}
return result.OrderBy(r => r.Path).ToArray();
}
private static ReadOnlySpan<char> TrimFilenameDelimiters(ReadOnlySpan<char> name, ReadOnlySpan<char> videoFlagDelimiters)
{
return name.IsEmpty ? name : name.TrimEnd().TrimEnd(videoFlagDelimiters).TrimEnd();
}
private static bool StartsWith(ReadOnlySpan<char> fileName, ReadOnlySpan<char> baseName)
{
return !baseName.IsEmpty && fileName.StartsWith(baseName, StringComparison.OrdinalIgnoreCase);
}
} }
} }

View File

@ -1,6 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using Jellyfin.Extensions;
namespace Emby.Naming.Video namespace Emby.Naming.Video
{ {
@ -12,25 +12,30 @@ namespace Emby.Naming.Video
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="FileStack"/> class. /// Initializes a new instance of the <see cref="FileStack"/> class.
/// </summary> /// </summary>
public FileStack() /// <param name="name">The stack name.</param>
/// <param name="isDirectory">Whether the stack files are directories.</param>
/// <param name="files">The stack files.</param>
public FileStack(string name, bool isDirectory, IReadOnlyList<string> files)
{ {
Files = new List<string>(); Name = name;
IsDirectoryStack = isDirectory;
Files = files;
} }
/// <summary> /// <summary>
/// Gets or sets name of file stack. /// Gets the name of file stack.
/// </summary> /// </summary>
public string Name { get; set; } = string.Empty; public string Name { get; }
/// <summary> /// <summary>
/// Gets or sets list of paths in stack. /// Gets the list of paths in stack.
/// </summary> /// </summary>
public List<string> Files { get; set; } public IReadOnlyList<string> Files { get; }
/// <summary> /// <summary>
/// Gets or sets a value indicating whether stack is directory stack. /// Gets a value indicating whether stack is directory stack.
/// </summary> /// </summary>
public bool IsDirectoryStack { get; set; } public bool IsDirectoryStack { get; }
/// <summary> /// <summary>
/// Helper function to determine if path is in the stack. /// Helper function to determine if path is in the stack.
@ -40,12 +45,12 @@ namespace Emby.Naming.Video
/// <returns>True if file is in the stack.</returns> /// <returns>True if file is in the stack.</returns>
public bool ContainsFile(string file, bool isDirectory) public bool ContainsFile(string file, bool isDirectory)
{ {
if (IsDirectoryStack == isDirectory) if (string.IsNullOrEmpty(file))
{ {
return Files.Contains(file, StringComparer.OrdinalIgnoreCase);
}
return false; return false;
} }
return IsDirectoryStack == isDirectory && Files.Contains(file, StringComparison.OrdinalIgnoreCase);
}
} }
} }

View File

@ -0,0 +1,48 @@
using System.Diagnostics.CodeAnalysis;
using System.Text.RegularExpressions;
namespace Emby.Naming.Video;
/// <summary>
/// Regex based rule for file stacking (eg. disc1, disc2).
/// </summary>
public class FileStackRule
{
private readonly Regex _tokenRegex;
/// <summary>
/// Initializes a new instance of the <see cref="FileStackRule"/> class.
/// </summary>
/// <param name="token">Token.</param>
/// <param name="isNumerical">Whether the file stack rule uses numerical or alphabetical numbering.</param>
public FileStackRule(string token, bool isNumerical)
{
_tokenRegex = new Regex(token, RegexOptions.IgnoreCase);
IsNumerical = isNumerical;
}
/// <summary>
/// Gets a value indicating whether the rule uses numerical or alphabetical numbering.
/// </summary>
public bool IsNumerical { get; }
/// <summary>
/// Match the input against the rule regex.
/// </summary>
/// <param name="input">The input.</param>
/// <param name="result">The part type and number or <c>null</c>.</param>
/// <returns>A value indicating whether the input matched the rule.</returns>
public bool Match(string input, [NotNullWhen(true)] out (string StackName, string PartType, string PartNumber)? result)
{
result = null;
var match = _tokenRegex.Match(input);
if (!match.Success)
{
return false;
}
var partType = match.Groups["parttype"].Success ? match.Groups["parttype"].Value : "unknown";
result = (match.Groups["filename"].Value, partType, match.Groups["number"].Value);
return true;
}
}

View File

@ -2,7 +2,6 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text.RegularExpressions;
using Emby.Naming.AudioBook; using Emby.Naming.AudioBook;
using Emby.Naming.Common; using Emby.Naming.Common;
using MediaBrowser.Model.IO; using MediaBrowser.Model.IO;
@ -12,37 +11,28 @@ namespace Emby.Naming.Video
/// <summary> /// <summary>
/// Resolve <see cref="FileStack"/> from list of paths. /// Resolve <see cref="FileStack"/> from list of paths.
/// </summary> /// </summary>
public class StackResolver public static class StackResolver
{ {
private readonly NamingOptions _options;
/// <summary>
/// Initializes a new instance of the <see cref="StackResolver"/> class.
/// </summary>
/// <param name="options"><see cref="NamingOptions"/> object containing VideoFileStackingRegexes and passes options to <see cref="VideoResolver"/>.</param>
public StackResolver(NamingOptions options)
{
_options = options;
}
/// <summary> /// <summary>
/// Resolves only directories from paths. /// Resolves only directories from paths.
/// </summary> /// </summary>
/// <param name="files">List of paths.</param> /// <param name="files">List of paths.</param>
/// <param name="namingOptions">The naming options.</param>
/// <returns>Enumerable <see cref="FileStack"/> of directories.</returns> /// <returns>Enumerable <see cref="FileStack"/> of directories.</returns>
public IEnumerable<FileStack> ResolveDirectories(IEnumerable<string> files) public static IEnumerable<FileStack> ResolveDirectories(IEnumerable<string> files, NamingOptions namingOptions)
{ {
return Resolve(files.Select(i => new FileSystemMetadata { FullName = i, IsDirectory = true })); return Resolve(files.Select(i => new FileSystemMetadata { FullName = i, IsDirectory = true }), namingOptions);
} }
/// <summary> /// <summary>
/// Resolves only files from paths. /// Resolves only files from paths.
/// </summary> /// </summary>
/// <param name="files">List of paths.</param> /// <param name="files">List of paths.</param>
/// <param name="namingOptions">The naming options.</param>
/// <returns>Enumerable <see cref="FileStack"/> of files.</returns> /// <returns>Enumerable <see cref="FileStack"/> of files.</returns>
public IEnumerable<FileStack> ResolveFiles(IEnumerable<string> files) public static IEnumerable<FileStack> ResolveFiles(IEnumerable<string> files, NamingOptions namingOptions)
{ {
return Resolve(files.Select(i => new FileSystemMetadata { FullName = i, IsDirectory = false })); return Resolve(files.Select(i => new FileSystemMetadata { FullName = i, IsDirectory = false }), namingOptions);
} }
/// <summary> /// <summary>
@ -50,7 +40,7 @@ namespace Emby.Naming.Video
/// </summary> /// </summary>
/// <param name="files">List of paths.</param> /// <param name="files">List of paths.</param>
/// <returns>Enumerable <see cref="FileStack"/> of directories.</returns> /// <returns>Enumerable <see cref="FileStack"/> of directories.</returns>
public IEnumerable<FileStack> ResolveAudioBooks(IEnumerable<AudioBookFileInfo> files) public static IEnumerable<FileStack> ResolveAudioBooks(IEnumerable<AudioBookFileInfo> files)
{ {
var groupedDirectoryFiles = files.GroupBy(file => Path.GetDirectoryName(file.Path)); var groupedDirectoryFiles = files.GroupBy(file => Path.GetDirectoryName(file.Path));
@ -60,19 +50,13 @@ namespace Emby.Naming.Video
{ {
foreach (var file in directory) foreach (var file in directory)
{ {
var stack = new FileStack { Name = Path.GetFileNameWithoutExtension(file.Path), IsDirectoryStack = false }; var stack = new FileStack(Path.GetFileNameWithoutExtension(file.Path), false, new[] { file.Path });
stack.Files.Add(file.Path);
yield return stack; yield return stack;
} }
} }
else else
{ {
var stack = new FileStack { Name = Path.GetFileName(directory.Key), IsDirectoryStack = false }; var stack = new FileStack(Path.GetFileName(directory.Key), false, directory.Select(f => f.Path).ToArray());
foreach (var file in directory)
{
stack.Files.Add(file.Path);
}
yield return stack; yield return stack;
} }
} }
@ -82,158 +66,91 @@ namespace Emby.Naming.Video
/// Resolves videos from paths. /// Resolves videos from paths.
/// </summary> /// </summary>
/// <param name="files">List of paths.</param> /// <param name="files">List of paths.</param>
/// <param name="namingOptions">The naming options.</param>
/// <returns>Enumerable <see cref="FileStack"/> of videos.</returns> /// <returns>Enumerable <see cref="FileStack"/> of videos.</returns>
public IEnumerable<FileStack> Resolve(IEnumerable<FileSystemMetadata> files) public static IEnumerable<FileStack> Resolve(IEnumerable<FileSystemMetadata> files, NamingOptions namingOptions)
{ {
var list = files var potentialFiles = files
.Where(i => i.IsDirectory || VideoResolver.IsVideoFile(i.FullName, _options) || VideoResolver.IsStubFile(i.FullName, _options)) .Where(i => i.IsDirectory || VideoResolver.IsVideoFile(i.FullName, namingOptions) || VideoResolver.IsStubFile(i.FullName, namingOptions))
.OrderBy(i => i.FullName) .OrderBy(i => i.FullName);
.ToList();
var expressions = _options.VideoFileStackingRegexes; var potentialStacks = new Dictionary<string, StackMetadata>();
foreach (var file in potentialFiles)
for (var i = 0; i < list.Count; i++)
{ {
var offset = 0; var name = file.Name;
if (string.IsNullOrEmpty(name))
var file1 = list[i];
var expressionIndex = 0;
while (expressionIndex < expressions.Length)
{ {
var exp = expressions[expressionIndex]; name = Path.GetFileName(file.FullName);
var stack = new FileStack(); }
// (Title)(Volume)(Ignore)(Extension) for (var i = 0; i < namingOptions.VideoFileStackingRules.Length; i++)
var match1 = FindMatch(file1, exp, offset);
if (match1.Success)
{ {
var title1 = match1.Groups["title"].Value; var rule = namingOptions.VideoFileStackingRules[i];
var volume1 = match1.Groups["volume"].Value; if (!rule.Match(name, out var stackParsingResult))
var ignore1 = match1.Groups["ignore"].Value;
var extension1 = match1.Groups["extension"].Value;
var j = i + 1;
while (j < list.Count)
{ {
var file2 = list[j];
if (file1.IsDirectory != file2.IsDirectory)
{
j++;
continue; continue;
} }
// (Title)(Volume)(Ignore)(Extension) var stackName = stackParsingResult.Value.StackName;
var match2 = FindMatch(file2, exp, offset); var partNumber = stackParsingResult.Value.PartNumber;
var partType = stackParsingResult.Value.PartType;
if (match2.Success) if (!potentialStacks.TryGetValue(stackName, out var stackResult))
{ {
var title2 = match2.Groups[1].Value; stackResult = new StackMetadata(file.IsDirectory, rule.IsNumerical, partType);
var volume2 = match2.Groups[2].Value; potentialStacks[stackName] = stackResult;
var ignore2 = match2.Groups[3].Value;
var extension2 = match2.Groups[4].Value;
if (string.Equals(title1, title2, StringComparison.OrdinalIgnoreCase))
{
if (!string.Equals(volume1, volume2, StringComparison.OrdinalIgnoreCase))
{
if (string.Equals(ignore1, ignore2, StringComparison.OrdinalIgnoreCase)
&& string.Equals(extension1, extension2, StringComparison.OrdinalIgnoreCase))
{
if (stack.Files.Count == 0)
{
stack.Name = title1 + ignore1;
stack.IsDirectoryStack = file1.IsDirectory;
stack.Files.Add(file1.FullName);
} }
stack.Files.Add(file2.FullName); if (stackResult.Parts.Count > 0)
} {
else if (stackResult.IsDirectory != file.IsDirectory
|| !string.Equals(partType, stackResult.PartType, StringComparison.OrdinalIgnoreCase)
|| stackResult.ContainsPart(partNumber))
{
continue;
}
if (rule.IsNumerical != stackResult.IsNumerical)
{ {
// Sequel
offset = 0;
expressionIndex++;
break; break;
} }
} }
else if (!string.Equals(ignore1, ignore2, StringComparison.OrdinalIgnoreCase))
{ stackResult.Parts.Add(partNumber, file);
// False positive, try again with offset
offset = match1.Groups[3].Index;
break;
}
else
{
// Extension mismatch
offset = 0;
expressionIndex++;
break; break;
} }
} }
else
foreach (var (fileName, stack) in potentialStacks)
{ {
// Title mismatch if (stack.Parts.Count < 2)
offset = 0;
expressionIndex++;
break;
}
}
else
{ {
// No match 2, next expression continue;
offset = 0;
expressionIndex++;
break;
} }
j++; yield return new FileStack(fileName, stack.IsDirectory, stack.Parts.Select(kv => kv.Value.FullName).ToArray());
}
if (j == list.Count)
{
expressionIndex = expressions.Length;
}
}
else
{
// No match 1
offset = 0;
expressionIndex++;
}
if (stack.Files.Count > 1)
{
yield return stack;
i += stack.Files.Count - 1;
break;
}
}
} }
} }
private static string GetRegexInput(FileSystemMetadata file) private class StackMetadata
{ {
// For directories, dummy up an extension otherwise the expressions will fail public StackMetadata(bool isDirectory, bool isNumerical, string partType)
var input = !file.IsDirectory
? file.FullName
: file.FullName + ".mkv";
return Path.GetFileName(input);
}
private static Match FindMatch(FileSystemMetadata input, Regex regex, int offset)
{ {
var regexInput = GetRegexInput(input); Parts = new Dictionary<string, FileSystemMetadata>(StringComparer.OrdinalIgnoreCase);
IsDirectory = isDirectory;
if (offset < 0 || offset >= regexInput.Length) IsNumerical = isNumerical;
{ PartType = partType;
return Match.Empty;
} }
return regex.Match(regexInput, offset); public Dictionary<string, FileSystemMetadata> Parts { get; }
public bool IsDirectory { get; }
public bool IsNumerical { get; }
public string PartType { get; }
public bool ContainsPart(string partNumber) => Parts.ContainsKey(partNumber);
} }
} }
} }

View File

@ -1,7 +1,7 @@
using System; using System;
using System.IO; using System.IO;
using System.Linq;
using Emby.Naming.Common; using Emby.Naming.Common;
using Jellyfin.Extensions;
namespace Emby.Naming.Video namespace Emby.Naming.Video
{ {
@ -28,7 +28,7 @@ namespace Emby.Naming.Video
var extension = Path.GetExtension(path); var extension = Path.GetExtension(path);
if (!options.StubFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase)) if (!options.StubFileExtensions.Contains(extension, StringComparison.OrdinalIgnoreCase))
{ {
return false; return false;
} }

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using MediaBrowser.Model.Entities;
namespace Emby.Naming.Video namespace Emby.Naming.Video
{ {
@ -17,7 +18,6 @@ namespace Emby.Naming.Video
Name = name; Name = name;
Files = Array.Empty<VideoFileInfo>(); Files = Array.Empty<VideoFileInfo>();
Extras = Array.Empty<VideoFileInfo>();
AlternateVersions = Array.Empty<VideoFileInfo>(); AlternateVersions = Array.Empty<VideoFileInfo>();
} }
@ -39,16 +39,15 @@ namespace Emby.Naming.Video
/// <value>The files.</value> /// <value>The files.</value>
public IReadOnlyList<VideoFileInfo> Files { get; set; } public IReadOnlyList<VideoFileInfo> Files { get; set; }
/// <summary>
/// Gets or sets the extras.
/// </summary>
/// <value>The extras.</value>
public IReadOnlyList<VideoFileInfo> Extras { get; set; }
/// <summary> /// <summary>
/// Gets or sets the alternate versions. /// Gets or sets the alternate versions.
/// </summary> /// </summary>
/// <value>The alternate versions.</value> /// <value>The alternate versions.</value>
public IReadOnlyList<VideoFileInfo> AlternateVersions { get; set; } public IReadOnlyList<VideoFileInfo> AlternateVersions { get; set; }
/// <summary>
/// Gets or sets the extra type.
/// </summary>
public ExtraType? ExtraType { get; set; }
} }
} }

View File

@ -4,7 +4,6 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using Emby.Naming.Common; using Emby.Naming.Common;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO; using MediaBrowser.Model.IO;
namespace Emby.Naming.Video namespace Emby.Naming.Video
@ -17,29 +16,38 @@ namespace Emby.Naming.Video
/// <summary> /// <summary>
/// Resolves alternative versions and extras from list of video files. /// Resolves alternative versions and extras from list of video files.
/// </summary> /// </summary>
/// <param name="files">List of related video files.</param> /// <param name="videoInfos">List of related video files.</param>
/// <param name="namingOptions">The naming options.</param> /// <param name="namingOptions">The naming options.</param>
/// <param name="supportMultiVersion">Indication we should consider multi-versions of content.</param> /// <param name="supportMultiVersion">Indication we should consider multi-versions of content.</param>
/// <param name="parseName">Whether to parse the name or use the filename.</param>
/// <returns>Returns enumerable of <see cref="VideoInfo"/> which groups files together when related.</returns> /// <returns>Returns enumerable of <see cref="VideoInfo"/> which groups files together when related.</returns>
public static IEnumerable<VideoInfo> Resolve(IEnumerable<FileSystemMetadata> files, NamingOptions namingOptions, bool supportMultiVersion = true) public static IReadOnlyList<VideoInfo> Resolve(IReadOnlyList<VideoFileInfo> videoInfos, NamingOptions namingOptions, bool supportMultiVersion = true, bool parseName = true)
{ {
var videoInfos = files
.Select(i => VideoResolver.Resolve(i.FullName, i.IsDirectory, namingOptions))
.OfType<VideoFileInfo>()
.ToList();
// Filter out all extras, otherwise they could cause stacks to not be resolved // Filter out all extras, otherwise they could cause stacks to not be resolved
// See the unit test TestStackedWithTrailer // See the unit test TestStackedWithTrailer
var nonExtras = videoInfos var nonExtras = videoInfos
.Where(i => i.ExtraType == null) .Where(i => i.ExtraType == null)
.Select(i => new FileSystemMetadata { FullName = i.Path, IsDirectory = i.IsDirectory }); .Select(i => new FileSystemMetadata { FullName = i.Path, IsDirectory = i.IsDirectory });
var stackResult = new StackResolver(namingOptions) var stackResult = StackResolver.Resolve(nonExtras, namingOptions).ToList();
.Resolve(nonExtras).ToList();
var remainingFiles = videoInfos var remainingFiles = new List<VideoFileInfo>();
.Where(i => !stackResult.Any(s => i.Path != null && s.ContainsFile(i.Path, i.IsDirectory))) var standaloneMedia = new List<VideoFileInfo>();
.ToList();
for (var i = 0; i < videoInfos.Count; i++)
{
var current = videoInfos[i];
if (stackResult.Any(s => s.ContainsFile(current.Path, current.IsDirectory)))
{
continue;
}
remainingFiles.Add(current);
if (current.ExtraType == null)
{
standaloneMedia.Add(current);
}
}
var list = new List<VideoInfo>(); var list = new List<VideoInfo>();
@ -47,27 +55,15 @@ namespace Emby.Naming.Video
{ {
var info = new VideoInfo(stack.Name) var info = new VideoInfo(stack.Name)
{ {
Files = stack.Files.Select(i => VideoResolver.Resolve(i, stack.IsDirectoryStack, namingOptions)) Files = stack.Files.Select(i => VideoResolver.Resolve(i, stack.IsDirectoryStack, namingOptions, parseName))
.OfType<VideoFileInfo>() .OfType<VideoFileInfo>()
.ToList() .ToList()
}; };
info.Year = info.Files[0].Year; info.Year = info.Files[0].Year;
var extras = ExtractExtras(remainingFiles, stack.Name, Path.GetFileNameWithoutExtension(stack.Files[0].AsSpan()), namingOptions.VideoFlagDelimiters);
if (extras.Count > 0)
{
info.Extras = extras;
}
list.Add(info); list.Add(info);
} }
var standaloneMedia = remainingFiles
.Where(i => i.ExtraType == null)
.ToList();
foreach (var media in standaloneMedia) foreach (var media in standaloneMedia)
{ {
var info = new VideoInfo(media.Name) { Files = new[] { media } }; var info = new VideoInfo(media.Name) { Files = new[] { media } };
@ -75,10 +71,6 @@ namespace Emby.Naming.Video
info.Year = info.Files[0].Year; info.Year = info.Files[0].Year;
remainingFiles.Remove(media); remainingFiles.Remove(media);
var extras = ExtractExtras(remainingFiles, media.FileNameWithoutExtension, namingOptions.VideoFlagDelimiters);
info.Extras = extras;
list.Add(info); list.Add(info);
} }
@ -87,58 +79,12 @@ namespace Emby.Naming.Video
list = GetVideosGroupedByVersion(list, namingOptions); list = GetVideosGroupedByVersion(list, namingOptions);
} }
// If there's only one resolved video, use the folder name as well to find extras
if (list.Count == 1)
{
var info = list[0];
var videoPath = list[0].Files[0].Path;
var parentPath = Path.GetDirectoryName(videoPath.AsSpan());
if (!parentPath.IsEmpty)
{
var folderName = Path.GetFileName(parentPath);
if (!folderName.IsEmpty)
{
var extras = ExtractExtras(remainingFiles, folderName, namingOptions.VideoFlagDelimiters);
extras.AddRange(info.Extras);
info.Extras = extras;
}
}
// Add the extras that are just based on file name as well
var extrasByFileName = remainingFiles
.Where(i => i.ExtraRule != null && i.ExtraRule.RuleType == ExtraRuleType.Filename)
.ToList();
remainingFiles = remainingFiles
.Except(extrasByFileName)
.ToList();
extrasByFileName.AddRange(info.Extras);
info.Extras = extrasByFileName;
}
// If there's only one video, accept all trailers
// Be lenient because people use all kinds of mishmash conventions with trailers.
if (list.Count == 1)
{
var trailers = remainingFiles
.Where(i => i.ExtraType == ExtraType.Trailer)
.ToList();
trailers.AddRange(list[0].Extras);
list[0].Extras = trailers;
remainingFiles = remainingFiles
.Except(trailers)
.ToList();
}
// Whatever files are left, just add them // Whatever files are left, just add them
list.AddRange(remainingFiles.Select(i => new VideoInfo(i.Name) list.AddRange(remainingFiles.Select(i => new VideoInfo(i.Name)
{ {
Files = new[] { i }, Files = new[] { i },
Year = i.Year Year = i.Year,
ExtraType = i.ExtraType
})); }));
return list; return list;
@ -162,6 +108,11 @@ namespace Emby.Naming.Video
for (var i = 0; i < videos.Count; i++) for (var i = 0; i < videos.Count; i++)
{ {
var video = videos[i]; var video = videos[i];
if (video.ExtraType != null)
{
continue;
}
if (!IsEligibleForMultiVersion(folderName, video.Files[0].Path, namingOptions)) if (!IsEligibleForMultiVersion(folderName, video.Files[0].Path, namingOptions))
{ {
return videos; return videos;
@ -178,17 +129,14 @@ namespace Emby.Naming.Video
var alternateVersionsLen = videos.Count - 1; var alternateVersionsLen = videos.Count - 1;
var alternateVersions = new VideoFileInfo[alternateVersionsLen]; var alternateVersions = new VideoFileInfo[alternateVersionsLen];
var extras = new List<VideoFileInfo>(list[0].Extras);
for (int i = 0; i < alternateVersionsLen; i++) for (int i = 0; i < alternateVersionsLen; i++)
{ {
var video = videos[i + 1]; var video = videos[i + 1];
alternateVersions[i] = video.Files[0]; alternateVersions[i] = video.Files[0];
extras.AddRange(video.Extras);
} }
list[0].AlternateVersions = alternateVersions; list[0].AlternateVersions = alternateVersions;
list[0].Name = folderName.ToString(); list[0].Name = folderName.ToString();
list[0].Extras = extras;
return list; return list;
} }
@ -230,7 +178,7 @@ namespace Emby.Naming.Video
var tmpTestFilename = testFilename.ToString(); var tmpTestFilename = testFilename.ToString();
if (CleanStringParser.TryClean(tmpTestFilename, namingOptions.CleanStringRegexes, out var cleanName)) if (CleanStringParser.TryClean(tmpTestFilename, namingOptions.CleanStringRegexes, out var cleanName))
{ {
tmpTestFilename = cleanName.Trim().ToString(); tmpTestFilename = cleanName.Trim();
} }
// The CleanStringParser should have removed common keywords etc. // The CleanStringParser should have removed common keywords etc.
@ -238,67 +186,5 @@ namespace Emby.Naming.Video
|| testFilename[0] == '-' || testFilename[0] == '-'
|| Regex.IsMatch(tmpTestFilename, @"^\[([^]]*)\]", RegexOptions.Compiled); || Regex.IsMatch(tmpTestFilename, @"^\[([^]]*)\]", RegexOptions.Compiled);
} }
private static ReadOnlySpan<char> TrimFilenameDelimiters(ReadOnlySpan<char> name, ReadOnlySpan<char> videoFlagDelimiters)
{
return name.IsEmpty ? name : name.TrimEnd().TrimEnd(videoFlagDelimiters).TrimEnd();
}
private static bool StartsWith(ReadOnlySpan<char> fileName, ReadOnlySpan<char> baseName, ReadOnlySpan<char> trimmedBaseName)
{
if (baseName.IsEmpty)
{
return false;
}
return fileName.StartsWith(baseName, StringComparison.OrdinalIgnoreCase)
|| (!trimmedBaseName.IsEmpty && fileName.StartsWith(trimmedBaseName, StringComparison.OrdinalIgnoreCase));
}
/// <summary>
/// Finds similar filenames to that of [baseName] and removes any matches from [remainingFiles].
/// </summary>
/// <param name="remainingFiles">The list of remaining filenames.</param>
/// <param name="baseName">The base name to use for the comparison.</param>
/// <param name="videoFlagDelimiters">The video flag delimiters.</param>
/// <returns>A list of video extras for [baseName].</returns>
private static List<VideoFileInfo> ExtractExtras(IList<VideoFileInfo> remainingFiles, ReadOnlySpan<char> baseName, ReadOnlySpan<char> videoFlagDelimiters)
{
return ExtractExtras(remainingFiles, baseName, ReadOnlySpan<char>.Empty, videoFlagDelimiters);
}
/// <summary>
/// Finds similar filenames to that of [firstBaseName] and [secondBaseName] and removes any matches from [remainingFiles].
/// </summary>
/// <param name="remainingFiles">The list of remaining filenames.</param>
/// <param name="firstBaseName">The first base name to use for the comparison.</param>
/// <param name="secondBaseName">The second base name to use for the comparison.</param>
/// <param name="videoFlagDelimiters">The video flag delimiters.</param>
/// <returns>A list of video extras for [firstBaseName] and [secondBaseName].</returns>
private static List<VideoFileInfo> ExtractExtras(IList<VideoFileInfo> remainingFiles, ReadOnlySpan<char> firstBaseName, ReadOnlySpan<char> secondBaseName, ReadOnlySpan<char> videoFlagDelimiters)
{
var trimmedFirstBaseName = TrimFilenameDelimiters(firstBaseName, videoFlagDelimiters);
var trimmedSecondBaseName = TrimFilenameDelimiters(secondBaseName, videoFlagDelimiters);
var result = new List<VideoFileInfo>();
for (var pos = remainingFiles.Count - 1; pos >= 0; pos--)
{
var file = remainingFiles[pos];
if (file.ExtraType == null)
{
continue;
}
var filename = file.FileNameWithoutExtension;
if (StartsWith(filename, firstBaseName, trimmedFirstBaseName)
|| StartsWith(filename, secondBaseName, trimmedSecondBaseName))
{
result.Add(file);
remainingFiles.RemoveAt(pos);
}
}
return result;
}
} }
} }

View File

@ -16,10 +16,11 @@ namespace Emby.Naming.Video
/// </summary> /// </summary>
/// <param name="path">The path.</param> /// <param name="path">The path.</param>
/// <param name="namingOptions">The naming options.</param> /// <param name="namingOptions">The naming options.</param>
/// <param name="parseName">Whether to parse the name or use the filename.</param>
/// <returns>VideoFileInfo.</returns> /// <returns>VideoFileInfo.</returns>
public static VideoFileInfo? ResolveDirectory(string? path, NamingOptions namingOptions) public static VideoFileInfo? ResolveDirectory(string? path, NamingOptions namingOptions, bool parseName = true)
{ {
return Resolve(path, true, namingOptions); return Resolve(path, true, namingOptions, parseName);
} }
/// <summary> /// <summary>
@ -74,7 +75,7 @@ namespace Emby.Naming.Video
var format3DResult = Format3DParser.Parse(path, namingOptions); var format3DResult = Format3DParser.Parse(path, namingOptions);
var extraResult = new ExtraResolver(namingOptions).GetExtraInfo(path); var extraResult = ExtraResolver.GetExtraInfo(path, namingOptions);
var name = Path.GetFileNameWithoutExtension(path); var name = Path.GetFileNameWithoutExtension(path);

View File

@ -5,6 +5,7 @@ using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Jellyfin.Data.Events; using Jellyfin.Data.Events;
using Jellyfin.Extensions;
using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller; using MediaBrowser.Controller;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
@ -104,7 +105,7 @@ namespace Emby.Notifications
var type = entry.Type; var type = entry.Type;
if (string.IsNullOrEmpty(type) || !_coreNotificationTypes.Contains(type, StringComparer.OrdinalIgnoreCase)) if (string.IsNullOrEmpty(type) || !_coreNotificationTypes.Contains(type, StringComparison.OrdinalIgnoreCase))
{ {
return; return;
} }

View File

@ -3,6 +3,7 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Jellyfin.Extensions;
using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
@ -60,7 +61,7 @@ namespace Emby.Photos
item.SetImagePath(ImageType.Primary, item.Path); item.SetImagePath(ImageType.Primary, item.Path);
// Examples: https://github.com/mono/taglib-sharp/blob/a5f6949a53d09ce63ee7495580d6802921a21f14/tests/fixtures/TagLib.Tests.Images/NullOrientationTest.cs // Examples: https://github.com/mono/taglib-sharp/blob/a5f6949a53d09ce63ee7495580d6802921a21f14/tests/fixtures/TagLib.Tests.Images/NullOrientationTest.cs
if (_includeExtensions.Contains(Path.GetExtension(item.Path), StringComparer.OrdinalIgnoreCase)) if (_includeExtensions.Contains(Path.GetExtension(item.Path), StringComparison.OrdinalIgnoreCase))
{ {
try try
{ {

View File

@ -301,7 +301,7 @@ namespace Emby.Server.Implementations.AppBase
{ {
return _configurations.GetOrAdd( return _configurations.GetOrAdd(
key, key,
(k, configurationManager) => static (k, configurationManager) =>
{ {
var file = configurationManager.GetConfigurationFile(k); var file = configurationManager.GetConfigurationFile(k);

View File

@ -1,6 +1,5 @@
using System; using System;
using System.IO; using System.IO;
using System.Linq;
using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Serialization;
namespace Emby.Server.Implementations.AppBase namespace Emby.Server.Implementations.AppBase

View File

@ -1,11 +1,8 @@
using System.IO; using System.IO;
using MediaBrowser.Model.IO; using MediaBrowser.Model.IO;
using SharpCompress.Archives.SevenZip;
using SharpCompress.Archives.Tar;
using SharpCompress.Common; using SharpCompress.Common;
using SharpCompress.Readers; using SharpCompress.Readers;
using SharpCompress.Readers.GZip; using SharpCompress.Readers.GZip;
using SharpCompress.Readers.Zip;
namespace Emby.Server.Implementations.Archiving namespace Emby.Server.Implementations.Archiving
{ {
@ -14,55 +11,6 @@ namespace Emby.Server.Implementations.Archiving
/// </summary> /// </summary>
public class ZipClient : IZipClient public class ZipClient : IZipClient
{ {
/// <summary>
/// Extracts all.
/// </summary>
/// <param name="sourceFile">The source file.</param>
/// <param name="targetPath">The target path.</param>
/// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param>
public void ExtractAll(string sourceFile, string targetPath, bool overwriteExistingFiles)
{
using var fileStream = File.OpenRead(sourceFile);
ExtractAll(fileStream, targetPath, overwriteExistingFiles);
}
/// <summary>
/// Extracts all.
/// </summary>
/// <param name="source">The source.</param>
/// <param name="targetPath">The target path.</param>
/// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param>
public void ExtractAll(Stream source, string targetPath, bool overwriteExistingFiles)
{
using var reader = ReaderFactory.Open(source);
var options = new ExtractionOptions
{
ExtractFullPath = true
};
if (overwriteExistingFiles)
{
options.Overwrite = true;
}
Directory.CreateDirectory(targetPath);
reader.WriteAllToDirectory(targetPath, options);
}
/// <inheritdoc />
public void ExtractAllFromZip(Stream source, string targetPath, bool overwriteExistingFiles)
{
using var reader = ZipReader.Open(source);
var options = new ExtractionOptions
{
ExtractFullPath = true,
Overwrite = overwriteExistingFiles
};
Directory.CreateDirectory(targetPath);
reader.WriteAllToDirectory(targetPath, options);
}
/// <inheritdoc /> /// <inheritdoc />
public void ExtractAllFromGz(Stream source, string targetPath, bool overwriteExistingFiles) public void ExtractAllFromGz(Stream source, string targetPath, bool overwriteExistingFiles)
{ {
@ -94,69 +42,5 @@ namespace Emby.Server.Implementations.Archiving
reader.WriteEntryToFile(Path.Combine(targetPath, filename)); reader.WriteEntryToFile(Path.Combine(targetPath, filename));
} }
} }
/// <summary>
/// Extracts all from7z.
/// </summary>
/// <param name="sourceFile">The source file.</param>
/// <param name="targetPath">The target path.</param>
/// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param>
public void ExtractAllFrom7z(string sourceFile, string targetPath, bool overwriteExistingFiles)
{
using var fileStream = File.OpenRead(sourceFile);
ExtractAllFrom7z(fileStream, targetPath, overwriteExistingFiles);
}
/// <summary>
/// Extracts all from7z.
/// </summary>
/// <param name="source">The source.</param>
/// <param name="targetPath">The target path.</param>
/// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param>
public void ExtractAllFrom7z(Stream source, string targetPath, bool overwriteExistingFiles)
{
using var archive = SevenZipArchive.Open(source);
using var reader = archive.ExtractAllEntries();
var options = new ExtractionOptions
{
ExtractFullPath = true,
Overwrite = overwriteExistingFiles
};
Directory.CreateDirectory(targetPath);
reader.WriteAllToDirectory(targetPath, options);
}
/// <summary>
/// Extracts all from tar.
/// </summary>
/// <param name="sourceFile">The source file.</param>
/// <param name="targetPath">The target path.</param>
/// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param>
public void ExtractAllFromTar(string sourceFile, string targetPath, bool overwriteExistingFiles)
{
using var fileStream = File.OpenRead(sourceFile);
ExtractAllFromTar(fileStream, targetPath, overwriteExistingFiles);
}
/// <summary>
/// Extracts all from tar.
/// </summary>
/// <param name="source">The source.</param>
/// <param name="targetPath">The target path.</param>
/// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param>
public void ExtractAllFromTar(Stream source, string targetPath, bool overwriteExistingFiles)
{
using var archive = TarArchive.Open(source);
using var reader = archive.ExtractAllEntries();
var options = new ExtractionOptions
{
ExtractFullPath = true,
Overwrite = overwriteExistingFiles
};
Directory.CreateDirectory(targetPath);
reader.WriteAllToDirectory(targetPath, options);
}
} }
} }

View File

@ -10,6 +10,7 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Jellyfin.Data.Entities; using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums; using Jellyfin.Data.Enums;
using Jellyfin.Extensions;
using Jellyfin.Extensions.Json; using Jellyfin.Extensions.Json;
using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Progress; using MediaBrowser.Common.Progress;
@ -179,7 +180,7 @@ namespace Emby.Server.Implementations.Channels
try try
{ {
return (GetChannelProvider(i) is IHasFolderAttributes hasAttributes return (GetChannelProvider(i) is IHasFolderAttributes hasAttributes
&& hasAttributes.Attributes.Contains("Recordings", StringComparer.OrdinalIgnoreCase)) == val; && hasAttributes.Attributes.Contains("Recordings", StringComparison.OrdinalIgnoreCase)) == val;
} }
catch catch
{ {
@ -541,7 +542,7 @@ namespace Emby.Server.Implementations.Channels
return _libraryManager.GetItemIds( return _libraryManager.GetItemIds(
new InternalItemsQuery new InternalItemsQuery
{ {
IncludeItemTypes = new[] { nameof(Channel) }, IncludeItemTypes = new[] { BaseItemKind.Channel },
OrderBy = new[] { (ItemSortBy.SortName, SortOrder.Ascending) } OrderBy = new[] { (ItemSortBy.SortName, SortOrder.Ascending) }
}).Select(i => GetChannelFeatures(i)).ToArray(); }).Select(i => GetChannelFeatures(i)).ToArray();
} }
@ -1135,7 +1136,7 @@ namespace Emby.Server.Implementations.Channels
if (!info.IsLiveStream) if (!info.IsLiveStream)
{ {
if (item.Tags.Contains("livestream", StringComparer.OrdinalIgnoreCase)) if (item.Tags.Contains("livestream", StringComparison.OrdinalIgnoreCase))
{ {
item.Tags = item.Tags.Except(new[] { "livestream" }, StringComparer.OrdinalIgnoreCase).ToArray(); item.Tags = item.Tags.Except(new[] { "livestream" }, StringComparer.OrdinalIgnoreCase).ToArray();
_logger.LogDebug("Forcing update due to Tags {0}", item.Name); _logger.LogDebug("Forcing update due to Tags {0}", item.Name);
@ -1144,7 +1145,7 @@ namespace Emby.Server.Implementations.Channels
} }
else else
{ {
if (!item.Tags.Contains("livestream", StringComparer.OrdinalIgnoreCase)) if (!item.Tags.Contains("livestream", StringComparison.OrdinalIgnoreCase))
{ {
item.Tags = item.Tags.Concat(new[] { "livestream" }).ToArray(); item.Tags = item.Tags.Concat(new[] { "livestream" }).ToArray();
_logger.LogDebug("Forcing update due to Tags {0}", item.Name); _logger.LogDebug("Forcing update due to Tags {0}", item.Name);

View File

@ -2,6 +2,7 @@ using System;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
@ -51,7 +52,7 @@ namespace Emby.Server.Implementations.Channels
var uninstalledChannels = _libraryManager.GetItemList(new InternalItemsQuery var uninstalledChannels = _libraryManager.GetItemList(new InternalItemsQuery
{ {
IncludeItemTypes = new[] { nameof(Channel) }, IncludeItemTypes = new[] { BaseItemKind.Channel },
ExcludeItemIds = installedChannelIds.ToArray() ExcludeItemIds = installedChannelIds.ToArray()
}); });

View File

@ -4,8 +4,8 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Threading; using System.Threading;
using Jellyfin.Extensions;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using SQLitePCL.pretty; using SQLitePCL.pretty;
@ -194,7 +194,7 @@ namespace Emby.Server.Implementations.Data
protected void AddColumn(IDatabaseConnection connection, string table, string columnName, string type, List<string> existingColumnNames) protected void AddColumn(IDatabaseConnection connection, string table, string columnName, string type, List<string> existingColumnNames)
{ {
if (existingColumnNames.Contains(columnName, StringComparer.OrdinalIgnoreCase)) if (existingColumnNames.Contains(columnName, StringComparison.OrdinalIgnoreCase))
{ {
return; return;
} }

View File

@ -62,7 +62,7 @@ namespace Emby.Server.Implementations.Data
private readonly ItemFields[] _allItemFields = Enum.GetValues<ItemFields>(); private readonly ItemFields[] _allItemFields = Enum.GetValues<ItemFields>();
private static readonly string[] _retriveItemColumns = private static readonly string[] _retrieveItemColumns =
{ {
"type", "type",
"data", "data",
@ -133,7 +133,7 @@ namespace Emby.Server.Implementations.Data
"OwnerId" "OwnerId"
}; };
private static readonly string _retriveItemColumnsSelectQuery = $"select {string.Join(',', _retriveItemColumns)} from TypedBaseItems where guid = @guid"; private static readonly string _retrieveItemColumnsSelectQuery = $"select {string.Join(',', _retrieveItemColumns)} from TypedBaseItems where guid = @guid";
private static readonly string[] _mediaStreamSaveColumns = private static readonly string[] _mediaStreamSaveColumns =
{ {
@ -196,57 +196,56 @@ namespace Emby.Server.Implementations.Data
private static readonly string _mediaAttachmentInsertPrefix; private static readonly string _mediaAttachmentInsertPrefix;
private static readonly HashSet<string> _programTypes = new HashSet<string>(StringComparer.OrdinalIgnoreCase) private static readonly BaseItemKind[] _programTypes = new[]
{ {
"Program", BaseItemKind.Program,
"TvChannel", BaseItemKind.TvChannel,
"LiveTvProgram", BaseItemKind.LiveTvProgram,
"LiveTvTvChannel" BaseItemKind.LiveTvChannel
}; };
private static readonly HashSet<string> _programExcludeParentTypes = new HashSet<string>(StringComparer.OrdinalIgnoreCase) private static readonly BaseItemKind[] _programExcludeParentTypes = new[]
{ {
"Series", BaseItemKind.Series,
"Season", BaseItemKind.Season,
"MusicAlbum", BaseItemKind.MusicAlbum,
"MusicArtist", BaseItemKind.MusicArtist,
"PhotoAlbum" BaseItemKind.PhotoAlbum
}; };
private static readonly HashSet<string> _serviceTypes = new HashSet<string>(StringComparer.OrdinalIgnoreCase) private static readonly BaseItemKind[] _serviceTypes = new[]
{ {
"TvChannel", BaseItemKind.TvChannel,
"LiveTvTvChannel" BaseItemKind.LiveTvChannel
}; };
private static readonly HashSet<string> _startDateTypes = new HashSet<string>(StringComparer.OrdinalIgnoreCase) private static readonly BaseItemKind[] _startDateTypes = new[]
{ {
"Program", BaseItemKind.Program,
"LiveTvProgram" BaseItemKind.LiveTvProgram
}; };
private static readonly HashSet<string> _seriesTypes = new HashSet<string>(StringComparer.OrdinalIgnoreCase) private static readonly BaseItemKind[] _seriesTypes = new[]
{ {
"Book", BaseItemKind.Book,
"AudioBook", BaseItemKind.AudioBook,
"Episode", BaseItemKind.Episode,
"Season" BaseItemKind.Season
}; };
private static readonly HashSet<string> _artistExcludeParentTypes = new HashSet<string>(StringComparer.OrdinalIgnoreCase) private static readonly BaseItemKind[] _artistExcludeParentTypes = new[]
{ {
"Series", BaseItemKind.Series,
"Season", BaseItemKind.Season,
"PhotoAlbum" BaseItemKind.PhotoAlbum
}; };
private static readonly HashSet<string> _artistsTypes = new HashSet<string>(StringComparer.OrdinalIgnoreCase) private static readonly BaseItemKind[] _artistsTypes = new[]
{ {
"Audio", BaseItemKind.Audio,
"MusicAlbum", BaseItemKind.MusicAlbum,
"MusicVideo", BaseItemKind.MusicVideo,
"AudioBook", BaseItemKind.AudioBook
"AudioPodcast"
}; };
private static readonly Type[] _knownTypes = private static readonly Type[] _knownTypes =
@ -285,6 +284,43 @@ namespace Emby.Server.Implementations.Data
private readonly Dictionary<string, string> _types = GetTypeMapDictionary(); private readonly Dictionary<string, string> _types = GetTypeMapDictionary();
private static readonly Dictionary<BaseItemKind, string> _baseItemKindNames = new()
{
{ BaseItemKind.AggregateFolder, typeof(AggregateFolder).FullName },
{ BaseItemKind.Audio, typeof(Audio).FullName },
{ BaseItemKind.AudioBook, typeof(AudioBook).FullName },
{ BaseItemKind.BasePluginFolder, typeof(BasePluginFolder).FullName },
{ BaseItemKind.Book, typeof(Book).FullName },
{ BaseItemKind.BoxSet, typeof(BoxSet).FullName },
{ BaseItemKind.Channel, typeof(Channel).FullName },
{ BaseItemKind.CollectionFolder, typeof(CollectionFolder).FullName },
{ BaseItemKind.Episode, typeof(Episode).FullName },
{ BaseItemKind.Folder, typeof(Folder).FullName },
{ BaseItemKind.Genre, typeof(Genre).FullName },
{ BaseItemKind.Movie, typeof(Movie).FullName },
{ BaseItemKind.LiveTvChannel, typeof(LiveTvChannel).FullName },
{ BaseItemKind.LiveTvProgram, typeof(LiveTvProgram).FullName },
{ BaseItemKind.MusicAlbum, typeof(MusicAlbum).FullName },
{ BaseItemKind.MusicArtist, typeof(MusicArtist).FullName },
{ BaseItemKind.MusicGenre, typeof(MusicGenre).FullName },
{ BaseItemKind.MusicVideo, typeof(MusicVideo).FullName },
{ BaseItemKind.Person, typeof(Person).FullName },
{ BaseItemKind.Photo, typeof(Photo).FullName },
{ BaseItemKind.PhotoAlbum, typeof(PhotoAlbum).FullName },
{ BaseItemKind.Playlist, typeof(Playlist).FullName },
{ BaseItemKind.PlaylistsFolder, typeof(PlaylistsFolder).FullName },
{ BaseItemKind.Season, typeof(Season).FullName },
{ BaseItemKind.Series, typeof(Series).FullName },
{ BaseItemKind.Studio, typeof(Studio).FullName },
{ BaseItemKind.Trailer, typeof(Trailer).FullName },
{ BaseItemKind.TvChannel, typeof(LiveTvChannel).FullName },
{ BaseItemKind.TvProgram, typeof(LiveTvProgram).FullName },
{ BaseItemKind.UserRootFolder, typeof(UserRootFolder).FullName },
{ BaseItemKind.UserView, typeof(UserView).FullName },
{ BaseItemKind.Video, typeof(Video).FullName },
{ BaseItemKind.Year, typeof(Year).FullName }
};
static SqliteItemRepository() static SqliteItemRepository()
{ {
var queryPrefixText = new StringBuilder(); var queryPrefixText = new StringBuilder();
@ -1320,7 +1356,7 @@ namespace Emby.Server.Implementations.Data
using (var connection = GetConnection(true)) using (var connection = GetConnection(true))
{ {
using (var statement = PrepareStatement(connection, _retriveItemColumnsSelectQuery)) using (var statement = PrepareStatement(connection, _retrieveItemColumnsSelectQuery))
{ {
statement.TryBind("@guid", id); statement.TryBind("@guid", id);
@ -2212,7 +2248,7 @@ namespace Emby.Server.Implementations.Data
private bool HasProgramAttributes(InternalItemsQuery query) private bool HasProgramAttributes(InternalItemsQuery query)
{ {
if (_programExcludeParentTypes.Contains(query.ParentType)) if (query.ParentType != null && _programExcludeParentTypes.Contains(query.ParentType.Value))
{ {
return false; return false;
} }
@ -2227,7 +2263,7 @@ namespace Emby.Server.Implementations.Data
private bool HasServiceName(InternalItemsQuery query) private bool HasServiceName(InternalItemsQuery query)
{ {
if (_programExcludeParentTypes.Contains(query.ParentType)) if (query.ParentType != null && _programExcludeParentTypes.Contains(query.ParentType.Value))
{ {
return false; return false;
} }
@ -2242,7 +2278,7 @@ namespace Emby.Server.Implementations.Data
private bool HasStartDate(InternalItemsQuery query) private bool HasStartDate(InternalItemsQuery query)
{ {
if (_programExcludeParentTypes.Contains(query.ParentType)) if (query.ParentType != null && _programExcludeParentTypes.Contains(query.ParentType.Value))
{ {
return false; return false;
} }
@ -2262,7 +2298,7 @@ namespace Emby.Server.Implementations.Data
return true; return true;
} }
return query.IncludeItemTypes.Contains("Episode", StringComparer.OrdinalIgnoreCase); return query.IncludeItemTypes.Contains(BaseItemKind.Episode);
} }
private bool HasTrailerTypes(InternalItemsQuery query) private bool HasTrailerTypes(InternalItemsQuery query)
@ -2272,12 +2308,12 @@ namespace Emby.Server.Implementations.Data
return true; return true;
} }
return query.IncludeItemTypes.Contains("Trailer", StringComparer.OrdinalIgnoreCase); return query.IncludeItemTypes.Contains(BaseItemKind.Trailer);
} }
private bool HasArtistFields(InternalItemsQuery query) private bool HasArtistFields(InternalItemsQuery query)
{ {
if (_artistExcludeParentTypes.Contains(query.ParentType)) if (query.ParentType != null && _artistExcludeParentTypes.Contains(query.ParentType.Value))
{ {
return false; return false;
} }
@ -2292,7 +2328,7 @@ namespace Emby.Server.Implementations.Data
private bool HasSeriesFields(InternalItemsQuery query) private bool HasSeriesFields(InternalItemsQuery query)
{ {
if (string.Equals(query.ParentType, "PhotoAlbum", StringComparison.OrdinalIgnoreCase)) if (query.ParentType == BaseItemKind.PhotoAlbum)
{ {
return false; return false;
} }
@ -2630,7 +2666,7 @@ namespace Emby.Server.Implementations.Data
query.Limit = query.Limit.Value + 4; query.Limit = query.Limit.Value + 4;
} }
var columns = _retriveItemColumns.ToList(); var columns = _retrieveItemColumns.ToList();
SetFinalColumnsToSelect(query, columns); SetFinalColumnsToSelect(query, columns);
var commandTextBuilder = new StringBuilder("select ", 1024) var commandTextBuilder = new StringBuilder("select ", 1024)
.AppendJoin(',', columns) .AppendJoin(',', columns)
@ -2821,7 +2857,7 @@ namespace Emby.Server.Implementations.Data
query.Limit = query.Limit.Value + 4; query.Limit = query.Limit.Value + 4;
} }
var columns = _retriveItemColumns.ToList(); var columns = _retrieveItemColumns.ToList();
SetFinalColumnsToSelect(query, columns); SetFinalColumnsToSelect(query, columns);
var commandTextBuilder = new StringBuilder("select ", 512) var commandTextBuilder = new StringBuilder("select ", 512)
.AppendJoin(',', columns) .AppendJoin(',', columns)
@ -3487,8 +3523,8 @@ namespace Emby.Server.Implementations.Data
if (query.IsMovie == true) if (query.IsMovie == true)
{ {
if (query.IncludeItemTypes.Length == 0 if (query.IncludeItemTypes.Length == 0
|| query.IncludeItemTypes.Contains(nameof(Movie)) || query.IncludeItemTypes.Contains(BaseItemKind.Movie)
|| query.IncludeItemTypes.Contains(nameof(Trailer))) || query.IncludeItemTypes.Contains(BaseItemKind.Trailer))
{ {
whereClauses.Add("(IsMovie is null OR IsMovie=@IsMovie)"); whereClauses.Add("(IsMovie is null OR IsMovie=@IsMovie)");
} }
@ -3563,31 +3599,81 @@ namespace Emby.Server.Implementations.Data
statement?.TryBind("@IsFolder", query.IsFolder); statement?.TryBind("@IsFolder", query.IsFolder);
} }
var includeTypes = query.IncludeItemTypes.Select(MapIncludeItemTypes).Where(x => x != null).ToArray(); var includeTypes = query.IncludeItemTypes;
// Only specify excluded types if no included types are specified // Only specify excluded types if no included types are specified
if (includeTypes.Length == 0) if (query.IncludeItemTypes.Length == 0)
{ {
var excludeTypes = query.ExcludeItemTypes.Select(MapIncludeItemTypes).Where(x => x != null).ToArray(); var excludeTypes = query.ExcludeItemTypes;
if (excludeTypes.Length == 1) if (excludeTypes.Length == 1)
{
if (_baseItemKindNames.TryGetValue(excludeTypes[0], out var excludeTypeName))
{ {
whereClauses.Add("type<>@type"); whereClauses.Add("type<>@type");
statement?.TryBind("@type", excludeTypes[0]); statement?.TryBind("@type", excludeTypeName);
}
else
{
Logger.LogWarning("Undefined BaseItemKind to Type mapping: {BaseItemKind}", excludeTypes[0]);
}
} }
else if (excludeTypes.Length > 1) else if (excludeTypes.Length > 1)
{ {
var inClause = string.Join(',', excludeTypes.Select(i => "'" + i + "'")); var whereBuilder = new StringBuilder("type not in (");
whereClauses.Add($"type not in ({inClause})"); foreach (var excludeType in excludeTypes)
{
if (_baseItemKindNames.TryGetValue(excludeType, out var baseItemKindName))
{
whereBuilder
.Append('\'')
.Append(baseItemKindName)
.Append("',");
}
else
{
Logger.LogWarning("Undefined BaseItemKind to Type mapping: {BaseItemKind}", excludeType);
}
}
// Remove trailing comma.
whereBuilder.Length--;
whereBuilder.Append(')');
whereClauses.Add(whereBuilder.ToString());
} }
} }
else if (includeTypes.Length == 1) else if (includeTypes.Length == 1)
{
if (_baseItemKindNames.TryGetValue(includeTypes[0], out var includeTypeName))
{ {
whereClauses.Add("type=@type"); whereClauses.Add("type=@type");
statement?.TryBind("@type", includeTypes[0]); statement?.TryBind("@type", includeTypeName);
}
else
{
Logger.LogWarning("Undefined BaseItemKind to Type mapping: {BaseItemKind}", includeTypes[0]);
}
} }
else if (includeTypes.Length > 1) else if (includeTypes.Length > 1)
{ {
var inClause = string.Join(',', includeTypes.Select(i => "'" + i + "'")); var whereBuilder = new StringBuilder("type in (");
whereClauses.Add($"type in ({inClause})"); foreach (var includeType in includeTypes)
{
if (_baseItemKindNames.TryGetValue(includeType, out var baseItemKindName))
{
whereBuilder
.Append('\'')
.Append(baseItemKindName)
.Append("',");
}
else
{
Logger.LogWarning("Undefined BaseItemKind to Type mapping: {BaseItemKind}", includeType);
}
}
// Remove trailing comma.
whereBuilder.Length--;
whereBuilder.Append(')');
whereClauses.Add(whereBuilder.ToString());
} }
if (query.ChannelIds.Count == 1) if (query.ChannelIds.Count == 1)
@ -3911,7 +3997,7 @@ namespace Emby.Server.Implementations.Data
if (query.IsPlayed.HasValue) if (query.IsPlayed.HasValue)
{ {
// We should probably figure this out for all folders, but for right now, this is the only place where we need it // We should probably figure this out for all folders, but for right now, this is the only place where we need it
if (query.IncludeItemTypes.Length == 1 && string.Equals(query.IncludeItemTypes[0], nameof(Series), StringComparison.OrdinalIgnoreCase)) if (query.IncludeItemTypes.Length == 1 && query.IncludeItemTypes[0] == BaseItemKind.Series)
{ {
if (query.IsPlayed.Value) if (query.IsPlayed.Value)
{ {
@ -4761,27 +4847,27 @@ namespace Emby.Server.Implementations.Data
{ {
var list = new List<string>(); var list = new List<string>();
if (IsTypeInQuery(nameof(Person), query)) if (IsTypeInQuery(BaseItemKind.Person, query))
{ {
list.Add(typeof(Person).FullName); list.Add(typeof(Person).FullName);
} }
if (IsTypeInQuery(nameof(Genre), query)) if (IsTypeInQuery(BaseItemKind.Genre, query))
{ {
list.Add(typeof(Genre).FullName); list.Add(typeof(Genre).FullName);
} }
if (IsTypeInQuery(nameof(MusicGenre), query)) if (IsTypeInQuery(BaseItemKind.MusicGenre, query))
{ {
list.Add(typeof(MusicGenre).FullName); list.Add(typeof(MusicGenre).FullName);
} }
if (IsTypeInQuery(nameof(MusicArtist), query)) if (IsTypeInQuery(BaseItemKind.MusicArtist, query))
{ {
list.Add(typeof(MusicArtist).FullName); list.Add(typeof(MusicArtist).FullName);
} }
if (IsTypeInQuery(nameof(Studio), query)) if (IsTypeInQuery(BaseItemKind.Studio, query))
{ {
list.Add(typeof(Studio).FullName); list.Add(typeof(Studio).FullName);
} }
@ -4789,14 +4875,14 @@ namespace Emby.Server.Implementations.Data
return list; return list;
} }
private bool IsTypeInQuery(string type, InternalItemsQuery query) private bool IsTypeInQuery(BaseItemKind type, InternalItemsQuery query)
{ {
if (query.ExcludeItemTypes.Contains(type, StringComparer.OrdinalIgnoreCase)) if (query.ExcludeItemTypes.Contains(type))
{ {
return false; return false;
} }
return query.IncludeItemTypes.Length == 0 || query.IncludeItemTypes.Contains(type, StringComparer.OrdinalIgnoreCase); return query.IncludeItemTypes.Length == 0 || query.IncludeItemTypes.Contains(type);
} }
private string GetCleanValue(string value) private string GetCleanValue(string value)
@ -4836,12 +4922,12 @@ namespace Emby.Server.Implementations.Data
return true; return true;
} }
if (query.IncludeItemTypes.Contains(nameof(Episode), StringComparer.OrdinalIgnoreCase) if (query.IncludeItemTypes.Contains(BaseItemKind.Episode)
|| query.IncludeItemTypes.Contains(nameof(Video), StringComparer.OrdinalIgnoreCase) || query.IncludeItemTypes.Contains(BaseItemKind.Video)
|| query.IncludeItemTypes.Contains(nameof(Movie), StringComparer.OrdinalIgnoreCase) || query.IncludeItemTypes.Contains(BaseItemKind.Movie)
|| query.IncludeItemTypes.Contains(nameof(MusicVideo), StringComparer.OrdinalIgnoreCase) || query.IncludeItemTypes.Contains(BaseItemKind.MusicVideo)
|| query.IncludeItemTypes.Contains(nameof(Series), StringComparer.OrdinalIgnoreCase) || query.IncludeItemTypes.Contains(BaseItemKind.Series)
|| query.IncludeItemTypes.Contains(nameof(Season), StringComparer.OrdinalIgnoreCase)) || query.IncludeItemTypes.Contains(BaseItemKind.Season))
{ {
return true; return true;
} }
@ -4890,22 +4976,6 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
return dict; return dict;
} }
private string MapIncludeItemTypes(string value)
{
if (_types.TryGetValue(value, out string result))
{
return result;
}
if (IsValidType(value))
{
return value;
}
Logger.LogWarning("Unknown item type: {ItemType}", value);
return null;
}
public void DeleteItem(Guid id) public void DeleteItem(Guid id)
{ {
if (id == Guid.Empty) if (id == Guid.Empty)
@ -5351,7 +5421,7 @@ AND Type = @InternalPersonType)");
stringBuilder.Clear(); stringBuilder.Clear();
} }
List<string> columns = _retriveItemColumns.ToList(); List<string> columns = _retrieveItemColumns.ToList();
// Unfortunately we need to add it to columns to ensure the order of the columns in the select // Unfortunately we need to add it to columns to ensure the order of the columns in the select
if (!string.IsNullOrEmpty(itemCountColumns)) if (!string.IsNullOrEmpty(itemCountColumns))
{ {
@ -5569,7 +5639,7 @@ AND Type = @InternalPersonType)");
return result; return result;
} }
private static ItemCounts GetItemCounts(IReadOnlyList<ResultSetValue> reader, int countStartColumn, string[] typesToCount) private static ItemCounts GetItemCounts(IReadOnlyList<ResultSetValue> reader, int countStartColumn, BaseItemKind[] typesToCount)
{ {
var counts = new ItemCounts(); var counts = new ItemCounts();

View File

@ -7,9 +7,9 @@ using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using Jellyfin.Data.Entities; using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums; using Jellyfin.Data.Enums;
using Jellyfin.Extensions;
using MediaBrowser.Common; using MediaBrowser.Common;
using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Drawing;
@ -294,7 +294,7 @@ namespace Emby.Server.Implementations.Dto
path = path.TrimStart('.'); path = path.TrimStart('.');
} }
if (!string.IsNullOrEmpty(path) && containers.Contains(path, StringComparer.OrdinalIgnoreCase)) if (!string.IsNullOrEmpty(path) && containers.Contains(path, StringComparison.OrdinalIgnoreCase))
{ {
fileExtensionContainer = path; fileExtensionContainer = path;
} }
@ -470,7 +470,7 @@ namespace Emby.Server.Implementations.Dto
{ {
var parentAlbumIds = _libraryManager.GetItemIds(new InternalItemsQuery var parentAlbumIds = _libraryManager.GetItemIds(new InternalItemsQuery
{ {
IncludeItemTypes = new[] { nameof(MusicAlbum) }, IncludeItemTypes = new[] { BaseItemKind.MusicAlbum },
Name = item.Album, Name = item.Album,
Limit = 1 Limit = 1
}); });

View File

@ -29,7 +29,7 @@
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="6.0.0" /> <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="6.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="6.0.0" /> <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="6.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="6.0.0" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="6.0.1" />
<PackageReference Include="Mono.Nat" Version="3.0.2" /> <PackageReference Include="Mono.Nat" Version="3.0.2" />
<PackageReference Include="prometheus-net.DotNetRuntime" Version="4.2.2" /> <PackageReference Include="prometheus-net.DotNetRuntime" Version="4.2.2" />
<PackageReference Include="sharpcompress" Version="0.30.1" /> <PackageReference Include="sharpcompress" Version="0.30.1" />

View File

@ -13,7 +13,6 @@ using Jellyfin.Networking.Configuration;
using MediaBrowser.Controller; using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Plugins; using MediaBrowser.Controller.Plugins;
using MediaBrowser.Model.Dlna;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Mono.Nat; using Mono.Nat;

View File

@ -2,7 +2,6 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using Jellyfin.Data.Enums; using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Authentication;
using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Net;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;

View File

@ -28,35 +28,35 @@ namespace Emby.Server.Implementations.Images
var view = (CollectionFolder)item; var view = (CollectionFolder)item;
var viewType = view.CollectionType; var viewType = view.CollectionType;
string[] includeItemTypes; BaseItemKind[] includeItemTypes;
if (string.Equals(viewType, CollectionType.Movies, StringComparison.Ordinal)) if (string.Equals(viewType, CollectionType.Movies, StringComparison.Ordinal))
{ {
includeItemTypes = new string[] { "Movie" }; includeItemTypes = new[] { BaseItemKind.Movie };
} }
else if (string.Equals(viewType, CollectionType.TvShows, StringComparison.Ordinal)) else if (string.Equals(viewType, CollectionType.TvShows, StringComparison.Ordinal))
{ {
includeItemTypes = new string[] { "Series" }; includeItemTypes = new[] { BaseItemKind.Series };
} }
else if (string.Equals(viewType, CollectionType.Music, StringComparison.Ordinal)) else if (string.Equals(viewType, CollectionType.Music, StringComparison.Ordinal))
{ {
includeItemTypes = new string[] { "MusicAlbum" }; includeItemTypes = new[] { BaseItemKind.MusicAlbum };
} }
else if (string.Equals(viewType, CollectionType.Books, StringComparison.Ordinal)) else if (string.Equals(viewType, CollectionType.Books, StringComparison.Ordinal))
{ {
includeItemTypes = new string[] { "Book", "AudioBook" }; includeItemTypes = new[] { BaseItemKind.Book, BaseItemKind.AudioBook };
} }
else if (string.Equals(viewType, CollectionType.BoxSets, StringComparison.Ordinal)) else if (string.Equals(viewType, CollectionType.BoxSets, StringComparison.Ordinal))
{ {
includeItemTypes = new string[] { "BoxSet" }; includeItemTypes = new[] { BaseItemKind.BoxSet };
} }
else if (string.Equals(viewType, CollectionType.HomeVideos, StringComparison.Ordinal) || string.Equals(viewType, CollectionType.Photos, StringComparison.Ordinal)) else if (string.Equals(viewType, CollectionType.HomeVideos, StringComparison.Ordinal) || string.Equals(viewType, CollectionType.Photos, StringComparison.Ordinal))
{ {
includeItemTypes = new string[] { "Video", "Photo" }; includeItemTypes = new[] { BaseItemKind.Video, BaseItemKind.Photo };
} }
else else
{ {
includeItemTypes = new string[] { "Video", "Audio", "Photo", "Movie", "Series" }; includeItemTypes = new[] { BaseItemKind.Video, BaseItemKind.Audio, BaseItemKind.Photo, BaseItemKind.Movie, BaseItemKind.Series };
} }
var recursive = !string.Equals(CollectionType.Playlists, viewType, StringComparison.OrdinalIgnoreCase); var recursive = !string.Equals(CollectionType.Playlists, viewType, StringComparison.OrdinalIgnoreCase);

View File

@ -6,6 +6,8 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using Jellyfin.Data.Enums;
using Jellyfin.Extensions;
using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Dto;
@ -34,14 +36,14 @@ namespace Emby.Server.Implementations.Images
var view = (UserView)item; var view = (UserView)item;
var isUsingCollectionStrip = IsUsingCollectionStrip(view); var isUsingCollectionStrip = IsUsingCollectionStrip(view);
var recursive = isUsingCollectionStrip && !new[] { CollectionType.BoxSets, CollectionType.Playlists }.Contains(view.ViewType ?? string.Empty, StringComparer.OrdinalIgnoreCase); var recursive = isUsingCollectionStrip && !new[] { CollectionType.BoxSets, CollectionType.Playlists }.Contains(view.ViewType ?? string.Empty, StringComparison.OrdinalIgnoreCase);
var result = view.GetItemList(new InternalItemsQuery var result = view.GetItemList(new InternalItemsQuery
{ {
User = view.UserId.HasValue ? _userManager.GetUserById(view.UserId.Value) : null, User = view.UserId.HasValue ? _userManager.GetUserById(view.UserId.Value) : null,
CollapseBoxSetItems = false, CollapseBoxSetItems = false,
Recursive = recursive, Recursive = recursive,
ExcludeItemTypes = new[] { "UserView", "CollectionFolder", "Person" }, ExcludeItemTypes = new[] { BaseItemKind.UserView, BaseItemKind.CollectionFolder, BaseItemKind.Person },
DtoOptions = new DtoOptions(false) DtoOptions = new DtoOptions(false)
}); });

View File

@ -8,8 +8,6 @@ using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
@ -43,7 +41,7 @@ namespace Emby.Server.Implementations.Images
return _libraryManager.GetItemList(new InternalItemsQuery return _libraryManager.GetItemList(new InternalItemsQuery
{ {
Genres = new[] { item.Name }, Genres = new[] { item.Name },
IncludeItemTypes = new[] { nameof(Series), nameof(Movie) }, IncludeItemTypes = new[] { BaseItemKind.Series, BaseItemKind.Movie },
OrderBy = new[] { (ItemSortBy.Random, SortOrder.Ascending) }, OrderBy = new[] { (ItemSortBy.Random, SortOrder.Ascending) },
Limit = 4, Limit = 4,
Recursive = true, Recursive = true,

View File

@ -44,9 +44,9 @@ namespace Emby.Server.Implementations.Images
Genres = new[] { item.Name }, Genres = new[] { item.Name },
IncludeItemTypes = new[] IncludeItemTypes = new[]
{ {
nameof(MusicAlbum), BaseItemKind.MusicAlbum,
nameof(MusicVideo), BaseItemKind.MusicVideo,
nameof(Audio) BaseItemKind.Audio
}, },
OrderBy = new[] { (ItemSortBy.Random, SortOrder.Ascending) }, OrderBy = new[] { (ItemSortBy.Random, SortOrder.Ascending) },
Limit = 4, Limit = 4,

View File

@ -54,20 +54,10 @@ namespace Emby.Server.Implementations.Library
{ {
if (parent != null) if (parent != null)
{ {
// Ignore trailer folders but allow it at the collection level // Ignore extras folders but allow it at the collection level
if (string.Equals(filename, BaseItem.TrailersFolderName, StringComparison.OrdinalIgnoreCase) if (_namingOptions.AllExtrasTypesFolderNames.ContainsKey(filename)
&& !(parent is AggregateFolder) && parent is not AggregateFolder
&& !(parent is UserRootFolder)) && parent is not UserRootFolder)
{
return true;
}
if (string.Equals(filename, BaseItem.ThemeVideosFolderName, StringComparison.OrdinalIgnoreCase))
{
return true;
}
if (string.Equals(filename, BaseItem.ThemeSongsFolderName, StringComparison.OrdinalIgnoreCase))
{ {
return true; return true;
} }

View File

@ -11,11 +11,9 @@ using System.Net;
using System.Net.Http; using System.Net.Http;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Emby.Naming.Audio;
using Emby.Naming.Common; using Emby.Naming.Common;
using Emby.Naming.TV; using Emby.Naming.TV;
using Emby.Naming.Video; using Emby.Naming.Video;
using Emby.Server.Implementations.Library.Resolvers;
using Emby.Server.Implementations.Library.Validators; using Emby.Server.Implementations.Library.Validators;
using Emby.Server.Implementations.Playlists; using Emby.Server.Implementations.Playlists;
using Emby.Server.Implementations.ScheduledTasks; using Emby.Server.Implementations.ScheduledTasks;
@ -533,8 +531,8 @@ namespace Emby.Server.Implementations.Library
return key.GetMD5(); return key.GetMD5();
} }
public BaseItem ResolvePath(FileSystemMetadata fileInfo, Folder parent = null) public BaseItem ResolvePath(FileSystemMetadata fileInfo, Folder parent = null, IDirectoryService directoryService = null)
=> ResolvePath(fileInfo, new DirectoryService(_fileSystem), null, parent); => ResolvePath(fileInfo, directoryService ?? new DirectoryService(_fileSystem), null, parent);
private BaseItem ResolvePath( private BaseItem ResolvePath(
FileSystemMetadata fileInfo, FileSystemMetadata fileInfo,
@ -654,7 +652,7 @@ namespace Emby.Server.Implementations.Library
return !args.ContainsFileSystemEntryByName(".ignore"); return !args.ContainsFileSystemEntryByName(".ignore");
} }
public IEnumerable<BaseItem> ResolvePaths(IEnumerable<FileSystemMetadata> files, IDirectoryService directoryService, Folder parent, LibraryOptions libraryOptions, string collectionType) public IEnumerable<BaseItem> ResolvePaths(IEnumerable<FileSystemMetadata> files, IDirectoryService directoryService, Folder parent, LibraryOptions libraryOptions, string collectionType = null)
{ {
return ResolvePaths(files, directoryService, parent, libraryOptions, collectionType, EntityResolvers); return ResolvePaths(files, directoryService, parent, libraryOptions, collectionType, EntityResolvers);
} }
@ -677,7 +675,7 @@ namespace Emby.Server.Implementations.Library
{ {
var result = resolver.ResolveMultiple(parent, fileList, collectionType, directoryService); var result = resolver.ResolveMultiple(parent, fileList, collectionType, directoryService);
if (result != null && result.Items.Count > 0) if (result?.Items.Count > 0)
{ {
var items = new List<BaseItem>(); var items = new List<BaseItem>();
items.AddRange(result.Items); items.AddRange(result.Items);
@ -965,7 +963,7 @@ namespace Emby.Server.Implementations.Library
{ {
var existing = GetItemList(new InternalItemsQuery var existing = GetItemList(new InternalItemsQuery
{ {
IncludeItemTypes = new[] { nameof(MusicArtist) }, IncludeItemTypes = new[] { BaseItemKind.MusicArtist },
Name = name, Name = name,
DtoOptions = options DtoOptions = options
}).Cast<MusicArtist>() }).Cast<MusicArtist>()
@ -2685,89 +2683,105 @@ namespace Emby.Server.Implementations.Library
}; };
} }
public IEnumerable<Video> FindTrailers(BaseItem owner, List<FileSystemMetadata> fileSystemChildren, IDirectoryService directoryService) public IEnumerable<BaseItem> FindExtras(BaseItem owner, List<FileSystemMetadata> fileSystemChildren, IDirectoryService directoryService)
{ {
var namingOptions = _namingOptions; var ownerVideoInfo = VideoResolver.Resolve(owner.Path, owner.IsFolder, _namingOptions);
if (ownerVideoInfo == null)
var files = owner.IsInMixedFolder ? new List<FileSystemMetadata>() : fileSystemChildren.Where(i => i.IsDirectory)
.Where(i => string.Equals(i.Name, BaseItem.TrailersFolderName, StringComparison.OrdinalIgnoreCase))
.SelectMany(i => _fileSystem.GetFiles(i.FullName, namingOptions.VideoFileExtensions, false, false))
.ToList();
var videos = VideoListResolver.Resolve(fileSystemChildren, namingOptions);
var currentVideo = videos.FirstOrDefault(i => string.Equals(owner.Path, i.Files[0].Path, StringComparison.OrdinalIgnoreCase));
if (currentVideo != null)
{ {
files.AddRange(currentVideo.Extras.Where(i => i.ExtraType == ExtraType.Trailer).Select(i => _fileSystem.GetFileInfo(i.Path))); yield break;
} }
var resolvers = new IItemResolver[] var count = fileSystemChildren.Count;
var files = new List<VideoFileInfo>();
var nonVideoFiles = new List<FileSystemMetadata>();
for (var i = 0; i < count; i++)
{ {
new GenericVideoResolver<Trailer>(_namingOptions) var current = fileSystemChildren[i];
}; if (current.IsDirectory && _namingOptions.AllExtrasTypesFolderNames.ContainsKey(current.Name))
{
var filesInSubFolder = _fileSystem.GetFiles(current.FullName, _namingOptions.VideoFileExtensions, false, false);
foreach (var file in filesInSubFolder)
{
var videoInfo = VideoResolver.Resolve(file.FullName, file.IsDirectory, _namingOptions);
if (videoInfo == null)
{
nonVideoFiles.Add(file);
continue;
}
return ResolvePaths(files, directoryService, null, new LibraryOptions(), null, resolvers) files.Add(videoInfo);
.OfType<Trailer>() }
.Select(video => }
else if (!current.IsDirectory)
{ {
var videoInfo = VideoResolver.Resolve(current.FullName, current.IsDirectory, _namingOptions);
if (videoInfo == null)
{
nonVideoFiles.Add(current);
continue;
}
files.Add(videoInfo);
}
}
if (files.Count == 0)
{
yield break;
}
var videos = VideoListResolver.Resolve(files, _namingOptions);
// owner video info cannot be null as that implies it has no path
var extras = ExtraResolver.GetExtras(videos, ownerVideoInfo, _namingOptions.VideoFlagDelimiters);
for (var i = 0; i < extras.Count; i++)
{
var currentExtra = extras[i];
var resolved = ResolvePath(_fileSystem.GetFileInfo(currentExtra.Path), null, directoryService);
if (resolved is not Video video)
{
continue;
}
// Try to retrieve it from the db. If we don't find it, use the resolved version // Try to retrieve it from the db. If we don't find it, use the resolved version
if (GetItemById(video.Id) is Trailer dbItem) if (GetItemById(resolved.Id) is Video dbItem)
{ {
video = dbItem; video = dbItem;
} }
video.ExtraType = currentExtra.ExtraType;
video.ParentId = Guid.Empty; video.ParentId = Guid.Empty;
video.OwnerId = owner.Id; video.OwnerId = owner.Id;
video.ExtraType = ExtraType.Trailer; yield return video;
video.TrailerTypes = new[] { TrailerType.LocalTrailer };
return video;
// Sort them so that the list can be easily compared for changes
}).OrderBy(i => i.Path);
} }
public IEnumerable<Video> FindExtras(BaseItem owner, List<FileSystemMetadata> fileSystemChildren, IDirectoryService directoryService) // TODO: theme songs must be handled "manually" (but should we?) since they aren't video files
for (var i = 0; i < nonVideoFiles.Count; i++)
{ {
var namingOptions = _namingOptions; var current = nonVideoFiles[i];
var extraInfo = ExtraResolver.GetExtraInfo(current.FullName, _namingOptions);
var files = owner.IsInMixedFolder ? new List<FileSystemMetadata>() : fileSystemChildren.Where(i => i.IsDirectory) if (extraInfo.ExtraType != ExtraType.ThemeSong)
.Where(i => BaseItem.AllExtrasTypesFolderNames.ContainsKey(i.Name ?? string.Empty))
.SelectMany(i => _fileSystem.GetFiles(i.FullName, namingOptions.VideoFileExtensions, false, false))
.ToList();
var videos = VideoListResolver.Resolve(fileSystemChildren, namingOptions);
var currentVideo = videos.FirstOrDefault(i => string.Equals(owner.Path, i.Files[0].Path, StringComparison.OrdinalIgnoreCase));
if (currentVideo != null)
{ {
files.AddRange(currentVideo.Extras.Where(i => i.ExtraType != ExtraType.Trailer).Select(i => _fileSystem.GetFileInfo(i.Path))); continue;
} }
return ResolvePaths(files, directoryService, null, new LibraryOptions(), null) var resolved = ResolvePath(current, null, directoryService);
.OfType<Video>() if (resolved is not Audio themeSong)
.Select(video =>
{ {
continue;
}
// Try to retrieve it from the db. If we don't find it, use the resolved version // Try to retrieve it from the db. If we don't find it, use the resolved version
var dbItem = GetItemById(video.Id) as Video; if (GetItemById(themeSong.Id) is Audio dbItem)
if (dbItem != null)
{ {
video = dbItem; themeSong = dbItem;
} }
video.ParentId = Guid.Empty; themeSong.ExtraType = ExtraType.ThemeSong;
video.OwnerId = owner.Id; themeSong.OwnerId = owner.Id;
themeSong.ParentId = Guid.Empty;
SetExtraTypeFromFilename(video); yield return themeSong;
}
return video;
// Sort them so that the list can be easily compared for changes
}).OrderBy(i => i.Path);
} }
public string GetPathAfterNetworkSubstitution(string path, BaseItem ownerItem) public string GetPathAfterNetworkSubstitution(string path, BaseItem ownerItem)
@ -2817,15 +2831,6 @@ namespace Emby.Server.Implementations.Library
return path; return path;
} }
private void SetExtraTypeFromFilename(Video item)
{
var resolver = new ExtraResolver(_namingOptions);
var result = resolver.GetExtraInfo(item.Path);
item.ExtraType = result.ExtraType;
}
public List<PersonInfo> GetPeople(InternalPeopleQuery query) public List<PersonInfo> GetPeople(InternalPeopleQuery query)
{ {
return _itemRepository.GetPeople(query); return _itemRepository.GetPeople(query);
@ -2932,11 +2937,12 @@ namespace Emby.Server.Implementations.Library
var rootFolderPath = _configurationManager.ApplicationPaths.DefaultUserViewsPath; var rootFolderPath = _configurationManager.ApplicationPaths.DefaultUserViewsPath;
var existingNameCount = 1; // first numbered name will be 2
var virtualFolderPath = Path.Combine(rootFolderPath, name); var virtualFolderPath = Path.Combine(rootFolderPath, name);
while (Directory.Exists(virtualFolderPath)) while (Directory.Exists(virtualFolderPath))
{ {
name += "1"; existingNameCount++;
virtualFolderPath = Path.Combine(rootFolderPath, name); virtualFolderPath = Path.Combine(rootFolderPath, name + " " + existingNameCount);
} }
var mediaPathInfos = options.PathInfos; var mediaPathInfos = options.PathInfos;

View File

@ -6,6 +6,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Jellyfin.Data.Enums; using Jellyfin.Data.Enums;
using Jellyfin.Extensions;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
namespace Emby.Server.Implementations.Library namespace Emby.Server.Implementations.Library
@ -64,18 +65,18 @@ namespace Emby.Server.Implementations.Library
stream = sortedStreams.FirstOrDefault(s => s.IsExternal || s.IsForced || s.IsDefault); stream = sortedStreams.FirstOrDefault(s => s.IsExternal || s.IsForced || s.IsDefault);
// if the audio language is not understood by the user, load their preferred subs, if there are any // if the audio language is not understood by the user, load their preferred subs, if there are any
if (stream == null && !preferredLanguages.Contains(audioTrackLanguage, StringComparer.OrdinalIgnoreCase)) if (stream == null && !preferredLanguages.Contains(audioTrackLanguage, StringComparison.OrdinalIgnoreCase))
{ {
stream = sortedStreams.FirstOrDefault(s => !s.IsForced && preferredLanguages.Contains(s.Language, StringComparer.OrdinalIgnoreCase)); stream = sortedStreams.FirstOrDefault(s => !s.IsForced && preferredLanguages.Contains(s.Language, StringComparison.OrdinalIgnoreCase));
} }
} }
else if (mode == SubtitlePlaybackMode.Smart) else if (mode == SubtitlePlaybackMode.Smart)
{ {
// if the audio language is not understood by the user, load their preferred subs, if there are any // if the audio language is not understood by the user, load their preferred subs, if there are any
if (!preferredLanguages.Contains(audioTrackLanguage, StringComparer.OrdinalIgnoreCase)) if (!preferredLanguages.Contains(audioTrackLanguage, StringComparison.OrdinalIgnoreCase))
{ {
stream = streams.FirstOrDefault(s => !s.IsForced && preferredLanguages.Contains(s.Language, StringComparer.OrdinalIgnoreCase)) ?? stream = streams.FirstOrDefault(s => !s.IsForced && preferredLanguages.Contains(s.Language, StringComparison.OrdinalIgnoreCase)) ??
streams.FirstOrDefault(s => preferredLanguages.Contains(s.Language, StringComparer.OrdinalIgnoreCase)); streams.FirstOrDefault(s => preferredLanguages.Contains(s.Language, StringComparison.OrdinalIgnoreCase));
} }
} }
else if (mode == SubtitlePlaybackMode.Always) else if (mode == SubtitlePlaybackMode.Always)
@ -136,9 +137,9 @@ namespace Emby.Server.Implementations.Library
else if (mode == SubtitlePlaybackMode.Smart) else if (mode == SubtitlePlaybackMode.Smart)
{ {
// Prefer smart logic over embedded metadata // Prefer smart logic over embedded metadata
if (!preferredLanguages.Contains(audioTrackLanguage, StringComparer.OrdinalIgnoreCase)) if (!preferredLanguages.Contains(audioTrackLanguage, StringComparison.OrdinalIgnoreCase))
{ {
filteredStreams = streams.Where(s => !s.IsForced && preferredLanguages.Contains(s.Language, StringComparer.OrdinalIgnoreCase)) filteredStreams = streams.Where(s => !s.IsForced && preferredLanguages.Contains(s.Language, StringComparison.OrdinalIgnoreCase))
.ToList(); .ToList();
} }
} }

View File

@ -52,7 +52,7 @@ namespace Emby.Server.Implementations.Library
var genres = item var genres = item
.GetRecursiveChildren(user, new InternalItemsQuery(user) .GetRecursiveChildren(user, new InternalItemsQuery(user)
{ {
IncludeItemTypes = new[] { nameof(Audio) }, IncludeItemTypes = new[] { BaseItemKind.Audio },
DtoOptions = dtoOptions DtoOptions = dtoOptions
}) })
.Cast<Audio>() .Cast<Audio>()
@ -89,7 +89,7 @@ namespace Emby.Server.Implementations.Library
{ {
return _libraryManager.GetItemList(new InternalItemsQuery(user) return _libraryManager.GetItemList(new InternalItemsQuery(user)
{ {
IncludeItemTypes = new[] { nameof(Audio) }, IncludeItemTypes = new[] { BaseItemKind.Audio },
GenreIds = genreIds.ToArray(), GenreIds = genreIds.ToArray(),

View File

@ -16,7 +16,7 @@ namespace Emby.Server.Implementations.Library
/// <param name="attribute">The attrib.</param> /// <param name="attribute">The attrib.</param>
/// <returns>System.String.</returns> /// <returns>System.String.</returns>
/// <exception cref="ArgumentException"><paramref name="str" /> or <paramref name="attribute" /> is empty.</exception> /// <exception cref="ArgumentException"><paramref name="str" /> or <paramref name="attribute" /> is empty.</exception>
public static string? GetAttributeValue(this string str, string attribute) public static string? GetAttributeValue(this ReadOnlySpan<char> str, ReadOnlySpan<char> attribute)
{ {
if (str.Length == 0) if (str.Length == 0)
{ {
@ -28,17 +28,31 @@ namespace Emby.Server.Implementations.Library
throw new ArgumentException("String can't be empty.", nameof(attribute)); throw new ArgumentException("String can't be empty.", nameof(attribute));
} }
string srch = "[" + attribute + "="; var attributeIndex = str.IndexOf(attribute, StringComparison.OrdinalIgnoreCase);
int start = str.IndexOf(srch, StringComparison.OrdinalIgnoreCase);
if (start != -1) // Must be at least 3 characters after the attribute =, ], any character.
var maxIndex = str.Length - attribute.Length - 3;
while (attributeIndex > -1 && attributeIndex < maxIndex)
{ {
start += srch.Length; var attributeEnd = attributeIndex + attribute.Length;
int end = str.IndexOf(']', start); if (attributeIndex > 0
return str.Substring(start, end - start); && str[attributeIndex - 1] == '['
&& str[attributeEnd] == '=')
{
var closingIndex = str[attributeEnd..].IndexOf(']');
// Must be at least 1 character before the closing bracket.
if (closingIndex > 1)
{
return str[(attributeEnd + 1)..(attributeEnd + closingIndex)].Trim().ToString();
}
}
str = str[attributeEnd..];
attributeIndex = str.IndexOf(attribute, StringComparison.OrdinalIgnoreCase);
} }
// for imdbid we also accept pattern matching // for imdbid we also accept pattern matching
if (string.Equals(attribute, "imdbid", StringComparison.OrdinalIgnoreCase)) if (attribute.Equals("imdbid", StringComparison.OrdinalIgnoreCase))
{ {
var match = ProviderIdParsers.TryFindImdbId(str, out var imdbId); var match = ProviderIdParsers.TryFindImdbId(str, out var imdbId);
return match ? imdbId.ToString() : null; return match ? imdbId.ToString() : null;

View File

@ -4,12 +4,10 @@ using System;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Emby.Naming.Common; using Emby.Naming.Common;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Resolvers; using MediaBrowser.Controller.Resolvers;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.Library.Resolvers.Audio namespace Emby.Server.Implementations.Library.Resolvers.Audio

View File

@ -49,121 +49,72 @@ namespace Emby.Server.Implementations.Library.Resolvers
protected virtual TVideoType ResolveVideo<TVideoType>(ItemResolveArgs args, bool parseName) protected virtual TVideoType ResolveVideo<TVideoType>(ItemResolveArgs args, bool parseName)
where TVideoType : Video, new() where TVideoType : Video, new()
{ {
var namingOptions = NamingOptions; VideoFileInfo videoInfo = null;
VideoType? videoType = null;
// If the path is a file check for a matching extensions // If the path is a file check for a matching extensions
if (args.IsDirectory) if (args.IsDirectory)
{ {
TVideoType video = null;
VideoFileInfo videoInfo = null;
// Loop through each child file/folder and see if we find a video // Loop through each child file/folder and see if we find a video
foreach (var child in args.FileSystemChildren) foreach (var child in args.FileSystemChildren)
{ {
var filename = child.Name; var filename = child.Name;
if (child.IsDirectory) if (child.IsDirectory)
{ {
if (IsDvdDirectory(child.FullName, filename, args.DirectoryService)) if (IsDvdDirectory(child.FullName, filename, args.DirectoryService))
{ {
videoInfo = VideoResolver.ResolveDirectory(args.Path, namingOptions); videoType = VideoType.Dvd;
if (videoInfo == null)
{
return null;
} }
else if (IsBluRayDirectory(filename))
video = new TVideoType
{ {
Path = args.Path, videoType = VideoType.BluRay;
VideoType = VideoType.Dvd,
ProductionYear = videoInfo.Year
};
break;
}
if (IsBluRayDirectory(filename))
{
videoInfo = VideoResolver.ResolveDirectory(args.Path, namingOptions);
if (videoInfo == null)
{
return null;
}
video = new TVideoType
{
Path = args.Path,
VideoType = VideoType.BluRay,
ProductionYear = videoInfo.Year
};
break;
} }
} }
else if (IsDvdFile(filename)) else if (IsDvdFile(filename))
{ {
videoInfo = VideoResolver.ResolveDirectory(args.Path, namingOptions); videoType = VideoType.Dvd;
if (videoInfo == null)
{
return null;
} }
video = new TVideoType if (videoType == null)
{ {
Path = args.Path, continue;
VideoType = VideoType.Dvd, }
ProductionYear = videoInfo.Year
}; videoInfo = VideoResolver.ResolveDirectory(args.Path, NamingOptions, parseName);
break; break;
} }
} }
if (video != null)
{
video.Name = parseName ?
videoInfo.Name :
Path.GetFileName(args.Path);
Set3DFormat(video, videoInfo);
}
return video;
}
else else
{ {
var videoInfo = VideoResolver.Resolve(args.Path, false, namingOptions, false); videoInfo = VideoResolver.Resolve(args.Path, false, NamingOptions, parseName);
}
if (videoInfo == null) if (videoInfo == null || (!videoInfo.IsStub && !VideoResolver.IsVideoFile(args.Path, NamingOptions)))
{ {
return null; return null;
} }
if (VideoResolver.IsVideoFile(args.Path, NamingOptions) || videoInfo.IsStub)
{
var path = args.Path;
var video = new TVideoType var video = new TVideoType
{ {
Path = path, Name = videoInfo.Name,
IsInMixedFolder = true, Path = args.Path,
ProductionYear = videoInfo.Year ProductionYear = videoInfo.Year,
ExtraType = videoInfo.ExtraType
}; };
if (videoType.HasValue)
{
video.VideoType = videoType.Value;
}
else
{
SetVideoType(video, videoInfo); SetVideoType(video, videoInfo);
}
video.Name = parseName ?
videoInfo.Name :
Path.GetFileNameWithoutExtension(args.Path);
Set3DFormat(video, videoInfo); Set3DFormat(video, videoInfo);
return video; return video;
} }
}
return null;
}
protected void SetVideoType(Video video, VideoFileInfo videoInfo) protected void SetVideoType(Video video, VideoFileInfo videoInfo)
{ {
@ -207,8 +158,8 @@ namespace Emby.Server.Implementations.Library.Resolvers
{ {
// use disc-utils, both DVDs and BDs use UDF filesystem // use disc-utils, both DVDs and BDs use UDF filesystem
using (var videoFileStream = File.Open(video.Path, FileMode.Open, FileAccess.Read)) using (var videoFileStream = File.Open(video.Path, FileMode.Open, FileAccess.Read))
using (UdfReader udfReader = new UdfReader(videoFileStream))
{ {
UdfReader udfReader = new UdfReader(videoFileStream);
if (udfReader.DirectoryExists("VIDEO_TS")) if (udfReader.DirectoryExists("VIDEO_TS"))
{ {
video.IsoType = IsoType.Dvd; video.IsoType = IsoType.Dvd;

View File

@ -5,6 +5,7 @@
using System; using System;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using Jellyfin.Extensions;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
@ -32,7 +33,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Books
var extension = Path.GetExtension(args.Path); var extension = Path.GetExtension(args.Path);
if (extension != null && _validExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase)) if (extension != null && _validExtensions.Contains(extension, StringComparison.OrdinalIgnoreCase))
{ {
// It's a book // It's a book
return new Book return new Book

View File

@ -65,7 +65,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
private static void SetProviderIdFromPath(BaseItem item) private static void SetProviderIdFromPath(BaseItem item)
{ {
// we need to only look at the name of this actual item (not parents) // we need to only look at the name of this actual item (not parents)
var justName = Path.GetFileName(item.Path); var justName = Path.GetFileName(item.Path.AsSpan());
var id = justName.GetAttributeValue("tmdbid"); var id = justName.GetAttributeValue("tmdbid");

View File

@ -26,7 +26,6 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
public class MovieResolver : BaseVideoResolver<Video>, IMultiItemResolver public class MovieResolver : BaseVideoResolver<Video>, IMultiItemResolver
{ {
private readonly IImageProcessor _imageProcessor; private readonly IImageProcessor _imageProcessor;
private readonly StackResolver _stackResolver;
private string[] _validCollectionTypes = new[] private string[] _validCollectionTypes = new[]
{ {
@ -46,7 +45,6 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
: base(namingOptions) : base(namingOptions)
{ {
_imageProcessor = imageProcessor; _imageProcessor = imageProcessor;
_stackResolver = new StackResolver(NamingOptions);
} }
/// <summary> /// <summary>
@ -62,7 +60,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
string collectionType, string collectionType,
IDirectoryService directoryService) IDirectoryService directoryService)
{ {
var result = ResolveMultipleInternal(parent, files, collectionType, directoryService); var result = ResolveMultipleInternal(parent, files, collectionType);
if (result != null) if (result != null)
{ {
@ -92,16 +90,17 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
return null; return null;
} }
Video movie = null;
var files = args.GetActualFileSystemChildren().ToList(); var files = args.GetActualFileSystemChildren().ToList();
if (string.Equals(collectionType, CollectionType.MusicVideos, StringComparison.OrdinalIgnoreCase)) if (string.Equals(collectionType, CollectionType.MusicVideos, StringComparison.OrdinalIgnoreCase))
{ {
return FindMovie<MusicVideo>(args, args.Path, args.Parent, files, args.DirectoryService, collectionType, false); movie = FindMovie<MusicVideo>(args, args.Path, args.Parent, files, args.DirectoryService, collectionType, false);
} }
if (string.Equals(collectionType, CollectionType.HomeVideos, StringComparison.OrdinalIgnoreCase)) if (string.Equals(collectionType, CollectionType.HomeVideos, StringComparison.OrdinalIgnoreCase))
{ {
return FindMovie<Video>(args, args.Path, args.Parent, files, args.DirectoryService, collectionType, false); movie = FindMovie<Video>(args, args.Path, args.Parent, files, args.DirectoryService, collectionType, false);
} }
if (string.IsNullOrEmpty(collectionType)) if (string.IsNullOrEmpty(collectionType))
@ -118,17 +117,16 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
return null; return null;
} }
{ movie = FindMovie<Movie>(args, args.Path, args.Parent, files, args.DirectoryService, collectionType, true);
return FindMovie<Movie>(args, args.Path, args.Parent, files, args.DirectoryService, collectionType, true);
}
} }
if (string.Equals(collectionType, CollectionType.Movies, StringComparison.OrdinalIgnoreCase)) if (string.Equals(collectionType, CollectionType.Movies, StringComparison.OrdinalIgnoreCase))
{ {
return FindMovie<Movie>(args, args.Path, args.Parent, files, args.DirectoryService, collectionType, true); movie = FindMovie<Movie>(args, args.Path, args.Parent, files, args.DirectoryService, collectionType, true);
} }
return null; // ignore extras
return movie?.ExtraType == null ? movie : null;
} }
// Handle owned items // Handle owned items
@ -169,6 +167,12 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
item = ResolveVideo<Video>(args, false); item = ResolveVideo<Video>(args, false);
} }
// Ignore extras
if (item?.ExtraType != null)
{
return null;
}
if (item != null) if (item != null)
{ {
item.IsInMixedFolder = true; item.IsInMixedFolder = true;
@ -180,8 +184,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
private MultiItemResolverResult ResolveMultipleInternal( private MultiItemResolverResult ResolveMultipleInternal(
Folder parent, Folder parent,
List<FileSystemMetadata> files, List<FileSystemMetadata> files,
string collectionType, string collectionType)
IDirectoryService directoryService)
{ {
if (IsInvalid(parent, collectionType)) if (IsInvalid(parent, collectionType))
{ {
@ -190,13 +193,13 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
if (string.Equals(collectionType, CollectionType.MusicVideos, StringComparison.OrdinalIgnoreCase)) if (string.Equals(collectionType, CollectionType.MusicVideos, StringComparison.OrdinalIgnoreCase))
{ {
return ResolveVideos<MusicVideo>(parent, files, directoryService, true, collectionType, false); return ResolveVideos<MusicVideo>(parent, files, true, collectionType, false);
} }
if (string.Equals(collectionType, CollectionType.HomeVideos, StringComparison.OrdinalIgnoreCase) || if (string.Equals(collectionType, CollectionType.HomeVideos, StringComparison.OrdinalIgnoreCase) ||
string.Equals(collectionType, CollectionType.Photos, StringComparison.OrdinalIgnoreCase)) string.Equals(collectionType, CollectionType.Photos, StringComparison.OrdinalIgnoreCase))
{ {
return ResolveVideos<Video>(parent, files, directoryService, false, collectionType, false); return ResolveVideos<Video>(parent, files, false, collectionType, false);
} }
if (string.IsNullOrEmpty(collectionType)) if (string.IsNullOrEmpty(collectionType))
@ -204,7 +207,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
// Owned items should just use the plain video type // Owned items should just use the plain video type
if (parent == null) if (parent == null)
{ {
return ResolveVideos<Video>(parent, files, directoryService, false, collectionType, false); return ResolveVideos<Video>(parent, files, false, collectionType, false);
} }
if (parent is Series || parent.GetParents().OfType<Series>().Any()) if (parent is Series || parent.GetParents().OfType<Series>().Any())
@ -212,12 +215,12 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
return null; return null;
} }
return ResolveVideos<Movie>(parent, files, directoryService, false, collectionType, true); return ResolveVideos<Movie>(parent, files, false, collectionType, true);
} }
if (string.Equals(collectionType, CollectionType.Movies, StringComparison.OrdinalIgnoreCase)) if (string.Equals(collectionType, CollectionType.Movies, StringComparison.OrdinalIgnoreCase))
{ {
return ResolveVideos<Movie>(parent, files, directoryService, true, collectionType, true); return ResolveVideos<Movie>(parent, files, true, collectionType, true);
} }
return null; return null;
@ -226,21 +229,20 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
private MultiItemResolverResult ResolveVideos<T>( private MultiItemResolverResult ResolveVideos<T>(
Folder parent, Folder parent,
IEnumerable<FileSystemMetadata> fileSystemEntries, IEnumerable<FileSystemMetadata> fileSystemEntries,
IDirectoryService directoryService, bool supportMultiEditions,
bool suppportMultiEditions,
string collectionType, string collectionType,
bool parseName) bool parseName)
where T : Video, new() where T : Video, new()
{ {
var files = new List<FileSystemMetadata>(); var files = new List<FileSystemMetadata>();
var videos = new List<BaseItem>();
var leftOver = new List<FileSystemMetadata>(); var leftOver = new List<FileSystemMetadata>();
var hasCollectionType = !string.IsNullOrEmpty(collectionType);
// Loop through each child file/folder and see if we find a video // Loop through each child file/folder and see if we find a video
foreach (var child in fileSystemEntries) foreach (var child in fileSystemEntries)
{ {
// This is a hack but currently no better way to resolve a sometimes ambiguous situation // This is a hack but currently no better way to resolve a sometimes ambiguous situation
if (string.IsNullOrEmpty(collectionType)) if (!hasCollectionType)
{ {
if (string.Equals(child.Name, "tvshow.nfo", StringComparison.OrdinalIgnoreCase) if (string.Equals(child.Name, "tvshow.nfo", StringComparison.OrdinalIgnoreCase)
|| string.Equals(child.Name, "season.nfo", StringComparison.OrdinalIgnoreCase)) || string.Equals(child.Name, "season.nfo", StringComparison.OrdinalIgnoreCase))
@ -259,29 +261,39 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
} }
} }
var resolverResult = VideoListResolver.Resolve(files, NamingOptions, suppportMultiEditions).ToList(); var videoInfos = files
.Select(i => VideoResolver.Resolve(i.FullName, i.IsDirectory, NamingOptions, parseName))
.Where(f => f != null)
.ToList();
var resolverResult = VideoListResolver.Resolve(videoInfos, NamingOptions, supportMultiEditions, parseName);
var result = new MultiItemResolverResult var result = new MultiItemResolverResult
{ {
ExtraFiles = leftOver, ExtraFiles = leftOver
Items = videos
}; };
var isInMixedFolder = resolverResult.Count > 1 || (parent != null && parent.IsTopParent); var isInMixedFolder = resolverResult.Count > 1 || parent?.IsTopParent == true;
foreach (var video in resolverResult) foreach (var video in resolverResult)
{ {
var firstVideo = video.Files[0]; var firstVideo = video.Files[0];
var path = firstVideo.Path;
if (video.ExtraType != null)
{
result.ExtraFiles.Add(files.Find(f => string.Equals(f.FullName, path, StringComparison.OrdinalIgnoreCase)));
continue;
}
var additionalParts = video.Files.Count > 1 ? video.Files.Skip(1).Select(i => i.Path).ToArray() : Array.Empty<string>();
var videoItem = new T var videoItem = new T
{ {
Path = video.Files[0].Path, Path = path,
IsInMixedFolder = isInMixedFolder, IsInMixedFolder = isInMixedFolder,
ProductionYear = video.Year, ProductionYear = video.Year,
Name = parseName ? Name = parseName ? video.Name : firstVideo.Name,
video.Name : AdditionalParts = additionalParts,
Path.GetFileNameWithoutExtension(video.Files[0].Path),
AdditionalParts = video.Files.Skip(1).Select(i => i.Path).ToArray(),
LocalAlternateVersions = video.AlternateVersions.Select(i => i.Path).ToArray() LocalAlternateVersions = video.AlternateVersions.Select(i => i.Path).ToArray()
}; };
@ -299,21 +311,34 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
private static bool IsIgnored(string filename) private static bool IsIgnored(string filename)
{ {
// Ignore samples // Ignore samples
Match m = Regex.Match(filename, @"\bsample\b", RegexOptions.IgnoreCase); Match m = Regex.Match(filename, @"\bsample\b", RegexOptions.IgnoreCase | RegexOptions.Compiled);
return m.Success; return m.Success;
} }
private bool ContainsFile(List<VideoInfo> result, FileSystemMetadata file) private static bool ContainsFile(IReadOnlyList<VideoInfo> result, FileSystemMetadata file)
{ {
return result.Any(i => ContainsFile(i, file)); for (var i = 0; i < result.Count; i++)
{
var current = result[i];
for (var j = 0; j < current.Files.Count; j++)
{
if (ContainsFile(current.Files[j], file))
{
return true;
}
} }
private bool ContainsFile(VideoInfo result, FileSystemMetadata file) for (var j = 0; j < current.AlternateVersions.Count; j++)
{ {
return result.Files.Any(i => ContainsFile(i, file)) || if (ContainsFile(current.AlternateVersions[j], file))
result.AlternateVersions.Any(i => ContainsFile(i, file)) || {
result.Extras.Any(i => ContainsFile(i, file)); return true;
}
}
}
return false;
} }
private static bool ContainsFile(VideoFileInfo result, FileSystemMetadata file) private static bool ContainsFile(VideoFileInfo result, FileSystemMetadata file)
@ -342,9 +367,9 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
if (item is Movie || item is MusicVideo) if (item is Movie || item is MusicVideo)
{ {
// We need to only look at the name of this actual item (not parents) // We need to only look at the name of this actual item (not parents)
var justName = item.IsInMixedFolder ? Path.GetFileName(item.Path) : Path.GetFileName(item.ContainingFolderPath); var justName = item.IsInMixedFolder ? Path.GetFileName(item.Path.AsSpan()) : Path.GetFileName(item.ContainingFolderPath.AsSpan());
if (!string.IsNullOrEmpty(justName)) if (!justName.IsEmpty)
{ {
// check for tmdb id // check for tmdb id
var tmdbid = justName.GetAttributeValue("tmdbid"); var tmdbid = justName.GetAttributeValue("tmdbid");
@ -358,7 +383,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
if (!string.IsNullOrEmpty(item.Path)) if (!string.IsNullOrEmpty(item.Path))
{ {
// check for imdb id - we use full media path, as we can assume, that this will match in any use case (wither id in parent dir or in file name) // check for imdb id - we use full media path, as we can assume, that this will match in any use case (wither id in parent dir or in file name)
var imdbid = item.Path.GetAttributeValue("imdbid"); var imdbid = item.Path.AsSpan().GetAttributeValue("imdbid");
if (!string.IsNullOrWhiteSpace(imdbid)) if (!string.IsNullOrWhiteSpace(imdbid))
{ {
@ -431,7 +456,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
// TODO: Allow GetMultiDiscMovie in here // TODO: Allow GetMultiDiscMovie in here
const bool SupportsMultiVersion = true; const bool SupportsMultiVersion = true;
var result = ResolveVideos<T>(parent, fileSystemEntries, directoryService, SupportsMultiVersion, collectionType, parseName) ?? var result = ResolveVideos<T>(parent, fileSystemEntries, SupportsMultiVersion, collectionType, parseName) ??
new MultiItemResolverResult(); new MultiItemResolverResult();
if (result.Items.Count == 1) if (result.Items.Count == 1)
@ -510,7 +535,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
return null; return null;
} }
var result = _stackResolver.ResolveDirectories(folderPaths).ToList(); var result = StackResolver.ResolveDirectories(folderPaths, NamingOptions).ToList();
if (result.Count != 1) if (result.Count != 1)
{ {

View File

@ -8,6 +8,7 @@ using System.IO;
using System.Linq; using System.Linq;
using Emby.Naming.Common; using Emby.Naming.Common;
using Emby.Naming.Video; using Emby.Naming.Video;
using Jellyfin.Extensions;
using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
@ -109,7 +110,7 @@ namespace Emby.Server.Implementations.Library.Resolvers
} }
string extension = Path.GetExtension(path).TrimStart('.'); string extension = Path.GetExtension(path).TrimStart('.');
return imageProcessor.SupportedInputFormats.Contains(extension, StringComparer.OrdinalIgnoreCase); return imageProcessor.SupportedInputFormats.Contains(extension, StringComparison.OrdinalIgnoreCase);
} }
} }
} }

View File

@ -5,6 +5,7 @@
using System; using System;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using Jellyfin.Extensions;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Playlists; using MediaBrowser.Controller.Playlists;
using MediaBrowser.Controller.Resolvers; using MediaBrowser.Controller.Resolvers;
@ -57,10 +58,10 @@ namespace Emby.Server.Implementations.Library.Resolvers
// Check if this is a music playlist file // Check if this is a music playlist file
// It should have the correct collection type and a supported file extension // It should have the correct collection type and a supported file extension
else if (_musicPlaylistCollectionTypes.Contains(args.CollectionType ?? string.Empty, StringComparer.OrdinalIgnoreCase)) else if (_musicPlaylistCollectionTypes.Contains(args.CollectionType ?? string.Empty, StringComparison.OrdinalIgnoreCase))
{ {
var extension = Path.GetExtension(args.Path); var extension = Path.GetExtension(args.Path);
if (Playlist.SupportedExtensions.Contains(extension ?? string.Empty, StringComparer.OrdinalIgnoreCase)) if (Playlist.SupportedExtensions.Contains(extension ?? string.Empty, StringComparison.OrdinalIgnoreCase))
{ {
return new Playlist return new Playlist
{ {

View File

@ -3,7 +3,6 @@
using System; using System;
using System.Linq; using System.Linq;
using Emby.Naming.Common; using Emby.Naming.Common;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
@ -45,15 +44,18 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
// If the parent is a Season or Series and the parent is not an extras folder, then this is an Episode if the VideoResolver returns something // If the parent is a Season or Series and the parent is not an extras folder, then this is an Episode if the VideoResolver returns something
// Also handle flat tv folders // Also handle flat tv folders
if ((season != null || if (season != null ||
string.Equals(args.GetCollectionType(), CollectionType.TvShows, StringComparison.OrdinalIgnoreCase) || string.Equals(args.GetCollectionType(), CollectionType.TvShows, StringComparison.OrdinalIgnoreCase) ||
args.HasParent<Series>()) args.HasParent<Series>())
&& (parent is Series || !BaseItem.AllExtrasTypesFolderNames.ContainsKey(parent.Name)))
{ {
var episode = ResolveVideo<Episode>(args, false); var episode = ResolveVideo<Episode>(args, false);
if (episode != null) // Ignore extras
if (episode == null || episode.ExtraType != null)
{ {
return null;
}
var series = parent as Series ?? parent.GetParents().OfType<Series>().FirstOrDefault(); var series = parent as Series ?? parent.GetParents().OfType<Series>().FirstOrDefault();
if (series != null) if (series != null)
@ -73,7 +75,6 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
{ {
episode.ParentIndexNumber = 1; episode.ParentIndexNumber = 1;
} }
}
return episode; return episode;
} }

View File

@ -5,12 +5,9 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
using Emby.Naming.Common; using Emby.Naming.Common;
using Emby.Naming.TV; using Emby.Naming.TV;
using Emby.Naming.Video; using Emby.Naming.Video;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Resolvers; using MediaBrowser.Controller.Resolvers;
@ -185,13 +182,42 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
/// <param name="path">The path.</param> /// <param name="path">The path.</param>
private static void SetProviderIdFromPath(Series item, string path) private static void SetProviderIdFromPath(Series item, string path)
{ {
var justName = Path.GetFileName(path); var justName = Path.GetFileName(path.AsSpan());
var id = justName.GetAttributeValue("tvdbid"); var tvdbId = justName.GetAttributeValue("tvdbid");
if (!string.IsNullOrEmpty(tvdbId))
if (!string.IsNullOrEmpty(id))
{ {
item.SetProviderId(MetadataProvider.Tvdb, id); item.SetProviderId(MetadataProvider.Tvdb, tvdbId);
}
var tvmazeId = justName.GetAttributeValue("tvmazeid");
if (!string.IsNullOrEmpty(tvmazeId))
{
item.SetProviderId(MetadataProvider.TvMaze, tvmazeId);
}
var tmdbId = justName.GetAttributeValue("tmdbid");
if (!string.IsNullOrEmpty(tmdbId))
{
item.SetProviderId(MetadataProvider.Tmdb, tmdbId);
}
var anidbId = justName.GetAttributeValue("anidbid");
if (!string.IsNullOrEmpty(anidbId))
{
item.SetProviderId("AniDB", anidbId);
}
var aniListId = justName.GetAttributeValue("anilistid");
if (!string.IsNullOrEmpty(aniListId))
{
item.SetProviderId("AniList", aniListId);
}
var aniSearchId = justName.GetAttributeValue("anisearchid");
if (!string.IsNullOrEmpty(aniSearchId))
{
item.SetProviderId("AniSearch", aniSearchId);
} }
} }
} }

View File

@ -10,12 +10,9 @@ using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums; using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Querying; using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Search; using MediaBrowser.Model.Search;
using Genre = MediaBrowser.Controller.Entities.Genre;
using Person = MediaBrowser.Controller.Entities.Person;
namespace Emby.Server.Implementations.Library namespace Emby.Server.Implementations.Library
{ {
@ -59,9 +56,9 @@ namespace Emby.Server.Implementations.Library
}; };
} }
private static void AddIfMissing(List<string> list, string value) private static void AddIfMissing(List<BaseItemKind> list, BaseItemKind value)
{ {
if (!list.Contains(value, StringComparer.OrdinalIgnoreCase)) if (!list.Contains(value))
{ {
list.Add(value); list.Add(value);
} }
@ -86,63 +83,63 @@ namespace Emby.Server.Implementations.Library
searchTerm = searchTerm.Trim().RemoveDiacritics(); searchTerm = searchTerm.Trim().RemoveDiacritics();
var excludeItemTypes = query.ExcludeItemTypes.ToList(); var excludeItemTypes = query.ExcludeItemTypes.ToList();
var includeItemTypes = (query.IncludeItemTypes ?? Array.Empty<string>()).ToList(); var includeItemTypes = (query.IncludeItemTypes ?? Array.Empty<BaseItemKind>()).ToList();
excludeItemTypes.Add(nameof(Year)); excludeItemTypes.Add(BaseItemKind.Year);
excludeItemTypes.Add(nameof(Folder)); excludeItemTypes.Add(BaseItemKind.Folder);
if (query.IncludeGenres && (includeItemTypes.Count == 0 || includeItemTypes.Contains("Genre", StringComparer.OrdinalIgnoreCase))) if (query.IncludeGenres && (includeItemTypes.Count == 0 || includeItemTypes.Contains(BaseItemKind.Genre)))
{ {
if (!query.IncludeMedia) if (!query.IncludeMedia)
{ {
AddIfMissing(includeItemTypes, nameof(Genre)); AddIfMissing(includeItemTypes, BaseItemKind.Genre);
AddIfMissing(includeItemTypes, nameof(MusicGenre)); AddIfMissing(includeItemTypes, BaseItemKind.MusicGenre);
} }
} }
else else
{ {
AddIfMissing(excludeItemTypes, nameof(Genre)); AddIfMissing(excludeItemTypes, BaseItemKind.Genre);
AddIfMissing(excludeItemTypes, nameof(MusicGenre)); AddIfMissing(excludeItemTypes, BaseItemKind.MusicGenre);
} }
if (query.IncludePeople && (includeItemTypes.Count == 0 || includeItemTypes.Contains("People", StringComparer.OrdinalIgnoreCase) || includeItemTypes.Contains("Person", StringComparer.OrdinalIgnoreCase))) if (query.IncludePeople && (includeItemTypes.Count == 0 || includeItemTypes.Contains(BaseItemKind.Person)))
{ {
if (!query.IncludeMedia) if (!query.IncludeMedia)
{ {
AddIfMissing(includeItemTypes, nameof(Person)); AddIfMissing(includeItemTypes, BaseItemKind.Person);
} }
} }
else else
{ {
AddIfMissing(excludeItemTypes, nameof(Person)); AddIfMissing(excludeItemTypes, BaseItemKind.Person);
} }
if (query.IncludeStudios && (includeItemTypes.Count == 0 || includeItemTypes.Contains("Studio", StringComparer.OrdinalIgnoreCase))) if (query.IncludeStudios && (includeItemTypes.Count == 0 || includeItemTypes.Contains(BaseItemKind.Studio)))
{ {
if (!query.IncludeMedia) if (!query.IncludeMedia)
{ {
AddIfMissing(includeItemTypes, nameof(Studio)); AddIfMissing(includeItemTypes, BaseItemKind.Studio);
} }
} }
else else
{ {
AddIfMissing(excludeItemTypes, nameof(Studio)); AddIfMissing(excludeItemTypes, BaseItemKind.Studio);
} }
if (query.IncludeArtists && (includeItemTypes.Count == 0 || includeItemTypes.Contains("MusicArtist", StringComparer.OrdinalIgnoreCase))) if (query.IncludeArtists && (includeItemTypes.Count == 0 || includeItemTypes.Contains(BaseItemKind.MusicArtist)))
{ {
if (!query.IncludeMedia) if (!query.IncludeMedia)
{ {
AddIfMissing(includeItemTypes, nameof(MusicArtist)); AddIfMissing(includeItemTypes, BaseItemKind.MusicArtist);
} }
} }
else else
{ {
AddIfMissing(excludeItemTypes, nameof(MusicArtist)); AddIfMissing(excludeItemTypes, BaseItemKind.MusicArtist);
} }
AddIfMissing(excludeItemTypes, nameof(CollectionFolder)); AddIfMissing(excludeItemTypes, BaseItemKind.CollectionFolder);
AddIfMissing(excludeItemTypes, nameof(Folder)); AddIfMissing(excludeItemTypes, BaseItemKind.Folder);
var mediaTypes = query.MediaTypes.ToList(); var mediaTypes = query.MediaTypes.ToList();
if (includeItemTypes.Count > 0) if (includeItemTypes.Count > 0)
@ -183,7 +180,7 @@ namespace Emby.Server.Implementations.Library
List<BaseItem> mediaItems; List<BaseItem> mediaItems;
if (searchQuery.IncludeItemTypes.Length == 1 && string.Equals(searchQuery.IncludeItemTypes[0], "MusicArtist", StringComparison.OrdinalIgnoreCase)) if (searchQuery.IncludeItemTypes.Length == 1 && searchQuery.IncludeItemTypes[0] == BaseItemKind.MusicArtist)
{ {
if (!searchQuery.ParentId.Equals(Guid.Empty)) if (!searchQuery.ParentId.Equals(Guid.Empty))
{ {
@ -192,7 +189,7 @@ namespace Emby.Server.Implementations.Library
searchQuery.ParentId = Guid.Empty; searchQuery.ParentId = Guid.Empty;
searchQuery.IncludeItemsByName = true; searchQuery.IncludeItemsByName = true;
searchQuery.IncludeItemTypes = Array.Empty<string>(); searchQuery.IncludeItemTypes = Array.Empty<BaseItemKind>();
mediaItems = _libraryManager.GetAllArtists(searchQuery).Items.Select(i => i.Item1).ToList(); mediaItems = _libraryManager.GetAllArtists(searchQuery).Items.Select(i => i.Item1).ToList();
} }
else else

View File

@ -8,11 +8,11 @@ using System.Linq;
using System.Threading; using System.Threading;
using Jellyfin.Data.Entities; using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums; using Jellyfin.Data.Enums;
using Jellyfin.Extensions;
using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Model.Channels; using MediaBrowser.Model.Channels;
@ -20,8 +20,6 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Library; using MediaBrowser.Model.Library;
using MediaBrowser.Model.Querying; using MediaBrowser.Model.Querying;
using Genre = MediaBrowser.Controller.Entities.Genre;
using Person = MediaBrowser.Controller.Entities.Person;
namespace Emby.Server.Implementations.Library namespace Emby.Server.Implementations.Library
{ {
@ -80,7 +78,7 @@ namespace Emby.Server.Implementations.Library
continue; continue;
} }
if (query.PresetViews.Contains(folderViewType ?? string.Empty, StringComparer.OrdinalIgnoreCase)) if (query.PresetViews.Contains(folderViewType ?? string.Empty, StringComparison.OrdinalIgnoreCase))
{ {
list.Add(GetUserView(folder, folderViewType, string.Empty)); list.Add(GetUserView(folder, folderViewType, string.Empty));
} }
@ -180,7 +178,7 @@ namespace Emby.Server.Implementations.Library
{ {
if (parents.Count == 1 && parents.All(i => string.Equals(i.CollectionType, viewType, StringComparison.OrdinalIgnoreCase))) if (parents.Count == 1 && parents.All(i => string.Equals(i.CollectionType, viewType, StringComparison.OrdinalIgnoreCase)))
{ {
if (!presetViews.Contains(viewType, StringComparer.OrdinalIgnoreCase)) if (!presetViews.Contains(viewType, StringComparison.OrdinalIgnoreCase))
{ {
return (Folder)parents[0]; return (Folder)parents[0];
} }
@ -300,11 +298,11 @@ namespace Emby.Server.Implementations.Library
{ {
if (hasCollectionType.All(i => string.Equals(i.CollectionType, CollectionType.Movies, StringComparison.OrdinalIgnoreCase))) if (hasCollectionType.All(i => string.Equals(i.CollectionType, CollectionType.Movies, StringComparison.OrdinalIgnoreCase)))
{ {
includeItemTypes = new string[] { "Movie" }; includeItemTypes = new[] { BaseItemKind.Movie };
} }
else if (hasCollectionType.All(i => string.Equals(i.CollectionType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase))) else if (hasCollectionType.All(i => string.Equals(i.CollectionType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase)))
{ {
includeItemTypes = new string[] { "Episode" }; includeItemTypes = new[] { BaseItemKind.Episode };
} }
} }
} }
@ -344,13 +342,13 @@ namespace Emby.Server.Implementations.Library
var excludeItemTypes = includeItemTypes.Length == 0 && mediaTypes.Count == 0 var excludeItemTypes = includeItemTypes.Length == 0 && mediaTypes.Count == 0
? new[] ? new[]
{ {
nameof(Person), BaseItemKind.Person,
nameof(Studio), BaseItemKind.Studio,
nameof(Year), BaseItemKind.Year,
nameof(MusicGenre), BaseItemKind.MusicGenre,
nameof(Genre) BaseItemKind.Genre
} }
: Array.Empty<string>(); : Array.Empty<BaseItemKind>();
var query = new InternalItemsQuery(user) var query = new InternalItemsQuery(user)
{ {

View File

@ -3,6 +3,7 @@ using System.Globalization;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
@ -81,7 +82,7 @@ namespace Emby.Server.Implementations.Library.Validators
var deadEntities = _libraryManager.GetItemList(new InternalItemsQuery var deadEntities = _libraryManager.GetItemList(new InternalItemsQuery
{ {
IncludeItemTypes = new[] { nameof(MusicArtist) }, IncludeItemTypes = new[] { BaseItemKind.MusicArtist },
IsDeadArtist = true, IsDeadArtist = true,
IsLocked = false IsLocked = false
}).Cast<MusicArtist>().ToList(); }).Cast<MusicArtist>().ToList();

View File

@ -64,7 +64,7 @@ namespace Emby.Server.Implementations.Library.Validators
var movies = _libraryManager.GetItemList(new InternalItemsQuery var movies = _libraryManager.GetItemList(new InternalItemsQuery
{ {
MediaTypes = new string[] { MediaType.Video }, MediaTypes = new string[] { MediaType.Video },
IncludeItemTypes = new[] { nameof(Movie) }, IncludeItemTypes = new[] { BaseItemKind.Movie },
IsVirtualItem = false, IsVirtualItem = false,
OrderBy = new[] { (ItemSortBy.SortName, SortOrder.Ascending) }, OrderBy = new[] { (ItemSortBy.SortName, SortOrder.Ascending) },
Parent = library, Parent = library,
@ -108,7 +108,7 @@ namespace Emby.Server.Implementations.Library.Validators
var boxSets = _libraryManager.GetItemList(new InternalItemsQuery var boxSets = _libraryManager.GetItemList(new InternalItemsQuery
{ {
IncludeItemTypes = new[] { nameof(BoxSet) }, IncludeItemTypes = new[] { BaseItemKind.BoxSet },
CollapseBoxSetItems = false, CollapseBoxSetItems = false,
Recursive = true Recursive = true
}); });

View File

@ -2,6 +2,7 @@ using System;
using System.Globalization; using System.Globalization;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Providers;
@ -91,7 +92,7 @@ namespace Emby.Server.Implementations.Library.Validators
var deadEntities = _libraryManager.GetItemList(new InternalItemsQuery var deadEntities = _libraryManager.GetItemList(new InternalItemsQuery
{ {
IncludeItemTypes = new[] { nameof(Person) }, IncludeItemTypes = new[] { BaseItemKind.Person },
IsDeadPerson = true, IsDeadPerson = true,
IsLocked = false IsLocked = false
}); });

View File

@ -2,6 +2,7 @@ using System;
using System.Globalization; using System.Globalization;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Persistence;
@ -80,7 +81,7 @@ namespace Emby.Server.Implementations.Library.Validators
var deadEntities = _libraryManager.GetItemList(new InternalItemsQuery var deadEntities = _libraryManager.GetItemList(new InternalItemsQuery
{ {
IncludeItemTypes = new[] { nameof(Studio) }, IncludeItemTypes = new[] { BaseItemKind.Studio },
IsDeadStudio = true, IsDeadStudio = true,
IsLocked = false IsLocked = false
}); });

View File

@ -17,6 +17,7 @@ using System.Xml;
using Emby.Server.Implementations.Library; using Emby.Server.Implementations.Library;
using Jellyfin.Data.Enums; using Jellyfin.Data.Enums;
using Jellyfin.Data.Events; using Jellyfin.Data.Events;
using Jellyfin.Extensions;
using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Progress; using MediaBrowser.Common.Progress;
@ -227,7 +228,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
foreach (var virtualFolder in virtualFolders) foreach (var virtualFolder in virtualFolders)
{ {
if (!virtualFolder.Locations.Contains(path, StringComparer.OrdinalIgnoreCase)) if (!virtualFolder.Locations.Contains(path, StringComparison.OrdinalIgnoreCase))
{ {
continue; continue;
} }
@ -891,7 +892,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
throw new ArgumentNullException(nameof(tunerHostId)); throw new ArgumentNullException(nameof(tunerHostId));
} }
return info.EnabledTuners.Contains(tunerHostId, StringComparer.OrdinalIgnoreCase); return info.EnabledTuners.Contains(tunerHostId, StringComparison.OrdinalIgnoreCase);
} }
public async Task<IEnumerable<ProgramInfo>> GetProgramsAsync(string channelId, DateTime startDateUtc, DateTime endDateUtc, CancellationToken cancellationToken) public async Task<IEnumerable<ProgramInfo>> GetProgramsAsync(string channelId, DateTime startDateUtc, DateTime endDateUtc, CancellationToken cancellationToken)
@ -1778,7 +1779,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{ {
var program = string.IsNullOrWhiteSpace(timer.ProgramId) ? null : _libraryManager.GetItemList(new InternalItemsQuery var program = string.IsNullOrWhiteSpace(timer.ProgramId) ? null : _libraryManager.GetItemList(new InternalItemsQuery
{ {
IncludeItemTypes = new[] { nameof(LiveTvProgram) }, IncludeItemTypes = new[] { BaseItemKind.LiveTvProgram },
Limit = 1, Limit = 1,
ExternalId = timer.ProgramId, ExternalId = timer.ProgramId,
DtoOptions = new DtoOptions(true) DtoOptions = new DtoOptions(true)
@ -2137,7 +2138,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{ {
var query = new InternalItemsQuery var query = new InternalItemsQuery
{ {
IncludeItemTypes = new string[] { nameof(LiveTvProgram) }, IncludeItemTypes = new[] { BaseItemKind.LiveTvProgram },
Limit = 1, Limit = 1,
DtoOptions = new DtoOptions(true) DtoOptions = new DtoOptions(true)
{ {
@ -2332,7 +2333,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
var deletes = _timerProvider.GetAll() var deletes = _timerProvider.GetAll()
.Where(i => string.Equals(i.SeriesTimerId, seriesTimer.Id, StringComparison.OrdinalIgnoreCase)) .Where(i => string.Equals(i.SeriesTimerId, seriesTimer.Id, StringComparison.OrdinalIgnoreCase))
.Where(i => !allTimerIds.Contains(i.Id, StringComparer.OrdinalIgnoreCase) && i.StartDate > DateTime.UtcNow) .Where(i => !allTimerIds.Contains(i.Id, StringComparison.OrdinalIgnoreCase) && i.StartDate > DateTime.UtcNow)
.Where(i => deleteStatuses.Contains(i.Status)) .Where(i => deleteStatuses.Contains(i.Status))
.ToList(); .ToList();
@ -2352,7 +2353,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
var query = new InternalItemsQuery var query = new InternalItemsQuery
{ {
IncludeItemTypes = new string[] { nameof(LiveTvProgram) }, IncludeItemTypes = new[] { BaseItemKind.LiveTvProgram },
ExternalSeriesId = seriesTimer.SeriesId, ExternalSeriesId = seriesTimer.SeriesId,
DtoOptions = new DtoOptions(true) DtoOptions = new DtoOptions(true)
{ {
@ -2387,7 +2388,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
channel = _libraryManager.GetItemList( channel = _libraryManager.GetItemList(
new InternalItemsQuery new InternalItemsQuery
{ {
IncludeItemTypes = new string[] { nameof(LiveTvChannel) }, IncludeItemTypes = new[] { BaseItemKind.LiveTvChannel },
ItemIds = new[] { parent.ChannelId }, ItemIds = new[] { parent.ChannelId },
DtoOptions = new DtoOptions() DtoOptions = new DtoOptions()
}).FirstOrDefault() as LiveTvChannel; }).FirstOrDefault() as LiveTvChannel;
@ -2446,7 +2447,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
channel = _libraryManager.GetItemList( channel = _libraryManager.GetItemList(
new InternalItemsQuery new InternalItemsQuery
{ {
IncludeItemTypes = new string[] { nameof(LiveTvChannel) }, IncludeItemTypes = new[] { BaseItemKind.LiveTvChannel },
ItemIds = new[] { programInfo.ChannelId }, ItemIds = new[] { programInfo.ChannelId },
DtoOptions = new DtoOptions() DtoOptions = new DtoOptions()
}).FirstOrDefault() as LiveTvChannel; }).FirstOrDefault() as LiveTvChannel;
@ -2511,7 +2512,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
var seriesIds = _libraryManager.GetItemIds( var seriesIds = _libraryManager.GetItemIds(
new InternalItemsQuery new InternalItemsQuery
{ {
IncludeItemTypes = new[] { nameof(Series) }, IncludeItemTypes = new[] { BaseItemKind.Series },
Name = program.Name Name = program.Name
}).ToArray(); }).ToArray();
@ -2524,7 +2525,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{ {
var result = _libraryManager.GetItemIds(new InternalItemsQuery var result = _libraryManager.GetItemIds(new InternalItemsQuery
{ {
IncludeItemTypes = new[] { nameof(Episode) }, IncludeItemTypes = new[] { BaseItemKind.Episode },
ParentIndexNumber = program.SeasonNumber.Value, ParentIndexNumber = program.SeasonNumber.Value,
IndexNumber = program.EpisodeNumber.Value, IndexNumber = program.EpisodeNumber.Value,
AncestorIds = seriesIds, AncestorIds = seriesIds,
@ -2621,7 +2622,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
if (newDevicesOnly) if (newDevicesOnly)
{ {
discoveredDevices = discoveredDevices.Where(d => !configuredDeviceIds.Contains(d.DeviceId, StringComparer.OrdinalIgnoreCase)) discoveredDevices = discoveredDevices.Where(d => !configuredDeviceIds.Contains(d.DeviceId, StringComparison.OrdinalIgnoreCase))
.ToList(); .ToList();
} }

View File

@ -10,7 +10,6 @@ using System.Linq;
using System.Net; using System.Net;
using System.Net.Http; using System.Net.Http;
using System.Net.Http.Json; using System.Net.Http.Json;
using System.Net.Http.Headers;
using System.Net.Mime; using System.Net.Mime;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text; using System.Text;
@ -242,19 +241,19 @@ namespace Emby.Server.Implementations.LiveTv.Listings
if (programInfo.AudioProperties.Count != 0) if (programInfo.AudioProperties.Count != 0)
{ {
if (programInfo.AudioProperties.Contains("atmos", StringComparer.OrdinalIgnoreCase)) if (programInfo.AudioProperties.Contains("atmos", StringComparison.OrdinalIgnoreCase))
{ {
audioType = ProgramAudio.Atmos; audioType = ProgramAudio.Atmos;
} }
else if (programInfo.AudioProperties.Contains("dd 5.1", StringComparer.OrdinalIgnoreCase)) else if (programInfo.AudioProperties.Contains("dd 5.1", StringComparison.OrdinalIgnoreCase))
{ {
audioType = ProgramAudio.DolbyDigital; audioType = ProgramAudio.DolbyDigital;
} }
else if (programInfo.AudioProperties.Contains("dd", StringComparer.OrdinalIgnoreCase)) else if (programInfo.AudioProperties.Contains("dd", StringComparison.OrdinalIgnoreCase))
{ {
audioType = ProgramAudio.DolbyDigital; audioType = ProgramAudio.DolbyDigital;
} }
else if (programInfo.AudioProperties.Contains("stereo", StringComparer.OrdinalIgnoreCase)) else if (programInfo.AudioProperties.Contains("stereo", StringComparison.OrdinalIgnoreCase))
{ {
audioType = ProgramAudio.Stereo; audioType = ProgramAudio.Stereo;
} }
@ -316,8 +315,8 @@ namespace Emby.Server.Implementations.LiveTv.Listings
if (programInfo.VideoProperties != null) if (programInfo.VideoProperties != null)
{ {
info.IsHD = programInfo.VideoProperties.Contains("hdtv", StringComparer.OrdinalIgnoreCase); info.IsHD = programInfo.VideoProperties.Contains("hdtv", StringComparison.OrdinalIgnoreCase);
info.Is3D = programInfo.VideoProperties.Contains("3d", StringComparer.OrdinalIgnoreCase); info.Is3D = programInfo.VideoProperties.Contains("3d", StringComparison.OrdinalIgnoreCase);
} }
if (details.ContentRating != null && details.ContentRating.Count > 0) if (details.ContentRating != null && details.ContentRating.Count > 0)
@ -326,7 +325,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
.Replace("--", "-", StringComparison.Ordinal); .Replace("--", "-", StringComparison.Ordinal);
var invalid = new[] { "N/A", "Approved", "Not Rated", "Passed" }; var invalid = new[] { "N/A", "Approved", "Not Rated", "Passed" };
if (invalid.Contains(info.OfficialRating, StringComparer.OrdinalIgnoreCase)) if (invalid.Contains(info.OfficialRating, StringComparison.OrdinalIgnoreCase))
{ {
info.OfficialRating = null; info.OfficialRating = null;
} }
@ -388,9 +387,9 @@ namespace Emby.Server.Implementations.LiveTv.Listings
if (details.Genres != null) if (details.Genres != null)
{ {
info.Genres = details.Genres.Where(g => !string.IsNullOrWhiteSpace(g)).ToList(); info.Genres = details.Genres.Where(g => !string.IsNullOrWhiteSpace(g)).ToList();
info.IsNews = details.Genres.Contains("news", StringComparer.OrdinalIgnoreCase); info.IsNews = details.Genres.Contains("news", StringComparison.OrdinalIgnoreCase);
if (info.Genres.Contains("children", StringComparer.OrdinalIgnoreCase)) if (info.Genres.Contains("children", StringComparison.OrdinalIgnoreCase))
{ {
info.IsKids = true; info.IsKids = true;
} }

View File

@ -190,10 +190,10 @@ namespace Emby.Server.Implementations.LiveTv.Listings
IsSeries = program.Episode != null, IsSeries = program.Episode != null,
IsRepeat = program.IsPreviouslyShown && !program.IsNew, IsRepeat = program.IsPreviouslyShown && !program.IsNew,
IsPremiere = program.Premiere != null, IsPremiere = program.Premiere != null,
IsKids = program.Categories.Any(c => info.KidsCategories.Contains(c, StringComparer.OrdinalIgnoreCase)), IsKids = program.Categories.Any(c => info.KidsCategories.Contains(c, StringComparison.OrdinalIgnoreCase)),
IsMovie = program.Categories.Any(c => info.MovieCategories.Contains(c, StringComparer.OrdinalIgnoreCase)), IsMovie = program.Categories.Any(c => info.MovieCategories.Contains(c, StringComparison.OrdinalIgnoreCase)),
IsNews = program.Categories.Any(c => info.NewsCategories.Contains(c, StringComparer.OrdinalIgnoreCase)), IsNews = program.Categories.Any(c => info.NewsCategories.Contains(c, StringComparison.OrdinalIgnoreCase)),
IsSports = program.Categories.Any(c => info.SportsCategories.Contains(c, StringComparer.OrdinalIgnoreCase)), IsSports = program.Categories.Any(c => info.SportsCategories.Contains(c, StringComparison.OrdinalIgnoreCase)),
ImageUrl = program.Icon != null && !string.IsNullOrEmpty(program.Icon.Source) ? program.Icon.Source : null, ImageUrl = program.Icon != null && !string.IsNullOrEmpty(program.Icon.Source) ? program.Icon.Source : null,
HasImage = program.Icon != null && !string.IsNullOrEmpty(program.Icon.Source), HasImage = program.Icon != null && !string.IsNullOrEmpty(program.Icon.Source),
OfficialRating = program.Rating != null && !string.IsNullOrEmpty(program.Rating.Value) ? program.Rating.Value : null, OfficialRating = program.Rating != null && !string.IsNullOrEmpty(program.Rating.Value) ? program.Rating.Value : null,

View File

@ -7,12 +7,12 @@ using System.Globalization;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Jellyfin.Data.Enums;
using MediaBrowser.Common; using MediaBrowser.Common;
using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Model.Dto; using MediaBrowser.Model.Dto;
@ -161,7 +161,7 @@ namespace Emby.Server.Implementations.LiveTv
{ {
var librarySeries = _libraryManager.GetItemList(new InternalItemsQuery var librarySeries = _libraryManager.GetItemList(new InternalItemsQuery
{ {
IncludeItemTypes = new string[] { nameof(Series) }, IncludeItemTypes = new[] { BaseItemKind.Series },
Name = seriesName, Name = seriesName,
Limit = 1, Limit = 1,
ImageTypes = new ImageType[] { ImageType.Thumb }, ImageTypes = new ImageType[] { ImageType.Thumb },
@ -204,7 +204,7 @@ namespace Emby.Server.Implementations.LiveTv
var program = _libraryManager.GetItemList(new InternalItemsQuery var program = _libraryManager.GetItemList(new InternalItemsQuery
{ {
IncludeItemTypes = new string[] { nameof(LiveTvProgram) }, IncludeItemTypes = new[] { BaseItemKind.LiveTvProgram },
ExternalSeriesId = programSeriesId, ExternalSeriesId = programSeriesId,
Limit = 1, Limit = 1,
ImageTypes = new ImageType[] { ImageType.Primary }, ImageTypes = new ImageType[] { ImageType.Primary },
@ -255,7 +255,7 @@ namespace Emby.Server.Implementations.LiveTv
{ {
var librarySeries = _libraryManager.GetItemList(new InternalItemsQuery var librarySeries = _libraryManager.GetItemList(new InternalItemsQuery
{ {
IncludeItemTypes = new string[] { nameof(Series) }, IncludeItemTypes = new[] { BaseItemKind.Series },
Name = seriesName, Name = seriesName,
Limit = 1, Limit = 1,
ImageTypes = new ImageType[] { ImageType.Thumb }, ImageTypes = new ImageType[] { ImageType.Thumb },
@ -298,7 +298,7 @@ namespace Emby.Server.Implementations.LiveTv
var program = _libraryManager.GetItemList(new InternalItemsQuery var program = _libraryManager.GetItemList(new InternalItemsQuery
{ {
IncludeItemTypes = new string[] { nameof(Series) }, IncludeItemTypes = new[] { BaseItemKind.Series },
Name = seriesName, Name = seriesName,
Limit = 1, Limit = 1,
ImageTypes = new ImageType[] { ImageType.Primary }, ImageTypes = new ImageType[] { ImageType.Primary },
@ -309,7 +309,7 @@ namespace Emby.Server.Implementations.LiveTv
{ {
program = _libraryManager.GetItemList(new InternalItemsQuery program = _libraryManager.GetItemList(new InternalItemsQuery
{ {
IncludeItemTypes = new string[] { nameof(LiveTvProgram) }, IncludeItemTypes = new[] { BaseItemKind.LiveTvProgram },
ExternalSeriesId = programSeriesId, ExternalSeriesId = programSeriesId,
Limit = 1, Limit = 1,
ImageTypes = new ImageType[] { ImageType.Primary }, ImageTypes = new ImageType[] { ImageType.Primary },

View File

@ -33,8 +33,6 @@ using MediaBrowser.Model.LiveTv;
using MediaBrowser.Model.Querying; using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Tasks; using MediaBrowser.Model.Tasks;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Episode = MediaBrowser.Controller.Entities.TV.Episode;
using Movie = MediaBrowser.Controller.Entities.Movies.Movie;
namespace Emby.Server.Implementations.LiveTv namespace Emby.Server.Implementations.LiveTv
{ {
@ -191,7 +189,7 @@ namespace Emby.Server.Implementations.LiveTv
IsKids = query.IsKids, IsKids = query.IsKids,
IsSports = query.IsSports, IsSports = query.IsSports,
IsSeries = query.IsSeries, IsSeries = query.IsSeries,
IncludeItemTypes = new[] { nameof(LiveTvChannel) }, IncludeItemTypes = new[] { BaseItemKind.LiveTvChannel },
TopParentIds = new[] { topFolder.Id }, TopParentIds = new[] { topFolder.Id },
IsFavorite = query.IsFavorite, IsFavorite = query.IsFavorite,
IsLiked = query.IsLiked, IsLiked = query.IsLiked,
@ -810,7 +808,7 @@ namespace Emby.Server.Implementations.LiveTv
var internalQuery = new InternalItemsQuery(user) var internalQuery = new InternalItemsQuery(user)
{ {
IncludeItemTypes = new[] { nameof(LiveTvProgram) }, IncludeItemTypes = new[] { BaseItemKind.LiveTvProgram },
MinEndDate = query.MinEndDate, MinEndDate = query.MinEndDate,
MinStartDate = query.MinStartDate, MinStartDate = query.MinStartDate,
MaxEndDate = query.MaxEndDate, MaxEndDate = query.MaxEndDate,
@ -874,7 +872,7 @@ namespace Emby.Server.Implementations.LiveTv
var internalQuery = new InternalItemsQuery(user) var internalQuery = new InternalItemsQuery(user)
{ {
IncludeItemTypes = new[] { nameof(LiveTvProgram) }, IncludeItemTypes = new[] { BaseItemKind.LiveTvProgram },
IsAiring = query.IsAiring, IsAiring = query.IsAiring,
HasAired = query.HasAired, HasAired = query.HasAired,
IsNews = query.IsNews, IsNews = query.IsNews,
@ -1085,8 +1083,8 @@ namespace Emby.Server.Implementations.LiveTv
if (cleanDatabase) if (cleanDatabase)
{ {
CleanDatabaseInternal(newChannelIdList.ToArray(), new[] { nameof(LiveTvChannel) }, progress, cancellationToken); CleanDatabaseInternal(newChannelIdList.ToArray(), new[] { BaseItemKind.LiveTvChannel }, progress, cancellationToken);
CleanDatabaseInternal(newProgramIdList.ToArray(), new[] { nameof(LiveTvProgram) }, progress, cancellationToken); CleanDatabaseInternal(newProgramIdList.ToArray(), new[] { BaseItemKind.LiveTvProgram }, progress, cancellationToken);
} }
var coreService = _services.OfType<EmbyTV.EmbyTV>().FirstOrDefault(); var coreService = _services.OfType<EmbyTV.EmbyTV>().FirstOrDefault();
@ -1177,7 +1175,7 @@ namespace Emby.Server.Implementations.LiveTv
var existingPrograms = _libraryManager.GetItemList(new InternalItemsQuery var existingPrograms = _libraryManager.GetItemList(new InternalItemsQuery
{ {
IncludeItemTypes = new string[] { nameof(LiveTvProgram) }, IncludeItemTypes = new[] { BaseItemKind.LiveTvProgram },
ChannelIds = new Guid[] { currentChannel.Id }, ChannelIds = new Guid[] { currentChannel.Id },
DtoOptions = new DtoOptions(true) DtoOptions = new DtoOptions(true)
}).Cast<LiveTvProgram>().ToDictionary(i => i.Id); }).Cast<LiveTvProgram>().ToDictionary(i => i.Id);
@ -1261,7 +1259,7 @@ namespace Emby.Server.Implementations.LiveTv
return new Tuple<List<Guid>, List<Guid>>(channels, programs); return new Tuple<List<Guid>, List<Guid>>(channels, programs);
} }
private void CleanDatabaseInternal(Guid[] currentIdList, string[] validTypes, IProgress<double> progress, CancellationToken cancellationToken) private void CleanDatabaseInternal(Guid[] currentIdList, BaseItemKind[] validTypes, IProgress<double> progress, CancellationToken cancellationToken)
{ {
var list = _itemRepo.GetItemIdsList(new InternalItemsQuery var list = _itemRepo.GetItemIdsList(new InternalItemsQuery
{ {
@ -1328,25 +1326,25 @@ namespace Emby.Server.Implementations.LiveTv
.Select(i => i.Id) .Select(i => i.Id)
.ToList(); .ToList();
var excludeItemTypes = new List<string>(); var excludeItemTypes = new List<BaseItemKind>();
if (folderIds.Count == 0) if (folderIds.Count == 0)
{ {
return new QueryResult<BaseItem>(); return new QueryResult<BaseItem>();
} }
var includeItemTypes = new List<string>(); var includeItemTypes = new List<BaseItemKind>();
var genres = new List<string>(); var genres = new List<string>();
if (query.IsMovie.HasValue) if (query.IsMovie.HasValue)
{ {
if (query.IsMovie.Value) if (query.IsMovie.Value)
{ {
includeItemTypes.Add(nameof(Movie)); includeItemTypes.Add(BaseItemKind.Movie);
} }
else else
{ {
excludeItemTypes.Add(nameof(Movie)); excludeItemTypes.Add(BaseItemKind.Movie);
} }
} }
@ -1354,11 +1352,11 @@ namespace Emby.Server.Implementations.LiveTv
{ {
if (query.IsSeries.Value) if (query.IsSeries.Value)
{ {
includeItemTypes.Add(nameof(Episode)); includeItemTypes.Add(BaseItemKind.Episode);
} }
else else
{ {
excludeItemTypes.Add(nameof(Episode)); excludeItemTypes.Add(BaseItemKind.Episode);
} }
} }
@ -1878,7 +1876,7 @@ namespace Emby.Server.Implementations.LiveTv
var programs = options.AddCurrentProgram ? _libraryManager.GetItemList(new InternalItemsQuery(user) var programs = options.AddCurrentProgram ? _libraryManager.GetItemList(new InternalItemsQuery(user)
{ {
IncludeItemTypes = new[] { nameof(LiveTvProgram) }, IncludeItemTypes = new[] { BaseItemKind.LiveTvProgram },
ChannelIds = channelIds, ChannelIds = channelIds,
MaxStartDate = now, MaxStartDate = now,
MinEndDate = now, MinEndDate = now,

View File

@ -3,7 +3,6 @@
#pragma warning disable CS1591 #pragma warning disable CS1591
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net; using System.Net;

View File

@ -10,6 +10,7 @@ using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Jellyfin.Extensions;
using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net; using MediaBrowser.Common.Net;
using MediaBrowser.Controller; using MediaBrowser.Controller;
@ -119,7 +120,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
{ {
var extension = Path.GetExtension(mediaSource.Path) ?? string.Empty; var extension = Path.GetExtension(mediaSource.Path) ?? string.Empty;
if (!_disallowedSharedStreamExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase)) if (!_disallowedSharedStreamExtensions.Contains(extension, StringComparison.OrdinalIgnoreCase))
{ {
return new SharedHttpStream(mediaSource, tunerHost, streamId, FileSystem, _httpClientFactory, Logger, Config, _appHost, _streamHelper); return new SharedHttpStream(mediaSource, tunerHost, streamId, FileSystem, _httpClientFactory, Logger, Config, _appHost, _streamHelper);
} }

View File

@ -10,5 +10,49 @@
"AuthenticationSucceededWithUserName": "{0} wedii ddilysun llwyddiannus", "AuthenticationSucceededWithUserName": "{0} wedii ddilysun llwyddiannus",
"Artists": "Artistiaid", "Artists": "Artistiaid",
"AppDeviceValues": "Ap: {0}, Dyfais: {1}", "AppDeviceValues": "Ap: {0}, Dyfais: {1}",
"Albums": "Albwmau" "Albums": "Albwmau",
"Genres": "Genres",
"Folders": "Ffolderi",
"Favorites": "Ffefrynnau",
"LabelRunningTimeValue": "Amser rhedeg: {0}",
"TaskOptimizeDatabase": "Cronfa ddata Optimeiddio",
"TaskRefreshChannels": "Adnewyddu Sianeli",
"TaskRefreshPeople": "Adnewyddu Pobl",
"TasksChannelsCategory": "Sianeli Internet",
"VersionNumber": "Fersiwn {0}",
"ScheduledTaskStartedWithName": "{0} wedi dechrau",
"ScheduledTaskFailedWithName": "{0} wedi methu",
"ProviderValue": "Darparwr: {0}",
"NotificationOptionInstallationFailed": "Fethu Gosod",
"NameSeasonUnknown": "Tymor Anhysbys",
"NameSeasonNumber": "Tymor {0}",
"MusicVideos": "Fideos Cerddoriaeth",
"MixedContent": "Cynnwys amrywiol",
"HomeVideos": "Fideos Cartref",
"HeaderNextUp": "Nesaf i Fyny",
"HeaderFavoriteArtists": "Ffefryn Artistiaid",
"HeaderFavoriteAlbums": "Ffefryn Albwmau",
"HeaderContinueWatching": "Parhewch i Weithio",
"TasksApplicationCategory": "Rhaglen",
"TasksLibraryCategory": "Llyfrgell",
"TasksMaintenanceCategory": "Cynnal a Chadw",
"System": "System",
"Plugin": "Ategyn",
"Music": "Cerddoriaeth",
"Latest": "Diweddaraf",
"Inherit": "Etifeddu",
"Forced": "Orfodi",
"Application": "Rhaglen",
"HeaderAlbumArtists": "Artistiaid albwm",
"Sync": "Cysoni",
"Songs": "Caneuon",
"Shows": "Rhaglenni",
"Playlists": "Rhestri Chwarae",
"Photos": "Lluniau",
"ValueSpecialEpisodeName": "Arbennig - {0}",
"Movies": "Ffilmiau",
"Undefined": "Heb ddiffiniad",
"TvShows": "Rhaglenni teledu",
"HeaderLiveTV": "Teledu Byw",
"User": "Defnyddiwr"
} }

View File

@ -117,5 +117,7 @@
"TaskCleanActivityLogDescription": "Tanggalin ang mga tala ng aktibidad na mas luma sa nakatakda na edad.", "TaskCleanActivityLogDescription": "Tanggalin ang mga tala ng aktibidad na mas luma sa nakatakda na edad.",
"Default": "Default", "Default": "Default",
"Undefined": "Hindi tiyak", "Undefined": "Hindi tiyak",
"Forced": "Sapilitan" "Forced": "Sapilitan",
"TaskOptimizeDatabaseDescription": "Iko-compact ang database at ita-truncate ang free space. Ang pagpapatakbo ng gawaing ito pagkatapos ng pag-scan sa library o paggawa ng iba pang mga pagbabago na nagpapahiwatig ng mga pagbabago sa database ay maaaring magpa-improve ng performance.",
"TaskOptimizeDatabase": "I-optimize ang database"
} }

View File

@ -2,7 +2,7 @@
"Albums": "Album-album", "Albums": "Album-album",
"AppDeviceValues": "Apl: {0}, Peranti: {1}", "AppDeviceValues": "Apl: {0}, Peranti: {1}",
"Application": "Aplikasi", "Application": "Aplikasi",
"Artists": "Artis", "Artists": "Artis-artis",
"AuthenticationSucceededWithUserName": "{0} berjaya disahkan", "AuthenticationSucceededWithUserName": "{0} berjaya disahkan",
"Books": "Buku-buku", "Books": "Buku-buku",
"CameraImageUploadedFrom": "Gambar baharu telah dimuat naik melalui {0}", "CameraImageUploadedFrom": "Gambar baharu telah dimuat naik melalui {0}",
@ -39,7 +39,7 @@
"MixedContent": "Kandungan campuran", "MixedContent": "Kandungan campuran",
"Movies": "Filem-filem", "Movies": "Filem-filem",
"Music": "Muzik", "Music": "Muzik",
"MusicVideos": "", "MusicVideos": "Video muzik",
"NameInstallFailed": "{0} pemasangan gagal", "NameInstallFailed": "{0} pemasangan gagal",
"NameSeasonNumber": "Musim {0}", "NameSeasonNumber": "Musim {0}",
"NameSeasonUnknown": "Musim Tidak Diketahui", "NameSeasonUnknown": "Musim Tidak Diketahui",
@ -75,7 +75,7 @@
"StartupEmbyServerIsLoading": "Pelayan Jellyfin sedang dimuatkan. Sila cuba sebentar lagi.", "StartupEmbyServerIsLoading": "Pelayan Jellyfin sedang dimuatkan. Sila cuba sebentar lagi.",
"SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}", "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}",
"SubtitleDownloadFailureFromForItem": "Muat turun sarikata gagal dari {0} untuk {1}", "SubtitleDownloadFailureFromForItem": "Muat turun sarikata gagal dari {0} untuk {1}",
"Sync": "", "Sync": "Segerak",
"System": "Sistem", "System": "Sistem",
"TvShows": "Tayangan TV", "TvShows": "Tayangan TV",
"User": "Pengguna", "User": "Pengguna",

View File

@ -21,7 +21,7 @@
"Inherit": "மரபுரிமையாகப் பெறு", "Inherit": "மரபுரிமையாகப் பெறு",
"HeaderRecordingGroups": "பதிவு குழுக்கள்", "HeaderRecordingGroups": "பதிவு குழுக்கள்",
"Folders": "கோப்புறைகள்", "Folders": "கோப்புறைகள்",
"FailedLoginAttemptWithUserName": "{0} இல் இருந்து உள்நுழைவு முயற்சி தோல்வியடைந்தது", "FailedLoginAttemptWithUserName": "{0} இன் உள்நுழைவு முயற்சி தோல்வியடைந்தது",
"DeviceOnlineWithName": "{0} இணைக்கப்பட்டது", "DeviceOnlineWithName": "{0} இணைக்கப்பட்டது",
"DeviceOfflineWithName": "{0} துண்டிக்கப்பட்டது", "DeviceOfflineWithName": "{0} துண்டிக்கப்பட்டது",
"Collections": "தொகுப்புகள்", "Collections": "தொகுப்புகள்",

View File

@ -310,7 +310,7 @@ namespace Emby.Server.Implementations.Localization
return _dictionaries.GetOrAdd( return _dictionaries.GetOrAdd(
culture, culture,
(key, localizationManager) => localizationManager.GetDictionary(Prefix, key, DefaultCulture + ".json").GetAwaiter().GetResult(), static (key, localizationManager) => localizationManager.GetDictionary(Prefix, key, DefaultCulture + ".json").GetAwaiter().GetResult(),
this); this);
} }

View File

@ -9,6 +9,7 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Jellyfin.Extensions;
using MediaBrowser.Controller.Chapters; using MediaBrowser.Controller.Chapters;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
@ -120,7 +121,7 @@ namespace Emby.Server.Implementations.MediaEncoder
var path = GetChapterImagePath(video, chapter.StartPositionTicks); var path = GetChapterImagePath(video, chapter.StartPositionTicks);
if (!currentImages.Contains(path, StringComparer.OrdinalIgnoreCase)) if (!currentImages.Contains(path, StringComparison.OrdinalIgnoreCase))
{ {
if (extractImages) if (extractImages)
{ {
@ -219,7 +220,7 @@ namespace Emby.Server.Implementations.MediaEncoder
{ {
var deadImages = images var deadImages = images
.Except(chapters.Select(i => i.ImagePath).Where(i => !string.IsNullOrEmpty(i)), StringComparer.OrdinalIgnoreCase) .Except(chapters.Select(i => i.ImagePath).Where(i => !string.IsNullOrEmpty(i)), StringComparer.OrdinalIgnoreCase)
.Where(i => BaseItem.SupportedImageExtensions.Contains(Path.GetExtension(i), StringComparer.OrdinalIgnoreCase)) .Where(i => BaseItem.SupportedImageExtensions.Contains(Path.GetExtension(i), StringComparison.OrdinalIgnoreCase))
.ToList(); .ToList();
foreach (var image in deadImages) foreach (var image in deadImages)

View File

@ -4,6 +4,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using Jellyfin.Data.Entities; using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Playlists; using MediaBrowser.Controller.Playlists;
using MediaBrowser.Model.Querying; using MediaBrowser.Model.Querying;
@ -45,7 +46,7 @@ namespace Emby.Server.Implementations.Playlists
} }
query.Recursive = true; query.Recursive = true;
query.IncludeItemTypes = new[] { "Playlist" }; query.IncludeItemTypes = new[] { BaseItemKind.Playlist };
query.Parent = null; query.Parent = null;
return LibraryManager.GetItemsResult(query); return LibraryManager.GetItemsResult(query);
} }

View File

@ -4,6 +4,7 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Jellyfin.Extensions;
using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
@ -143,7 +144,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
var key = video.Path + video.DateModified.Ticks; var key = video.Path + video.DateModified.Ticks;
var extract = !previouslyFailedImages.Contains(key, StringComparer.OrdinalIgnoreCase); var extract = !previouslyFailedImages.Contains(key, StringComparison.OrdinalIgnoreCase);
try try
{ {

View File

@ -21,7 +21,7 @@ namespace Emby.Server.Implementations.Serialization
private static XmlSerializer GetSerializer(Type type) private static XmlSerializer GetSerializer(Type type)
=> _serializers.GetOrAdd( => _serializers.GetOrAdd(
type.FullName ?? throw new ArgumentException($"Invalid type {type}."), type.FullName ?? throw new ArgumentException($"Invalid type {type}."),
(_, t) => new XmlSerializer(t), static (_, t) => new XmlSerializer(t),
type); type);
/// <summary> /// <summary>

View File

@ -1292,7 +1292,7 @@ namespace Emby.Server.Implementations.Session
{ {
["ItemId"] = command.ItemId, ["ItemId"] = command.ItemId,
["ItemName"] = command.ItemName, ["ItemName"] = command.ItemName,
["ItemType"] = command.ItemType ["ItemType"] = command.ItemType.ToString()
} }
}; };

View File

@ -116,7 +116,7 @@ namespace Emby.Server.Implementations.TV
.GetItemList( .GetItemList(
new InternalItemsQuery(user) new InternalItemsQuery(user)
{ {
IncludeItemTypes = new[] { nameof(Episode) }, IncludeItemTypes = new[] { BaseItemKind.Episode },
OrderBy = new[] { new ValueTuple<string, SortOrder>(ItemSortBy.DatePlayed, SortOrder.Descending) }, OrderBy = new[] { new ValueTuple<string, SortOrder>(ItemSortBy.DatePlayed, SortOrder.Descending) },
SeriesPresentationUniqueKey = presentationUniqueKey, SeriesPresentationUniqueKey = presentationUniqueKey,
Limit = limit, Limit = limit,
@ -191,7 +191,7 @@ namespace Emby.Server.Implementations.TV
{ {
AncestorWithPresentationUniqueKey = null, AncestorWithPresentationUniqueKey = null,
SeriesPresentationUniqueKey = seriesKey, SeriesPresentationUniqueKey = seriesKey,
IncludeItemTypes = new[] { nameof(Episode) }, IncludeItemTypes = new[] { BaseItemKind.Episode },
OrderBy = new[] { new ValueTuple<string, SortOrder>(ItemSortBy.SortName, SortOrder.Descending) }, OrderBy = new[] { new ValueTuple<string, SortOrder>(ItemSortBy.SortName, SortOrder.Descending) },
IsPlayed = true, IsPlayed = true,
Limit = 1, Limit = 1,
@ -209,7 +209,7 @@ namespace Emby.Server.Implementations.TV
{ {
AncestorWithPresentationUniqueKey = null, AncestorWithPresentationUniqueKey = null,
SeriesPresentationUniqueKey = seriesKey, SeriesPresentationUniqueKey = seriesKey,
IncludeItemTypes = new[] { nameof(Episode) }, IncludeItemTypes = new[] { BaseItemKind.Episode },
OrderBy = new[] { new ValueTuple<string, SortOrder>(ItemSortBy.SortName, SortOrder.Ascending) }, OrderBy = new[] { new ValueTuple<string, SortOrder>(ItemSortBy.SortName, SortOrder.Ascending) },
Limit = 1, Limit = 1,
IsPlayed = false, IsPlayed = false,
@ -226,7 +226,7 @@ namespace Emby.Server.Implementations.TV
AncestorWithPresentationUniqueKey = null, AncestorWithPresentationUniqueKey = null,
SeriesPresentationUniqueKey = seriesKey, SeriesPresentationUniqueKey = seriesKey,
ParentIndexNumber = 0, ParentIndexNumber = 0,
IncludeItemTypes = new[] { nameof(Episode) }, IncludeItemTypes = new[] { BaseItemKind.Episode },
IsPlayed = false, IsPlayed = false,
IsVirtualItem = false, IsVirtualItem = false,
DtoOptions = dtoOptions DtoOptions = dtoOptions

View File

@ -2,6 +2,7 @@ using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.IO.Compression;
using System.Linq; using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Net.Http.Json; using System.Net.Http.Json;
@ -19,7 +20,6 @@ using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Events; using MediaBrowser.Controller.Events;
using MediaBrowser.Controller.Events.Updates; using MediaBrowser.Controller.Events.Updates;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Plugins; using MediaBrowser.Model.Plugins;
using MediaBrowser.Model.Updates; using MediaBrowser.Model.Updates;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
@ -47,7 +47,6 @@ namespace Emby.Server.Implementations.Updates
/// </summary> /// </summary>
/// <value>The application host.</value> /// <value>The application host.</value>
private readonly IServerApplicationHost _applicationHost; private readonly IServerApplicationHost _applicationHost;
private readonly IZipClient _zipClient;
private readonly object _currentInstallationsLock = new object(); private readonly object _currentInstallationsLock = new object();
/// <summary> /// <summary>
@ -69,7 +68,6 @@ namespace Emby.Server.Implementations.Updates
/// <param name="eventManager">The <see cref="IEventManager"/>.</param> /// <param name="eventManager">The <see cref="IEventManager"/>.</param>
/// <param name="httpClientFactory">The <see cref="IHttpClientFactory"/>.</param> /// <param name="httpClientFactory">The <see cref="IHttpClientFactory"/>.</param>
/// <param name="config">The <see cref="IServerConfigurationManager"/>.</param> /// <param name="config">The <see cref="IServerConfigurationManager"/>.</param>
/// <param name="zipClient">The <see cref="IZipClient"/>.</param>
/// <param name="pluginManager">The <see cref="IPluginManager"/>.</param> /// <param name="pluginManager">The <see cref="IPluginManager"/>.</param>
public InstallationManager( public InstallationManager(
ILogger<InstallationManager> logger, ILogger<InstallationManager> logger,
@ -78,7 +76,6 @@ namespace Emby.Server.Implementations.Updates
IEventManager eventManager, IEventManager eventManager,
IHttpClientFactory httpClientFactory, IHttpClientFactory httpClientFactory,
IServerConfigurationManager config, IServerConfigurationManager config,
IZipClient zipClient,
IPluginManager pluginManager) IPluginManager pluginManager)
{ {
_currentInstallations = new List<(InstallationInfo, CancellationTokenSource)>(); _currentInstallations = new List<(InstallationInfo, CancellationTokenSource)>();
@ -90,7 +87,6 @@ namespace Emby.Server.Implementations.Updates
_eventManager = eventManager; _eventManager = eventManager;
_httpClientFactory = httpClientFactory; _httpClientFactory = httpClientFactory;
_config = config; _config = config;
_zipClient = zipClient;
_jsonSerializerOptions = JsonDefaults.Options; _jsonSerializerOptions = JsonDefaults.Options;
_pluginManager = pluginManager; _pluginManager = pluginManager;
} }
@ -560,7 +556,8 @@ namespace Emby.Server.Implementations.Updates
} }
stream.Position = 0; stream.Position = 0;
_zipClient.ExtractAllFromZip(stream, targetDir, true); using var reader = new ZipArchive(stream);
reader.ExtractToDirectory(targetDir, true);
await _pluginManager.GenerateManifest(package.PackageInfo, package.Version, targetDir, status).ConfigureAwait(false); await _pluginManager.GenerateManifest(package.PackageInfo, package.Version, targetDir, status).ConfigureAwait(false);
_pluginManager.ImportPluginFrom(targetDir); _pluginManager.ImportPluginFrom(targetDir);
} }

View File

@ -133,8 +133,8 @@ namespace Jellyfin.Api.Controllers
var query = new InternalItemsQuery(user) var query = new InternalItemsQuery(user)
{ {
ExcludeItemTypes = RequestHelpers.GetItemTypeStrings(excludeItemTypes), ExcludeItemTypes = excludeItemTypes,
IncludeItemTypes = RequestHelpers.GetItemTypeStrings(includeItemTypes), IncludeItemTypes = includeItemTypes,
MediaTypes = mediaTypes, MediaTypes = mediaTypes,
StartIndex = startIndex, StartIndex = startIndex,
Limit = limit, Limit = limit,
@ -337,8 +337,8 @@ namespace Jellyfin.Api.Controllers
var query = new InternalItemsQuery(user) var query = new InternalItemsQuery(user)
{ {
ExcludeItemTypes = RequestHelpers.GetItemTypeStrings(excludeItemTypes), ExcludeItemTypes = excludeItemTypes,
IncludeItemTypes = RequestHelpers.GetItemTypeStrings(includeItemTypes), IncludeItemTypes = includeItemTypes,
MediaTypes = mediaTypes, MediaTypes = mediaTypes,
StartIndex = startIndex, StartIndex = startIndex,
Limit = limit, Limit = limit,

View File

@ -1,7 +1,6 @@
using System; using System;
using System.Linq; using System.Linq;
using Jellyfin.Api.Constants; using Jellyfin.Api.Constants;
using Jellyfin.Api.Helpers;
using Jellyfin.Api.ModelBinders; using Jellyfin.Api.ModelBinders;
using Jellyfin.Data.Enums; using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Dto;
@ -71,7 +70,7 @@ namespace Jellyfin.Api.Controllers
{ {
User = user, User = user,
MediaTypes = mediaTypes, MediaTypes = mediaTypes,
IncludeItemTypes = RequestHelpers.GetItemTypeStrings(includeItemTypes), IncludeItemTypes = includeItemTypes,
Recursive = true, Recursive = true,
EnableTotalRecordCount = false, EnableTotalRecordCount = false,
DtoOptions = new DtoOptions DtoOptions = new DtoOptions
@ -166,7 +165,7 @@ namespace Jellyfin.Api.Controllers
var filters = new QueryFilters(); var filters = new QueryFilters();
var genreQuery = new InternalItemsQuery(user) var genreQuery = new InternalItemsQuery(user)
{ {
IncludeItemTypes = RequestHelpers.GetItemTypeStrings(includeItemTypes), IncludeItemTypes = includeItemTypes,
DtoOptions = new DtoOptions DtoOptions = new DtoOptions
{ {
Fields = Array.Empty<ItemFields>(), Fields = Array.Empty<ItemFields>(),

View File

@ -101,8 +101,8 @@ namespace Jellyfin.Api.Controllers
var query = new InternalItemsQuery(user) var query = new InternalItemsQuery(user)
{ {
ExcludeItemTypes = RequestHelpers.GetItemTypeStrings(excludeItemTypes), ExcludeItemTypes = excludeItemTypes,
IncludeItemTypes = RequestHelpers.GetItemTypeStrings(includeItemTypes), IncludeItemTypes = includeItemTypes,
StartIndex = startIndex, StartIndex = startIndex,
Limit = limit, Limit = limit,
IsFavorite = isFavorite, IsFavorite = isFavorite,
@ -160,7 +160,7 @@ namespace Jellyfin.Api.Controllers
Genre item = new Genre(); Genre item = new Genre();
if (genreName.IndexOf(BaseItem.SlugChar, StringComparison.OrdinalIgnoreCase) != -1) if (genreName.IndexOf(BaseItem.SlugChar, StringComparison.OrdinalIgnoreCase) != -1)
{ {
var result = GetItemFromSlugName<Genre>(_libraryManager, genreName, dtoOptions); var result = GetItemFromSlugName<Genre>(_libraryManager, genreName, dtoOptions, BaseItemKind.Genre);
if (result != null) if (result != null)
{ {
@ -182,27 +182,27 @@ namespace Jellyfin.Api.Controllers
return _dtoService.GetBaseItemDto(item, dtoOptions); return _dtoService.GetBaseItemDto(item, dtoOptions);
} }
private T? GetItemFromSlugName<T>(ILibraryManager libraryManager, string name, DtoOptions dtoOptions) private T? GetItemFromSlugName<T>(ILibraryManager libraryManager, string name, DtoOptions dtoOptions, BaseItemKind baseItemKind)
where T : BaseItem, new() where T : BaseItem, new()
{ {
var result = libraryManager.GetItemList(new InternalItemsQuery var result = libraryManager.GetItemList(new InternalItemsQuery
{ {
Name = name.Replace(BaseItem.SlugChar, '&'), Name = name.Replace(BaseItem.SlugChar, '&'),
IncludeItemTypes = new[] { typeof(T).Name }, IncludeItemTypes = new[] { baseItemKind },
DtoOptions = dtoOptions DtoOptions = dtoOptions
}).OfType<T>().FirstOrDefault(); }).OfType<T>().FirstOrDefault();
result ??= libraryManager.GetItemList(new InternalItemsQuery result ??= libraryManager.GetItemList(new InternalItemsQuery
{ {
Name = name.Replace(BaseItem.SlugChar, '/'), Name = name.Replace(BaseItem.SlugChar, '/'),
IncludeItemTypes = new[] { typeof(T).Name }, IncludeItemTypes = new[] { baseItemKind },
DtoOptions = dtoOptions DtoOptions = dtoOptions
}).OfType<T>().FirstOrDefault(); }).OfType<T>().FirstOrDefault();
result ??= libraryManager.GetItemList(new InternalItemsQuery result ??= libraryManager.GetItemList(new InternalItemsQuery
{ {
Name = name.Replace(BaseItem.SlugChar, '?'), Name = name.Replace(BaseItem.SlugChar, '?'),
IncludeItemTypes = new[] { typeof(T).Name }, IncludeItemTypes = new[] { baseItemKind },
DtoOptions = dtoOptions DtoOptions = dtoOptions
}).OfType<T>().FirstOrDefault(); }).OfType<T>().FirstOrDefault();

View File

@ -8,7 +8,6 @@ using Jellyfin.Api.ModelBinders;
using Jellyfin.Data.Enums; using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Session; using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Dto; using MediaBrowser.Model.Dto;
@ -296,8 +295,8 @@ namespace Jellyfin.Api.Controllers
{ {
IsPlayed = isPlayed, IsPlayed = isPlayed,
MediaTypes = mediaTypes, MediaTypes = mediaTypes,
IncludeItemTypes = RequestHelpers.GetItemTypeStrings(includeItemTypes), IncludeItemTypes = includeItemTypes,
ExcludeItemTypes = RequestHelpers.GetItemTypeStrings(excludeItemTypes), ExcludeItemTypes = excludeItemTypes,
Recursive = recursive ?? false, Recursive = recursive ?? false,
OrderBy = RequestHelpers.GetOrderBy(sortBy, sortOrder), OrderBy = RequestHelpers.GetOrderBy(sortBy, sortOrder),
IsFavorite = isFavorite, IsFavorite = isFavorite,
@ -459,7 +458,7 @@ namespace Jellyfin.Api.Controllers
{ {
query.AlbumIds = albums.SelectMany(i => query.AlbumIds = albums.SelectMany(i =>
{ {
return _libraryManager.GetItemIds(new InternalItemsQuery { IncludeItemTypes = new[] { nameof(MusicAlbum) }, Name = i, Limit = 1 }); return _libraryManager.GetItemIds(new InternalItemsQuery { IncludeItemTypes = new[] { BaseItemKind.MusicAlbum }, Name = i, Limit = 1 });
}).ToArray(); }).ToArray();
} }
@ -483,7 +482,7 @@ namespace Jellyfin.Api.Controllers
if (query.OrderBy.Count == 0) if (query.OrderBy.Count == 0)
{ {
// Albums by artist // Albums by artist
if (query.ArtistIds.Length > 0 && query.IncludeItemTypes.Length == 1 && string.Equals(query.IncludeItemTypes[0], "MusicAlbum", StringComparison.OrdinalIgnoreCase)) if (query.ArtistIds.Length > 0 && query.IncludeItemTypes.Length == 1 && query.IncludeItemTypes[0] == BaseItemKind.MusicAlbum)
{ {
query.OrderBy = new[] { new ValueTuple<string, SortOrder>(ItemSortBy.ProductionYear, SortOrder.Descending), new ValueTuple<string, SortOrder>(ItemSortBy.SortName, SortOrder.Ascending) }; query.OrderBy = new[] { new ValueTuple<string, SortOrder>(ItemSortBy.ProductionYear, SortOrder.Descending), new ValueTuple<string, SortOrder>(ItemSortBy.SortName, SortOrder.Ascending) };
} }
@ -831,8 +830,8 @@ namespace Jellyfin.Api.Controllers
CollapseBoxSetItems = false, CollapseBoxSetItems = false,
EnableTotalRecordCount = enableTotalRecordCount, EnableTotalRecordCount = enableTotalRecordCount,
AncestorIds = ancestorIds, AncestorIds = ancestorIds,
IncludeItemTypes = RequestHelpers.GetItemTypeStrings(includeItemTypes), IncludeItemTypes = includeItemTypes,
ExcludeItemTypes = RequestHelpers.GetItemTypeStrings(excludeItemTypes), ExcludeItemTypes = excludeItemTypes,
SearchTerm = searchTerm, SearchTerm = searchTerm,
ExcludeItemIds = excludeItemIds ExcludeItemIds = excludeItemIds
}); });

View File

@ -14,6 +14,8 @@ using Jellyfin.Api.Extensions;
using Jellyfin.Api.ModelBinders; using Jellyfin.Api.ModelBinders;
using Jellyfin.Api.Models.LibraryDtos; using Jellyfin.Api.Models.LibraryDtos;
using Jellyfin.Data.Entities; using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
using Jellyfin.Extensions;
using MediaBrowser.Common.Progress; using MediaBrowser.Common.Progress;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Dto;
@ -22,7 +24,6 @@ using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.Movies; 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.LiveTv;
using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Activity; using MediaBrowser.Model.Activity;
@ -36,7 +37,6 @@ using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Book = MediaBrowser.Controller.Entities.Book;
namespace Jellyfin.Api.Controllers namespace Jellyfin.Api.Controllers
{ {
@ -413,14 +413,14 @@ namespace Jellyfin.Api.Controllers
var counts = new ItemCounts var counts = new ItemCounts
{ {
AlbumCount = GetCount(typeof(MusicAlbum), user, isFavorite), AlbumCount = GetCount(BaseItemKind.MusicAlbum, user, isFavorite),
EpisodeCount = GetCount(typeof(Episode), user, isFavorite), EpisodeCount = GetCount(BaseItemKind.Episode, user, isFavorite),
MovieCount = GetCount(typeof(Movie), user, isFavorite), MovieCount = GetCount(BaseItemKind.Movie, user, isFavorite),
SeriesCount = GetCount(typeof(Series), user, isFavorite), SeriesCount = GetCount(BaseItemKind.Series, user, isFavorite),
SongCount = GetCount(typeof(Audio), user, isFavorite), SongCount = GetCount(BaseItemKind.Audio, user, isFavorite),
MusicVideoCount = GetCount(typeof(MusicVideo), user, isFavorite), MusicVideoCount = GetCount(BaseItemKind.MusicVideo, user, isFavorite),
BoxSetCount = GetCount(typeof(BoxSet), user, isFavorite), BoxSetCount = GetCount(BaseItemKind.BoxSet, user, isFavorite),
BookCount = GetCount(typeof(Book), user, isFavorite) BookCount = GetCount(BaseItemKind.Book, user, isFavorite)
}; };
return counts; return counts;
@ -529,7 +529,7 @@ namespace Jellyfin.Api.Controllers
{ {
var series = _libraryManager.GetItemList(new InternalItemsQuery var series = _libraryManager.GetItemList(new InternalItemsQuery
{ {
IncludeItemTypes = new[] { nameof(Series) }, IncludeItemTypes = new[] { BaseItemKind.Series },
DtoOptions = new DtoOptions(false) DtoOptions = new DtoOptions(false)
{ {
EnableImages = false EnableImages = false
@ -559,7 +559,7 @@ namespace Jellyfin.Api.Controllers
{ {
var movies = _libraryManager.GetItemList(new InternalItemsQuery var movies = _libraryManager.GetItemList(new InternalItemsQuery
{ {
IncludeItemTypes = new[] { nameof(Movie) }, IncludeItemTypes = new[] { BaseItemKind.Movie },
DtoOptions = new DtoOptions(false) DtoOptions = new DtoOptions(false)
{ {
EnableImages = false EnableImages = false
@ -715,30 +715,31 @@ namespace Jellyfin.Api.Controllers
bool? isMovie = item is Movie || (program != null && program.IsMovie) || item is Trailer; bool? isMovie = item is Movie || (program != null && program.IsMovie) || item is Trailer;
bool? isSeries = item is Series || (program != null && program.IsSeries); bool? isSeries = item is Series || (program != null && program.IsSeries);
var includeItemTypes = new List<string>(); var includeItemTypes = new List<BaseItemKind>();
if (isMovie.Value) if (isMovie.Value)
{ {
includeItemTypes.Add(nameof(Movie)); includeItemTypes.Add(BaseItemKind.Movie);
if (_serverConfigurationManager.Configuration.EnableExternalContentInSuggestions) if (_serverConfigurationManager.Configuration.EnableExternalContentInSuggestions)
{ {
includeItemTypes.Add(nameof(Trailer)); includeItemTypes.Add(BaseItemKind.Trailer);
includeItemTypes.Add(nameof(LiveTvProgram)); includeItemTypes.Add(BaseItemKind.LiveTvProgram);
} }
} }
else if (isSeries.Value) else if (isSeries.Value)
{ {
includeItemTypes.Add(nameof(Series)); includeItemTypes.Add(BaseItemKind.Series);
} }
else else
{ {
// For non series and movie types these columns are typically null // For non series and movie types these columns are typically null
isSeries = null; isSeries = null;
isMovie = null; isMovie = null;
includeItemTypes.Add(item.GetType().Name); includeItemTypes.Add(item.GetBaseItemKind());
} }
var query = new InternalItemsQuery(user) var query = new InternalItemsQuery(user)
{ {
Genres = item.Genres,
Limit = limit, Limit = limit,
IncludeItemTypes = includeItemTypes.ToArray(), IncludeItemTypes = includeItemTypes.ToArray(),
SimilarTo = item, SimilarTo = item,
@ -785,7 +786,7 @@ namespace Jellyfin.Api.Controllers
var typesList = types.ToList(); var typesList = types.ToList();
var plugins = _providerManager.GetAllMetadataPlugins() var plugins = _providerManager.GetAllMetadataPlugins()
.Where(i => types.Contains(i.ItemType, StringComparer.OrdinalIgnoreCase)) .Where(i => types.Contains(i.ItemType, StringComparison.OrdinalIgnoreCase))
.OrderBy(i => typesList.IndexOf(i.ItemType)) .OrderBy(i => typesList.IndexOf(i.ItemType))
.ToList(); .ToList();
@ -871,11 +872,11 @@ namespace Jellyfin.Api.Controllers
return result; return result;
} }
private int GetCount(Type type, User? user, bool? isFavorite) private int GetCount(BaseItemKind itemKind, User? user, bool? isFavorite)
{ {
var query = new InternalItemsQuery(user) var query = new InternalItemsQuery(user)
{ {
IncludeItemTypes = new[] { type.Name }, IncludeItemTypes = new[] { itemKind },
Limit = 0, Limit = 0,
Recursive = true, Recursive = true,
IsVirtualItem = false, IsVirtualItem = false,
@ -940,10 +941,10 @@ namespace Jellyfin.Api.Controllers
} }
var metadataOptions = _serverConfigurationManager.Configuration.MetadataOptions var metadataOptions = _serverConfigurationManager.Configuration.MetadataOptions
.Where(i => itemTypes.Contains(i.ItemType ?? string.Empty, StringComparer.OrdinalIgnoreCase)) .Where(i => itemTypes.Contains(i.ItemType ?? string.Empty, StringComparison.OrdinalIgnoreCase))
.ToArray(); .ToArray();
return metadataOptions.Length == 0 || metadataOptions.Any(i => !i.DisabledMetadataSavers.Contains(name, StringComparer.OrdinalIgnoreCase)); return metadataOptions.Length == 0 || metadataOptions.Any(i => !i.DisabledMetadataSavers.Contains(name, StringComparison.OrdinalIgnoreCase));
} }
private bool IsMetadataFetcherEnabledByDefault(string name, string type, bool isNewLibrary) private bool IsMetadataFetcherEnabledByDefault(string name, string type, bool isNewLibrary)
@ -967,7 +968,7 @@ namespace Jellyfin.Api.Controllers
.ToArray(); .ToArray();
return metadataOptions.Length == 0 return metadataOptions.Length == 0
|| metadataOptions.Any(i => !i.DisabledMetadataFetchers.Contains(name, StringComparer.OrdinalIgnoreCase)); || metadataOptions.Any(i => !i.DisabledMetadataFetchers.Contains(name, StringComparison.OrdinalIgnoreCase));
} }
private bool IsImageFetcherEnabledByDefault(string name, string type, bool isNewLibrary) private bool IsImageFetcherEnabledByDefault(string name, string type, bool isNewLibrary)
@ -997,7 +998,7 @@ namespace Jellyfin.Api.Controllers
return true; return true;
} }
return metadataOptions.Any(i => !i.DisabledImageFetchers.Contains(name, StringComparer.OrdinalIgnoreCase)); return metadataOptions.Any(i => !i.DisabledImageFetchers.Contains(name, StringComparison.OrdinalIgnoreCase));
} }
} }
} }

View File

@ -11,9 +11,7 @@ using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Model.Dto; using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Querying; using MediaBrowser.Model.Querying;
@ -84,7 +82,7 @@ namespace Jellyfin.Api.Controllers
{ {
IncludeItemTypes = new[] IncludeItemTypes = new[]
{ {
nameof(Movie), BaseItemKind.Movie,
// nameof(Trailer), // nameof(Trailer),
// nameof(LiveTvProgram) // nameof(LiveTvProgram)
}, },
@ -99,11 +97,11 @@ namespace Jellyfin.Api.Controllers
var recentlyPlayedMovies = _libraryManager.GetItemList(query); var recentlyPlayedMovies = _libraryManager.GetItemList(query);
var itemTypes = new List<string> { nameof(Movie) }; var itemTypes = new List<BaseItemKind> { BaseItemKind.Movie };
if (_serverConfigurationManager.Configuration.EnableExternalContentInSuggestions) if (_serverConfigurationManager.Configuration.EnableExternalContentInSuggestions)
{ {
itemTypes.Add(nameof(Trailer)); itemTypes.Add(BaseItemKind.Trailer);
itemTypes.Add(nameof(LiveTvProgram)); itemTypes.Add(BaseItemKind.LiveTvProgram);
} }
var likedMovies = _libraryManager.GetItemList(new InternalItemsQuery(user) var likedMovies = _libraryManager.GetItemList(new InternalItemsQuery(user)
@ -182,11 +180,11 @@ namespace Jellyfin.Api.Controllers
DtoOptions dtoOptions, DtoOptions dtoOptions,
RecommendationType type) RecommendationType type)
{ {
var itemTypes = new List<string> { nameof(Movie) }; var itemTypes = new List<BaseItemKind> { BaseItemKind.Movie };
if (_serverConfigurationManager.Configuration.EnableExternalContentInSuggestions) if (_serverConfigurationManager.Configuration.EnableExternalContentInSuggestions)
{ {
itemTypes.Add(nameof(Trailer)); itemTypes.Add(BaseItemKind.Trailer);
itemTypes.Add(nameof(LiveTvProgram)); itemTypes.Add(BaseItemKind.LiveTvProgram);
} }
foreach (var name in names) foreach (var name in names)
@ -224,11 +222,11 @@ namespace Jellyfin.Api.Controllers
private IEnumerable<RecommendationDto> GetWithActor(User? user, IEnumerable<string> names, int itemLimit, DtoOptions dtoOptions, RecommendationType type) private IEnumerable<RecommendationDto> GetWithActor(User? user, IEnumerable<string> names, int itemLimit, DtoOptions dtoOptions, RecommendationType type)
{ {
var itemTypes = new List<string> { nameof(Movie) }; var itemTypes = new List<BaseItemKind> { BaseItemKind.Movie };
if (_serverConfigurationManager.Configuration.EnableExternalContentInSuggestions) if (_serverConfigurationManager.Configuration.EnableExternalContentInSuggestions)
{ {
itemTypes.Add(nameof(Trailer)); itemTypes.Add(BaseItemKind.Trailer);
itemTypes.Add(nameof(LiveTvProgram)); itemTypes.Add(BaseItemKind.LiveTvProgram);
} }
foreach (var name in names) foreach (var name in names)
@ -264,11 +262,11 @@ namespace Jellyfin.Api.Controllers
private IEnumerable<RecommendationDto> GetSimilarTo(User? user, IEnumerable<BaseItem> baselineItems, int itemLimit, DtoOptions dtoOptions, RecommendationType type) private IEnumerable<RecommendationDto> GetSimilarTo(User? user, IEnumerable<BaseItem> baselineItems, int itemLimit, DtoOptions dtoOptions, RecommendationType type)
{ {
var itemTypes = new List<string> { nameof(Movie) }; var itemTypes = new List<BaseItemKind> { BaseItemKind.Movie };
if (_serverConfigurationManager.Configuration.EnableExternalContentInSuggestions) if (_serverConfigurationManager.Configuration.EnableExternalContentInSuggestions)
{ {
itemTypes.Add(nameof(Trailer)); itemTypes.Add(BaseItemKind.Trailer);
itemTypes.Add(nameof(LiveTvProgram)); itemTypes.Add(BaseItemKind.LiveTvProgram);
} }
foreach (var item in baselineItems) foreach (var item in baselineItems)

View File

@ -101,8 +101,8 @@ namespace Jellyfin.Api.Controllers
var query = new InternalItemsQuery(user) var query = new InternalItemsQuery(user)
{ {
ExcludeItemTypes = RequestHelpers.GetItemTypeStrings(excludeItemTypes), ExcludeItemTypes = excludeItemTypes,
IncludeItemTypes = RequestHelpers.GetItemTypeStrings(includeItemTypes), IncludeItemTypes = includeItemTypes,
StartIndex = startIndex, StartIndex = startIndex,
Limit = limit, Limit = limit,
IsFavorite = isFavorite, IsFavorite = isFavorite,
@ -149,7 +149,7 @@ namespace Jellyfin.Api.Controllers
if (genreName.IndexOf(BaseItem.SlugChar, StringComparison.OrdinalIgnoreCase) != -1) if (genreName.IndexOf(BaseItem.SlugChar, StringComparison.OrdinalIgnoreCase) != -1)
{ {
item = GetItemFromSlugName<MusicGenre>(_libraryManager, genreName, dtoOptions); item = GetItemFromSlugName<MusicGenre>(_libraryManager, genreName, dtoOptions, BaseItemKind.MusicGenre);
} }
else else
{ {
@ -166,27 +166,27 @@ namespace Jellyfin.Api.Controllers
return _dtoService.GetBaseItemDto(item, dtoOptions); return _dtoService.GetBaseItemDto(item, dtoOptions);
} }
private T? GetItemFromSlugName<T>(ILibraryManager libraryManager, string name, DtoOptions dtoOptions) private T? GetItemFromSlugName<T>(ILibraryManager libraryManager, string name, DtoOptions dtoOptions, BaseItemKind baseItemKind)
where T : BaseItem, new() where T : BaseItem, new()
{ {
var result = libraryManager.GetItemList(new InternalItemsQuery var result = libraryManager.GetItemList(new InternalItemsQuery
{ {
Name = name.Replace(BaseItem.SlugChar, '&'), Name = name.Replace(BaseItem.SlugChar, '&'),
IncludeItemTypes = new[] { typeof(T).Name }, IncludeItemTypes = new[] { baseItemKind },
DtoOptions = dtoOptions DtoOptions = dtoOptions
}).OfType<T>().FirstOrDefault(); }).OfType<T>().FirstOrDefault();
result ??= libraryManager.GetItemList(new InternalItemsQuery result ??= libraryManager.GetItemList(new InternalItemsQuery
{ {
Name = name.Replace(BaseItem.SlugChar, '/'), Name = name.Replace(BaseItem.SlugChar, '/'),
IncludeItemTypes = new[] { typeof(T).Name }, IncludeItemTypes = new[] { baseItemKind },
DtoOptions = dtoOptions DtoOptions = dtoOptions
}).OfType<T>().FirstOrDefault(); }).OfType<T>().FirstOrDefault();
result ??= libraryManager.GetItemList(new InternalItemsQuery result ??= libraryManager.GetItemList(new InternalItemsQuery
{ {
Name = name.Replace(BaseItem.SlugChar, '?'), Name = name.Replace(BaseItem.SlugChar, '?'),
IncludeItemTypes = new[] { typeof(T).Name }, IncludeItemTypes = new[] { baseItemKind },
DtoOptions = dtoOptions DtoOptions = dtoOptions
}).OfType<T>().FirstOrDefault(); }).OfType<T>().FirstOrDefault();

View File

@ -9,7 +9,6 @@ using Jellyfin.Api.Attributes;
using Jellyfin.Api.Constants; using Jellyfin.Api.Constants;
using Jellyfin.Api.Models.PluginDtos; using Jellyfin.Api.Models.PluginDtos;
using Jellyfin.Extensions.Json; using Jellyfin.Extensions.Json;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Plugins; using MediaBrowser.Common.Plugins;
using MediaBrowser.Common.Updates; using MediaBrowser.Common.Updates;
using MediaBrowser.Model.Net; using MediaBrowser.Model.Net;

View File

@ -3,18 +3,13 @@ using System.Collections.Generic;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net.Http;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Jellyfin.Api.Constants; using Jellyfin.Api.Constants;
using Jellyfin.Extensions;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller; using MediaBrowser.Controller;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Providers; using MediaBrowser.Model.Providers;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;

View File

@ -4,7 +4,6 @@ using System.ComponentModel.DataAnnotations;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
using Jellyfin.Api.Constants; using Jellyfin.Api.Constants;
using Jellyfin.Api.Helpers;
using Jellyfin.Api.ModelBinders; using Jellyfin.Api.ModelBinders;
using Jellyfin.Data.Enums; using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Drawing;
@ -110,8 +109,8 @@ namespace Jellyfin.Api.Controllers
IncludeStudios = includeStudios, IncludeStudios = includeStudios,
StartIndex = startIndex, StartIndex = startIndex,
UserId = userId ?? Guid.Empty, UserId = userId ?? Guid.Empty,
IncludeItemTypes = RequestHelpers.GetItemTypeStrings(includeItemTypes), IncludeItemTypes = includeItemTypes,
ExcludeItemTypes = RequestHelpers.GetItemTypeStrings(excludeItemTypes), ExcludeItemTypes = excludeItemTypes,
MediaTypes = mediaTypes, MediaTypes = mediaTypes,
ParentId = parentId, ParentId = parentId,

View File

@ -127,7 +127,7 @@ namespace Jellyfin.Api.Controllers
[ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status204NoContent)]
public async Task<ActionResult> DisplayContent( public async Task<ActionResult> DisplayContent(
[FromRoute, Required] string sessionId, [FromRoute, Required] string sessionId,
[FromQuery, Required] string itemType, [FromQuery, Required] BaseItemKind itemType,
[FromQuery, Required] string itemId, [FromQuery, Required] string itemId,
[FromQuery, Required] string itemName) [FromQuery, Required] string itemName)
{ {

View File

@ -97,8 +97,8 @@ namespace Jellyfin.Api.Controllers
var query = new InternalItemsQuery(user) var query = new InternalItemsQuery(user)
{ {
ExcludeItemTypes = RequestHelpers.GetItemTypeStrings(excludeItemTypes), ExcludeItemTypes = excludeItemTypes,
IncludeItemTypes = RequestHelpers.GetItemTypeStrings(includeItemTypes), IncludeItemTypes = includeItemTypes,
StartIndex = startIndex, StartIndex = startIndex,
Limit = limit, Limit = limit,
IsFavorite = isFavorite, IsFavorite = isFavorite,

View File

@ -58,7 +58,7 @@ namespace Jellyfin.Api.Controllers
public ActionResult<QueryResult<BaseItemDto>> GetSuggestions( public ActionResult<QueryResult<BaseItemDto>> GetSuggestions(
[FromRoute, Required] Guid userId, [FromRoute, Required] Guid userId,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] mediaType, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] mediaType,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] type, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] BaseItemKind[] type,
[FromQuery] int? startIndex, [FromQuery] int? startIndex,
[FromQuery] int? limit, [FromQuery] int? limit,
[FromQuery] bool enableTotalRecordCount = false) [FromQuery] bool enableTotalRecordCount = false)

View File

@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net;
using System.Net.Mime; using System.Net.Mime;
using System.Threading.Tasks; using System.Threading.Tasks;
using Jellyfin.Api.Attributes; using Jellyfin.Api.Attributes;

View File

@ -61,7 +61,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="fields">Optional. Specify additional fields of information to return in the output.</param> /// <param name="fields">Optional. Specify additional fields of information to return in the output.</param>
/// <param name="seriesId">Optional. Filter by series id.</param> /// <param name="seriesId">Optional. Filter by series id.</param>
/// <param name="parentId">Optional. Specify this to localize the search to a specific item or folder. Omit to use the root.</param> /// <param name="parentId">Optional. Specify this to localize the search to a specific item or folder. Omit to use the root.</param>
/// <param name="enableImges">Optional. Include image information in output.</param> /// <param name="enableImages">Optional. Include image information in output.</param>
/// <param name="imageTypeLimit">Optional. The max number of images to return, per image type.</param> /// <param name="imageTypeLimit">Optional. The max number of images to return, per image type.</param>
/// <param name="enableImageTypes">Optional. The image types to include in the output.</param> /// <param name="enableImageTypes">Optional. The image types to include in the output.</param>
/// <param name="enableUserData">Optional. Include user data.</param> /// <param name="enableUserData">Optional. Include user data.</param>
@ -78,7 +78,7 @@ namespace Jellyfin.Api.Controllers
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields,
[FromQuery] string? seriesId, [FromQuery] string? seriesId,
[FromQuery] Guid? parentId, [FromQuery] Guid? parentId,
[FromQuery] bool? enableImges, [FromQuery] bool? enableImages,
[FromQuery] int? imageTypeLimit, [FromQuery] int? imageTypeLimit,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] enableImageTypes, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] enableImageTypes,
[FromQuery] bool? enableUserData, [FromQuery] bool? enableUserData,
@ -88,7 +88,7 @@ namespace Jellyfin.Api.Controllers
{ {
var options = new DtoOptions { Fields = fields } var options = new DtoOptions { Fields = fields }
.AddClientFields(Request) .AddClientFields(Request)
.AddAdditionalDtoOptions(enableImges, enableUserData, imageTypeLimit, enableImageTypes); .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes);
var result = _tvSeriesManager.GetNextUp( var result = _tvSeriesManager.GetNextUp(
new NextUpQuery new NextUpQuery
@ -125,7 +125,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="limit">Optional. The maximum number of records to return.</param> /// <param name="limit">Optional. The maximum number of records to return.</param>
/// <param name="fields">Optional. Specify additional fields of information to return in the output.</param> /// <param name="fields">Optional. Specify additional fields of information to return in the output.</param>
/// <param name="parentId">Optional. Specify this to localize the search to a specific item or folder. Omit to use the root.</param> /// <param name="parentId">Optional. Specify this to localize the search to a specific item or folder. Omit to use the root.</param>
/// <param name="enableImges">Optional. Include image information in output.</param> /// <param name="enableImages">Optional. Include image information in output.</param>
/// <param name="imageTypeLimit">Optional. The max number of images to return, per image type.</param> /// <param name="imageTypeLimit">Optional. The max number of images to return, per image type.</param>
/// <param name="enableImageTypes">Optional. The image types to include in the output.</param> /// <param name="enableImageTypes">Optional. The image types to include in the output.</param>
/// <param name="enableUserData">Optional. Include user data.</param> /// <param name="enableUserData">Optional. Include user data.</param>
@ -138,7 +138,7 @@ namespace Jellyfin.Api.Controllers
[FromQuery] int? limit, [FromQuery] int? limit,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields,
[FromQuery] Guid? parentId, [FromQuery] Guid? parentId,
[FromQuery] bool? enableImges, [FromQuery] bool? enableImages,
[FromQuery] int? imageTypeLimit, [FromQuery] int? imageTypeLimit,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] enableImageTypes, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] enableImageTypes,
[FromQuery] bool? enableUserData) [FromQuery] bool? enableUserData)
@ -153,11 +153,11 @@ namespace Jellyfin.Api.Controllers
var options = new DtoOptions { Fields = fields } var options = new DtoOptions { Fields = fields }
.AddClientFields(Request) .AddClientFields(Request)
.AddAdditionalDtoOptions(enableImges, enableUserData, imageTypeLimit, enableImageTypes); .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes);
var itemsResult = _libraryManager.GetItemList(new InternalItemsQuery(user) var itemsResult = _libraryManager.GetItemList(new InternalItemsQuery(user)
{ {
IncludeItemTypes = new[] { nameof(Episode) }, IncludeItemTypes = new[] { BaseItemKind.Episode },
OrderBy = new[] { (ItemSortBy.PremiereDate, SortOrder.Ascending), (ItemSortBy.SortName, SortOrder.Ascending) }, OrderBy = new[] { (ItemSortBy.PremiereDate, SortOrder.Ascending), (ItemSortBy.SortName, SortOrder.Ascending) },
MinPremiereDate = minPremiereDate, MinPremiereDate = minPremiereDate,
StartIndex = startIndex, StartIndex = startIndex,

View File

@ -6,7 +6,6 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Jellyfin.Api.Constants; using Jellyfin.Api.Constants;
using Jellyfin.Api.Extensions; using Jellyfin.Api.Extensions;
using Jellyfin.Api.Helpers;
using Jellyfin.Api.ModelBinders; using Jellyfin.Api.ModelBinders;
using Jellyfin.Data.Enums; using Jellyfin.Data.Enums;
using Jellyfin.Extensions; using Jellyfin.Extensions;
@ -213,7 +212,7 @@ namespace Jellyfin.Api.Controllers
if (item is IHasTrailers hasTrailers) if (item is IHasTrailers hasTrailers)
{ {
var trailers = hasTrailers.GetTrailers(); var trailers = hasTrailers.LocalTrailers;
var dtosTrailers = _dtoService.GetBaseItemDtos(trailers, dtoOptions, user, item); var dtosTrailers = _dtoService.GetBaseItemDtos(trailers, dtoOptions, user, item);
var allTrailers = new BaseItemDto[dtosExtras.Length + dtosTrailers.Count]; var allTrailers = new BaseItemDto[dtosExtras.Length + dtosTrailers.Count];
dtosExtras.CopyTo(allTrailers, 0); dtosExtras.CopyTo(allTrailers, 0);
@ -297,7 +296,7 @@ namespace Jellyfin.Api.Controllers
new LatestItemsQuery new LatestItemsQuery
{ {
GroupItems = groupItems, GroupItems = groupItems,
IncludeItemTypes = RequestHelpers.GetItemTypeStrings(includeItemTypes), IncludeItemTypes = includeItemTypes,
IsPlayed = isPlayed, IsPlayed = isPlayed,
Limit = limit, Limit = limit,
ParentId = parentId ?? Guid.Empty, ParentId = parentId ?? Guid.Empty,

View File

@ -8,6 +8,7 @@ using Jellyfin.Api.Helpers;
using Jellyfin.Api.ModelBinders; using Jellyfin.Api.ModelBinders;
using Jellyfin.Data.Entities; using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums; using Jellyfin.Data.Enums;
using Jellyfin.Extensions;
using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
@ -101,8 +102,8 @@ namespace Jellyfin.Api.Controllers
var query = new InternalItemsQuery(user) var query = new InternalItemsQuery(user)
{ {
ExcludeItemTypes = RequestHelpers.GetItemTypeStrings(excludeItemTypes), ExcludeItemTypes = excludeItemTypes,
IncludeItemTypes = RequestHelpers.GetItemTypeStrings(includeItemTypes), IncludeItemTypes = includeItemTypes,
MediaTypes = mediaTypes, MediaTypes = mediaTypes,
DtoOptions = dtoOptions DtoOptions = dtoOptions
}; };
@ -209,7 +210,7 @@ namespace Jellyfin.Api.Controllers
} }
// Include MediaTypes // Include MediaTypes
if (mediaTypes.Count > 0 && !mediaTypes.Contains(f.MediaType ?? string.Empty, StringComparer.OrdinalIgnoreCase)) if (mediaTypes.Count > 0 && !mediaTypes.Contains(f.MediaType ?? string.Empty, StringComparison.OrdinalIgnoreCase))
{ {
return false; return false;
} }

View File

@ -137,21 +137,5 @@ namespace Jellyfin.Api.Helpers
TotalRecordCount = result.TotalRecordCount TotalRecordCount = result.TotalRecordCount
}; };
} }
internal static string[] GetItemTypeStrings(IReadOnlyList<BaseItemKind> itemKinds)
{
if (itemKinds.Count == 0)
{
return Array.Empty<string>();
}
var itemTypes = new string[itemKinds.Count];
for (var i = 0; i < itemKinds.Count; i++)
{
itemTypes[i] = itemKinds[i].ToString();
}
return itemTypes;
}
} }
} }

View File

@ -13,7 +13,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authorization" Version="6.0.0" /> <PackageReference Include="Microsoft.AspNetCore.Authorization" Version="6.0.1" />
<PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0" /> <PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.2.3" /> <PackageReference Include="Swashbuckle.AspNetCore" Version="6.2.3" />
<PackageReference Include="Swashbuckle.AspNetCore.ReDoc" Version="6.2.3" /> <PackageReference Include="Swashbuckle.AspNetCore.ReDoc" Version="6.2.3" />

View File

@ -7,6 +7,7 @@ using Jellyfin.Data.Entities.Security;
using Jellyfin.Data.Enums; using Jellyfin.Data.Enums;
using Jellyfin.Data.Events; using Jellyfin.Data.Events;
using Jellyfin.Data.Queries; using Jellyfin.Data.Queries;
using Jellyfin.Extensions;
using MediaBrowser.Controller.Devices; using MediaBrowser.Controller.Devices;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Devices; using MediaBrowser.Model.Devices;
@ -219,7 +220,7 @@ namespace Jellyfin.Server.Implementations.Devices
return true; return true;
} }
return user.GetPreference(PreferenceKind.EnabledDevices).Contains(deviceId, StringComparer.OrdinalIgnoreCase) return user.GetPreference(PreferenceKind.EnabledDevices).Contains(deviceId, StringComparison.OrdinalIgnoreCase)
|| !GetCapabilities(deviceId).SupportsPersistentIdentifier; || !GetCapabilities(deviceId).SupportsPersistentIdentifier;
} }

View File

@ -18,14 +18,14 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="System.Linq.Async" Version="5.0.0" /> <PackageReference Include="System.Linq.Async" Version="5.1.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.0" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="6.0.0" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="6.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.0"> <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.1">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.0"> <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.1">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>

View File

@ -33,8 +33,8 @@
<PackageReference Include="CommandLineParser" Version="2.8.0" /> <PackageReference Include="CommandLineParser" Version="2.8.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="6.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="6.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="6.0.0" /> <PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="6.0.1" />
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" Version="6.0.0" /> <PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" Version="6.0.1" />
<PackageReference Include="prometheus-net" Version="5.0.2" /> <PackageReference Include="prometheus-net" Version="5.0.2" />
<PackageReference Include="prometheus-net.AspNetCore" Version="5.0.2" /> <PackageReference Include="prometheus-net.AspNetCore" Version="5.0.2" />
<PackageReference Include="Serilog.AspNetCore" Version="4.1.0" /> <PackageReference Include="Serilog.AspNetCore" Version="4.1.0" />

View File

@ -75,10 +75,15 @@ namespace Jellyfin.Server.Migrations
var xmlSerializer = new MyXmlSerializer(); var xmlSerializer = new MyXmlSerializer();
var migrationConfigPath = Path.Join(appPaths.ConfigurationDirectoryPath, MigrationsListStore.StoreKey.ToLowerInvariant() + ".xml"); var migrationConfigPath = Path.Join(appPaths.ConfigurationDirectoryPath, MigrationsListStore.StoreKey.ToLowerInvariant() + ".xml");
var migrationOptions = (MigrationOptions)xmlSerializer.DeserializeFromFile(typeof(MigrationOptions), migrationConfigPath)!; var migrationOptions = File.Exists(migrationConfigPath)
? (MigrationOptions)xmlSerializer.DeserializeFromFile(typeof(MigrationOptions), migrationConfigPath)!
: new MigrationOptions();
// We have to deserialize it manually since the configuration manager may overwrite it // We have to deserialize it manually since the configuration manager may overwrite it
var serverConfig = (ServerConfiguration)xmlSerializer.DeserializeFromFile(typeof(ServerConfiguration), appPaths.SystemConfigurationFilePath)!; var serverConfig = File.Exists(appPaths.SystemConfigurationFilePath)
? (ServerConfiguration)xmlSerializer.DeserializeFromFile(typeof(ServerConfiguration), appPaths.SystemConfigurationFilePath)!
: new ServerConfiguration();
HandleStartupWizardCondition(migrations, migrationOptions, serverConfig.IsStartupWizardCompleted, logger); HandleStartupWizardCondition(migrations, migrationOptions, serverConfig.IsStartupWizardCompleted, logger);
PerformMigrations(migrations, migrationOptions, options => xmlSerializer.SerializeToFile(options, migrationConfigPath), logger); PerformMigrations(migrations, migrationOptions, options => xmlSerializer.SerializeToFile(options, migrationConfigPath), logger);
} }

View File

@ -55,11 +55,6 @@ namespace MediaBrowser.Controller.BaseItemManager
return typeOptions.MetadataFetchers.Contains(name.AsSpan(), StringComparison.OrdinalIgnoreCase); return typeOptions.MetadataFetchers.Contains(name.AsSpan(), StringComparison.OrdinalIgnoreCase);
} }
if (!libraryOptions.EnableInternetProviders)
{
return false;
}
var itemConfig = _serverConfigurationManager.Configuration.MetadataOptions.FirstOrDefault(i => string.Equals(i.ItemType, baseItem.GetType().Name, StringComparison.OrdinalIgnoreCase)); var itemConfig = _serverConfigurationManager.Configuration.MetadataOptions.FirstOrDefault(i => string.Equals(i.ItemType, baseItem.GetType().Name, StringComparison.OrdinalIgnoreCase));
return itemConfig == null || !itemConfig.DisabledMetadataFetchers.Contains(name.AsSpan(), StringComparison.OrdinalIgnoreCase); return itemConfig == null || !itemConfig.DisabledMetadataFetchers.Contains(name.AsSpan(), StringComparison.OrdinalIgnoreCase);
@ -86,11 +81,6 @@ namespace MediaBrowser.Controller.BaseItemManager
return typeOptions.ImageFetchers.Contains(name.AsSpan(), StringComparison.OrdinalIgnoreCase); return typeOptions.ImageFetchers.Contains(name.AsSpan(), StringComparison.OrdinalIgnoreCase);
} }
if (!libraryOptions.EnableInternetProviders)
{
return false;
}
var itemConfig = _serverConfigurationManager.Configuration.MetadataOptions.FirstOrDefault(i => string.Equals(i.ItemType, baseItem.GetType().Name, StringComparison.OrdinalIgnoreCase)); var itemConfig = _serverConfigurationManager.Configuration.MetadataOptions.FirstOrDefault(i => string.Equals(i.ItemType, baseItem.GetType().Name, StringComparison.OrdinalIgnoreCase));
return itemConfig == null || !itemConfig.DisabledImageFetchers.Contains(name.AsSpan(), StringComparison.OrdinalIgnoreCase); return itemConfig == null || !itemConfig.DisabledImageFetchers.Contains(name.AsSpan(), StringComparison.OrdinalIgnoreCase);

View File

@ -8,10 +8,8 @@ using System.Globalization;
using System.Linq; using System.Linq;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using Jellyfin.Data.Enums; using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Dto; using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
namespace MediaBrowser.Controller.Entities.Audio namespace MediaBrowser.Controller.Entities.Audio
{ {

Some files were not shown because too many files have changed in this diff Show More