mirror of
https://github.com/jellyfin/jellyfin.git
synced 2025-07-09 03:04:24 -04:00
Merge branch 'master' into what_could_go_wrong
This commit is contained in:
commit
9a0618552b
@ -39,6 +39,14 @@ jobs:
|
|||||||
vmImage: 'ubuntu-latest'
|
vmImage: 'ubuntu-latest'
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
- script: echo "##vso[task.setvariable variable=JellyfinVersion]$( awk -F '/' '{ print $NF }' <<<'$(Build.SourceBranch)' | sed 's/^v//' )"
|
||||||
|
displayName: Set release version (stable)
|
||||||
|
condition: startsWith(variables['Build.SourceBranch'], 'refs/tags/v')
|
||||||
|
|
||||||
|
- script: './bump-version $(JellyfinVersion)'
|
||||||
|
displayName: Bump internal version (stable)
|
||||||
|
condition: startsWith(variables['Build.SourceBranch'], 'refs/tags/v')
|
||||||
|
|
||||||
- script: 'docker build -f deployment/Dockerfile.$(BuildConfiguration) -t jellyfin-server-$(BuildConfiguration) deployment'
|
- script: 'docker build -f deployment/Dockerfile.$(BuildConfiguration) -t jellyfin-server-$(BuildConfiguration) deployment'
|
||||||
displayName: 'Build Dockerfile'
|
displayName: 'Build Dockerfile'
|
||||||
|
|
||||||
@ -80,6 +88,14 @@ jobs:
|
|||||||
vmImage: 'ubuntu-latest'
|
vmImage: 'ubuntu-latest'
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
- script: echo "##vso[task.setvariable variable=JellyfinVersion]$( awk -F '/' '{ print $NF }' <<<'$(Build.SourceBranch)' | sed 's/^v//' )"
|
||||||
|
displayName: Set release version (stable)
|
||||||
|
condition: startsWith(variables['Build.SourceBranch'], 'refs/tags/v')
|
||||||
|
|
||||||
|
- script: './bump-version $(JellyfinVersion)'
|
||||||
|
displayName: Bump internal version (stable)
|
||||||
|
condition: startsWith(variables['Build.SourceBranch'], 'refs/tags/v')
|
||||||
|
|
||||||
- task: DownloadPipelineArtifact@2
|
- task: DownloadPipelineArtifact@2
|
||||||
displayName: 'Download OpenAPI Spec'
|
displayName: 'Download OpenAPI Spec'
|
||||||
inputs:
|
inputs:
|
||||||
@ -127,6 +143,10 @@ jobs:
|
|||||||
displayName: Set release version (stable)
|
displayName: Set release version (stable)
|
||||||
condition: startsWith(variables['Build.SourceBranch'], 'refs/tags/v')
|
condition: startsWith(variables['Build.SourceBranch'], 'refs/tags/v')
|
||||||
|
|
||||||
|
- script: './bump-version $(JellyfinVersion)'
|
||||||
|
displayName: Bump internal version (stable)
|
||||||
|
condition: startsWith(variables['Build.SourceBranch'], 'refs/tags/v')
|
||||||
|
|
||||||
- task: Docker@2
|
- task: Docker@2
|
||||||
displayName: 'Push Unstable Image'
|
displayName: 'Push Unstable Image'
|
||||||
condition: startsWith(variables['Build.SourceBranch'], 'refs/heads/master')
|
condition: startsWith(variables['Build.SourceBranch'], 'refs/heads/master')
|
||||||
|
@ -150,6 +150,7 @@
|
|||||||
- [ianjazz246](https://github.com/ianjazz246)
|
- [ianjazz246](https://github.com/ianjazz246)
|
||||||
- [peterspenler](https://github.com/peterspenler)
|
- [peterspenler](https://github.com/peterspenler)
|
||||||
- [MBR-0001](https://github.com/MBR-0001)
|
- [MBR-0001](https://github.com/MBR-0001)
|
||||||
|
- [jonas-resch](https://github.com/jonas-resch)
|
||||||
|
|
||||||
# Emby Contributors
|
# Emby Contributors
|
||||||
|
|
||||||
|
@ -539,7 +539,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 +619,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 +644,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 +675,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 +746,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 +831,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 +898,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 +913,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 +1013,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 +1052,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 +1086,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 +1115,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 +1144,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(),
|
||||||
|
@ -124,7 +124,7 @@ namespace Emby.Dlna.Main
|
|||||||
config);
|
config);
|
||||||
Current = this;
|
Current = this;
|
||||||
|
|
||||||
var netConfig = config.GetConfiguration<NetworkConfiguration>("network");
|
var netConfig = config.GetConfiguration<NetworkConfiguration>(NetworkConfigurationStore.StoreKey);
|
||||||
_disabled = appHost.ListenWithHttps && netConfig.RequireHttps;
|
_disabled = appHost.ListenWithHttps && netConfig.RequireHttps;
|
||||||
|
|
||||||
if (_disabled && _config.GetDlnaConfiguration().EnableServer)
|
if (_disabled && _config.GetDlnaConfiguration().EnableServer)
|
||||||
|
@ -313,22 +313,6 @@ namespace Emby.Server.Implementations
|
|||||||
? Environment.MachineName
|
? Environment.MachineName
|
||||||
: ConfigurationManager.Configuration.ServerName;
|
: ConfigurationManager.Configuration.ServerName;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Temporary function to migration network settings out of system.xml and into network.xml.
|
|
||||||
/// TODO: remove at the point when a fixed migration path has been decided upon.
|
|
||||||
/// </summary>
|
|
||||||
private void MigrateNetworkConfiguration()
|
|
||||||
{
|
|
||||||
string path = Path.Combine(ConfigurationManager.CommonApplicationPaths.ConfigurationDirectoryPath, "network.xml");
|
|
||||||
if (!File.Exists(path))
|
|
||||||
{
|
|
||||||
var networkSettings = new NetworkConfiguration();
|
|
||||||
ClassMigrationHelper.CopyProperties(ConfigurationManager.Configuration, networkSettings);
|
|
||||||
_xmlSerializer.SerializeToFile(networkSettings, path);
|
|
||||||
Logger.LogDebug("Successfully migrated network settings.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string ExpandVirtualPath(string path)
|
public string ExpandVirtualPath(string path)
|
||||||
{
|
{
|
||||||
var appPaths = ApplicationPaths;
|
var appPaths = ApplicationPaths;
|
||||||
@ -513,8 +497,6 @@ namespace Emby.Server.Implementations
|
|||||||
|
|
||||||
ConfigurationManager.AddParts(GetExports<IConfigurationFactory>());
|
ConfigurationManager.AddParts(GetExports<IConfigurationFactory>());
|
||||||
|
|
||||||
// Have to migrate settings here as migration subsystem not yet initialised.
|
|
||||||
MigrateNetworkConfiguration();
|
|
||||||
NetManager = new NetworkManager(ConfigurationManager, LoggerFactory.CreateLogger<NetworkManager>());
|
NetManager = new NetworkManager(ConfigurationManager, LoggerFactory.CreateLogger<NetworkManager>());
|
||||||
|
|
||||||
// Initialize runtime stat collection
|
// Initialize runtime stat collection
|
||||||
|
@ -541,7 +541,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();
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -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 =
|
||||||
@ -2212,7 +2211,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 +2226,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 +2241,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 +2261,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 +2271,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 +2291,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;
|
||||||
}
|
}
|
||||||
@ -3487,8 +3486,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,15 +3562,15 @@ 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)
|
||||||
{
|
{
|
||||||
whereClauses.Add("type<>@type");
|
whereClauses.Add("type<>@type");
|
||||||
statement?.TryBind("@type", excludeTypes[0]);
|
statement?.TryBind("@type", excludeTypes[0].ToString());
|
||||||
}
|
}
|
||||||
else if (excludeTypes.Length > 1)
|
else if (excludeTypes.Length > 1)
|
||||||
{
|
{
|
||||||
@ -3582,7 +3581,7 @@ namespace Emby.Server.Implementations.Data
|
|||||||
else if (includeTypes.Length == 1)
|
else if (includeTypes.Length == 1)
|
||||||
{
|
{
|
||||||
whereClauses.Add("type=@type");
|
whereClauses.Add("type=@type");
|
||||||
statement?.TryBind("@type", includeTypes[0]);
|
statement?.TryBind("@type", includeTypes[0].ToString());
|
||||||
}
|
}
|
||||||
else if (includeTypes.Length > 1)
|
else if (includeTypes.Length > 1)
|
||||||
{
|
{
|
||||||
@ -3911,7 +3910,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 +4760,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 +4788,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 +4835,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 +4889,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)
|
||||||
@ -5569,7 +5552,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();
|
||||||
|
|
||||||
|
@ -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
|
||||||
});
|
});
|
||||||
|
@ -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);
|
||||||
|
@ -6,6 +6,7 @@ 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 MediaBrowser.Common.Configuration;
|
using MediaBrowser.Common.Configuration;
|
||||||
using MediaBrowser.Controller.Drawing;
|
using MediaBrowser.Controller.Drawing;
|
||||||
using MediaBrowser.Controller.Dto;
|
using MediaBrowser.Controller.Dto;
|
||||||
@ -41,7 +42,7 @@ namespace Emby.Server.Implementations.Images
|
|||||||
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)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -43,7 +43,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,
|
||||||
|
@ -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,
|
||||||
|
@ -963,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>()
|
||||||
|
@ -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(),
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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");
|
||||||
|
|
||||||
|
@ -362,9 +362,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");
|
||||||
@ -378,7 +378,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))
|
||||||
{
|
{
|
||||||
|
@ -185,13 +185,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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -59,9 +59,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 +86,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 +183,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 +192,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
|
||||||
|
@ -300,11 +300,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 +344,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)
|
||||||
{
|
{
|
||||||
|
@ -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();
|
||||||
|
@ -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
|
||||||
});
|
});
|
||||||
|
@ -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
|
||||||
});
|
});
|
||||||
|
@ -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
|
||||||
});
|
});
|
||||||
|
@ -1778,7 +1778,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 +2137,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)
|
||||||
{
|
{
|
||||||
@ -2352,7 +2352,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 +2387,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 +2446,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 +2511,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 +2524,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,
|
||||||
|
@ -7,6 +7,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.Common;
|
using MediaBrowser.Common;
|
||||||
using MediaBrowser.Common.Extensions;
|
using MediaBrowser.Common.Extensions;
|
||||||
using MediaBrowser.Controller.Drawing;
|
using MediaBrowser.Controller.Drawing;
|
||||||
@ -161,7 +162,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 +205,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 +256,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 +299,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 +310,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 },
|
||||||
|
@ -191,7 +191,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 +810,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 +874,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 +1085,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 +1177,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 +1261,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 +1328,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 +1354,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 +1878,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,
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
"Favorites": "مفضلات",
|
"Favorites": "مفضلات",
|
||||||
"Folders": "المجلدات",
|
"Folders": "المجلدات",
|
||||||
"Genres": "التضنيفات",
|
"Genres": "التضنيفات",
|
||||||
"HeaderAlbumArtists": "ألبوم الفنان",
|
"HeaderAlbumArtists": "فناني الألبوم",
|
||||||
"HeaderContinueWatching": "استمر بالمشاهدة",
|
"HeaderContinueWatching": "استمر بالمشاهدة",
|
||||||
"HeaderFavoriteAlbums": "الألبومات المفضلة",
|
"HeaderFavoriteAlbums": "الألبومات المفضلة",
|
||||||
"HeaderFavoriteArtists": "الفنانون المفضلون",
|
"HeaderFavoriteArtists": "الفنانون المفضلون",
|
||||||
|
@ -104,7 +104,7 @@
|
|||||||
"TaskRefreshChannelsDescription": "Refreŝigas informon pri interretaj kanaloj.",
|
"TaskRefreshChannelsDescription": "Refreŝigas informon pri interretaj kanaloj.",
|
||||||
"TaskDownloadMissingSubtitles": "Elŝuti mankantajn subtekstojn",
|
"TaskDownloadMissingSubtitles": "Elŝuti mankantajn subtekstojn",
|
||||||
"TaskCleanTranscode": "Malplenigi Transkodadan Katalogon",
|
"TaskCleanTranscode": "Malplenigi Transkodadan Katalogon",
|
||||||
"TaskRefreshChapterImages": "Eltiri Ĉapitraj Bildojn",
|
"TaskRefreshChapterImages": "Eltiri Ĉapitrajn Bildojn",
|
||||||
"TaskCleanCache": "Malplenigi Staplan Katalogon",
|
"TaskCleanCache": "Malplenigi Staplan Katalogon",
|
||||||
"TaskCleanActivityLog": "Malplenigi Aktivecan Ĵurnalon",
|
"TaskCleanActivityLog": "Malplenigi Aktivecan Ĵurnalon",
|
||||||
"PluginUpdatedWithName": "{0} estis ĝisdatigita",
|
"PluginUpdatedWithName": "{0} estis ĝisdatigita",
|
||||||
|
@ -118,5 +118,6 @@
|
|||||||
"Default": "پیشفرض",
|
"Default": "پیشفرض",
|
||||||
"TaskCleanActivityLogDescription": "ورودیهای قدیمیتر از سن تنظیم شده در سیاهه فعالیت را حذف میکند.",
|
"TaskCleanActivityLogDescription": "ورودیهای قدیمیتر از سن تنظیم شده در سیاهه فعالیت را حذف میکند.",
|
||||||
"TaskCleanActivityLog": "پاکسازی سیاهه فعالیت",
|
"TaskCleanActivityLog": "پاکسازی سیاهه فعالیت",
|
||||||
"Undefined": "تعریف نشده"
|
"Undefined": "تعریف نشده",
|
||||||
|
"TaskOptimizeDatabase": "بهینه سازی پایگاه داده"
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
"Favorites": "Favoriti",
|
"Favorites": "Favoriti",
|
||||||
"Folders": "Mape",
|
"Folders": "Mape",
|
||||||
"Genres": "Žanrovi",
|
"Genres": "Žanrovi",
|
||||||
"HeaderAlbumArtists": "Album od izvođača",
|
"HeaderAlbumArtists": "Izvođači albuma",
|
||||||
"HeaderContinueWatching": "Nastavi gledati",
|
"HeaderContinueWatching": "Nastavi gledati",
|
||||||
"HeaderFavoriteAlbums": "Omiljeni albumi",
|
"HeaderFavoriteAlbums": "Omiljeni albumi",
|
||||||
"HeaderFavoriteArtists": "Omiljeni izvođači",
|
"HeaderFavoriteArtists": "Omiljeni izvođači",
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
"Folders": "フォルダー",
|
"Folders": "フォルダー",
|
||||||
"Genres": "ジャンル",
|
"Genres": "ジャンル",
|
||||||
"HeaderAlbumArtists": "アルバムアーティスト",
|
"HeaderAlbumArtists": "アルバムアーティスト",
|
||||||
"HeaderContinueWatching": "視聴を続ける",
|
"HeaderContinueWatching": "続きを見る",
|
||||||
"HeaderFavoriteAlbums": "お気に入りのアルバム",
|
"HeaderFavoriteAlbums": "お気に入りのアルバム",
|
||||||
"HeaderFavoriteArtists": "お気に入りのアーティスト",
|
"HeaderFavoriteArtists": "お気に入りのアーティスト",
|
||||||
"HeaderFavoriteEpisodes": "お気に入りのエピソード",
|
"HeaderFavoriteEpisodes": "お気に入りのエピソード",
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"Albums": "Albumai",
|
"Albums": "Albumai",
|
||||||
"AppDeviceValues": "Programa: {0}, Įrenginys: {1}",
|
"AppDeviceValues": "Programa: {0}, Įrenginys: {1}",
|
||||||
"Application": "Programa",
|
"Application": "Programėlė",
|
||||||
"Artists": "Atlikėjai",
|
"Artists": "Atlikėjai",
|
||||||
"AuthenticationSucceededWithUserName": "{0} sėkmingai autentifikuota",
|
"AuthenticationSucceededWithUserName": "{0} sėkmingai autentifikuota",
|
||||||
"Books": "Knygos",
|
"Books": "Knygos",
|
||||||
|
@ -97,5 +97,8 @@
|
|||||||
"TasksChannelsCategory": "Интернет Канали",
|
"TasksChannelsCategory": "Интернет Канали",
|
||||||
"TasksApplicationCategory": "Апликација",
|
"TasksApplicationCategory": "Апликација",
|
||||||
"TasksLibraryCategory": "Библиотека",
|
"TasksLibraryCategory": "Библиотека",
|
||||||
"TasksMaintenanceCategory": "Одржување"
|
"TasksMaintenanceCategory": "Одржување",
|
||||||
|
"Undefined": "Недефинирано",
|
||||||
|
"Forced": "Принудно",
|
||||||
|
"Default": "Зададено"
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,7 @@
|
|||||||
"NotificationOptionPluginInstalled": "Plugin telah dipasang",
|
"NotificationOptionPluginInstalled": "Plugin telah dipasang",
|
||||||
"NotificationOptionPluginUninstalled": "Plugin telah dinyahpasang",
|
"NotificationOptionPluginUninstalled": "Plugin telah dinyahpasang",
|
||||||
"NotificationOptionPluginUpdateInstalled": "Kemaskini plugin telah dipasang",
|
"NotificationOptionPluginUpdateInstalled": "Kemaskini plugin telah dipasang",
|
||||||
"NotificationOptionServerRestartRequired": "Server restart required",
|
"NotificationOptionServerRestartRequired": "",
|
||||||
"NotificationOptionTaskFailed": "Kegagalan tugas berjadual",
|
"NotificationOptionTaskFailed": "Kegagalan tugas berjadual",
|
||||||
"NotificationOptionUserLockedOut": "Pengguna telah dikunci",
|
"NotificationOptionUserLockedOut": "Pengguna telah dikunci",
|
||||||
"NotificationOptionVideoPlayback": "Ulangmain video bermula",
|
"NotificationOptionVideoPlayback": "Ulangmain video bermula",
|
||||||
@ -66,19 +66,19 @@
|
|||||||
"PluginInstalledWithName": "{0} telah dipasang",
|
"PluginInstalledWithName": "{0} telah dipasang",
|
||||||
"PluginUninstalledWithName": "{0} telah dinyahpasang",
|
"PluginUninstalledWithName": "{0} telah dinyahpasang",
|
||||||
"PluginUpdatedWithName": "{0} telah dikemaskini",
|
"PluginUpdatedWithName": "{0} telah dikemaskini",
|
||||||
"ProviderValue": "Provider: {0}",
|
"ProviderValue": "Pembekal: {0}",
|
||||||
"ScheduledTaskFailedWithName": "{0} gagal",
|
"ScheduledTaskFailedWithName": "{0} gagal",
|
||||||
"ScheduledTaskStartedWithName": "{0} bermula",
|
"ScheduledTaskStartedWithName": "{0} bermula",
|
||||||
"ServerNameNeedsToBeRestarted": "{0} perlu di ulangmula",
|
"ServerNameNeedsToBeRestarted": "{0} perlu di ulangmula",
|
||||||
"Shows": "Series",
|
"Shows": "Tayangan",
|
||||||
"Songs": "Lagu-lagu",
|
"Songs": "Lagu-lagu",
|
||||||
"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",
|
"Sync": "",
|
||||||
"System": "Sistem",
|
"System": "Sistem",
|
||||||
"TvShows": "Tayangan TV",
|
"TvShows": "Tayangan TV",
|
||||||
"User": "User",
|
"User": "Pengguna",
|
||||||
"UserCreatedWithName": "Pengguna {0} telah diwujudkan",
|
"UserCreatedWithName": "Pengguna {0} telah diwujudkan",
|
||||||
"UserDeletedWithName": "Pengguna {0} telah dipadamkan",
|
"UserDeletedWithName": "Pengguna {0} telah dipadamkan",
|
||||||
"UserDownloadingItemWithValues": "{0} sedang memuat turun {1}",
|
"UserDownloadingItemWithValues": "{0} sedang memuat turun {1}",
|
||||||
|
@ -74,7 +74,7 @@
|
|||||||
"HeaderFavoriteArtists": "Artiști Favoriți",
|
"HeaderFavoriteArtists": "Artiști Favoriți",
|
||||||
"HeaderFavoriteAlbums": "Albume Favorite",
|
"HeaderFavoriteAlbums": "Albume Favorite",
|
||||||
"HeaderContinueWatching": "Vizionează în continuare",
|
"HeaderContinueWatching": "Vizionează în continuare",
|
||||||
"HeaderAlbumArtists": "Album Artiști",
|
"HeaderAlbumArtists": "Albume Artiști",
|
||||||
"Genres": "Genuri",
|
"Genres": "Genuri",
|
||||||
"Folders": "Dosare",
|
"Folders": "Dosare",
|
||||||
"Favorites": "Favorite",
|
"Favorites": "Favorite",
|
||||||
|
@ -96,8 +96,8 @@
|
|||||||
"TaskDownloadMissingSubtitles": "Ladda ned saknade undertexter",
|
"TaskDownloadMissingSubtitles": "Ladda ned saknade undertexter",
|
||||||
"TaskRefreshChannelsDescription": "Uppdaterar information för internetkanaler.",
|
"TaskRefreshChannelsDescription": "Uppdaterar information för internetkanaler.",
|
||||||
"TaskRefreshChannels": "Uppdatera kanaler",
|
"TaskRefreshChannels": "Uppdatera kanaler",
|
||||||
"TaskCleanTranscodeDescription": "Raderar transkodningsfiler som är mer än en dag gamla.",
|
"TaskCleanTranscodeDescription": "Raderar omkodningsfiler som är mer än en dag gamla.",
|
||||||
"TaskCleanTranscode": "Töm transkodningskatalog",
|
"TaskCleanTranscode": "Töm omkodningskatalog",
|
||||||
"TaskUpdatePluginsDescription": "Laddar ned och installerar uppdateringar till insticksprogram som är konfigurerade att uppdateras automatiskt.",
|
"TaskUpdatePluginsDescription": "Laddar ned och installerar uppdateringar till insticksprogram som är konfigurerade att uppdateras automatiskt.",
|
||||||
"TaskUpdatePlugins": "Uppdatera insticksprogram",
|
"TaskUpdatePlugins": "Uppdatera insticksprogram",
|
||||||
"TaskRefreshPeopleDescription": "Uppdaterar metadata för skådespelare och regissörer i ditt mediabibliotek.",
|
"TaskRefreshPeopleDescription": "Uppdaterar metadata för skådespelare och regissörer i ditt mediabibliotek.",
|
||||||
|
@ -1 +1,29 @@
|
|||||||
{}
|
{
|
||||||
|
"TasksApplicationCategory": "Ukusetshenziswa",
|
||||||
|
"TasksLibraryCategory": "Umtapo",
|
||||||
|
"TasksMaintenanceCategory": "Ukunakekela",
|
||||||
|
"User": "Umsebenzisi",
|
||||||
|
"Undefined": "Akuchaziwe",
|
||||||
|
"System": "Isistimu",
|
||||||
|
"Sync": "Vumelanisa",
|
||||||
|
"Songs": "Amaculo",
|
||||||
|
"Shows": "Izinhlelo",
|
||||||
|
"Plugin": "Isijobelelo",
|
||||||
|
"Playlists": "Izinhla Zokudlalayo",
|
||||||
|
"Photos": "Izithombe",
|
||||||
|
"Music": "Umculo",
|
||||||
|
"Movies": "Amamuvi",
|
||||||
|
"Latest": "lwakamuva",
|
||||||
|
"Inherit": "Ngefa",
|
||||||
|
"Forced": "Kuphoqiwe",
|
||||||
|
"Application": "Ukusetshenziswa",
|
||||||
|
"Genres": "Izinhlobo",
|
||||||
|
"Folders": "Izikhwama",
|
||||||
|
"Favorites": "Izintandokazi",
|
||||||
|
"Default": "Okumisiwe",
|
||||||
|
"Collections": "Amaqoqo",
|
||||||
|
"Channels": "Amashaneli",
|
||||||
|
"Books": "Izincwadi",
|
||||||
|
"Artists": "Abadlali",
|
||||||
|
"Albums": "Ama-albhamu"
|
||||||
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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,
|
||||||
|
@ -71,7 +71,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 +166,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>(),
|
||||||
|
@ -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();
|
||||||
|
|
||||||
|
@ -296,8 +296,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 +459,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 +483,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 +831,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
|
||||||
});
|
});
|
||||||
|
@ -14,6 +14,7 @@ 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 MediaBrowser.Common.Progress;
|
using MediaBrowser.Common.Progress;
|
||||||
using MediaBrowser.Controller.Configuration;
|
using MediaBrowser.Controller.Configuration;
|
||||||
using MediaBrowser.Controller.Dto;
|
using MediaBrowser.Controller.Dto;
|
||||||
@ -413,14 +414,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 +530,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 +560,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,26 +716,26 @@ 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)
|
||||||
@ -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,
|
||||||
|
@ -84,7 +84,7 @@ namespace Jellyfin.Api.Controllers
|
|||||||
{
|
{
|
||||||
IncludeItemTypes = new[]
|
IncludeItemTypes = new[]
|
||||||
{
|
{
|
||||||
nameof(Movie),
|
BaseItemKind.Movie,
|
||||||
// nameof(Trailer),
|
// nameof(Trailer),
|
||||||
// nameof(LiveTvProgram)
|
// nameof(LiveTvProgram)
|
||||||
},
|
},
|
||||||
@ -99,11 +99,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 +182,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 +224,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 +264,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)
|
||||||
|
@ -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();
|
||||||
|
|
||||||
|
@ -110,8 +110,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,
|
||||||
|
|
||||||
|
@ -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)
|
||||||
{
|
{
|
||||||
|
@ -93,7 +93,7 @@ namespace Jellyfin.Api.Controllers
|
|||||||
NetworkConfiguration settings = _config.GetNetworkConfiguration();
|
NetworkConfiguration settings = _config.GetNetworkConfiguration();
|
||||||
settings.EnableRemoteAccess = startupRemoteAccessDto.EnableRemoteAccess;
|
settings.EnableRemoteAccess = startupRemoteAccessDto.EnableRemoteAccess;
|
||||||
settings.EnableUPnP = startupRemoteAccessDto.EnableAutomaticPortMapping;
|
settings.EnableUPnP = startupRemoteAccessDto.EnableAutomaticPortMapping;
|
||||||
_config.SaveConfiguration("network", settings);
|
_config.SaveConfiguration(NetworkConfigurationStore.StoreKey, settings);
|
||||||
return NoContent();
|
return NoContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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,
|
||||||
|
@ -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)
|
||||||
|
@ -157,7 +157,7 @@ namespace Jellyfin.Api.Controllers
|
|||||||
|
|
||||||
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,
|
||||||
|
@ -297,7 +297,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,
|
||||||
|
@ -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,
|
||||||
MediaTypes = mediaTypes,
|
MediaTypes = mediaTypes,
|
||||||
DtoOptions = dtoOptions
|
DtoOptions = dtoOptions
|
||||||
};
|
};
|
||||||
|
@ -1,71 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace Jellyfin.Api.Helpers
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// A static class for copying matching properties from one object to another.
|
|
||||||
/// TODO: remove at the point when a fixed migration path has been decided upon.
|
|
||||||
/// </summary>
|
|
||||||
public static class ClassMigrationHelper
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Extension for 'Object' that copies the properties to a destination object.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="source">The source.</param>
|
|
||||||
/// <param name="destination">The destination.</param>
|
|
||||||
public static void CopyProperties(this object source, object destination)
|
|
||||||
{
|
|
||||||
// If any this null throw an exception.
|
|
||||||
if (source == null || destination == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("Source or/and Destination Objects are null");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Getting the Types of the objects.
|
|
||||||
Type typeDest = destination.GetType();
|
|
||||||
Type typeSrc = source.GetType();
|
|
||||||
|
|
||||||
// Iterate the Properties of the source instance and populate them from their destination counterparts.
|
|
||||||
PropertyInfo[] srcProps = typeSrc.GetProperties();
|
|
||||||
foreach (PropertyInfo srcProp in srcProps)
|
|
||||||
{
|
|
||||||
if (!srcProp.CanRead)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var targetProperty = typeDest.GetProperty(srcProp.Name);
|
|
||||||
if (targetProperty == null)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!targetProperty.CanWrite)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var obj = targetProperty.GetSetMethod(true);
|
|
||||||
if (obj != null && obj.IsPrivate)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var target = targetProperty.GetSetMethod();
|
|
||||||
if (target != null && (target.Attributes & MethodAttributes.Static) != 0)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!targetProperty.PropertyType.IsAssignableFrom(srcProp.PropertyType))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Passed all tests, lets set the value.
|
|
||||||
targetProperty.SetValue(destination, srcProp.GetValue(source, null), null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,11 +16,7 @@ namespace Jellyfin.Networking.Configuration
|
|||||||
{
|
{
|
||||||
return new[]
|
return new[]
|
||||||
{
|
{
|
||||||
new ConfigurationStore
|
new NetworkConfigurationStore()
|
||||||
{
|
|
||||||
Key = "network",
|
|
||||||
ConfigurationType = typeof(NetworkConfiguration)
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,24 @@
|
|||||||
|
using MediaBrowser.Common.Configuration;
|
||||||
|
|
||||||
|
namespace Jellyfin.Networking.Configuration
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A configuration that stores network related settings.
|
||||||
|
/// </summary>
|
||||||
|
public class NetworkConfigurationStore : ConfigurationStore
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The name of the configuration in the storage.
|
||||||
|
/// </summary>
|
||||||
|
public const string StoreKey = "network";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="NetworkConfigurationStore"/> class.
|
||||||
|
/// </summary>
|
||||||
|
public NetworkConfigurationStore()
|
||||||
|
{
|
||||||
|
ConfigurationType = typeof(NetworkConfiguration);
|
||||||
|
Key = StoreKey;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -727,7 +727,7 @@ namespace Jellyfin.Networking.Manager
|
|||||||
|
|
||||||
private void ConfigurationUpdated(object? sender, ConfigurationUpdateEventArgs evt)
|
private void ConfigurationUpdated(object? sender, ConfigurationUpdateEventArgs evt)
|
||||||
{
|
{
|
||||||
if (evt.Key.Equals("network", StringComparison.Ordinal))
|
if (evt.Key.Equals(NetworkConfigurationStore.StoreKey, StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
UpdateSettings((NetworkConfiguration)evt.NewConfiguration);
|
UpdateSettings((NetworkConfiguration)evt.NewConfiguration);
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
</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.0" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="6.0.0" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="6.0.0" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.0">
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.0">
|
||||||
|
@ -1,6 +1,11 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Emby.Server.Implementations;
|
||||||
|
using Emby.Server.Implementations.Serialization;
|
||||||
using MediaBrowser.Common.Configuration;
|
using MediaBrowser.Common.Configuration;
|
||||||
|
using MediaBrowser.Model.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
@ -11,6 +16,14 @@ namespace Jellyfin.Server.Migrations
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class MigrationRunner
|
public sealed class MigrationRunner
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The list of known pre-startup migrations, in order of applicability.
|
||||||
|
/// </summary>
|
||||||
|
private static readonly Type[] _preStartupMigrationTypes =
|
||||||
|
{
|
||||||
|
typeof(PreStartupRoutines.CreateNetworkConfiguration)
|
||||||
|
};
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The list of known migrations, in order of applicability.
|
/// The list of known migrations, in order of applicability.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -41,17 +54,55 @@ namespace Jellyfin.Server.Migrations
|
|||||||
.Select(m => ActivatorUtilities.CreateInstance(host.ServiceProvider, m))
|
.Select(m => ActivatorUtilities.CreateInstance(host.ServiceProvider, m))
|
||||||
.OfType<IMigrationRoutine>()
|
.OfType<IMigrationRoutine>()
|
||||||
.ToArray();
|
.ToArray();
|
||||||
var migrationOptions = host.ConfigurationManager.GetConfiguration<MigrationOptions>(MigrationsListStore.StoreKey);
|
|
||||||
|
|
||||||
if (!host.ConfigurationManager.Configuration.IsStartupWizardCompleted && migrationOptions.Applied.Count == 0)
|
var migrationOptions = host.ConfigurationManager.GetConfiguration<MigrationOptions>(MigrationsListStore.StoreKey);
|
||||||
|
HandleStartupWizardCondition(migrations, migrationOptions, host.ConfigurationManager.Configuration.IsStartupWizardCompleted, logger);
|
||||||
|
PerformMigrations(migrations, migrationOptions, options => host.ConfigurationManager.SaveConfiguration(MigrationsListStore.StoreKey, options), logger);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Run all needed pre-startup migrations.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="appPaths">Application paths.</param>
|
||||||
|
/// <param name="loggerFactory">Factory for making the logger.</param>
|
||||||
|
public static void RunPreStartup(ServerApplicationPaths appPaths, ILoggerFactory loggerFactory)
|
||||||
|
{
|
||||||
|
var logger = loggerFactory.CreateLogger<MigrationRunner>();
|
||||||
|
var migrations = _preStartupMigrationTypes
|
||||||
|
.Select(m => Activator.CreateInstance(m, appPaths, loggerFactory))
|
||||||
|
.OfType<IMigrationRoutine>()
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
var xmlSerializer = new MyXmlSerializer();
|
||||||
|
var migrationConfigPath = Path.Join(appPaths.ConfigurationDirectoryPath, MigrationsListStore.StoreKey.ToLowerInvariant() + ".xml");
|
||||||
|
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
|
||||||
|
var serverConfig = File.Exists(appPaths.SystemConfigurationFilePath)
|
||||||
|
? (ServerConfiguration)xmlSerializer.DeserializeFromFile(typeof(ServerConfiguration), appPaths.SystemConfigurationFilePath)!
|
||||||
|
: new ServerConfiguration();
|
||||||
|
|
||||||
|
HandleStartupWizardCondition(migrations, migrationOptions, serverConfig.IsStartupWizardCompleted, logger);
|
||||||
|
PerformMigrations(migrations, migrationOptions, options => xmlSerializer.SerializeToFile(options, migrationConfigPath), logger);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void HandleStartupWizardCondition(IEnumerable<IMigrationRoutine> migrations, MigrationOptions migrationOptions, bool isStartWizardCompleted, ILogger logger)
|
||||||
|
{
|
||||||
|
if (isStartWizardCompleted || migrationOptions.Applied.Count != 0)
|
||||||
{
|
{
|
||||||
// If startup wizard is not finished, this is a fresh install.
|
return;
|
||||||
// Don't run any migrations, just mark all of them as applied.
|
|
||||||
logger.LogInformation("Marking all known migrations as applied because this is a fresh install");
|
|
||||||
migrationOptions.Applied.AddRange(migrations.Where(m => !m.PerformOnNewInstall).Select(m => (m.Id, m.Name)));
|
|
||||||
host.ConfigurationManager.SaveConfiguration(MigrationsListStore.StoreKey, migrationOptions);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If startup wizard is not finished, this is a fresh install.
|
||||||
|
var onlyOldInstalls = migrations.Where(m => !m.PerformOnNewInstall).ToArray();
|
||||||
|
logger.LogInformation("Marking following migrations as applied because this is a fresh install: {@OnlyOldInstalls}", onlyOldInstalls.Select(m => m.Name));
|
||||||
|
migrationOptions.Applied.AddRange(onlyOldInstalls.Select(m => (m.Id, m.Name)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void PerformMigrations(IMigrationRoutine[] migrations, MigrationOptions migrationOptions, Action<MigrationOptions> saveConfiguration, ILogger logger)
|
||||||
|
{
|
||||||
var appliedMigrationIds = migrationOptions.Applied.Select(m => m.Id).ToHashSet();
|
var appliedMigrationIds = migrationOptions.Applied.Select(m => m.Id).ToHashSet();
|
||||||
|
|
||||||
for (var i = 0; i < migrations.Length; i++)
|
for (var i = 0; i < migrations.Length; i++)
|
||||||
@ -78,7 +129,7 @@ namespace Jellyfin.Server.Migrations
|
|||||||
// Mark the migration as completed
|
// Mark the migration as completed
|
||||||
logger.LogInformation("Migration '{Name}' applied successfully", migrationRoutine.Name);
|
logger.LogInformation("Migration '{Name}' applied successfully", migrationRoutine.Name);
|
||||||
migrationOptions.Applied.Add((migrationRoutine.Id, migrationRoutine.Name));
|
migrationOptions.Applied.Add((migrationRoutine.Id, migrationRoutine.Name));
|
||||||
host.ConfigurationManager.SaveConfiguration(MigrationsListStore.StoreKey, migrationOptions);
|
saveConfiguration(migrationOptions);
|
||||||
logger.LogDebug("Migration '{Name}' marked as applied in configuration.", migrationRoutine.Name);
|
logger.LogDebug("Migration '{Name}' marked as applied in configuration.", migrationRoutine.Name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,138 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Xml;
|
||||||
|
using System.Xml.Serialization;
|
||||||
|
using Emby.Server.Implementations;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace Jellyfin.Server.Migrations.PreStartupRoutines;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public class CreateNetworkConfiguration : IMigrationRoutine
|
||||||
|
{
|
||||||
|
private readonly ServerApplicationPaths _applicationPaths;
|
||||||
|
private readonly ILogger<CreateNetworkConfiguration> _logger;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="CreateNetworkConfiguration"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="applicationPaths">An instance of <see cref="ServerApplicationPaths"/>.</param>
|
||||||
|
/// <param name="loggerFactory">An instance of the <see cref="ILoggerFactory"/> interface.</param>
|
||||||
|
public CreateNetworkConfiguration(ServerApplicationPaths applicationPaths, ILoggerFactory loggerFactory)
|
||||||
|
{
|
||||||
|
_applicationPaths = applicationPaths;
|
||||||
|
_logger = loggerFactory.CreateLogger<CreateNetworkConfiguration>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public Guid Id => Guid.Parse("9B354818-94D5-4B68-AC49-E35CB85F9D84");
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public string Name => nameof(CreateNetworkConfiguration);
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public bool PerformOnNewInstall => false;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Perform()
|
||||||
|
{
|
||||||
|
string path = Path.Combine(_applicationPaths.ConfigurationDirectoryPath, "network.xml");
|
||||||
|
if (File.Exists(path))
|
||||||
|
{
|
||||||
|
_logger.LogDebug("Network configuration file already exists, skipping");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var serverConfigSerializer = new XmlSerializer(typeof(OldNetworkConfiguration), new XmlRootAttribute("ServerConfiguration"));
|
||||||
|
using var xmlReader = XmlReader.Create(_applicationPaths.SystemConfigurationFilePath);
|
||||||
|
var networkSettings = serverConfigSerializer.Deserialize(xmlReader);
|
||||||
|
|
||||||
|
var networkConfigSerializer = new XmlSerializer(typeof(OldNetworkConfiguration), new XmlRootAttribute("NetworkConfiguration"));
|
||||||
|
var xmlWriterSettings = new XmlWriterSettings { Indent = true };
|
||||||
|
using var xmlWriter = XmlWriter.Create(path, xmlWriterSettings);
|
||||||
|
networkConfigSerializer.Serialize(xmlWriter, networkSettings);
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma warning disable CS1591
|
||||||
|
public sealed class OldNetworkConfiguration
|
||||||
|
{
|
||||||
|
public const int DefaultHttpPort = 8096;
|
||||||
|
|
||||||
|
public const int DefaultHttpsPort = 8920;
|
||||||
|
|
||||||
|
private string _baseUrl = string.Empty;
|
||||||
|
|
||||||
|
public bool RequireHttps { get; set; }
|
||||||
|
|
||||||
|
public string CertificatePath { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public string CertificatePassword { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public string BaseUrl
|
||||||
|
{
|
||||||
|
get => _baseUrl;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
// Normalize the start of the string
|
||||||
|
if (string.IsNullOrWhiteSpace(value))
|
||||||
|
{
|
||||||
|
// If baseUrl is empty, set an empty prefix string
|
||||||
|
_baseUrl = string.Empty;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value[0] != '/')
|
||||||
|
{
|
||||||
|
// If baseUrl was not configured with a leading slash, append one for consistency
|
||||||
|
value = "/" + value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normalize the end of the string
|
||||||
|
if (value[^1] == '/')
|
||||||
|
{
|
||||||
|
// If baseUrl was configured with a trailing slash, remove it for consistency
|
||||||
|
value = value.Remove(value.Length - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
_baseUrl = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int PublicHttpsPort { get; set; } = DefaultHttpsPort;
|
||||||
|
|
||||||
|
public int HttpServerPortNumber { get; set; } = DefaultHttpPort;
|
||||||
|
|
||||||
|
public int HttpsPortNumber { get; set; } = DefaultHttpsPort;
|
||||||
|
|
||||||
|
public bool EnableHttps { get; set; }
|
||||||
|
|
||||||
|
public int PublicPort { get; set; } = DefaultHttpPort;
|
||||||
|
|
||||||
|
public bool EnableIPV6 { get; set; }
|
||||||
|
|
||||||
|
public bool EnableIPV4 { get; set; } = true;
|
||||||
|
|
||||||
|
public bool IgnoreVirtualInterfaces { get; set; } = true;
|
||||||
|
|
||||||
|
public string VirtualInterfaceNames { get; set; } = "vEthernet*";
|
||||||
|
|
||||||
|
public bool TrustAllIP6Interfaces { get; set; }
|
||||||
|
|
||||||
|
public string[] PublishedServerUriBySubnet { get; set; } = Array.Empty<string>();
|
||||||
|
|
||||||
|
public string[] RemoteIPFilter { get; set; } = Array.Empty<string>();
|
||||||
|
|
||||||
|
public bool IsRemoteIPFilterBlacklist { get; set; }
|
||||||
|
|
||||||
|
public bool EnableUPnP { get; set; }
|
||||||
|
|
||||||
|
public bool EnableRemoteAccess { get; set; } = true;
|
||||||
|
|
||||||
|
public string[] LocalNetworkSubnets { get; set; } = Array.Empty<string>();
|
||||||
|
|
||||||
|
public string[] LocalNetworkAddresses { get; set; } = Array.Empty<string>();
|
||||||
|
|
||||||
|
public string[] KnownProxies { get; set; } = Array.Empty<string>();
|
||||||
|
}
|
||||||
|
#pragma warning restore CS1591
|
||||||
|
}
|
@ -175,6 +175,7 @@ namespace Jellyfin.Server
|
|||||||
}
|
}
|
||||||
|
|
||||||
PerformStaticInitialization();
|
PerformStaticInitialization();
|
||||||
|
Migrations.MigrationRunner.RunPreStartup(appPaths, _loggerFactory);
|
||||||
|
|
||||||
var appHost = new CoreAppHost(
|
var appHost = new CoreAppHost(
|
||||||
appPaths,
|
appPaths,
|
||||||
|
@ -88,7 +88,7 @@ namespace MediaBrowser.Controller.Entities.Audio
|
|||||||
{
|
{
|
||||||
if (query.IncludeItemTypes.Length == 0)
|
if (query.IncludeItemTypes.Length == 0)
|
||||||
{
|
{
|
||||||
query.IncludeItemTypes = new[] { nameof(Audio), nameof(MusicVideo), nameof(MusicAlbum) };
|
query.IncludeItemTypes = new[] { BaseItemKind.Audio, BaseItemKind.MusicVideo, BaseItemKind.MusicAlbum };
|
||||||
query.ArtistIds = new[] { Id };
|
query.ArtistIds = new[] { Id };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
using Diacritics.Extensions;
|
using Diacritics.Extensions;
|
||||||
|
using Jellyfin.Data.Enums;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace MediaBrowser.Controller.Entities.Audio
|
namespace MediaBrowser.Controller.Entities.Audio
|
||||||
@ -66,7 +67,7 @@ namespace MediaBrowser.Controller.Entities.Audio
|
|||||||
public IList<BaseItem> GetTaggedItems(InternalItemsQuery query)
|
public IList<BaseItem> GetTaggedItems(InternalItemsQuery query)
|
||||||
{
|
{
|
||||||
query.GenreIds = new[] { Id };
|
query.GenreIds = new[] { Id };
|
||||||
query.IncludeItemTypes = new[] { nameof(MusicVideo), nameof(Audio), nameof(MusicAlbum), nameof(MusicArtist) };
|
query.IncludeItemTypes = new[] { BaseItemKind.MusicVideo, BaseItemKind.Audio, BaseItemKind.MusicAlbum, BaseItemKind.MusicArtist };
|
||||||
|
|
||||||
return LibraryManager.GetItemList(query);
|
return LibraryManager.GetItemList(query);
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,8 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class BaseItem : IHasProviderIds, IHasLookupInfo<ItemLookupInfo>, IEquatable<BaseItem>
|
public abstract class BaseItem : IHasProviderIds, IHasLookupInfo<ItemLookupInfo>, IEquatable<BaseItem>
|
||||||
{
|
{
|
||||||
|
private BaseItemKind? _baseItemKind;
|
||||||
|
|
||||||
public const string TrailerFileName = "trailer";
|
public const string TrailerFileName = "trailer";
|
||||||
public const string TrailersFolderName = "trailers";
|
public const string TrailersFolderName = "trailers";
|
||||||
public const string ThemeSongsFolderName = "theme-music";
|
public const string ThemeSongsFolderName = "theme-music";
|
||||||
@ -1808,7 +1810,7 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
|
|
||||||
public BaseItemKind GetBaseItemKind()
|
public BaseItemKind GetBaseItemKind()
|
||||||
{
|
{
|
||||||
return Enum.Parse<BaseItemKind>(GetClientTypeName());
|
return _baseItemKind ??= Enum.Parse<BaseItemKind>(GetClientTypeName());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -792,7 +792,7 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
|
|
||||||
private bool RequiresPostFiltering2(InternalItemsQuery query)
|
private bool RequiresPostFiltering2(InternalItemsQuery query)
|
||||||
{
|
{
|
||||||
if (query.IncludeItemTypes.Length == 1 && string.Equals(query.IncludeItemTypes[0], nameof(BoxSet), StringComparison.OrdinalIgnoreCase))
|
if (query.IncludeItemTypes.Length == 1 && query.IncludeItemTypes[0] == BaseItemKind.BoxSet)
|
||||||
{
|
{
|
||||||
Logger.LogDebug("Query requires post-filtering due to BoxSet query");
|
Logger.LogDebug("Query requires post-filtering due to BoxSet query");
|
||||||
return true;
|
return true;
|
||||||
@ -882,7 +882,7 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
|
|
||||||
if (query.IsPlayed.HasValue)
|
if (query.IsPlayed.HasValue)
|
||||||
{
|
{
|
||||||
if (query.IncludeItemTypes.Length == 1 && query.IncludeItemTypes.Contains(nameof(Series)))
|
if (query.IncludeItemTypes.Length == 1 && query.IncludeItemTypes.Contains(BaseItemKind.Series))
|
||||||
{
|
{
|
||||||
Logger.LogDebug("Query requires post-filtering due to IsPlayed");
|
Logger.LogDebug("Query requires post-filtering due to IsPlayed");
|
||||||
return true;
|
return true;
|
||||||
@ -1101,7 +1101,7 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (query.IncludeItemTypes.Length == 0 || query.IncludeItemTypes.Contains("Movie", StringComparer.OrdinalIgnoreCase))
|
if (query.IncludeItemTypes.Length == 0 || query.IncludeItemTypes.Contains(BaseItemKind.Movie))
|
||||||
{
|
{
|
||||||
param = true;
|
param = true;
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
using Diacritics.Extensions;
|
using Diacritics.Extensions;
|
||||||
using MediaBrowser.Controller.Entities.Audio;
|
using Jellyfin.Data.Enums;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace MediaBrowser.Controller.Entities
|
namespace MediaBrowser.Controller.Entities
|
||||||
@ -66,10 +66,10 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
query.GenreIds = new[] { Id };
|
query.GenreIds = new[] { Id };
|
||||||
query.ExcludeItemTypes = new[]
|
query.ExcludeItemTypes = new[]
|
||||||
{
|
{
|
||||||
nameof(MusicVideo),
|
BaseItemKind.MusicVideo,
|
||||||
nameof(Entities.Audio.Audio),
|
BaseItemKind.Audio,
|
||||||
nameof(MusicAlbum),
|
BaseItemKind.MusicAlbum,
|
||||||
nameof(MusicArtist)
|
BaseItemKind.MusicArtist
|
||||||
};
|
};
|
||||||
|
|
||||||
return LibraryManager.GetItemList(query);
|
return LibraryManager.GetItemList(query);
|
||||||
|
@ -27,13 +27,13 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
ExcludeArtistIds = Array.Empty<Guid>();
|
ExcludeArtistIds = Array.Empty<Guid>();
|
||||||
ExcludeInheritedTags = Array.Empty<string>();
|
ExcludeInheritedTags = Array.Empty<string>();
|
||||||
ExcludeItemIds = Array.Empty<Guid>();
|
ExcludeItemIds = Array.Empty<Guid>();
|
||||||
ExcludeItemTypes = Array.Empty<string>();
|
ExcludeItemTypes = Array.Empty<BaseItemKind>();
|
||||||
ExcludeTags = Array.Empty<string>();
|
ExcludeTags = Array.Empty<string>();
|
||||||
GenreIds = Array.Empty<Guid>();
|
GenreIds = Array.Empty<Guid>();
|
||||||
Genres = Array.Empty<string>();
|
Genres = Array.Empty<string>();
|
||||||
GroupByPresentationUniqueKey = true;
|
GroupByPresentationUniqueKey = true;
|
||||||
ImageTypes = Array.Empty<ImageType>();
|
ImageTypes = Array.Empty<ImageType>();
|
||||||
IncludeItemTypes = Array.Empty<string>();
|
IncludeItemTypes = Array.Empty<BaseItemKind>();
|
||||||
ItemIds = Array.Empty<Guid>();
|
ItemIds = Array.Empty<Guid>();
|
||||||
MediaTypes = Array.Empty<string>();
|
MediaTypes = Array.Empty<string>();
|
||||||
MinSimilarityScore = 20;
|
MinSimilarityScore = 20;
|
||||||
@ -87,9 +87,9 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
|
|
||||||
public string[] MediaTypes { get; set; }
|
public string[] MediaTypes { get; set; }
|
||||||
|
|
||||||
public string[] IncludeItemTypes { get; set; }
|
public BaseItemKind[] IncludeItemTypes { get; set; }
|
||||||
|
|
||||||
public string[] ExcludeItemTypes { get; set; }
|
public BaseItemKind[] ExcludeItemTypes { get; set; }
|
||||||
|
|
||||||
public string[] ExcludeTags { get; set; }
|
public string[] ExcludeTags { get; set; }
|
||||||
|
|
||||||
@ -229,7 +229,7 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
|
|
||||||
public Guid ParentId { get; set; }
|
public Guid ParentId { get; set; }
|
||||||
|
|
||||||
public string? ParentType { get; set; }
|
public BaseItemKind? ParentType { get; set; }
|
||||||
|
|
||||||
public Guid[] AncestorIds { get; set; }
|
public Guid[] AncestorIds { get; set; }
|
||||||
|
|
||||||
@ -314,7 +314,7 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
ParentId = value.Id;
|
ParentId = value.Id;
|
||||||
ParentType = value.GetType().Name;
|
ParentType = value.GetBaseItemKind();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -127,7 +127,7 @@ namespace MediaBrowser.Controller.Entities.TV
|
|||||||
{
|
{
|
||||||
AncestorWithPresentationUniqueKey = null,
|
AncestorWithPresentationUniqueKey = null,
|
||||||
SeriesPresentationUniqueKey = seriesKey,
|
SeriesPresentationUniqueKey = seriesKey,
|
||||||
IncludeItemTypes = new[] { nameof(Season) },
|
IncludeItemTypes = new[] { BaseItemKind.Season },
|
||||||
IsVirtualItem = false,
|
IsVirtualItem = false,
|
||||||
Limit = 0,
|
Limit = 0,
|
||||||
DtoOptions = new DtoOptions(false)
|
DtoOptions = new DtoOptions(false)
|
||||||
@ -155,7 +155,7 @@ namespace MediaBrowser.Controller.Entities.TV
|
|||||||
|
|
||||||
if (query.IncludeItemTypes.Length == 0)
|
if (query.IncludeItemTypes.Length == 0)
|
||||||
{
|
{
|
||||||
query.IncludeItemTypes = new[] { nameof(Episode) };
|
query.IncludeItemTypes = new[] { BaseItemKind.Episode };
|
||||||
}
|
}
|
||||||
|
|
||||||
query.IsVirtualItem = false;
|
query.IsVirtualItem = false;
|
||||||
@ -209,7 +209,7 @@ namespace MediaBrowser.Controller.Entities.TV
|
|||||||
|
|
||||||
query.AncestorWithPresentationUniqueKey = null;
|
query.AncestorWithPresentationUniqueKey = null;
|
||||||
query.SeriesPresentationUniqueKey = seriesKey;
|
query.SeriesPresentationUniqueKey = seriesKey;
|
||||||
query.IncludeItemTypes = new[] { nameof(Season) };
|
query.IncludeItemTypes = new[] { BaseItemKind.Season };
|
||||||
query.OrderBy = new[] { (ItemSortBy.SortName, SortOrder.Ascending) };
|
query.OrderBy = new[] { (ItemSortBy.SortName, SortOrder.Ascending) };
|
||||||
|
|
||||||
if (user != null && !user.DisplayMissingEpisodes)
|
if (user != null && !user.DisplayMissingEpisodes)
|
||||||
@ -235,7 +235,7 @@ namespace MediaBrowser.Controller.Entities.TV
|
|||||||
|
|
||||||
if (query.IncludeItemTypes.Length == 0)
|
if (query.IncludeItemTypes.Length == 0)
|
||||||
{
|
{
|
||||||
query.IncludeItemTypes = new[] { nameof(Episode), nameof(Season) };
|
query.IncludeItemTypes = new[] { BaseItemKind.Episode, BaseItemKind.Season };
|
||||||
}
|
}
|
||||||
|
|
||||||
query.IsVirtualItem = false;
|
query.IsVirtualItem = false;
|
||||||
@ -255,7 +255,7 @@ namespace MediaBrowser.Controller.Entities.TV
|
|||||||
{
|
{
|
||||||
AncestorWithPresentationUniqueKey = null,
|
AncestorWithPresentationUniqueKey = null,
|
||||||
SeriesPresentationUniqueKey = seriesKey,
|
SeriesPresentationUniqueKey = seriesKey,
|
||||||
IncludeItemTypes = new[] { nameof(Episode), nameof(Season) },
|
IncludeItemTypes = new[] { BaseItemKind.Episode, BaseItemKind.Season },
|
||||||
OrderBy = new[] { (ItemSortBy.SortName, SortOrder.Ascending) },
|
OrderBy = new[] { (ItemSortBy.SortName, SortOrder.Ascending) },
|
||||||
DtoOptions = options
|
DtoOptions = options
|
||||||
};
|
};
|
||||||
@ -359,7 +359,7 @@ namespace MediaBrowser.Controller.Entities.TV
|
|||||||
{
|
{
|
||||||
AncestorWithPresentationUniqueKey = queryFromSeries ? null : seriesKey,
|
AncestorWithPresentationUniqueKey = queryFromSeries ? null : seriesKey,
|
||||||
SeriesPresentationUniqueKey = queryFromSeries ? seriesKey : null,
|
SeriesPresentationUniqueKey = queryFromSeries ? seriesKey : null,
|
||||||
IncludeItemTypes = new[] { nameof(Episode) },
|
IncludeItemTypes = new[] { BaseItemKind.Episode },
|
||||||
OrderBy = new[] { (ItemSortBy.SortName, SortOrder.Ascending) },
|
OrderBy = new[] { (ItemSortBy.SortName, SortOrder.Ascending) },
|
||||||
DtoOptions = options
|
DtoOptions = options
|
||||||
};
|
};
|
||||||
|
@ -140,7 +140,7 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
|
|
||||||
if (query.IncludeItemTypes.Length == 0)
|
if (query.IncludeItemTypes.Length == 0)
|
||||||
{
|
{
|
||||||
query.IncludeItemTypes = new[] { nameof(Movie) };
|
query.IncludeItemTypes = new[] { BaseItemKind.Movie };
|
||||||
}
|
}
|
||||||
|
|
||||||
return parent.QueryRecursive(query);
|
return parent.QueryRecursive(query);
|
||||||
@ -165,7 +165,7 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
query.Parent = parent;
|
query.Parent = parent;
|
||||||
query.SetUser(user);
|
query.SetUser(user);
|
||||||
query.IsFavorite = true;
|
query.IsFavorite = true;
|
||||||
query.IncludeItemTypes = new[] { nameof(Movie) };
|
query.IncludeItemTypes = new[] { BaseItemKind.Movie };
|
||||||
|
|
||||||
return _libraryManager.GetItemsResult(query);
|
return _libraryManager.GetItemsResult(query);
|
||||||
}
|
}
|
||||||
@ -176,7 +176,7 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
query.Parent = parent;
|
query.Parent = parent;
|
||||||
query.SetUser(user);
|
query.SetUser(user);
|
||||||
query.IsFavorite = true;
|
query.IsFavorite = true;
|
||||||
query.IncludeItemTypes = new[] { nameof(Series) };
|
query.IncludeItemTypes = new[] { BaseItemKind.Series };
|
||||||
|
|
||||||
return _libraryManager.GetItemsResult(query);
|
return _libraryManager.GetItemsResult(query);
|
||||||
}
|
}
|
||||||
@ -187,7 +187,7 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
query.Parent = parent;
|
query.Parent = parent;
|
||||||
query.SetUser(user);
|
query.SetUser(user);
|
||||||
query.IsFavorite = true;
|
query.IsFavorite = true;
|
||||||
query.IncludeItemTypes = new[] { nameof(Episode) };
|
query.IncludeItemTypes = new[] { BaseItemKind.Episode };
|
||||||
|
|
||||||
return _libraryManager.GetItemsResult(query);
|
return _libraryManager.GetItemsResult(query);
|
||||||
}
|
}
|
||||||
@ -198,7 +198,7 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
query.Parent = parent;
|
query.Parent = parent;
|
||||||
query.SetUser(user);
|
query.SetUser(user);
|
||||||
|
|
||||||
query.IncludeItemTypes = new[] { nameof(Movie) };
|
query.IncludeItemTypes = new[] { BaseItemKind.Movie };
|
||||||
|
|
||||||
return _libraryManager.GetItemsResult(query);
|
return _libraryManager.GetItemsResult(query);
|
||||||
}
|
}
|
||||||
@ -206,7 +206,7 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
private QueryResult<BaseItem> GetMovieCollections(User user, InternalItemsQuery query)
|
private QueryResult<BaseItem> GetMovieCollections(User user, InternalItemsQuery query)
|
||||||
{
|
{
|
||||||
query.Parent = null;
|
query.Parent = null;
|
||||||
query.IncludeItemTypes = new[] { nameof(BoxSet) };
|
query.IncludeItemTypes = new[] { BaseItemKind.BoxSet };
|
||||||
query.SetUser(user);
|
query.SetUser(user);
|
||||||
query.Recursive = true;
|
query.Recursive = true;
|
||||||
|
|
||||||
@ -220,7 +220,7 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
query.Parent = parent;
|
query.Parent = parent;
|
||||||
query.SetUser(user);
|
query.SetUser(user);
|
||||||
query.Limit = GetSpecialItemsLimit();
|
query.Limit = GetSpecialItemsLimit();
|
||||||
query.IncludeItemTypes = new[] { nameof(Movie) };
|
query.IncludeItemTypes = new[] { BaseItemKind.Movie };
|
||||||
|
|
||||||
return ConvertToResult(_libraryManager.GetItemList(query));
|
return ConvertToResult(_libraryManager.GetItemList(query));
|
||||||
}
|
}
|
||||||
@ -233,7 +233,7 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
query.Parent = parent;
|
query.Parent = parent;
|
||||||
query.SetUser(user);
|
query.SetUser(user);
|
||||||
query.Limit = GetSpecialItemsLimit();
|
query.Limit = GetSpecialItemsLimit();
|
||||||
query.IncludeItemTypes = new[] { nameof(Movie) };
|
query.IncludeItemTypes = new[] { BaseItemKind.Movie };
|
||||||
|
|
||||||
return ConvertToResult(_libraryManager.GetItemList(query));
|
return ConvertToResult(_libraryManager.GetItemList(query));
|
||||||
}
|
}
|
||||||
@ -252,7 +252,7 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
{
|
{
|
||||||
var genres = parent.QueryRecursive(new InternalItemsQuery(user)
|
var genres = parent.QueryRecursive(new InternalItemsQuery(user)
|
||||||
{
|
{
|
||||||
IncludeItemTypes = new[] { nameof(Movie) },
|
IncludeItemTypes = new[] { BaseItemKind.Movie },
|
||||||
Recursive = true,
|
Recursive = true,
|
||||||
EnableTotalRecordCount = false
|
EnableTotalRecordCount = false
|
||||||
}).Items
|
}).Items
|
||||||
@ -283,7 +283,7 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
query.GenreIds = new[] { displayParent.Id };
|
query.GenreIds = new[] { displayParent.Id };
|
||||||
query.SetUser(user);
|
query.SetUser(user);
|
||||||
|
|
||||||
query.IncludeItemTypes = new[] { nameof(Movie) };
|
query.IncludeItemTypes = new[] { BaseItemKind.Movie };
|
||||||
|
|
||||||
return _libraryManager.GetItemsResult(query);
|
return _libraryManager.GetItemsResult(query);
|
||||||
}
|
}
|
||||||
@ -299,9 +299,9 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
{
|
{
|
||||||
query.IncludeItemTypes = new[]
|
query.IncludeItemTypes = new[]
|
||||||
{
|
{
|
||||||
nameof(Series),
|
BaseItemKind.Series,
|
||||||
nameof(Season),
|
BaseItemKind.Season,
|
||||||
nameof(Episode)
|
BaseItemKind.Episode
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -329,7 +329,7 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
query.Parent = parent;
|
query.Parent = parent;
|
||||||
query.SetUser(user);
|
query.SetUser(user);
|
||||||
query.Limit = GetSpecialItemsLimit();
|
query.Limit = GetSpecialItemsLimit();
|
||||||
query.IncludeItemTypes = new[] { nameof(Episode) };
|
query.IncludeItemTypes = new[] { BaseItemKind.Episode };
|
||||||
query.IsVirtualItem = false;
|
query.IsVirtualItem = false;
|
||||||
|
|
||||||
return ConvertToResult(_libraryManager.GetItemList(query));
|
return ConvertToResult(_libraryManager.GetItemList(query));
|
||||||
@ -360,7 +360,7 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
query.Parent = parent;
|
query.Parent = parent;
|
||||||
query.SetUser(user);
|
query.SetUser(user);
|
||||||
query.Limit = GetSpecialItemsLimit();
|
query.Limit = GetSpecialItemsLimit();
|
||||||
query.IncludeItemTypes = new[] { nameof(Episode) };
|
query.IncludeItemTypes = new[] { BaseItemKind.Episode };
|
||||||
|
|
||||||
return ConvertToResult(_libraryManager.GetItemList(query));
|
return ConvertToResult(_libraryManager.GetItemList(query));
|
||||||
}
|
}
|
||||||
@ -371,7 +371,7 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
query.Parent = parent;
|
query.Parent = parent;
|
||||||
query.SetUser(user);
|
query.SetUser(user);
|
||||||
|
|
||||||
query.IncludeItemTypes = new[] { nameof(Series) };
|
query.IncludeItemTypes = new[] { BaseItemKind.Series };
|
||||||
|
|
||||||
return _libraryManager.GetItemsResult(query);
|
return _libraryManager.GetItemsResult(query);
|
||||||
}
|
}
|
||||||
@ -380,7 +380,7 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
{
|
{
|
||||||
var genres = parent.QueryRecursive(new InternalItemsQuery(user)
|
var genres = parent.QueryRecursive(new InternalItemsQuery(user)
|
||||||
{
|
{
|
||||||
IncludeItemTypes = new[] { nameof(Series) },
|
IncludeItemTypes = new[] { BaseItemKind.Series },
|
||||||
Recursive = true,
|
Recursive = true,
|
||||||
EnableTotalRecordCount = false
|
EnableTotalRecordCount = false
|
||||||
}).Items
|
}).Items
|
||||||
@ -411,7 +411,7 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
query.GenreIds = new[] { displayParent.Id };
|
query.GenreIds = new[] { displayParent.Id };
|
||||||
query.SetUser(user);
|
query.SetUser(user);
|
||||||
|
|
||||||
query.IncludeItemTypes = new[] { nameof(Series) };
|
query.IncludeItemTypes = new[] { BaseItemKind.Series };
|
||||||
|
|
||||||
return _libraryManager.GetItemsResult(query);
|
return _libraryManager.GetItemsResult(query);
|
||||||
}
|
}
|
||||||
@ -499,12 +499,12 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (query.IncludeItemTypes.Length > 0 && !query.IncludeItemTypes.Contains(item.GetClientTypeName(), StringComparer.OrdinalIgnoreCase))
|
if (query.IncludeItemTypes.Length > 0 && !query.IncludeItemTypes.Contains(item.GetBaseItemKind()))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (query.ExcludeItemTypes.Length > 0 && query.ExcludeItemTypes.Contains(item.GetClientTypeName(), StringComparer.OrdinalIgnoreCase))
|
if (query.ExcludeItemTypes.Length > 0 && query.ExcludeItemTypes.Contains(item.GetBaseItemKind()))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,7 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
AdditionalParts = Array.Empty<string>();
|
AdditionalParts = Array.Empty<string>();
|
||||||
LocalAlternateVersions = Array.Empty<string>();
|
LocalAlternateVersions = Array.Empty<string>();
|
||||||
SubtitleFiles = Array.Empty<string>();
|
SubtitleFiles = Array.Empty<string>();
|
||||||
|
AudioFiles = Array.Empty<string>();
|
||||||
LinkedAlternateVersions = Array.Empty<LinkedChild>();
|
LinkedAlternateVersions = Array.Empty<LinkedChild>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,6 +98,12 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
/// <value>The subtitle paths.</value>
|
/// <value>The subtitle paths.</value>
|
||||||
public string[] SubtitleFiles { get; set; }
|
public string[] SubtitleFiles { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the audio paths.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The audio paths.</value>
|
||||||
|
public string[] AudioFiles { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value indicating whether this instance has subtitles.
|
/// Gets or sets a value indicating whether this instance has subtitles.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -696,6 +696,11 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||||||
arg.Append(" -i \"").Append(subtitlePath).Append('\"');
|
arg.Append(" -i \"").Append(subtitlePath).Append('\"');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (state.AudioStream != null && state.AudioStream.IsExternal)
|
||||||
|
{
|
||||||
|
arg.Append(" -i \"").Append(state.AudioStream.Path).Append('"');
|
||||||
|
}
|
||||||
|
|
||||||
return arg.ToString();
|
return arg.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1999,10 +2004,24 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||||||
|
|
||||||
if (state.AudioStream != null)
|
if (state.AudioStream != null)
|
||||||
{
|
{
|
||||||
args += string.Format(
|
if (state.AudioStream.IsExternal)
|
||||||
CultureInfo.InvariantCulture,
|
{
|
||||||
" -map 0:{0}",
|
int externalAudioMapIndex = state.SubtitleStream != null && state.SubtitleStream.IsExternal ? 2 : 1;
|
||||||
state.AudioStream.Index);
|
int externalAudioStream = state.MediaSource.MediaStreams.Where(i => i.Path == state.AudioStream.Path).ToList().IndexOf(state.AudioStream);
|
||||||
|
|
||||||
|
args += string.Format(
|
||||||
|
CultureInfo.InvariantCulture,
|
||||||
|
" -map {0}:{1}",
|
||||||
|
externalAudioMapIndex,
|
||||||
|
externalAudioStream);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
args += string.Format(
|
||||||
|
CultureInfo.InvariantCulture,
|
||||||
|
" -map 0:{0}",
|
||||||
|
state.AudioStream.Index);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -189,7 +189,7 @@ namespace MediaBrowser.Controller.Playlists
|
|||||||
return LibraryManager.GetItemList(new InternalItemsQuery(user)
|
return LibraryManager.GetItemList(new InternalItemsQuery(user)
|
||||||
{
|
{
|
||||||
Recursive = true,
|
Recursive = true,
|
||||||
IncludeItemTypes = new[] { nameof(Audio) },
|
IncludeItemTypes = new[] { BaseItemKind.Audio },
|
||||||
GenreIds = new[] { musicGenre.Id },
|
GenreIds = new[] { musicGenre.Id },
|
||||||
OrderBy = new[] { (ItemSortBy.AlbumArtist, SortOrder.Ascending), (ItemSortBy.Album, SortOrder.Ascending), (ItemSortBy.SortName, SortOrder.Ascending) },
|
OrderBy = new[] { (ItemSortBy.AlbumArtist, SortOrder.Ascending), (ItemSortBy.Album, SortOrder.Ascending), (ItemSortBy.SortName, SortOrder.Ascending) },
|
||||||
DtoOptions = options
|
DtoOptions = options
|
||||||
@ -201,7 +201,7 @@ namespace MediaBrowser.Controller.Playlists
|
|||||||
return LibraryManager.GetItemList(new InternalItemsQuery(user)
|
return LibraryManager.GetItemList(new InternalItemsQuery(user)
|
||||||
{
|
{
|
||||||
Recursive = true,
|
Recursive = true,
|
||||||
IncludeItemTypes = new[] { nameof(Audio) },
|
IncludeItemTypes = new[] { BaseItemKind.Audio },
|
||||||
ArtistIds = new[] { musicArtist.Id },
|
ArtistIds = new[] { musicArtist.Id },
|
||||||
OrderBy = new[] { (ItemSortBy.AlbumArtist, SortOrder.Ascending), (ItemSortBy.Album, SortOrder.Ascending), (ItemSortBy.SortName, SortOrder.Ascending) },
|
OrderBy = new[] { (ItemSortBy.AlbumArtist, SortOrder.Ascending), (ItemSortBy.Album, SortOrder.Ascending), (ItemSortBy.SortName, SortOrder.Ascending) },
|
||||||
DtoOptions = options
|
DtoOptions = options
|
||||||
|
@ -45,6 +45,7 @@ namespace MediaBrowser.MediaEncoding.Probing
|
|||||||
{
|
{
|
||||||
"AC/DC",
|
"AC/DC",
|
||||||
"Au/Ra",
|
"Au/Ra",
|
||||||
|
"Bremer/McCoy",
|
||||||
"이달의 소녀 1/3",
|
"이달의 소녀 1/3",
|
||||||
"LOONA 1/3",
|
"LOONA 1/3",
|
||||||
"LOONA / yyxy",
|
"LOONA / yyxy",
|
||||||
@ -648,11 +649,6 @@ namespace MediaBrowser.MediaEncoding.Probing
|
|||||||
stream.IsAVC = false;
|
stream.IsAVC = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(streamInfo.FieldOrder) && !string.Equals(streamInfo.FieldOrder, "progressive", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
stream.IsInterlaced = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filter out junk
|
// Filter out junk
|
||||||
if (!string.IsNullOrWhiteSpace(streamInfo.CodecTagString) && !streamInfo.CodecTagString.Contains("[0]", StringComparison.OrdinalIgnoreCase))
|
if (!string.IsNullOrWhiteSpace(streamInfo.CodecTagString) && !streamInfo.CodecTagString.Contains("[0]", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
@ -724,6 +720,23 @@ namespace MediaBrowser.MediaEncoding.Probing
|
|||||||
stream.AverageFrameRate = GetFrameRate(streamInfo.AverageFrameRate);
|
stream.AverageFrameRate = GetFrameRate(streamInfo.AverageFrameRate);
|
||||||
stream.RealFrameRate = GetFrameRate(streamInfo.RFrameRate);
|
stream.RealFrameRate = GetFrameRate(streamInfo.RFrameRate);
|
||||||
|
|
||||||
|
// Some interlaced H.264 files in mp4 containers using MBAFF coding aren't flagged as being interlaced by FFprobe,
|
||||||
|
// so for H.264 files we also calculate the frame rate from the codec time base and check if it is double the reported
|
||||||
|
// frame rate (both rounded to the nearest integer) to determine if the file is interlaced
|
||||||
|
int roundedTimeBaseFPS = Convert.ToInt32(1 / GetFrameRate(stream.CodecTimeBase) ?? 0);
|
||||||
|
int roundedDoubleFrameRate = Convert.ToInt32(stream.AverageFrameRate * 2 ?? 0);
|
||||||
|
|
||||||
|
bool videoInterlaced = !string.IsNullOrWhiteSpace(streamInfo.FieldOrder)
|
||||||
|
&& !string.Equals(streamInfo.FieldOrder, "progressive", StringComparison.OrdinalIgnoreCase);
|
||||||
|
bool h264MbaffCoded = string.Equals(stream.Codec, "h264", StringComparison.OrdinalIgnoreCase)
|
||||||
|
&& string.IsNullOrWhiteSpace(streamInfo.FieldOrder)
|
||||||
|
&& roundedTimeBaseFPS == roundedDoubleFrameRate;
|
||||||
|
|
||||||
|
if (videoInterlaced || h264MbaffCoded)
|
||||||
|
{
|
||||||
|
stream.IsInterlaced = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (isAudio
|
if (isAudio
|
||||||
|| string.Equals(stream.Codec, "mjpeg", StringComparison.OrdinalIgnoreCase)
|
|| string.Equals(stream.Codec, "mjpeg", StringComparison.OrdinalIgnoreCase)
|
||||||
|| string.Equals(stream.Codec, "gif", StringComparison.OrdinalIgnoreCase)
|
|| string.Equals(stream.Codec, "gif", StringComparison.OrdinalIgnoreCase)
|
||||||
|
@ -13,18 +13,6 @@ namespace MediaBrowser.Model.Configuration
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class ServerConfiguration : BaseApplicationConfiguration
|
public class ServerConfiguration : BaseApplicationConfiguration
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// The default value for <see cref="HttpServerPortNumber"/>.
|
|
||||||
/// </summary>
|
|
||||||
public const int DefaultHttpPort = 8096;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The default value for <see cref="PublicHttpsPort"/> and <see cref="HttpsPortNumber"/>.
|
|
||||||
/// </summary>
|
|
||||||
public const int DefaultHttpsPort = 8920;
|
|
||||||
|
|
||||||
private string _baseUrl = string.Empty;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="ServerConfiguration" /> class.
|
/// Initializes a new instance of the <see cref="ServerConfiguration" /> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -75,149 +63,13 @@ namespace MediaBrowser.Model.Configuration
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets a value indicating whether to enable automatic port forwarding.
|
|
||||||
/// </summary>
|
|
||||||
public bool EnableUPnP { get; set; } = false;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value indicating whether to enable prometheus metrics exporting.
|
/// Gets or sets a value indicating whether to enable prometheus metrics exporting.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool EnableMetrics { get; set; } = false;
|
public bool EnableMetrics { get; set; } = false;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the public mapped port.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The public mapped port.</value>
|
|
||||||
public int PublicPort { get; set; } = DefaultHttpPort;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets a value indicating whether the http port should be mapped as part of UPnP automatic port forwarding.
|
|
||||||
/// </summary>
|
|
||||||
public bool UPnPCreateHttpPortMap { get; set; } = false;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets client udp port range.
|
|
||||||
/// </summary>
|
|
||||||
public string UDPPortRange { get; set; } = string.Empty;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets a value indicating whether IPV6 capability is enabled.
|
|
||||||
/// </summary>
|
|
||||||
public bool EnableIPV6 { get; set; } = false;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets a value indicating whether IPV4 capability is enabled.
|
|
||||||
/// </summary>
|
|
||||||
public bool EnableIPV4 { get; set; } = true;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets a value indicating whether detailed ssdp logs are sent to the console/log.
|
|
||||||
/// "Emby.Dlna": "Debug" must be set in logging.default.json for this property to work.
|
|
||||||
/// </summary>
|
|
||||||
public bool EnableSSDPTracing { get; set; } = false;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets a value indicating whether an IP address is to be used to filter the detailed ssdp logs that are being sent to the console/log.
|
|
||||||
/// If the setting "Emby.Dlna": "Debug" msut be set in logging.default.json for this property to work.
|
|
||||||
/// </summary>
|
|
||||||
public string SSDPTracingFilter { get; set; } = string.Empty;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the number of times SSDP UDP messages are sent.
|
|
||||||
/// </summary>
|
|
||||||
public int UDPSendCount { get; set; } = 2;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the delay between each groups of SSDP messages (in ms).
|
|
||||||
/// </summary>
|
|
||||||
public int UDPSendDelay { get; set; } = 100;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets a value indicating whether address names that match <see cref="VirtualInterfaceNames"/> should be Ignore for the purposes of binding.
|
|
||||||
/// </summary>
|
|
||||||
public bool IgnoreVirtualInterfaces { get; set; } = true;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets a value indicating the interfaces that should be ignored. The list can be comma separated. <seealso cref="IgnoreVirtualInterfaces"/>.
|
|
||||||
/// </summary>
|
|
||||||
public string VirtualInterfaceNames { get; set; } = "vEthernet*";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the time (in seconds) between the pings of SSDP gateway monitor.
|
|
||||||
/// </summary>
|
|
||||||
public int GatewayMonitorPeriod { get; set; } = 60;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a value indicating whether multi-socket binding is available.
|
|
||||||
/// </summary>
|
|
||||||
public bool EnableMultiSocketBinding { get; } = true;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets a value indicating whether all IPv6 interfaces should be treated as on the internal network.
|
|
||||||
/// Depending on the address range implemented ULA ranges might not be used.
|
|
||||||
/// </summary>
|
|
||||||
public bool TrustAllIP6Interfaces { get; set; } = false;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the ports that HDHomerun uses.
|
|
||||||
/// </summary>
|
|
||||||
public string HDHomerunPortRange { get; set; } = string.Empty;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets PublishedServerUri to advertise for specific subnets.
|
|
||||||
/// </summary>
|
|
||||||
public string[] PublishedServerUriBySubnet { get; set; } = Array.Empty<string>();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets a value indicating whether Autodiscovery tracing is enabled.
|
|
||||||
/// </summary>
|
|
||||||
public bool AutoDiscoveryTracing { get; set; } = false;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets a value indicating whether Autodiscovery is enabled.
|
|
||||||
/// </summary>
|
|
||||||
public bool AutoDiscovery { get; set; } = true;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the public HTTPS port.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The public HTTPS port.</value>
|
|
||||||
public int PublicHttpsPort { get; set; } = DefaultHttpsPort;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the HTTP server port number.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The HTTP server port number.</value>
|
|
||||||
public int HttpServerPortNumber { get; set; } = DefaultHttpPort;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the HTTPS server port number.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The HTTPS server port number.</value>
|
|
||||||
public int HttpsPortNumber { get; set; } = DefaultHttpsPort;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets a value indicating whether to use HTTPS.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// In order for HTTPS to be used, in addition to setting this to true, valid values must also be
|
|
||||||
/// provided for <see cref="CertificatePath"/> and <see cref="CertificatePassword"/>.
|
|
||||||
/// </remarks>
|
|
||||||
public bool EnableHttps { get; set; } = false;
|
|
||||||
|
|
||||||
public bool EnableNormalizedItemByNameIds { get; set; } = true;
|
public bool EnableNormalizedItemByNameIds { get; set; } = true;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the filesystem path of an X.509 certificate to use for SSL.
|
|
||||||
/// </summary>
|
|
||||||
public string CertificatePath { get; set; } = string.Empty;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the password required to access the X.509 certificate data in the file specified by <see cref="CertificatePath"/>.
|
|
||||||
/// </summary>
|
|
||||||
public string CertificatePassword { get; set; } = string.Empty;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value indicating whether this instance is port authorized.
|
/// Gets or sets a value indicating whether this instance is port authorized.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -229,11 +81,6 @@ namespace MediaBrowser.Model.Configuration
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool QuickConnectAvailable { get; set; } = false;
|
public bool QuickConnectAvailable { get; set; } = false;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets a value indicating whether access outside of the LAN is permitted.
|
|
||||||
/// </summary>
|
|
||||||
public bool EnableRemoteAccess { get; set; } = true;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value indicating whether [enable case sensitive item ids].
|
/// Gets or sets a value indicating whether [enable case sensitive item ids].
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -318,13 +165,6 @@ namespace MediaBrowser.Model.Configuration
|
|||||||
/// <value>The file watcher delay.</value>
|
/// <value>The file watcher delay.</value>
|
||||||
public int LibraryMonitorDelay { get; set; } = 60;
|
public int LibraryMonitorDelay { get; set; } = 60;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets a value indicating whether [enable dashboard response caching].
|
|
||||||
/// Allows potential contributors without visual studio to modify production dashboard code and test changes.
|
|
||||||
/// </summary>
|
|
||||||
/// <value><c>true</c> if [enable dashboard response caching]; otherwise, <c>false</c>.</value>
|
|
||||||
public bool EnableDashboardResponseCaching { get; set; } = true;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the image saving convention.
|
/// Gets or sets the image saving convention.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -337,36 +177,6 @@ namespace MediaBrowser.Model.Configuration
|
|||||||
|
|
||||||
public string ServerName { get; set; } = string.Empty;
|
public string ServerName { get; set; } = string.Empty;
|
||||||
|
|
||||||
public string BaseUrl
|
|
||||||
{
|
|
||||||
get => _baseUrl;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
// Normalize the start of the string
|
|
||||||
if (string.IsNullOrWhiteSpace(value))
|
|
||||||
{
|
|
||||||
// If baseUrl is empty, set an empty prefix string
|
|
||||||
_baseUrl = string.Empty;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value[0] != '/')
|
|
||||||
{
|
|
||||||
// If baseUrl was not configured with a leading slash, append one for consistency
|
|
||||||
value = "/" + value;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Normalize the end of the string
|
|
||||||
if (value[value.Length - 1] == '/')
|
|
||||||
{
|
|
||||||
// If baseUrl was configured with a trailing slash, remove it for consistency
|
|
||||||
value = value.Remove(value.Length - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
_baseUrl = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string UICulture { get; set; } = "en-US";
|
public string UICulture { get; set; } = "en-US";
|
||||||
|
|
||||||
public bool SaveMetadataHidden { get; set; } = false;
|
public bool SaveMetadataHidden { get; set; } = false;
|
||||||
@ -381,43 +191,16 @@ namespace MediaBrowser.Model.Configuration
|
|||||||
|
|
||||||
public bool DisplaySpecialsWithinSeasons { get; set; } = true;
|
public bool DisplaySpecialsWithinSeasons { get; set; } = true;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the subnets that are deemed to make up the LAN.
|
|
||||||
/// </summary>
|
|
||||||
public string[] LocalNetworkSubnets { get; set; } = Array.Empty<string>();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the interface addresses which Jellyfin will bind to. If empty, all interfaces will be used.
|
|
||||||
/// </summary>
|
|
||||||
public string[] LocalNetworkAddresses { get; set; } = Array.Empty<string>();
|
|
||||||
|
|
||||||
public string[] CodecsUsed { get; set; } = Array.Empty<string>();
|
public string[] CodecsUsed { get; set; } = Array.Empty<string>();
|
||||||
|
|
||||||
public List<RepositoryInfo> PluginRepositories { get; set; } = new List<RepositoryInfo>();
|
public List<RepositoryInfo> PluginRepositories { get; set; } = new List<RepositoryInfo>();
|
||||||
|
|
||||||
public bool EnableExternalContentInSuggestions { get; set; } = true;
|
public bool EnableExternalContentInSuggestions { get; set; } = true;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets a value indicating whether the server should force connections over HTTPS.
|
|
||||||
/// </summary>
|
|
||||||
public bool RequireHttps { get; set; } = false;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the filter for remote IP connectivity. Used in conjuntion with <seealso cref="IsRemoteIPFilterBlacklist"/>.
|
|
||||||
/// </summary>
|
|
||||||
public string[] RemoteIPFilter { get; set; } = Array.Empty<string>();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets a value indicating whether <seealso cref="RemoteIPFilter"/> contains a blacklist or a whitelist. Default is a whitelist.
|
|
||||||
/// </summary>
|
|
||||||
public bool IsRemoteIPFilterBlacklist { get; set; } = false;
|
|
||||||
|
|
||||||
public int ImageExtractionTimeoutMs { get; set; } = 0;
|
public int ImageExtractionTimeoutMs { get; set; } = 0;
|
||||||
|
|
||||||
public PathSubstitution[] PathSubstitutions { get; set; } = Array.Empty<PathSubstitution>();
|
public PathSubstitution[] PathSubstitutions { get; set; } = Array.Empty<PathSubstitution>();
|
||||||
|
|
||||||
public string[] UninstalledPlugins { get; set; } = Array.Empty<string>();
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value indicating whether slow server responses should be logged as a warning.
|
/// Gets or sets a value indicating whether slow server responses should be logged as a warning.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -433,11 +216,6 @@ namespace MediaBrowser.Model.Configuration
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public string[] CorsHosts { get; set; } = new[] { "*" };
|
public string[] CorsHosts { get; set; } = new[] { "*" };
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the known proxies.
|
|
||||||
/// </summary>
|
|
||||||
public string[] KnownProxies { get; set; } = Array.Empty<string>();
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the number of days we should retain activity logs.
|
/// Gets or sets the number of days we should retain activity logs.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -116,7 +116,7 @@ namespace MediaBrowser.Model.Net
|
|||||||
{ "audio/x-wavpack", ".wv" },
|
{ "audio/x-wavpack", ".wv" },
|
||||||
|
|
||||||
// Type image
|
// Type image
|
||||||
{ "image/jpg", ".jpg" },
|
{ "image/jpeg", ".jpg" },
|
||||||
{ "image/x-png", ".png" },
|
{ "image/x-png", ".png" },
|
||||||
|
|
||||||
// Type text
|
// Type text
|
||||||
@ -137,7 +137,7 @@ namespace MediaBrowser.Model.Net
|
|||||||
/// <param name="filename">The filename to find the MIME type of.</param>
|
/// <param name="filename">The filename to find the MIME type of.</param>
|
||||||
/// <param name="defaultValue">The default value to return if no fitting MIME type is found.</param>
|
/// <param name="defaultValue">The default value to return if no fitting MIME type is found.</param>
|
||||||
/// <returns>The correct MIME type for the given filename, or <paramref name="defaultValue"/> if it wasn't found.</returns>
|
/// <returns>The correct MIME type for the given filename, or <paramref name="defaultValue"/> if it wasn't found.</returns>
|
||||||
[return: NotNullIfNotNullAttribute("defaultValue")]
|
[return: NotNullIfNotNull("defaultValue")]
|
||||||
public static string? GetMimeType(string filename, string? defaultValue = null)
|
public static string? GetMimeType(string filename, string? defaultValue = null)
|
||||||
{
|
{
|
||||||
if (filename.Length == 0)
|
if (filename.Length == 0)
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using Jellyfin.Data.Enums;
|
||||||
using MediaBrowser.Model.Entities;
|
using MediaBrowser.Model.Entities;
|
||||||
|
|
||||||
namespace MediaBrowser.Model.Querying
|
namespace MediaBrowser.Model.Querying
|
||||||
@ -48,7 +49,7 @@ namespace MediaBrowser.Model.Querying
|
|||||||
/// Gets or sets the include item types.
|
/// Gets or sets the include item types.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The include item types.</value>
|
/// <value>The include item types.</value>
|
||||||
public string[] IncludeItemTypes { get; set; }
|
public BaseItemKind[] IncludeItemTypes { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value indicating whether this instance is played.
|
/// Gets or sets a value indicating whether this instance is played.
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using Jellyfin.Data.Enums;
|
||||||
|
|
||||||
namespace MediaBrowser.Model.Search
|
namespace MediaBrowser.Model.Search
|
||||||
{
|
{
|
||||||
@ -16,8 +17,8 @@ namespace MediaBrowser.Model.Search
|
|||||||
IncludeStudios = true;
|
IncludeStudios = true;
|
||||||
|
|
||||||
MediaTypes = Array.Empty<string>();
|
MediaTypes = Array.Empty<string>();
|
||||||
IncludeItemTypes = Array.Empty<string>();
|
IncludeItemTypes = Array.Empty<BaseItemKind>();
|
||||||
ExcludeItemTypes = Array.Empty<string>();
|
ExcludeItemTypes = Array.Empty<BaseItemKind>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -56,9 +57,9 @@ namespace MediaBrowser.Model.Search
|
|||||||
|
|
||||||
public string[] MediaTypes { get; set; }
|
public string[] MediaTypes { get; set; }
|
||||||
|
|
||||||
public string[] IncludeItemTypes { get; set; }
|
public BaseItemKind[] IncludeItemTypes { get; set; }
|
||||||
|
|
||||||
public string[] ExcludeItemTypes { get; set; }
|
public BaseItemKind[] ExcludeItemTypes { get; set; }
|
||||||
|
|
||||||
public Guid? ParentId { get; set; }
|
public Guid? ParentId { get; set; }
|
||||||
|
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
using Jellyfin.Data.Enums;
|
||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
namespace MediaBrowser.Model.Session
|
namespace MediaBrowser.Model.Session
|
||||||
{
|
{
|
||||||
@ -8,10 +10,9 @@ namespace MediaBrowser.Model.Session
|
|||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the item type.
|
/// Gets or sets the item type.
|
||||||
/// Artist, Genre, Studio, Person, or any kind of BaseItem.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The type of the item.</value>
|
/// <value>The type of the item.</value>
|
||||||
public string ItemType { get; set; }
|
public BaseItemKind ItemType { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the item id.
|
/// Gets or sets the item id.
|
||||||
|
@ -11,6 +11,7 @@ using System.Net.Http;
|
|||||||
using System.Net.Mime;
|
using System.Net.Mime;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Jellyfin.Data.Enums;
|
||||||
using Jellyfin.Data.Events;
|
using Jellyfin.Data.Events;
|
||||||
using MediaBrowser.Common.Net;
|
using MediaBrowser.Common.Net;
|
||||||
using MediaBrowser.Common.Progress;
|
using MediaBrowser.Common.Progress;
|
||||||
@ -1133,7 +1134,7 @@ namespace MediaBrowser.Providers.Manager
|
|||||||
var albums = _libraryManager
|
var albums = _libraryManager
|
||||||
.GetItemList(new InternalItemsQuery
|
.GetItemList(new InternalItemsQuery
|
||||||
{
|
{
|
||||||
IncludeItemTypes = new[] { nameof(MusicAlbum) },
|
IncludeItemTypes = new[] { BaseItemKind.MusicAlbum },
|
||||||
ArtistIds = new[] { item.Id },
|
ArtistIds = new[] { item.Id },
|
||||||
DtoOptions = new DtoOptions(false)
|
DtoOptions = new DtoOptions(false)
|
||||||
{
|
{
|
||||||
|
176
MediaBrowser.Providers/MediaInfo/AudioResolver.cs
Normal file
176
MediaBrowser.Providers/MediaInfo/AudioResolver.cs
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Emby.Naming.Audio;
|
||||||
|
using Emby.Naming.Common;
|
||||||
|
using Jellyfin.Extensions;
|
||||||
|
using MediaBrowser.Controller.Entities;
|
||||||
|
using MediaBrowser.Controller.MediaEncoding;
|
||||||
|
using MediaBrowser.Controller.Providers;
|
||||||
|
using MediaBrowser.Model.Dlna;
|
||||||
|
using MediaBrowser.Model.Dto;
|
||||||
|
using MediaBrowser.Model.Entities;
|
||||||
|
using MediaBrowser.Model.Globalization;
|
||||||
|
using MediaBrowser.Model.MediaInfo;
|
||||||
|
|
||||||
|
namespace MediaBrowser.Providers.MediaInfo
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Resolves external audios for videos.
|
||||||
|
/// </summary>
|
||||||
|
public class AudioResolver
|
||||||
|
{
|
||||||
|
private readonly ILocalizationManager _localizationManager;
|
||||||
|
private readonly IMediaEncoder _mediaEncoder;
|
||||||
|
private readonly NamingOptions _namingOptions;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="AudioResolver"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="localizationManager">The localization manager.</param>
|
||||||
|
/// <param name="mediaEncoder">The media encoder.</param>
|
||||||
|
/// <param name="namingOptions">The naming options.</param>
|
||||||
|
public AudioResolver(
|
||||||
|
ILocalizationManager localizationManager,
|
||||||
|
IMediaEncoder mediaEncoder,
|
||||||
|
NamingOptions namingOptions)
|
||||||
|
{
|
||||||
|
_localizationManager = localizationManager;
|
||||||
|
_mediaEncoder = mediaEncoder;
|
||||||
|
_namingOptions = namingOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the audio streams found in the external audio files for the given video.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="video">The video to get the external audio streams from.</param>
|
||||||
|
/// <param name="startIndex">The stream index to start adding audio streams at.</param>
|
||||||
|
/// <param name="directoryService">The directory service to search for files.</param>
|
||||||
|
/// <param name="clearCache">True if the directory service cache should be cleared before searching.</param>
|
||||||
|
/// <param name="cancellationToken">The cancellation token to cancel operation.</param>
|
||||||
|
/// <returns>A list of external audio streams.</returns>
|
||||||
|
public async IAsyncEnumerable<MediaStream> GetExternalAudioStreams(
|
||||||
|
Video video,
|
||||||
|
int startIndex,
|
||||||
|
IDirectoryService directoryService,
|
||||||
|
bool clearCache,
|
||||||
|
[EnumeratorCancellation] CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
|
if (!video.IsFileProtocol)
|
||||||
|
{
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerable<string> paths = GetExternalAudioFiles(video, directoryService, clearCache);
|
||||||
|
foreach (string path in paths)
|
||||||
|
{
|
||||||
|
string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(path);
|
||||||
|
Model.MediaInfo.MediaInfo mediaInfo = await GetMediaInfo(path, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
|
foreach (MediaStream mediaStream in mediaInfo.MediaStreams)
|
||||||
|
{
|
||||||
|
mediaStream.Index = startIndex++;
|
||||||
|
mediaStream.Type = MediaStreamType.Audio;
|
||||||
|
mediaStream.IsExternal = true;
|
||||||
|
mediaStream.Path = path;
|
||||||
|
mediaStream.IsDefault = false;
|
||||||
|
mediaStream.Title = null;
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(mediaStream.Language))
|
||||||
|
{
|
||||||
|
// Try to translate to three character code
|
||||||
|
// Be flexible and check against both the full and three character versions
|
||||||
|
var language = StringExtensions.RightPart(fileNameWithoutExtension, '.').ToString();
|
||||||
|
|
||||||
|
if (language != fileNameWithoutExtension)
|
||||||
|
{
|
||||||
|
var culture = _localizationManager.FindLanguageInfo(language);
|
||||||
|
|
||||||
|
language = culture == null ? language : culture.ThreeLetterISOLanguageName;
|
||||||
|
mediaStream.Language = language;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
yield return mediaStream;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the external audio file paths for the given video.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="video">The video to get the external audio file paths from.</param>
|
||||||
|
/// <param name="directoryService">The directory service to search for files.</param>
|
||||||
|
/// <param name="clearCache">True if the directory service cache should be cleared before searching.</param>
|
||||||
|
/// <returns>A list of external audio file paths.</returns>
|
||||||
|
public IEnumerable<string> GetExternalAudioFiles(
|
||||||
|
Video video,
|
||||||
|
IDirectoryService directoryService,
|
||||||
|
bool clearCache)
|
||||||
|
{
|
||||||
|
if (!video.IsFileProtocol)
|
||||||
|
{
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if video folder exists
|
||||||
|
string folder = video.ContainingFolderPath;
|
||||||
|
if (!Directory.Exists(folder))
|
||||||
|
{
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
|
||||||
|
string videoFileNameWithoutExtension = Path.GetFileNameWithoutExtension(video.Path);
|
||||||
|
|
||||||
|
var files = directoryService.GetFilePaths(folder, clearCache, true);
|
||||||
|
for (int i = 0; i < files.Count; i++)
|
||||||
|
{
|
||||||
|
string file = files[i];
|
||||||
|
if (string.Equals(video.Path, file, StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| !AudioFileParser.IsAudioFile(file, _namingOptions)
|
||||||
|
|| Path.GetExtension(file.AsSpan()).Equals(".strm", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(file);
|
||||||
|
// The audio filename must either be equal to the video filename or start with the video filename followed by a dot
|
||||||
|
if (videoFileNameWithoutExtension.Equals(fileNameWithoutExtension, StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| (fileNameWithoutExtension.Length > videoFileNameWithoutExtension.Length
|
||||||
|
&& fileNameWithoutExtension[videoFileNameWithoutExtension.Length] == '.'
|
||||||
|
&& fileNameWithoutExtension.StartsWith(videoFileNameWithoutExtension, StringComparison.OrdinalIgnoreCase)))
|
||||||
|
{
|
||||||
|
yield return file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the media info of the given audio file.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">The path to the audio file.</param>
|
||||||
|
/// <param name="cancellationToken">The cancellation token to cancel operation.</param>
|
||||||
|
/// <returns>The media info for the given audio file.</returns>
|
||||||
|
private Task<Model.MediaInfo.MediaInfo> GetMediaInfo(string path, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
|
return _mediaEncoder.GetMediaInfo(
|
||||||
|
new MediaInfoRequest
|
||||||
|
{
|
||||||
|
MediaType = DlnaProfileType.Audio,
|
||||||
|
MediaSource = new MediaSourceInfo
|
||||||
|
{
|
||||||
|
Path = path,
|
||||||
|
Protocol = MediaProtocol.File
|
||||||
|
}
|
||||||
|
},
|
||||||
|
cancellationToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -7,6 +7,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 Emby.Naming.Common;
|
||||||
using MediaBrowser.Controller.Chapters;
|
using MediaBrowser.Controller.Chapters;
|
||||||
using MediaBrowser.Controller.Configuration;
|
using MediaBrowser.Controller.Configuration;
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
@ -39,6 +40,7 @@ namespace MediaBrowser.Providers.MediaInfo
|
|||||||
{
|
{
|
||||||
private readonly ILogger<FFProbeProvider> _logger;
|
private readonly ILogger<FFProbeProvider> _logger;
|
||||||
private readonly SubtitleResolver _subtitleResolver;
|
private readonly SubtitleResolver _subtitleResolver;
|
||||||
|
private readonly AudioResolver _audioResolver;
|
||||||
private readonly FFProbeVideoInfo _videoProber;
|
private readonly FFProbeVideoInfo _videoProber;
|
||||||
private readonly FFProbeAudioInfo _audioProber;
|
private readonly FFProbeAudioInfo _audioProber;
|
||||||
|
|
||||||
@ -55,10 +57,11 @@ namespace MediaBrowser.Providers.MediaInfo
|
|||||||
IServerConfigurationManager config,
|
IServerConfigurationManager config,
|
||||||
ISubtitleManager subtitleManager,
|
ISubtitleManager subtitleManager,
|
||||||
IChapterManager chapterManager,
|
IChapterManager chapterManager,
|
||||||
ILibraryManager libraryManager)
|
ILibraryManager libraryManager,
|
||||||
|
NamingOptions namingOptions)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
|
_audioResolver = new AudioResolver(localization, mediaEncoder, namingOptions);
|
||||||
_subtitleResolver = new SubtitleResolver(BaseItem.LocalizationManager);
|
_subtitleResolver = new SubtitleResolver(BaseItem.LocalizationManager);
|
||||||
_videoProber = new FFProbeVideoInfo(
|
_videoProber = new FFProbeVideoInfo(
|
||||||
_logger,
|
_logger,
|
||||||
@ -71,7 +74,8 @@ namespace MediaBrowser.Providers.MediaInfo
|
|||||||
config,
|
config,
|
||||||
subtitleManager,
|
subtitleManager,
|
||||||
chapterManager,
|
chapterManager,
|
||||||
libraryManager);
|
libraryManager,
|
||||||
|
_audioResolver);
|
||||||
_audioProber = new FFProbeAudioInfo(mediaSourceManager, mediaEncoder, itemRepo, libraryManager);
|
_audioProber = new FFProbeAudioInfo(mediaSourceManager, mediaEncoder, itemRepo, libraryManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,7 +96,7 @@ namespace MediaBrowser.Providers.MediaInfo
|
|||||||
var file = directoryService.GetFile(path);
|
var file = directoryService.GetFile(path);
|
||||||
if (file != null && file.LastWriteTimeUtc != item.DateModified)
|
if (file != null && file.LastWriteTimeUtc != item.DateModified)
|
||||||
{
|
{
|
||||||
_logger.LogDebug("Refreshing {0} due to date modified timestamp change.", path);
|
_logger.LogDebug("Refreshing {ItemPath} due to date modified timestamp change.", path);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -102,7 +106,15 @@ namespace MediaBrowser.Providers.MediaInfo
|
|||||||
&& !video.SubtitleFiles.SequenceEqual(
|
&& !video.SubtitleFiles.SequenceEqual(
|
||||||
_subtitleResolver.GetExternalSubtitleFiles(video, directoryService, false), StringComparer.Ordinal))
|
_subtitleResolver.GetExternalSubtitleFiles(video, directoryService, false), StringComparer.Ordinal))
|
||||||
{
|
{
|
||||||
_logger.LogDebug("Refreshing {0} due to external subtitles change.", item.Path);
|
_logger.LogDebug("Refreshing {ItemPath} due to external subtitles change.", item.Path);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.SupportsLocalMetadata && video != null && !video.IsPlaceHolder
|
||||||
|
&& !video.AudioFiles.SequenceEqual(
|
||||||
|
_audioResolver.GetExternalAudioFiles(video, directoryService, false), StringComparer.Ordinal))
|
||||||
|
{
|
||||||
|
_logger.LogDebug("Refreshing {ItemPath} due to external audio change.", item.Path);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,6 +44,7 @@ namespace MediaBrowser.Providers.MediaInfo
|
|||||||
private readonly ISubtitleManager _subtitleManager;
|
private readonly ISubtitleManager _subtitleManager;
|
||||||
private readonly IChapterManager _chapterManager;
|
private readonly IChapterManager _chapterManager;
|
||||||
private readonly ILibraryManager _libraryManager;
|
private readonly ILibraryManager _libraryManager;
|
||||||
|
private readonly AudioResolver _audioResolver;
|
||||||
private readonly IMediaSourceManager _mediaSourceManager;
|
private readonly IMediaSourceManager _mediaSourceManager;
|
||||||
|
|
||||||
private readonly long _dummyChapterDuration = TimeSpan.FromMinutes(5).Ticks;
|
private readonly long _dummyChapterDuration = TimeSpan.FromMinutes(5).Ticks;
|
||||||
@ -59,7 +60,8 @@ namespace MediaBrowser.Providers.MediaInfo
|
|||||||
IServerConfigurationManager config,
|
IServerConfigurationManager config,
|
||||||
ISubtitleManager subtitleManager,
|
ISubtitleManager subtitleManager,
|
||||||
IChapterManager chapterManager,
|
IChapterManager chapterManager,
|
||||||
ILibraryManager libraryManager)
|
ILibraryManager libraryManager,
|
||||||
|
AudioResolver audioResolver)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_mediaEncoder = mediaEncoder;
|
_mediaEncoder = mediaEncoder;
|
||||||
@ -71,6 +73,7 @@ namespace MediaBrowser.Providers.MediaInfo
|
|||||||
_subtitleManager = subtitleManager;
|
_subtitleManager = subtitleManager;
|
||||||
_chapterManager = chapterManager;
|
_chapterManager = chapterManager;
|
||||||
_libraryManager = libraryManager;
|
_libraryManager = libraryManager;
|
||||||
|
_audioResolver = audioResolver;
|
||||||
_mediaSourceManager = mediaSourceManager;
|
_mediaSourceManager = mediaSourceManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -214,6 +217,8 @@ namespace MediaBrowser.Providers.MediaInfo
|
|||||||
|
|
||||||
await AddExternalSubtitles(video, mediaStreams, options, cancellationToken).ConfigureAwait(false);
|
await AddExternalSubtitles(video, mediaStreams, options, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
|
await AddExternalAudioAsync(video, mediaStreams, options, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
var libraryOptions = _libraryManager.GetLibraryOptions(video);
|
var libraryOptions = _libraryManager.GetLibraryOptions(video);
|
||||||
|
|
||||||
if (mediaInfo != null)
|
if (mediaInfo != null)
|
||||||
@ -574,6 +579,31 @@ namespace MediaBrowser.Providers.MediaInfo
|
|||||||
currentStreams.AddRange(externalSubtitleStreams);
|
currentStreams.AddRange(externalSubtitleStreams);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds the external audio.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="video">The video.</param>
|
||||||
|
/// <param name="currentStreams">The current streams.</param>
|
||||||
|
/// <param name="options">The refreshOptions.</param>
|
||||||
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
|
private async Task AddExternalAudioAsync(
|
||||||
|
Video video,
|
||||||
|
List<MediaStream> currentStreams,
|
||||||
|
MetadataRefreshOptions options,
|
||||||
|
CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var startIndex = currentStreams.Count == 0 ? 0 : currentStreams.Max(i => i.Index) + 1;
|
||||||
|
var externalAudioStreams = _audioResolver.GetExternalAudioStreams(video, startIndex, options.DirectoryService, false, cancellationToken);
|
||||||
|
|
||||||
|
await foreach (MediaStream externalAudioStream in externalAudioStreams)
|
||||||
|
{
|
||||||
|
currentStreams.Add(externalAudioStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Select all external audio file paths
|
||||||
|
video.AudioFiles = currentStreams.Where(i => i.Type == MediaStreamType.Audio && i.IsExternal).Select(i => i.Path).Distinct().ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates dummy chapters.
|
/// Creates dummy chapters.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -7,6 +7,7 @@ using System.Collections.Generic;
|
|||||||
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.Configuration;
|
using MediaBrowser.Common.Configuration;
|
||||||
using MediaBrowser.Controller.Configuration;
|
using MediaBrowser.Controller.Configuration;
|
||||||
using MediaBrowser.Controller.Dto;
|
using MediaBrowser.Controller.Dto;
|
||||||
@ -66,7 +67,7 @@ namespace MediaBrowser.Providers.MediaInfo
|
|||||||
{
|
{
|
||||||
var options = GetOptions();
|
var options = GetOptions();
|
||||||
|
|
||||||
var types = new[] { "Episode", "Movie" };
|
var types = new[] { BaseItemKind.Episode, BaseItemKind.Movie };
|
||||||
|
|
||||||
var dict = new Dictionary<Guid, BaseItem>();
|
var dict = new Dictionary<Guid, BaseItem>();
|
||||||
|
|
||||||
|
22
bump_version
22
bump_version
@ -52,7 +52,8 @@ echo $old_version
|
|||||||
|
|
||||||
# Set the build.yaml version to the specified new_version
|
# Set the build.yaml version to the specified new_version
|
||||||
old_version_sed="$( sed 's/\./\\./g' <<<"${old_version}" )" # Escape the '.' chars
|
old_version_sed="$( sed 's/\./\\./g' <<<"${old_version}" )" # Escape the '.' chars
|
||||||
sed -i "s/${old_version_sed}/${new_version}/g" ${build_file}
|
new_version_sed="$( cut -f1 -d'-' <<<"${new_version}" )"
|
||||||
|
sed -i "s/${old_version_sed}/${new_version_sed}/g" ${build_file}
|
||||||
|
|
||||||
# update nuget package version
|
# update nuget package version
|
||||||
for subproject in ${jellyfin_subprojects[@]}; do
|
for subproject in ${jellyfin_subprojects[@]}; do
|
||||||
@ -64,26 +65,29 @@ for subproject in ${jellyfin_subprojects[@]}; do
|
|||||||
| sed -E 's/<VersionPrefix>([0-9\.]+[-a-z0-9]*)<\/VersionPrefix>/\1/'
|
| sed -E 's/<VersionPrefix>([0-9\.]+[-a-z0-9]*)<\/VersionPrefix>/\1/'
|
||||||
)"
|
)"
|
||||||
echo old nuget version: $old_version
|
echo old nuget version: $old_version
|
||||||
|
new_version_sed="$( cut -f1 -d'-' <<<"${new_version}" )"
|
||||||
|
|
||||||
# Set the nuget version to the specified new_version
|
# Set the nuget version to the specified new_version
|
||||||
sed -i "s|${old_version}|${new_version}|g" ${subproject}
|
sed -i "s|${old_version}|${new_version_sed}|g" ${subproject}
|
||||||
done
|
done
|
||||||
|
|
||||||
if [[ ${new_version} == *"-"* ]]; then
|
if [[ ${new_version} == *"-"* ]]; then
|
||||||
new_version_deb="$( sed 's/-/~/g' <<<"${new_version}" )"
|
new_version_pkg="$( sed 's/-/~/g' <<<"${new_version}" )"
|
||||||
|
new_version_deb_sup=""
|
||||||
else
|
else
|
||||||
new_version_deb="${new_version}-1"
|
new_version_pkg="${new_version}"
|
||||||
|
new_version_deb_sup="-1"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Update the metapackage equivs file
|
# Update the metapackage equivs file
|
||||||
debian_equivs_file="debian/metapackage/jellyfin"
|
debian_equivs_file="debian/metapackage/jellyfin"
|
||||||
sed -i "s/${old_version_sed}/${new_version}/g" ${debian_equivs_file}
|
sed -i "s/${old_version_sed}/${new_version_pkg}/g" ${debian_equivs_file}
|
||||||
|
|
||||||
# Write out a temporary Debian changelog with our new stuff appended and some templated formatting
|
# Write out a temporary Debian changelog with our new stuff appended and some templated formatting
|
||||||
debian_changelog_file="debian/changelog"
|
debian_changelog_file="debian/changelog"
|
||||||
debian_changelog_temp="$( mktemp )"
|
debian_changelog_temp="$( mktemp )"
|
||||||
# Create new temp file with our changelog
|
# Create new temp file with our changelog
|
||||||
echo -e "jellyfin-server (${new_version_deb}) unstable; urgency=medium
|
echo -e "jellyfin-server (${new_version_pkg}${new_version_deb_sup}) unstable; urgency=medium
|
||||||
|
|
||||||
* New upstream version ${new_version}; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v${new_version}
|
* New upstream version ${new_version}; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v${new_version}
|
||||||
|
|
||||||
@ -104,7 +108,7 @@ pushd ${fedora_spec_temp_dir}
|
|||||||
# Split out the stuff before and after changelog
|
# Split out the stuff before and after changelog
|
||||||
csplit jellyfin.spec "/^%changelog/" # produces xx00 xx01
|
csplit jellyfin.spec "/^%changelog/" # produces xx00 xx01
|
||||||
# Update the version in xx00
|
# Update the version in xx00
|
||||||
sed -i "s/${old_version_sed}/${new_version_sed}/g" xx00
|
sed -i "s/${old_version_sed}/${new_version_pkg}/g" xx00
|
||||||
# Remove the header from xx01
|
# Remove the header from xx01
|
||||||
sed -i '/^%changelog/d' xx01
|
sed -i '/^%changelog/d' xx01
|
||||||
# Create new temp file with our changelog
|
# Create new temp file with our changelog
|
||||||
@ -121,5 +125,5 @@ mv ${fedora_spec_temp} ${fedora_spec_file}
|
|||||||
rm -rf ${fedora_spec_temp_dir}
|
rm -rf ${fedora_spec_temp_dir}
|
||||||
|
|
||||||
# Stage the changed files for commit
|
# Stage the changed files for commit
|
||||||
git add ${shared_version_file} ${build_file} ${debian_equivs_file} ${debian_changelog_file} ${fedora_spec_file}
|
git add .
|
||||||
git status
|
git status -v
|
||||||
|
15
debian/jellyfin.service
vendored
15
debian/jellyfin.service
vendored
@ -13,7 +13,20 @@ TimeoutSec = 15
|
|||||||
NoNewPrivileges=true
|
NoNewPrivileges=true
|
||||||
SystemCallArchitectures=native
|
SystemCallArchitectures=native
|
||||||
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 AF_NETLINK
|
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 AF_NETLINK
|
||||||
ProtectKernelModules=True
|
RestrictNamespaces=true
|
||||||
|
RestrictRealtime=true
|
||||||
|
RestrictSUIDSGID=true
|
||||||
|
ProtectClock=true
|
||||||
|
ProtectControlGroups=true
|
||||||
|
ProtectHostname=true
|
||||||
|
ProtectKernelLogs=true
|
||||||
|
ProtectKernelModules=true
|
||||||
|
ProtectKernelTunables=true
|
||||||
|
LockPersonality=true
|
||||||
|
PrivateTmp=true
|
||||||
|
PrivateDevices=false
|
||||||
|
PrivateUsers=true
|
||||||
|
RemoveIPC=true
|
||||||
SystemCallFilter=~@clock
|
SystemCallFilter=~@clock
|
||||||
SystemCallFilter=~@aio
|
SystemCallFilter=~@aio
|
||||||
SystemCallFilter=~@chown
|
SystemCallFilter=~@chown
|
||||||
|
@ -1,26 +1,41 @@
|
|||||||
VERSION := $(shell sed -ne '/^Version:/s/.* *//p' fedora/jellyfin.spec)
|
VERSION := $(shell sed -ne '/^Version:/s/.* *//p' fedora/jellyfin.spec)
|
||||||
|
outdir ?= fedora/
|
||||||
|
TARGET ?= fedora-35-x86_64
|
||||||
|
|
||||||
srpm:
|
srpm:
|
||||||
cd fedora/; \
|
pushd fedora/; \
|
||||||
SOURCE_DIR=.. \
|
if [ "$$(id -u)" = "0" ]; then \
|
||||||
WORKDIR="$${PWD}"; \
|
dnf -y install git; \
|
||||||
tar \
|
fi; \
|
||||||
--transform "s,^\.,jellyfin-server-$(VERSION)," \
|
version=$$(git describe --tags | sed -e 's/^v//' \
|
||||||
--exclude='.git*' \
|
-e 's/-[0-9]*-g.*$$//' \
|
||||||
--exclude='**/.git' \
|
-e 's/-/~/'); \
|
||||||
--exclude='**/.hg' \
|
SOURCE_DIR=.. \
|
||||||
--exclude='**/.vs' \
|
WORKDIR="$${PWD}"; \
|
||||||
--exclude='**/.vscode' \
|
tar \
|
||||||
--exclude='deployment' \
|
--transform "s,^\.,jellyfin-server-$$version," \
|
||||||
--exclude='**/bin' \
|
--exclude='.git*' \
|
||||||
--exclude='**/obj' \
|
--exclude='**/.git' \
|
||||||
--exclude='**/.nuget' \
|
--exclude='**/.hg' \
|
||||||
--exclude='*.deb' \
|
--exclude='**/.vs' \
|
||||||
--exclude='*.rpm' \
|
--exclude='**/.vscode' \
|
||||||
--exclude='jellyfin-server-$(VERSION).tar.gz' \
|
--exclude=deployment \
|
||||||
-czf "jellyfin-server-$(VERSION).tar.gz" \
|
--exclude='**/bin' \
|
||||||
-C $${SOURCE_DIR} ./
|
--exclude='**/obj' \
|
||||||
cd fedora/; \
|
--exclude='**/.nuget' \
|
||||||
rpmbuild -bs jellyfin.spec \
|
--exclude='*.deb' \
|
||||||
--define "_sourcedir $$PWD/" \
|
--exclude='*.rpm' \
|
||||||
|
--exclude=jellyfin-server-$$version.tar.gz \
|
||||||
|
-czf "jellyfin-server-$$version.tar.gz" \
|
||||||
|
-C $${SOURCE_DIR} ./; \
|
||||||
|
popd; \
|
||||||
|
./bump_version $$version
|
||||||
|
cd fedora/; \
|
||||||
|
rpmbuild -bs jellyfin.spec \
|
||||||
|
--define "_sourcedir $$PWD/" \
|
||||||
--define "_srcrpmdir $(outdir)"
|
--define "_srcrpmdir $(outdir)"
|
||||||
|
|
||||||
|
rpms: fedora/jellyfin-$(shell git describe --tags | sed -e 's/^v//' -e 's/-[0-9]*-g.*$$//' -e 's/-/~/')-1$(shell rpm --eval %dist).src.rpm
|
||||||
|
mock --addrepo=https://download.copr.fedorainfracloud.org/results/@dotnet-sig/dotnet-preview/$(TARGET)/ \
|
||||||
|
--enable-network \
|
||||||
|
-r $(TARGET) $<
|
||||||
|
4
fedora/jellyfin-server-lowports.conf
Normal file
4
fedora/jellyfin-server-lowports.conf
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# This allows Jellyfin to bind to low ports such as 80 and/or 443
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
AmbientCapabilities=CAP_NET_BIND_SERVICE
|
@ -12,7 +12,7 @@ Release: 1%{?dist}
|
|||||||
Summary: The Free Software Media System
|
Summary: The Free Software Media System
|
||||||
License: GPLv3
|
License: GPLv3
|
||||||
URL: https://jellyfin.org
|
URL: https://jellyfin.org
|
||||||
# Jellyfin Server tarball created by `make -f .copr/Makefile srpm`, real URL ends with `v%{version}.tar.gz`
|
# Jellyfin Server tarball created by `make -f .copr/Makefile srpm`, real URL ends with `v%%{version}.tar.gz`
|
||||||
Source0: jellyfin-server-%{version}.tar.gz
|
Source0: jellyfin-server-%{version}.tar.gz
|
||||||
Source11: jellyfin.service
|
Source11: jellyfin.service
|
||||||
Source12: jellyfin.env
|
Source12: jellyfin.env
|
||||||
@ -20,6 +20,7 @@ Source13: jellyfin.sudoers
|
|||||||
Source14: restart.sh
|
Source14: restart.sh
|
||||||
Source15: jellyfin.override.conf
|
Source15: jellyfin.override.conf
|
||||||
Source16: jellyfin-firewalld.xml
|
Source16: jellyfin-firewalld.xml
|
||||||
|
Source17: jellyfin-server-lowports.conf
|
||||||
|
|
||||||
%{?systemd_requires}
|
%{?systemd_requires}
|
||||||
BuildRequires: systemd
|
BuildRequires: systemd
|
||||||
@ -45,6 +46,16 @@ Requires: libcurl, fontconfig, freetype, openssl, glibc, libicu, at, sudo
|
|||||||
%description server
|
%description server
|
||||||
The Jellyfin media server backend.
|
The Jellyfin media server backend.
|
||||||
|
|
||||||
|
%package server-lowports
|
||||||
|
# RPMfusion free
|
||||||
|
Summary: The Free Software Media System Server backend. Low-port binding.
|
||||||
|
Requires: jellyfin-server
|
||||||
|
|
||||||
|
%description server-lowports
|
||||||
|
The Jellyfin media server backend low port binding package. This package
|
||||||
|
enables binding to ports < 1024. You would install this if you want
|
||||||
|
the Jellyfin server to bind to ports 80 and/or 443 for example.
|
||||||
|
|
||||||
%prep
|
%prep
|
||||||
%autosetup -n jellyfin-server-%{version} -b 0
|
%autosetup -n jellyfin-server-%{version} -b 0
|
||||||
|
|
||||||
@ -57,6 +68,7 @@ dotnet publish --configuration Release --output='%{buildroot}%{_libdir}/jellyfin
|
|||||||
"-p:DebugSymbols=false;DebugType=none" Jellyfin.Server
|
"-p:DebugSymbols=false;DebugType=none" Jellyfin.Server
|
||||||
%{__install} -D -m 0644 LICENSE %{buildroot}%{_datadir}/licenses/jellyfin/LICENSE
|
%{__install} -D -m 0644 LICENSE %{buildroot}%{_datadir}/licenses/jellyfin/LICENSE
|
||||||
%{__install} -D -m 0644 %{SOURCE15} %{buildroot}%{_sysconfdir}/systemd/system/jellyfin.service.d/override.conf
|
%{__install} -D -m 0644 %{SOURCE15} %{buildroot}%{_sysconfdir}/systemd/system/jellyfin.service.d/override.conf
|
||||||
|
%{__install} -D -m 0644 %{SOURCE17} %{buildroot}%{_unitdir}/jellyfin.service.d/jellyfin-server-lowports.conf
|
||||||
%{__install} -D -m 0644 Jellyfin.Server/Resources/Configuration/logging.json %{buildroot}%{_sysconfdir}/jellyfin/logging.json
|
%{__install} -D -m 0644 Jellyfin.Server/Resources/Configuration/logging.json %{buildroot}%{_sysconfdir}/jellyfin/logging.json
|
||||||
%{__mkdir} -p %{buildroot}%{_bindir}
|
%{__mkdir} -p %{buildroot}%{_bindir}
|
||||||
tee %{buildroot}%{_bindir}/jellyfin << EOF
|
tee %{buildroot}%{_bindir}/jellyfin << EOF
|
||||||
@ -95,6 +107,9 @@ EOF
|
|||||||
%attr(750,jellyfin,jellyfin) %dir %{_var}/cache/jellyfin
|
%attr(750,jellyfin,jellyfin) %dir %{_var}/cache/jellyfin
|
||||||
%{_datadir}/licenses/jellyfin/LICENSE
|
%{_datadir}/licenses/jellyfin/LICENSE
|
||||||
|
|
||||||
|
%files server-lowports
|
||||||
|
%{_unitdir}/jellyfin.service.d/jellyfin-server-lowports.conf
|
||||||
|
|
||||||
%pre server
|
%pre server
|
||||||
getent group jellyfin >/dev/null || groupadd -r jellyfin
|
getent group jellyfin >/dev/null || groupadd -r jellyfin
|
||||||
getent passwd jellyfin >/dev/null || \
|
getent passwd jellyfin >/dev/null || \
|
||||||
@ -137,6 +152,9 @@ fi
|
|||||||
%systemd_postun_with_restart jellyfin.service
|
%systemd_postun_with_restart jellyfin.service
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
|
* Mon Nov 29 2021 Brian J. Murrell <brian@interlinx.bc.ca>
|
||||||
|
- Add jellyfin-server-lowports.service drop-in in a server-lowports
|
||||||
|
subpackage to allow binding to low ports
|
||||||
* Fri Dec 04 2020 Jellyfin Packaging Team <packaging@jellyfin.org>
|
* Fri Dec 04 2020 Jellyfin Packaging Team <packaging@jellyfin.org>
|
||||||
- Forthcoming stable release
|
- Forthcoming stable release
|
||||||
* Mon Jul 27 2020 Jellyfin Packaging Team <packaging@jellyfin.org>
|
* Mon Jul 27 2020 Jellyfin Packaging Team <packaging@jellyfin.org>
|
||||||
|
@ -55,35 +55,5 @@ namespace Jellyfin.Api.Tests.Helpers
|
|||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public static void GetItemTypeStrings_Empty_Empty()
|
|
||||||
{
|
|
||||||
Assert.Empty(RequestHelpers.GetItemTypeStrings(Array.Empty<BaseItemKind>()));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public static void GetItemTypeStrings_Valid_Success()
|
|
||||||
{
|
|
||||||
BaseItemKind[] input =
|
|
||||||
{
|
|
||||||
BaseItemKind.AggregateFolder,
|
|
||||||
BaseItemKind.Audio,
|
|
||||||
BaseItemKind.BasePluginFolder,
|
|
||||||
BaseItemKind.CollectionFolder
|
|
||||||
};
|
|
||||||
|
|
||||||
string[] expected =
|
|
||||||
{
|
|
||||||
"AggregateFolder",
|
|
||||||
"Audio",
|
|
||||||
"BasePluginFolder",
|
|
||||||
"CollectionFolder"
|
|
||||||
};
|
|
||||||
|
|
||||||
var res = RequestHelpers.GetItemTypeStrings(input);
|
|
||||||
|
|
||||||
Assert.Equal(expected, res);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,3 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using MediaBrowser.Model.Net;
|
using MediaBrowser.Model.Net;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
@ -129,7 +124,7 @@ namespace Jellyfin.Model.Tests.Net
|
|||||||
[InlineData("font/woff2", ".woff2")]
|
[InlineData("font/woff2", ".woff2")]
|
||||||
[InlineData("image/bmp", ".bmp")]
|
[InlineData("image/bmp", ".bmp")]
|
||||||
[InlineData("image/gif", ".gif")]
|
[InlineData("image/gif", ".gif")]
|
||||||
[InlineData("image/jpg", ".jpg")]
|
[InlineData("image/jpeg", ".jpg")]
|
||||||
[InlineData("image/png", ".png")]
|
[InlineData("image/png", ".png")]
|
||||||
[InlineData("image/svg+xml", ".svg")]
|
[InlineData("image/svg+xml", ".svg")]
|
||||||
[InlineData("image/tiff", ".tif")]
|
[InlineData("image/tiff", ".tif")]
|
||||||
|
@ -11,6 +11,18 @@ namespace Jellyfin.Server.Implementations.Tests.Library
|
|||||||
[InlineData("Superman: Red Son - tt10985510", "imdbid", "tt10985510")]
|
[InlineData("Superman: Red Son - tt10985510", "imdbid", "tt10985510")]
|
||||||
[InlineData("Superman: Red Son", "imdbid", null)]
|
[InlineData("Superman: Red Son", "imdbid", null)]
|
||||||
[InlineData("Superman: Red Son", "something", null)]
|
[InlineData("Superman: Red Son", "something", null)]
|
||||||
|
[InlineData("Superman: Red Son [imdbid1=tt11111111][imdbid=tt10985510]", "imdbid", "tt10985510")]
|
||||||
|
[InlineData("Superman: Red Son [tmdbid=618355][imdbid=tt10985510]", "imdbid", "tt10985510")]
|
||||||
|
[InlineData("Superman: Red Son [tmdbid=618355][imdbid=tt10985510]", "tmdbid", "618355")]
|
||||||
|
[InlineData("[tmdbid=618355]", "tmdbid", "618355")]
|
||||||
|
[InlineData("tmdbid=111111][tmdbid=618355]", "tmdbid", "618355")]
|
||||||
|
[InlineData("[tmdbid=618355]tmdbid=111111]", "tmdbid", "618355")]
|
||||||
|
[InlineData("tmdbid=618355]", "tmdbid", null)]
|
||||||
|
[InlineData("[tmdbid=618355", "tmdbid", null)]
|
||||||
|
[InlineData("tmdbid=618355", "tmdbid", null)]
|
||||||
|
[InlineData("tmdbid=", "tmdbid", null)]
|
||||||
|
[InlineData("tmdbid", "tmdbid", null)]
|
||||||
|
[InlineData("[tmdbid=][imdbid=tt10985510]", "tmdbid", null)]
|
||||||
public void GetAttributeValue_ValidArgs_Correct(string input, string attribute, string? expectedResult)
|
public void GetAttributeValue_ValidArgs_Correct(string input, string attribute, string? expectedResult)
|
||||||
{
|
{
|
||||||
Assert.Equal(expectedResult, PathExtensions.GetAttributeValue(input, attribute));
|
Assert.Equal(expectedResult, PathExtensions.GetAttributeValue(input, attribute));
|
||||||
|
Loading…
x
Reference in New Issue
Block a user