diff options
23 files changed, 98 insertions, 240 deletions
diff --git a/Emby.Naming/Common/NamingOptions.cs b/Emby.Naming/Common/NamingOptions.cs index a72e0ffc7..513733ab5 100644 --- a/Emby.Naming/Common/NamingOptions.cs +++ b/Emby.Naming/Common/NamingOptions.cs @@ -153,7 +153,7 @@ namespace Emby.Naming.Common CleanStrings = new[] { - @"^\s*(?<cleaned>.+?)[ _\,\.\(\)\[\]\-](3d|sbs|tab|hsbs|htab|mvc|HDR|HDC|UHD|UltraHD|4k|ac3|dts|custom|dc|divx|divx5|dsr|dsrip|dutch|dvd|dvdrip|dvdscr|dvdscreener|screener|dvdivx|cam|fragment|fs|hdtv|hdrip|hdtvrip|internal|limited|multisubs|ntsc|ogg|ogm|pal|pdtv|proper|repack|rerip|retail|cd[1-9]|r3|r5|bd5|bd|se|svcd|swedish|german|read.nfo|nfofix|unrated|ws|telesync|ts|telecine|tc|brrip|bdrip|480p|480i|576p|576i|720p|720i|1080p|1080i|2160p|hrhd|hrhdtv|hddvd|bluray|blu-ray|x264|x265|h264|h265|xvid|xvidvd|xxx|www.www|AAC|DTS|\[.*\])([ _\,\.\(\)\[\]\-]|$)", + @"^\s*(?<cleaned>.+?)[ _\,\.\(\)\[\]\-](3d|sbs|tab|hsbs|htab|mvc|HDR|HDC|UHD|UltraHD|4k|ac3|dts|custom|dc|divx|divx5|dsr|dsrip|dutch|dvd|dvdrip|dvdscr|dvdscreener|screener|dvdivx|cam|fragment|fs|hdtv|hdrip|hdtvrip|internal|limited|multisubs|ntsc|ogg|ogm|pal|pdtv|proper|repack|rerip|retail|cd[1-9]|r5|bd5|bd|se|svcd|swedish|german|read.nfo|nfofix|unrated|ws|telesync|ts|telecine|tc|brrip|bdrip|480p|480i|576p|576i|720p|720i|1080p|1080i|2160p|hrhd|hrhdtv|hddvd|bluray|blu-ray|x264|x265|h264|h265|xvid|xvidvd|xxx|www.www|AAC|DTS|\[.*\])([ _\,\.\(\)\[\]\-]|$)", @"^(?<cleaned>.+?)(\[.*\])", @"^\s*(?<cleaned>.+?)\WE[0-9]+(-|~)E?[0-9]+(\W|$)", @"^\s*\[[^\]]+\](?!\.\w+$)\s*(?<cleaned>.+)", @@ -530,6 +530,12 @@ namespace Emby.Naming.Common MediaType.Video), new ExtraRule( + ExtraType.Unknown, + ExtraRuleType.DirectoryName, + "other", + MediaType.Video), + + new ExtraRule( ExtraType.Trailer, ExtraRuleType.Filename, "trailer", @@ -647,6 +653,12 @@ namespace Emby.Naming.Common ExtraType.Unknown, ExtraRuleType.Suffix, "-extra", + MediaType.Video), + + new ExtraRule( + ExtraType.Unknown, + ExtraRuleType.Suffix, + "-other", MediaType.Video) }; diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 8b7e216e4..909972469 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -22,7 +22,6 @@ using Emby.Drawing; using Emby.Naming.Common; using Emby.Notifications; using Emby.Photos; -using Emby.Server.Implementations.Archiving; using Emby.Server.Implementations.Channels; using Emby.Server.Implementations.Collections; using Emby.Server.Implementations.Configuration; @@ -561,8 +560,6 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton<IInstallationManager, InstallationManager>(); - serviceCollection.AddSingleton<IZipClient, ZipClient>(); - serviceCollection.AddSingleton<IServerApplicationHost>(this); serviceCollection.AddSingleton(ApplicationPaths); diff --git a/Emby.Server.Implementations/Archiving/ZipClient.cs b/Emby.Server.Implementations/Archiving/ZipClient.cs deleted file mode 100644 index 6a3b250d2..000000000 --- a/Emby.Server.Implementations/Archiving/ZipClient.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System.IO; -using MediaBrowser.Model.IO; -using SharpCompress.Common; -using SharpCompress.Readers; -using SharpCompress.Readers.GZip; - -namespace Emby.Server.Implementations.Archiving -{ - /// <summary> - /// Class DotNetZipClient. - /// </summary> - public class ZipClient : IZipClient - { - /// <inheritdoc /> - public void ExtractAllFromGz(Stream source, string targetPath, bool overwriteExistingFiles) - { - using var reader = GZipReader.Open(source); - var options = new ExtractionOptions - { - ExtractFullPath = true, - Overwrite = overwriteExistingFiles - }; - - Directory.CreateDirectory(targetPath); - reader.WriteAllToDirectory(targetPath, options); - } - - /// <inheritdoc /> - public void ExtractFirstFileFromGz(Stream source, string targetPath, string defaultFileName) - { - using var reader = GZipReader.Open(source); - if (reader.MoveToNextEntry()) - { - var entry = reader.Entry; - - var filename = entry.Key; - if (string.IsNullOrWhiteSpace(filename)) - { - filename = defaultFileName; - } - - reader.WriteEntryToFile(Path.Combine(targetPath, filename)); - } - } - } -} diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index 2792a4c7c..c4fdf08b9 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -32,7 +32,6 @@ <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="6.0.9" /> <PackageReference Include="Mono.Nat" Version="3.0.3" /> <PackageReference Include="prometheus-net.DotNetRuntime" Version="4.2.4" /> - <PackageReference Include="sharpcompress" Version="0.32.2" /> <PackageReference Include="SQLitePCL.pretty.netstandard" Version="3.1.0" /> <PackageReference Include="DotNet.Glob" Version="3.1.3" /> </ItemGroup> diff --git a/Emby.Server.Implementations/Library/MediaStreamSelector.cs b/Emby.Server.Implementations/Library/MediaStreamSelector.cs index 20a2edb05..609b95772 100644 --- a/Emby.Server.Implementations/Library/MediaStreamSelector.cs +++ b/Emby.Server.Implementations/Library/MediaStreamSelector.cs @@ -45,42 +45,42 @@ namespace Emby.Server.Implementations.Library .ThenByDescending(x => x.IsForced && string.Equals(x.Language, audioTrackLanguage, StringComparison.OrdinalIgnoreCase)) .ThenByDescending(x => x.IsForced) .ThenByDescending(x => x.IsDefault) + .ThenByDescending(x => preferredLanguages.Contains(x.Language, StringComparison.OrdinalIgnoreCase)) .ToList(); MediaStream? stream = null; if (mode == SubtitlePlaybackMode.Default) { - // Prefer embedded metadata over smart logic - stream = sortedStreams.FirstOrDefault(s => s.IsExternal || s.IsForced || s.IsDefault); - - // if the audio language is not understood by the user, load their preferred subs, if there are any - if (stream == null && !preferredLanguages.Contains(audioTrackLanguage, StringComparison.OrdinalIgnoreCase)) - { - stream = sortedStreams.FirstOrDefault(s => !s.IsForced && preferredLanguages.Contains(s.Language, StringComparison.OrdinalIgnoreCase)); - } + // Load subtitles according to external, forced and default flags. + stream = sortedStreams.FirstOrDefault(x => x.IsExternal || x.IsForced || x.IsDefault); } else if (mode == SubtitlePlaybackMode.Smart) { - // if the audio language is not understood by the user, load their preferred subs, if there are any + // Only attempt to load subtitles if the audio language is not one of the user's preferred subtitle languages. + // If no subtitles of preferred language available, use default behaviour. if (!preferredLanguages.Contains(audioTrackLanguage, StringComparison.OrdinalIgnoreCase)) { - stream = streams.FirstOrDefault(s => !s.IsForced && preferredLanguages.Contains(s.Language, StringComparison.OrdinalIgnoreCase)) ?? - streams.FirstOrDefault(s => preferredLanguages.Contains(s.Language, StringComparison.OrdinalIgnoreCase)); + stream = sortedStreams.FirstOrDefault(x => preferredLanguages.Contains(x.Language, StringComparison.OrdinalIgnoreCase)) ?? + sortedStreams.FirstOrDefault(x => x.IsExternal || x.IsForced || x.IsDefault); + } + else + { + // Respect forced flag. + stream = sortedStreams.FirstOrDefault(x => x.IsForced); } } else if (mode == SubtitlePlaybackMode.Always) { - // always load the most suitable full subtitles - stream = sortedStreams.FirstOrDefault(s => !s.IsForced); + // Always load (full/non-forced) subtitles of the user's preferred subtitle language if possible, otherwise default behaviour. + stream = sortedStreams.FirstOrDefault(x => !x.IsForced && preferredLanguages.Contains(x.Language, StringComparison.OrdinalIgnoreCase)) ?? + sortedStreams.FirstOrDefault(x => x.IsExternal || x.IsForced || x.IsDefault); } else if (mode == SubtitlePlaybackMode.OnlyForced) { - // always load the most suitable full subtitles + // Only load subtitles that are flagged forced. stream = sortedStreams.FirstOrDefault(x => x.IsForced); } - // load forced subs if we have found no suitable full subtitles - stream ??= sortedStreams.FirstOrDefault(s => s.IsForced && string.Equals(s.Language, audioTrackLanguage, StringComparison.OrdinalIgnoreCase)); return stream?.Index; } diff --git a/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs index 3d6b9f3b6..b2a7abb1b 100644 --- a/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs @@ -38,7 +38,7 @@ namespace Emby.Server.Implementations.Library.Resolvers /// </summary> /// <param name="args">The args.</param> /// <returns>`0.</returns> - public override T Resolve(ItemResolveArgs args) + protected override T Resolve(ItemResolveArgs args) { return ResolveVideo<T>(args, false); } diff --git a/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs index 8f224f547..6fc200e3b 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs @@ -8,15 +8,16 @@ using System.Linq; using Jellyfin.Extensions; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Resolvers; using MediaBrowser.Model.Entities; namespace Emby.Server.Implementations.Library.Resolvers.Books { - public class BookResolver : MediaBrowser.Controller.Resolvers.ItemResolver<Book> + public class BookResolver : ItemResolver<Book> { private readonly string[] _validExtensions = { ".azw", ".azw3", ".cb7", ".cbr", ".cbt", ".cbz", ".epub", ".mobi", ".pdf" }; - public override Book Resolve(ItemResolveArgs args) + protected override Book Resolve(ItemResolveArgs args) { var collectionType = args.GetCollectionType(); diff --git a/Emby.Server.Implementations/Library/Resolvers/GenericFolderResolver.cs b/Emby.Server.Implementations/Library/Resolvers/GenericFolderResolver.cs index f109a5e9a..079962282 100644 --- a/Emby.Server.Implementations/Library/Resolvers/GenericFolderResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/GenericFolderResolver.cs @@ -2,6 +2,7 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Resolvers; namespace Emby.Server.Implementations.Library.Resolvers { diff --git a/Emby.Server.Implementations/Library/Resolvers/ItemResolver.cs b/Emby.Server.Implementations/Library/Resolvers/ItemResolver.cs deleted file mode 100644 index 3f29ab191..000000000 --- a/Emby.Server.Implementations/Library/Resolvers/ItemResolver.cs +++ /dev/null @@ -1,58 +0,0 @@ -#nullable disable - -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Resolvers; - -namespace Emby.Server.Implementations.Library.Resolvers -{ - /// <summary> - /// Class ItemResolver. - /// </summary> - /// <typeparam name="T">The type of BaseItem.</typeparam> - public abstract class ItemResolver<T> : IItemResolver - where T : BaseItem, new() - { - /// <summary> - /// Gets the priority. - /// </summary> - /// <value>The priority.</value> - public virtual ResolverPriority Priority => ResolverPriority.First; - - /// <summary> - /// Resolves the specified args. - /// </summary> - /// <param name="args">The args.</param> - /// <returns>`0.</returns> - protected virtual T Resolve(ItemResolveArgs args) - { - return null; - } - - /// <summary> - /// Sets initial values on the newly resolved item. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="args">The args.</param> - protected virtual void SetInitialItemValues(T item, ItemResolveArgs args) - { - } - - /// <summary> - /// Resolves the path. - /// </summary> - /// <param name="args">The args.</param> - /// <returns>BaseItem.</returns> - BaseItem IItemResolver.ResolvePath(ItemResolveArgs args) - { - var item = Resolve(args); - - if (item != null) - { - SetInitialItemValues(item, args); - } - - return item; - } - } -} diff --git a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs index b2f388a66..8f9e5f01b 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs @@ -80,7 +80,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies /// </summary> /// <param name="args">The args.</param> /// <returns>Video.</returns> - public override Video Resolve(ItemResolveArgs args) + protected override Video Resolve(ItemResolveArgs args) { var collectionType = args.GetCollectionType(); diff --git a/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs index af4abfb80..e11fb262e 100644 --- a/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs @@ -12,6 +12,7 @@ using Jellyfin.Extensions; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Resolvers; using MediaBrowser.Model.Entities; namespace Emby.Server.Implementations.Library.Resolvers diff --git a/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs b/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs index bfa73af2f..9ba079edf 100644 --- a/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs @@ -30,7 +30,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV /// </summary> /// <param name="args">The args.</param> /// <returns>Episode.</returns> - public override Episode Resolve(ItemResolveArgs args) + protected override Episode Resolve(ItemResolveArgs args) { var parent = args.Parent; diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index 4da677636..74321a256 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -2219,6 +2219,12 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV continue; } + // Skip ShowId without SubKey from duplicate removal actions - https://github.com/jellyfin/jellyfin/issues/5856 + if (group.Key.EndsWith("0000", StringComparison.Ordinal)) + { + continue; + } + HandleDuplicateShowIds(groupTimers); } } diff --git a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs index bd1cd1e1d..7570a2bcf 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs +++ b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs @@ -6,9 +6,9 @@ using System; using System.Collections.Generic; using System.Globalization; using System.IO; +using System.IO.Compression; using System.Linq; using System.Net.Http; -using System.Security.Cryptography; using System.Threading; using System.Threading.Tasks; using Jellyfin.Extensions; @@ -33,20 +33,17 @@ namespace Emby.Server.Implementations.LiveTv.Listings private readonly IHttpClientFactory _httpClientFactory; private readonly ILogger<XmlTvListingsProvider> _logger; private readonly IFileSystem _fileSystem; - private readonly IZipClient _zipClient; public XmlTvListingsProvider( IServerConfigurationManager config, IHttpClientFactory httpClientFactory, ILogger<XmlTvListingsProvider> logger, - IFileSystem fileSystem, - IZipClient zipClient) + IFileSystem fileSystem) { _config = config; _httpClientFactory = httpClientFactory; _logger = logger; _fileSystem = fileSystem; - _zipClient = zipClient; } public string Name => "XmlTV"; @@ -67,16 +64,12 @@ namespace Emby.Server.Implementations.LiveTv.Listings { _logger.LogInformation("xmltv path: {Path}", info.Path); - if (!info.Path.StartsWith("http", StringComparison.OrdinalIgnoreCase)) - { - return UnzipIfNeeded(info.Path, info.Path); - } - string cacheFilename = info.Id + ".xml"; string cacheFile = Path.Combine(_config.ApplicationPaths.CachePath, "xmltv", cacheFilename); + if (File.Exists(cacheFile) && File.GetLastWriteTimeUtc(cacheFile) >= DateTime.UtcNow.Subtract(_maxCacheAge)) { - return UnzipIfNeeded(info.Path, cacheFile); + return cacheFile; } // Must check if file exists as parent directory may not exist. @@ -84,93 +77,48 @@ namespace Emby.Server.Implementations.LiveTv.Listings { File.Delete(cacheFile); } + else + { + Directory.CreateDirectory(Path.GetDirectoryName(cacheFile)); + } - _logger.LogInformation("Downloading xmltv listings from {Path}", info.Path); - - Directory.CreateDirectory(Path.GetDirectoryName(cacheFile)); + if (info.Path.StartsWith("http", StringComparison.OrdinalIgnoreCase)) + { + _logger.LogInformation("Downloading xmltv listings from {Path}", info.Path); - using var response = await _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(info.Path, cancellationToken).ConfigureAwait(false); - await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false); - await using (var fileStream = new FileStream(cacheFile, FileMode.CreateNew, FileAccess.Write, FileShare.None, IODefaults.CopyToBufferSize, FileOptions.Asynchronous)) + using var response = await _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(info.Path, cancellationToken).ConfigureAwait(false); + await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false); + return await UnzipIfNeededAndCopy(info.Path, stream, cacheFile, cancellationToken).ConfigureAwait(false); + } + else { - await stream.CopyToAsync(fileStream, cancellationToken).ConfigureAwait(false); + await using var stream = AsyncFile.OpenRead(info.Path); + return await UnzipIfNeededAndCopy(info.Path, stream, cacheFile, cancellationToken).ConfigureAwait(false); } - - return UnzipIfNeeded(info.Path, cacheFile); } - private string UnzipIfNeeded(ReadOnlySpan<char> originalUrl, string file) + private async Task<string> UnzipIfNeededAndCopy(string originalUrl, Stream stream, string file, CancellationToken cancellationToken) { - ReadOnlySpan<char> ext = Path.GetExtension(originalUrl.LeftPart('?')); + await using var fileStream = new FileStream(file, FileMode.CreateNew, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous); - if (ext.Equals(".gz", StringComparison.OrdinalIgnoreCase)) + if (Path.GetExtension(originalUrl.AsSpan().LeftPart('?')).Equals(".gz", StringComparison.OrdinalIgnoreCase)) { try { - string tempFolder = ExtractGz(file); - return FindXmlFile(tempFolder); - } - catch (Exception ex) - { - _logger.LogError(ex, "Error extracting from gz file {File}", file); - } - - try - { - string tempFolder = ExtractFirstFileFromGz(file); - return FindXmlFile(tempFolder); + using var reader = new GZipStream(stream, CompressionMode.Decompress); + await reader.CopyToAsync(fileStream, cancellationToken).ConfigureAwait(false); } catch (Exception ex) { - _logger.LogError(ex, "Error extracting from zip file {File}", file); + _logger.LogError(ex, "Error extracting from gz file {File}", originalUrl); } } - - return file; - } - - private string ExtractFirstFileFromGz(string file) - { - using (var stream = File.OpenRead(file)) - { - string tempFolder = GetTempFolderPath(stream); - Directory.CreateDirectory(tempFolder); - - _zipClient.ExtractFirstFileFromGz(stream, tempFolder, "data.xml"); - - return tempFolder; - } - } - - private string ExtractGz(string file) - { - using (var stream = File.OpenRead(file)) + else { - string tempFolder = GetTempFolderPath(stream); - Directory.CreateDirectory(tempFolder); - - _zipClient.ExtractAllFromGz(stream, tempFolder, true); - - return tempFolder; + await stream.CopyToAsync(fileStream, cancellationToken).ConfigureAwait(false); } - } - private string GetTempFolderPath(Stream stream) - { -#pragma warning disable CA5351 - using var md5 = MD5.Create(); -#pragma warning restore CA5351 - var checksum = Convert.ToHexString(md5.ComputeHash(stream)); - stream.Position = 0; - return Path.Combine(_config.ApplicationPaths.TempDirectory, checksum); - } - - private string FindXmlFile(string directory) - { - return _fileSystem.GetFiles(directory, true) - .Where(i => string.Equals(i.Extension, ".xml", StringComparison.OrdinalIgnoreCase)) - .Select(i => i.FullName) - .FirstOrDefault(); + return file; } public async Task<IEnumerable<ProgramInfo>> GetProgramsAsync(ListingsProviderInfo info, string channelId, DateTime startDateUtc, DateTime endDateUtc, CancellationToken cancellationToken) @@ -213,16 +161,16 @@ namespace Emby.Server.Implementations.LiveTv.Listings IsMovie = program.Categories.Any(c => info.MovieCategories.Contains(c, StringComparison.OrdinalIgnoreCase)), IsNews = program.Categories.Any(c => info.NewsCategories.Contains(c, StringComparison.OrdinalIgnoreCase)), IsSports = program.Categories.Any(c => info.SportsCategories.Contains(c, StringComparison.OrdinalIgnoreCase)), - ImageUrl = program.Icon != null && !string.IsNullOrEmpty(program.Icon.Source) ? program.Icon.Source : null, - HasImage = program.Icon != null && !string.IsNullOrEmpty(program.Icon.Source), - OfficialRating = program.Rating != null && !string.IsNullOrEmpty(program.Rating.Value) ? program.Rating.Value : null, + ImageUrl = string.IsNullOrEmpty(program.Icon?.Source) ? null : program.Icon.Source, + HasImage = !string.IsNullOrEmpty(program.Icon?.Source), + OfficialRating = string.IsNullOrEmpty(program.Rating?.Value) ? null : program.Rating.Value, CommunityRating = program.StarRating, SeriesId = program.Episode == null ? null : program.Title.GetMD5().ToString("N", CultureInfo.InvariantCulture) }; if (string.IsNullOrWhiteSpace(program.ProgramId)) { - string uniqueString = (program.Title ?? string.Empty) + (episodeTitle ?? string.Empty) /*+ (p.IceTvEpisodeNumber ?? string.Empty)*/; + string uniqueString = (program.Title ?? string.Empty) + (episodeTitle ?? string.Empty); if (programInfo.SeasonNumber.HasValue) { diff --git a/Jellyfin.Api/Controllers/PlaystateController.cs b/Jellyfin.Api/Controllers/PlaystateController.cs index 0dd4bf803..3a2ba033e 100644 --- a/Jellyfin.Api/Controllers/PlaystateController.cs +++ b/Jellyfin.Api/Controllers/PlaystateController.cs @@ -274,7 +274,7 @@ namespace Jellyfin.Api.Controllers }; playbackProgressInfo.PlayMethod = ValidatePlayMethod(playbackProgressInfo.PlayMethod, playbackProgressInfo.PlaySessionId); - playbackProgressInfo.SessionId = await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false);; + playbackProgressInfo.SessionId = await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); await _sessionManager.OnPlaybackProgress(playbackProgressInfo).ConfigureAwait(false); return NoContent(); } @@ -319,7 +319,7 @@ namespace Jellyfin.Api.Controllers await _transcodingJobHelper.KillTranscodingJobs(User.GetDeviceId()!, playbackStopInfo.PlaySessionId, s => true).ConfigureAwait(false); } - playbackStopInfo.SessionId = await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false);; + playbackStopInfo.SessionId = await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); await _sessionManager.OnPlaybackStopped(playbackStopInfo).ConfigureAwait(false); return NoContent(); } diff --git a/Jellyfin.Api/Controllers/SessionController.cs b/Jellyfin.Api/Controllers/SessionController.cs index 28415555e..31b95162d 100644 --- a/Jellyfin.Api/Controllers/SessionController.cs +++ b/Jellyfin.Api/Controllers/SessionController.cs @@ -182,7 +182,7 @@ namespace Jellyfin.Api.Controllers }; await _sessionManager.SendPlayCommand( - await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false), + await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false), sessionId, playRequest, CancellationToken.None) @@ -210,7 +210,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] string? controllingUserId) { await _sessionManager.SendPlaystateCommand( - await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false), + await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false), sessionId, new PlaystateRequest() { diff --git a/Jellyfin.Api/Controllers/UniversalAudioController.cs b/Jellyfin.Api/Controllers/UniversalAudioController.cs index 01e13b4fe..d77126a35 100644 --- a/Jellyfin.Api/Controllers/UniversalAudioController.cs +++ b/Jellyfin.Api/Controllers/UniversalAudioController.cs @@ -108,7 +108,7 @@ namespace Jellyfin.Api.Controllers { var deviceProfile = GetDeviceProfile(container, transcodingContainer, audioCodec, transcodingProtocol, breakOnNonKeyFrames, transcodingAudioChannels, maxAudioSampleRate, maxAudioBitDepth, maxAudioChannels); - if (!userId.HasValue || userId.Value.Equals(Guid.Empty)) + if (!userId.HasValue || userId.Value.Equals(default)) { userId = User.GetUserId(); } diff --git a/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj b/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj index cbbeee024..89de998ca 100644 --- a/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj +++ b/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj @@ -18,8 +18,8 @@ <ItemGroup> <PackageReference Include="BlurHashSharp" Version="1.2.0" /> <PackageReference Include="BlurHashSharp.SkiaSharp" Version="1.2.0" /> - <PackageReference Include="SkiaSharp" Version="2.88.2" /> - <PackageReference Include="SkiaSharp.NativeAssets.Linux" Version="2.88.2" /> + <PackageReference Include="SkiaSharp" Version="2.88.3" /> + <PackageReference Include="SkiaSharp.NativeAssets.Linux" Version="2.88.3" /> <PackageReference Include="SkiaSharp.Svg" Version="1.60.0" /> </ItemGroup> diff --git a/MediaBrowser.Controller/Properties/AssemblyInfo.cs b/MediaBrowser.Controller/Properties/AssemblyInfo.cs index 60e792309..4cd5c76c1 100644 --- a/MediaBrowser.Controller/Properties/AssemblyInfo.cs +++ b/MediaBrowser.Controller/Properties/AssemblyInfo.cs @@ -1,5 +1,6 @@ using System.Reflection; using System.Resources; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following @@ -14,6 +15,7 @@ using System.Runtime.InteropServices; [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] [assembly: NeutralResourcesLanguage("en")] +[assembly: InternalsVisibleTo("Jellyfin.Server.Implementations.Tests")] // Setting ComVisible to false makes the types in this assembly not visible // to COM components. If you need to access a type in this assembly from diff --git a/MediaBrowser.Controller/Resolvers/ItemResolver.cs b/MediaBrowser.Controller/Resolvers/ItemResolver.cs index 7fd54fcc6..e7bf013fa 100644 --- a/MediaBrowser.Controller/Resolvers/ItemResolver.cs +++ b/MediaBrowser.Controller/Resolvers/ItemResolver.cs @@ -23,7 +23,7 @@ namespace MediaBrowser.Controller.Resolvers /// </summary> /// <param name="args">The args.</param> /// <returns>`0.</returns> - public virtual T Resolve(ItemResolveArgs args) + protected internal virtual T Resolve(ItemResolveArgs args) { return null; } @@ -42,7 +42,7 @@ namespace MediaBrowser.Controller.Resolvers /// </summary> /// <param name="args">The args.</param> /// <returns>BaseItem.</returns> - BaseItem IItemResolver.ResolvePath(ItemResolveArgs args) + public BaseItem ResolvePath(ItemResolveArgs args) { var item = Resolve(args); diff --git a/MediaBrowser.Model/IO/IZipClient.cs b/MediaBrowser.Model/IO/IZipClient.cs deleted file mode 100644 index 2448575d1..000000000 --- a/MediaBrowser.Model/IO/IZipClient.cs +++ /dev/null @@ -1,16 +0,0 @@ -#pragma warning disable CS1591 - -using System.IO; - -namespace MediaBrowser.Model.IO -{ - /// <summary> - /// Interface IZipClient. - /// </summary> - public interface IZipClient - { - void ExtractAllFromGz(Stream source, string targetPath, bool overwriteExistingFiles); - - void ExtractFirstFileFromGz(Stream source, string targetPath, string defaultFileName); - } -} diff --git a/MediaBrowser.Providers/MediaInfo/EmbeddedImageProvider.cs b/MediaBrowser.Providers/MediaInfo/EmbeddedImageProvider.cs index 96d7d139a..d60d829de 100644 --- a/MediaBrowser.Providers/MediaInfo/EmbeddedImageProvider.cs +++ b/MediaBrowser.Providers/MediaInfo/EmbeddedImageProvider.cs @@ -31,13 +31,14 @@ namespace MediaBrowser.Providers.MediaInfo "poster", "folder", "cover", - "default" + "default", + "movie", + "show" }; private static readonly string[] _backdropImageFileNames = { "backdrop", - "fanart", "background", "art" }; diff --git a/jellyfin.ruleset b/jellyfin.ruleset index 5ac5f4923..8144db93d 100644 --- a/jellyfin.ruleset +++ b/jellyfin.ruleset @@ -5,8 +5,16 @@ <Rule Id="SA1000" Action="Error" /> <!-- error on SA1001: Commas should not be preceded by whitespace --> <Rule Id="SA1001" Action="Error" /> + <!-- error on SA1106: Code should not contain empty statements --> + <Rule Id="SA1106" Action="Error" /> + <!-- error on SA1107: Code should not contain multiple statements on one line --> + <Rule Id="SA1107" Action="Error" /> + <!-- error on SA1028: Code should not contain trailing whitespace --> + <Rule Id="SA1028" Action="Error" /> <!-- error on SA1117: The parameters should all be placed on the same line or each parameter should be placed on its own line --> <Rule Id="SA1117" Action="Error" /> + <!-- error on SA1137: Elements should have the same indentation --> + <Rule Id="SA1137" Action="Error" /> <!-- error on SA1142: Refer to tuple fields by name --> <Rule Id="SA1142" Action="Error" /> <!-- error on SA1210: Using directives should be ordered alphabetically by the namespaces --> @@ -69,6 +77,8 @@ <Rule Id="CA1307" Action="Error" /> <!-- error on CA1309: Use ordinal StringComparison --> <Rule Id="CA1309" Action="Error" /> + <!-- error on CA1310: Specify StringComparison for correctness --> + <Rule Id="CA1310" Action="Error" /> <!-- error on CA1725: Parameter names should match base declaration --> <Rule Id="CA1725" Action="Error" /> <!-- error on CA1725: Call async methods when in an async method --> |
