diff options
172 files changed, 2605 insertions, 3274 deletions
diff --git a/Dockerfile b/Dockerfile index f8e6fec31..ec64398b2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,5 @@ ARG DOTNET_VERSION=2.2 +ARG FFMPEG_VERSION=latest FROM mcr.microsoft.com/dotnet/core/sdk:${DOTNET_VERSION} as builder WORKDIR /repo @@ -7,7 +8,7 @@ ENV DOTNET_CLI_TELEMETRY_OPTOUT=1 RUN bash -c "source deployment/common.build.sh && \ build_jellyfin Jellyfin.Server Release linux-x64 /jellyfin" -FROM jellyfin/ffmpeg as ffmpeg +FROM jellyfin/ffmpeg:${FFMPEG_VERSION} as ffmpeg FROM mcr.microsoft.com/dotnet/core/runtime:${DOTNET_VERSION} # libfontconfig1 is required for Skia RUN apt-get update \ @@ -21,7 +22,7 @@ RUN apt-get update \ COPY --from=ffmpeg / / COPY --from=builder /jellyfin /jellyfin -ARG JELLYFIN_WEB_VERSION=v10.3.7 +ARG JELLYFIN_WEB_VERSION=v10.4.0 RUN curl -L https://github.com/jellyfin/jellyfin-web/archive/${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \ && rm -rf /jellyfin/jellyfin-web \ && mv jellyfin-web-* /jellyfin/jellyfin-web diff --git a/Dockerfile.arm b/Dockerfile.arm index 651bdeff4..2b1c6bb62 100644 --- a/Dockerfile.arm +++ b/Dockerfile.arm @@ -26,7 +26,7 @@ RUN apt-get update \ && chmod 777 /cache /config /media COPY --from=builder /jellyfin /jellyfin -ARG JELLYFIN_WEB_VERSION=v10.3.7 +ARG JELLYFIN_WEB_VERSION=v10.4.0 RUN curl -L https://github.com/jellyfin/jellyfin-web/archive/${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \ && rm -rf /jellyfin/jellyfin-web \ && mv jellyfin-web-* /jellyfin/jellyfin-web diff --git a/Dockerfile.arm64 b/Dockerfile.arm64 index f6bd81e4c..5ebc82ebc 100644 --- a/Dockerfile.arm64 +++ b/Dockerfile.arm64 @@ -26,7 +26,7 @@ RUN apt-get update \ && chmod 777 /cache /config /media COPY --from=builder /jellyfin /jellyfin -ARG JELLYFIN_WEB_VERSION=v10.3.7 +ARG JELLYFIN_WEB_VERSION=v10.4.0 RUN curl -L https://github.com/jellyfin/jellyfin-web/archive/${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \ && rm -rf /jellyfin/jellyfin-web \ && mv jellyfin-web-* /jellyfin/jellyfin-web diff --git a/Emby.Dlna/ContentDirectory/ControlHandler.cs b/Emby.Dlna/ContentDirectory/ControlHandler.cs index 4f8c89e48..d22fc2177 100644 --- a/Emby.Dlna/ContentDirectory/ControlHandler.cs +++ b/Emby.Dlna/ContentDirectory/ControlHandler.cs @@ -289,7 +289,7 @@ namespace Emby.Dlna.ContentDirectory var childrenResult = GetUserItems(item, serverItem.StubType, user, sortCriteria, start, requestedCount); totalCount = childrenResult.TotalRecordCount; - provided = childrenResult.Items.Length; + provided = childrenResult.Items.Count; foreach (var i in childrenResult.Items) { @@ -309,6 +309,7 @@ namespace Emby.Dlna.ContentDirectory } } } + writer.WriteFullEndElement(); //writer.WriteEndDocument(); } @@ -386,7 +387,7 @@ namespace Emby.Dlna.ContentDirectory totalCount = childrenResult.TotalRecordCount; - provided = childrenResult.Items.Length; + provided = childrenResult.Items.Count; var dlnaOptions = _config.GetDlnaConfiguration(); @@ -677,7 +678,7 @@ namespace Emby.Dlna.ContentDirectory return new QueryResult<ServerItem> { - Items = list.ToArray(), + Items = list, TotalRecordCount = list.Count }; } @@ -755,7 +756,7 @@ namespace Emby.Dlna.ContentDirectory return new QueryResult<ServerItem> { - Items = list.ToArray(), + Items = list, TotalRecordCount = list.Count }; } @@ -860,7 +861,7 @@ namespace Emby.Dlna.ContentDirectory return new QueryResult<ServerItem> { - Items = list.ToArray(), + Items = list, TotalRecordCount = list.Count }; } diff --git a/Emby.Dlna/Didl/DidlBuilder.cs b/Emby.Dlna/Didl/DidlBuilder.cs index 26adfde83..85ef9d482 100644 --- a/Emby.Dlna/Didl/DidlBuilder.cs +++ b/Emby.Dlna/Didl/DidlBuilder.cs @@ -158,7 +158,7 @@ namespace Emby.Dlna.Didl AddGeneralProperties(item, null, context, writer, filter); - AddSamsungBookmarkInfo(item, user, writer); + AddSamsungBookmarkInfo(item, user, writer, streamInfo); // refID? // storeAttribute(itemNode, object, ClassProperties.REF_ID, false); @@ -581,7 +581,7 @@ namespace Emby.Dlna.Didl writer.WriteFullEndElement(); } - private void AddSamsungBookmarkInfo(BaseItem item, User user, XmlWriter writer) + private void AddSamsungBookmarkInfo(BaseItem item, User user, XmlWriter writer, StreamInfo streamInfo) { if (!item.SupportsPositionTicksResume || item is Folder) { @@ -605,10 +605,11 @@ namespace Emby.Dlna.Didl } var userdata = _userDataManager.GetUserData(user, item); + var playbackPositionTicks = (streamInfo != null && streamInfo.StartPositionTicks > 0) ? streamInfo.StartPositionTicks : userdata.PlaybackPositionTicks; - if (userdata.PlaybackPositionTicks > 0) + if (playbackPositionTicks > 0) { - var elementValue = string.Format("BM={0}", Convert.ToInt32(TimeSpan.FromTicks(userdata.PlaybackPositionTicks).TotalSeconds).ToString(_usCulture)); + var elementValue = string.Format("BM={0}", Convert.ToInt32(TimeSpan.FromTicks(playbackPositionTicks).TotalSeconds).ToString(_usCulture)); AddValue(writer, "sec", "dcmInfo", elementValue, secAttribute.Value); } } diff --git a/Emby.IsoMounting/.gitignore b/Emby.IsoMounting/.gitignore deleted file mode 100644 index bdc3535f7..000000000 --- a/Emby.IsoMounting/.gitignore +++ /dev/null @@ -1,108 +0,0 @@ -# Build Folders (you can keep bin if you'd like, to store dlls and pdbs) -[Bb]in/ -[Oo]bj/ - -# mstest test results -TestResults - -## Ignore Visual Studio temporary files, build results, and -## files generated by popular Visual Studio add-ons. - -# User-specific files -*.suo -*.user -*.sln.docstates - -# Build results -[Dd]ebug/ -[Rr]elease/ -x64/ -*_i.c -*_p.c -*.ilk -*.meta -*.obj -*.pch -*.pdb -*.pgc -*.pgd -*.rsp -*.sbr -*.tlb -*.tli -*.tlh -*.tmp -*.log -*.vspscc -*.vssscc -.builds - -# Visual C++ cache files -ipch/ -*.aps -*.ncb -*.opensdf -*.sdf - -# Visual Studio profiler -*.psess -*.vsp -*.vspx - -# Guidance Automation Toolkit -*.gpState - -# ReSharper is a .NET coding add-in -_ReSharper* - -# NCrunch -*.ncrunch* -.*crunch*.local.xml - -# Installshield output folder -[Ee]xpress - -# DocProject is a documentation generator add-in -DocProject/buildhelp/ -DocProject/Help/*.HxT -DocProject/Help/*.HxC -DocProject/Help/*.hhc -DocProject/Help/*.hhk -DocProject/Help/*.hhp -DocProject/Help/Html2 -DocProject/Help/html - -# Click-Once directory -publish - -# Publish Web Output -*.Publish.xml - -# NuGet Packages Directory -packages - -# Windows Azure Build Output -csx -*.build.csdef - -# Windows Store app package directory -AppPackages/ - -# Others -[Bb]in -[Oo]bj -sql -TestResults -[Tt]est[Rr]esult* -*.Cache -ClientBin -[Ss]tyle[Cc]op.* -~$* -*.dbmdl -Generated_Code #added for RIA/Silverlight projects - -# Backup & report files from converting an old project file to a newer -# Visual Studio version. Backup files are not needed, because we have git ;-) -_UpgradeReport_Files/ -Backup*/ -UpgradeLog*.XML diff --git a/Emby.IsoMounting/IsoMounter.sln b/Emby.IsoMounting/IsoMounter.sln deleted file mode 100644 index 55db1b1ae..000000000 --- a/Emby.IsoMounting/IsoMounter.sln +++ /dev/null @@ -1,25 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.27004.2009 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IsoMounter", "IsoMounter\IsoMounter.csproj", "{B94C929C-6552-4620-9BE5-422DD9A151BA}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {B94C929C-6552-4620-9BE5-422DD9A151BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B94C929C-6552-4620-9BE5-422DD9A151BA}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B94C929C-6552-4620-9BE5-422DD9A151BA}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B94C929C-6552-4620-9BE5-422DD9A151BA}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {C0E8EAD1-E4D7-44CD-B801-03BD12F30B1B} - EndGlobalSection -EndGlobal diff --git a/Emby.IsoMounting/IsoMounter/Configuration/PluginConfiguration.cs b/Emby.IsoMounting/IsoMounter/Configuration/PluginConfiguration.cs deleted file mode 100644 index ca6f40cc4..000000000 --- a/Emby.IsoMounting/IsoMounter/Configuration/PluginConfiguration.cs +++ /dev/null @@ -1,11 +0,0 @@ -using MediaBrowser.Model.Plugins; - -namespace IsoMounter.Configuration -{ - /// <summary> - /// Class PluginConfiguration. - /// </summary> - public class PluginConfiguration : BasePluginConfiguration - { - } -} diff --git a/Emby.IsoMounting/IsoMounter/IsoMounter.csproj b/Emby.IsoMounting/IsoMounter/IsoMounter.csproj deleted file mode 100644 index 4fa07fbf1..000000000 --- a/Emby.IsoMounting/IsoMounter/IsoMounter.csproj +++ /dev/null @@ -1,30 +0,0 @@ -<Project Sdk="Microsoft.NET.Sdk"> - - <ItemGroup> - <Compile Include="..\..\SharedVersion.cs" /> - </ItemGroup> - - <ItemGroup> - <ProjectReference Include="..\..\MediaBrowser.Model\MediaBrowser.Model.csproj" /> - <ProjectReference Include="..\..\MediaBrowser.Common\MediaBrowser.Common.csproj" /> - </ItemGroup> - - <PropertyGroup> - <TargetFramework>netstandard2.0</TargetFramework> - <GenerateAssemblyInfo>false</GenerateAssemblyInfo> - <GenerateDocumentationFile>true</GenerateDocumentationFile> - <TreatWarningsAsErrors>true</TreatWarningsAsErrors> - </PropertyGroup> - - <!-- Code analysers--> - <ItemGroup Condition=" '$(Configuration)' == 'Debug' "> - <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.4" /> - <PackageReference Include="StyleCop.Analyzers" Version="1.1.118" /> - <PackageReference Include="SerilogAnalyzer" Version="0.15.0" /> - </ItemGroup> - - <PropertyGroup Condition=" '$(Configuration)' == 'Debug' "> - <CodeAnalysisRuleSet>../../jellyfin.ruleset</CodeAnalysisRuleSet> - </PropertyGroup> - -</Project> diff --git a/Emby.IsoMounting/IsoMounter/LinuxIsoManager.cs b/Emby.IsoMounting/IsoMounter/LinuxIsoManager.cs deleted file mode 100644 index 48cb2e1d5..000000000 --- a/Emby.IsoMounting/IsoMounter/LinuxIsoManager.cs +++ /dev/null @@ -1,298 +0,0 @@ -using System; -using System.Diagnostics; -using System.Globalization; -using System.IO; -using System.Runtime.InteropServices; -using System.Threading; -using System.Threading.Tasks; -using MediaBrowser.Model.IO; -using MediaBrowser.Model.System; -using Microsoft.Extensions.Logging; -using OperatingSystem = MediaBrowser.Common.System.OperatingSystem; - -namespace IsoMounter -{ - /// <summary> - /// The ISO manager implementation for Linux. - /// </summary> - public class LinuxIsoManager : IIsoMounter - { - private const string MountCommand = "mount"; - private const string UnmountCommand = "umount"; - private const string SudoCommand = "sudo"; - - private readonly ILogger _logger; - private readonly string _mountPointRoot; - - /// <summary> - /// Initializes a new instance of the <see cref="LinuxIsoManager" /> class. - /// </summary> - /// <param name="logger">The logger.</param> - public LinuxIsoManager(ILogger logger) - { - _logger = logger; - - _mountPointRoot = Path.DirectorySeparatorChar + "tmp" + Path.DirectorySeparatorChar + "Emby"; - - _logger.LogDebug( - "[{0}] System PATH is currently set to [{1}].", - Name, - Environment.GetEnvironmentVariable("PATH") ?? string.Empty); - - _logger.LogDebug( - "[{0}] System path separator is [{1}].", - Name, - Path.PathSeparator); - - _logger.LogDebug( - "[{0}] Mount point root is [{1}].", - Name, - _mountPointRoot); - } - - /// <inheritdoc /> - public string Name => "LinuxMount"; - -#pragma warning disable SA1300 -#pragma warning disable SA1400 - [DllImport("libc", SetLastError = true)] - static extern uint getuid(); - -#pragma warning restore SA1300 -#pragma warning restore SA1400 - - /// <inheritdoc /> - public bool CanMount(string path) - { - if (OperatingSystem.Id != OperatingSystemId.Linux) - { - return false; - } - - _logger.LogInformation( - "[{0}] Checking we can attempt to mount [{1}], Extension = [{2}], Operating System = [{3}].", - Name, - path, - Path.GetExtension(path), - OperatingSystem.Name); - - return string.Equals(Path.GetExtension(path), ".iso", StringComparison.OrdinalIgnoreCase); - } - - /// <inheritdoc /> - public Task<IIsoMount> Mount(string isoPath, CancellationToken cancellationToken) - { - string cmdArguments; - string cmdFilename; - string mountPoint = Path.Combine(_mountPointRoot, Guid.NewGuid().ToString()); - - if (string.IsNullOrEmpty(isoPath)) - { - throw new ArgumentNullException(nameof(isoPath)); - } - - _logger.LogInformation( - "[{Name}] Attempting to mount [{Path}].", - Name, - isoPath); - - _logger.LogDebug( - "[{Name}] ISO will be mounted at [{Path}].", - Name, - mountPoint); - - try - { - Directory.CreateDirectory(mountPoint); - } - catch (UnauthorizedAccessException ex) - { - throw new IOException("Unable to create mount point(Permission denied) for " + isoPath, ex); - } - catch (Exception ex) - { - throw new IOException("Unable to create mount point for " + isoPath, ex); - } - - if (GetUID() == 0) - { - cmdFilename = MountCommand; - cmdArguments = string.Format( - CultureInfo.InvariantCulture, - "\"{0}\" \"{1}\"", - isoPath, - mountPoint); - } - else - { - cmdFilename = SudoCommand; - cmdArguments = string.Format( - CultureInfo.InvariantCulture, - "\"{0}\" \"{1}\" \"{2}\"", - MountCommand, - isoPath, - mountPoint); - } - - _logger.LogDebug( - "[{0}] Mount command [{1}], mount arguments [{2}].", - Name, - cmdFilename, - cmdArguments); - - int exitcode = ExecuteCommand(cmdFilename, cmdArguments); - if (exitcode == 0) - { - _logger.LogInformation( - "[{0}] ISO mount completed successfully.", - Name); - - return Task.FromResult<IIsoMount>(new LinuxMount(this, isoPath, mountPoint)); - } - - _logger.LogInformation( - "[{0}] ISO mount completed with errors.", - Name); - - try - { - Directory.Delete(mountPoint, false); - } - catch (Exception ex) - { - _logger.LogError(ex, "[{Name}] Unhandled exception removing mount point.", Name); - throw; - } - - throw new ExternalException("Mount command failed", exitcode); - } - - private uint GetUID() - { - var uid = getuid(); - - _logger.LogDebug( - "[{0}] GetUserId() returned [{2}].", - Name, - uid); - - return uid; - } - - private int ExecuteCommand(string cmdFilename, string cmdArguments) - { - var startInfo = new ProcessStartInfo - { - FileName = cmdFilename, - Arguments = cmdArguments, - UseShellExecute = false, - CreateNoWindow = true, - ErrorDialog = false, - RedirectStandardOutput = true, - RedirectStandardError = true - }; - - var process = new Process() - { - StartInfo = startInfo - }; - - try - { - process.Start(); - - _logger.LogDebug( - "[{Name}] Standard output from process is [{Error}].", - Name, - process.StandardOutput.ReadToEnd()); - - _logger.LogDebug( - "[{Name}] Standard error from process is [{Error}].", - Name, - process.StandardError.ReadToEnd()); - - return process.ExitCode; - } - catch (Exception ex) - { - _logger.LogDebug(ex, "[{Name}] Unhandled exception executing command.", Name); - throw; - } - finally - { - process?.Dispose(); - } - } - - /// <summary> - /// Unmounts the specified mount. - /// </summary> - /// <param name="mount">The mount.</param> - internal void OnUnmount(LinuxMount mount) - { - if (mount == null) - { - throw new ArgumentNullException(nameof(mount)); - } - - _logger.LogInformation( - "[{0}] Attempting to unmount ISO [{1}] mounted on [{2}].", - Name, - mount.IsoPath, - mount.MountedPath); - - string cmdArguments; - string cmdFilename; - - if (GetUID() == 0) - { - cmdFilename = UnmountCommand; - cmdArguments = string.Format( - CultureInfo.InvariantCulture, - "\"{0}\"", - mount.MountedPath); - } - else - { - cmdFilename = SudoCommand; - cmdArguments = string.Format( - CultureInfo.InvariantCulture, - "\"{0}\" \"{1}\"", - UnmountCommand, - mount.MountedPath); - } - - _logger.LogDebug( - "[{0}] Umount command [{1}], umount arguments [{2}].", - Name, - cmdFilename, - cmdArguments); - - int exitcode = ExecuteCommand(cmdFilename, cmdArguments); - if (exitcode == 0) - { - _logger.LogInformation( - "[{0}] ISO unmount completed successfully.", - Name); - } - else - { - _logger.LogInformation( - "[{0}] ISO unmount completed with errors.", - Name); - } - - try - { - Directory.Delete(mount.MountedPath, false); - } - catch (Exception ex) - { - _logger.LogError(ex, "[{Name}] Unhandled exception removing mount point.", Name); - throw; - } - - throw new ExternalException("Mount command failed", exitcode); - } - } -} diff --git a/Emby.IsoMounting/IsoMounter/LinuxMount.cs b/Emby.IsoMounting/IsoMounter/LinuxMount.cs deleted file mode 100644 index ccad8ce20..000000000 --- a/Emby.IsoMounting/IsoMounter/LinuxMount.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System; -using MediaBrowser.Model.IO; - -namespace IsoMounter -{ - /// <summary> - /// Class LinuxMount. - /// </summary> - internal class LinuxMount : IIsoMount - { - private readonly LinuxIsoManager _linuxIsoManager; - - private bool _disposed = false; - - /// <summary> - /// Initializes a new instance of the <see cref="LinuxMount" /> class. - /// </summary> - /// <param name="isoManager">The ISO manager that mounted this ISO file.</param> - /// <param name="isoPath">The path to the ISO file.</param> - /// <param name="mountFolder">The folder the ISO is mounted in.</param> - internal LinuxMount(LinuxIsoManager isoManager, string isoPath, string mountFolder) - { - _linuxIsoManager = isoManager; - - IsoPath = isoPath; - MountedPath = mountFolder; - } - - /// <inheritdoc /> - public string IsoPath { get; } - - /// <inheritdoc /> - public string MountedPath { get; } - - /// <inheritdoc /> - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// <summary> - /// Releases the unmanaged resources and disposes of the managed resources used. - /// </summary> - /// <param name="disposing">Whether or not the managed resources should be disposed.</param> - protected virtual void Dispose(bool disposing) - { - if (_disposed) - { - return; - } - - _linuxIsoManager.OnUnmount(this); - - _disposed = true; - } - } -} diff --git a/Emby.IsoMounting/IsoMounter/Plugin.cs b/Emby.IsoMounting/IsoMounter/Plugin.cs deleted file mode 100644 index 433294d74..000000000 --- a/Emby.IsoMounting/IsoMounter/Plugin.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using IsoMounter.Configuration; -using MediaBrowser.Common.Configuration; -using MediaBrowser.Common.Plugins; -using MediaBrowser.Model.Serialization; - -namespace IsoMounter -{ - /// <summary> - /// The LinuxMount plugin class. - /// </summary> - public class Plugin : BasePlugin<PluginConfiguration> - { - /// <summary> - /// Initializes a new instance of the <see cref="Plugin" /> class. - /// </summary> - /// <param name="applicationPaths">The application paths.</param> - /// <param name="xmlSerializer">The XML serializer.</param> - public Plugin(IApplicationPaths applicationPaths, IXmlSerializer xmlSerializer) - : base(applicationPaths, xmlSerializer) - { - } - - /// <inheritdoc /> - public override Guid Id { get; } = new Guid("4682DD4C-A675-4F1B-8E7C-79ADF137A8F8"); - - /// <inheritdoc /> - public override string Name => "Iso Mounter"; - - /// <inheritdoc /> - public override string Description => "Mount and stream ISO contents"; - } -} diff --git a/Emby.IsoMounting/IsoMounter/Properties/AssemblyInfo.cs b/Emby.IsoMounting/IsoMounter/Properties/AssemblyInfo.cs deleted file mode 100644 index 5956fc3b3..000000000 --- a/Emby.IsoMounting/IsoMounter/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Reflection; -using System.Resources; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("IsoMounter")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Jellyfin Project")] -[assembly: AssemblyProduct("Jellyfin Server")] -[assembly: AssemblyCopyright("Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] -[assembly: NeutralResourcesLanguage("en")] - -// 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 -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] diff --git a/Emby.IsoMounting/LICENSE b/Emby.IsoMounting/LICENSE deleted file mode 100644 index d7f105139..000000000 --- a/Emby.IsoMounting/LICENSE +++ /dev/null @@ -1,339 +0,0 @@ -GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc., <http://fsf.org/> - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Lesser General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - {description} - Copyright (C) {year} {fullname} - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - {signature of Ty Coon}, 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. diff --git a/Emby.IsoMounting/README.md b/Emby.IsoMounting/README.md deleted file mode 100644 index 78bab9936..000000000 --- a/Emby.IsoMounting/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# MediaBrowser.IsoMounting.Linux -This implements two core interfaces, IIsoManager, and IIsoMount. -### IIsoManager -The manager class can be used to create a mount, and also determine if the mounter is capable of mounting a given file. -### IIsoMount -IIsoMount then represents a mount instance, which will be unmounted on disposal. -*** -This Linux version use sudo, mount and umount. - -You need to add this to your sudo file via visudo(change the username): - - Defaults:jsmith !requiretty - jsmith ALL=(root) NOPASSWD: /bin/mount - jsmith ALL=(root) NOPASSWD: /bin/umount diff --git a/Emby.Notifications/Notifications.cs b/Emby.Notifications/Notifications.cs index ec08fd193..7aa1e7ae8 100644 --- a/Emby.Notifications/Notifications.cs +++ b/Emby.Notifications/Notifications.cs @@ -209,47 +209,48 @@ namespace Emby.Notifications public static string GetItemName(BaseItem item) { var name = item.Name; - var episode = item as Episode; - if (episode != null) + if (item is Episode episode) { if (episode.IndexNumber.HasValue) { - name = string.Format("Ep{0} - {1}", episode.IndexNumber.Value.ToString(CultureInfo.InvariantCulture), name); + name = string.Format( + CultureInfo.InvariantCulture, + "Ep{0} - {1}", + episode.IndexNumber.Value, + name); } if (episode.ParentIndexNumber.HasValue) { - name = string.Format("S{0}, {1}", episode.ParentIndexNumber.Value.ToString(CultureInfo.InvariantCulture), name); + name = string.Format( + CultureInfo.InvariantCulture, + "S{0}, {1}", + episode.ParentIndexNumber.Value, + name); } } - var hasSeries = item as IHasSeries; - if (hasSeries != null) + if (item is IHasSeries hasSeries) { name = hasSeries.SeriesName + " - " + name; } - var hasAlbumArtist = item as IHasAlbumArtist; - if (hasAlbumArtist != null) + if (item is IHasAlbumArtist hasAlbumArtist) { var artists = hasAlbumArtist.AlbumArtists; - if (artists.Length > 0) + if (artists.Count > 0) { name = artists[0] + " - " + name; } } - else + else if (item is IHasArtist hasArtist) { - var hasArtist = item as IHasArtist; - if (hasArtist != null) - { - var artists = hasArtist.Artists; + var artists = hasArtist.Artists; - if (artists.Length > 0) - { - name = artists[0] + " - " + name; - } + if (artists.Count > 0) + { + name = artists[0] + " - " + name; } } diff --git a/Emby.Photos/PhotoProvider.cs b/Emby.Photos/PhotoProvider.cs index 99a635e60..1591609ab 100644 --- a/Emby.Photos/PhotoProvider.cs +++ b/Emby.Photos/PhotoProvider.cs @@ -170,8 +170,8 @@ namespace Emby.Photos } } - const ItemUpdateType result = ItemUpdateType.ImageUpdate | ItemUpdateType.MetadataImport; - return Task.FromResult(result); + const ItemUpdateType Result = ItemUpdateType.ImageUpdate | ItemUpdateType.MetadataImport; + return Task.FromResult(Result); } } } diff --git a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs index fb4ffd74b..1514402d6 100644 --- a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs +++ b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs @@ -96,7 +96,10 @@ namespace Emby.Server.Implementations.Activity { CreateLogEntry(new ActivityLogEntry { - Name = string.Format(_localization.GetLocalizedString("CameraImageUploadedFrom"), e.Argument.Device.Name), + Name = string.Format( + CultureInfo.InvariantCulture, + _localization.GetLocalizedString("CameraImageUploadedFrom"), + e.Argument.Device.Name), Type = NotificationType.CameraImageUploaded.ToString() }); } @@ -105,7 +108,10 @@ namespace Emby.Server.Implementations.Activity { CreateLogEntry(new ActivityLogEntry { - Name = string.Format(_localization.GetLocalizedString("UserLockedOutWithName"), e.Argument.Name), + Name = string.Format( + CultureInfo.InvariantCulture, + _localization.GetLocalizedString("UserLockedOutWithName"), + e.Argument.Name), Type = NotificationType.UserLockedOut.ToString(), UserId = e.Argument.Id }); @@ -115,7 +121,11 @@ namespace Emby.Server.Implementations.Activity { CreateLogEntry(new ActivityLogEntry { - Name = string.Format(_localization.GetLocalizedString("SubtitleDownloadFailureFromForItem"), e.Provider, Notifications.Notifications.GetItemName(e.Item)), + Name = string.Format( + CultureInfo.InvariantCulture, + _localization.GetLocalizedString("SubtitleDownloadFailureFromForItem"), + e.Provider, + Notifications.Notifications.GetItemName(e.Item)), Type = "SubtitleDownloadFailure", ItemId = e.Item.Id.ToString("N", CultureInfo.InvariantCulture), ShortOverview = e.Exception.Message @@ -178,7 +188,12 @@ namespace Emby.Server.Implementations.Activity CreateLogEntry(new ActivityLogEntry { - Name = string.Format(_localization.GetLocalizedString("UserStartedPlayingItemWithValues"), user.Name, GetItemName(item), e.DeviceName), + Name = string.Format( + CultureInfo.InvariantCulture, + _localization.GetLocalizedString("UserStartedPlayingItemWithValues"), + user.Name, + GetItemName(item), + e.DeviceName), Type = GetPlaybackNotificationType(item.MediaType), UserId = user.Id }); @@ -193,7 +208,7 @@ namespace Emby.Server.Implementations.Activity name = item.SeriesName + " - " + name; } - if (item.Artists != null && item.Artists.Length > 0) + if (item.Artists != null && item.Artists.Count > 0) { name = item.Artists[0] + " - " + name; } @@ -238,21 +253,31 @@ namespace Emby.Server.Implementations.Activity if (string.IsNullOrEmpty(session.UserName)) { - name = string.Format(_localization.GetLocalizedString("DeviceOfflineWithName"), session.DeviceName); + name = string.Format( + CultureInfo.InvariantCulture, + _localization.GetLocalizedString("DeviceOfflineWithName"), + session.DeviceName); // Causing too much spam for now return; } else { - name = string.Format(_localization.GetLocalizedString("UserOfflineFromDevice"), session.UserName, session.DeviceName); + name = string.Format( + CultureInfo.InvariantCulture, + _localization.GetLocalizedString("UserOfflineFromDevice"), + session.UserName, + session.DeviceName); } CreateLogEntry(new ActivityLogEntry { Name = name, Type = "SessionEnded", - ShortOverview = string.Format(_localization.GetLocalizedString("LabelIpAddressValue"), session.RemoteEndPoint), + ShortOverview = string.Format( + CultureInfo.InvariantCulture, + _localization.GetLocalizedString("LabelIpAddressValue"), + session.RemoteEndPoint), UserId = session.UserId }); } @@ -263,9 +288,15 @@ namespace Emby.Server.Implementations.Activity CreateLogEntry(new ActivityLogEntry { - Name = string.Format(_localization.GetLocalizedString("AuthenticationSucceededWithUserName"), user.Name), + Name = string.Format( + CultureInfo.InvariantCulture, + _localization.GetLocalizedString("AuthenticationSucceededWithUserName"), + user.Name), Type = "AuthenticationSucceeded", - ShortOverview = string.Format(_localization.GetLocalizedString("LabelIpAddressValue"), e.Argument.SessionInfo.RemoteEndPoint), + ShortOverview = string.Format( + CultureInfo.InvariantCulture, + _localization.GetLocalizedString("LabelIpAddressValue"), + e.Argument.SessionInfo.RemoteEndPoint), UserId = user.Id }); } @@ -274,9 +305,15 @@ namespace Emby.Server.Implementations.Activity { CreateLogEntry(new ActivityLogEntry { - Name = string.Format(_localization.GetLocalizedString("FailedLoginAttemptWithUserName"), e.Argument.Username), + Name = string.Format( + CultureInfo.InvariantCulture, + _localization.GetLocalizedString("FailedLoginAttemptWithUserName"), + e.Argument.Username), Type = "AuthenticationFailed", - ShortOverview = string.Format(_localization.GetLocalizedString("LabelIpAddressValue"), e.Argument.RemoteEndPoint), + ShortOverview = string.Format( + CultureInfo.InvariantCulture, + _localization.GetLocalizedString("LabelIpAddressValue"), + e.Argument.RemoteEndPoint), Severity = LogLevel.Error }); } @@ -285,7 +322,10 @@ namespace Emby.Server.Implementations.Activity { CreateLogEntry(new ActivityLogEntry { - Name = string.Format(_localization.GetLocalizedString("UserPolicyUpdatedWithName"), e.Argument.Name), + Name = string.Format( + CultureInfo.InvariantCulture, + _localization.GetLocalizedString("UserPolicyUpdatedWithName"), + e.Argument.Name), Type = "UserPolicyUpdated", UserId = e.Argument.Id }); @@ -295,7 +335,10 @@ namespace Emby.Server.Implementations.Activity { CreateLogEntry(new ActivityLogEntry { - Name = string.Format(_localization.GetLocalizedString("UserDeletedWithName"), e.Argument.Name), + Name = string.Format( + CultureInfo.InvariantCulture, + _localization.GetLocalizedString("UserDeletedWithName"), + e.Argument.Name), Type = "UserDeleted" }); } @@ -304,7 +347,10 @@ namespace Emby.Server.Implementations.Activity { CreateLogEntry(new ActivityLogEntry { - Name = string.Format(_localization.GetLocalizedString("UserPasswordChangedWithName"), e.Argument.Name), + Name = string.Format( + CultureInfo.InvariantCulture, + _localization.GetLocalizedString("UserPasswordChangedWithName"), + e.Argument.Name), Type = "UserPasswordChanged", UserId = e.Argument.Id }); @@ -314,7 +360,10 @@ namespace Emby.Server.Implementations.Activity { CreateLogEntry(new ActivityLogEntry { - Name = string.Format(_localization.GetLocalizedString("UserCreatedWithName"), e.Argument.Name), + Name = string.Format( + CultureInfo.InvariantCulture, + _localization.GetLocalizedString("UserCreatedWithName"), + e.Argument.Name), Type = "UserCreated", UserId = e.Argument.Id }); @@ -327,21 +376,31 @@ namespace Emby.Server.Implementations.Activity if (string.IsNullOrEmpty(session.UserName)) { - name = string.Format(_localization.GetLocalizedString("DeviceOnlineWithName"), session.DeviceName); + name = string.Format( + CultureInfo.InvariantCulture, + _localization.GetLocalizedString("DeviceOnlineWithName"), + session.DeviceName); // Causing too much spam for now return; } else { - name = string.Format(_localization.GetLocalizedString("UserOnlineFromDevice"), session.UserName, session.DeviceName); + name = string.Format( + CultureInfo.InvariantCulture, + _localization.GetLocalizedString("UserOnlineFromDevice"), + session.UserName, + session.DeviceName); } CreateLogEntry(new ActivityLogEntry { Name = name, Type = "SessionStarted", - ShortOverview = string.Format(_localization.GetLocalizedString("LabelIpAddressValue"), session.RemoteEndPoint), + ShortOverview = string.Format( + CultureInfo.InvariantCulture, + _localization.GetLocalizedString("LabelIpAddressValue"), + session.RemoteEndPoint), UserId = session.UserId }); } @@ -350,9 +409,15 @@ namespace Emby.Server.Implementations.Activity { CreateLogEntry(new ActivityLogEntry { - Name = string.Format(_localization.GetLocalizedString("PluginUpdatedWithName"), e.Argument.Item1.Name), + Name = string.Format( + CultureInfo.InvariantCulture, + _localization.GetLocalizedString("PluginUpdatedWithName"), + e.Argument.Item1.Name), Type = NotificationType.PluginUpdateInstalled.ToString(), - ShortOverview = string.Format(_localization.GetLocalizedString("VersionNumber"), e.Argument.Item2.versionStr), + ShortOverview = string.Format( + CultureInfo.InvariantCulture, + _localization.GetLocalizedString("VersionNumber"), + e.Argument.Item2.versionStr), Overview = e.Argument.Item2.description }); } @@ -361,7 +426,10 @@ namespace Emby.Server.Implementations.Activity { CreateLogEntry(new ActivityLogEntry { - Name = string.Format(_localization.GetLocalizedString("PluginUninstalledWithName"), e.Argument.Name), + Name = string.Format( + CultureInfo.InvariantCulture, + _localization.GetLocalizedString("PluginUninstalledWithName"), + e.Argument.Name), Type = NotificationType.PluginUninstalled.ToString() }); } @@ -370,9 +438,15 @@ namespace Emby.Server.Implementations.Activity { CreateLogEntry(new ActivityLogEntry { - Name = string.Format(_localization.GetLocalizedString("PluginInstalledWithName"), e.Argument.name), + Name = string.Format( + CultureInfo.InvariantCulture, + _localization.GetLocalizedString("PluginInstalledWithName"), + e.Argument.name), Type = NotificationType.PluginInstalled.ToString(), - ShortOverview = string.Format(_localization.GetLocalizedString("VersionNumber"), e.Argument.versionStr) + ShortOverview = string.Format( + CultureInfo.InvariantCulture, + _localization.GetLocalizedString("VersionNumber"), + e.Argument.versionStr) }); } @@ -382,9 +456,15 @@ namespace Emby.Server.Implementations.Activity CreateLogEntry(new ActivityLogEntry { - Name = string.Format(_localization.GetLocalizedString("NameInstallFailed"), installationInfo.Name), + Name = string.Format( + CultureInfo.InvariantCulture, + _localization.GetLocalizedString("NameInstallFailed"), + installationInfo.Name), Type = NotificationType.InstallationFailed.ToString(), - ShortOverview = string.Format(_localization.GetLocalizedString("VersionNumber"), installationInfo.Version), + ShortOverview = string.Format( + CultureInfo.InvariantCulture, + _localization.GetLocalizedString("VersionNumber"), + installationInfo.Version), Overview = e.Exception.Message }); } @@ -401,7 +481,10 @@ namespace Emby.Server.Implementations.Activity } var time = result.EndTimeUtc - result.StartTimeUtc; - var runningTime = string.Format(_localization.GetLocalizedString("LabelRunningTimeValue"), ToUserFriendlyString(time)); + var runningTime = string.Format( + CultureInfo.InvariantCulture, + _localization.GetLocalizedString("LabelRunningTimeValue"), + ToUserFriendlyString(time)); if (result.Status == TaskCompletionStatus.Failed) { @@ -419,7 +502,10 @@ namespace Emby.Server.Implementations.Activity CreateLogEntry(new ActivityLogEntry { - Name = string.Format(_localization.GetLocalizedString("ScheduledTaskFailedWithName"), task.Name), + Name = string.Format( + CultureInfo.InvariantCulture, + _localization.GetLocalizedString("ScheduledTaskFailedWithName"), + task.Name), Type = NotificationType.TaskFailed.ToString(), Overview = string.Join(Environment.NewLine, vals), ShortOverview = runningTime, @@ -534,8 +620,11 @@ namespace Emby.Server.Implementations.Activity /// <param name="description">The name of this item (singular form)</param> private static string CreateValueString(int value, string description) { - return string.Format("{0:#,##0} {1}", - value, value == 1 ? description : string.Format("{0}s", description)); + return string.Format( + CultureInfo.InvariantCulture, + "{0:#,##0} {1}", + value, + value == 1 ? description : string.Format(CultureInfo.InvariantCulture, "{0}s", description)); } } } diff --git a/Emby.Server.Implementations/Activity/ActivityRepository.cs b/Emby.Server.Implementations/Activity/ActivityRepository.cs index 541b23afd..ffaeaa541 100644 --- a/Emby.Server.Implementations/Activity/ActivityRepository.cs +++ b/Emby.Server.Implementations/Activity/ActivityRepository.cs @@ -247,7 +247,7 @@ namespace Emby.Server.Implementations.Activity ReadTransactionMode); } - result.Items = list.ToArray(); + result.Items = list; return result; } diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 6ab3d1bb1..24f59478c 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -202,10 +202,10 @@ namespace Emby.Server.Implementations /// Gets or sets all concrete types. /// </summary> /// <value>All concrete types.</value> - public Type[] AllConcreteTypes { get; protected set; } + private Type[] _allConcreteTypes; /// <summary> - /// The disposable parts + /// The disposable parts. /// </summary> private readonly List<IDisposable> _disposableParts = new List<IDisposable>(); @@ -499,7 +499,7 @@ namespace Emby.Server.Implementations { var currentType = typeof(T); - return AllConcreteTypes.Where(i => currentType.IsAssignableFrom(i)); + return _allConcreteTypes.Where(i => currentType.IsAssignableFrom(i)); } /// <inheritdoc /> @@ -1010,9 +1010,11 @@ namespace Emby.Server.Implementations .Select(x => Assembly.LoadFrom(x)) .SelectMany(x => x.ExportedTypes) .Where(x => x.IsClass && !x.IsAbstract && !x.IsInterface && !x.IsGenericType) - .ToList(); + .ToArray(); - types.AddRange(types); + int oldLen = _allConcreteTypes.Length; + Array.Resize(ref _allConcreteTypes, oldLen + types.Length); + types.CopyTo(_allConcreteTypes, oldLen); var plugins = types.Where(x => x.IsAssignableFrom(typeof(IPlugin))) .Select(CreateInstanceSafe) @@ -1022,8 +1024,8 @@ namespace Emby.Server.Implementations .Where(x => x != null) .ToArray(); - int oldLen = _plugins.Length; - Array.Resize<IPlugin>(ref _plugins, _plugins.Length + plugins.Length); + oldLen = _plugins.Length; + Array.Resize(ref _plugins, oldLen + plugins.Length); plugins.CopyTo(_plugins, oldLen); var entries = types.Where(x => x.IsAssignableFrom(typeof(IServerEntryPoint))) @@ -1140,7 +1142,7 @@ namespace Emby.Server.Implementations { Logger.LogInformation("Loading assemblies"); - AllConcreteTypes = GetTypes(GetComposablePartAssemblies()).ToArray(); + _allConcreteTypes = GetTypes(GetComposablePartAssemblies()).ToArray(); } private IEnumerable<Type> GetTypes(IEnumerable<Assembly> assemblies) diff --git a/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs b/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs index c4fa68cac..c7f92b80b 100644 --- a/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs +++ b/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs @@ -66,7 +66,7 @@ namespace Emby.Server.Implementations.Configuration { base.AddParts(factories); - UpdateTranscodingTempPath(); + UpdateTranscodePath(); } /// <summary> @@ -87,13 +87,13 @@ namespace Emby.Server.Implementations.Configuration /// <summary> /// Updates the transcoding temporary path. /// </summary> - private void UpdateTranscodingTempPath() + private void UpdateTranscodePath() { var encodingConfig = this.GetConfiguration<EncodingOptions>("encoding"); ((ServerApplicationPaths)ApplicationPaths).TranscodingTempPath = string.IsNullOrEmpty(encodingConfig.TranscodingTempPath) ? null : - Path.Combine(encodingConfig.TranscodingTempPath, "transcoding-temp"); + Path.Combine(encodingConfig.TranscodingTempPath, "transcodes"); } protected override void OnNamedConfigurationUpdated(string key, object configuration) @@ -102,7 +102,7 @@ namespace Emby.Server.Implementations.Configuration if (string.Equals(key, "encoding", StringComparison.OrdinalIgnoreCase)) { - UpdateTranscodingTempPath(); + UpdateTranscodePath(); } } diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 9d983307f..2f083dda4 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -979,7 +979,7 @@ namespace Emby.Server.Implementations.Data } string artists = null; - if (item is IHasArtist hasArtists && hasArtists.Artists.Length > 0) + if (item is IHasArtist hasArtists && hasArtists.Artists.Count > 0) { artists = string.Join("|", hasArtists.Artists); } @@ -987,7 +987,7 @@ namespace Emby.Server.Implementations.Data string albumArtists = null; if (item is IHasAlbumArtist hasAlbumArtists - && hasAlbumArtists.AlbumArtists.Length > 0) + && hasAlbumArtists.AlbumArtists.Count > 0) { albumArtists = string.Join("|", hasAlbumArtists.AlbumArtists); } @@ -2746,7 +2746,7 @@ namespace Emby.Server.Implementations.Data var returnList = GetItemList(query); return new QueryResult<BaseItem> { - Items = returnList.ToArray(), + Items = returnList, TotalRecordCount = returnList.Count }; } @@ -2883,7 +2883,7 @@ namespace Emby.Server.Implementations.Data } LogQueryTime("GetItems", commandText, now); - result.Items = list.ToArray(); + result.Items = list; return result; } @@ -3161,7 +3161,7 @@ namespace Emby.Server.Implementations.Data var returnList = GetItemIdsList(query); return new QueryResult<Guid> { - Items = returnList.ToArray(), + Items = returnList, TotalRecordCount = returnList.Count }; } @@ -3281,7 +3281,7 @@ namespace Emby.Server.Implementations.Data LogQueryTime("GetItemIds", commandText, now); - result.Items = list.ToArray(); + result.Items = list; return result; } @@ -5520,7 +5520,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type result.TotalRecordCount = list.Count; } - result.Items = list.ToArray(); + result.Items = list; return result; } @@ -5731,7 +5731,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type { if (isSubsequentRow) { - insertText.Append(","); + insertText.Append(','); } insertText.AppendFormat("(@ItemId, @Name{0}, @Role{0}, @PersonType{0}, @SortOrder{0}, @ListOrder{0})", i.ToString(CultureInfo.InvariantCulture)); diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs index 1a7f10634..75192a8f1 100644 --- a/Emby.Server.Implementations/Dto/DtoService.cs +++ b/Emby.Server.Implementations/Dto/DtoService.cs @@ -80,27 +80,25 @@ namespace Emby.Server.Implementations.Dto return GetBaseItemDto(item, options, user, owner); } - public BaseItemDto[] GetBaseItemDtos(IReadOnlyList<BaseItem> items, DtoOptions options, User user = null, BaseItem owner = null) - => GetBaseItemDtos(items, items.Count, options, user, owner); - - public BaseItemDto[] GetBaseItemDtos(IEnumerable<BaseItem> items, int itemCount, DtoOptions options, User user = null, BaseItem owner = null) + /// <inheritdoc /> + public IReadOnlyList<BaseItemDto> GetBaseItemDtos(IReadOnlyList<BaseItem> items, DtoOptions options, User user = null, BaseItem owner = null) { - var returnItems = new BaseItemDto[itemCount]; - var programTuples = new List<Tuple<BaseItem, BaseItemDto>>(); - var channelTuples = new List<Tuple<BaseItemDto, LiveTvChannel>>(); + var returnItems = new BaseItemDto[items.Count]; + var programTuples = new List<(BaseItem, BaseItemDto)>(); + var channelTuples = new List<(BaseItemDto, LiveTvChannel)>(); - var index = 0; - foreach (var item in items) + for (int index = 0; index < items.Count; index++) { + var item = items[index]; var dto = GetBaseItemDtoInternal(item, options, user, owner); if (item is LiveTvChannel tvChannel) { - channelTuples.Add(new Tuple<BaseItemDto, LiveTvChannel>(dto, tvChannel)); + channelTuples.Add((dto, tvChannel)); } else if (item is LiveTvProgram) { - programTuples.Add(new Tuple<BaseItem, BaseItemDto>(item, dto)); + programTuples.Add((item, dto)); } if (item is IItemByName byName) @@ -121,7 +119,6 @@ namespace Emby.Server.Implementations.Dto } returnItems[index] = dto; - index++; } if (programTuples.Count > 0) @@ -140,33 +137,32 @@ namespace Emby.Server.Implementations.Dto public BaseItemDto GetBaseItemDto(BaseItem item, DtoOptions options, User user = null, BaseItem owner = null) { var dto = GetBaseItemDtoInternal(item, options, user, owner); - var tvChannel = item as LiveTvChannel; - if (tvChannel != null) + if (item is LiveTvChannel tvChannel) { - var list = new List<Tuple<BaseItemDto, LiveTvChannel>> { new Tuple<BaseItemDto, LiveTvChannel>(dto, tvChannel) }; + var list = new List<(BaseItemDto, LiveTvChannel)>(1) { (dto, tvChannel) }; _livetvManager().AddChannelInfo(list, options, user); } else if (item is LiveTvProgram) { - var list = new List<Tuple<BaseItem, BaseItemDto>> { new Tuple<BaseItem, BaseItemDto>(item, dto) }; + var list = new List<(BaseItem, BaseItemDto)>(1) { (item, dto) }; var task = _livetvManager().AddInfoToProgramDto(list, options.Fields, user); Task.WaitAll(task); } - var byName = item as IItemByName; - - if (byName != null) + if (item is IItemByName itemByName + && options.ContainsField(ItemFields.ItemCounts)) { - if (options.ContainsField(ItemFields.ItemCounts)) - { - SetItemByNameInfo(item, dto, GetTaggedItems(byName, user, new DtoOptions(false) - { - EnableImages = false - - }), user); - } - - return dto; + SetItemByNameInfo( + item, + dto, + GetTaggedItems( + itemByName, + user, + new DtoOptions(false) + { + EnableImages = false + }), + user); } return dto; @@ -174,12 +170,12 @@ namespace Emby.Server.Implementations.Dto private static IList<BaseItem> GetTaggedItems(IItemByName byName, User user, DtoOptions options) { - return byName.GetTaggedItems(new InternalItemsQuery(user) - { - Recursive = true, - DtoOptions = options - - }); + return byName.GetTaggedItems( + new InternalItemsQuery(user) + { + Recursive = true, + DtoOptions = options + }); } private BaseItemDto GetBaseItemDtoInternal(BaseItem item, DtoOptions options, User user = null, BaseItem owner = null) @@ -222,8 +218,7 @@ namespace Emby.Server.Implementations.Dto AttachUserSpecificInfo(dto, item, user, options); } - var hasMediaSources = item as IHasMediaSources; - if (hasMediaSources != null) + if (item is IHasMediaSources hasMediaSources) { if (options.ContainsField(ItemFields.MediaSources)) { @@ -769,14 +764,12 @@ namespace Emby.Server.Implementations.Dto dto.CriticRating = item.CriticRating; - var hasDisplayOrder = item as IHasDisplayOrder; - if (hasDisplayOrder != null) + if (item is IHasDisplayOrder hasDisplayOrder) { dto.DisplayOrder = hasDisplayOrder.DisplayOrder; } - var hasCollectionType = item as IHasCollectionType; - if (hasCollectionType != null) + if (item is IHasCollectionType hasCollectionType) { dto.CollectionType = hasCollectionType.CollectionType; } @@ -886,8 +879,7 @@ namespace Emby.Server.Implementations.Dto //} } - var hasArtist = item as IHasArtist; - if (hasArtist != null) + if (item is IHasArtist hasArtist) { dto.Artists = hasArtist.Artists; @@ -1074,17 +1066,24 @@ namespace Emby.Server.Implementations.Dto if (options.ContainsField(ItemFields.LocalTrailerCount)) { + int trailerCount = 0; if (allExtras == null) { allExtras = item.GetExtras().ToArray(); } - dto.LocalTrailerCount = allExtras.Count(i => i.ExtraType.HasValue && i.ExtraType.Value == ExtraType.Trailer); + trailerCount += allExtras.Count(i => i.ExtraType.HasValue && i.ExtraType.Value == ExtraType.Trailer); + + if (item is IHasTrailers hasTrailers) + { + trailerCount += hasTrailers.GetTrailerCount(); + } + + dto.LocalTrailerCount = trailerCount; } // Add EpisodeInfo - var episode = item as Episode; - if (episode != null) + if (item is Episode episode) { dto.IndexNumberEnd = episode.IndexNumberEnd; dto.SeriesName = episode.SeriesName; @@ -1102,7 +1101,7 @@ namespace Emby.Server.Implementations.Dto Series episodeSeries = null; - //if (options.ContainsField(ItemFields.SeriesPrimaryImage)) + if (options.ContainsField(ItemFields.SeriesPrimaryImage)) { episodeSeries = episodeSeries ?? episode.Series; if (episodeSeries != null) @@ -1122,8 +1121,7 @@ namespace Emby.Server.Implementations.Dto } // Add SeriesInfo - var series = item as Series; - if (series != null) + if (item is Series series) { dto.AirDays = series.AirDays; dto.AirTime = series.AirTime; @@ -1131,8 +1129,7 @@ namespace Emby.Server.Implementations.Dto } // Add SeasonInfo - var season = item as Season; - if (season != null) + if (item is Season season) { dto.SeriesName = season.SeriesName; dto.SeriesId = season.SeriesId; @@ -1148,7 +1145,7 @@ namespace Emby.Server.Implementations.Dto } } - //if (options.ContainsField(ItemFields.SeriesPrimaryImage)) + if (options.ContainsField(ItemFields.SeriesPrimaryImage)) { series = series ?? season.Series; if (series != null) @@ -1158,14 +1155,12 @@ namespace Emby.Server.Implementations.Dto } } - var musicVideo = item as MusicVideo; - if (musicVideo != null) + if (item is MusicVideo musicVideo) { SetMusicVideoProperties(dto, musicVideo); } - var book = item as Book; - if (book != null) + if (item is Book book) { SetBookProperties(dto, book); } @@ -1205,8 +1200,7 @@ namespace Emby.Server.Implementations.Dto } } - var photo = item as Photo; - if (photo != null) + if (item is Photo photo) { SetPhotoProperties(dto, photo); } @@ -1225,8 +1219,7 @@ namespace Emby.Server.Implementations.Dto private BaseItem GetImageDisplayParent(BaseItem currentItem, BaseItem originalItem) { - var musicAlbum = currentItem as MusicAlbum; - if (musicAlbum != null) + if (currentItem is MusicAlbum musicAlbum) { var artist = musicAlbum.GetMusicArtist(new DtoOptions(false)); if (artist != null) diff --git a/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs b/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs index 276312a30..457448604 100644 --- a/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs +++ b/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Net; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Security; @@ -89,7 +90,7 @@ namespace Emby.Server.Implementations.HttpServer.Security AccessToken = token }); - var tokenInfo = result.Items.Length > 0 ? result.Items[0] : null; + var tokenInfo = result.Items.Count > 0 ? result.Items[0] : null; if (tokenInfo != null) { @@ -190,17 +191,23 @@ namespace Emby.Server.Implementations.HttpServer.Security /// <returns>Dictionary{System.StringSystem.String}.</returns> private Dictionary<string, string> GetAuthorization(string authorizationHeader) { - if (authorizationHeader == null) return null; + if (authorizationHeader == null) + { + return null; + } var parts = authorizationHeader.Split(new[] { ' ' }, 2); // There should be at least to parts - if (parts.Length != 2) return null; + if (parts.Length != 2) + { + return null; + } var acceptedNames = new[] { "MediaBrowser", "Emby" }; // It has to be a digest request - if (!acceptedNames.Contains(parts[0] ?? string.Empty, StringComparer.OrdinalIgnoreCase)) + if (!acceptedNames.Contains(parts[0], StringComparer.OrdinalIgnoreCase)) { return null; } @@ -232,7 +239,7 @@ namespace Emby.Server.Implementations.HttpServer.Security return value; } - return System.Net.WebUtility.HtmlEncode(value); + return WebUtility.HtmlEncode(value); } } } diff --git a/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs b/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs index b07244fda..2282b8efb 100644 --- a/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs +++ b/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs @@ -37,7 +37,7 @@ namespace Emby.Server.Implementations.Library throw new ArgumentNullException(nameof(resolvedUser)); } - // As long as jellyfin supports passwordless users, we need this little block here to accomodate + // As long as jellyfin supports passwordless users, we need this little block here to accommodate if (!HasPassword(resolvedUser) && string.IsNullOrEmpty(password)) { return Task.FromResult(new ProviderAuthenticationResult @@ -105,6 +105,7 @@ namespace Emby.Server.Implementations.Library public Task ChangePassword(User user, string newPassword) { ConvertPasswordFormat(user); + // This is needed to support changing a no password user to a password user if (string.IsNullOrEmpty(user.Password)) { diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 30ff855cc..36934f65f 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -1441,7 +1441,7 @@ namespace Emby.Server.Implementations.Library return new QueryResult<BaseItem> { - Items = list.ToArray() + Items = list }; } @@ -1977,8 +1977,7 @@ namespace Emby.Server.Implementations.Library public LibraryOptions GetLibraryOptions(BaseItem item) { - var collectionFolder = item as CollectionFolder; - if (collectionFolder == null) + if (!(item is CollectionFolder collectionFolder)) { collectionFolder = GetCollectionFolders(item) .OfType<CollectionFolder>() diff --git a/Emby.Server.Implementations/Library/UserViewManager.cs b/Emby.Server.Implementations/Library/UserViewManager.cs index 71f16ac3e..4d79cae13 100644 --- a/Emby.Server.Implementations/Library/UserViewManager.cs +++ b/Emby.Server.Implementations/Library/UserViewManager.cs @@ -224,7 +224,7 @@ namespace Emby.Server.Implementations.Library return list; } - private List<BaseItem> GetItemsForLatestItems(User user, LatestItemsQuery request, DtoOptions options) + private IReadOnlyList<BaseItem> GetItemsForLatestItems(User user, LatestItemsQuery request, DtoOptions options) { var parentId = request.ParentId; @@ -236,24 +236,22 @@ namespace Emby.Server.Implementations.Library if (!parentId.Equals(Guid.Empty)) { var parentItem = _libraryManager.GetItemById(parentId); - var parentItemChannel = parentItem as Channel; - if (parentItemChannel != null) + if (parentItem is Channel parentItemChannel) { - return _channelManager.GetLatestChannelItemsInternal(new InternalItemsQuery(user) - { - ChannelIds = new[] { parentId }, - IsPlayed = request.IsPlayed, - StartIndex = request.StartIndex, - Limit = request.Limit, - IncludeItemTypes = request.IncludeItemTypes, - EnableTotalRecordCount = false - - - }, CancellationToken.None).Result.Items.ToList(); + return _channelManager.GetLatestChannelItemsInternal( + new InternalItemsQuery(user) + { + ChannelIds = new[] { parentId }, + IsPlayed = request.IsPlayed, + StartIndex = request.StartIndex, + Limit = request.Limit, + IncludeItemTypes = request.IncludeItemTypes, + EnableTotalRecordCount = false + }, + CancellationToken.None).Result.Items; } - var parent = parentItem as Folder; - if (parent != null) + if (parentItem is Folder parent) { parents.Add(parent); } diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs index 9a9bae215..3cc0541e7 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs @@ -87,8 +87,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV CreateNoWindow = true, UseShellExecute = false, - // Must consume both stdout and stderr or deadlocks may occur - //RedirectStandardOutput = true, RedirectStandardError = true, RedirectStandardInput = true, @@ -120,9 +118,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV cancellationToken.Register(Stop); - // MUST read both stdout and stderr asynchronously or a deadlock may occurr - //process.BeginOutputReadLine(); - onStarted(); // Important - don't await the log task or we won't be able to kill ffmpeg when the user stops playback @@ -138,11 +133,12 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV string videoArgs; if (EncodeVideo(mediaSource)) { - var maxBitrate = 25000000; + const int MaxBitrate = 25000000; videoArgs = string.Format( - "-codec:v:0 libx264 -force_key_frames \"expr:gte(t,n_forced*5)\" {0} -pix_fmt yuv420p -preset superfast -crf 23 -b:v {1} -maxrate {1} -bufsize ({1}*2) -vsync -1 -profile:v high -level 41", - GetOutputSizeParam(), - maxBitrate.ToString(CultureInfo.InvariantCulture)); + CultureInfo.InvariantCulture, + "-codec:v:0 libx264 -force_key_frames \"expr:gte(t,n_forced*5)\" {0} -pix_fmt yuv420p -preset superfast -crf 23 -b:v {1} -maxrate {1} -bufsize ({1}*2) -vsync -1 -profile:v high -level 41", + GetOutputSizeParam(), + MaxBitrate); } else { @@ -151,18 +147,17 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV videoArgs += " -fflags +genpts"; - var durationParam = " -t " + _mediaEncoder.GetTimeParameter(duration.Ticks); - durationParam = string.Empty; - var flags = new List<string>(); if (mediaSource.IgnoreDts) { flags.Add("+igndts"); } + if (mediaSource.IgnoreIndex) { flags.Add("+ignidx"); } + if (mediaSource.GenPtsInput) { flags.Add("+genpts"); @@ -172,11 +167,9 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV if (flags.Count > 0) { - inputModifier += " -fflags " + string.Join("", flags.ToArray()); + inputModifier += " -fflags " + string.Join(string.Empty, flags); } - var videoStream = mediaSource.VideoStream; - if (mediaSource.ReadAtNativeFramerate) { inputModifier += " -re"; @@ -200,13 +193,14 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV var outputParam = string.Empty; - var commandLineArgs = string.Format("-i \"{0}\"{5} {2} -map_metadata -1 -threads 0 {3}{4}{6} -y \"{1}\"", + var commandLineArgs = string.Format( + CultureInfo.InvariantCulture, + "-i \"{0}\" {2} -map_metadata -1 -threads 0 {3}{4}{5} -y \"{1}\"", inputTempFile, targetFile, videoArgs, GetAudioArgs(mediaSource), subtitleArgs, - durationParam, outputParam); return inputModifier + " " + commandLineArgs; @@ -257,7 +251,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV { _logger.LogInformation("Stopping ffmpeg recording process for {path}", _targetPath); - //process.Kill(); _process.StandardInput.WriteLine("q"); } catch (Exception ex) @@ -309,44 +302,26 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV { _hasExited = true; - DisposeLogStream(); + _logFileStream?.Dispose(); + _logFileStream = null; - try - { - var exitCode = process.ExitCode; + var exitCode = process.ExitCode; - _logger.LogInformation("FFMpeg recording exited with code {ExitCode} for {path}", exitCode, _targetPath); + _logger.LogInformation("FFMpeg recording exited with code {ExitCode} for {Path}", exitCode, _targetPath); - if (exitCode == 0) - { - _taskCompletionSource.TrySetResult(true); - } - else - { - _taskCompletionSource.TrySetException(new Exception(string.Format("Recording for {path} failed. Exit code {ExitCode}", _targetPath, exitCode))); - } - } - catch + if (exitCode == 0) { - _logger.LogError("FFMpeg recording exited with an error for {path}.", _targetPath); - _taskCompletionSource.TrySetException(new Exception(string.Format("Recording for {path} failed", _targetPath))); + _taskCompletionSource.TrySetResult(true); } - } - - private void DisposeLogStream() - { - if (_logFileStream != null) + else { - try - { - _logFileStream.Dispose(); - } - catch (Exception ex) - { - _logger.LogError(ex, "Error disposing recording log stream"); - } - - _logFileStream = null; + _taskCompletionSource.TrySetException( + new Exception( + string.Format( + CultureInfo.InvariantCulture, + "Recording for {0} failed. Exit code {1}", + _targetPath, + exitCode))); } } diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs index 1e5198dd6..ee975e19a 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs @@ -881,7 +881,7 @@ namespace Emby.Server.Implementations.LiveTv } var programList = _libraryManager.QueryItems(internalQuery).Items; - var totalCount = programList.Length; + var totalCount = programList.Count; var orderedPrograms = programList.Cast<LiveTvProgram>().OrderBy(i => i.StartDate.Date); @@ -969,8 +969,8 @@ namespace Emby.Server.Implementations.LiveTv var timers = new Dictionary<string, List<TimerInfo>>(); var seriesTimers = new Dictionary<string, List<SeriesTimerInfo>>(); - TimerInfo[] timerList = null; - SeriesTimerInfo[] seriesTimerList = null; + IReadOnlyList<TimerInfo> timerList = null; + IReadOnlyList<SeriesTimerInfo> seriesTimerList = null; foreach (var programTuple in programs) { @@ -1296,6 +1296,7 @@ namespace Emby.Server.Implementations.LiveTv } private const int MaxGuideDays = 14; + private double GetGuideDays() { var config = GetConfiguration(); @@ -1340,6 +1341,7 @@ namespace Emby.Server.Implementations.LiveTv excludeItemTypes.Add(typeof(Movie).Name); } } + if (query.IsSeries.HasValue) { if (query.IsSeries.Value) @@ -1351,10 +1353,12 @@ namespace Emby.Server.Implementations.LiveTv excludeItemTypes.Add(typeof(Episode).Name); } } + if (query.IsSports ?? false) { genres.Add("Sports"); } + if (query.IsKids ?? false) { genres.Add("Kids"); @@ -1400,20 +1404,20 @@ namespace Emby.Server.Implementations.LiveTv if (query.IsInProgress ?? false) { - //TODO Fix The co-variant conversion between Video[] and BaseItem[], this can generate runtime issues. + // TODO: Fix The co-variant conversion between Video[] and BaseItem[], this can generate runtime issues. result.Items = result .Items .OfType<Video>() .Where(i => !i.IsCompleteMedia) .ToArray(); - result.TotalRecordCount = result.Items.Length; + result.TotalRecordCount = result.Items.Count; } return result; } - public Task AddInfoToProgramDto(List<Tuple<BaseItem, BaseItemDto>> tuples, ItemFields[] fields, User user = null) + public Task AddInfoToProgramDto(IReadOnlyCollection<(BaseItem, BaseItemDto)> tuples, ItemFields[] fields, User user = null) { var programTuples = new List<Tuple<BaseItemDto, string, string>>(); var hasChannelImage = fields.Contains(ItemFields.ChannelImage); @@ -1877,7 +1881,7 @@ namespace Emby.Server.Implementations.LiveTv return _libraryManager.GetItemById(internalChannelId); } - public void AddChannelInfo(List<Tuple<BaseItemDto, LiveTvChannel>> tuples, DtoOptions options, User user) + public void AddChannelInfo(IReadOnlyCollection<(BaseItemDto, LiveTvChannel)> tuples, DtoOptions options, User user) { var now = DateTime.UtcNow; diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs index 85754ca8b..da98f3e58 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs @@ -584,9 +584,9 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun Logger, Config.ApplicationPaths, _appHost, - _socketFactory, _networkManager, _streamHelper); + } var enableHttpStream = true; @@ -601,9 +601,19 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun { httpUrl += "?transcode=" + profile; } + mediaSource.Path = httpUrl; - return new SharedHttpStream(mediaSource, info, streamId, FileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost, _streamHelper); + return new SharedHttpStream( + mediaSource, + info, + streamId, + FileSystem, + _httpClient, + Logger, + Config.ApplicationPaths, + _appHost, + _streamHelper); } return new HdHomerunUdpStream( @@ -616,7 +626,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun Logger, Config.ApplicationPaths, _appHost, - _socketFactory, _networkManager, _streamHelper); } diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs index fbbab07f8..eafa86d54 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs @@ -1,5 +1,4 @@ using System; -using System.Buffers; using System.Collections.Generic; using System.IO; using System.Net; @@ -22,8 +21,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun private const int RtpHeaderBytes = 12; private readonly IServerApplicationHost _appHost; - private readonly MediaBrowser.Model.Net.ISocketFactory _socketFactory; - private readonly IHdHomerunChannelCommands _channelCommands; private readonly int _numTuners; private readonly INetworkManager _networkManager; @@ -38,13 +35,11 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun ILogger logger, IServerApplicationPaths appPaths, IServerApplicationHost appHost, - MediaBrowser.Model.Net.ISocketFactory socketFactory, INetworkManager networkManager, IStreamHelper streamHelper) : base(mediaSource, tunerHostInfo, fileSystem, logger, appPaths, streamHelper) { _appHost = appHost; - _socketFactory = socketFactory; _networkManager = networkManager; OriginalStreamId = originalStreamId; _channelCommands = channelCommands; @@ -82,7 +77,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun } } - var udpClient = _socketFactory.CreateUdpSocket(localPort); + var udpClient = new UdpClient(localPort, AddressFamily.InterNetwork); var hdHomerunManager = new HdHomerunManager(); try @@ -133,7 +128,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun await taskCompletionSource.Task.ConfigureAwait(false); } - private Task StartStreaming(MediaBrowser.Model.Net.ISocket udpClient, HdHomerunManager hdHomerunManager, IPAddress remoteAddress, TaskCompletionSource<bool> openTaskCompletionSource, CancellationToken cancellationToken) + private Task StartStreaming(UdpClient udpClient, HdHomerunManager hdHomerunManager, IPAddress remoteAddress, TaskCompletionSource<bool> openTaskCompletionSource, CancellationToken cancellationToken) { return Task.Run(async () => { @@ -162,28 +157,37 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun }); } - private async Task CopyTo(MediaBrowser.Model.Net.ISocket udpClient, string file, TaskCompletionSource<bool> openTaskCompletionSource, CancellationToken cancellationToken) + private async Task CopyTo(UdpClient udpClient, string file, TaskCompletionSource<bool> openTaskCompletionSource, CancellationToken cancellationToken) { - byte[] buffer = ArrayPool<byte>.Shared.Rent(StreamDefaults.DefaultCopyToBufferSize); - try + var resolved = false; + + using (var fileStream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.Read)) { - using (var source = _socketFactory.CreateNetworkStream(udpClient, false)) - using (var fileStream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.Read)) + while (true) { - var currentCancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, new CancellationTokenSource(TimeSpan.FromSeconds(30)).Token).Token; - int read; - var resolved = false; - while ((read = await source.ReadAsync(buffer, 0, buffer.Length, currentCancellationToken).ConfigureAwait(false)) != 0) + cancellationToken.ThrowIfCancellationRequested(); + using (var timeOutSource = new CancellationTokenSource()) + using (var linkedSource = CancellationTokenSource.CreateLinkedTokenSource( + cancellationToken, + timeOutSource.Token)) { - cancellationToken.ThrowIfCancellationRequested(); + var resTask = udpClient.ReceiveAsync(); + if (await Task.WhenAny(resTask, Task.Delay(30000, linkedSource.Token)).ConfigureAwait(false) != resTask) + { + resTask.Dispose(); + break; + } - currentCancellationToken = cancellationToken; + // We don't want all these delay tasks to keep running + timeOutSource.Cancel(); + var res = await resTask.ConfigureAwait(false); + var buffer = res.Buffer; - read -= RtpHeaderBytes; + var read = buffer.Length - RtpHeaderBytes; if (read > 0) { - await fileStream.WriteAsync(buffer, RtpHeaderBytes, read).ConfigureAwait(false); + fileStream.Write(buffer, RtpHeaderBytes, read); } if (!resolved) @@ -195,10 +199,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun } } } - finally - { - ArrayPool<byte>.Shared.Return(buffer); - } } } } diff --git a/Emby.Server.Implementations/Localization/Core/el.json b/Emby.Server.Implementations/Localization/Core/el.json index db7ebb0c0..3589a4893 100644 --- a/Emby.Server.Implementations/Localization/Core/el.json +++ b/Emby.Server.Implementations/Localization/Core/el.json @@ -3,7 +3,7 @@ "AppDeviceValues": "Εφαρμογή: {0}, Συσκευή: {1}", "Application": "Εφαρμογή", "Artists": "Καλλιτέχνες", - "AuthenticationSucceededWithUserName": "{0} επιτυχείς σύνδεση", + "AuthenticationSucceededWithUserName": "Ο χρήστης {0} επαληθεύτηκε με επιτυχία", "Books": "Βιβλία", "CameraImageUploadedFrom": "Μια νέα εικόνα κάμερας έχει αποσταλεί από {0}", "Channels": "Κανάλια", @@ -15,9 +15,9 @@ "Favorites": "Αγαπημένα", "Folders": "Φάκελοι", "Genres": "Είδη", - "HeaderAlbumArtists": "Άλμπουμ Καλλιτεχνών", + "HeaderAlbumArtists": "Καλλιτέχνες του Άλμπουμ", "HeaderCameraUploads": "Μεταφορτώσεις Κάμερας", - "HeaderContinueWatching": "Συνεχίστε να παρακολουθείτε", + "HeaderContinueWatching": "Συνεχίστε την παρακολούθηση", "HeaderFavoriteAlbums": "Αγαπημένα Άλμπουμ", "HeaderFavoriteArtists": "Αγαπημένοι Καλλιτέχνες", "HeaderFavoriteEpisodes": "Αγαπημένα Επεισόδια", @@ -27,7 +27,7 @@ "HeaderNextUp": "Επόμενο", "HeaderRecordingGroups": "Γκρουπ Εγγραφών", "HomeVideos": "Προσωπικά βίντεο", - "Inherit": "Inherit", + "Inherit": "Κληρονόμηση", "ItemAddedWithName": "{0} προστέθηκε στη βιβλιοθήκη", "ItemRemovedWithName": "{0} διαγράφηκε από τη βιβλιοθήκη", "LabelIpAddressValue": "Διεύθυνση IP: {0}", @@ -92,6 +92,6 @@ "UserStartedPlayingItemWithValues": "{0} παίζει {1} σε {2}", "UserStoppedPlayingItemWithValues": "{0} τελείωσε να παίζει {1} σε {2}", "ValueHasBeenAddedToLibrary": "{0} προστέθηκαν στη βιβλιοθήκη πολυμέσων σας", - "ValueSpecialEpisodeName": "Special - {0}", + "ValueSpecialEpisodeName": "Σπέσιαλ - {0}", "VersionNumber": "Έκδοση {0}" } diff --git a/Emby.Server.Implementations/Localization/Core/es.json b/Emby.Server.Implementations/Localization/Core/es.json index f03184d5b..c78794967 100644 --- a/Emby.Server.Implementations/Localization/Core/es.json +++ b/Emby.Server.Implementations/Localization/Core/es.json @@ -23,7 +23,7 @@ "HeaderFavoriteEpisodes": "Episodios favoritos", "HeaderFavoriteShows": "Series favoritas", "HeaderFavoriteSongs": "Canciones favoritas", - "HeaderLiveTV": "TV en directo", + "HeaderLiveTV": "Televisión en directo", "HeaderNextUp": "Siguiendo", "HeaderRecordingGroups": "Grupos de grabación", "HomeVideos": "Vídeos caseros", @@ -79,7 +79,7 @@ "SubtitlesDownloadedForItem": "Descargar subtítulos para {0}", "Sync": "Sincronizar", "System": "Sistema", - "TvShows": "Series de TV", + "TvShows": "Programas de televisión", "User": "Usuario", "UserCreatedWithName": "El usuario {0} ha sido creado", "UserDeletedWithName": "El usuario {0} ha sido borrado", diff --git a/Emby.Server.Implementations/Localization/Core/fr.json b/Emby.Server.Implementations/Localization/Core/fr.json index e434b7605..6bfedb712 100644 --- a/Emby.Server.Implementations/Localization/Core/fr.json +++ b/Emby.Server.Implementations/Localization/Core/fr.json @@ -17,14 +17,14 @@ "Genres": "Genres", "HeaderAlbumArtists": "Artistes de l'album", "HeaderCameraUploads": "Photos transférées", - "HeaderContinueWatching": "Continuer à regarder", + "HeaderContinueWatching": "Reprendre", "HeaderFavoriteAlbums": "Albums favoris", "HeaderFavoriteArtists": "Artistes favoris", "HeaderFavoriteEpisodes": "Épisodes favoris", "HeaderFavoriteShows": "Séries favorites", "HeaderFavoriteSongs": "Chansons favorites", "HeaderLiveTV": "TV en direct", - "HeaderNextUp": "En Cours", + "HeaderNextUp": "À suivre", "HeaderRecordingGroups": "Groupes d'enregistrements", "HomeVideos": "Vidéos personnelles", "Inherit": "Hériter", diff --git a/Emby.Server.Implementations/Localization/Core/nb.json b/Emby.Server.Implementations/Localization/Core/nb.json index dbda794ad..daa3f5880 100644 --- a/Emby.Server.Implementations/Localization/Core/nb.json +++ b/Emby.Server.Implementations/Localization/Core/nb.json @@ -1,11 +1,11 @@ { "Albums": "Album", - "AppDeviceValues": "App:{0}, Enhet {1}", + "AppDeviceValues": "App:{0}, Enhet: {1}", "Application": "Applikasjon", "Artists": "Artister", "AuthenticationSucceededWithUserName": "{0} vellykkede autentisert", "Books": "Bøker", - "CameraImageUploadedFrom": "A new camera image has been uploaded from {0}", + "CameraImageUploadedFrom": "Et nytt kamerabilde er lastet opp fra {0}", "Channels": "Kanaler", "ChapterNameValue": "Kapittel {0}", "Collections": "Samlinger", diff --git a/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs b/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs index 52d07d784..840aca7a6 100644 --- a/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs +++ b/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs @@ -139,7 +139,7 @@ namespace Emby.Server.Implementations.MediaEncoder var protocol = MediaProtocol.File; - var inputPath = MediaEncoderHelpers.GetInputArgument(_fileSystem, video.Path, protocol, null, Array.Empty<string>()); + var inputPath = MediaEncoderHelpers.GetInputArgument(_fileSystem, video.Path, null, Array.Empty<string>()); Directory.CreateDirectory(Path.GetDirectoryName(path)); diff --git a/Emby.Server.Implementations/Net/SocketFactory.cs b/Emby.Server.Implementations/Net/SocketFactory.cs index cb53ce50c..0870db003 100644 --- a/Emby.Server.Implementations/Net/SocketFactory.cs +++ b/Emby.Server.Implementations/Net/SocketFactory.cs @@ -19,7 +19,8 @@ namespace Emby.Server.Implementations.Net throw new ArgumentException("localPort cannot be less than zero.", nameof(localPort)); } - var retVal = new Socket(AddressFamily.InterNetwork, System.Net.Sockets.SocketType.Dgram, System.Net.Sockets.ProtocolType.Udp); + var retVal = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + try { retVal.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodingTempTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs index ad9b56535..c343a7d48 100644 --- a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodingTempTask.cs +++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs @@ -13,23 +13,22 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks /// <summary> /// Deletes all transcoding temp files /// </summary> - public class DeleteTranscodingTempTask : IScheduledTask, IConfigurableScheduledTask + public class DeleteTranscodeFileTask : IScheduledTask, IConfigurableScheduledTask { /// <summary> /// Gets or sets the application paths. /// </summary> /// <value>The application paths.</value> - protected ServerApplicationPaths ApplicationPaths { get; set; } - + private ServerApplicationPaths ApplicationPaths { get; set; } private readonly ILogger _logger; private readonly IFileSystem _fileSystem; /// <summary> - /// Initializes a new instance of the <see cref="DeleteTranscodingTempTask" /> class. + /// Initializes a new instance of the <see cref="DeleteTranscodeFileTask" /> class. /// </summary> - public DeleteTranscodingTempTask(ServerApplicationPaths appPaths, ILogger logger, IFileSystem fileSystem) + public DeleteTranscodeFileTask(ServerApplicationPaths appPaths, ILogger logger, IFileSystem fileSystem) { ApplicationPaths = appPaths; _logger = logger; diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/RefreshMediaLibraryTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/RefreshMediaLibraryTask.cs index 1a3d85ad7..3e6d251c9 100644 --- a/Emby.Server.Implementations/ScheduledTasks/Tasks/RefreshMediaLibraryTask.cs +++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/RefreshMediaLibraryTask.cs @@ -10,7 +10,7 @@ using MediaBrowser.Model.Tasks; namespace Emby.Server.Implementations.ScheduledTasks { /// <summary> - /// Class RefreshMediaLibraryTask + /// Class RefreshMediaLibraryTask. /// </summary> public class RefreshMediaLibraryTask : IScheduledTask { @@ -31,15 +31,14 @@ namespace Emby.Server.Implementations.ScheduledTasks } /// <summary> - /// Creates the triggers that define when the task will run + /// Creates the triggers that define when the task will run. /// </summary> /// <returns>IEnumerable{BaseTaskTrigger}.</returns> public IEnumerable<TaskTriggerInfo> GetDefaultTriggers() { - return new[] { - - // Every so often - new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(12).Ticks} + yield return new TaskTriggerInfo + { + Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(12).Ticks }; } @@ -60,7 +59,7 @@ namespace Emby.Server.Implementations.ScheduledTasks public string Name => "Scan media library"; - public string Description => "Scans your media library and refreshes metatata based on configuration."; + public string Description => "Scans your media library for new files and refreshes metadata."; public string Category => "Library"; diff --git a/Emby.Server.Implementations/Services/ResponseHelper.cs b/Emby.Server.Implementations/Services/ResponseHelper.cs index ca2b22fe0..a566b18dd 100644 --- a/Emby.Server.Implementations/Services/ResponseHelper.cs +++ b/Emby.Server.Implementations/Services/ResponseHelper.cs @@ -6,8 +6,8 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using Emby.Server.Implementations.HttpServer; -using Microsoft.AspNetCore.Http; using MediaBrowser.Model.Services; +using Microsoft.AspNetCore.Http; namespace Emby.Server.Implementations.Services { diff --git a/Emby.Server.Implementations/Services/ServiceExec.cs b/Emby.Server.Implementations/Services/ServiceExec.cs index 9124b9c14..9f5f97028 100644 --- a/Emby.Server.Implementations/Services/ServiceExec.cs +++ b/Emby.Server.Implementations/Services/ServiceExec.cs @@ -87,8 +87,7 @@ namespace Emby.Server.Implementations.Services var response = actionContext.ServiceAction(instance, requestDto); - var taskResponse = response as Task; - if (taskResponse != null) + if (response is Task taskResponse) { return GetTaskResult(taskResponse); } @@ -104,8 +103,7 @@ namespace Emby.Server.Implementations.Services { try { - var taskObject = task as Task<object>; - if (taskObject != null) + if (task is Task<object> taskObject) { return await taskObject.ConfigureAwait(false); } @@ -136,7 +134,7 @@ namespace Emby.Server.Implementations.Services } catch (TypeAccessException) { - return null; //return null for void Task's + return null; // return null for void Task's } } @@ -155,29 +153,22 @@ namespace Emby.Server.Implementations.Services Id = ServiceMethod.Key(serviceType, actionName, requestType.GetMethodName()) }; - try - { - actionCtx.ServiceAction = CreateExecFn(serviceType, requestType, mi); - } - catch - { - //Potential problems with MONO, using reflection for fallback - actionCtx.ServiceAction = (service, request) => - mi.Invoke(service, new[] { request }); - } + actionCtx.ServiceAction = CreateExecFn(serviceType, requestType, mi); var reqFilters = new List<IHasRequestFilter>(); foreach (var attr in mi.GetCustomAttributes(true)) { - var hasReqFilter = attr as IHasRequestFilter; - - if (hasReqFilter != null) + if (attr is IHasRequestFilter hasReqFilter) + { reqFilters.Add(hasReqFilter); + } } if (reqFilters.Count > 0) + { actionCtx.RequestFilters = reqFilters.OrderBy(i => i.Priority).ToArray(); + } actions.Add(actionCtx); } @@ -198,15 +189,19 @@ namespace Emby.Server.Implementations.Services if (mi.ReturnType != typeof(void)) { - var executeFunc = Expression.Lambda<ActionInvokerFn> - (callExecute, serviceParam, requestDtoParam).Compile(); + var executeFunc = Expression.Lambda<ActionInvokerFn>( + callExecute, + serviceParam, + requestDtoParam).Compile(); return executeFunc; } else { - var executeFunc = Expression.Lambda<VoidActionInvokerFn> - (callExecute, serviceParam, requestDtoParam).Compile(); + var executeFunc = Expression.Lambda<VoidActionInvokerFn>( + callExecute, + serviceParam, + requestDtoParam).Compile(); return (service, request) => { diff --git a/Emby.Server.Implementations/Services/UrlExtensions.cs b/Emby.Server.Implementations/Services/UrlExtensions.cs index 8899fbfa3..5d4407f3b 100644 --- a/Emby.Server.Implementations/Services/UrlExtensions.cs +++ b/Emby.Server.Implementations/Services/UrlExtensions.cs @@ -12,10 +12,10 @@ namespace Emby.Server.Implementations.Services { public static string GetMethodName(this Type type) { - var typeName = type.FullName != null //can be null, e.g. generic types - ? LeftPart(type.FullName, "[[") //Generic Fullname - .Replace(type.Namespace + ".", "") //Trim Namespaces - .Replace("+", ".") //Convert nested into normal type + var typeName = type.FullName != null // can be null, e.g. generic types + ? LeftPart(type.FullName, "[[") // Generic Fullname + .Replace(type.Namespace + ".", string.Empty) // Trim Namespaces + .Replace("+", ".") // Convert nested into normal type : type.Name; return type.IsGenericParameter ? "'" + typeName : typeName; @@ -23,7 +23,11 @@ namespace Emby.Server.Implementations.Services private static string LeftPart(string strVal, string needle) { - if (strVal == null) return null; + if (strVal == null) + { + return null; + } + var pos = strVal.IndexOf(needle, StringComparison.OrdinalIgnoreCase); return pos == -1 ? strVal diff --git a/Emby.Server.Implementations/Sorting/ArtistComparer.cs b/Emby.Server.Implementations/Sorting/ArtistComparer.cs index 9d5befc9a..756d3c5b6 100644 --- a/Emby.Server.Implementations/Sorting/ArtistComparer.cs +++ b/Emby.Server.Implementations/Sorting/ArtistComparer.cs @@ -7,16 +7,14 @@ using MediaBrowser.Model.Querying; namespace Emby.Server.Implementations.Sorting { /// <summary> - /// Class ArtistComparer + /// Class ArtistComparer. /// </summary> public class ArtistComparer : IBaseItemComparer { - /// <summary> - /// Compares the specified x. - /// </summary> - /// <param name="x">The x.</param> - /// <param name="y">The y.</param> - /// <returns>System.Int32.</returns> + /// <inheritdoc /> + public string Name => ItemSortBy.Artist; + + /// <inheritdoc /> public int Compare(BaseItem x, BaseItem y) { return string.Compare(GetValue(x), GetValue(y), StringComparison.CurrentCultureIgnoreCase); @@ -29,20 +27,12 @@ namespace Emby.Server.Implementations.Sorting /// <returns>System.String.</returns> private static string GetValue(BaseItem x) { - var audio = x as Audio; - - if (audio == null) + if (!(x is Audio audio)) { return string.Empty; } - return audio.Artists.Length == 0 ? null : audio.Artists[0]; + return audio.Artists.Count == 0 ? null : audio.Artists[0]; } - - /// <summary> - /// Gets the name. - /// </summary> - /// <value>The name.</value> - public string Name => ItemSortBy.Artist; } } diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj index e87283477..8c57ee453 100644 --- a/Jellyfin.Server/Jellyfin.Server.csproj +++ b/Jellyfin.Server/Jellyfin.Server.csproj @@ -49,7 +49,6 @@ <ItemGroup> <ProjectReference Include="..\Emby.Drawing\Emby.Drawing.csproj" /> - <ProjectReference Include="..\Emby.IsoMounting\IsoMounter\IsoMounter.csproj" /> <ProjectReference Include="..\Emby.Server.Implementations\Emby.Server.Implementations.csproj" /> <ProjectReference Include="..\Jellyfin.Drawing.Skia\Jellyfin.Drawing.Skia.csproj" /> </ItemGroup> diff --git a/MediaBrowser.Api/LiveTv/LiveTvService.cs b/MediaBrowser.Api/LiveTv/LiveTvService.cs index 8a4d6e216..3a15c3776 100644 --- a/MediaBrowser.Api/LiveTv/LiveTvService.cs +++ b/MediaBrowser.Api/LiveTv/LiveTvService.cs @@ -741,7 +741,7 @@ namespace MediaBrowser.Api.LiveTv var result = new QueryResult<BaseItemDto> { Items = returnArray, - TotalRecordCount = returnArray.Length + TotalRecordCount = returnArray.Count }; return ToOptimizedResult(result); diff --git a/MediaBrowser.Api/Movies/MoviesService.cs b/MediaBrowser.Api/Movies/MoviesService.cs index d601fb500..c1c6ffc2e 100644 --- a/MediaBrowser.Api/Movies/MoviesService.cs +++ b/MediaBrowser.Api/Movies/MoviesService.cs @@ -243,7 +243,7 @@ namespace MediaBrowser.Api.Movies } } - return categories.OrderBy(i => i.RecommendationType).ThenBy(i => Guid.NewGuid()); + return categories.OrderBy(i => i.RecommendationType); } private IEnumerable<RecommendationDto> GetWithDirector(User user, IEnumerable<string> names, int itemLimit, DtoOptions dtoOptions, RecommendationType type) diff --git a/MediaBrowser.Api/Music/AlbumsService.cs b/MediaBrowser.Api/Music/AlbumsService.cs index c48bcde5c..2cd3a1003 100644 --- a/MediaBrowser.Api/Music/AlbumsService.cs +++ b/MediaBrowser.Api/Music/AlbumsService.cs @@ -104,16 +104,15 @@ namespace MediaBrowser.Api.Music var album2 = (MusicAlbum)item2; var artists1 = album1 - .AllArtists + .GetAllArtists() .DistinctNames() .ToList(); - var artists2 = album2 - .AllArtists - .DistinctNames() - .ToDictionary(i => i, StringComparer.OrdinalIgnoreCase); + var artists2 = new HashSet<string>( + album2.GetAllArtists().DistinctNames(), + StringComparer.OrdinalIgnoreCase); - return points + artists1.Where(artists2.ContainsKey).Sum(i => 5); + return points + artists1.Where(artists2.Contains).Sum(i => 5); } } } diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 496c2032a..17aa6b23a 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -634,7 +634,7 @@ namespace MediaBrowser.Api.Playback } else { - value = value.Substring(Npt.Length, index); + value = value.Substring(Npt.Length, index - Npt.Length); } if (value.IndexOf(':') == -1) diff --git a/MediaBrowser.Api/PlaylistService.cs b/MediaBrowser.Api/PlaylistService.cs index 482c1a2a8..483bf98fb 100644 --- a/MediaBrowser.Api/PlaylistService.cs +++ b/MediaBrowser.Api/PlaylistService.cs @@ -189,11 +189,9 @@ namespace MediaBrowser.Api var dtos = _dtoService.GetBaseItemDtos(items.Select(i => i.Item2).ToList(), dtoOptions, user); - var index = 0; - foreach (var item in dtos) + for (int index = 0; index < dtos.Count; index++) { - item.PlaylistItemId = items[index].Item1.Id; - index++; + dtos[index].PlaylistItemId = items[index].Item1.Id; } var result = new QueryResult<BaseItemDto> diff --git a/MediaBrowser.Api/SuggestionsService.cs b/MediaBrowser.Api/SuggestionsService.cs index 72fdae365..4e857eafc 100644 --- a/MediaBrowser.Api/SuggestionsService.cs +++ b/MediaBrowser.Api/SuggestionsService.cs @@ -61,11 +61,6 @@ namespace MediaBrowser.Api var dtoList = _dtoService.GetBaseItemDtos(result.Items, dtoOptions, user); - if (dtoList == null) - { - throw new InvalidOperationException("GetBaseItemDtos returned null"); - } - return new QueryResult<BaseItemDto> { TotalRecordCount = result.TotalRecordCount, diff --git a/MediaBrowser.Api/TvShowsService.cs b/MediaBrowser.Api/TvShowsService.cs index 2951fa6b4..1340bd8ef 100644 --- a/MediaBrowser.Api/TvShowsService.cs +++ b/MediaBrowser.Api/TvShowsService.cs @@ -382,13 +382,13 @@ namespace MediaBrowser.Api throw new ResourceNotFoundException("Series not found"); } - var seasons = (series.GetItemList(new InternalItemsQuery(user) + var seasons = series.GetItemList(new InternalItemsQuery(user) { IsMissing = request.IsMissing, IsSpecialSeason = request.IsSpecialSeason, AdjacentTo = request.AdjacentTo - })); + }); var dtoOptions = GetDtoOptions(_authContext, request); @@ -396,7 +396,7 @@ namespace MediaBrowser.Api return new QueryResult<BaseItemDto> { - TotalRecordCount = returnItems.Length, + TotalRecordCount = returnItems.Count, Items = returnItems }; } diff --git a/MediaBrowser.Api/UserLibrary/ItemsService.cs b/MediaBrowser.Api/UserLibrary/ItemsService.cs index a1e976bed..ada540ba6 100644 --- a/MediaBrowser.Api/UserLibrary/ItemsService.cs +++ b/MediaBrowser.Api/UserLibrary/ItemsService.cs @@ -175,11 +175,6 @@ namespace MediaBrowser.Api.UserLibrary var dtoList = _dtoService.GetBaseItemDtos(result.Items, dtoOptions, user); - if (dtoList == null) - { - throw new InvalidOperationException("GetBaseItemDtos returned null"); - } - return new QueryResult<BaseItemDto> { TotalRecordCount = result.TotalRecordCount, diff --git a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs index a9b06095d..45694a678 100644 --- a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs +++ b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs @@ -2,6 +2,7 @@ using System; using System.Linq; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; @@ -366,11 +367,21 @@ namespace MediaBrowser.Api.UserLibrary var dtoOptions = GetDtoOptions(_authContext, request); - var dtos = item.GetExtras(new[] { ExtraType.Trailer }) + var dtosExtras = item.GetExtras(new[] { ExtraType.Trailer }) .Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user, item)) .ToArray(); - return ToOptimizedResult(dtos); + if (item is IHasTrailers hasTrailers) + { + var trailers = hasTrailers.GetTrailers(); + var dtosTrailers = _dtoService.GetBaseItemDtos(trailers, dtoOptions, user, item); + var allTrailers = new BaseItemDto[dtosExtras.Length + dtosTrailers.Count]; + dtosExtras.CopyTo(allTrailers, 0); + dtosTrailers.CopyTo(allTrailers, dtosExtras.Length); + return ToOptimizedResult(allTrailers); + } + + return ToOptimizedResult(dtosExtras); } /// <summary> diff --git a/MediaBrowser.Api/UserService.cs b/MediaBrowser.Api/UserService.cs index f08d070ca..2c0a0b443 100644 --- a/MediaBrowser.Api/UserService.cs +++ b/MediaBrowser.Api/UserService.cs @@ -248,9 +248,14 @@ namespace MediaBrowser.Api private readonly INetworkManager _networkManager; private readonly IDeviceManager _deviceManager; private readonly IAuthorizationContext _authContext; - private readonly ILogger _logger; - public UserService(IUserManager userManager, ISessionManager sessionMananger, IServerConfigurationManager config, INetworkManager networkManager, IDeviceManager deviceManager, IAuthorizationContext authContext, ILoggerFactory loggerFactory) + public UserService( + IUserManager userManager, + ISessionManager sessionMananger, + IServerConfigurationManager config, + INetworkManager networkManager, + IDeviceManager deviceManager, + IAuthorizationContext authContext) { _userManager = userManager; _sessionMananger = sessionMananger; @@ -258,7 +263,6 @@ namespace MediaBrowser.Api _networkManager = networkManager; _deviceManager = deviceManager; _authContext = authContext; - _logger = loggerFactory.CreateLogger(nameof(UserService)); } public object Get(GetPublicUsers request) @@ -418,7 +422,7 @@ namespace MediaBrowser.Api return ToOptimizedResult(result); } - catch(SecurityException e) + catch (SecurityException e) { // rethrow adding IP address to message throw new SecurityException($"[{Request.RemoteIp}] {e.Message}"); diff --git a/MediaBrowser.Common/Extensions/CollectionExtensions.cs b/MediaBrowser.Common/Extensions/CollectionExtensions.cs index f7c0e3cf0..3bc0295a0 100644 --- a/MediaBrowser.Common/Extensions/CollectionExtensions.cs +++ b/MediaBrowser.Common/Extensions/CollectionExtensions.cs @@ -10,5 +10,22 @@ namespace MediaBrowser.Common.Extensions dictionary.TryGetValue(key, out var ret); return ret; } + + // REVIEW: Inline? + /// <summary> + /// Copies all the elements of the current collection to the specified list + /// starting at the specified destination array index. The index is specified as a 32-bit integer. + /// </summary> + /// <param name="source">The current collection that is the source of the elements.</param> + /// <param name="destination">The list that is the destination of the elements copied from the current collection.</param> + /// <param name="index">A 32-bit integer that represents the index in <c>destination</c> at which copying begins.</param> + /// <typeparam name="T"></typeparam> + public static void CopyTo<T>(this IReadOnlyCollection<T> source, IList<T> destination, int index = 0) + { + foreach (T item in source) + { + destination[index++] = item; + } + } } } diff --git a/MediaBrowser.Common/Net/CustomHeaderNames.cs b/MediaBrowser.Common/Net/CustomHeaderNames.cs index ff148dc80..bda897ed9 100644 --- a/MediaBrowser.Common/Net/CustomHeaderNames.cs +++ b/MediaBrowser.Common/Net/CustomHeaderNames.cs @@ -8,4 +8,4 @@ namespace MediaBrowser.Common.Net public const string XForwardedProto = "X-Forwarded-Proto"; public const string XRealIP = "X-Real-IP"; } -}
\ No newline at end of file +} diff --git a/MediaBrowser.Controller/Dto/IDtoService.cs b/MediaBrowser.Controller/Dto/IDtoService.cs index 4b6fd58fe..ba693a065 100644 --- a/MediaBrowser.Controller/Dto/IDtoService.cs +++ b/MediaBrowser.Controller/Dto/IDtoService.cs @@ -57,7 +57,7 @@ namespace MediaBrowser.Controller.Dto /// <param name="options">The options.</param> /// <param name="user">The user.</param> /// <param name="owner">The owner.</param> - BaseItemDto[] GetBaseItemDtos(IReadOnlyList<BaseItem> items, DtoOptions options, User user = null, BaseItem owner = null); + IReadOnlyList<BaseItemDto> GetBaseItemDtos(IReadOnlyList<BaseItem> items, DtoOptions options, User user = null, BaseItem owner = null); /// <summary> /// Gets the item by name dto. diff --git a/MediaBrowser.Controller/Entities/Audio/Audio.cs b/MediaBrowser.Controller/Entities/Audio/Audio.cs index 13a6fe44a..67b21068a 100644 --- a/MediaBrowser.Controller/Entities/Audio/Audio.cs +++ b/MediaBrowser.Controller/Entities/Audio/Audio.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Configuration; @@ -19,15 +20,13 @@ namespace MediaBrowser.Controller.Entities.Audio IHasLookupInfo<SongInfo>, IHasMediaSources { - /// <summary> - /// Gets or sets the artist. - /// </summary> - /// <value>The artist.</value> + /// <inheritdoc /> [IgnoreDataMember] - public string[] Artists { get; set; } + public IReadOnlyList<string> Artists { get; set; } + /// <inheritdoc /> [IgnoreDataMember] - public string[] AlbumArtists { get; set; } + public IReadOnlyList<string> AlbumArtists { get; set; } public Audio() { @@ -64,30 +63,6 @@ namespace MediaBrowser.Controller.Entities.Audio } [IgnoreDataMember] - public string[] AllArtists - { - get - { - var list = new string[AlbumArtists.Length + Artists.Length]; - - var index = 0; - foreach (var artist in AlbumArtists) - { - list[index] = artist; - index++; - } - foreach (var artist in Artists) - { - list[index] = artist; - index++; - } - - return list; - - } - } - - [IgnoreDataMember] public MusicAlbum AlbumEntity => FindParent<MusicAlbum>(); /// <summary> @@ -125,7 +100,7 @@ namespace MediaBrowser.Controller.Entities.Audio songKey = Album + "-" + songKey; } - var albumArtist = AlbumArtists.Length == 0 ? null : AlbumArtists[0]; + var albumArtist = AlbumArtists.FirstOrDefault(); if (!string.IsNullOrEmpty(albumArtist)) { songKey = albumArtist + "-" + songKey; diff --git a/MediaBrowser.Controller/Entities/Audio/IHasAlbumArtist.cs b/MediaBrowser.Controller/Entities/Audio/IHasAlbumArtist.cs index a269b3486..056f31f78 100644 --- a/MediaBrowser.Controller/Entities/Audio/IHasAlbumArtist.cs +++ b/MediaBrowser.Controller/Entities/Audio/IHasAlbumArtist.cs @@ -1,14 +1,35 @@ +using System.Collections.Generic; + namespace MediaBrowser.Controller.Entities.Audio { public interface IHasAlbumArtist { - string[] AlbumArtists { get; set; } + IReadOnlyList<string> AlbumArtists { get; set; } } public interface IHasArtist { - string[] AllArtists { get; } + /// <summary> + /// Gets or sets the artists. + /// </summary> + /// <value>The artists.</value> + IReadOnlyList<string> Artists { get; set; } + } + + public static class Extentions + { + public static IEnumerable<string> GetAllArtists<T>(this T item) + where T : IHasArtist, IHasAlbumArtist + { + foreach (var i in item.AlbumArtists) + { + yield return i; + } - string[] Artists { get; set; } + foreach (var i in item.Artists) + { + yield return i; + } + } } } diff --git a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs index 5b2939b75..edf6ffa21 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs @@ -18,8 +18,11 @@ namespace MediaBrowser.Controller.Entities.Audio /// </summary> public class MusicAlbum : Folder, IHasAlbumArtist, IHasArtist, IHasMusicGenres, IHasLookupInfo<AlbumInfo>, IMetadataContainer { - public string[] AlbumArtists { get; set; } - public string[] Artists { get; set; } + /// <inheritdoc /> + public IReadOnlyList<string> AlbumArtists { get; set; } + + /// <inheritdoc /> + public IReadOnlyList<string> Artists { get; set; } public MusicAlbum() { @@ -41,8 +44,7 @@ namespace MediaBrowser.Controller.Entities.Audio var parents = GetParents(); foreach (var parent in parents) { - var artist = parent as MusicArtist; - if (artist != null) + if (parent is MusicArtist artist) { return artist; } @@ -63,30 +65,7 @@ namespace MediaBrowser.Controller.Entities.Audio public override bool SupportsCumulativeRunTimeTicks => true; [IgnoreDataMember] - public string[] AllArtists - { - get - { - var list = new string[AlbumArtists.Length + Artists.Length]; - - var index = 0; - foreach (var artist in AlbumArtists) - { - list[index] = artist; - index++; - } - foreach (var artist in Artists) - { - list[index] = artist; - index++; - } - - return list; - } - } - - [IgnoreDataMember] - public string AlbumArtist => AlbumArtists.Length == 0 ? null : AlbumArtists[0]; + public string AlbumArtist => AlbumArtists.FirstOrDefault(); [IgnoreDataMember] public override bool SupportsPeople => false; @@ -216,8 +195,7 @@ namespace MediaBrowser.Controller.Entities.Audio private async Task RefreshArtists(MetadataRefreshOptions refreshOptions, CancellationToken cancellationToken) { - var all = AllArtists; - foreach (var i in all) + foreach (var i in this.GetAllArtists()) { // This should not be necessary but we're seeing some cases of it if (string.IsNullOrEmpty(i)) diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 2ae856b02..0e9f7ee44 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -2871,16 +2871,24 @@ namespace MediaBrowser.Controller.Entities /// Gets or sets the remote trailers. /// </summary> /// <value>The remote trailers.</value> - public MediaUrl[] RemoteTrailers { get; set; } + public IReadOnlyList<MediaUrl> RemoteTrailers { get; set; } public IEnumerable<BaseItem> GetExtras() { return ExtraIds.Select(LibraryManager.GetItemById).Where(i => i != null).OrderBy(i => i.SortName); } - public IEnumerable<BaseItem> GetExtras(ExtraType[] extraTypes) + public IEnumerable<BaseItem> GetExtras(IReadOnlyCollection<ExtraType> extraTypes) { - return ExtraIds.Select(LibraryManager.GetItemById).Where(i => i != null && extraTypes.Contains(i.ExtraType.Value)).OrderBy(i => i.SortName); + return ExtraIds.Select(LibraryManager.GetItemById).Where(i => i != null && extraTypes.Contains(i.ExtraType.Value)); + } + + public IEnumerable<BaseItem> GetTrailers() + { + if (this is IHasTrailers) + return ((IHasTrailers)this).LocalTrailerIds.Select(LibraryManager.GetItemById).Where(i => i != null).OrderBy(i => i.SortName); + else + return Array.Empty<BaseItem>(); } public IEnumerable<BaseItem> GetDisplayExtras() @@ -2900,7 +2908,7 @@ namespace MediaBrowser.Controller.Entities } // Possible types of extra videos - public static ExtraType[] DisplayExtraTypes = new[] { Model.Entities.ExtraType.BehindTheScenes, Model.Entities.ExtraType.Clip, Model.Entities.ExtraType.DeletedScene, Model.Entities.ExtraType.Interview, Model.Entities.ExtraType.Sample, Model.Entities.ExtraType.Scene }; + public static readonly IReadOnlyCollection<ExtraType> DisplayExtraTypes = new[] { Model.Entities.ExtraType.BehindTheScenes, Model.Entities.ExtraType.Clip, Model.Entities.ExtraType.DeletedScene, Model.Entities.ExtraType.Interview, Model.Entities.ExtraType.Sample, Model.Entities.ExtraType.Scene }; public virtual bool SupportsExternalTransfer => false; } diff --git a/MediaBrowser.Controller/Entities/Extensions.cs b/MediaBrowser.Controller/Entities/Extensions.cs index f3bddd29c..d2ca11740 100644 --- a/MediaBrowser.Controller/Entities/Extensions.cs +++ b/MediaBrowser.Controller/Entities/Extensions.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using MediaBrowser.Common.Extensions; using MediaBrowser.Model.Entities; namespace MediaBrowser.Controller.Entities @@ -28,13 +29,17 @@ namespace MediaBrowser.Controller.Entities Url = url }; - if (item.RemoteTrailers.Length == 0) + if (item.RemoteTrailers.Count == 0) { item.RemoteTrailers = new[] { mediaUrl }; } else { - item.RemoteTrailers = item.RemoteTrailers.Concat(new[] { mediaUrl }).ToArray(); + var oldIds = item.RemoteTrailers; + var newIds = new MediaUrl[oldIds.Count + 1]; + oldIds.CopyTo(newIds); + newIds[oldIds.Count] = mediaUrl; + item.RemoteTrailers = newIds; } } } diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index d841b7ef8..d61a07066 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -666,36 +666,36 @@ namespace MediaBrowser.Controller.Entities query.StartIndex = null; query.Limit = null; - var itemsList = LibraryManager.GetItemList(query); + IEnumerable<BaseItem> itemsList = LibraryManager.GetItemList(query); var user = query.User; if (user != null) { // needed for boxsets - itemsList = itemsList.Where(i => i.IsVisibleStandalone(query.User)).ToList(); + itemsList = itemsList.Where(i => i.IsVisibleStandalone(query.User)); } - BaseItem[] returnItems; + IEnumerable<BaseItem> returnItems; int totalCount = 0; if (query.EnableTotalRecordCount) { - var itemsArray = itemsList.ToArray(); - totalCount = itemsArray.Length; - returnItems = itemsArray; + var itemArray = itemsList.ToArray(); + totalCount = itemArray.Length; + returnItems = itemArray; } else { - returnItems = itemsList.ToArray(); + returnItems = itemsList; } if (limit.HasValue) { - returnItems = returnItems.Skip(startIndex ?? 0).Take(limit.Value).ToArray(); + returnItems = returnItems.Skip(startIndex ?? 0).Take(limit.Value); } else if (startIndex.HasValue) { - returnItems = returnItems.Skip(startIndex.Value).ToArray(); + returnItems = returnItems.Skip(startIndex.Value); } return new QueryResult<BaseItem> diff --git a/MediaBrowser.Controller/Entities/IHasTrailers.cs b/MediaBrowser.Controller/Entities/IHasTrailers.cs index 3bdb9b64a..dd8e3c45f 100644 --- a/MediaBrowser.Controller/Entities/IHasTrailers.cs +++ b/MediaBrowser.Controller/Entities/IHasTrailers.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; using MediaBrowser.Model.Entities; namespace MediaBrowser.Controller.Entities @@ -11,29 +10,82 @@ namespace MediaBrowser.Controller.Entities /// Gets or sets the remote trailers. /// </summary> /// <value>The remote trailers.</value> - MediaUrl[] RemoteTrailers { get; set; } + IReadOnlyList<MediaUrl> RemoteTrailers { get; set; } /// <summary> /// Gets or sets the local trailer ids. /// </summary> /// <value>The local trailer ids.</value> - Guid[] LocalTrailerIds { get; set; } - Guid[] RemoteTrailerIds { get; set; } + IReadOnlyList<Guid> LocalTrailerIds { get; set; } + + /// <summary> + /// Gets or sets the remote trailer ids. + /// </summary> + /// <value>The remote trailer ids.</value> + IReadOnlyList<Guid> RemoteTrailerIds { get; set; } + Guid Id { get; set; } } + /// <summary> + /// Class providing extension methods for working with <see cref="IHasTrailers" />. + /// </summary> public static class HasTrailerExtensions { /// <summary> + /// Gets the trailer count. + /// </summary> + /// <returns><see cref="IReadOnlyList{Guid}" />.</returns> + public static int GetTrailerCount(this IHasTrailers item) + => item.LocalTrailerIds.Count + item.RemoteTrailerIds.Count; + + /// <summary> /// Gets the trailer ids. /// </summary> - /// <returns>List<Guid>.</returns> - public static List<Guid> GetTrailerIds(this IHasTrailers item) + /// <returns><see cref="IReadOnlyList{Guid}" />.</returns> + public static IReadOnlyList<Guid> GetTrailerIds(this IHasTrailers item) { - var list = item.LocalTrailerIds.ToList(); - list.AddRange(item.RemoteTrailerIds); - return list; + var localIds = item.LocalTrailerIds; + var remoteIds = item.RemoteTrailerIds; + + var all = new Guid[localIds.Count + remoteIds.Count]; + var index = 0; + foreach (var id in localIds) + { + all[index++] = id; + } + + foreach (var id in remoteIds) + { + all[index++] = id; + } + + return all; } + /// <summary> + /// Gets the trailers. + /// </summary> + /// <returns><see cref="IReadOnlyList{BaseItem}" />.</returns> + public static IReadOnlyList<BaseItem> GetTrailers(this IHasTrailers item) + { + var localIds = item.LocalTrailerIds; + var remoteIds = item.RemoteTrailerIds; + var libraryManager = BaseItem.LibraryManager; + + var all = new BaseItem[localIds.Count + remoteIds.Count]; + var index = 0; + foreach (var id in localIds) + { + all[index++] = libraryManager.GetItemById(id); + } + + foreach (var id in remoteIds) + { + all[index++] = libraryManager.GetItemById(id); + } + + return all; + } } } diff --git a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs index a532b5ee9..e7ac2a05c 100644 --- a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs +++ b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs @@ -33,8 +33,11 @@ namespace MediaBrowser.Controller.Entities.Movies [IgnoreDataMember] public override bool SupportsPeople => true; - public Guid[] LocalTrailerIds { get; set; } - public Guid[] RemoteTrailerIds { get; set; } + /// <inheritdoc /> + public IReadOnlyList<Guid> LocalTrailerIds { get; set; } + + /// <inheritdoc /> + public IReadOnlyList<Guid> RemoteTrailerIds { get; set; } /// <summary> /// Gets or sets the display order. @@ -61,7 +64,8 @@ namespace MediaBrowser.Controller.Entities.Movies { return base.GetNonCachedChildren(directoryService); } - return new List<BaseItem>(); + + return Enumerable.Empty<BaseItem>(); } protected override List<BaseItem> LoadChildren() diff --git a/MediaBrowser.Controller/Entities/Movies/Movie.cs b/MediaBrowser.Controller/Entities/Movies/Movie.cs index 20c5b3521..184528fdc 100644 --- a/MediaBrowser.Controller/Entities/Movies/Movie.cs +++ b/MediaBrowser.Controller/Entities/Movies/Movie.cs @@ -27,8 +27,11 @@ namespace MediaBrowser.Controller.Entities.Movies RemoteTrailerIds = Array.Empty<Guid>(); } - public Guid[] LocalTrailerIds { get; set; } - public Guid[] RemoteTrailerIds { get; set; } + /// <inheritdoc /> + public IReadOnlyList<Guid> LocalTrailerIds { get; set; } + + /// <inheritdoc /> + public IReadOnlyList<Guid> RemoteTrailerIds { get; set; } /// <summary> /// Gets or sets the name of the TMDB collection. diff --git a/MediaBrowser.Controller/Entities/MusicVideo.cs b/MediaBrowser.Controller/Entities/MusicVideo.cs index 5bf082b7e..94fe11e9d 100644 --- a/MediaBrowser.Controller/Entities/MusicVideo.cs +++ b/MediaBrowser.Controller/Entities/MusicVideo.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Configuration; @@ -8,17 +9,15 @@ namespace MediaBrowser.Controller.Entities { public class MusicVideo : Video, IHasArtist, IHasMusicGenres, IHasLookupInfo<MusicVideoInfo> { + /// <inheritdoc /> [IgnoreDataMember] - public string[] Artists { get; set; } + public IReadOnlyList<string> Artists { get; set; } public MusicVideo() { Artists = Array.Empty<string>(); } - [IgnoreDataMember] - public string[] AllArtists => Artists; - public override UnratedItem GetBlockUnratedType() { return UnratedItem.Music; diff --git a/MediaBrowser.Controller/Entities/TV/Episode.cs b/MediaBrowser.Controller/Entities/TV/Episode.cs index fb29c07b0..e67c00fed 100644 --- a/MediaBrowser.Controller/Entities/TV/Episode.cs +++ b/MediaBrowser.Controller/Entities/TV/Episode.cs @@ -23,8 +23,11 @@ namespace MediaBrowser.Controller.Entities.TV RemoteTrailerIds = Array.Empty<Guid>(); } - public Guid[] LocalTrailerIds { get; set; } - public Guid[] RemoteTrailerIds { get; set; } + /// <inheritdoc /> + public IReadOnlyList<Guid> LocalTrailerIds { get; set; } + + /// <inheritdoc /> + public IReadOnlyList<Guid> RemoteTrailerIds { get; set; } /// <summary> /// Gets the season in which it aired. diff --git a/MediaBrowser.Controller/Entities/TV/Series.cs b/MediaBrowser.Controller/Entities/TV/Series.cs index 1aacc13c9..a50da9b0a 100644 --- a/MediaBrowser.Controller/Entities/TV/Series.cs +++ b/MediaBrowser.Controller/Entities/TV/Series.cs @@ -46,8 +46,11 @@ namespace MediaBrowser.Controller.Entities.TV [IgnoreDataMember] public override bool SupportsPeople => true; - public Guid[] LocalTrailerIds { get; set; } - public Guid[] RemoteTrailerIds { get; set; } + /// <inheritdoc /> + public IReadOnlyList<Guid> LocalTrailerIds { get; set; } + + /// <inheritdoc /> + public IReadOnlyList<Guid> RemoteTrailerIds { get; set; } /// <summary> /// airdate, dvd or absolute diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs index f99df6c7c..e02c387e4 100644 --- a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs +++ b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs @@ -221,7 +221,7 @@ namespace MediaBrowser.Controller.LiveTv /// <param name="fields">The fields.</param> /// <param name="user">The user.</param> /// <returns>Task.</returns> - Task AddInfoToProgramDto(List<Tuple<BaseItem, BaseItemDto>> programs, ItemFields[] fields, User user = null); + Task AddInfoToProgramDto(IReadOnlyCollection<(BaseItem, BaseItemDto)> programs, ItemFields[] fields, User user = null); /// <summary> /// Saves the tuner host. @@ -258,7 +258,7 @@ namespace MediaBrowser.Controller.LiveTv /// <param name="items">The items.</param> /// <param name="options">The options.</param> /// <param name="user">The user.</param> - void AddChannelInfo(List<Tuple<BaseItemDto, LiveTvChannel>> items, DtoOptions options, User user); + void AddChannelInfo(IReadOnlyCollection<(BaseItemDto, LiveTvChannel)> items, DtoOptions options, User user); Task<List<ChannelInfo>> GetChannelsForListingsProvider(string id, CancellationToken cancellationToken); Task<List<ChannelInfo>> GetChannelsFromListingsProviderData(string id, CancellationToken cancellationToken); diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 87874001a..963091673 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; +using System.Text; using System.Threading; using MediaBrowser.Controller.Entities; using MediaBrowser.Model.Configuration; @@ -22,8 +23,17 @@ namespace MediaBrowser.Controller.MediaEncoding private readonly IMediaEncoder _mediaEncoder; private readonly IFileSystem _fileSystem; private readonly ISubtitleEncoder _subtitleEncoder; - // private readonly IApplicationPaths _appPaths; - // private readonly IAssemblyInfo _assemblyInfo; + + private static readonly string[] _videoProfiles = new[] + { + "ConstrainedBaseline", + "Baseline", + "Extended", + "Main", + "High", + "ProgressiveHigh", + "ConstrainedHigh" + }; public EncodingHelper(IMediaEncoder mediaEncoder, IFileSystem fileSystem, ISubtitleEncoder subtitleEncoder) { @@ -33,14 +43,10 @@ namespace MediaBrowser.Controller.MediaEncoding } public string GetH264Encoder(EncodingJobInfo state, EncodingOptions encodingOptions) - { - return GetH264OrH265Encoder("libx264", "h264", state, encodingOptions); - } + => GetH264OrH265Encoder("libx264", "h264", state, encodingOptions); public string GetH265Encoder(EncodingJobInfo state, EncodingOptions encodingOptions) - { - return GetH264OrH265Encoder("libx265", "hevc", state, encodingOptions); - } + => GetH264OrH265Encoder("libx265", "hevc", state, encodingOptions); private string GetH264OrH265Encoder(string defaultEncoder, string hwEncoder, EncodingJobInfo state, EncodingOptions encodingOptions) { @@ -64,19 +70,17 @@ namespace MediaBrowser.Controller.MediaEncoding }; if (!string.IsNullOrEmpty(hwType) - && encodingOptions.EnableHardwareEncoding && codecMap.ContainsKey(hwType)) + && encodingOptions.EnableHardwareEncoding + && codecMap.ContainsKey(hwType) + && CheckVaapi(state, hwType, encodingOptions)) { - if (CheckVaapi(state, hwType, encodingOptions)) - { - var preferredEncoder = codecMap[hwType]; + var preferredEncoder = codecMap[hwType]; - if (_mediaEncoder.SupportsEncoder(preferredEncoder)) - { - return preferredEncoder; - } + if (_mediaEncoder.SupportsEncoder(preferredEncoder)) + { + return preferredEncoder; } } - } // Avoid performing a second attempt when the first one @@ -106,15 +110,13 @@ namespace MediaBrowser.Controller.MediaEncoding { var videoStream = state.VideoStream; - if (videoStream != null) + // vaapi will throw an error with this input + // [vaapi @ 0x7faed8000960] No VAAPI support for codec mpeg4 profile -99. + if (string.Equals(videoStream?.Codec, "mpeg4", StringComparison.OrdinalIgnoreCase)) { - // vaapi will throw an error with this input - // [vaapi @ 0x7faed8000960] No VAAPI support for codec mpeg4 profile -99. - if (string.Equals(videoStream.Codec, "mpeg4", StringComparison.OrdinalIgnoreCase)) - { - return false; - } + return false; } + return true; } @@ -127,23 +129,27 @@ namespace MediaBrowser.Controller.MediaEncoding if (!string.IsNullOrEmpty(codec)) { - if (string.Equals(codec, "h265", StringComparison.OrdinalIgnoreCase) || - string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(codec, "h265", StringComparison.OrdinalIgnoreCase) + || string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase)) { return GetH265Encoder(state, encodingOptions); } + if (string.Equals(codec, "h264", StringComparison.OrdinalIgnoreCase)) { return GetH264Encoder(state, encodingOptions); } + if (string.Equals(codec, "vpx", StringComparison.OrdinalIgnoreCase)) { return "libvpx"; } + if (string.Equals(codec, "wmv", StringComparison.OrdinalIgnoreCase)) { return "wmv2"; } + if (string.Equals(codec, "theora", StringComparison.OrdinalIgnoreCase)) { return "libtheora"; @@ -162,9 +168,7 @@ namespace MediaBrowser.Controller.MediaEncoding /// <returns>System.String.</returns> public string GetUserAgentParam(EncodingJobInfo state) { - string useragent = null; - - state.RemoteHttpHeaders.TryGetValue("User-Agent", out useragent); + state.RemoteHttpHeaders.TryGetValue("User-Agent", out string useragent); if (!string.IsNullOrEmpty(useragent)) { @@ -193,50 +197,62 @@ namespace MediaBrowser.Controller.MediaEncoding { return null; } + if (string.Equals(container, "wmv", StringComparison.OrdinalIgnoreCase)) { return null; } + if (string.Equals(container, "mts", StringComparison.OrdinalIgnoreCase)) { return null; } + if (string.Equals(container, "vob", StringComparison.OrdinalIgnoreCase)) { return null; } + if (string.Equals(container, "mpg", StringComparison.OrdinalIgnoreCase)) { return null; } + if (string.Equals(container, "mpeg", StringComparison.OrdinalIgnoreCase)) { return null; } + if (string.Equals(container, "rec", StringComparison.OrdinalIgnoreCase)) { return null; } + if (string.Equals(container, "dvr-ms", StringComparison.OrdinalIgnoreCase)) { return null; } + if (string.Equals(container, "ogm", StringComparison.OrdinalIgnoreCase)) { return null; } + if (string.Equals(container, "divx", StringComparison.OrdinalIgnoreCase)) { return null; } + if (string.Equals(container, "tp", StringComparison.OrdinalIgnoreCase)) { return null; } + if (string.Equals(container, "rmvb", StringComparison.OrdinalIgnoreCase)) { return null; } + if (string.Equals(container, "rtp", StringComparison.OrdinalIgnoreCase)) { return null; @@ -264,10 +280,12 @@ namespace MediaBrowser.Controller.MediaEncoding { return null; } + if (string.Equals(codec, "aac_latm", StringComparison.OrdinalIgnoreCase)) { return null; } + if (string.Equals(codec, "eac3", StringComparison.OrdinalIgnoreCase)) { return null; @@ -292,30 +310,37 @@ namespace MediaBrowser.Controller.MediaEncoding { return "mp3"; } + if (string.Equals(ext, ".aac", StringComparison.OrdinalIgnoreCase)) { return "aac"; } + if (string.Equals(ext, ".wma", StringComparison.OrdinalIgnoreCase)) { return "wma"; } + if (string.Equals(ext, ".ogg", StringComparison.OrdinalIgnoreCase)) { return "vorbis"; } + if (string.Equals(ext, ".oga", StringComparison.OrdinalIgnoreCase)) { return "vorbis"; } + if (string.Equals(ext, ".ogv", StringComparison.OrdinalIgnoreCase)) { return "vorbis"; } + if (string.Equals(ext, ".webm", StringComparison.OrdinalIgnoreCase)) { return "vorbis"; } + if (string.Equals(ext, ".webma", StringComparison.OrdinalIgnoreCase)) { return "vorbis"; @@ -337,14 +362,17 @@ namespace MediaBrowser.Controller.MediaEncoding { return "wmv"; } + if (string.Equals(ext, ".webm", StringComparison.OrdinalIgnoreCase)) { return "vpx"; } + if (string.Equals(ext, ".ogg", StringComparison.OrdinalIgnoreCase) || string.Equals(ext, ".ogv", StringComparison.OrdinalIgnoreCase)) { return "theora"; } + if (string.Equals(ext, ".m3u8", StringComparison.OrdinalIgnoreCase) || string.Equals(ext, ".ts", StringComparison.OrdinalIgnoreCase)) { return "h264"; @@ -355,19 +383,9 @@ namespace MediaBrowser.Controller.MediaEncoding public int GetVideoProfileScore(string profile) { - var list = new[] - { - "ConstrainedBaseline", - "Baseline", - "Extended", - "Main", - "High", - "ProgressiveHigh", - "ConstrainedHigh" - }; - // strip spaces because they may be stripped out on the query string - return Array.FindIndex(list, t => string.Equals(t, profile.Replace(" ", ""), StringComparison.OrdinalIgnoreCase)); + profile = profile.Replace(" ", ""); + return Array.FindIndex(_videoProfiles, x => string.Equals(x, profile, StringComparison.OrdinalIgnoreCase)); } public string GetInputPathArgument(EncodingJobInfo state) @@ -375,14 +393,19 @@ namespace MediaBrowser.Controller.MediaEncoding var protocol = state.InputProtocol; var mediaPath = state.MediaPath ?? string.Empty; - var inputPath = new[] { mediaPath }; - - if (state.IsInputVideo) + string[] inputPath; + if (state.IsInputVideo + && !(state.VideoType == VideoType.Iso && state.IsoMount == null)) { - if (!(state.VideoType == VideoType.Iso && state.IsoMount == null)) - { - inputPath = MediaEncoderHelpers.GetInputArgument(_fileSystem, mediaPath, state.InputProtocol, state.IsoMount, state.PlayableStreamFileNames); - } + inputPath = MediaEncoderHelpers.GetInputArgument( + _fileSystem, + mediaPath, + state.IsoMount, + state.PlayableStreamFileNames); + } + else + { + inputPath = new[] { mediaPath }; } return _mediaEncoder.GetInputArgument(inputPath, protocol); @@ -401,18 +424,22 @@ namespace MediaBrowser.Controller.MediaEncoding { return "aac -strict experimental"; } + if (string.Equals(codec, "mp3", StringComparison.OrdinalIgnoreCase)) { return "libmp3lame"; } + if (string.Equals(codec, "vorbis", StringComparison.OrdinalIgnoreCase)) { return "libvorbis"; } + if (string.Equals(codec, "wma", StringComparison.OrdinalIgnoreCase)) { return "wmav2"; } + if (string.Equals(codec, "opus", StringComparison.OrdinalIgnoreCase)) { return "libopus"; @@ -426,54 +453,59 @@ namespace MediaBrowser.Controller.MediaEncoding /// </summary> public string GetInputArgument(EncodingJobInfo state, EncodingOptions encodingOptions) { - var request = state.BaseRequest; - - var arg = string.Format("-i {0}", GetInputPathArgument(state)); + var arg = new StringBuilder(); - if (state.SubtitleStream != null && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode) + if (state.IsVideoRequest + && string.Equals(encodingOptions.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase)) { - if (state.SubtitleStream.IsExternal && !state.SubtitleStream.IsTextSubtitleStream) + var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode; + var hwOutputFormat = "vaapi"; + + if (hasGraphicalSubs) { - if (state.VideoStream != null && state.VideoStream.Width.HasValue) - { - // This is hacky but not sure how to get the exact subtitle resolution - int height = Convert.ToInt32((double)state.VideoStream.Width.Value / 16.0 * 9.0); + hwOutputFormat = "yuv420p"; + } - arg += string.Format(" -canvas_size {0}:{1}", state.VideoStream.Width.Value.ToString(CultureInfo.InvariantCulture), height.ToString(CultureInfo.InvariantCulture)); - } + arg.Append("-hwaccel vaapi -hwaccel_output_format ") + .Append(hwOutputFormat) + .Append(" -vaapi_device ") + .Append(encodingOptions.VaapiDevice) + .Append(' '); + } - var subtitlePath = state.SubtitleStream.Path; + arg.Append("-i ") + .Append(GetInputPathArgument(state)); - if (string.Equals(Path.GetExtension(subtitlePath), ".sub", StringComparison.OrdinalIgnoreCase)) - { - var idxFile = Path.ChangeExtension(subtitlePath, ".idx"); - if (File.Exists(idxFile)) - { - subtitlePath = idxFile; - } - } + if (state.SubtitleStream != null + && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode + && state.SubtitleStream.IsExternal && !state.SubtitleStream.IsTextSubtitleStream) + { + if (state.VideoStream != null && state.VideoStream.Width.HasValue) + { + // This is hacky but not sure how to get the exact subtitle resolution + int height = Convert.ToInt32(state.VideoStream.Width.Value / 16.0 * 9.0); - arg += " -i \"" + subtitlePath + "\""; + arg.Append(" -canvas_size ") + .Append(state.VideoStream.Width.Value.ToString(CultureInfo.InvariantCulture)) + .Append(':') + .Append(height.ToString(CultureInfo.InvariantCulture)); } - } - if (state.IsVideoRequest) - { - if (GetVideoEncoder(state, encodingOptions).IndexOf("vaapi", StringComparison.OrdinalIgnoreCase) != -1) - { - var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode; - var hwOutputFormat = "vaapi"; + var subtitlePath = state.SubtitleStream.Path; - if (hasGraphicalSubs) + if (string.Equals(Path.GetExtension(subtitlePath), ".sub", StringComparison.OrdinalIgnoreCase)) + { + var idxFile = Path.ChangeExtension(subtitlePath, ".idx"); + if (File.Exists(idxFile)) { - hwOutputFormat = "yuv420p"; + subtitlePath = idxFile; } - - arg = "-hwaccel vaapi -hwaccel_output_format " + hwOutputFormat + " -vaapi_device " + encodingOptions.VaapiDevice + " " + arg; } + + arg.Append(" -i \"").Append(subtitlePath).Append('\"'); } - return arg.Trim(); + return arg.ToString(); } /// <summary> @@ -485,16 +517,16 @@ namespace MediaBrowser.Controller.MediaEncoding { var codec = stream.Codec ?? string.Empty; - return codec.IndexOf("264", StringComparison.OrdinalIgnoreCase) != -1 || - codec.IndexOf("avc", StringComparison.OrdinalIgnoreCase) != -1; + return codec.IndexOf("264", StringComparison.OrdinalIgnoreCase) != -1 + || codec.IndexOf("avc", StringComparison.OrdinalIgnoreCase) != -1; } public bool IsH265(MediaStream stream) { var codec = stream.Codec ?? string.Empty; - return codec.IndexOf("265", StringComparison.OrdinalIgnoreCase) != -1 || - codec.IndexOf("hevc", StringComparison.OrdinalIgnoreCase) != -1; + return codec.IndexOf("265", StringComparison.OrdinalIgnoreCase) != -1 + || codec.IndexOf("hevc", StringComparison.OrdinalIgnoreCase) != -1; } public string GetBitStreamArgs(MediaStream stream) @@ -523,27 +555,38 @@ namespace MediaBrowser.Controller.MediaEncoding { // With vpx when crf is used, b:v becomes a max rate // https://trac.ffmpeg.org/wiki/vpxEncodingGuide. - return string.Format(" -maxrate:v {0} -bufsize:v {1} -b:v {0}", bitrate.Value.ToString(_usCulture), (bitrate.Value * 2).ToString(_usCulture)); + return string.Format( + CultureInfo.InvariantCulture, + " -maxrate:v {0} -bufsize:v {1} -b:v {0}", + bitrate.Value, + bitrate.Value * 2); } if (string.Equals(videoCodec, "msmpeg4", StringComparison.OrdinalIgnoreCase)) { - return string.Format(" -b:v {0}", bitrate.Value.ToString(_usCulture)); + return string.Format( + CultureInfo.InvariantCulture, + " -b:v {0}", + bitrate.Value); } if (string.Equals(videoCodec, "libx264", StringComparison.OrdinalIgnoreCase) || string.Equals(videoCodec, "libx265", StringComparison.OrdinalIgnoreCase)) { // h264 - return string.Format(" -maxrate {0} -bufsize {1}", - bitrate.Value.ToString(_usCulture), - (bitrate.Value * 2).ToString(_usCulture)); + return string.Format( + CultureInfo.InvariantCulture, + " -maxrate {0} -bufsize {1}", + bitrate.Value, + bitrate.Value * 2); } // h264 - return string.Format(" -b:v {0} -maxrate {0} -bufsize {1}", - bitrate.Value.ToString(_usCulture), - (bitrate.Value * 2).ToString(_usCulture)); + return string.Format( + CultureInfo.InvariantCulture, + " -b:v {0} -maxrate {0} -bufsize {1}", + bitrate.Value, + bitrate.Value * 2); } return string.Empty; @@ -576,7 +619,7 @@ namespace MediaBrowser.Controller.MediaEncoding // hls always copies timestamps var setPtsParam = state.CopyTimestamps || state.TranscodingType != TranscodingJobType.Progressive ? string.Empty - : string.Format(",setpts=PTS -{0}/TB", seconds.ToString(_usCulture)); + : string.Format(CultureInfo.InvariantCulture, ",setpts=PTS -{0}/TB", seconds); // TODO // var fallbackFontPath = Path.Combine(_appPaths.ProgramDataPath, "fonts", "DroidSansFallback.ttf"); @@ -684,6 +727,7 @@ namespace MediaBrowser.Controller.MediaEncoding { encodeCrf = encodingOptions.H265Crf; } + if (encodeCrf >= 0 && encodeCrf <= 51) { param += " -crf " + encodeCrf.ToString(CultureInfo.InvariantCulture); @@ -695,12 +739,11 @@ namespace MediaBrowser.Controller.MediaEncoding { defaultCrf = "28"; } + param += " -crf " + defaultCrf; } } - - // h264 (h264_qsv) - else if (string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase)) + else if (string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase)) // h264 (h264_qsv) { string[] valid_h264_qsv = { "veryslow", "slower", "slow", "medium", "fast", "faster", "veryfast" }; @@ -716,10 +759,8 @@ namespace MediaBrowser.Controller.MediaEncoding param += " -look_ahead 0"; } - - // h264 (h264_nvenc) - else if (string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase) || - string.Equals(videoEncoder, "hevc_nvenc", StringComparison.OrdinalIgnoreCase)) + else if (string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase) // h264 (h264_nvenc) + || string.Equals(videoEncoder, "hevc_nvenc", StringComparison.OrdinalIgnoreCase)) { switch (encodingOptions.EncoderPreset) { @@ -750,9 +791,7 @@ namespace MediaBrowser.Controller.MediaEncoding break; } } - - // webm - else if (string.Equals(videoEncoder, "libvpx", StringComparison.OrdinalIgnoreCase)) + else if (string.Equals(videoEncoder, "libvpx", StringComparison.OrdinalIgnoreCase)) // webm { // Values 0-3, 0 being highest quality but slower var profileScore = 0; @@ -778,18 +817,14 @@ namespace MediaBrowser.Controller.MediaEncoding qmin, qmax); } - else if (string.Equals(videoEncoder, "mpeg4", StringComparison.OrdinalIgnoreCase)) { param += "-mbd rd -flags +mv4+aic -trellis 2 -cmp 2 -subcmp 2 -bf 2"; } - - // asf/wmv - else if (string.Equals(videoEncoder, "wmv2", StringComparison.OrdinalIgnoreCase)) + else if (string.Equals(videoEncoder, "wmv2", StringComparison.OrdinalIgnoreCase)) // asf/wmv { param += "-qmin 2"; } - else if (string.Equals(videoEncoder, "msmpeg4", StringComparison.OrdinalIgnoreCase)) { param += "-mbd 2"; @@ -805,21 +840,21 @@ namespace MediaBrowser.Controller.MediaEncoding var targetVideoCodec = state.ActualOutputVideoCodec; - var request = state.BaseRequest; var profile = state.GetRequestedProfiles(targetVideoCodec).FirstOrDefault(); // vaapi does not support Baseline profile, force Constrained Baseline in this case, // which is compatible (and ugly) - if (string.Equals(videoEncoder, "h264_vaapi", StringComparison.OrdinalIgnoreCase) && - profile != null && profile.ToLowerInvariant().Contains("baseline")) + if (string.Equals(videoEncoder, "h264_vaapi", StringComparison.OrdinalIgnoreCase) + && profile != null + && profile.IndexOf("baseline", StringComparison.OrdinalIgnoreCase) != -1) { - profile = "constrained_baseline"; + profile = "constrained_baseline"; } if (!string.IsNullOrEmpty(profile)) { - if (!string.Equals(videoEncoder, "h264_omx", StringComparison.OrdinalIgnoreCase) && - !string.Equals(videoEncoder, "h264_v4l2m2m", StringComparison.OrdinalIgnoreCase)) + if (!string.Equals(videoEncoder, "h264_omx", StringComparison.OrdinalIgnoreCase) + && !string.Equals(videoEncoder, "h264_v4l2m2m", StringComparison.OrdinalIgnoreCase)) { // not supported by h264_omx param += " -profile:v " + profile; @@ -834,9 +869,9 @@ namespace MediaBrowser.Controller.MediaEncoding // h264_qsv and h264_nvenc expect levels to be expressed as a decimal. libx264 supports decimal and non-decimal format // also needed for libx264 due to https://trac.ffmpeg.org/ticket/3307 - if (string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase) || - string.Equals(videoEncoder, "libx264", StringComparison.OrdinalIgnoreCase) || - string.Equals(videoEncoder, "libx265", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase) + || string.Equals(videoEncoder, "libx264", StringComparison.OrdinalIgnoreCase) + || string.Equals(videoEncoder, "libx265", StringComparison.OrdinalIgnoreCase)) { switch (level) { @@ -872,11 +907,11 @@ namespace MediaBrowser.Controller.MediaEncoding break; } } - // nvenc doesn't decode with param -level set ?! - else if (string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase) || - string.Equals(videoEncoder, "hevc_nvenc", StringComparison.OrdinalIgnoreCase)) + else if (string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase) + || string.Equals(videoEncoder, "hevc_nvenc", StringComparison.OrdinalIgnoreCase)) { - // todo param += ""; + // nvenc doesn't decode with param -level set ?! + // TODO: } else if (!string.Equals(videoEncoder, "h264_omx", StringComparison.OrdinalIgnoreCase)) { @@ -894,10 +929,10 @@ namespace MediaBrowser.Controller.MediaEncoding // todo } - if (!string.Equals(videoEncoder, "h264_omx", StringComparison.OrdinalIgnoreCase) && - !string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase) && - !string.Equals(videoEncoder, "h264_vaapi", StringComparison.OrdinalIgnoreCase) && - !string.Equals(videoEncoder, "h264_v4l2m2m", StringComparison.OrdinalIgnoreCase)) + if (!string.Equals(videoEncoder, "h264_omx", StringComparison.OrdinalIgnoreCase) + && !string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase) + && !string.Equals(videoEncoder, "h264_vaapi", StringComparison.OrdinalIgnoreCase) + && !string.Equals(videoEncoder, "h264_v4l2m2m", StringComparison.OrdinalIgnoreCase)) { param = "-pix_fmt yuv420p " + param; } @@ -919,12 +954,10 @@ namespace MediaBrowser.Controller.MediaEncoding return false; } - if (videoStream.IsInterlaced) + if (videoStream.IsInterlaced + && state.DeInterlace(videoStream.Codec, false)) { - if (state.DeInterlace(videoStream.Codec, false)) - { - return false; - } + return false; } if (videoStream.IsAnamorphic ?? false) @@ -936,24 +969,23 @@ namespace MediaBrowser.Controller.MediaEncoding } // Can't stream copy if we're burning in subtitles - if (request.SubtitleStreamIndex.HasValue) + if (request.SubtitleStreamIndex.HasValue + && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode) { - if (state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode) - { - return false; - } + return false; } - if (string.Equals("h264", videoStream.Codec, StringComparison.OrdinalIgnoreCase)) + if (string.Equals("h264", videoStream.Codec, StringComparison.OrdinalIgnoreCase) + && videoStream.IsAVC.HasValue + && !videoStream.IsAVC.Value + && request.RequireAvc) { - if (videoStream.IsAVC.HasValue && !videoStream.IsAVC.Value && request.RequireAvc) - { - return false; - } + return false; } // Source and target codecs must match - if (string.IsNullOrEmpty(videoStream.Codec) || !state.SupportedVideoCodecs.Contains(videoStream.Codec, StringComparer.OrdinalIgnoreCase)) + if (string.IsNullOrEmpty(videoStream.Codec) + || !state.SupportedVideoCodecs.Contains(videoStream.Codec, StringComparer.OrdinalIgnoreCase)) { return false; } @@ -983,21 +1015,17 @@ namespace MediaBrowser.Controller.MediaEncoding } // Video width must fall within requested value - if (request.MaxWidth.HasValue) + if (request.MaxWidth.HasValue + && (!videoStream.Width.HasValue || videoStream.Width.Value > request.MaxWidth.Value)) { - if (!videoStream.Width.HasValue || videoStream.Width.Value > request.MaxWidth.Value) - { - return false; - } + return false; } // Video height must fall within requested value - if (request.MaxHeight.HasValue) + if (request.MaxHeight.HasValue + && (!videoStream.Height.HasValue || videoStream.Height.Value > request.MaxHeight.Value)) { - if (!videoStream.Height.HasValue || videoStream.Height.Value > request.MaxHeight.Value) - { - return false; - } + return false; } // Video framerate must fall within requested value @@ -1013,12 +1041,10 @@ namespace MediaBrowser.Controller.MediaEncoding } // Video bitrate must fall within requested value - if (request.VideoBitRate.HasValue) + if (request.VideoBitRate.HasValue + && (!videoStream.BitRate.HasValue || videoStream.BitRate.Value > request.VideoBitRate.Value)) { - if (!videoStream.BitRate.HasValue || videoStream.BitRate.Value > request.VideoBitRate.Value) - { - return false; - } + return false; } var maxBitDepth = state.GetRequestedVideoBitDepth(videoStream.Codec); @@ -1031,35 +1057,31 @@ namespace MediaBrowser.Controller.MediaEncoding } var maxRefFrames = state.GetRequestedMaxRefFrames(videoStream.Codec); - if (maxRefFrames.HasValue) + if (maxRefFrames.HasValue + && videoStream.RefFrames.HasValue && videoStream.RefFrames.Value > maxRefFrames.Value) { - if (videoStream.RefFrames.HasValue && videoStream.RefFrames.Value > maxRefFrames.Value) - { - return false; - } + return false; } // If a specific level was requested, the source must match or be less than var level = state.GetRequestedLevel(videoStream.Codec); - if (!string.IsNullOrEmpty(level)) + if (!string.IsNullOrEmpty(level) + && double.TryParse(level, NumberStyles.Any, _usCulture, out var requestLevel)) { - if (double.TryParse(level, NumberStyles.Any, _usCulture, out var requestLevel)) + if (!videoStream.Level.HasValue) { - if (!videoStream.Level.HasValue) - { - //return false; - } + //return false; + } - if (videoStream.Level.HasValue && videoStream.Level.Value > requestLevel) - { - return false; - } + if (videoStream.Level.HasValue && videoStream.Level.Value > requestLevel) + { + return false; } } - if (string.Equals(state.InputContainer, "avi", StringComparison.OrdinalIgnoreCase) && - string.Equals(videoStream.Codec, "h264", StringComparison.OrdinalIgnoreCase) && - !(videoStream.IsAVC ?? false)) + if (string.Equals(state.InputContainer, "avi", StringComparison.OrdinalIgnoreCase) + && string.Equals(videoStream.Codec, "h264", StringComparison.OrdinalIgnoreCase) + && !(videoStream.IsAVC ?? false)) { // see Coach S01E01 - Kelly and the Professor(0).avi return false; @@ -1068,7 +1090,7 @@ namespace MediaBrowser.Controller.MediaEncoding return request.EnableAutoStreamCopy; } - public bool CanStreamCopyAudio(EncodingJobInfo state, MediaStream audioStream, string[] supportedAudioCodecs) + public bool CanStreamCopyAudio(EncodingJobInfo state, MediaStream audioStream, IEnumerable<string> supportedAudioCodecs) { var request = state.BaseRequest; @@ -1078,16 +1100,16 @@ namespace MediaBrowser.Controller.MediaEncoding } var maxBitDepth = state.GetRequestedAudioBitDepth(audioStream.Codec); - if (maxBitDepth.HasValue) + if (maxBitDepth.HasValue + && audioStream.BitDepth.HasValue + && audioStream.BitDepth.Value > maxBitDepth.Value) { - if (audioStream.BitDepth.HasValue && audioStream.BitDepth.Value > maxBitDepth.Value) - { - return false; - } + return false; } // Source and target codecs must match - if (string.IsNullOrEmpty(audioStream.Codec) || !supportedAudioCodecs.Contains(audioStream.Codec, StringComparer.OrdinalIgnoreCase)) + if (string.IsNullOrEmpty(audioStream.Codec) + || !supportedAudioCodecs.Contains(audioStream.Codec, StringComparer.OrdinalIgnoreCase)) { return false; } @@ -1100,6 +1122,7 @@ namespace MediaBrowser.Controller.MediaEncoding { return false; } + if (audioStream.Channels.Value > channels.Value) { return false; @@ -1113,6 +1136,7 @@ namespace MediaBrowser.Controller.MediaEncoding { return false; } + if (audioStream.SampleRate.Value > request.AudioSampleRate.Value) { return false; @@ -1126,6 +1150,7 @@ namespace MediaBrowser.Controller.MediaEncoding { return false; } + if (audioStream.BitRate.Value > request.AudioBitRate.Value) { return false; @@ -1141,17 +1166,17 @@ namespace MediaBrowser.Controller.MediaEncoding if (videoStream != null) { - var isUpscaling = request.Height.HasValue && videoStream.Height.HasValue && - request.Height.Value > videoStream.Height.Value && request.Width.HasValue && videoStream.Width.HasValue && - request.Width.Value > videoStream.Width.Value; + var isUpscaling = request.Height.HasValue + && videoStream.Height.HasValue + && request.Height.Value > videoStream.Height.Value + && request.Width.HasValue + && videoStream.Width.HasValue + && request.Width.Value > videoStream.Width.Value; // Don't allow bitrate increases unless upscaling - if (!isUpscaling) + if (!isUpscaling && bitrate.HasValue && videoStream.BitRate.HasValue) { - if (bitrate.HasValue && videoStream.BitRate.HasValue) - { - bitrate = GetMinBitrate(videoStream.BitRate.Value, bitrate.Value); - } + bitrate = GetMinBitrate(videoStream.BitRate.Value, bitrate.Value); } } @@ -1179,7 +1204,7 @@ namespace MediaBrowser.Controller.MediaEncoding } else if (sourceBitrate <= 3000000) { - sourceBitrate = Convert.ToInt32(sourceBitrate * 2); + sourceBitrate *= 2; } var bitrate = Math.Min(sourceBitrate, requestedBitrate); @@ -1189,12 +1214,13 @@ namespace MediaBrowser.Controller.MediaEncoding private static double GetVideoBitrateScaleFactor(string codec) { - if (StringHelper.EqualsIgnoreCase(codec, "h265") || - StringHelper.EqualsIgnoreCase(codec, "hevc") || - StringHelper.EqualsIgnoreCase(codec, "vp9")) + if (string.Equals(codec, "h265", StringComparison.OrdinalIgnoreCase) + || string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase) + || string.Equals(codec, "vp9", StringComparison.OrdinalIgnoreCase)) { return .5; } + return 1; } @@ -1221,9 +1247,7 @@ namespace MediaBrowser.Controller.MediaEncoding scaleFactor = Math.Max(scaleFactor, 2); } - var newBitrate = scaleFactor * bitrate; - - return Convert.ToInt32(newBitrate); + return Convert.ToInt32(scaleFactor * bitrate); } public int? GetAudioBitrateParam(BaseEncodingJobOptions request, MediaStream audioStream) @@ -1235,7 +1259,6 @@ namespace MediaBrowser.Controller.MediaEncoding // Don't encode any higher than this return Math.Min(384000, request.AudioBitRate.Value); - //return Math.Min(currentBitrate, request.AudioBitRate.Value); } return null; @@ -1248,12 +1271,14 @@ namespace MediaBrowser.Controller.MediaEncoding var filters = new List<string>(); // Boost volume to 200% when downsampling from 6ch to 2ch - if (channels.HasValue && channels.Value <= 2) + if (channels.HasValue + && channels.Value <= 2 + && state.AudioStream != null + && state.AudioStream.Channels.HasValue + && state.AudioStream.Channels.Value > 5 + && !encodingOptions.DownMixAudioBoost.Equals(1)) { - if (state.AudioStream != null && state.AudioStream.Channels.HasValue && state.AudioStream.Channels.Value > 5 && !encodingOptions.DownMixAudioBoost.Equals(1)) - { - filters.Add("volume=" + encodingOptions.DownMixAudioBoost.ToString(_usCulture)); - } + filters.Add("volume=" + encodingOptions.DownMixAudioBoost.ToString(_usCulture)); } var isCopyingTimestamps = state.CopyTimestamps || state.TranscodingType != TranscodingJobType.Progressive; @@ -1261,12 +1286,16 @@ namespace MediaBrowser.Controller.MediaEncoding { var seconds = TimeSpan.FromTicks(state.StartTimeTicks ?? 0).TotalSeconds; - filters.Add(string.Format("asetpts=PTS-{0}/TB", Math.Round(seconds).ToString(_usCulture))); + filters.Add( + string.Format( + CultureInfo.InvariantCulture, + "asetpts=PTS-{0}/TB", + Math.Round(seconds))); } if (filters.Count > 0) { - return "-af \"" + string.Join(",", filters.ToArray()) + "\""; + return "-af \"" + string.Join(",", filters) + "\""; } return string.Empty; @@ -1283,18 +1312,17 @@ namespace MediaBrowser.Controller.MediaEncoding { var request = state.BaseRequest; - var inputChannels = audioStream == null - ? null - : audioStream.Channels; + var inputChannels = audioStream?.Channels; if (inputChannels <= 0) { inputChannels = null; } - int? transcoderChannelLimit = null; var codec = outputAudioCodec ?? string.Empty; + + int? transcoderChannelLimit; if (codec.IndexOf("wma", StringComparison.OrdinalIgnoreCase) != -1) { // wmav2 currently only supports two channel output @@ -1343,6 +1371,7 @@ namespace MediaBrowser.Controller.MediaEncoding { return val2; } + if (!val2.HasValue) { return val1; @@ -1416,7 +1445,10 @@ namespace MediaBrowser.Controller.MediaEncoding if (state.VideoStream != null) { - args += string.Format("-map 0:{0}", state.VideoStream.Index); + args += string.Format( + CultureInfo.InvariantCulture, + "-map 0:{0}", + state.VideoStream.Index); } else { @@ -1426,7 +1458,10 @@ namespace MediaBrowser.Controller.MediaEncoding if (state.AudioStream != null) { - args += string.Format(" -map 0:{0}", state.AudioStream.Index); + args += string.Format( + CultureInfo.InvariantCulture, + " -map 0:{0}", + state.AudioStream.Index); } else @@ -1441,7 +1476,10 @@ namespace MediaBrowser.Controller.MediaEncoding } else if (subtitleMethod == SubtitleDeliveryMethod.Embed) { - args += string.Format(" -map 0:{0}", state.SubtitleStream.Index); + args += string.Format( + CultureInfo.InvariantCulture, + " -map 0:{0}", + state.SubtitleStream.Index); } else if (state.SubtitleStream.IsExternal && !state.SubtitleStream.IsTextSubtitleStream) { @@ -1493,7 +1531,10 @@ namespace MediaBrowser.Controller.MediaEncoding var request = state.BaseRequest; // Add resolution params, if specified - if (request.Width.HasValue || request.Height.HasValue || request.MaxHeight.HasValue || request.MaxWidth.HasValue) + if (request.Width.HasValue + || request.Height.HasValue + || request.MaxHeight.HasValue + || request.MaxWidth.HasValue) { outputSizeParam = GetOutputSizeParam(state, options, outputVideoCodec).TrimEnd('"'); @@ -1515,12 +1556,15 @@ namespace MediaBrowser.Controller.MediaEncoding } } - if (string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase) && outputSizeParam.Length == 0) + if (string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase) + && outputSizeParam.Length == 0) { outputSizeParam = ",format=nv12|vaapi,hwupload"; // Add parameters to use VAAPI with burn-in subttiles (GH issue #642) - if (state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode) { + if (state.SubtitleStream != null + && state.SubtitleStream.IsTextSubtitleStream + && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode) { outputSizeParam += ",hwmap=mode=read+write+direct"; } } @@ -1529,7 +1573,11 @@ namespace MediaBrowser.Controller.MediaEncoding if (state.VideoStream != null && state.VideoStream.Width.HasValue && state.VideoStream.Height.HasValue) { - videoSizeParam = string.Format("scale={0}:{1}", state.VideoStream.Width.Value.ToString(_usCulture), state.VideoStream.Height.Value.ToString(_usCulture)); + videoSizeParam = string.Format( + CultureInfo.InvariantCulture, + "scale={0}:{1}", + state.VideoStream.Width.Value, + state.VideoStream.Height.Value); videoSizeParam += ":force_original_aspect_ratio=decrease"; } @@ -1542,15 +1590,18 @@ namespace MediaBrowser.Controller.MediaEncoding ? 0 : state.SubtitleStream.Index; - return string.Format(" -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}][sub]overlay{3}\"", - mapPrefix.ToString(_usCulture), - subtitleStreamIndex.ToString(_usCulture), - state.VideoStream.Index.ToString(_usCulture), + return string.Format( + CultureInfo.InvariantCulture, + " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}][sub]overlay{3}\"", + mapPrefix, + subtitleStreamIndex, + state.VideoStream.Index, outputSizeParam, videoSizeParam); } - private ValueTuple<int?, int?> GetFixedOutputSize(int? videoWidth, + private (int? width, int? height) GetFixedOutputSize( + int? videoWidth, int? videoHeight, int? requestedWidth, int? requestedHeight, @@ -1559,11 +1610,11 @@ namespace MediaBrowser.Controller.MediaEncoding { if (!videoWidth.HasValue && !requestedWidth.HasValue) { - return new ValueTuple<int?, int?>(null, null); + return (null, null); } if (!videoHeight.HasValue && !requestedHeight.HasValue) { - return new ValueTuple<int?, int?>(null, null); + return (null, null); } decimal inputWidth = Convert.ToDecimal(videoWidth ?? requestedWidth); @@ -1583,7 +1634,7 @@ namespace MediaBrowser.Controller.MediaEncoding outputWidth = 2 * Math.Truncate(outputWidth / 2); outputHeight = 2 * Math.Truncate(outputHeight / 2); - return new ValueTuple<int?, int?>(Convert.ToInt32(outputWidth), Convert.ToInt32(outputHeight)); + return (Convert.ToInt32(outputWidth), Convert.ToInt32(outputHeight)); } public List<string> GetScalingFilters(int? videoWidth, @@ -1597,9 +1648,17 @@ namespace MediaBrowser.Controller.MediaEncoding int? requestedMaxHeight) { var filters = new List<string>(); - var fixedOutputSize = GetFixedOutputSize(videoWidth, videoHeight, requestedWidth, requestedHeight, requestedMaxWidth, requestedMaxHeight); - - if (string.Equals(videoEncoder, "h264_vaapi", StringComparison.OrdinalIgnoreCase) && fixedOutputSize.Item1.HasValue && fixedOutputSize.Item2.HasValue) + var (width, height) = GetFixedOutputSize( + videoWidth, + videoHeight, + requestedWidth, + requestedHeight, + requestedMaxWidth, + requestedMaxHeight); + + if (string.Equals(videoEncoder, "h264_vaapi", StringComparison.OrdinalIgnoreCase) + && width.HasValue + && height.HasValue) { // Work around vaapi's reduced scaling features var scaler = "scale_vaapi"; @@ -1607,15 +1666,26 @@ namespace MediaBrowser.Controller.MediaEncoding // Given the input dimensions (inputWidth, inputHeight), determine the output dimensions // (outputWidth, outputHeight). The user may request precise output dimensions or maximum // output dimensions. Output dimensions are guaranteed to be even. - var outputWidth = fixedOutputSize.Item1.Value; - var outputHeight = fixedOutputSize.Item2.Value; + var outputWidth = width.Value; + var outputHeight = height.Value; - if (!videoWidth.HasValue || outputWidth != videoWidth.Value || !videoHeight.HasValue || outputHeight != videoHeight.Value) + if (!videoWidth.HasValue + || outputWidth != videoWidth.Value + || !videoHeight.HasValue + || outputHeight != videoHeight.Value) { - filters.Add(string.Format("{0}=w={1}:h={2}", scaler, outputWidth.ToString(_usCulture), outputHeight.ToString(_usCulture))); + filters.Add( + string.Format( + CultureInfo.InvariantCulture, + "{0}=w={1}:h={2}", + scaler, + outputWidth, + outputHeight)); } } - else if ((videoDecoder ?? string.Empty).IndexOf("_cuvid", StringComparison.OrdinalIgnoreCase) != -1 && fixedOutputSize.Item1.HasValue && fixedOutputSize.Item2.HasValue) + else if ((videoDecoder ?? string.Empty).IndexOf("_cuvid", StringComparison.OrdinalIgnoreCase) != -1 + && width.HasValue + && height.HasValue) { // Nothing to do, it's handled as an input resize filter } @@ -1631,7 +1701,12 @@ namespace MediaBrowser.Controller.MediaEncoding var widthParam = requestedWidth.Value.ToString(_usCulture); var heightParam = requestedHeight.Value.ToString(_usCulture); - filters.Add(string.Format("scale=trunc({0}/64)*64:trunc({1}/2)*2", widthParam, heightParam)); + filters.Add( + string.Format( + CultureInfo.InvariantCulture, + "scale=trunc({0}/64)*64:trunc({1}/2)*2", + widthParam, + heightParam)); } else { @@ -1647,11 +1722,21 @@ namespace MediaBrowser.Controller.MediaEncoding if (isExynosV4L2) { - filters.Add(string.Format("scale=trunc(min(max(iw\\,ih*dar)\\,min({0}\\,{1}*dar))/64)*64:trunc(min(max(iw/dar\\,ih)\\,min({0}/dar\\,{1}))/2)*2", maxWidthParam, maxHeightParam)); + filters.Add( + string.Format( + CultureInfo.InvariantCulture, + "scale=trunc(min(max(iw\\,ih*dar)\\,min({0}\\,{1}*dar))/64)*64:trunc(min(max(iw/dar\\,ih)\\,min({0}/dar\\,{1}))/2)*2", + maxWidthParam, + maxHeightParam)); } else { - filters.Add(string.Format("scale=trunc(min(max(iw\\,ih*dar)\\,min({0}\\,{1}*dar))/2)*2:trunc(min(max(iw/dar\\,ih)\\,min({0}/dar\\,{1}))/2)*2", maxWidthParam, maxHeightParam)); + filters.Add( + string.Format( + CultureInfo.InvariantCulture, + "scale=trunc(min(max(iw\\,ih*dar)\\,min({0}\\,{1}*dar))/2)*2:trunc(min(max(iw/dar\\,ih)\\,min({0}/dar\\,{1}))/2)*2", + maxWidthParam, + maxHeightParam)); } } @@ -1667,7 +1752,11 @@ namespace MediaBrowser.Controller.MediaEncoding { var widthParam = requestedWidth.Value.ToString(_usCulture); - filters.Add(string.Format("scale={0}:trunc(ow/a/2)*2", widthParam)); + filters.Add( + string.Format( + CultureInfo.InvariantCulture, + "scale={0}:trunc(ow/a/2)*2", + widthParam)); } } @@ -1678,11 +1767,19 @@ namespace MediaBrowser.Controller.MediaEncoding if (isExynosV4L2) { - filters.Add(string.Format("scale=trunc(oh*a/64)*64:{0}", heightParam)); + filters.Add( + string.Format( + CultureInfo.InvariantCulture, + "scale=trunc(oh*a/64)*64:{0}", + heightParam)); } else { - filters.Add(string.Format("scale=trunc(oh*a/2)*2:{0}", heightParam)); + filters.Add( + string.Format( + CultureInfo.InvariantCulture, + "scale=trunc(oh*a/2)*2:{0}", + heightParam)); } } @@ -1693,11 +1790,19 @@ namespace MediaBrowser.Controller.MediaEncoding if (isExynosV4L2) { - filters.Add(string.Format("scale=trunc(min(max(iw\\,ih*dar)\\,{0})/64)*64:trunc(ow/dar/2)*2", maxWidthParam)); + filters.Add( + string.Format( + CultureInfo.InvariantCulture, + "scale=trunc(min(max(iw\\,ih*dar)\\,{0})/64)*64:trunc(ow/dar/2)*2", + maxWidthParam)); } else { - filters.Add(string.Format("scale=trunc(min(max(iw\\,ih*dar)\\,{0})/2)*2:trunc(ow/dar/2)*2", maxWidthParam)); + filters.Add( + string.Format( + CultureInfo.InvariantCulture, + "scale=trunc(min(max(iw\\,ih*dar)\\,{0})/2)*2:trunc(ow/dar/2)*2", + maxWidthParam)); } } @@ -1708,11 +1813,19 @@ namespace MediaBrowser.Controller.MediaEncoding if (isExynosV4L2) { - filters.Add(string.Format("scale=trunc(oh*a/64)*64:min(max(iw/dar\\,ih)\\,{0})", maxHeightParam)); + filters.Add( + string.Format( + CultureInfo.InvariantCulture, + "scale=trunc(oh*a/64)*64:min(max(iw/dar\\,ih)\\,{0})", + maxHeightParam)); } else { - filters.Add(string.Format("scale=trunc(oh*a/2)*2:min(max(iw/dar\\,ih)\\,{0})", maxHeightParam)); + filters.Add( + string.Format( + CultureInfo.InvariantCulture, + "scale=trunc(oh*a/2)*2:min(max(iw/dar\\,ih)\\,{0})", + maxHeightParam)); } } } @@ -1722,8 +1835,8 @@ namespace MediaBrowser.Controller.MediaEncoding private string GetFixedSizeScalingFilter(Video3DFormat? threedFormat, int requestedWidth, int requestedHeight) { - var widthParam = requestedWidth.ToString(_usCulture); - var heightParam = requestedHeight.ToString(_usCulture); + var widthParam = requestedWidth.ToString(CultureInfo.InvariantCulture); + var heightParam = requestedHeight.ToString(CultureInfo.InvariantCulture); string filter = null; @@ -1765,13 +1878,14 @@ namespace MediaBrowser.Controller.MediaEncoding } } - return string.Format(filter, widthParam, heightParam); + return string.Format(CultureInfo.InvariantCulture, filter, widthParam, heightParam); } /// <summary> /// If we're going to put a fixed size on the command line, this will calculate it /// </summary> - public string GetOutputSizeParam(EncodingJobInfo state, + public string GetOutputSizeParam( + EncodingJobInfo state, EncodingOptions options, string outputVideoCodec, bool allowTimeStampCopy = true) @@ -1780,25 +1894,45 @@ namespace MediaBrowser.Controller.MediaEncoding var request = state.BaseRequest; + var videoStream = state.VideoStream; var filters = new List<string>(); + // If we're hardware VAAPI decoding and software encoding, download frames from the decoder first + var hwType = options.HardwareAccelerationType ?? string.Empty; + if (string.Equals(hwType, "vaapi", StringComparison.OrdinalIgnoreCase) && !options.EnableHardwareEncoding ) + { + filters.Add("hwdownload"); + + // If transcoding from 10 bit, transform colour spaces too + if (!string.IsNullOrEmpty(videoStream.PixelFormat) + && videoStream.PixelFormat.IndexOf("p10", StringComparison.OrdinalIgnoreCase) != -1 + && string.Equals(outputVideoCodec,"libx264", StringComparison.OrdinalIgnoreCase)) + { + filters.Add("format=p010le"); + filters.Add("format=nv12"); + } + else + { + filters.Add("format=nv12"); + } + } + if (string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase)) { filters.Add("format=nv12|vaapi"); filters.Add("hwupload"); } - if (state.DeInterlace("h264", true) && string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase)) + if (state.DeInterlace("h264", true) + && string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase)) { - filters.Add(string.Format("deinterlace_vaapi")); + filters.Add(string.Format(CultureInfo.InvariantCulture, "deinterlace_vaapi")); } - var videoStream = state.VideoStream; - - if ((state.DeInterlace("h264", true) || state.DeInterlace("h265", true) || state.DeInterlace("hevc", true)) && - !string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase)) + if ((state.DeInterlace("h264", true) || state.DeInterlace("h265", true) || state.DeInterlace("hevc", true)) + && !string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase)) { - var inputFramerate = videoStream == null ? null : videoStream.RealFrameRate; + var inputFramerate = videoStream?.RealFrameRate; // If it is already 60fps then it will create an output framerate that is much too high for roku and others to handle if (string.Equals(options.DeinterlaceMethod, "bobandweave", StringComparison.OrdinalIgnoreCase) && (inputFramerate ?? 60) <= 30) @@ -1811,11 +1945,11 @@ namespace MediaBrowser.Controller.MediaEncoding } } - var inputWidth = videoStream == null ? null : videoStream.Width; - var inputHeight = videoStream == null ? null : videoStream.Height; + var inputWidth = videoStream?.Width; + var inputHeight = videoStream?.Height; var threeDFormat = state.MediaSource.Video3DFormat; - var videoDecoder = this.GetHardwareAcceleratedVideoDecoder(state, options); + var videoDecoder = GetHardwareAcceleratedVideoDecoder(state, options); filters.AddRange(GetScalingFilters(inputWidth, inputHeight, threeDFormat, videoDecoder, outputVideoCodec, request.Width, request.Height, request.MaxWidth, request.MaxHeight)); @@ -1833,6 +1967,7 @@ namespace MediaBrowser.Controller.MediaEncoding { filters.Add("hwmap"); } + if (allowTimeStampCopy) { output += " -copyts"; @@ -1841,7 +1976,10 @@ namespace MediaBrowser.Controller.MediaEncoding if (filters.Count > 0) { - output += string.Format(" -vf \"{0}\"", string.Join(",", filters.ToArray())); + output += string.Format( + CultureInfo.InvariantCulture, + " -vf \"{0}\"", + string.Join(",", filters)); } return output; @@ -1889,7 +2027,8 @@ namespace MediaBrowser.Controller.MediaEncoding } } - if (state.AudioStream != null && CanStreamCopyAudio(state, state.AudioStream, state.SupportedAudioCodecs)) + if (state.AudioStream != null + && CanStreamCopyAudio(state, state.AudioStream, state.SupportedAudioCodecs)) { state.OutputAudioCodec = "copy"; } @@ -1906,14 +2045,10 @@ namespace MediaBrowser.Controller.MediaEncoding } public static string GetProbeSizeArgument(int numInputFiles) - { - return numInputFiles > 1 ? "-probesize 1G" : ""; - } + => numInputFiles > 1 ? "-probesize 1G" : ""; public static string GetAnalyzeDurationArgument(int numInputFiles) - { - return numInputFiles > 1 ? "-analyzeduration 200M" : ""; - } + => numInputFiles > 1 ? "-analyzeduration 200M" : ""; public string GetInputModifier(EncodingJobInfo state, EncodingOptions encodingOptions) { @@ -1981,18 +2116,22 @@ namespace MediaBrowser.Controller.MediaEncoding { flags.Add("+igndts"); } + if (state.IgnoreInputIndex) { flags.Add("+ignidx"); } + if (state.GenPtsInput || string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase)) { flags.Add("+genpts"); } + if (state.DiscardCorruptFramesInput) { flags.Add("+discardcorrupt"); } + if (state.EnableFastSeekInput) { flags.Add("+fastseek"); @@ -2000,24 +2139,33 @@ namespace MediaBrowser.Controller.MediaEncoding if (flags.Count > 0) { - inputModifier += " -fflags " + string.Join("", flags.ToArray()); + inputModifier += " -fflags " + string.Join(string.Empty, flags); } - var videoDecoder = this.GetHardwareAcceleratedVideoDecoder(state, encodingOptions); + var videoDecoder = GetHardwareAcceleratedVideoDecoder(state, encodingOptions); if (!string.IsNullOrEmpty(videoDecoder)) { inputModifier += " " + videoDecoder; - var videoStream = state.VideoStream; - var inputWidth = videoStream == null ? null : videoStream.Width; - var inputHeight = videoStream == null ? null : videoStream.Height; - var request = state.BaseRequest; + if ((videoDecoder ?? string.Empty).IndexOf("_cuvid", StringComparison.OrdinalIgnoreCase) != -1) + { + var videoStream = state.VideoStream; + var inputWidth = videoStream?.Width; + var inputHeight = videoStream?.Height; + var request = state.BaseRequest; - var fixedOutputSize = GetFixedOutputSize(inputWidth, inputHeight, request.Width, request.Height, request.MaxWidth, request.MaxHeight); + var (width, height) = GetFixedOutputSize(inputWidth, inputHeight, request.Width, request.Height, request.MaxWidth, request.MaxHeight); - if ((videoDecoder ?? string.Empty).IndexOf("_cuvid", StringComparison.OrdinalIgnoreCase) != -1 && fixedOutputSize.Item1.HasValue && fixedOutputSize.Item2.HasValue) - { - inputModifier += string.Format(" -resize {0}x{1}", fixedOutputSize.Item1.Value.ToString(_usCulture), fixedOutputSize.Item2.Value.ToString(_usCulture)); + if ((videoDecoder ?? string.Empty).IndexOf("_cuvid", StringComparison.OrdinalIgnoreCase) != -1 + && width.HasValue + && height.HasValue) + { + inputModifier += string.Format( + CultureInfo.InvariantCulture, + " -resize {0}x{1}", + width.Value, + height.Value); + } } } @@ -2026,9 +2174,9 @@ namespace MediaBrowser.Controller.MediaEncoding var outputVideoCodec = GetVideoEncoder(state, encodingOptions); // Important: If this is ever re-enabled, make sure not to use it with wtv because it breaks seeking - if (!string.Equals(state.InputContainer, "wtv", StringComparison.OrdinalIgnoreCase) && - state.TranscodingType != TranscodingJobType.Progressive && - state.EnableBreakOnNonKeyFrames(outputVideoCodec)) + if (!string.Equals(state.InputContainer, "wtv", StringComparison.OrdinalIgnoreCase) + && state.TranscodingType != TranscodingJobType.Progressive + && state.EnableBreakOnNonKeyFrames(outputVideoCodec)) { inputModifier += " -noaccurate_seek"; } @@ -2052,14 +2200,16 @@ namespace MediaBrowser.Controller.MediaEncoding } - public void AttachMediaSourceInfo(EncodingJobInfo state, - MediaSourceInfo mediaSource, - string requestedUrl) + public void AttachMediaSourceInfo( + EncodingJobInfo state, + MediaSourceInfo mediaSource, + string requestedUrl) { if (state == null) { throw new ArgumentNullException(nameof(state)); } + if (mediaSource == null) { throw new ArgumentNullException(nameof(mediaSource)); @@ -2117,15 +2267,16 @@ namespace MediaBrowser.Controller.MediaEncoding state.RemoteHttpHeaders = mediaSource.RequiredHttpHeaders; state.ReadInputAtNativeFramerate = mediaSource.ReadAtNativeFramerate; - if (state.ReadInputAtNativeFramerate || - mediaSource.Protocol == MediaProtocol.File && string.Equals(mediaSource.Container, "wtv", StringComparison.OrdinalIgnoreCase)) + if (state.ReadInputAtNativeFramerate + || mediaSource.Protocol == MediaProtocol.File + && string.Equals(mediaSource.Container, "wtv", StringComparison.OrdinalIgnoreCase)) { state.InputVideoSync = "-1"; state.InputAudioSync = "1"; } - if (string.Equals(mediaSource.Container, "wma", StringComparison.OrdinalIgnoreCase) || - string.Equals(mediaSource.Container, "asf", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(mediaSource.Container, "wma", StringComparison.OrdinalIgnoreCase) + || string.Equals(mediaSource.Container, "asf", StringComparison.OrdinalIgnoreCase)) { // Seeing some stuttering when transcoding wma to audio-only HLS state.InputAudioSync = "1"; @@ -2237,7 +2388,7 @@ namespace MediaBrowser.Controller.MediaEncoding return null; } - return this.GetHardwareAcceleratedVideoDecoder(state.MediaSource.VideoType ?? VideoType.VideoFile, state.VideoStream, encodingOptions); + return GetHardwareAcceleratedVideoDecoder(state.MediaSource.VideoType ?? VideoType.VideoFile, state.VideoStream, encodingOptions); } public string GetHardwareAcceleratedVideoDecoder(VideoType videoType, MediaStream videoStream, EncodingOptions encodingOptions) @@ -2250,9 +2401,9 @@ namespace MediaBrowser.Controller.MediaEncoding return null; } - if (videoStream != null && - !string.IsNullOrEmpty(videoStream.Codec) && - !string.IsNullOrEmpty(encodingOptions.HardwareAccelerationType)) + if (videoStream != null + && !string.IsNullOrEmpty(videoStream.Codec) + && !string.IsNullOrEmpty(encodingOptions.HardwareAccelerationType)) { if (string.Equals(encodingOptions.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase)) { @@ -2450,11 +2601,7 @@ namespace MediaBrowser.Controller.MediaEncoding codec = format; } - var args = " -codec:s:0 " + codec; - - args += " -disposition:s:0 default"; - - return args; + return " -codec:s:0 " + codec + " -disposition:s:0 default"; } public string GetProgressiveVideoFullCommandLine(EncodingJobInfo state, EncodingOptions encodingOptions, string outputPath, string defaultPreset) @@ -2465,8 +2612,8 @@ namespace MediaBrowser.Controller.MediaEncoding var format = string.Empty; var keyFrame = string.Empty; - if (string.Equals(Path.GetExtension(outputPath), ".mp4", StringComparison.OrdinalIgnoreCase) && - state.BaseRequest.Context == EncodingContext.Streaming) + if (string.Equals(Path.GetExtension(outputPath), ".mp4", StringComparison.OrdinalIgnoreCase) + && state.BaseRequest.Context == EncodingContext.Streaming) { // Comparison: https://github.com/jansmolders86/mediacenterjs/blob/master/lib/transcoding/desktop.js format = " -f mp4 -movflags frag_keyframe+empty_moov"; @@ -2476,7 +2623,9 @@ namespace MediaBrowser.Controller.MediaEncoding var inputModifier = GetInputModifier(state, encodingOptions); - return string.Format("{0} {1}{2} {3} {4} -map_metadata -1 -map_chapters -1 -threads {5} {6}{7}{8} -y \"{9}\"", + return string.Format( + CultureInfo.InvariantCulture, + "{0} {1}{2} {3} {4} -map_metadata -1 -map_chapters -1 -threads {5} {6}{7}{8} -y \"{9}\"", inputModifier, GetInputArgument(state, encodingOptions), keyFrame, @@ -2486,8 +2635,7 @@ namespace MediaBrowser.Controller.MediaEncoding GetProgressiveVideoAudioArguments(state, encodingOptions), GetSubtitleEmbedArguments(state), format, - outputPath - ).Trim(); + outputPath).Trim(); } public string GetOutputFFlags(EncodingJobInfo state) @@ -2500,7 +2648,7 @@ namespace MediaBrowser.Controller.MediaEncoding if (flags.Count > 0) { - return " -fflags " + string.Join("", flags.ToArray()); + return " -fflags " + string.Join("", flags); } return string.Empty; @@ -2517,9 +2665,9 @@ namespace MediaBrowser.Controller.MediaEncoding if (string.Equals(videoCodec, "copy", StringComparison.OrdinalIgnoreCase)) { - if (state.VideoStream != null && - string.Equals(state.OutputContainer, "ts", StringComparison.OrdinalIgnoreCase) && - !string.Equals(state.VideoStream.NalLengthSize, "0", StringComparison.OrdinalIgnoreCase)) + if (state.VideoStream != null + && string.Equals(state.OutputContainer, "ts", StringComparison.OrdinalIgnoreCase) + && !string.Equals(state.VideoStream.NalLengthSize, "0", StringComparison.OrdinalIgnoreCase)) { string bitStreamArgs = GetBitStreamArgs(state.VideoStream); if (!string.IsNullOrEmpty(bitStreamArgs)) @@ -2540,8 +2688,10 @@ namespace MediaBrowser.Controller.MediaEncoding } else { - var keyFrameArg = string.Format(" -force_key_frames \"expr:gte(t,n_forced*{0})\"", - 5.ToString(_usCulture)); + var keyFrameArg = string.Format( + CultureInfo.InvariantCulture, + " -force_key_frames \"expr:gte(t,n_forced*{0})\"", + 5); args += keyFrameArg; @@ -2562,6 +2712,7 @@ namespace MediaBrowser.Controller.MediaEncoding { args += " -copyts"; } + args += " -avoid_negative_ts disabled -start_at_zero"; } @@ -2662,39 +2813,22 @@ namespace MediaBrowser.Controller.MediaEncoding } } - var albumCoverInput = string.Empty; - var mapArgs = string.Empty; - var metadata = string.Empty; - var vn = string.Empty; - - var hasArt = !string.IsNullOrEmpty(state.AlbumCoverPath); - hasArt = false; - - if (hasArt) - { - albumCoverInput = " -i \"" + state.AlbumCoverPath + "\""; - mapArgs = " -map 0:a -map 1:v -c:1:v copy"; - metadata = " -metadata:s:v title=\"Album cover\" -metadata:s:v comment=\"Cover(Front)\""; - } - else - { - vn = " -vn"; - } - var threads = GetNumberOfThreads(state, encodingOptions, null); var inputModifier = GetInputModifier(state, encodingOptions); - return string.Format("{0} {1}{7}{8} -threads {2}{3} {4} -id3v2_version 3 -write_id3v1 1{6} -y \"{5}\"", + return string.Format( + CultureInfo.InvariantCulture, + "{0} {1}{7}{8} -threads {2}{3} {4} -id3v2_version 3 -write_id3v1 1{6} -y \"{5}\"", inputModifier, GetInputArgument(state, encodingOptions), threads, - vn, - string.Join(" ", audioTranscodeParams.ToArray()), + " -vn", + string.Join(" ", audioTranscodeParams), outputPath, - metadata, - albumCoverInput, - mapArgs).Trim(); + string.Empty, + string.Empty, + string.Empty).Trim(); } } diff --git a/MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs b/MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs index 7f842c1d0..5cedc3d57 100644 --- a/MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs +++ b/MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs @@ -1,8 +1,8 @@ using System; +using System.Collections.Generic; using System.IO; using System.Linq; using MediaBrowser.Model.IO; -using MediaBrowser.Model.MediaInfo; namespace MediaBrowser.Controller.MediaEncoding { @@ -16,31 +16,26 @@ namespace MediaBrowser.Controller.MediaEncoding /// </summary> /// <param name="fileSystem">The file system.</param> /// <param name="videoPath">The video path.</param> - /// <param name="protocol">The protocol.</param> /// <param name="isoMount">The iso mount.</param> /// <param name="playableStreamFileNames">The playable stream file names.</param> - /// <returns>System.String[][].</returns> - public static string[] GetInputArgument(IFileSystem fileSystem, string videoPath, MediaProtocol protocol, IIsoMount isoMount, string[] playableStreamFileNames) + /// <returns>string[].</returns> + public static string[] GetInputArgument(IFileSystem fileSystem, string videoPath, IIsoMount isoMount, IReadOnlyCollection<string> playableStreamFileNames) { - if (playableStreamFileNames.Length > 0) + if (playableStreamFileNames.Count > 0) { if (isoMount == null) { return GetPlayableStreamFiles(fileSystem, videoPath, playableStreamFileNames); } + return GetPlayableStreamFiles(fileSystem, isoMount.MountedPath, playableStreamFileNames); } return new[] { videoPath }; } - private static string[] GetPlayableStreamFiles(IFileSystem fileSystem, string rootPath, string[] filenames) + private static string[] GetPlayableStreamFiles(IFileSystem fileSystem, string rootPath, IEnumerable<string> filenames) { - if (filenames.Length == 0) - { - return new string[] { }; - } - var allFiles = fileSystem .GetFilePaths(rootPath, true) .ToArray(); diff --git a/MediaBrowser.Controller/Providers/AlbumInfo.cs b/MediaBrowser.Controller/Providers/AlbumInfo.cs index b0b443fc0..ac6b86c1d 100644 --- a/MediaBrowser.Controller/Providers/AlbumInfo.cs +++ b/MediaBrowser.Controller/Providers/AlbumInfo.cs @@ -9,7 +9,7 @@ namespace MediaBrowser.Controller.Providers /// Gets or sets the album artist. /// </summary> /// <value>The album artist.</value> - public string[] AlbumArtists { get; set; } + public IReadOnlyList<string> AlbumArtists { get; set; } /// <summary> /// Gets or sets the artist provider ids. diff --git a/MediaBrowser.Controller/Providers/MusicVideoInfo.cs b/MediaBrowser.Controller/Providers/MusicVideoInfo.cs index 194b26484..9835351fc 100644 --- a/MediaBrowser.Controller/Providers/MusicVideoInfo.cs +++ b/MediaBrowser.Controller/Providers/MusicVideoInfo.cs @@ -1,7 +1,9 @@ +using System.Collections.Generic; + namespace MediaBrowser.Controller.Providers { public class MusicVideoInfo : ItemLookupInfo { - public string[] Artists { get; set; } + public IReadOnlyList<string> Artists { get; set; } } } diff --git a/MediaBrowser.Controller/Providers/SongInfo.cs b/MediaBrowser.Controller/Providers/SongInfo.cs index 61e950130..50615b0bd 100644 --- a/MediaBrowser.Controller/Providers/SongInfo.cs +++ b/MediaBrowser.Controller/Providers/SongInfo.cs @@ -1,12 +1,15 @@ using System; +using System.Collections.Generic; namespace MediaBrowser.Controller.Providers { public class SongInfo : ItemLookupInfo { - public string[] AlbumArtists { get; set; } + public IReadOnlyList<string> AlbumArtists { get; set; } + public string Album { get; set; } - public string[] Artists { get; set; } + + public IReadOnlyList<string> Artists { get; set; } public SongInfo() { diff --git a/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs index 30a33b729..19009e577 100644 --- a/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs +++ b/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs @@ -228,7 +228,7 @@ namespace MediaBrowser.LocalMetadata.Savers } } - if (item.RemoteTrailers.Length > 0) + if (item.RemoteTrailers.Count > 0) { writer.WriteStartElement("Trailers"); diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index a8874b6d0..75bb960c3 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -303,7 +303,7 @@ namespace MediaBrowser.MediaEncoding.Encoder { var extractChapters = request.MediaType == DlnaProfileType.Video && request.ExtractChapters; - var inputFiles = MediaEncoderHelpers.GetInputArgument(FileSystem, request.MediaSource.Path, request.MediaSource.Protocol, request.MountedIso, request.PlayableStreamFileNames); + var inputFiles = MediaEncoderHelpers.GetInputArgument(FileSystem, request.MediaSource.Path, request.MountedIso, request.PlayableStreamFileNames); var probeSize = EncodingHelper.GetProbeSizeArgument(inputFiles.Length); string analyzeDuration; diff --git a/MediaBrowser.Model/Cryptography/PasswordHash.cs b/MediaBrowser.Model/Cryptography/PasswordHash.cs index 4bcf0c117..6e66f2088 100644 --- a/MediaBrowser.Model/Cryptography/PasswordHash.cs +++ b/MediaBrowser.Model/Cryptography/PasswordHash.cs @@ -69,6 +69,13 @@ namespace MediaBrowser.Model.Cryptography } } + public PasswordHash(ICryptoProvider cryptoProvider) + { + _id = cryptoProvider.DefaultHashMethod; + _salt = cryptoProvider.GenerateSalt(); + _hash = Array.Empty<Byte>(); + } + public string Id { get => _id; set => _id = value; } public Dictionary<string, string> Parameters { get => _parameters; set => _parameters = value; } @@ -77,13 +84,6 @@ namespace MediaBrowser.Model.Cryptography public byte[] Hash { get => _hash; set => _hash = value; } - public PasswordHash(ICryptoProvider cryptoProvider) - { - _id = cryptoProvider.DefaultHashMethod; - _salt = cryptoProvider.GenerateSalt(); - _hash = Array.Empty<Byte>(); - } - // TODO: move this class and use the HexHelper class public static byte[] ConvertFromByteString(string byteString) { @@ -127,7 +127,7 @@ namespace MediaBrowser.Model.Cryptography str.Append(_id); SerializeParameters(str); - if (_salt.Length == 0) + if (_salt.Length != 0) { str.Append('$'); str.Append(ConvertToByteString(_salt)); diff --git a/MediaBrowser.Model/Dto/BaseItemDto.cs b/MediaBrowser.Model/Dto/BaseItemDto.cs index b382d9d4a..4da5508b4 100644 --- a/MediaBrowser.Model/Dto/BaseItemDto.cs +++ b/MediaBrowser.Model/Dto/BaseItemDto.cs @@ -234,7 +234,7 @@ namespace MediaBrowser.Model.Dto /// Gets or sets the trailer urls. /// </summary> /// <value>The trailer urls.</value> - public MediaUrl[] RemoteTrailers { get; set; } + public IReadOnlyCollection<MediaUrl> RemoteTrailers { get; set; } /// <summary> /// Gets or sets the provider ids. @@ -386,7 +386,7 @@ namespace MediaBrowser.Model.Dto /// Gets or sets the artists. /// </summary> /// <value>The artists.</value> - public string[] Artists { get; set; } + public IReadOnlyList<string> Artists { get; set; } /// <summary> /// Gets or sets the artist items. diff --git a/MediaBrowser.Model/Dto/RecommendationDto.cs b/MediaBrowser.Model/Dto/RecommendationDto.cs index 0a890573b..acfb85e9b 100644 --- a/MediaBrowser.Model/Dto/RecommendationDto.cs +++ b/MediaBrowser.Model/Dto/RecommendationDto.cs @@ -1,10 +1,11 @@ using System; +using System.Collections.Generic; namespace MediaBrowser.Model.Dto { public class RecommendationDto { - public BaseItemDto[] Items { get; set; } + public IReadOnlyCollection<BaseItemDto> Items { get; set; } public RecommendationType RecommendationType { get; set; } diff --git a/MediaBrowser.Model/Net/MimeTypes.cs b/MediaBrowser.Model/Net/MimeTypes.cs index d7bf956bb..de5e58d22 100644 --- a/MediaBrowser.Model/Net/MimeTypes.cs +++ b/MediaBrowser.Model/Net/MimeTypes.cs @@ -120,6 +120,7 @@ namespace MediaBrowser.Model.Net { ".m4b", "audio/m4b" }, { ".xsp", "audio/xsp" }, { ".dsp", "audio/dsp" }, + { ".flac", "audio/flac" }, }; private static readonly Dictionary<string, string> _extensionLookup = CreateExtensionLookup(); diff --git a/MediaBrowser.Model/Querying/QueryResult.cs b/MediaBrowser.Model/Querying/QueryResult.cs index e81f2b868..c007a45d6 100644 --- a/MediaBrowser.Model/Querying/QueryResult.cs +++ b/MediaBrowser.Model/Querying/QueryResult.cs @@ -1,3 +1,6 @@ +using System; +using System.Collections.Generic; + namespace MediaBrowser.Model.Querying { public class QueryResult<T> @@ -6,7 +9,7 @@ namespace MediaBrowser.Model.Querying /// Gets or sets the items. /// </summary> /// <value>The items.</value> - public T[] Items { get; set; } + public IReadOnlyList<T> Items { get; set; } /// <summary> /// The total number of records available @@ -16,7 +19,7 @@ namespace MediaBrowser.Model.Querying public QueryResult() { - Items = new T[] { }; + Items = Array.Empty<T>(); } } } diff --git a/MediaBrowser.Model/Search/SearchHint.cs b/MediaBrowser.Model/Search/SearchHint.cs index 8a187f18e..8f4824903 100644 --- a/MediaBrowser.Model/Search/SearchHint.cs +++ b/MediaBrowser.Model/Search/SearchHint.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; namespace MediaBrowser.Model.Search { @@ -111,6 +112,7 @@ namespace MediaBrowser.Model.Search /// </summary> /// <value>The album.</value> public string Album { get; set; } + public Guid AlbumId { get; set; } /// <summary> @@ -123,7 +125,7 @@ namespace MediaBrowser.Model.Search /// Gets or sets the artists. /// </summary> /// <value>The artists.</value> - public string[] Artists { get; set; } + public IReadOnlyList<string> Artists { get; set; } /// <summary> /// Gets or sets the song count. diff --git a/MediaBrowser.Providers/Books/AudioBookMetadataService.cs b/MediaBrowser.Providers/Books/AudioBookMetadataService.cs index 4820e12ab..0062d5ab3 100644 --- a/MediaBrowser.Providers/Books/AudioBookMetadataService.cs +++ b/MediaBrowser.Providers/Books/AudioBookMetadataService.cs @@ -11,14 +11,31 @@ namespace MediaBrowser.Providers.Books { public class AudioBookMetadataService : MetadataService<AudioBook, SongInfo> { - protected override void MergeData(MetadataResult<AudioBook> source, MetadataResult<AudioBook> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings) + public AudioBookMetadataService( + IServerConfigurationManager serverConfigurationManager, + ILogger logger, + IProviderManager providerManager, + IFileSystem fileSystem, + IUserDataManager userDataManager, + ILibraryManager libraryManager) + : base(serverConfigurationManager, logger, providerManager, fileSystem, userDataManager, libraryManager) + { + } + + /// <inheritdoc /> + protected override void MergeData( + MetadataResult<AudioBook> source, + MetadataResult<AudioBook> target, + MetadataFields[] lockedFields, + bool replaceData, + bool mergeMetadataSettings) { ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings); var sourceItem = source.Item; var targetItem = target.Item; - if (replaceData || targetItem.Artists.Length == 0) + if (replaceData || targetItem.Artists.Count == 0) { targetItem.Artists = sourceItem.Artists; } @@ -28,9 +45,5 @@ namespace MediaBrowser.Providers.Books targetItem.Album = sourceItem.Album; } } - - public AudioBookMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager) : base(serverConfigurationManager, logger, providerManager, fileSystem, userDataManager, libraryManager) - { - } } } diff --git a/MediaBrowser.Providers/Manager/ProviderManager.cs b/MediaBrowser.Providers/Manager/ProviderManager.cs index a22eaaa51..6a8d03f6d 100644 --- a/MediaBrowser.Providers/Manager/ProviderManager.cs +++ b/MediaBrowser.Providers/Manager/ProviderManager.cs @@ -278,7 +278,7 @@ namespace MediaBrowser.Providers.Manager var currentOptions = options; var typeOptions = libraryOptions.GetTypeOptions(item.GetType().Name); - var typeFetcherOrder = typeOptions == null ? null : typeOptions.ImageFetcherOrder; + var typeFetcherOrder = typeOptions?.ImageFetcherOrder; return ImageProviders.Where(i => CanRefresh(i, item, libraryOptions, options, refreshOptions, includeDisabled)) .OrderBy(i => @@ -287,7 +287,6 @@ namespace MediaBrowser.Providers.Manager if (!(i is ILocalImageProvider)) { var fetcherOrder = typeFetcherOrder ?? currentOptions.ImageFetcherOrder; - var index = Array.IndexOf(fetcherOrder, i.Name); if (index != -1) diff --git a/MediaBrowser.Providers/Manager/ProviderUtils.cs b/MediaBrowser.Providers/Manager/ProviderUtils.cs index 2031449d9..8d1588c4e 100644 --- a/MediaBrowser.Providers/Manager/ProviderUtils.cs +++ b/MediaBrowser.Providers/Manager/ProviderUtils.cs @@ -11,7 +11,8 @@ namespace MediaBrowser.Providers.Manager { public static class ProviderUtils { - public static void MergeBaseItemData<T>(MetadataResult<T> sourceResult, + public static void MergeBaseItemData<T>( + MetadataResult<T> sourceResult, MetadataResult<T> targetResult, MetadataFields[] lockedFields, bool replaceData, @@ -174,11 +175,11 @@ namespace MediaBrowser.Providers.Manager } } - MergeAlbumArtist(source, target, lockedFields, replaceData); - MergeCriticRating(source, target, lockedFields, replaceData); - MergeTrailers(source, target, lockedFields, replaceData); - MergeVideoInfo(source, target, lockedFields, replaceData); - MergeDisplayOrder(source, target, lockedFields, replaceData); + MergeAlbumArtist(source, target, replaceData); + MergeCriticRating(source, target, replaceData); + MergeTrailers(source, target, replaceData); + MergeVideoInfo(source, target, replaceData); + MergeDisplayOrder(source, target, replaceData); if (replaceData || string.IsNullOrEmpty(target.ForcedSortName)) { @@ -196,7 +197,7 @@ namespace MediaBrowser.Providers.Manager target.IsLocked = source.IsLocked; // Grab the value if it's there, but if not then don't overwrite the default - if (source.DateCreated != default(DateTime)) + if (source.DateCreated != default) { target.DateCreated = source.DateCreated; } @@ -231,12 +232,10 @@ namespace MediaBrowser.Providers.Manager } } - private static void MergeDisplayOrder(BaseItem source, BaseItem target, MetadataFields[] lockedFields, bool replaceData) + private static void MergeDisplayOrder(BaseItem source, BaseItem target, bool replaceData) { - var sourceHasDisplayOrder = source as IHasDisplayOrder; - var targetHasDisplayOrder = target as IHasDisplayOrder; - - if (sourceHasDisplayOrder != null && targetHasDisplayOrder != null) + if (source is IHasDisplayOrder sourceHasDisplayOrder + && target is IHasDisplayOrder targetHasDisplayOrder) { if (replaceData || string.IsNullOrEmpty(targetHasDisplayOrder.DisplayOrder)) { @@ -250,21 +249,19 @@ namespace MediaBrowser.Providers.Manager } } - private static void MergeAlbumArtist(BaseItem source, BaseItem target, MetadataFields[] lockedFields, bool replaceData) + private static void MergeAlbumArtist(BaseItem source, BaseItem target, bool replaceData) { - var sourceHasAlbumArtist = source as IHasAlbumArtist; - var targetHasAlbumArtist = target as IHasAlbumArtist; - - if (sourceHasAlbumArtist != null && targetHasAlbumArtist != null) + if (source is IHasAlbumArtist sourceHasAlbumArtist + && target is IHasAlbumArtist targetHasAlbumArtist) { - if (replaceData || targetHasAlbumArtist.AlbumArtists.Length == 0) + if (replaceData || targetHasAlbumArtist.AlbumArtists.Count == 0) { targetHasAlbumArtist.AlbumArtists = sourceHasAlbumArtist.AlbumArtists; } } } - private static void MergeCriticRating(BaseItem source, BaseItem target, MetadataFields[] lockedFields, bool replaceData) + private static void MergeCriticRating(BaseItem source, BaseItem target, bool replaceData) { if (replaceData || !target.CriticRating.HasValue) { @@ -272,20 +269,17 @@ namespace MediaBrowser.Providers.Manager } } - private static void MergeTrailers(BaseItem source, BaseItem target, MetadataFields[] lockedFields, bool replaceData) + private static void MergeTrailers(BaseItem source, BaseItem target, bool replaceData) { - if (replaceData || target.RemoteTrailers.Length == 0) + if (replaceData || target.RemoteTrailers.Count == 0) { target.RemoteTrailers = source.RemoteTrailers; } } - private static void MergeVideoInfo(BaseItem source, BaseItem target, MetadataFields[] lockedFields, bool replaceData) + private static void MergeVideoInfo(BaseItem source, BaseItem target, bool replaceData) { - var sourceCast = source as Video; - var targetCast = target as Video; - - if (sourceCast != null && targetCast != null) + if (source is Video sourceCast && target is Video targetCast) { if (replaceData || targetCast.Video3DFormat == null) { diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index ab4759c61..c7ecc59c9 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -24,4 +24,9 @@ <GenerateDocumentationFile>true</GenerateDocumentationFile> </PropertyGroup> + <PropertyGroup> + <!-- We need at least C# 7.1 --> + <LangVersion>latest</LangVersion> + </PropertyGroup> + </Project> diff --git a/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs b/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs index d80084acf..e0b23108f 100644 --- a/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs +++ b/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs @@ -62,7 +62,7 @@ namespace MediaBrowser.Providers.MediaInfo { var protocol = item.PathProtocol ?? MediaProtocol.File; - var inputPath = MediaEncoderHelpers.GetInputArgument(_fileSystem, item.Path, protocol, null, item.GetPlayableStreamFileNames(_mediaEncoder)); + var inputPath = MediaEncoderHelpers.GetInputArgument(_fileSystem, item.Path, null, item.GetPlayableStreamFileNames(_mediaEncoder)); var mediaStreams = item.GetMediaStreams(); diff --git a/MediaBrowser.Providers/Movies/MovieExternalIds.cs b/MediaBrowser.Providers/Movies/MovieExternalIds.cs index 3783c5032..09ed6034c 100644 --- a/MediaBrowser.Providers/Movies/MovieExternalIds.cs +++ b/MediaBrowser.Providers/Movies/MovieExternalIds.cs @@ -7,85 +7,6 @@ using MediaBrowser.Model.Entities; namespace MediaBrowser.Providers.Movies { - public class MovieDbMovieExternalId : IExternalId - { - public const string BaseMovieDbUrl = "https://www.themoviedb.org/"; - - public string Name => "TheMovieDb"; - - public string Key => MetadataProviders.Tmdb.ToString(); - - public string UrlFormatString => BaseMovieDbUrl + "movie/{0}"; - - public bool Supports(IHasProviderIds item) - { - // Supports images for tv movies - var tvProgram = item as LiveTvProgram; - if (tvProgram != null && tvProgram.IsMovie) - { - return true; - } - - return item is Movie || item is MusicVideo || item is Trailer; - } - } - - public class MovieDbSeriesExternalId : IExternalId - { - public string Name => "TheMovieDb"; - - public string Key => MetadataProviders.Tmdb.ToString(); - - public string UrlFormatString => MovieDbMovieExternalId.BaseMovieDbUrl + "tv/{0}"; - - public bool Supports(IHasProviderIds item) - { - return item is Series; - } - } - - public class MovieDbMovieCollectionExternalId : IExternalId - { - public string Name => "TheMovieDb Collection"; - - public string Key => MetadataProviders.TmdbCollection.ToString(); - - public string UrlFormatString => MovieDbMovieExternalId.BaseMovieDbUrl + "collection/{0}"; - - public bool Supports(IHasProviderIds item) - { - return item is Movie || item is MusicVideo || item is Trailer; - } - } - - public class MovieDbPersonExternalId : IExternalId - { - public string Name => "TheMovieDb"; - - public string Key => MetadataProviders.Tmdb.ToString(); - - public string UrlFormatString => MovieDbMovieExternalId.BaseMovieDbUrl + "person/{0}"; - - public bool Supports(IHasProviderIds item) - { - return item is Person; - } - } - - public class MovieDbCollectionExternalId : IExternalId - { - public string Name => "TheMovieDb"; - - public string Key => MetadataProviders.Tmdb.ToString(); - - public string UrlFormatString => MovieDbMovieExternalId.BaseMovieDbUrl + "collection/{0}"; - - public bool Supports(IHasProviderIds item) - { - return item is BoxSet; - } - } - public class ImdbExternalId : IExternalId { public string Name => "IMDb"; diff --git a/MediaBrowser.Providers/Music/AlbumMetadataService.cs b/MediaBrowser.Providers/Music/AlbumMetadataService.cs index 33a6c2fa3..4e59b4119 100644 --- a/MediaBrowser.Providers/Music/AlbumMetadataService.cs +++ b/MediaBrowser.Providers/Music/AlbumMetadataService.cs @@ -15,12 +15,34 @@ namespace MediaBrowser.Providers.Music { public class AlbumMetadataService : MetadataService<MusicAlbum, AlbumInfo> { + public AlbumMetadataService( + IServerConfigurationManager serverConfigurationManager, + ILogger logger, + IProviderManager providerManager, + IFileSystem fileSystem, + IUserDataManager userDataManager, + ILibraryManager libraryManager) + : base(serverConfigurationManager, logger, providerManager, fileSystem, userDataManager, libraryManager) + { + } + + /// <inheritdoc /> + protected override bool EnableUpdatingPremiereDateFromChildren => true; + + /// <inheritdoc /> + protected override bool EnableUpdatingGenresFromChildren => true; + + /// <inheritdoc /> + protected override bool EnableUpdatingStudiosFromChildren => true; + + /// <inheritdoc /> protected override IList<BaseItem> GetChildrenForMetadataUpdates(MusicAlbum item) { return item.GetRecursiveChildren(i => i is Audio) .ToList(); } + /// <inheritdoc /> protected override ItemUpdateType UpdateMetadataFromChildren(MusicAlbum item, IList<BaseItem> children, bool isFullRefresh, ItemUpdateType currentUpdateType) { var updateType = base.UpdateMetadataFromChildren(item, children, isFullRefresh, currentUpdateType); @@ -50,12 +72,6 @@ namespace MediaBrowser.Providers.Music return updateType; } - protected override bool EnableUpdatingPremiereDateFromChildren => true; - - protected override bool EnableUpdatingGenresFromChildren => true; - - protected override bool EnableUpdatingStudiosFromChildren => true; - private ItemUpdateType SetAlbumArtistFromSongs(MusicAlbum item, IEnumerable<Audio> songs) { var updateType = ItemUpdateType.None; @@ -94,21 +110,23 @@ namespace MediaBrowser.Providers.Music return updateType; } - protected override void MergeData(MetadataResult<MusicAlbum> source, MetadataResult<MusicAlbum> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings) + /// <inheritdoc /> + protected override void MergeData( + MetadataResult<MusicAlbum> source, + MetadataResult<MusicAlbum> target, + MetadataFields[] lockedFields, + bool replaceData, + bool mergeMetadataSettings) { ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings); var sourceItem = source.Item; var targetItem = target.Item; - if (replaceData || targetItem.Artists.Length == 0) + if (replaceData || targetItem.Artists.Count == 0) { targetItem.Artists = sourceItem.Artists; } } - - public AlbumMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager) : base(serverConfigurationManager, logger, providerManager, fileSystem, userDataManager, libraryManager) - { - } } } diff --git a/MediaBrowser.Providers/Music/AudioMetadataService.cs b/MediaBrowser.Providers/Music/AudioMetadataService.cs index 1422a4eaa..3bf854b91 100644 --- a/MediaBrowser.Providers/Music/AudioMetadataService.cs +++ b/MediaBrowser.Providers/Music/AudioMetadataService.cs @@ -11,6 +11,18 @@ namespace MediaBrowser.Providers.Music { public class AudioMetadataService : MetadataService<Audio, SongInfo> { + public AudioMetadataService( + IServerConfigurationManager serverConfigurationManager, + ILogger logger, + IProviderManager providerManager, + IFileSystem fileSystem, + IUserDataManager userDataManager, + ILibraryManager libraryManager) + : base(serverConfigurationManager, logger, providerManager, fileSystem, userDataManager, libraryManager) + { + } + + /// <inheritdoc /> protected override void MergeData(MetadataResult<Audio> source, MetadataResult<Audio> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings) { ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings); @@ -18,7 +30,7 @@ namespace MediaBrowser.Providers.Music var sourceItem = source.Item; var targetItem = target.Item; - if (replaceData || targetItem.Artists.Length == 0) + if (replaceData || targetItem.Artists.Count == 0) { targetItem.Artists = sourceItem.Artists; } @@ -28,9 +40,5 @@ namespace MediaBrowser.Providers.Music targetItem.Album = sourceItem.Album; } } - - public AudioMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager) : base(serverConfigurationManager, logger, providerManager, fileSystem, userDataManager, libraryManager) - { - } } } diff --git a/MediaBrowser.Providers/Music/MusicVideoMetadataService.cs b/MediaBrowser.Providers/Music/MusicVideoMetadataService.cs index 93412306f..c743ffcb0 100644 --- a/MediaBrowser.Providers/Music/MusicVideoMetadataService.cs +++ b/MediaBrowser.Providers/Music/MusicVideoMetadataService.cs @@ -11,7 +11,24 @@ namespace MediaBrowser.Providers.Music { public class MusicVideoMetadataService : MetadataService<MusicVideo, MusicVideoInfo> { - protected override void MergeData(MetadataResult<MusicVideo> source, MetadataResult<MusicVideo> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings) + public MusicVideoMetadataService( + IServerConfigurationManager serverConfigurationManager, + ILogger logger, + IProviderManager providerManager, + IFileSystem fileSystem, + IUserDataManager userDataManager, + ILibraryManager libraryManager) + : base(serverConfigurationManager, logger, providerManager, fileSystem, userDataManager, libraryManager) + { + } + + /// <inheritdoc /> + protected override void MergeData( + MetadataResult<MusicVideo> source, + MetadataResult<MusicVideo> target, + MetadataFields[] lockedFields, + bool replaceData, + bool mergeMetadataSettings) { ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings); @@ -23,14 +40,10 @@ namespace MediaBrowser.Providers.Music targetItem.Album = sourceItem.Album; } - if (replaceData || targetItem.Artists.Length == 0) + if (replaceData || targetItem.Artists.Count == 0) { targetItem.Artists = sourceItem.Artists; } } - - public MusicVideoMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager) : base(serverConfigurationManager, logger, providerManager, fileSystem, userDataManager, libraryManager) - { - } } } diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs index 1578e4341..10ed4f073 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Text; +using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Net; @@ -312,14 +313,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB } sb.Replace(", the", string.Empty).Replace("the ", " ").Replace(" the ", " "); - int prevLength; - do - { - prevLength = sb.Length; - sb.Replace(" ", " "); - } while (name.Length != prevLength); - - return sb.ToString().Trim(); + return Regex.Replace(sb.ToString().Trim(), @"\s+", " "); } private void MapSeriesToResult(MetadataResult<Series> result, TvDbSharper.Dto.Series tvdbSeries, string metadataLanguage) diff --git a/MediaBrowser.Providers/Tmdb/BoxSets/TmdbBoxSetExternalId.cs b/MediaBrowser.Providers/Tmdb/BoxSets/TmdbBoxSetExternalId.cs new file mode 100644 index 000000000..187295e1e --- /dev/null +++ b/MediaBrowser.Providers/Tmdb/BoxSets/TmdbBoxSetExternalId.cs @@ -0,0 +1,25 @@ +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.Movies; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Entities; + +namespace MediaBrowser.Providers.Tmdb.BoxSets +{ + public class TmdbBoxSetExternalId : IExternalId + { + /// <inheritdoc /> + public string Name => TmdbUtils.ProviderName; + + /// <inheritdoc /> + public string Key => MetadataProviders.TmdbCollection.ToString(); + + /// <inheritdoc /> + public string UrlFormatString => TmdbUtils.BaseTmdbUrl + "collection/{0}"; + + /// <inheritdoc /> + public bool Supports(IHasProviderIds item) + { + return item is Movie || item is MusicVideo || item is Trailer; + } + } +} diff --git a/MediaBrowser.Providers/BoxSets/MovieDbBoxSetImageProvider.cs b/MediaBrowser.Providers/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs index 4d12b2f4a..5db0edac2 100644 --- a/MediaBrowser.Providers/BoxSets/MovieDbBoxSetImageProvider.cs +++ b/MediaBrowser.Providers/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs @@ -11,21 +11,24 @@ using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Providers; using MediaBrowser.Providers.Movies; +using MediaBrowser.Providers.Tmdb.Models.Collections; +using MediaBrowser.Providers.Tmdb.Models.General; +using MediaBrowser.Providers.Tmdb.Movies; -namespace MediaBrowser.Providers.BoxSets +namespace MediaBrowser.Providers.Tmdb.BoxSets { - public class MovieDbBoxSetImageProvider : IRemoteImageProvider, IHasOrder + public class TmdbBoxSetImageProvider : IRemoteImageProvider, IHasOrder { private readonly IHttpClient _httpClient; - public MovieDbBoxSetImageProvider(IHttpClient httpClient) + public TmdbBoxSetImageProvider(IHttpClient httpClient) { _httpClient = httpClient; } public string Name => ProviderName; - public static string ProviderName => "TheMovieDb"; + public static string ProviderName => TmdbUtils.ProviderName; public bool Supports(BaseItem item) { @@ -49,11 +52,11 @@ namespace MediaBrowser.Providers.BoxSets { var language = item.GetPreferredMetadataLanguage(); - var mainResult = await MovieDbBoxSetProvider.Current.GetMovieDbResult(tmdbId, null, cancellationToken).ConfigureAwait(false); + var mainResult = await TmdbBoxSetProvider.Current.GetMovieDbResult(tmdbId, null, cancellationToken).ConfigureAwait(false); if (mainResult != null) { - var tmdbSettings = await MovieDbProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false); + var tmdbSettings = await TmdbMovieProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false); var tmdbImageUrl = tmdbSettings.images.GetImageUrl("original"); @@ -64,20 +67,20 @@ namespace MediaBrowser.Providers.BoxSets return new List<RemoteImageInfo>(); } - private IEnumerable<RemoteImageInfo> GetImages(MovieDbBoxSetProvider.RootObject obj, string language, string baseUrl) + private IEnumerable<RemoteImageInfo> GetImages(CollectionResult obj, string language, string baseUrl) { var list = new List<RemoteImageInfo>(); - var images = obj.images ?? new MovieDbBoxSetProvider.Images(); + var images = obj.Images ?? new CollectionImages(); list.AddRange(GetPosters(images).Select(i => new RemoteImageInfo { - Url = baseUrl + i.file_path, - CommunityRating = i.vote_average, - VoteCount = i.vote_count, - Width = i.width, - Height = i.height, - Language = MovieDbProvider.AdjustImageLanguage(i.iso_639_1, language), + Url = baseUrl + i.File_Path, + CommunityRating = i.Vote_Average, + VoteCount = i.Vote_Count, + Width = i.Width, + Height = i.Height, + Language = TmdbMovieProvider.AdjustImageLanguage(i.Iso_639_1, language), ProviderName = Name, Type = ImageType.Primary, RatingType = RatingType.Score @@ -85,11 +88,11 @@ namespace MediaBrowser.Providers.BoxSets list.AddRange(GetBackdrops(images).Select(i => new RemoteImageInfo { - Url = baseUrl + i.file_path, - CommunityRating = i.vote_average, - VoteCount = i.vote_count, - Width = i.width, - Height = i.height, + Url = baseUrl + i.File_Path, + CommunityRating = i.Vote_Average, + VoteCount = i.Vote_Count, + Width = i.Width, + Height = i.Height, ProviderName = Name, Type = ImageType.Backdrop, RatingType = RatingType.Score @@ -125,9 +128,9 @@ namespace MediaBrowser.Providers.BoxSets /// </summary> /// <param name="images">The images.</param> /// <returns>IEnumerable{MovieDbProvider.Poster}.</returns> - private IEnumerable<MovieDbBoxSetProvider.Poster> GetPosters(MovieDbBoxSetProvider.Images images) + private IEnumerable<Poster> GetPosters(CollectionImages images) { - return images.posters ?? new List<MovieDbBoxSetProvider.Poster>(); + return images.Posters ?? new List<Poster>(); } /// <summary> @@ -135,13 +138,13 @@ namespace MediaBrowser.Providers.BoxSets /// </summary> /// <param name="images">The images.</param> /// <returns>IEnumerable{MovieDbProvider.Backdrop}.</returns> - private IEnumerable<MovieDbBoxSetProvider.Backdrop> GetBackdrops(MovieDbBoxSetProvider.Images images) + private IEnumerable<Backdrop> GetBackdrops(CollectionImages images) { - var eligibleBackdrops = images.backdrops == null ? new List<MovieDbBoxSetProvider.Backdrop>() : - images.backdrops; + var eligibleBackdrops = images.Backdrops == null ? new List<Backdrop>() : + images.Backdrops; - return eligibleBackdrops.OrderByDescending(i => i.vote_average) - .ThenByDescending(i => i.vote_count); + return eligibleBackdrops.OrderByDescending(i => i.Vote_Average) + .ThenByDescending(i => i.Vote_Count); } public int Order => 0; diff --git a/MediaBrowser.Providers/BoxSets/MovieDbBoxSetProvider.cs b/MediaBrowser.Providers/Tmdb/BoxSets/TmdbBoxSetProvider.cs index 4e41694c4..a215177a9 100644 --- a/MediaBrowser.Providers/BoxSets/MovieDbBoxSetProvider.cs +++ b/MediaBrowser.Providers/Tmdb/BoxSets/TmdbBoxSetProvider.cs @@ -16,16 +16,18 @@ using MediaBrowser.Model.Globalization; using MediaBrowser.Model.IO; using MediaBrowser.Model.Providers; using MediaBrowser.Model.Serialization; -using MediaBrowser.Providers.Movies; +using MediaBrowser.Providers.Tmdb.Models.Collections; +using MediaBrowser.Providers.Tmdb.Models.General; +using MediaBrowser.Providers.Tmdb.Movies; using Microsoft.Extensions.Logging; -namespace MediaBrowser.Providers.BoxSets +namespace MediaBrowser.Providers.Tmdb.BoxSets { - public class MovieDbBoxSetProvider : IRemoteMetadataProvider<BoxSet, BoxSetInfo> + public class TmdbBoxSetProvider : IRemoteMetadataProvider<BoxSet, BoxSetInfo> { - private const string GetCollectionInfo3 = MovieDbProvider.BaseMovieDbUrl + @"3/collection/{0}?api_key={1}&append_to_response=images"; + private const string GetCollectionInfo3 = TmdbUtils.BaseTmdbApiUrl + @"3/collection/{0}?api_key={1}&append_to_response=images"; - internal static MovieDbBoxSetProvider Current; + internal static TmdbBoxSetProvider Current; private readonly ILogger _logger; private readonly IJsonSerializer _json; @@ -35,7 +37,7 @@ namespace MediaBrowser.Providers.BoxSets private readonly IHttpClient _httpClient; private readonly ILibraryManager _libraryManager; - public MovieDbBoxSetProvider(ILogger logger, IJsonSerializer json, IServerConfigurationManager config, IFileSystem fileSystem, ILocalizationManager localization, IHttpClient httpClient, ILibraryManager libraryManager) + public TmdbBoxSetProvider(ILogger logger, IJsonSerializer json, IServerConfigurationManager config, IFileSystem fileSystem, ILocalizationManager localization, IHttpClient httpClient, ILibraryManager libraryManager) { _logger = logger; _json = json; @@ -58,29 +60,29 @@ namespace MediaBrowser.Providers.BoxSets await EnsureInfo(tmdbId, searchInfo.MetadataLanguage, cancellationToken).ConfigureAwait(false); var dataFilePath = GetDataFilePath(_config.ApplicationPaths, tmdbId, searchInfo.MetadataLanguage); - var info = _json.DeserializeFromFile<RootObject>(dataFilePath); + var info = _json.DeserializeFromFile<CollectionResult>(dataFilePath); - var images = (info.images ?? new Images()).posters ?? new List<Poster>(); + var images = (info.Images ?? new CollectionImages()).Posters ?? new List<Poster>(); - var tmdbSettings = await MovieDbProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false); + var tmdbSettings = await TmdbMovieProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false); var tmdbImageUrl = tmdbSettings.images.GetImageUrl("original"); var result = new RemoteSearchResult { - Name = info.name, + Name = info.Name, SearchProviderName = Name, - ImageUrl = images.Count == 0 ? null : (tmdbImageUrl + images[0].file_path) + ImageUrl = images.Count == 0 ? null : (tmdbImageUrl + images[0].File_Path) }; - result.SetProviderId(MetadataProviders.Tmdb, info.id.ToString(_usCulture)); + result.SetProviderId(MetadataProviders.Tmdb, info.Id.ToString(_usCulture)); return new[] { result }; } - return await new MovieDbSearch(_logger, _json, _libraryManager).GetSearchResults(searchInfo, cancellationToken).ConfigureAwait(false); + return await new TmdbSearch(_logger, _json, _libraryManager).GetSearchResults(searchInfo, cancellationToken).ConfigureAwait(false); } public async Task<MetadataResult<BoxSet>> GetMetadata(BoxSetInfo id, CancellationToken cancellationToken) @@ -90,7 +92,7 @@ namespace MediaBrowser.Providers.BoxSets // We don't already have an Id, need to fetch it if (string.IsNullOrEmpty(tmdbId)) { - var searchResults = await new MovieDbSearch(_logger, _json, _libraryManager).GetSearchResults(id, cancellationToken).ConfigureAwait(false); + var searchResults = await new TmdbSearch(_logger, _json, _libraryManager).GetSearchResults(id, cancellationToken).ConfigureAwait(false); var searchResult = searchResults.FirstOrDefault(); @@ -116,7 +118,7 @@ namespace MediaBrowser.Providers.BoxSets return result; } - internal async Task<RootObject> GetMovieDbResult(string tmdbId, string language, CancellationToken cancellationToken) + internal async Task<CollectionResult> GetMovieDbResult(string tmdbId, string language, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(tmdbId)) { @@ -129,21 +131,21 @@ namespace MediaBrowser.Providers.BoxSets if (!string.IsNullOrEmpty(dataFilePath)) { - return _json.DeserializeFromFile<RootObject>(dataFilePath); + return _json.DeserializeFromFile<CollectionResult>(dataFilePath); } return null; } - private BoxSet GetItem(RootObject obj) + private BoxSet GetItem(CollectionResult obj) { var item = new BoxSet { - Name = obj.name, - Overview = obj.overview + Name = obj.Name, + Overview = obj.Overview }; - item.SetProviderId(MetadataProviders.Tmdb, obj.id.ToString(_usCulture)); + item.SetProviderId(MetadataProviders.Tmdb, obj.Id.ToString(_usCulture)); return item; } @@ -161,61 +163,61 @@ namespace MediaBrowser.Providers.BoxSets _json.SerializeToFile(mainResult, dataFilePath); } - private async Task<RootObject> FetchMainResult(string id, string language, CancellationToken cancellationToken) + private async Task<CollectionResult> FetchMainResult(string id, string language, CancellationToken cancellationToken) { - var url = string.Format(GetCollectionInfo3, id, MovieDbProvider.ApiKey); + var url = string.Format(GetCollectionInfo3, id, TmdbUtils.ApiKey); if (!string.IsNullOrEmpty(language)) { - url += string.Format("&language={0}", MovieDbProvider.NormalizeLanguage(language)); + url += string.Format("&language={0}", TmdbMovieProvider.NormalizeLanguage(language)); // Get images in english and with no language - url += "&include_image_language=" + MovieDbProvider.GetImageLanguagesParam(language); + url += "&include_image_language=" + TmdbMovieProvider.GetImageLanguagesParam(language); } cancellationToken.ThrowIfCancellationRequested(); - RootObject mainResult = null; + CollectionResult mainResult; - using (var response = await MovieDbProvider.Current.GetMovieDbResponse(new HttpRequestOptions + using (var response = await TmdbMovieProvider.Current.GetMovieDbResponse(new HttpRequestOptions { Url = url, CancellationToken = cancellationToken, - AcceptHeader = MovieDbSearch.AcceptHeader + AcceptHeader = TmdbUtils.AcceptHeader }).ConfigureAwait(false)) { using (var json = response.Content) { - mainResult = await _json.DeserializeFromStreamAsync<RootObject>(json).ConfigureAwait(false); + mainResult = await _json.DeserializeFromStreamAsync<CollectionResult>(json).ConfigureAwait(false); } } cancellationToken.ThrowIfCancellationRequested(); - if (mainResult != null && string.IsNullOrEmpty(mainResult.name)) + if (mainResult != null && string.IsNullOrEmpty(mainResult.Name)) { if (!string.IsNullOrEmpty(language) && !string.Equals(language, "en", StringComparison.OrdinalIgnoreCase)) { - url = string.Format(GetCollectionInfo3, id, MovieDbSearch.ApiKey) + "&language=en"; + url = string.Format(GetCollectionInfo3, id, TmdbUtils.ApiKey) + "&language=en"; if (!string.IsNullOrEmpty(language)) { // Get images in english and with no language - url += "&include_image_language=" + MovieDbProvider.GetImageLanguagesParam(language); + url += "&include_image_language=" + TmdbMovieProvider.GetImageLanguagesParam(language); } - using (var response = await MovieDbProvider.Current.GetMovieDbResponse(new HttpRequestOptions + using (var response = await TmdbMovieProvider.Current.GetMovieDbResponse(new HttpRequestOptions { Url = url, CancellationToken = cancellationToken, - AcceptHeader = MovieDbSearch.AcceptHeader + AcceptHeader = TmdbUtils.AcceptHeader }).ConfigureAwait(false)) { using (var json = response.Content) { - mainResult = await _json.DeserializeFromStreamAsync<RootObject>(json).ConfigureAwait(false); + mainResult = await _json.DeserializeFromStreamAsync<CollectionResult>(json).ConfigureAwait(false); } } } @@ -241,7 +243,7 @@ namespace MediaBrowser.Providers.BoxSets return DownloadInfo(tmdbId, preferredMetadataLanguage, cancellationToken); } - public string Name => "TheMovieDb"; + public string Name => TmdbUtils.ProviderName; private static string GetDataFilePath(IApplicationPaths appPaths, string tmdbId, string preferredLanguage) { @@ -266,54 +268,6 @@ namespace MediaBrowser.Providers.BoxSets return dataPath; } - internal class Part - { - public string title { get; set; } - public int id { get; set; } - public string release_date { get; set; } - public string poster_path { get; set; } - public string backdrop_path { get; set; } - } - - internal class Backdrop - { - public double aspect_ratio { get; set; } - public string file_path { get; set; } - public int height { get; set; } - public string iso_639_1 { get; set; } - public double vote_average { get; set; } - public int vote_count { get; set; } - public int width { get; set; } - } - - internal class Poster - { - public double aspect_ratio { get; set; } - public string file_path { get; set; } - public int height { get; set; } - public string iso_639_1 { get; set; } - public double vote_average { get; set; } - public int vote_count { get; set; } - public int width { get; set; } - } - - internal class Images - { - public List<Backdrop> backdrops { get; set; } - public List<Poster> posters { get; set; } - } - - internal class RootObject - { - public int id { get; set; } - public string name { get; set; } - public string overview { get; set; } - public string poster_path { get; set; } - public string backdrop_path { get; set; } - public List<Part> parts { get; set; } - public Images images { get; set; } - } - public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken) { return _httpClient.GetResponse(new HttpRequestOptions diff --git a/MediaBrowser.Providers/Tmdb/Models/Collections/CollectionImages.cs b/MediaBrowser.Providers/Tmdb/Models/Collections/CollectionImages.cs new file mode 100644 index 000000000..18f26c397 --- /dev/null +++ b/MediaBrowser.Providers/Tmdb/Models/Collections/CollectionImages.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; +using MediaBrowser.Providers.Tmdb.Models.General; + +namespace MediaBrowser.Providers.Tmdb.Models.Collections +{ + public class CollectionImages + { + public List<Backdrop> Backdrops { get; set; } + public List<Poster> Posters { get; set; } + } +} diff --git a/MediaBrowser.Providers/Tmdb/Models/Collections/CollectionResult.cs b/MediaBrowser.Providers/Tmdb/Models/Collections/CollectionResult.cs new file mode 100644 index 000000000..53d2599f8 --- /dev/null +++ b/MediaBrowser.Providers/Tmdb/Models/Collections/CollectionResult.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; + +namespace MediaBrowser.Providers.Tmdb.Models.Collections +{ + public class CollectionResult + { + public int Id { get; set; } + public string Name { get; set; } + public string Overview { get; set; } + public string Poster_Path { get; set; } + public string Backdrop_Path { get; set; } + public List<Part> Parts { get; set; } + public CollectionImages Images { get; set; } + } +} diff --git a/MediaBrowser.Providers/Tmdb/Models/Collections/Part.cs b/MediaBrowser.Providers/Tmdb/Models/Collections/Part.cs new file mode 100644 index 000000000..ff19291c7 --- /dev/null +++ b/MediaBrowser.Providers/Tmdb/Models/Collections/Part.cs @@ -0,0 +1,11 @@ +namespace MediaBrowser.Providers.Tmdb.Models.Collections +{ + public class Part + { + public string Title { get; set; } + public int Id { get; set; } + public string Release_Date { get; set; } + public string Poster_Path { get; set; } + public string Backdrop_Path { get; set; } + } +} diff --git a/MediaBrowser.Providers/Tmdb/Models/General/Backdrop.cs b/MediaBrowser.Providers/Tmdb/Models/General/Backdrop.cs new file mode 100644 index 000000000..db4cd6681 --- /dev/null +++ b/MediaBrowser.Providers/Tmdb/Models/General/Backdrop.cs @@ -0,0 +1,13 @@ +namespace MediaBrowser.Providers.Tmdb.Models.General +{ + public class Backdrop + { + public double Aspect_Ratio { get; set; } + public string File_Path { get; set; } + public int Height { get; set; } + public string Iso_639_1 { get; set; } + public double Vote_Average { get; set; } + public int Vote_Count { get; set; } + public int Width { get; set; } + } +} diff --git a/MediaBrowser.Providers/Tmdb/Models/General/Crew.cs b/MediaBrowser.Providers/Tmdb/Models/General/Crew.cs new file mode 100644 index 000000000..47b985403 --- /dev/null +++ b/MediaBrowser.Providers/Tmdb/Models/General/Crew.cs @@ -0,0 +1,12 @@ +namespace MediaBrowser.Providers.Tmdb.Models.General +{ + public class Crew + { + public int Id { get; set; } + public string Credit_Id { get; set; } + public string Name { get; set; } + public string Department { get; set; } + public string Job { get; set; } + public string Profile_Path { get; set; } + } +} diff --git a/MediaBrowser.Providers/Tmdb/Models/General/ExternalIds.cs b/MediaBrowser.Providers/Tmdb/Models/General/ExternalIds.cs new file mode 100644 index 000000000..37e37b0be --- /dev/null +++ b/MediaBrowser.Providers/Tmdb/Models/General/ExternalIds.cs @@ -0,0 +1,11 @@ +namespace MediaBrowser.Providers.Tmdb.Models.General +{ + public class ExternalIds + { + public string Imdb_Id { get; set; } + public object Freebase_Id { get; set; } + public string Freebase_Mid { get; set; } + public int Tvdb_Id { get; set; } + public int Tvrage_Id { get; set; } + } +} diff --git a/MediaBrowser.Providers/Tmdb/Models/General/Genre.cs b/MediaBrowser.Providers/Tmdb/Models/General/Genre.cs new file mode 100644 index 000000000..9a6686d50 --- /dev/null +++ b/MediaBrowser.Providers/Tmdb/Models/General/Genre.cs @@ -0,0 +1,8 @@ +namespace MediaBrowser.Providers.Tmdb.Models.General +{ + public class Genre + { + public int Id { get; set; } + public string Name { get; set; } + } +} diff --git a/MediaBrowser.Providers/Tmdb/Models/General/Images.cs b/MediaBrowser.Providers/Tmdb/Models/General/Images.cs new file mode 100644 index 000000000..f1c99537d --- /dev/null +++ b/MediaBrowser.Providers/Tmdb/Models/General/Images.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; + +namespace MediaBrowser.Providers.Tmdb.Models.General +{ + public class Images + { + public List<Backdrop> Backdrops { get; set; } + public List<Poster> Posters { get; set; } + } +} diff --git a/MediaBrowser.Providers/Tmdb/Models/General/Keyword.cs b/MediaBrowser.Providers/Tmdb/Models/General/Keyword.cs new file mode 100644 index 000000000..4e3011349 --- /dev/null +++ b/MediaBrowser.Providers/Tmdb/Models/General/Keyword.cs @@ -0,0 +1,8 @@ +namespace MediaBrowser.Providers.Tmdb.Models.General +{ + public class Keyword + { + public int Id { get; set; } + public string Name { get; set; } + } +} diff --git a/MediaBrowser.Providers/Tmdb/Models/General/Keywords.cs b/MediaBrowser.Providers/Tmdb/Models/General/Keywords.cs new file mode 100644 index 000000000..1950a51b3 --- /dev/null +++ b/MediaBrowser.Providers/Tmdb/Models/General/Keywords.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; + +namespace MediaBrowser.Providers.Tmdb.Models.General +{ + public class Keywords + { + public List<Keyword> Results { get; set; } + } +} diff --git a/MediaBrowser.Providers/Tmdb/Models/General/Poster.cs b/MediaBrowser.Providers/Tmdb/Models/General/Poster.cs new file mode 100644 index 000000000..33401b15d --- /dev/null +++ b/MediaBrowser.Providers/Tmdb/Models/General/Poster.cs @@ -0,0 +1,13 @@ +namespace MediaBrowser.Providers.Tmdb.Models.General +{ + public class Poster + { + public double Aspect_Ratio { get; set; } + public string File_Path { get; set; } + public int Height { get; set; } + public string Iso_639_1 { get; set; } + public double Vote_Average { get; set; } + public int Vote_Count { get; set; } + public int Width { get; set; } + } +} diff --git a/MediaBrowser.Providers/Tmdb/Models/General/Profile.cs b/MediaBrowser.Providers/Tmdb/Models/General/Profile.cs new file mode 100644 index 000000000..73a049c73 --- /dev/null +++ b/MediaBrowser.Providers/Tmdb/Models/General/Profile.cs @@ -0,0 +1,11 @@ +namespace MediaBrowser.Providers.Tmdb.Models.General +{ + public class Profile + { + public string File_Path { get; set; } + public int Width { get; set; } + public int Height { get; set; } + public object Iso_639_1 { get; set; } + public double Aspect_Ratio { get; set; } + } +} diff --git a/MediaBrowser.Providers/Tmdb/Models/General/Still.cs b/MediaBrowser.Providers/Tmdb/Models/General/Still.cs new file mode 100644 index 000000000..15ff4a099 --- /dev/null +++ b/MediaBrowser.Providers/Tmdb/Models/General/Still.cs @@ -0,0 +1,14 @@ +namespace MediaBrowser.Providers.Tmdb.Models.General +{ + public class Still + { + public double Aspect_Ratio { get; set; } + public string File_Path { get; set; } + public int Height { get; set; } + public string Id { get; set; } + public string Iso_639_1 { get; set; } + public double Vote_Average { get; set; } + public int Vote_Count { get; set; } + public int Width { get; set; } + } +} diff --git a/MediaBrowser.Providers/Tmdb/Models/General/StillImages.cs b/MediaBrowser.Providers/Tmdb/Models/General/StillImages.cs new file mode 100644 index 000000000..266965c47 --- /dev/null +++ b/MediaBrowser.Providers/Tmdb/Models/General/StillImages.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; + +namespace MediaBrowser.Providers.Tmdb.Models.General +{ + public class StillImages + { + public List<Still> Stills { get; set; } + } +} diff --git a/MediaBrowser.Providers/Tmdb/Models/General/Video.cs b/MediaBrowser.Providers/Tmdb/Models/General/Video.cs new file mode 100644 index 000000000..fb69e7767 --- /dev/null +++ b/MediaBrowser.Providers/Tmdb/Models/General/Video.cs @@ -0,0 +1,14 @@ +namespace MediaBrowser.Providers.Tmdb.Models.General +{ + public class Video + { + public string Id { get; set; } + public string Iso_639_1 { get; set; } + public string Iso_3166_1 { get; set; } + public string Key { get; set; } + public string Name { get; set; } + public string Site { get; set; } + public string Size { get; set; } + public string Type { get; set; } + } +} diff --git a/MediaBrowser.Providers/Tmdb/Models/General/Videos.cs b/MediaBrowser.Providers/Tmdb/Models/General/Videos.cs new file mode 100644 index 000000000..26812780d --- /dev/null +++ b/MediaBrowser.Providers/Tmdb/Models/General/Videos.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; + +namespace MediaBrowser.Providers.Tmdb.Models.General +{ + public class Videos + { + public List<Video> Results { get; set; } + } +} diff --git a/MediaBrowser.Providers/Tmdb/Models/Movies/BelongsToCollection.cs b/MediaBrowser.Providers/Tmdb/Models/Movies/BelongsToCollection.cs new file mode 100644 index 000000000..ac673df61 --- /dev/null +++ b/MediaBrowser.Providers/Tmdb/Models/Movies/BelongsToCollection.cs @@ -0,0 +1,10 @@ +namespace MediaBrowser.Providers.Tmdb.Models.Movies +{ + public class BelongsToCollection + { + public int Id { get; set; } + public string Name { get; set; } + public string Poster_Path { get; set; } + public string Backdrop_Path { get; set; } + } +} diff --git a/MediaBrowser.Providers/Tmdb/Models/Movies/Cast.cs b/MediaBrowser.Providers/Tmdb/Models/Movies/Cast.cs new file mode 100644 index 000000000..44af9e568 --- /dev/null +++ b/MediaBrowser.Providers/Tmdb/Models/Movies/Cast.cs @@ -0,0 +1,12 @@ +namespace MediaBrowser.Providers.Tmdb.Models.Movies +{ + public class Cast + { + public int Id { get; set; } + public string Name { get; set; } + public string Character { get; set; } + public int Order { get; set; } + public int Cast_Id { get; set; } + public string Profile_Path { get; set; } + } +} diff --git a/MediaBrowser.Providers/Tmdb/Models/Movies/Casts.cs b/MediaBrowser.Providers/Tmdb/Models/Movies/Casts.cs new file mode 100644 index 000000000..7b5094fa3 --- /dev/null +++ b/MediaBrowser.Providers/Tmdb/Models/Movies/Casts.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; +using MediaBrowser.Providers.Tmdb.Models.General; + +namespace MediaBrowser.Providers.Tmdb.Models.Movies +{ + public class Casts + { + public List<Cast> Cast { get; set; } + public List<Crew> Crew { get; set; } + } +} diff --git a/MediaBrowser.Providers/Tmdb/Models/Movies/Country.cs b/MediaBrowser.Providers/Tmdb/Models/Movies/Country.cs new file mode 100644 index 000000000..6f843addd --- /dev/null +++ b/MediaBrowser.Providers/Tmdb/Models/Movies/Country.cs @@ -0,0 +1,11 @@ +using System; + +namespace MediaBrowser.Providers.Tmdb.Models.Movies +{ + public class Country + { + public string Iso_3166_1 { get; set; } + public string Certification { get; set; } + public DateTime Release_Date { get; set; } + } +} diff --git a/MediaBrowser.Providers/Tmdb/Models/Movies/MovieResult.cs b/MediaBrowser.Providers/Tmdb/Models/Movies/MovieResult.cs new file mode 100644 index 000000000..1b262946f --- /dev/null +++ b/MediaBrowser.Providers/Tmdb/Models/Movies/MovieResult.cs @@ -0,0 +1,49 @@ +using System.Collections.Generic; +using MediaBrowser.Providers.Tmdb.Models.General; + +namespace MediaBrowser.Providers.Tmdb.Models.Movies +{ + public class MovieResult + { + public bool Adult { get; set; } + public string Backdrop_Path { get; set; } + public BelongsToCollection Belongs_To_Collection { get; set; } + public int Budget { get; set; } + public List<Genre> Genres { get; set; } + public string Homepage { get; set; } + public int Id { get; set; } + public string Imdb_Id { get; set; } + public string Original_Title { get; set; } + public string Original_Name { get; set; } + public string Overview { get; set; } + public double Popularity { get; set; } + public string Poster_Path { get; set; } + public List<ProductionCompany> Production_Companies { get; set; } + public List<ProductionCountry> Production_Countries { get; set; } + public string Release_Date { get; set; } + public int Revenue { get; set; } + public int Runtime { get; set; } + public List<SpokenLanguage> Spoken_Languages { get; set; } + public string Status { get; set; } + public string Tagline { get; set; } + public string Title { get; set; } + public string Name { get; set; } + public double Vote_Average { get; set; } + public int Vote_Count { get; set; } + public Casts Casts { get; set; } + public Releases Releases { get; set; } + public Images Images { get; set; } + public Keywords Keywords { get; set; } + public Trailers Trailers { get; set; } + + public string GetOriginalTitle() + { + return Original_Name ?? Original_Title; + } + + public string GetTitle() + { + return Name ?? Title ?? GetOriginalTitle(); + } + } +} diff --git a/MediaBrowser.Providers/Tmdb/Models/Movies/ProductionCompany.cs b/MediaBrowser.Providers/Tmdb/Models/Movies/ProductionCompany.cs new file mode 100644 index 000000000..c3382f305 --- /dev/null +++ b/MediaBrowser.Providers/Tmdb/Models/Movies/ProductionCompany.cs @@ -0,0 +1,8 @@ +namespace MediaBrowser.Providers.Tmdb.Models.Movies +{ + public class ProductionCompany + { + public string Name { get; set; } + public int Id { get; set; } + } +} diff --git a/MediaBrowser.Providers/Tmdb/Models/Movies/ProductionCountry.cs b/MediaBrowser.Providers/Tmdb/Models/Movies/ProductionCountry.cs new file mode 100644 index 000000000..78112c915 --- /dev/null +++ b/MediaBrowser.Providers/Tmdb/Models/Movies/ProductionCountry.cs @@ -0,0 +1,8 @@ +namespace MediaBrowser.Providers.Tmdb.Models.Movies +{ + public class ProductionCountry + { + public string Iso_3166_1 { get; set; } + public string Name { get; set; } + } +} diff --git a/MediaBrowser.Providers/Tmdb/Models/Movies/Releases.cs b/MediaBrowser.Providers/Tmdb/Models/Movies/Releases.cs new file mode 100644 index 000000000..c44f31e46 --- /dev/null +++ b/MediaBrowser.Providers/Tmdb/Models/Movies/Releases.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; + +namespace MediaBrowser.Providers.Tmdb.Models.Movies +{ + public class Releases + { + public List<Country> Countries { get; set; } + } +} diff --git a/MediaBrowser.Providers/Tmdb/Models/Movies/SpokenLanguage.cs b/MediaBrowser.Providers/Tmdb/Models/Movies/SpokenLanguage.cs new file mode 100644 index 000000000..4bc5cfa48 --- /dev/null +++ b/MediaBrowser.Providers/Tmdb/Models/Movies/SpokenLanguage.cs @@ -0,0 +1,8 @@ +namespace MediaBrowser.Providers.Tmdb.Models.Movies +{ + public class SpokenLanguage + { + public string Iso_639_1 { get; set; } + public string Name { get; set; } + } +} diff --git a/MediaBrowser.Providers/Tmdb/Models/Movies/Trailers.cs b/MediaBrowser.Providers/Tmdb/Models/Movies/Trailers.cs new file mode 100644 index 000000000..4bfa02f06 --- /dev/null +++ b/MediaBrowser.Providers/Tmdb/Models/Movies/Trailers.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; + +namespace MediaBrowser.Providers.Tmdb.Models.Movies +{ + public class Trailers + { + public List<Youtube> Youtube { get; set; } + } +} diff --git a/MediaBrowser.Providers/Tmdb/Models/Movies/Youtube.cs b/MediaBrowser.Providers/Tmdb/Models/Movies/Youtube.cs new file mode 100644 index 000000000..069572824 --- /dev/null +++ b/MediaBrowser.Providers/Tmdb/Models/Movies/Youtube.cs @@ -0,0 +1,9 @@ +namespace MediaBrowser.Providers.Tmdb.Models.Movies +{ + public class Youtube + { + public string Name { get; set; } + public string Size { get; set; } + public string Source { get; set; } + } +} diff --git a/MediaBrowser.Providers/Tmdb/Models/People/PersonImages.cs b/MediaBrowser.Providers/Tmdb/Models/People/PersonImages.cs new file mode 100644 index 000000000..113f410b2 --- /dev/null +++ b/MediaBrowser.Providers/Tmdb/Models/People/PersonImages.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; +using MediaBrowser.Providers.Tmdb.Models.General; + +namespace MediaBrowser.Providers.Tmdb.Models.People +{ + public class PersonImages + { + public List<Profile> Profiles { get; set; } + } +} diff --git a/MediaBrowser.Providers/Tmdb/Models/People/PersonResult.cs b/MediaBrowser.Providers/Tmdb/Models/People/PersonResult.cs new file mode 100644 index 000000000..6e997050f --- /dev/null +++ b/MediaBrowser.Providers/Tmdb/Models/People/PersonResult.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using MediaBrowser.Providers.Tmdb.Models.General; + +namespace MediaBrowser.Providers.Tmdb.Models.People +{ + public class PersonResult + { + public bool Adult { get; set; } + public List<string> Also_Known_As { get; set; } + public string Biography { get; set; } + public string Birthday { get; set; } + public string Deathday { get; set; } + public string Homepage { get; set; } + public int Id { get; set; } + public string Imdb_Id { get; set; } + public string Name { get; set; } + public string Place_Of_Birth { get; set; } + public double Popularity { get; set; } + public string Profile_Path { get; set; } + public PersonImages Images { get; set; } + public ExternalIds External_Ids { get; set; } + } +} diff --git a/MediaBrowser.Providers/Tmdb/Models/Search/ExternalIdLookupResult.cs b/MediaBrowser.Providers/Tmdb/Models/Search/ExternalIdLookupResult.cs new file mode 100644 index 000000000..6d9fe7081 --- /dev/null +++ b/MediaBrowser.Providers/Tmdb/Models/Search/ExternalIdLookupResult.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; +using MediaBrowser.Providers.Movies; + +namespace MediaBrowser.Providers.Tmdb.Models.Search +{ + public class ExternalIdLookupResult + { + public List<TvResult> Tv_Results { get; set; } + } +} diff --git a/MediaBrowser.Providers/Tmdb/Models/Search/MovieResult.cs b/MediaBrowser.Providers/Tmdb/Models/Search/MovieResult.cs new file mode 100644 index 000000000..25a211fa8 --- /dev/null +++ b/MediaBrowser.Providers/Tmdb/Models/Search/MovieResult.cs @@ -0,0 +1,65 @@ +namespace MediaBrowser.Providers.Tmdb.Models.Search +{ + public class MovieResult + { + /// <summary> + /// Gets or sets a value indicating whether this <see cref="TmdbMovieSearchResult" /> is adult. + /// </summary> + /// <value><c>true</c> if adult; otherwise, <c>false</c>.</value> + public bool Adult { get; set; } + /// <summary> + /// Gets or sets the backdrop_path. + /// </summary> + /// <value>The backdrop_path.</value> + public string Backdrop_Path { get; set; } + /// <summary> + /// Gets or sets the id. + /// </summary> + /// <value>The id.</value> + public int Id { get; set; } + /// <summary> + /// Gets or sets the original_title. + /// </summary> + /// <value>The original_title.</value> + public string Original_Title { get; set; } + /// <summary> + /// Gets or sets the original_name. + /// </summary> + /// <value>The original_name.</value> + public string Original_Name { get; set; } + /// <summary> + /// Gets or sets the release_date. + /// </summary> + /// <value>The release_date.</value> + public string Release_Date { get; set; } + /// <summary> + /// Gets or sets the poster_path. + /// </summary> + /// <value>The poster_path.</value> + public string Poster_Path { get; set; } + /// <summary> + /// Gets or sets the popularity. + /// </summary> + /// <value>The popularity.</value> + public double Popularity { get; set; } + /// <summary> + /// Gets or sets the title. + /// </summary> + /// <value>The title.</value> + public string Title { get; set; } + /// <summary> + /// Gets or sets the vote_average. + /// </summary> + /// <value>The vote_average.</value> + public double Vote_Average { get; set; } + /// <summary> + /// For collection search results + /// </summary> + public string Name { get; set; } + /// <summary> + /// Gets or sets the vote_count. + /// </summary> + /// <value>The vote_count.</value> + public int Vote_Count { get; set; } + } +} diff --git a/MediaBrowser.Providers/Tmdb/Models/Search/PersonSearchResult.cs b/MediaBrowser.Providers/Tmdb/Models/Search/PersonSearchResult.cs new file mode 100644 index 000000000..93916068f --- /dev/null +++ b/MediaBrowser.Providers/Tmdb/Models/Search/PersonSearchResult.cs @@ -0,0 +1,29 @@ +namespace MediaBrowser.Providers.Tmdb.Models.Search +{ + public class PersonSearchResult + { + /// <summary> + /// Gets or sets a value indicating whether this <see cref="PersonSearchResult" /> is adult. + /// </summary> + /// <value><c>true</c> if adult; otherwise, <c>false</c>.</value> + public bool Adult { get; set; } + + /// <summary> + /// Gets or sets the id. + /// </summary> + /// <value>The id.</value> + public int Id { get; set; } + + /// <summary> + /// Gets or sets the name. + /// </summary> + /// <value>The name.</value> + public string Name { get; set; } + + /// <summary> + /// Gets or sets the profile_ path. + /// </summary> + /// <value>The profile_ path.</value> + public string Profile_Path { get; set; } + } +} diff --git a/MediaBrowser.Providers/Tmdb/Models/Search/TmdbSearchResult.cs b/MediaBrowser.Providers/Tmdb/Models/Search/TmdbSearchResult.cs new file mode 100644 index 000000000..a9f888e75 --- /dev/null +++ b/MediaBrowser.Providers/Tmdb/Models/Search/TmdbSearchResult.cs @@ -0,0 +1,31 @@ +using System.Collections.Generic; + +namespace MediaBrowser.Providers.Tmdb.Models.Search +{ + public class TmdbSearchResult<T> + { + /// <summary> + /// Gets or sets the page. + /// </summary> + /// <value>The page.</value> + public int Page { get; set; } + + /// <summary> + /// Gets or sets the results. + /// </summary> + /// <value>The results.</value> + public List<T> Results { get; set; } + + /// <summary> + /// Gets or sets the total_pages. + /// </summary> + /// <value>The total_pages.</value> + public int Total_Pages { get; set; } + + /// <summary> + /// Gets or sets the total_results. + /// </summary> + /// <value>The total_results.</value> + public int Total_Results { get; set; } + } +} diff --git a/MediaBrowser.Providers/Tmdb/Models/Search/TvResult.cs b/MediaBrowser.Providers/Tmdb/Models/Search/TvResult.cs new file mode 100644 index 000000000..ed140bedd --- /dev/null +++ b/MediaBrowser.Providers/Tmdb/Models/Search/TvResult.cs @@ -0,0 +1,15 @@ +namespace MediaBrowser.Providers.Tmdb.Models.Search +{ + public class TvResult + { + public string Backdrop_Path { get; set; } + public string First_Air_Date { get; set; } + public int Id { get; set; } + public string Original_Name { get; set; } + public string Poster_Path { get; set; } + public double Popularity { get; set; } + public string Name { get; set; } + public double Vote_Average { get; set; } + public int Vote_Count { get; set; } + } +} diff --git a/MediaBrowser.Providers/Tmdb/Models/TV/Cast.cs b/MediaBrowser.Providers/Tmdb/Models/TV/Cast.cs new file mode 100644 index 000000000..c659df9ac --- /dev/null +++ b/MediaBrowser.Providers/Tmdb/Models/TV/Cast.cs @@ -0,0 +1,12 @@ +namespace MediaBrowser.Providers.Tmdb.Models.TV +{ + public class Cast + { + public string Character { get; set; } + public string Credit_Id { get; set; } + public int Id { get; set; } + public string Name { get; set; } + public string Profile_Path { get; set; } + public int Order { get; set; } + } +} diff --git a/MediaBrowser.Providers/Tmdb/Models/TV/ContentRating.cs b/MediaBrowser.Providers/Tmdb/Models/TV/ContentRating.cs new file mode 100644 index 000000000..3177cd71b --- /dev/null +++ b/MediaBrowser.Providers/Tmdb/Models/TV/ContentRating.cs @@ -0,0 +1,8 @@ +namespace MediaBrowser.Providers.Tmdb.Models.TV +{ + public class ContentRating + { + public string Iso_3166_1 { get; set; } + public string Rating { get; set; } + } +} diff --git a/MediaBrowser.Providers/Tmdb/Models/TV/ContentRatings.cs b/MediaBrowser.Providers/Tmdb/Models/TV/ContentRatings.cs new file mode 100644 index 000000000..883e605c9 --- /dev/null +++ b/MediaBrowser.Providers/Tmdb/Models/TV/ContentRatings.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; + +namespace MediaBrowser.Providers.Tmdb.Models.TV +{ + public class ContentRatings + { + public List<ContentRating> Results { get; set; } + } +} diff --git a/MediaBrowser.Providers/Tmdb/Models/TV/CreatedBy.cs b/MediaBrowser.Providers/Tmdb/Models/TV/CreatedBy.cs new file mode 100644 index 000000000..21588d897 --- /dev/null +++ b/MediaBrowser.Providers/Tmdb/Models/TV/CreatedBy.cs @@ -0,0 +1,9 @@ +namespace MediaBrowser.Providers.Tmdb.Models.TV +{ + public class CreatedBy + { + public int Id { get; set; } + public string Name { get; set; } + public string Profile_Path { get; set; } + } +} diff --git a/MediaBrowser.Providers/Tmdb/Models/TV/Credits.cs b/MediaBrowser.Providers/Tmdb/Models/TV/Credits.cs new file mode 100644 index 000000000..b62b5f605 --- /dev/null +++ b/MediaBrowser.Providers/Tmdb/Models/TV/Credits.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; +using MediaBrowser.Providers.Tmdb.Models.General; + +namespace MediaBrowser.Providers.Tmdb.Models.TV +{ + public class Credits + { + public List<Cast> Cast { get; set; } + public List<Crew> Crew { get; set; } + } +} diff --git a/MediaBrowser.Providers/Tmdb/Models/TV/Episode.cs b/MediaBrowser.Providers/Tmdb/Models/TV/Episode.cs new file mode 100644 index 000000000..ab11a6cd2 --- /dev/null +++ b/MediaBrowser.Providers/Tmdb/Models/TV/Episode.cs @@ -0,0 +1,14 @@ +namespace MediaBrowser.Providers.Tmdb.Models.TV +{ + public class Episode + { + public string Air_Date { get; set; } + public int Episode_Number { get; set; } + public int Id { get; set; } + public string Name { get; set; } + public string Overview { get; set; } + public string Still_Path { get; set; } + public double Vote_Average { get; set; } + public int Vote_Count { get; set; } + } +} diff --git a/MediaBrowser.Providers/Tmdb/Models/TV/EpisodeCredits.cs b/MediaBrowser.Providers/Tmdb/Models/TV/EpisodeCredits.cs new file mode 100644 index 000000000..1c86be0f4 --- /dev/null +++ b/MediaBrowser.Providers/Tmdb/Models/TV/EpisodeCredits.cs @@ -0,0 +1,12 @@ +using System.Collections.Generic; +using MediaBrowser.Providers.Tmdb.Models.General; + +namespace MediaBrowser.Providers.Tmdb.Models.TV +{ + public class EpisodeCredits + { + public List<Cast> Cast { get; set; } + public List<Crew> Crew { get; set; } + public List<GuestStar> Guest_Stars { get; set; } + } +} diff --git a/MediaBrowser.Providers/Tmdb/Models/TV/EpisodeResult.cs b/MediaBrowser.Providers/Tmdb/Models/TV/EpisodeResult.cs new file mode 100644 index 000000000..0513ce7e2 --- /dev/null +++ b/MediaBrowser.Providers/Tmdb/Models/TV/EpisodeResult.cs @@ -0,0 +1,23 @@ +using System; +using MediaBrowser.Providers.Tmdb.Models.General; + +namespace MediaBrowser.Providers.Tmdb.Models.TV +{ + public class EpisodeResult + { + public DateTime Air_Date { get; set; } + public int Episode_Number { get; set; } + public string Name { get; set; } + public string Overview { get; set; } + public int Id { get; set; } + public object Production_Code { get; set; } + public int Season_Number { get; set; } + public string Still_Path { get; set; } + public double Vote_Average { get; set; } + public int Vote_Count { get; set; } + public StillImages Images { get; set; } + public ExternalIds External_Ids { get; set; } + public EpisodeCredits Credits { get; set; } + public Tmdb.Models.General.Videos Videos { get; set; } + } +} diff --git a/MediaBrowser.Providers/Tmdb/Models/TV/GuestStar.cs b/MediaBrowser.Providers/Tmdb/Models/TV/GuestStar.cs new file mode 100644 index 000000000..2dfe7a862 --- /dev/null +++ b/MediaBrowser.Providers/Tmdb/Models/TV/GuestStar.cs @@ -0,0 +1,12 @@ +namespace MediaBrowser.Providers.Tmdb.Models.TV +{ + public class GuestStar + { + public int Id { get; set; } + public string Name { get; set; } + public string Credit_Id { get; set; } + public string Character { get; set; } + public int Order { get; set; } + public string Profile_Path { get; set; } + } +} diff --git a/MediaBrowser.Providers/Tmdb/Models/TV/Network.cs b/MediaBrowser.Providers/Tmdb/Models/TV/Network.cs new file mode 100644 index 000000000..f982682d1 --- /dev/null +++ b/MediaBrowser.Providers/Tmdb/Models/TV/Network.cs @@ -0,0 +1,8 @@ +namespace MediaBrowser.Providers.Tmdb.Models.TV +{ + public class Network + { + public int Id { get; set; } + public string Name { get; set; } + } +} diff --git a/MediaBrowser.Providers/Tmdb/Models/TV/Season.cs b/MediaBrowser.Providers/Tmdb/Models/TV/Season.cs new file mode 100644 index 000000000..976e3c97e --- /dev/null +++ b/MediaBrowser.Providers/Tmdb/Models/TV/Season.cs @@ -0,0 +1,11 @@ +namespace MediaBrowser.Providers.Tmdb.Models.TV +{ + public class Season + { + public string Air_Date { get; set; } + public int Episode_Count { get; set; } + public int Id { get; set; } + public string Poster_Path { get; set; } + public int Season_Number { get; set; } + } +} diff --git a/MediaBrowser.Providers/Tmdb/Models/TV/SeasonImages.cs b/MediaBrowser.Providers/Tmdb/Models/TV/SeasonImages.cs new file mode 100644 index 000000000..9a93dd6ae --- /dev/null +++ b/MediaBrowser.Providers/Tmdb/Models/TV/SeasonImages.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; +using MediaBrowser.Providers.Tmdb.Models.General; + +namespace MediaBrowser.Providers.Tmdb.Models.TV +{ + public class SeasonImages + { + public List<Poster> Posters { get; set; } + } +} diff --git a/MediaBrowser.Providers/Tmdb/Models/TV/SeasonResult.cs b/MediaBrowser.Providers/Tmdb/Models/TV/SeasonResult.cs new file mode 100644 index 000000000..bc9213c04 --- /dev/null +++ b/MediaBrowser.Providers/Tmdb/Models/TV/SeasonResult.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using MediaBrowser.Providers.Tmdb.Models.General; + +namespace MediaBrowser.Providers.Tmdb.Models.TV +{ + public class SeasonResult + { + public DateTime Air_Date { get; set; } + public List<Episode> Episodes { get; set; } + public string Name { get; set; } + public string Overview { get; set; } + public int Id { get; set; } + public string Poster_Path { get; set; } + public int Season_Number { get; set; } + public Credits Credits { get; set; } + public SeasonImages Images { get; set; } + public ExternalIds External_Ids { get; set; } + public General.Videos Videos { get; set; } + } +} diff --git a/MediaBrowser.Providers/Tmdb/Models/TV/SeriesResult.cs b/MediaBrowser.Providers/Tmdb/Models/TV/SeriesResult.cs new file mode 100644 index 000000000..ad95e502e --- /dev/null +++ b/MediaBrowser.Providers/Tmdb/Models/TV/SeriesResult.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using MediaBrowser.Providers.Tmdb.Models.General; + +namespace MediaBrowser.Providers.Tmdb.Models.TV +{ + public class SeriesResult + { + public string Backdrop_Path { get; set; } + public List<CreatedBy> Created_By { get; set; } + public List<int> Episode_Run_Time { get; set; } + public DateTime First_Air_Date { get; set; } + public List<Genre> Genres { get; set; } + public string Homepage { get; set; } + public int Id { get; set; } + public bool In_Production { get; set; } + public List<string> Languages { get; set; } + public DateTime Last_Air_Date { get; set; } + public string Name { get; set; } + public List<Network> Networks { get; set; } + public int Number_Of_Episodes { get; set; } + public int Number_Of_Seasons { get; set; } + public string Original_Name { get; set; } + public List<string> Origin_Country { get; set; } + public string Overview { get; set; } + public string Popularity { get; set; } + public string Poster_Path { get; set; } + public List<Season> Seasons { get; set; } + public string Status { get; set; } + public double Vote_Average { get; set; } + public int Vote_Count { get; set; } + public Credits Credits { get; set; } + public Images Images { get; set; } + public Keywords Keywords { get; set; } + public ExternalIds External_Ids { get; set; } + public General.Videos Videos { get; set; } + public ContentRatings Content_Ratings { get; set; } + public string ResultLanguage { get; set; } + } +} diff --git a/MediaBrowser.Providers/Movies/GenericMovieDbInfo.cs b/MediaBrowser.Providers/Tmdb/Movies/GenericTmdbMovieInfo.cs index 4300b84a9..b7b447b68 100644 --- a/MediaBrowser.Providers/Movies/GenericMovieDbInfo.cs +++ b/MediaBrowser.Providers/Tmdb/Movies/GenericTmdbMovieInfo.cs @@ -14,11 +14,13 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.Extensions; using MediaBrowser.Model.IO; using MediaBrowser.Model.Serialization; +using MediaBrowser.Providers.Movies; +using MediaBrowser.Providers.Tmdb.Models.Movies; using Microsoft.Extensions.Logging; -namespace MediaBrowser.Providers.Movies +namespace MediaBrowser.Providers.Tmdb.Movies { - public class GenericMovieDbInfo<T> + public class GenericTmdbMovieInfo<T> where T : BaseItem, new() { private readonly ILogger _logger; @@ -28,7 +30,7 @@ namespace MediaBrowser.Providers.Movies private readonly CultureInfo _usCulture = new CultureInfo("en-US"); - public GenericMovieDbInfo(ILogger logger, IJsonSerializer jsonSerializer, ILibraryManager libraryManager, IFileSystem fileSystem) + public GenericTmdbMovieInfo(ILogger logger, IJsonSerializer jsonSerializer, ILibraryManager libraryManager, IFileSystem fileSystem) { _logger = logger; _jsonSerializer = jsonSerializer; @@ -44,7 +46,7 @@ namespace MediaBrowser.Providers.Movies // Don't search for music video id's because it is very easy to misidentify. if (string.IsNullOrEmpty(tmdbId) && string.IsNullOrEmpty(imdbId) && typeof(T) != typeof(MusicVideo)) { - var searchResults = await new MovieDbSearch(_logger, _jsonSerializer, _libraryManager).GetMovieSearchResults(itemId, cancellationToken).ConfigureAwait(false); + var searchResults = await new TmdbSearch(_logger, _jsonSerializer, _libraryManager).GetMovieSearchResults(itemId, cancellationToken).ConfigureAwait(false); var searchResult = searchResults.FirstOrDefault(); @@ -81,17 +83,17 @@ namespace MediaBrowser.Providers.Movies }; string dataFilePath = null; - MovieDbProvider.CompleteMovieData movieInfo = null; + MovieResult movieInfo = null; // Id could be ImdbId or TmdbId if (string.IsNullOrEmpty(tmdbId)) { - movieInfo = await MovieDbProvider.Current.FetchMainResult(imdbId, false, language, cancellationToken).ConfigureAwait(false); + movieInfo = await TmdbMovieProvider.Current.FetchMainResult(imdbId, false, language, cancellationToken).ConfigureAwait(false); if (movieInfo != null) { - tmdbId = movieInfo.id.ToString(_usCulture); + tmdbId = movieInfo.Id.ToString(_usCulture); - dataFilePath = MovieDbProvider.Current.GetDataFilePath(tmdbId, language); + dataFilePath = TmdbMovieProvider.Current.GetDataFilePath(tmdbId, language); Directory.CreateDirectory(Path.GetDirectoryName(dataFilePath)); _jsonSerializer.SerializeToFile(movieInfo, dataFilePath); } @@ -99,12 +101,12 @@ namespace MediaBrowser.Providers.Movies if (!string.IsNullOrWhiteSpace(tmdbId)) { - await MovieDbProvider.Current.EnsureMovieInfo(tmdbId, language, cancellationToken).ConfigureAwait(false); + await TmdbMovieProvider.Current.EnsureMovieInfo(tmdbId, language, cancellationToken).ConfigureAwait(false); - dataFilePath = dataFilePath ?? MovieDbProvider.Current.GetDataFilePath(tmdbId, language); - movieInfo = movieInfo ?? _jsonSerializer.DeserializeFromFile<MovieDbProvider.CompleteMovieData>(dataFilePath); + dataFilePath = dataFilePath ?? TmdbMovieProvider.Current.GetDataFilePath(tmdbId, language); + movieInfo = movieInfo ?? _jsonSerializer.DeserializeFromFile<MovieResult>(dataFilePath); - var settings = await MovieDbProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false); + var settings = await TmdbMovieProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false); ProcessMainInfo(item, settings, preferredCountryCode, movieInfo); item.HasMetadata = true; @@ -120,7 +122,7 @@ namespace MediaBrowser.Providers.Movies /// <param name="settings">The settings.</param> /// <param name="preferredCountryCode">The preferred country code.</param> /// <param name="movieData">The movie data.</param> - private void ProcessMainInfo(MetadataResult<T> resultItem, TmdbSettingsResult settings, string preferredCountryCode, MovieDbProvider.CompleteMovieData movieData) + private void ProcessMainInfo(MetadataResult<T> resultItem, TmdbSettingsResult settings, string preferredCountryCode, MovieResult movieData) { var movie = resultItem.Item; @@ -128,41 +130,39 @@ namespace MediaBrowser.Providers.Movies movie.OriginalTitle = movieData.GetOriginalTitle(); - movie.Overview = string.IsNullOrWhiteSpace(movieData.overview) ? null : WebUtility.HtmlDecode(movieData.overview); + movie.Overview = string.IsNullOrWhiteSpace(movieData.Overview) ? null : WebUtility.HtmlDecode(movieData.Overview); movie.Overview = movie.Overview != null ? movie.Overview.Replace("\n\n", "\n") : null; //movie.HomePageUrl = movieData.homepage; - if (!string.IsNullOrEmpty(movieData.tagline)) + if (!string.IsNullOrEmpty(movieData.Tagline)) { - movie.Tagline = movieData.tagline; + movie.Tagline = movieData.Tagline; } - if (movieData.production_countries != null) + if (movieData.Production_Countries != null) { movie.ProductionLocations = movieData - .production_countries - .Select(i => i.name) + .Production_Countries + .Select(i => i.Name) .ToArray(); } - movie.SetProviderId(MetadataProviders.Tmdb, movieData.id.ToString(_usCulture)); - movie.SetProviderId(MetadataProviders.Imdb, movieData.imdb_id); + movie.SetProviderId(MetadataProviders.Tmdb, movieData.Id.ToString(_usCulture)); + movie.SetProviderId(MetadataProviders.Imdb, movieData.Imdb_Id); - if (movieData.belongs_to_collection != null) + if (movieData.Belongs_To_Collection != null) { movie.SetProviderId(MetadataProviders.TmdbCollection, - movieData.belongs_to_collection.id.ToString(CultureInfo.InvariantCulture)); + movieData.Belongs_To_Collection.Id.ToString(CultureInfo.InvariantCulture)); - var movieItem = movie as Movie; - - if (movieItem != null) + if (movie is Movie movieItem) { - movieItem.CollectionName = movieData.belongs_to_collection.name; + movieItem.CollectionName = movieData.Belongs_To_Collection.Name; } } - string voteAvg = movieData.vote_average.ToString(CultureInfo.InvariantCulture); + string voteAvg = movieData.Vote_Average.ToString(CultureInfo.InvariantCulture); if (float.TryParse(voteAvg, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out var rating)) { @@ -171,17 +171,17 @@ namespace MediaBrowser.Providers.Movies //movie.VoteCount = movieData.vote_count; - if (movieData.releases != null && movieData.releases.countries != null) + if (movieData.Releases != null && movieData.Releases.Countries != null) { - var releases = movieData.releases.countries.Where(i => !string.IsNullOrWhiteSpace(i.certification)).ToList(); + var releases = movieData.Releases.Countries.Where(i => !string.IsNullOrWhiteSpace(i.Certification)).ToList(); - var ourRelease = releases.FirstOrDefault(c => string.Equals(c.iso_3166_1, preferredCountryCode, StringComparison.OrdinalIgnoreCase)); - var usRelease = releases.FirstOrDefault(c => string.Equals(c.iso_3166_1, "US", StringComparison.OrdinalIgnoreCase)); + var ourRelease = releases.FirstOrDefault(c => string.Equals(c.Iso_3166_1, preferredCountryCode, StringComparison.OrdinalIgnoreCase)); + var usRelease = releases.FirstOrDefault(c => string.Equals(c.Iso_3166_1, "US", StringComparison.OrdinalIgnoreCase)); if (ourRelease != null) { var ratingPrefix = string.Equals(preferredCountryCode, "us", StringComparison.OrdinalIgnoreCase) ? "" : preferredCountryCode + "-"; - var newRating = ratingPrefix + ourRelease.certification; + var newRating = ratingPrefix + ourRelease.Certification; newRating = newRating.Replace("de-", "FSK-", StringComparison.OrdinalIgnoreCase); @@ -189,14 +189,14 @@ namespace MediaBrowser.Providers.Movies } else if (usRelease != null) { - movie.OfficialRating = usRelease.certification; + movie.OfficialRating = usRelease.Certification; } } - if (!string.IsNullOrWhiteSpace(movieData.release_date)) + if (!string.IsNullOrWhiteSpace(movieData.Release_Date)) { // These dates are always in this exact format - if (DateTime.TryParse(movieData.release_date, _usCulture, DateTimeStyles.None, out var r)) + if (DateTime.TryParse(movieData.Release_Date, _usCulture, DateTimeStyles.None, out var r)) { movie.PremiereDate = r.ToUniversalTime(); movie.ProductionYear = movie.PremiereDate.Value.Year; @@ -204,16 +204,16 @@ namespace MediaBrowser.Providers.Movies } //studios - if (movieData.production_companies != null) + if (movieData.Production_Companies != null) { - movie.SetStudios(movieData.production_companies.Select(c => c.name)); + movie.SetStudios(movieData.Production_Companies.Select(c => c.Name)); } // genres // Movies get this from imdb - var genres = movieData.genres ?? new List<MovieDbProvider.GenreItem>(); + var genres = movieData.Genres ?? new List<Tmdb.Models.General.Genre>(); - foreach (var genre in genres.Select(g => g.name)) + foreach (var genre in genres.Select(g => g.Name)) { movie.AddGenre(genre); } @@ -223,26 +223,26 @@ namespace MediaBrowser.Providers.Movies //Actors, Directors, Writers - all in People //actors come from cast - if (movieData.casts != null && movieData.casts.cast != null) + if (movieData.Casts != null && movieData.Casts.Cast != null) { - foreach (var actor in movieData.casts.cast.OrderBy(a => a.order)) + foreach (var actor in movieData.Casts.Cast.OrderBy(a => a.Order)) { var personInfo = new PersonInfo { - Name = actor.name.Trim(), - Role = actor.character, + Name = actor.Name.Trim(), + Role = actor.Character, Type = PersonType.Actor, - SortOrder = actor.order + SortOrder = actor.Order }; - if (!string.IsNullOrWhiteSpace(actor.profile_path)) + if (!string.IsNullOrWhiteSpace(actor.Profile_Path)) { - personInfo.ImageUrl = tmdbImageUrl + actor.profile_path; + personInfo.ImageUrl = tmdbImageUrl + actor.Profile_Path; } - if (actor.id > 0) + if (actor.Id > 0) { - personInfo.SetProviderId(MetadataProviders.Tmdb, actor.id.ToString(CultureInfo.InvariantCulture)); + personInfo.SetProviderId(MetadataProviders.Tmdb, actor.Id.ToString(CultureInfo.InvariantCulture)); } resultItem.AddPerson(personInfo); @@ -250,45 +250,41 @@ namespace MediaBrowser.Providers.Movies } //and the rest from crew - if (movieData.casts != null && movieData.casts.crew != null) + if (movieData.Casts?.Crew != null) { var keepTypes = new[] { PersonType.Director, - //PersonType.Writer, - //PersonType.Producer + PersonType.Writer, + PersonType.Producer }; - foreach (var person in movieData.casts.crew) + foreach (var person in movieData.Casts.Crew) { // Normalize this - var type = person.department; - if (string.Equals(type, "writing", StringComparison.OrdinalIgnoreCase)) - { - type = PersonType.Writer; - } + var type = TmdbUtils.MapCrewToPersonType(person); - if (!keepTypes.Contains(type ?? string.Empty, StringComparer.OrdinalIgnoreCase) && - !keepTypes.Contains(person.job ?? string.Empty, StringComparer.OrdinalIgnoreCase)) + if (!keepTypes.Contains(type, StringComparer.OrdinalIgnoreCase) && + !keepTypes.Contains(person.Job ?? string.Empty, StringComparer.OrdinalIgnoreCase)) { continue; } var personInfo = new PersonInfo { - Name = person.name.Trim(), - Role = person.job, + Name = person.Name.Trim(), + Role = person.Job, Type = type }; - if (!string.IsNullOrWhiteSpace(person.profile_path)) + if (!string.IsNullOrWhiteSpace(person.Profile_Path)) { - personInfo.ImageUrl = tmdbImageUrl + person.profile_path; + personInfo.ImageUrl = tmdbImageUrl + person.Profile_Path; } - if (person.id > 0) + if (person.Id > 0) { - personInfo.SetProviderId(MetadataProviders.Tmdb, person.id.ToString(CultureInfo.InvariantCulture)); + personInfo.SetProviderId(MetadataProviders.Tmdb, person.Id.ToString(CultureInfo.InvariantCulture)); } resultItem.AddPerson(personInfo); @@ -300,12 +296,12 @@ namespace MediaBrowser.Providers.Movies // movie.Keywords = movieData.keywords.keywords.Select(i => i.name).ToList(); //} - if (movieData.trailers != null && movieData.trailers.youtube != null) + if (movieData.Trailers != null && movieData.Trailers.Youtube != null) { - movie.RemoteTrailers = movieData.trailers.youtube.Select(i => new MediaUrl + movie.RemoteTrailers = movieData.Trailers.Youtube.Select(i => new MediaUrl { - Url = string.Format("https://www.youtube.com/watch?v={0}", i.source), - Name = i.name + Url = string.Format("https://www.youtube.com/watch?v={0}", i.Source), + Name = i.Name }).ToArray(); } diff --git a/MediaBrowser.Providers/Movies/MovieDbImageProvider.cs b/MediaBrowser.Providers/Tmdb/Movies/TmdbImageProvider.cs index 20b53d58a..cdb96e6ac 100644 --- a/MediaBrowser.Providers/Movies/MovieDbImageProvider.cs +++ b/MediaBrowser.Providers/Tmdb/Movies/TmdbImageProvider.cs @@ -13,16 +13,19 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; using MediaBrowser.Model.Providers; using MediaBrowser.Model.Serialization; +using MediaBrowser.Providers.Movies; +using MediaBrowser.Providers.Tmdb.Models.General; +using MediaBrowser.Providers.Tmdb.Models.Movies; -namespace MediaBrowser.Providers.Movies +namespace MediaBrowser.Providers.Tmdb.Movies { - public class MovieDbImageProvider : IRemoteImageProvider, IHasOrder + public class TmdbImageProvider : IRemoteImageProvider, IHasOrder { private readonly IJsonSerializer _jsonSerializer; private readonly IHttpClient _httpClient; private readonly IFileSystem _fileSystem; - public MovieDbImageProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, IFileSystem fileSystem) + public TmdbImageProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, IFileSystem fileSystem) { _jsonSerializer = jsonSerializer; _httpClient = httpClient; @@ -31,7 +34,7 @@ namespace MediaBrowser.Providers.Movies public string Name => ProviderName; - public static string ProviderName => "TheMovieDb"; + public static string ProviderName => TmdbUtils.ProviderName; public bool Supports(BaseItem item) { @@ -60,7 +63,7 @@ namespace MediaBrowser.Providers.Movies return list; } - var tmdbSettings = await MovieDbProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false); + var tmdbSettings = await TmdbMovieProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false); var tmdbImageUrl = tmdbSettings.images.GetImageUrl("original"); @@ -70,12 +73,12 @@ namespace MediaBrowser.Providers.Movies { list.AddRange(GetPosters(results).Select(i => new RemoteImageInfo { - Url = tmdbImageUrl + i.file_path, - CommunityRating = i.vote_average, - VoteCount = i.vote_count, - Width = i.width, - Height = i.height, - Language = MovieDbProvider.AdjustImageLanguage(i.iso_639_1, language), + Url = tmdbImageUrl + i.File_Path, + CommunityRating = i.Vote_Average, + VoteCount = i.Vote_Count, + Width = i.Width, + Height = i.Height, + Language = TmdbMovieProvider.AdjustImageLanguage(i.Iso_639_1, language), ProviderName = Name, Type = ImageType.Primary, RatingType = RatingType.Score @@ -86,11 +89,11 @@ namespace MediaBrowser.Providers.Movies { list.AddRange(GetBackdrops(results).Select(i => new RemoteImageInfo { - Url = tmdbImageUrl + i.file_path, - CommunityRating = i.vote_average, - VoteCount = i.vote_count, - Width = i.width, - Height = i.height, + Url = tmdbImageUrl + i.File_Path, + CommunityRating = i.Vote_Average, + VoteCount = i.Vote_Count, + Width = i.Width, + Height = i.Height, ProviderName = Name, Type = ImageType.Backdrop, RatingType = RatingType.Score @@ -127,9 +130,9 @@ namespace MediaBrowser.Providers.Movies /// </summary> /// <param name="images">The images.</param> /// <returns>IEnumerable{MovieDbProvider.Poster}.</returns> - private IEnumerable<MovieDbProvider.Poster> GetPosters(MovieDbProvider.Images images) + private IEnumerable<Poster> GetPosters(Images images) { - return images.posters ?? new List<MovieDbProvider.Poster>(); + return images.Posters ?? new List<Poster>(); } /// <summary> @@ -137,13 +140,13 @@ namespace MediaBrowser.Providers.Movies /// </summary> /// <param name="images">The images.</param> /// <returns>IEnumerable{MovieDbProvider.Backdrop}.</returns> - private IEnumerable<MovieDbProvider.Backdrop> GetBackdrops(MovieDbProvider.Images images) + private IEnumerable<Backdrop> GetBackdrops(Images images) { - var eligibleBackdrops = images.backdrops == null ? new List<MovieDbProvider.Backdrop>() : - images.backdrops; + var eligibleBackdrops = images.Backdrops == null ? new List<Backdrop>() : + images.Backdrops; - return eligibleBackdrops.OrderByDescending(i => i.vote_average) - .ThenByDescending(i => i.vote_count); + return eligibleBackdrops.OrderByDescending(i => i.Vote_Average) + .ThenByDescending(i => i.Vote_Count); } /// <summary> @@ -154,7 +157,7 @@ namespace MediaBrowser.Providers.Movies /// <param name="jsonSerializer">The json serializer.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task{MovieImages}.</returns> - private async Task<MovieDbProvider.Images> FetchImages(BaseItem item, string language, IJsonSerializer jsonSerializer, CancellationToken cancellationToken) + private async Task<Images> FetchImages(BaseItem item, string language, IJsonSerializer jsonSerializer, CancellationToken cancellationToken) { var tmdbId = item.GetProviderId(MetadataProviders.Tmdb); @@ -163,10 +166,10 @@ namespace MediaBrowser.Providers.Movies var imdbId = item.GetProviderId(MetadataProviders.Imdb); if (!string.IsNullOrWhiteSpace(imdbId)) { - var movieInfo = await MovieDbProvider.Current.FetchMainResult(imdbId, false, language, cancellationToken).ConfigureAwait(false); + var movieInfo = await TmdbMovieProvider.Current.FetchMainResult(imdbId, false, language, cancellationToken).ConfigureAwait(false); if (movieInfo != null) { - tmdbId = movieInfo.id.ToString(CultureInfo.InvariantCulture); + tmdbId = movieInfo.Id.ToString(CultureInfo.InvariantCulture); } } } @@ -176,9 +179,9 @@ namespace MediaBrowser.Providers.Movies return null; } - await MovieDbProvider.Current.EnsureMovieInfo(tmdbId, language, cancellationToken).ConfigureAwait(false); + await TmdbMovieProvider.Current.EnsureMovieInfo(tmdbId, language, cancellationToken).ConfigureAwait(false); - var path = MovieDbProvider.Current.GetDataFilePath(tmdbId, language); + var path = TmdbMovieProvider.Current.GetDataFilePath(tmdbId, language); if (!string.IsNullOrEmpty(path)) { @@ -186,7 +189,7 @@ namespace MediaBrowser.Providers.Movies if (fileInfo.Exists) { - return jsonSerializer.DeserializeFromFile<MovieDbProvider.CompleteMovieData>(path).images; + return jsonSerializer.DeserializeFromFile<MovieResult>(path).Images; } } diff --git a/MediaBrowser.Providers/Tmdb/Movies/TmdbMovieExternalId.cs b/MediaBrowser.Providers/Tmdb/Movies/TmdbMovieExternalId.cs new file mode 100644 index 000000000..fc7a4583f --- /dev/null +++ b/MediaBrowser.Providers/Tmdb/Movies/TmdbMovieExternalId.cs @@ -0,0 +1,32 @@ +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.Movies; +using MediaBrowser.Controller.LiveTv; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Entities; + +namespace MediaBrowser.Providers.Tmdb.Movies +{ + public class TmdbMovieExternalId : IExternalId + { + /// <inheritdoc /> + public string Name => TmdbUtils.ProviderName; + + /// <inheritdoc /> + public string Key => MetadataProviders.Tmdb.ToString(); + + /// <inheritdoc /> + public string UrlFormatString => TmdbUtils.BaseTmdbUrl + "movie/{0}"; + + /// <inheritdoc /> + public bool Supports(IHasProviderIds item) + { + // Supports images for tv movies + if (item is LiveTvProgram tvProgram && tvProgram.IsMovie) + { + return true; + } + + return item is Movie || item is MusicVideo || item is Trailer; + } + } +} diff --git a/MediaBrowser.Providers/Movies/MovieDbProvider.cs b/MediaBrowser.Providers/Tmdb/Movies/TmdbMovieProvider.cs index 3ff63a4bf..a1bea5847 100644 --- a/MediaBrowser.Providers/Movies/MovieDbProvider.cs +++ b/MediaBrowser.Providers/Tmdb/Movies/TmdbMovieProvider.cs @@ -19,16 +19,18 @@ using MediaBrowser.Model.IO; using MediaBrowser.Model.Net; using MediaBrowser.Model.Providers; using MediaBrowser.Model.Serialization; +using MediaBrowser.Providers.Movies; +using MediaBrowser.Providers.Tmdb.Models.Movies; using Microsoft.Extensions.Logging; -namespace MediaBrowser.Providers.Movies +namespace MediaBrowser.Providers.Tmdb.Movies { /// <summary> /// Class MovieDbProvider /// </summary> - public class MovieDbProvider : IRemoteMetadataProvider<Movie, MovieInfo>, IHasOrder + public class TmdbMovieProvider : IRemoteMetadataProvider<Movie, MovieInfo>, IHasOrder { - internal static MovieDbProvider Current { get; private set; } + internal static TmdbMovieProvider Current { get; private set; } private readonly IJsonSerializer _jsonSerializer; private readonly IHttpClient _httpClient; @@ -41,7 +43,7 @@ namespace MediaBrowser.Providers.Movies private readonly CultureInfo _usCulture = new CultureInfo("en-US"); - public MovieDbProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILogger logger, ILocalizationManager localization, ILibraryManager libraryManager, IApplicationHost appHost) + public TmdbMovieProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILogger logger, ILocalizationManager localization, ILibraryManager libraryManager, IApplicationHost appHost) { _jsonSerializer = jsonSerializer; _httpClient = httpClient; @@ -71,7 +73,7 @@ namespace MediaBrowser.Providers.Movies var dataFilePath = GetDataFilePath(tmdbId, searchInfo.MetadataLanguage); - var obj = _jsonSerializer.DeserializeFromFile<CompleteMovieData>(dataFilePath); + var obj = _jsonSerializer.DeserializeFromFile<MovieResult>(dataFilePath); var tmdbSettings = await GetTmdbSettings(cancellationToken).ConfigureAwait(false); @@ -81,30 +83,30 @@ namespace MediaBrowser.Providers.Movies { Name = obj.GetTitle(), SearchProviderName = Name, - ImageUrl = string.IsNullOrWhiteSpace(obj.poster_path) ? null : tmdbImageUrl + obj.poster_path + ImageUrl = string.IsNullOrWhiteSpace(obj.Poster_Path) ? null : tmdbImageUrl + obj.Poster_Path }; - if (!string.IsNullOrWhiteSpace(obj.release_date)) + if (!string.IsNullOrWhiteSpace(obj.Release_Date)) { // These dates are always in this exact format - if (DateTime.TryParse(obj.release_date, _usCulture, DateTimeStyles.None, out var r)) + if (DateTime.TryParse(obj.Release_Date, _usCulture, DateTimeStyles.None, out var r)) { remoteResult.PremiereDate = r.ToUniversalTime(); remoteResult.ProductionYear = remoteResult.PremiereDate.Value.Year; } } - remoteResult.SetProviderId(MetadataProviders.Tmdb, obj.id.ToString(_usCulture)); + remoteResult.SetProviderId(MetadataProviders.Tmdb, obj.Id.ToString(_usCulture)); - if (!string.IsNullOrWhiteSpace(obj.imdb_id)) + if (!string.IsNullOrWhiteSpace(obj.Imdb_Id)) { - remoteResult.SetProviderId(MetadataProviders.Imdb, obj.imdb_id); + remoteResult.SetProviderId(MetadataProviders.Imdb, obj.Imdb_Id); } return new[] { remoteResult }; } - return await new MovieDbSearch(_logger, _jsonSerializer, _libraryManager).GetMovieSearchResults(searchInfo, cancellationToken).ConfigureAwait(false); + return await new TmdbSearch(_logger, _jsonSerializer, _libraryManager).GetMovieSearchResults(searchInfo, cancellationToken).ConfigureAwait(false); } public Task<MetadataResult<Movie>> GetMetadata(MovieInfo info, CancellationToken cancellationToken) @@ -115,12 +117,12 @@ namespace MediaBrowser.Providers.Movies public Task<MetadataResult<T>> GetItemMetadata<T>(ItemLookupInfo id, CancellationToken cancellationToken) where T : BaseItem, new() { - var movieDb = new GenericMovieDbInfo<T>(_logger, _jsonSerializer, _libraryManager, _fileSystem); + var movieDb = new GenericTmdbMovieInfo<T>(_logger, _jsonSerializer, _libraryManager, _fileSystem); return movieDb.GetMetadata(id, cancellationToken); } - public string Name => "TheMovieDb"; + public string Name => TmdbUtils.ProviderName; /// <summary> /// The _TMDB settings task @@ -140,9 +142,9 @@ namespace MediaBrowser.Providers.Movies using (HttpResponseInfo response = await GetMovieDbResponse(new HttpRequestOptions { - Url = string.Format(TmdbConfigUrl, ApiKey), + Url = string.Format(TmdbConfigUrl, TmdbUtils.ApiKey), CancellationToken = cancellationToken, - AcceptHeader = AcceptHeader + AcceptHeader = TmdbUtils.AcceptHeader }).ConfigureAwait(false)) { @@ -155,13 +157,8 @@ namespace MediaBrowser.Providers.Movies } } - public const string BaseMovieDbUrl = "https://api.themoviedb.org/"; - - private const string TmdbConfigUrl = BaseMovieDbUrl + "3/configuration?api_key={0}"; - private const string GetMovieInfo3 = BaseMovieDbUrl + @"3/movie/{0}?api_key={1}&append_to_response=casts,releases,images,keywords,trailers"; - - internal static string ApiKey = "4219e299c89411838049ab0dab19ebd5"; - internal static string AcceptHeader = "application/json,image/*"; + private const string TmdbConfigUrl = TmdbUtils.BaseTmdbApiUrl + "3/configuration?api_key={0}"; + private const string GetMovieInfo3 = TmdbUtils.BaseTmdbApiUrl + @"3/movie/{0}?api_key={1}&append_to_response=casts,releases,images,keywords,trailers"; /// <summary> /// Gets the movie data path. @@ -314,9 +311,9 @@ namespace MediaBrowser.Providers.Movies /// <param name="language">The language.</param> /// <param name="cancellationToken">The cancellation token</param> /// <returns>Task{CompleteMovieData}.</returns> - internal async Task<CompleteMovieData> FetchMainResult(string id, bool isTmdbId, string language, CancellationToken cancellationToken) + internal async Task<MovieResult> FetchMainResult(string id, bool isTmdbId, string language, CancellationToken cancellationToken) { - var url = string.Format(GetMovieInfo3, id, ApiKey); + var url = string.Format(GetMovieInfo3, id, TmdbUtils.ApiKey); if (!string.IsNullOrEmpty(language)) { @@ -326,7 +323,7 @@ namespace MediaBrowser.Providers.Movies url += "&include_image_language=" + GetImageLanguagesParam(language); } - CompleteMovieData mainResult; + MovieResult mainResult; cancellationToken.ThrowIfCancellationRequested(); @@ -340,7 +337,7 @@ namespace MediaBrowser.Providers.Movies { Url = url, CancellationToken = cancellationToken, - AcceptHeader = AcceptHeader, + AcceptHeader = TmdbUtils.AcceptHeader, CacheMode = cacheMode, CacheLength = cacheLength @@ -348,7 +345,7 @@ namespace MediaBrowser.Providers.Movies { using (var json = response.Content) { - mainResult = await _jsonSerializer.DeserializeFromStreamAsync<CompleteMovieData>(json).ConfigureAwait(false); + mainResult = await _jsonSerializer.DeserializeFromStreamAsync<MovieResult>(json).ConfigureAwait(false); } } } @@ -367,13 +364,13 @@ namespace MediaBrowser.Providers.Movies // If the language preference isn't english, then have the overview fallback to english if it's blank if (mainResult != null && - string.IsNullOrEmpty(mainResult.overview) && + string.IsNullOrEmpty(mainResult.Overview) && !string.IsNullOrEmpty(language) && !string.Equals(language, "en", StringComparison.OrdinalIgnoreCase)) { _logger.LogInformation("MovieDbProvider couldn't find meta for language " + language + ". Trying English..."); - url = string.Format(GetMovieInfo3, id, ApiKey) + "&language=en"; + url = string.Format(GetMovieInfo3, id, TmdbUtils.ApiKey) + "&language=en"; if (!string.IsNullOrEmpty(language)) { @@ -385,7 +382,7 @@ namespace MediaBrowser.Providers.Movies { Url = url, CancellationToken = cancellationToken, - AcceptHeader = AcceptHeader, + AcceptHeader = TmdbUtils.AcceptHeader, CacheMode = cacheMode, CacheLength = cacheLength @@ -393,9 +390,9 @@ namespace MediaBrowser.Providers.Movies { using (var json = response.Content) { - var englishResult = await _jsonSerializer.DeserializeFromStreamAsync<CompleteMovieData>(json).ConfigureAwait(false); + var englishResult = await _jsonSerializer.DeserializeFromStreamAsync<MovieResult>(json).ConfigureAwait(false); - mainResult.overview = englishResult.overview; + mainResult.Overview = englishResult.Overview; } } } @@ -429,205 +426,6 @@ namespace MediaBrowser.Providers.Movies return await _httpClient.SendAsync(options, "GET").ConfigureAwait(false); } - /// <summary> - /// Class TmdbTitle - /// </summary> - internal class TmdbTitle - { - /// <summary> - /// Gets or sets the iso_3166_1. - /// </summary> - /// <value>The iso_3166_1.</value> - public string iso_3166_1 { get; set; } - /// <summary> - /// Gets or sets the title. - /// </summary> - /// <value>The title.</value> - public string title { get; set; } - } - - /// <summary> - /// Class TmdbAltTitleResults - /// </summary> - internal class TmdbAltTitleResults - { - /// <summary> - /// Gets or sets the id. - /// </summary> - /// <value>The id.</value> - public int id { get; set; } - /// <summary> - /// Gets or sets the titles. - /// </summary> - /// <value>The titles.</value> - public List<TmdbTitle> titles { get; set; } - } - - internal class BelongsToCollection - { - public int id { get; set; } - public string name { get; set; } - public string poster_path { get; set; } - public string backdrop_path { get; set; } - } - - internal class GenreItem - { - public int id { get; set; } - public string name { get; set; } - } - - internal class ProductionCompany - { - public string name { get; set; } - public int id { get; set; } - } - - internal class ProductionCountry - { - public string iso_3166_1 { get; set; } - public string name { get; set; } - } - - internal class SpokenLanguage - { - public string iso_639_1 { get; set; } - public string name { get; set; } - } - - internal class Cast - { - public int id { get; set; } - public string name { get; set; } - public string character { get; set; } - public int order { get; set; } - public int cast_id { get; set; } - public string profile_path { get; set; } - } - - internal class Crew - { - public int id { get; set; } - public string name { get; set; } - public string department { get; set; } - public string job { get; set; } - public string profile_path { get; set; } - } - - internal class Casts - { - public List<Cast> cast { get; set; } - public List<Crew> crew { get; set; } - } - - internal class Country - { - public string iso_3166_1 { get; set; } - public string certification { get; set; } - public DateTime release_date { get; set; } - } - - internal class Releases - { - public List<Country> countries { get; set; } - } - - internal class Backdrop - { - public string file_path { get; set; } - public int width { get; set; } - public int height { get; set; } - public object iso_639_1 { get; set; } - public double aspect_ratio { get; set; } - public double vote_average { get; set; } - public int vote_count { get; set; } - } - - internal class Poster - { - public string file_path { get; set; } - public int width { get; set; } - public int height { get; set; } - public string iso_639_1 { get; set; } - public double aspect_ratio { get; set; } - public double vote_average { get; set; } - public int vote_count { get; set; } - } - - internal class Images - { - public List<Backdrop> backdrops { get; set; } - public List<Poster> posters { get; set; } - } - - internal class Keyword - { - public int id { get; set; } - public string name { get; set; } - } - - internal class Keywords - { - public List<Keyword> keywords { get; set; } - } - - internal class Youtube - { - public string name { get; set; } - public string size { get; set; } - public string source { get; set; } - } - - internal class Trailers - { - public List<object> quicktime { get; set; } - public List<Youtube> youtube { get; set; } - } - - internal class CompleteMovieData - { - public bool adult { get; set; } - public string backdrop_path { get; set; } - public BelongsToCollection belongs_to_collection { get; set; } - public int budget { get; set; } - public List<GenreItem> genres { get; set; } - public string homepage { get; set; } - public int id { get; set; } - public string imdb_id { get; set; } - public string original_title { get; set; } - public string original_name { get; set; } - public string overview { get; set; } - public double popularity { get; set; } - public string poster_path { get; set; } - public List<ProductionCompany> production_companies { get; set; } - public List<ProductionCountry> production_countries { get; set; } - public string release_date { get; set; } - public int revenue { get; set; } - public int runtime { get; set; } - public List<SpokenLanguage> spoken_languages { get; set; } - public string status { get; set; } - public string tagline { get; set; } - public string title { get; set; } - public string name { get; set; } - public double vote_average { get; set; } - public int vote_count { get; set; } - public Casts casts { get; set; } - public Releases releases { get; set; } - public Images images { get; set; } - public Keywords keywords { get; set; } - public Trailers trailers { get; set; } - - public string GetOriginalTitle() - { - return original_name ?? original_title; - } - - public string GetTitle() - { - return name ?? title ?? GetOriginalTitle(); - } - } - public int Order => 1; public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken) diff --git a/MediaBrowser.Providers/Movies/MovieDbSearch.cs b/MediaBrowser.Providers/Tmdb/Movies/TmdbSearch.cs index e466d40a0..223cef086 100644 --- a/MediaBrowser.Providers/Movies/MovieDbSearch.cs +++ b/MediaBrowser.Providers/Tmdb/Movies/TmdbSearch.cs @@ -11,23 +11,21 @@ using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Providers; using MediaBrowser.Model.Serialization; +using MediaBrowser.Providers.Tmdb.Models.Search; using Microsoft.Extensions.Logging; -namespace MediaBrowser.Providers.Movies +namespace MediaBrowser.Providers.Tmdb.Movies { - public class MovieDbSearch + public class TmdbSearch { private static readonly CultureInfo EnUs = new CultureInfo("en-US"); - private const string Search3 = MovieDbProvider.BaseMovieDbUrl + @"3/search/{3}?api_key={1}&query={0}&language={2}"; - - internal static string ApiKey = "4219e299c89411838049ab0dab19ebd5"; - internal static string AcceptHeader = "application/json,image/*"; + private const string Search3 = TmdbUtils.BaseTmdbApiUrl + @"3/search/{3}?api_key={1}&query={0}&language={2}"; private readonly ILogger _logger; private readonly IJsonSerializer _json; private readonly ILibraryManager _libraryManager; - public MovieDbSearch(ILogger logger, IJsonSerializer json, ILibraryManager libraryManager) + public TmdbSearch(ILogger logger, IJsonSerializer json, ILibraryManager libraryManager) { _logger = logger; _json = json; @@ -59,7 +57,7 @@ namespace MediaBrowser.Providers.Movies return new List<RemoteSearchResult>(); } - var tmdbSettings = await MovieDbProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false); + var tmdbSettings = await TmdbMovieProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false); var tmdbImageUrl = tmdbSettings.images.GetImageUrl("original"); @@ -152,43 +150,43 @@ namespace MediaBrowser.Providers.Movies throw new ArgumentException("name"); } - var url3 = string.Format(Search3, WebUtility.UrlEncode(name), ApiKey, language, type); + var url3 = string.Format(Search3, WebUtility.UrlEncode(name), TmdbUtils.ApiKey, language, type); - using (var response = await MovieDbProvider.Current.GetMovieDbResponse(new HttpRequestOptions + using (var response = await TmdbMovieProvider.Current.GetMovieDbResponse(new HttpRequestOptions { Url = url3, CancellationToken = cancellationToken, - AcceptHeader = AcceptHeader + AcceptHeader = TmdbUtils.AcceptHeader }).ConfigureAwait(false)) { using (var json = response.Content) { - var searchResults = await _json.DeserializeFromStreamAsync<TmdbMovieSearchResults>(json).ConfigureAwait(false); + var searchResults = await _json.DeserializeFromStreamAsync<TmdbSearchResult<MovieResult>>(json).ConfigureAwait(false); - var results = searchResults.results ?? new List<TmdbMovieSearchResult>(); + var results = searchResults.Results ?? new List<MovieResult>(); return results .Select(i => { var remoteResult = new RemoteSearchResult { - SearchProviderName = MovieDbProvider.Current.Name, - Name = i.title ?? i.name ?? i.original_title, - ImageUrl = string.IsNullOrWhiteSpace(i.poster_path) ? null : baseImageUrl + i.poster_path + SearchProviderName = TmdbMovieProvider.Current.Name, + Name = i.Title ?? i.Name ?? i.Original_Title, + ImageUrl = string.IsNullOrWhiteSpace(i.Poster_Path) ? null : baseImageUrl + i.Poster_Path }; - if (!string.IsNullOrWhiteSpace(i.release_date)) + if (!string.IsNullOrWhiteSpace(i.Release_Date)) { // These dates are always in this exact format - if (DateTime.TryParseExact(i.release_date, "yyyy-MM-dd", EnUs, DateTimeStyles.None, out var r)) + if (DateTime.TryParseExact(i.Release_Date, "yyyy-MM-dd", EnUs, DateTimeStyles.None, out var r)) { remoteResult.PremiereDate = r.ToUniversalTime(); remoteResult.ProductionYear = remoteResult.PremiereDate.Value.Year; } } - remoteResult.SetProviderId(MetadataProviders.Tmdb, i.id.ToString(EnUs)); + remoteResult.SetProviderId(MetadataProviders.Tmdb, i.Id.ToString(EnUs)); return remoteResult; @@ -205,43 +203,43 @@ namespace MediaBrowser.Providers.Movies throw new ArgumentException("name"); } - var url3 = string.Format(Search3, WebUtility.UrlEncode(name), ApiKey, language, "tv"); + var url3 = string.Format(Search3, WebUtility.UrlEncode(name), TmdbUtils.ApiKey, language, "tv"); - using (var response = await MovieDbProvider.Current.GetMovieDbResponse(new HttpRequestOptions + using (var response = await TmdbMovieProvider.Current.GetMovieDbResponse(new HttpRequestOptions { Url = url3, CancellationToken = cancellationToken, - AcceptHeader = AcceptHeader + AcceptHeader = TmdbUtils.AcceptHeader }).ConfigureAwait(false)) { using (var json = response.Content) { - var searchResults = await _json.DeserializeFromStreamAsync<TmdbTvSearchResults>(json).ConfigureAwait(false); + var searchResults = await _json.DeserializeFromStreamAsync<TmdbSearchResult<TvResult>>(json).ConfigureAwait(false); - var results = searchResults.results ?? new List<TvResult>(); + var results = searchResults.Results ?? new List<TvResult>(); return results .Select(i => { var remoteResult = new RemoteSearchResult { - SearchProviderName = MovieDbProvider.Current.Name, - Name = i.name ?? i.original_name, - ImageUrl = string.IsNullOrWhiteSpace(i.poster_path) ? null : baseImageUrl + i.poster_path + SearchProviderName = TmdbMovieProvider.Current.Name, + Name = i.Name ?? i.Original_Name, + ImageUrl = string.IsNullOrWhiteSpace(i.Poster_Path) ? null : baseImageUrl + i.Poster_Path }; - if (!string.IsNullOrWhiteSpace(i.first_air_date)) + if (!string.IsNullOrWhiteSpace(i.First_Air_Date)) { // These dates are always in this exact format - if (DateTime.TryParseExact(i.first_air_date, "yyyy-MM-dd", EnUs, DateTimeStyles.None, out var r)) + if (DateTime.TryParseExact(i.First_Air_Date, "yyyy-MM-dd", EnUs, DateTimeStyles.None, out var r)) { remoteResult.PremiereDate = r.ToUniversalTime(); remoteResult.ProductionYear = remoteResult.PremiereDate.Value.Year; } } - remoteResult.SetProviderId(MetadataProviders.Tmdb, i.id.ToString(EnUs)); + remoteResult.SetProviderId(MetadataProviders.Tmdb, i.Id.ToString(EnUs)); return remoteResult; @@ -250,145 +248,5 @@ namespace MediaBrowser.Providers.Movies } } } - - /// <summary> - /// Class TmdbMovieSearchResult - /// </summary> - public class TmdbMovieSearchResult - { - /// <summary> - /// Gets or sets a value indicating whether this <see cref="TmdbMovieSearchResult" /> is adult. - /// </summary> - /// <value><c>true</c> if adult; otherwise, <c>false</c>.</value> - public bool adult { get; set; } - /// <summary> - /// Gets or sets the backdrop_path. - /// </summary> - /// <value>The backdrop_path.</value> - public string backdrop_path { get; set; } - /// <summary> - /// Gets or sets the id. - /// </summary> - /// <value>The id.</value> - public int id { get; set; } - /// <summary> - /// Gets or sets the original_title. - /// </summary> - /// <value>The original_title.</value> - public string original_title { get; set; } - /// <summary> - /// Gets or sets the original_name. - /// </summary> - /// <value>The original_name.</value> - public string original_name { get; set; } - /// <summary> - /// Gets or sets the release_date. - /// </summary> - /// <value>The release_date.</value> - public string release_date { get; set; } - /// <summary> - /// Gets or sets the poster_path. - /// </summary> - /// <value>The poster_path.</value> - public string poster_path { get; set; } - /// <summary> - /// Gets or sets the popularity. - /// </summary> - /// <value>The popularity.</value> - public double popularity { get; set; } - /// <summary> - /// Gets or sets the title. - /// </summary> - /// <value>The title.</value> - public string title { get; set; } - /// <summary> - /// Gets or sets the vote_average. - /// </summary> - /// <value>The vote_average.</value> - public double vote_average { get; set; } - /// <summary> - /// For collection search results - /// </summary> - public string name { get; set; } - /// <summary> - /// Gets or sets the vote_count. - /// </summary> - /// <value>The vote_count.</value> - public int vote_count { get; set; } - } - - /// <summary> - /// Class TmdbMovieSearchResults - /// </summary> - private class TmdbMovieSearchResults - { - /// <summary> - /// Gets or sets the page. - /// </summary> - /// <value>The page.</value> - public int page { get; set; } - /// <summary> - /// Gets or sets the results. - /// </summary> - /// <value>The results.</value> - public List<TmdbMovieSearchResult> results { get; set; } - /// <summary> - /// Gets or sets the total_pages. - /// </summary> - /// <value>The total_pages.</value> - public int total_pages { get; set; } - /// <summary> - /// Gets or sets the total_results. - /// </summary> - /// <value>The total_results.</value> - public int total_results { get; set; } - } - - public class TvResult - { - public string backdrop_path { get; set; } - public string first_air_date { get; set; } - public int id { get; set; } - public string original_name { get; set; } - public string poster_path { get; set; } - public double popularity { get; set; } - public string name { get; set; } - public double vote_average { get; set; } - public int vote_count { get; set; } - } - - /// <summary> - /// Class TmdbTvSearchResults - /// </summary> - private class TmdbTvSearchResults - { - /// <summary> - /// Gets or sets the page. - /// </summary> - /// <value>The page.</value> - public int page { get; set; } - /// <summary> - /// Gets or sets the results. - /// </summary> - /// <value>The results.</value> - public List<TvResult> results { get; set; } - /// <summary> - /// Gets or sets the total_pages. - /// </summary> - /// <value>The total_pages.</value> - public int total_pages { get; set; } - /// <summary> - /// Gets or sets the total_results. - /// </summary> - /// <value>The total_results.</value> - public int total_results { get; set; } - } - - public class ExternalIdLookupResult - { - public List<object> movie_results { get; set; } - public List<object> person_results { get; set; } - public List<TvResult> tv_results { get; set; } - } } } diff --git a/MediaBrowser.Providers/Movies/TmdbSettings.cs b/MediaBrowser.Providers/Tmdb/Movies/TmdbSettings.cs index 119bbfca6..dca406b99 100644 --- a/MediaBrowser.Providers/Movies/TmdbSettings.cs +++ b/MediaBrowser.Providers/Tmdb/Movies/TmdbSettings.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -namespace MediaBrowser.Providers.Movies +namespace MediaBrowser.Providers.Tmdb.Movies { internal class TmdbImageSettings { diff --git a/MediaBrowser.Providers/Music/MovieDbMusicVideoProvider.cs b/MediaBrowser.Providers/Tmdb/Music/TmdbMusicVideoProvider.cs index 506cbfb89..f3f8a92cf 100644 --- a/MediaBrowser.Providers/Music/MovieDbMusicVideoProvider.cs +++ b/MediaBrowser.Providers/Tmdb/Music/TmdbMusicVideoProvider.cs @@ -7,14 +7,15 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Providers; using MediaBrowser.Providers.Movies; +using MediaBrowser.Providers.Tmdb.Movies; -namespace MediaBrowser.Providers.Music +namespace MediaBrowser.Providers.Tmdb.Music { - public class MovieDbMusicVideoProvider : IRemoteMetadataProvider<MusicVideo, MusicVideoInfo> + public class TmdbMusicVideoProvider : IRemoteMetadataProvider<MusicVideo, MusicVideoInfo> { public Task<MetadataResult<MusicVideo>> GetMetadata(MusicVideoInfo info, CancellationToken cancellationToken) { - return MovieDbProvider.Current.GetItemMetadata<MusicVideo>(info, cancellationToken); + return TmdbMovieProvider.Current.GetItemMetadata<MusicVideo>(info, cancellationToken); } public Task<IEnumerable<RemoteSearchResult>> GetSearchResults(MusicVideoInfo searchInfo, CancellationToken cancellationToken) @@ -22,7 +23,7 @@ namespace MediaBrowser.Providers.Music return Task.FromResult((IEnumerable<RemoteSearchResult>)new List<RemoteSearchResult>()); } - public string Name => MovieDbProvider.Current.Name; + public string Name => TmdbMovieProvider.Current.Name; public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken) { diff --git a/MediaBrowser.Providers/Tmdb/People/TmdbPersonExternalId.cs b/MediaBrowser.Providers/Tmdb/People/TmdbPersonExternalId.cs new file mode 100644 index 000000000..2c61bc70a --- /dev/null +++ b/MediaBrowser.Providers/Tmdb/People/TmdbPersonExternalId.cs @@ -0,0 +1,24 @@ +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Entities; + +namespace MediaBrowser.Providers.Tmdb.People +{ + public class TmdbPersonExternalId : IExternalId + { + /// <inheritdoc /> + public string Name => TmdbUtils.ProviderName; + + /// <inheritdoc /> + public string Key => MetadataProviders.Tmdb.ToString(); + + /// <inheritdoc /> + public string UrlFormatString => TmdbUtils.BaseTmdbUrl + "person/{0}"; + + /// <inheritdoc /> + public bool Supports(IHasProviderIds item) + { + return item is Person; + } + } +} diff --git a/MediaBrowser.Providers/People/MovieDbPersonImageProvider.cs b/MediaBrowser.Providers/Tmdb/People/TmdbPersonImageProvider.cs index 3019ee506..44ccbf453 100644 --- a/MediaBrowser.Providers/People/MovieDbPersonImageProvider.cs +++ b/MediaBrowser.Providers/Tmdb/People/TmdbPersonImageProvider.cs @@ -11,16 +11,19 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.Providers; using MediaBrowser.Model.Serialization; using MediaBrowser.Providers.Movies; +using MediaBrowser.Providers.Tmdb.Models.General; +using MediaBrowser.Providers.Tmdb.Models.People; +using MediaBrowser.Providers.Tmdb.Movies; -namespace MediaBrowser.Providers.People +namespace MediaBrowser.Providers.Tmdb.People { - public class MovieDbPersonImageProvider : IRemoteImageProvider, IHasOrder + public class TmdbPersonImageProvider : IRemoteImageProvider, IHasOrder { private readonly IServerConfigurationManager _config; private readonly IJsonSerializer _jsonSerializer; private readonly IHttpClient _httpClient; - public MovieDbPersonImageProvider(IServerConfigurationManager config, IJsonSerializer jsonSerializer, IHttpClient httpClient) + public TmdbPersonImageProvider(IServerConfigurationManager config, IJsonSerializer jsonSerializer, IHttpClient httpClient) { _config = config; _jsonSerializer = jsonSerializer; @@ -29,7 +32,7 @@ namespace MediaBrowser.Providers.People public string Name => ProviderName; - public static string ProviderName => "TheMovieDb"; + public static string ProviderName => TmdbUtils.ProviderName; public bool Supports(BaseItem item) { @@ -51,15 +54,15 @@ namespace MediaBrowser.Providers.People if (!string.IsNullOrEmpty(id)) { - await MovieDbPersonProvider.Current.EnsurePersonInfo(id, cancellationToken).ConfigureAwait(false); + await TmdbPersonProvider.Current.EnsurePersonInfo(id, cancellationToken).ConfigureAwait(false); - var dataFilePath = MovieDbPersonProvider.GetPersonDataFilePath(_config.ApplicationPaths, id); + var dataFilePath = TmdbPersonProvider.GetPersonDataFilePath(_config.ApplicationPaths, id); - var result = _jsonSerializer.DeserializeFromFile<MovieDbPersonProvider.PersonResult>(dataFilePath); + var result = _jsonSerializer.DeserializeFromFile<PersonResult>(dataFilePath); - var images = result.images ?? new MovieDbPersonProvider.Images(); + var images = result.Images ?? new PersonImages(); - var tmdbSettings = await MovieDbProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false); + var tmdbSettings = await TmdbMovieProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false); var tmdbImageUrl = tmdbSettings.images.GetImageUrl("original"); @@ -69,20 +72,20 @@ namespace MediaBrowser.Providers.People return new List<RemoteImageInfo>(); } - private IEnumerable<RemoteImageInfo> GetImages(MovieDbPersonProvider.Images images, string preferredLanguage, string baseImageUrl) + private IEnumerable<RemoteImageInfo> GetImages(PersonImages images, string preferredLanguage, string baseImageUrl) { var list = new List<RemoteImageInfo>(); - if (images.profiles != null) + if (images.Profiles != null) { - list.AddRange(images.profiles.Select(i => new RemoteImageInfo + list.AddRange(images.Profiles.Select(i => new RemoteImageInfo { ProviderName = Name, Type = ImageType.Primary, - Width = i.width, - Height = i.height, + Width = i.Width, + Height = i.Height, Language = GetLanguage(i), - Url = baseImageUrl + i.file_path + Url = baseImageUrl + i.File_Path })); } @@ -113,9 +116,9 @@ namespace MediaBrowser.Providers.People .ThenByDescending(i => i.VoteCount ?? 0); } - private string GetLanguage(MovieDbPersonProvider.Profile profile) + private string GetLanguage(Profile profile) { - return profile.iso_639_1 == null ? null : profile.iso_639_1.ToString(); + return profile.Iso_639_1?.ToString(); } public int Order => 0; diff --git a/MediaBrowser.Providers/People/MovieDbPersonProvider.cs b/MediaBrowser.Providers/Tmdb/People/TmdbPersonProvider.cs index 6d9d66f80..130403e4d 100644 --- a/MediaBrowser.Providers/People/MovieDbPersonProvider.cs +++ b/MediaBrowser.Providers/Tmdb/People/TmdbPersonProvider.cs @@ -17,16 +17,19 @@ using MediaBrowser.Model.IO; using MediaBrowser.Model.Net; using MediaBrowser.Model.Providers; using MediaBrowser.Model.Serialization; -using MediaBrowser.Providers.Movies; +using MediaBrowser.Providers.Tmdb.Models.General; +using MediaBrowser.Providers.Tmdb.Models.People; +using MediaBrowser.Providers.Tmdb.Models.Search; +using MediaBrowser.Providers.Tmdb.Movies; using Microsoft.Extensions.Logging; -namespace MediaBrowser.Providers.People +namespace MediaBrowser.Providers.Tmdb.People { - public class MovieDbPersonProvider : IRemoteMetadataProvider<Person, PersonLookupInfo> + public class TmdbPersonProvider : IRemoteMetadataProvider<Person, PersonLookupInfo> { const string DataFileName = "info.json"; - internal static MovieDbPersonProvider Current { get; private set; } + internal static TmdbPersonProvider Current { get; private set; } private readonly IJsonSerializer _jsonSerializer; private readonly IFileSystem _fileSystem; @@ -34,7 +37,7 @@ namespace MediaBrowser.Providers.People private readonly IHttpClient _httpClient; private readonly ILogger _logger; - public MovieDbPersonProvider(IFileSystem fileSystem, IServerConfigurationManager configurationManager, IJsonSerializer jsonSerializer, IHttpClient httpClient, ILogger logger) + public TmdbPersonProvider(IFileSystem fileSystem, IServerConfigurationManager configurationManager, IJsonSerializer jsonSerializer, IHttpClient httpClient, ILogger logger) { _fileSystem = fileSystem; _configurationManager = configurationManager; @@ -44,13 +47,13 @@ namespace MediaBrowser.Providers.People Current = this; } - public string Name => "TheMovieDb"; + public string Name => TmdbUtils.ProviderName; public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(PersonLookupInfo searchInfo, CancellationToken cancellationToken) { var tmdbId = searchInfo.GetProviderId(MetadataProviders.Tmdb); - var tmdbSettings = await MovieDbProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false); + var tmdbSettings = await TmdbMovieProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false); var tmdbImageUrl = tmdbSettings.images.GetImageUrl("original"); @@ -61,19 +64,19 @@ namespace MediaBrowser.Providers.People var dataFilePath = GetPersonDataFilePath(_configurationManager.ApplicationPaths, tmdbId); var info = _jsonSerializer.DeserializeFromFile<PersonResult>(dataFilePath); - var images = (info.images ?? new Images()).profiles ?? new List<Profile>(); + var images = (info.Images ?? new PersonImages()).Profiles ?? new List<Profile>(); var result = new RemoteSearchResult { - Name = info.name, + Name = info.Name, SearchProviderName = Name, - ImageUrl = images.Count == 0 ? null : (tmdbImageUrl + images[0].file_path) + ImageUrl = images.Count == 0 ? null : (tmdbImageUrl + images[0].File_Path) }; - result.SetProviderId(MetadataProviders.Tmdb, info.id.ToString(_usCulture)); - result.SetProviderId(MetadataProviders.Imdb, info.imdb_id); + result.SetProviderId(MetadataProviders.Tmdb, info.Id.ToString(_usCulture)); + result.SetProviderId(MetadataProviders.Imdb, info.Imdb_Id); return new[] { result }; } @@ -84,20 +87,20 @@ namespace MediaBrowser.Providers.People return new List<RemoteSearchResult>(); } - var url = string.Format(MovieDbProvider.BaseMovieDbUrl + @"3/search/person?api_key={1}&query={0}", WebUtility.UrlEncode(searchInfo.Name), MovieDbProvider.ApiKey); + var url = string.Format(TmdbUtils.BaseTmdbApiUrl + @"3/search/person?api_key={1}&query={0}", WebUtility.UrlEncode(searchInfo.Name), TmdbUtils.ApiKey); - using (var response = await MovieDbProvider.Current.GetMovieDbResponse(new HttpRequestOptions + using (var response = await TmdbMovieProvider.Current.GetMovieDbResponse(new HttpRequestOptions { Url = url, CancellationToken = cancellationToken, - AcceptHeader = MovieDbProvider.AcceptHeader + AcceptHeader = TmdbUtils.AcceptHeader }).ConfigureAwait(false)) { using (var json = response.Content) { - var result = await _jsonSerializer.DeserializeFromStreamAsync<PersonSearchResults>(json).ConfigureAwait(false) ?? - new PersonSearchResults(); + var result = await _jsonSerializer.DeserializeFromStreamAsync<TmdbSearchResult<PersonSearchResult>>(json).ConfigureAwait(false) ?? + new TmdbSearchResult<PersonSearchResult>(); return result.Results.Select(i => GetSearchResult(i, tmdbImageUrl)); } @@ -112,7 +115,7 @@ namespace MediaBrowser.Providers.People Name = i.Name, - ImageUrl = string.IsNullOrEmpty(i.Profile_Path) ? null : (baseImageUrl + i.Profile_Path) + ImageUrl = string.IsNullOrEmpty(i.Profile_Path) ? null : baseImageUrl + i.Profile_Path }; result.SetProviderId(MetadataProviders.Tmdb, i.Id.ToString(_usCulture)); @@ -161,27 +164,27 @@ namespace MediaBrowser.Providers.People //item.HomePageUrl = info.homepage; - if (!string.IsNullOrWhiteSpace(info.place_of_birth)) + if (!string.IsNullOrWhiteSpace(info.Place_Of_Birth)) { - item.ProductionLocations = new string[] { info.place_of_birth }; + item.ProductionLocations = new string[] { info.Place_Of_Birth }; } - item.Overview = info.biography; + item.Overview = info.Biography; - if (DateTime.TryParseExact(info.birthday, "yyyy-MM-dd", new CultureInfo("en-US"), DateTimeStyles.None, out var date)) + if (DateTime.TryParseExact(info.Birthday, "yyyy-MM-dd", new CultureInfo("en-US"), DateTimeStyles.None, out var date)) { item.PremiereDate = date.ToUniversalTime(); } - if (DateTime.TryParseExact(info.deathday, "yyyy-MM-dd", new CultureInfo("en-US"), DateTimeStyles.None, out date)) + if (DateTime.TryParseExact(info.Deathday, "yyyy-MM-dd", new CultureInfo("en-US"), DateTimeStyles.None, out date)) { item.EndDate = date.ToUniversalTime(); } - item.SetProviderId(MetadataProviders.Tmdb, info.id.ToString(_usCulture)); + item.SetProviderId(MetadataProviders.Tmdb, info.Id.ToString(_usCulture)); - if (!string.IsNullOrEmpty(info.imdb_id)) + if (!string.IsNullOrEmpty(info.Imdb_Id)) { - item.SetProviderId(MetadataProviders.Imdb, info.imdb_id); + item.SetProviderId(MetadataProviders.Imdb, info.Imdb_Id); } result.HasMetadata = true; @@ -217,13 +220,13 @@ namespace MediaBrowser.Providers.People return; } - var url = string.Format(MovieDbProvider.BaseMovieDbUrl + @"3/person/{1}?api_key={0}&append_to_response=credits,images,external_ids", MovieDbProvider.ApiKey, id); + var url = string.Format(TmdbUtils.BaseTmdbApiUrl + @"3/person/{1}?api_key={0}&append_to_response=credits,images,external_ids", TmdbUtils.ApiKey, id); - using (var response = await MovieDbProvider.Current.GetMovieDbResponse(new HttpRequestOptions + using (var response = await TmdbMovieProvider.Current.GetMovieDbResponse(new HttpRequestOptions { Url = url, CancellationToken = cancellationToken, - AcceptHeader = MovieDbProvider.AcceptHeader + AcceptHeader = TmdbUtils.AcceptHeader }).ConfigureAwait(false)) { @@ -256,133 +259,6 @@ namespace MediaBrowser.Providers.People return Path.Combine(appPaths.CachePath, "tmdb-people"); } - #region Result Objects - /// <summary> - /// Class PersonSearchResult - /// </summary> - public class PersonSearchResult - { - /// <summary> - /// Gets or sets a value indicating whether this <see cref="PersonSearchResult" /> is adult. - /// </summary> - /// <value><c>true</c> if adult; otherwise, <c>false</c>.</value> - public bool Adult { get; set; } - /// <summary> - /// Gets or sets the id. - /// </summary> - /// <value>The id.</value> - public int Id { get; set; } - /// <summary> - /// Gets or sets the name. - /// </summary> - /// <value>The name.</value> - public string Name { get; set; } - /// <summary> - /// Gets or sets the profile_ path. - /// </summary> - /// <value>The profile_ path.</value> - public string Profile_Path { get; set; } - } - - /// <summary> - /// Class PersonSearchResults - /// </summary> - public class PersonSearchResults - { - /// <summary> - /// Gets or sets the page. - /// </summary> - /// <value>The page.</value> - public int Page { get; set; } - /// <summary> - /// Gets or sets the results. - /// </summary> - /// <value>The results.</value> - public List<PersonSearchResult> Results { get; set; } - /// <summary> - /// Gets or sets the total_ pages. - /// </summary> - /// <value>The total_ pages.</value> - public int Total_Pages { get; set; } - /// <summary> - /// Gets or sets the total_ results. - /// </summary> - /// <value>The total_ results.</value> - public int Total_Results { get; set; } - } - - public class Cast - { - public int id { get; set; } - public string title { get; set; } - public string character { get; set; } - public string original_title { get; set; } - public string poster_path { get; set; } - public string release_date { get; set; } - public bool adult { get; set; } - } - - public class Crew - { - public int id { get; set; } - public string title { get; set; } - public string original_title { get; set; } - public string department { get; set; } - public string job { get; set; } - public string poster_path { get; set; } - public string release_date { get; set; } - public bool adult { get; set; } - } - - public class Credits - { - public List<Cast> cast { get; set; } - public List<Crew> crew { get; set; } - } - - public class Profile - { - public string file_path { get; set; } - public int width { get; set; } - public int height { get; set; } - public object iso_639_1 { get; set; } - public double aspect_ratio { get; set; } - } - - public class Images - { - public List<Profile> profiles { get; set; } - } - - public class ExternalIds - { - public string imdb_id { get; set; } - public string freebase_mid { get; set; } - public string freebase_id { get; set; } - public int tvrage_id { get; set; } - } - - public class PersonResult - { - public bool adult { get; set; } - public List<object> also_known_as { get; set; } - public string biography { get; set; } - public string birthday { get; set; } - public string deathday { get; set; } - public string homepage { get; set; } - public int id { get; set; } - public string imdb_id { get; set; } - public string name { get; set; } - public string place_of_birth { get; set; } - public double popularity { get; set; } - public string profile_path { get; set; } - public Credits credits { get; set; } - public Images images { get; set; } - public ExternalIds external_ids { get; set; } - } - - #endregion - public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken) { return _httpClient.GetResponse(new HttpRequestOptions diff --git a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbEpisodeImageProvider.cs b/MediaBrowser.Providers/Tmdb/TV/TmdbEpisodeImageProvider.cs index e4248afe1..51e7891a1 100644 --- a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbEpisodeImageProvider.cs +++ b/MediaBrowser.Providers/Tmdb/TV/TmdbEpisodeImageProvider.cs @@ -14,16 +14,18 @@ using MediaBrowser.Model.IO; using MediaBrowser.Model.Providers; using MediaBrowser.Model.Serialization; using MediaBrowser.Providers.Movies; +using MediaBrowser.Providers.Tmdb.Models.General; +using MediaBrowser.Providers.Tmdb.Movies; using Microsoft.Extensions.Logging; -namespace MediaBrowser.Providers.TV.TheMovieDb +namespace MediaBrowser.Providers.Tmdb.TV { - public class MovieDbEpisodeImageProvider : - MovieDbProviderBase, + public class TmdbEpisodeImageProvider : + TmdbEpisodeProviderBase, IRemoteImageProvider, IHasOrder { - public MovieDbEpisodeImageProvider(IHttpClient httpClient, IServerConfigurationManager configurationManager, IJsonSerializer jsonSerializer, IFileSystem fileSystem, ILocalizationManager localization, ILoggerFactory loggerFactory) + public TmdbEpisodeImageProvider(IHttpClient httpClient, IServerConfigurationManager configurationManager, IJsonSerializer jsonSerializer, IFileSystem fileSystem, ILocalizationManager localization, ILoggerFactory loggerFactory) : base(httpClient, configurationManager, jsonSerializer, fileSystem, localization, loggerFactory) { } @@ -62,18 +64,18 @@ namespace MediaBrowser.Providers.TV.TheMovieDb var response = await GetEpisodeInfo(seriesId, seasonNumber.Value, episodeNumber.Value, language, cancellationToken).ConfigureAwait(false); - var tmdbSettings = await MovieDbProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false); + var tmdbSettings = await TmdbMovieProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false); var tmdbImageUrl = tmdbSettings.images.GetImageUrl("original"); - list.AddRange(GetPosters(response.images).Select(i => new RemoteImageInfo + list.AddRange(GetPosters(response.Images).Select(i => new RemoteImageInfo { - Url = tmdbImageUrl + i.file_path, - CommunityRating = i.vote_average, - VoteCount = i.vote_count, - Width = i.width, - Height = i.height, - Language = MovieDbProvider.AdjustImageLanguage(i.iso_639_1, language), + Url = tmdbImageUrl + i.File_Path, + CommunityRating = i.Vote_Average, + VoteCount = i.Vote_Count, + Width = i.Width, + Height = i.Height, + Language = TmdbMovieProvider.AdjustImageLanguage(i.Iso_639_1, language), ProviderName = Name, Type = ImageType.Primary, RatingType = RatingType.Score @@ -106,9 +108,9 @@ namespace MediaBrowser.Providers.TV.TheMovieDb } - private IEnumerable<Still> GetPosters(Images images) + private IEnumerable<Still> GetPosters(StillImages images) { - return images.stills ?? new List<Still>(); + return images.Stills ?? new List<Still>(); } @@ -117,12 +119,13 @@ namespace MediaBrowser.Providers.TV.TheMovieDb return GetResponse(url, cancellationToken); } - public string Name => "TheMovieDb"; + public string Name => TmdbUtils.ProviderName; public bool Supports(BaseItem item) { return item is Controller.Entities.TV.Episode; } + // After TheTvDb public int Order => 1; } diff --git a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbEpisodeProvider.cs b/MediaBrowser.Providers/Tmdb/TV/TmdbEpisodeProvider.cs index 3d7745085..a17f5d17a 100644 --- a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbEpisodeProvider.cs +++ b/MediaBrowser.Providers/Tmdb/TV/TmdbEpisodeProvider.cs @@ -18,14 +18,14 @@ using MediaBrowser.Model.Providers; using MediaBrowser.Model.Serialization; using Microsoft.Extensions.Logging; -namespace MediaBrowser.Providers.TV.TheMovieDb +namespace MediaBrowser.Providers.Tmdb.TV { - public class MovieDbEpisodeProvider : - MovieDbProviderBase, + public class TmdbEpisodeProvider : + TmdbEpisodeProviderBase, IRemoteMetadataProvider<Episode, EpisodeInfo>, IHasOrder { - public MovieDbEpisodeProvider(IHttpClient httpClient, IServerConfigurationManager configurationManager, IJsonSerializer jsonSerializer, IFileSystem fileSystem, ILocalizationManager localization, ILoggerFactory loggerFactory) + public TmdbEpisodeProvider(IHttpClient httpClient, IServerConfigurationManager configurationManager, IJsonSerializer jsonSerializer, IFileSystem fileSystem, ILocalizationManager localization, ILoggerFactory loggerFactory) : base(httpClient, configurationManager, jsonSerializer, fileSystem, localization, loggerFactory) { } @@ -93,7 +93,7 @@ namespace MediaBrowser.Providers.TV.TheMovieDb result.HasMetadata = true; result.QueriedById = true; - if (!string.IsNullOrEmpty(response.overview)) + if (!string.IsNullOrEmpty(response.Overview)) { // if overview is non-empty, we can assume that localized data was returned result.ResultLanguage = info.MetadataLanguage; @@ -107,30 +107,29 @@ namespace MediaBrowser.Providers.TV.TheMovieDb item.ParentIndexNumber = info.ParentIndexNumber; item.IndexNumberEnd = info.IndexNumberEnd; - if (response.external_ids.tvdb_id > 0) + if (response.External_Ids.Tvdb_Id > 0) { - item.SetProviderId(MetadataProviders.Tvdb, response.external_ids.tvdb_id.ToString(CultureInfo.InvariantCulture)); + item.SetProviderId(MetadataProviders.Tvdb, response.External_Ids.Tvdb_Id.ToString(CultureInfo.InvariantCulture)); } - item.PremiereDate = response.air_date; + item.PremiereDate = response.Air_Date; item.ProductionYear = result.Item.PremiereDate.Value.Year; - item.Name = response.name; - item.Overview = response.overview; + item.Name = response.Name; + item.Overview = response.Overview; - item.CommunityRating = (float)response.vote_average; - //item.VoteCount = response.vote_count; + item.CommunityRating = (float)response.Vote_Average; - if (response.videos != null && response.videos.results != null) + if (response.Videos?.Results != null) { - foreach (var video in response.videos.results) + foreach (var video in response.Videos.Results) { - if (video.type.Equals("trailer", System.StringComparison.OrdinalIgnoreCase) - || video.type.Equals("clip", System.StringComparison.OrdinalIgnoreCase)) + if (video.Type.Equals("trailer", System.StringComparison.OrdinalIgnoreCase) + || video.Type.Equals("clip", System.StringComparison.OrdinalIgnoreCase)) { - if (video.site.Equals("youtube", System.StringComparison.OrdinalIgnoreCase)) + if (video.Site.Equals("youtube", System.StringComparison.OrdinalIgnoreCase)) { - var videoUrl = string.Format("http://www.youtube.com/watch?v={0}", video.key); + var videoUrl = string.Format("http://www.youtube.com/watch?v={0}", video.Key); item.AddTrailerUrl(videoUrl); } } @@ -139,54 +138,50 @@ namespace MediaBrowser.Providers.TV.TheMovieDb result.ResetPeople(); - var credits = response.credits; + var credits = response.Credits; if (credits != null) { //Actors, Directors, Writers - all in People //actors come from cast - if (credits.cast != null) + if (credits.Cast != null) { - foreach (var actor in credits.cast.OrderBy(a => a.order)) + foreach (var actor in credits.Cast.OrderBy(a => a.Order)) { - result.AddPerson(new PersonInfo { Name = actor.name.Trim(), Role = actor.character, Type = PersonType.Actor, SortOrder = actor.order }); + result.AddPerson(new PersonInfo { Name = actor.Name.Trim(), Role = actor.Character, Type = PersonType.Actor, SortOrder = actor.Order }); } } // guest stars - if (credits.guest_stars != null) + if (credits.Guest_Stars != null) { - foreach (var guest in credits.guest_stars.OrderBy(a => a.order)) + foreach (var guest in credits.Guest_Stars.OrderBy(a => a.Order)) { - result.AddPerson(new PersonInfo { Name = guest.name.Trim(), Role = guest.character, Type = PersonType.GuestStar, SortOrder = guest.order }); + result.AddPerson(new PersonInfo { Name = guest.Name.Trim(), Role = guest.Character, Type = PersonType.GuestStar, SortOrder = guest.Order }); } } //and the rest from crew - if (credits.crew != null) + if (credits.Crew != null) { var keepTypes = new[] { PersonType.Director, - //PersonType.Writer, - //PersonType.Producer + PersonType.Writer, + PersonType.Producer }; - foreach (var person in credits.crew) + foreach (var person in credits.Crew) { // Normalize this - var type = person.department; - if (string.Equals(type, "writing", StringComparison.OrdinalIgnoreCase)) - { - type = PersonType.Writer; - } + var type = TmdbUtils.MapCrewToPersonType(person); - if (!keepTypes.Contains(type ?? string.Empty, StringComparer.OrdinalIgnoreCase) && - !keepTypes.Contains(person.job ?? string.Empty, StringComparer.OrdinalIgnoreCase)) + if (!keepTypes.Contains(type, StringComparer.OrdinalIgnoreCase) && + !keepTypes.Contains(person.Job ?? string.Empty, StringComparer.OrdinalIgnoreCase)) { continue; } - result.AddPerson(new PersonInfo { Name = person.name.Trim(), Role = person.job, Type = type }); + result.AddPerson(new PersonInfo { Name = person.Name.Trim(), Role = person.Job, Type = type }); } } } @@ -211,6 +206,6 @@ namespace MediaBrowser.Providers.TV.TheMovieDb // After TheTvDb public int Order => 1; - public string Name => "TheMovieDb"; + public string Name => TmdbUtils.ProviderName; } } diff --git a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbProviderBase.cs b/MediaBrowser.Providers/Tmdb/TV/TmdbEpisodeProviderBase.cs index 6e438ebd8..2003261c9 100644 --- a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbProviderBase.cs +++ b/MediaBrowser.Providers/Tmdb/TV/TmdbEpisodeProviderBase.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Globalization; using System.IO; using System.Threading; @@ -10,13 +9,15 @@ using MediaBrowser.Model.Globalization; using MediaBrowser.Model.IO; using MediaBrowser.Model.Serialization; using MediaBrowser.Providers.Movies; +using MediaBrowser.Providers.Tmdb.Models.TV; +using MediaBrowser.Providers.Tmdb.Movies; using Microsoft.Extensions.Logging; -namespace MediaBrowser.Providers.TV.TheMovieDb +namespace MediaBrowser.Providers.Tmdb.TV { - public abstract class MovieDbProviderBase + public abstract class TmdbEpisodeProviderBase { - private const string EpisodeUrlPattern = MovieDbProvider.BaseMovieDbUrl + @"3/tv/{0}/season/{1}/episode/{2}?api_key={3}&append_to_response=images,external_ids,credits,videos"; + private const string EpisodeUrlPattern = TmdbUtils.BaseTmdbApiUrl + @"3/tv/{0}/season/{1}/episode/{2}?api_key={3}&append_to_response=images,external_ids,credits,videos"; private readonly IHttpClient _httpClient; private readonly IServerConfigurationManager _configurationManager; private readonly IJsonSerializer _jsonSerializer; @@ -24,7 +25,7 @@ namespace MediaBrowser.Providers.TV.TheMovieDb private readonly ILocalizationManager _localization; private readonly ILogger _logger; - public MovieDbProviderBase(IHttpClient httpClient, IServerConfigurationManager configurationManager, IJsonSerializer jsonSerializer, IFileSystem fileSystem, ILocalizationManager localization, ILoggerFactory loggerFactory) + protected TmdbEpisodeProviderBase(IHttpClient httpClient, IServerConfigurationManager configurationManager, IJsonSerializer jsonSerializer, IFileSystem fileSystem, ILocalizationManager localization, ILoggerFactory loggerFactory) { _httpClient = httpClient; _configurationManager = configurationManager; @@ -36,7 +37,7 @@ namespace MediaBrowser.Providers.TV.TheMovieDb protected ILogger Logger => _logger; - protected async Task<RootObject> GetEpisodeInfo(string seriesTmdbId, int season, int episodeNumber, string preferredMetadataLanguage, + protected async Task<EpisodeResult> GetEpisodeInfo(string seriesTmdbId, int season, int episodeNumber, string preferredMetadataLanguage, CancellationToken cancellationToken) { await EnsureEpisodeInfo(seriesTmdbId, season, episodeNumber, preferredMetadataLanguage, cancellationToken) @@ -44,7 +45,7 @@ namespace MediaBrowser.Providers.TV.TheMovieDb var dataFilePath = GetDataFilePath(seriesTmdbId, season, episodeNumber, preferredMetadataLanguage); - return _jsonSerializer.DeserializeFromFile<RootObject>(dataFilePath); + return _jsonSerializer.DeserializeFromFile<EpisodeResult>(dataFilePath); } internal Task EnsureEpisodeInfo(string tmdbId, int seasonNumber, int episodeNumber, string language, CancellationToken cancellationToken) @@ -85,7 +86,7 @@ namespace MediaBrowser.Providers.TV.TheMovieDb throw new ArgumentNullException(nameof(preferredLanguage)); } - var path = MovieDbSeriesProvider.GetSeriesDataPath(_configurationManager.ApplicationPaths, tmdbId); + var path = TmdbSeriesProvider.GetSeriesDataPath(_configurationManager.ApplicationPaths, tmdbId); var filename = string.Format("season-{0}-episode-{1}-{2}.json", seasonNumber.ToString(CultureInfo.InvariantCulture), @@ -105,32 +106,32 @@ namespace MediaBrowser.Providers.TV.TheMovieDb _jsonSerializer.SerializeToFile(mainResult, dataFilePath); } - internal async Task<RootObject> FetchMainResult(string urlPattern, string id, int seasonNumber, int episodeNumber, string language, CancellationToken cancellationToken) + internal async Task<EpisodeResult> FetchMainResult(string urlPattern, string id, int seasonNumber, int episodeNumber, string language, CancellationToken cancellationToken) { - var url = string.Format(urlPattern, id, seasonNumber.ToString(CultureInfo.InvariantCulture), episodeNumber, MovieDbProvider.ApiKey); + var url = string.Format(urlPattern, id, seasonNumber.ToString(CultureInfo.InvariantCulture), episodeNumber, TmdbUtils.ApiKey); if (!string.IsNullOrEmpty(language)) { url += string.Format("&language={0}", language); } - var includeImageLanguageParam = MovieDbProvider.GetImageLanguagesParam(language); + var includeImageLanguageParam = TmdbMovieProvider.GetImageLanguagesParam(language); // Get images in english and with no language url += "&include_image_language=" + includeImageLanguageParam; cancellationToken.ThrowIfCancellationRequested(); - using (var response = await MovieDbProvider.Current.GetMovieDbResponse(new HttpRequestOptions + using (var response = await TmdbMovieProvider.Current.GetMovieDbResponse(new HttpRequestOptions { Url = url, CancellationToken = cancellationToken, - AcceptHeader = MovieDbProvider.AcceptHeader + AcceptHeader = TmdbUtils.AcceptHeader }).ConfigureAwait(false)) { using (var json = response.Content) { - return await _jsonSerializer.DeserializeFromStreamAsync<RootObject>(json).ConfigureAwait(false); + return await _jsonSerializer.DeserializeFromStreamAsync<EpisodeResult>(json).ConfigureAwait(false); } } } @@ -143,103 +144,5 @@ namespace MediaBrowser.Providers.TV.TheMovieDb Url = url }); } - - public class Still - { - public double aspect_ratio { get; set; } - public string file_path { get; set; } - public int height { get; set; } - public string id { get; set; } - public string iso_639_1 { get; set; } - public double vote_average { get; set; } - public int vote_count { get; set; } - public int width { get; set; } - } - - public class Images - { - public List<Still> stills { get; set; } - } - - public class ExternalIds - { - public string imdb_id { get; set; } - public object freebase_id { get; set; } - public string freebase_mid { get; set; } - public int tvdb_id { get; set; } - public int tvrage_id { get; set; } - } - - public class Cast - { - public string character { get; set; } - public string credit_id { get; set; } - public int id { get; set; } - public string name { get; set; } - public string profile_path { get; set; } - public int order { get; set; } - } - - public class Crew - { - public int id { get; set; } - public string credit_id { get; set; } - public string name { get; set; } - public string department { get; set; } - public string job { get; set; } - public string profile_path { get; set; } - } - - public class GuestStar - { - public int id { get; set; } - public string name { get; set; } - public string credit_id { get; set; } - public string character { get; set; } - public int order { get; set; } - public string profile_path { get; set; } - } - - public class Credits - { - public List<Cast> cast { get; set; } - public List<Crew> crew { get; set; } - public List<GuestStar> guest_stars { get; set; } - } - - public class Videos - { - public List<Video> results { get; set; } - } - - public class Video - { - public string id { get; set; } - public string iso_639_1 { get; set; } - public string iso_3166_1 { get; set; } - public string key { get; set; } - public string name { get; set; } - public string site { get; set; } - public string size { get; set; } - public string type { get; set; } - } - - public class RootObject - { - public DateTime air_date { get; set; } - public int episode_number { get; set; } - public string name { get; set; } - public string overview { get; set; } - public int id { get; set; } - public object production_code { get; set; } - public int season_number { get; set; } - public string still_path { get; set; } - public double vote_average { get; set; } - public int vote_count { get; set; } - public Images images { get; set; } - public ExternalIds external_ids { get; set; } - public Credits credits { get; set; } - public Videos videos { get; set; } - } } } diff --git a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeasonProvider.cs b/MediaBrowser.Providers/Tmdb/TV/TmdbSeasonProvider.cs index 6be1b101d..2f2ac58e8 100644 --- a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeasonProvider.cs +++ b/MediaBrowser.Providers/Tmdb/TV/TmdbSeasonProvider.cs @@ -7,7 +7,6 @@ using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Globalization; @@ -16,13 +15,16 @@ using MediaBrowser.Model.Net; using MediaBrowser.Model.Providers; using MediaBrowser.Model.Serialization; using MediaBrowser.Providers.Movies; +using MediaBrowser.Providers.Tmdb.Models.TV; +using MediaBrowser.Providers.Tmdb.Movies; using Microsoft.Extensions.Logging; +using Season = MediaBrowser.Controller.Entities.TV.Season; -namespace MediaBrowser.Providers.TV.TheMovieDb +namespace MediaBrowser.Providers.Tmdb.TV { - public class MovieDbSeasonProvider : IRemoteMetadataProvider<Season, SeasonInfo> + public class TmdbSeasonProvider : IRemoteMetadataProvider<Season, SeasonInfo> { - private const string GetTvInfo3 = MovieDbProvider.BaseMovieDbUrl + @"3/tv/{0}/season/{1}?api_key={2}&append_to_response=images,keywords,external_ids,credits,videos"; + private const string GetTvInfo3 = TmdbUtils.BaseTmdbApiUrl + @"3/tv/{0}/season/{1}?api_key={2}&append_to_response=images,keywords,external_ids,credits,videos"; private readonly IHttpClient _httpClient; private readonly IServerConfigurationManager _configurationManager; private readonly IJsonSerializer _jsonSerializer; @@ -30,7 +32,7 @@ namespace MediaBrowser.Providers.TV.TheMovieDb private readonly ILocalizationManager _localization; private readonly ILogger _logger; - public MovieDbSeasonProvider(IHttpClient httpClient, IServerConfigurationManager configurationManager, IFileSystem fileSystem, ILocalizationManager localization, IJsonSerializer jsonSerializer, ILoggerFactory loggerFactory) + public TmdbSeasonProvider(IHttpClient httpClient, IServerConfigurationManager configurationManager, IFileSystem fileSystem, ILocalizationManager localization, IJsonSerializer jsonSerializer, ILoggerFactory loggerFactory) { _httpClient = httpClient; _configurationManager = configurationManager; @@ -65,31 +67,31 @@ namespace MediaBrowser.Providers.TV.TheMovieDb result.Item.IndexNumber = seasonNumber; - result.Item.Overview = seasonInfo.overview; + result.Item.Overview = seasonInfo.Overview; - if (seasonInfo.external_ids.tvdb_id > 0) + if (seasonInfo.External_Ids.Tvdb_Id > 0) { - result.Item.SetProviderId(MetadataProviders.Tvdb, seasonInfo.external_ids.tvdb_id.ToString(CultureInfo.InvariantCulture)); + result.Item.SetProviderId(MetadataProviders.Tvdb, seasonInfo.External_Ids.Tvdb_Id.ToString(CultureInfo.InvariantCulture)); } - var credits = seasonInfo.credits; + var credits = seasonInfo.Credits; if (credits != null) { //Actors, Directors, Writers - all in People //actors come from cast - if (credits.cast != null) + if (credits.Cast != null) { //foreach (var actor in credits.cast.OrderBy(a => a.order)) result.Item.AddPerson(new PersonInfo { Name = actor.name.Trim(), Role = actor.character, Type = PersonType.Actor, SortOrder = actor.order }); } //and the rest from crew - if (credits.crew != null) + if (credits.Crew != null) { //foreach (var person in credits.crew) result.Item.AddPerson(new PersonInfo { Name = person.name.Trim(), Role = person.job, Type = person.department }); } } - result.Item.PremiereDate = seasonInfo.air_date; + result.Item.PremiereDate = seasonInfo.Air_Date; result.Item.ProductionYear = result.Item.PremiereDate.Value.Year; } catch (HttpException ex) @@ -108,7 +110,7 @@ namespace MediaBrowser.Providers.TV.TheMovieDb return result; } - public string Name => "TheMovieDb"; + public string Name => TmdbUtils.ProviderName; public Task<IEnumerable<RemoteSearchResult>> GetSearchResults(SeasonInfo searchInfo, CancellationToken cancellationToken) { @@ -124,7 +126,7 @@ namespace MediaBrowser.Providers.TV.TheMovieDb }); } - private async Task<RootObject> GetSeasonInfo(string seriesTmdbId, int season, string preferredMetadataLanguage, + private async Task<SeasonResult> GetSeasonInfo(string seriesTmdbId, int season, string preferredMetadataLanguage, CancellationToken cancellationToken) { await EnsureSeasonInfo(seriesTmdbId, season, preferredMetadataLanguage, cancellationToken) @@ -132,7 +134,7 @@ namespace MediaBrowser.Providers.TV.TheMovieDb var dataFilePath = GetDataFilePath(seriesTmdbId, season, preferredMetadataLanguage); - return _jsonSerializer.DeserializeFromFile<RootObject>(dataFilePath); + return _jsonSerializer.DeserializeFromFile<SeasonResult>(dataFilePath); } internal Task EnsureSeasonInfo(string tmdbId, int seasonNumber, string language, CancellationToken cancellationToken) @@ -173,7 +175,7 @@ namespace MediaBrowser.Providers.TV.TheMovieDb throw new ArgumentNullException(nameof(preferredLanguage)); } - var path = MovieDbSeriesProvider.GetSeriesDataPath(_configurationManager.ApplicationPaths, tmdbId); + var path = TmdbSeriesProvider.GetSeriesDataPath(_configurationManager.ApplicationPaths, tmdbId); var filename = string.Format("season-{0}-{1}.json", seasonNumber.ToString(CultureInfo.InvariantCulture), @@ -192,117 +194,34 @@ namespace MediaBrowser.Providers.TV.TheMovieDb _jsonSerializer.SerializeToFile(mainResult, dataFilePath); } - internal async Task<RootObject> FetchMainResult(string id, int seasonNumber, string language, CancellationToken cancellationToken) + internal async Task<SeasonResult> FetchMainResult(string id, int seasonNumber, string language, CancellationToken cancellationToken) { - var url = string.Format(GetTvInfo3, id, seasonNumber.ToString(CultureInfo.InvariantCulture), MovieDbProvider.ApiKey); + var url = string.Format(GetTvInfo3, id, seasonNumber.ToString(CultureInfo.InvariantCulture), TmdbUtils.ApiKey); if (!string.IsNullOrEmpty(language)) { - url += string.Format("&language={0}", MovieDbProvider.NormalizeLanguage(language)); + url += string.Format("&language={0}", TmdbMovieProvider.NormalizeLanguage(language)); } - var includeImageLanguageParam = MovieDbProvider.GetImageLanguagesParam(language); + var includeImageLanguageParam = TmdbMovieProvider.GetImageLanguagesParam(language); // Get images in english and with no language url += "&include_image_language=" + includeImageLanguageParam; cancellationToken.ThrowIfCancellationRequested(); - using (var response = await MovieDbProvider.Current.GetMovieDbResponse(new HttpRequestOptions + using (var response = await TmdbMovieProvider.Current.GetMovieDbResponse(new HttpRequestOptions { Url = url, CancellationToken = cancellationToken, - AcceptHeader = MovieDbProvider.AcceptHeader + AcceptHeader = TmdbUtils.AcceptHeader }).ConfigureAwait(false)) { using (var json = response.Content) { - return await _jsonSerializer.DeserializeFromStreamAsync<RootObject>(json).ConfigureAwait(false); + return await _jsonSerializer.DeserializeFromStreamAsync<SeasonResult>(json).ConfigureAwait(false); } } } - - public class Episode - { - public string air_date { get; set; } - public int episode_number { get; set; } - public int id { get; set; } - public string name { get; set; } - public string overview { get; set; } - public string still_path { get; set; } - public double vote_average { get; set; } - public int vote_count { get; set; } - } - - public class Cast - { - public string character { get; set; } - public string credit_id { get; set; } - public int id { get; set; } - public string name { get; set; } - public string profile_path { get; set; } - public int order { get; set; } - } - - public class Crew - { - public string credit_id { get; set; } - public string department { get; set; } - public int id { get; set; } - public string name { get; set; } - public string job { get; set; } - public string profile_path { get; set; } - } - - public class Credits - { - public List<Cast> cast { get; set; } - public List<Crew> crew { get; set; } - } - - public class Poster - { - public double aspect_ratio { get; set; } - public string file_path { get; set; } - public int height { get; set; } - public string id { get; set; } - public string iso_639_1 { get; set; } - public double vote_average { get; set; } - public int vote_count { get; set; } - public int width { get; set; } - } - - public class Images - { - public List<Poster> posters { get; set; } - } - - public class ExternalIds - { - public string freebase_id { get; set; } - public string freebase_mid { get; set; } - public int tvdb_id { get; set; } - public object tvrage_id { get; set; } - } - - public class Videos - { - public List<object> results { get; set; } - } - - public class RootObject - { - public DateTime air_date { get; set; } - public List<Episode> episodes { get; set; } - public string name { get; set; } - public string overview { get; set; } - public int id { get; set; } - public string poster_path { get; set; } - public int season_number { get; set; } - public Credits credits { get; set; } - public Images images { get; set; } - public ExternalIds external_ids { get; set; } - public Videos videos { get; set; } - } } } diff --git a/MediaBrowser.Providers/Tmdb/TV/TmdbSeriesExternalId.cs b/MediaBrowser.Providers/Tmdb/TV/TmdbSeriesExternalId.cs new file mode 100644 index 000000000..524a3b05e --- /dev/null +++ b/MediaBrowser.Providers/Tmdb/TV/TmdbSeriesExternalId.cs @@ -0,0 +1,24 @@ +using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Entities; + +namespace MediaBrowser.Providers.Tmdb.TV +{ + public class TmdbSeriesExternalId : IExternalId + { + /// <inheritdoc /> + public string Name => TmdbUtils.ProviderName; + + /// <inheritdoc /> + public string Key => MetadataProviders.Tmdb.ToString(); + + /// <inheritdoc /> + public string UrlFormatString => TmdbUtils.BaseTmdbUrl + "tv/{0}"; + + /// <inheritdoc /> + public bool Supports(IHasProviderIds item) + { + return item is Series; + } + } +} diff --git a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeriesImageProvider.cs b/MediaBrowser.Providers/Tmdb/TV/TmdbSeriesImageProvider.cs index 26686356f..882ec7574 100644 --- a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeriesImageProvider.cs +++ b/MediaBrowser.Providers/Tmdb/TV/TmdbSeriesImageProvider.cs @@ -13,16 +13,19 @@ using MediaBrowser.Model.IO; using MediaBrowser.Model.Providers; using MediaBrowser.Model.Serialization; using MediaBrowser.Providers.Movies; +using MediaBrowser.Providers.Tmdb.Models.General; +using MediaBrowser.Providers.Tmdb.Models.TV; +using MediaBrowser.Providers.Tmdb.Movies; -namespace MediaBrowser.Providers.TV.TheMovieDb +namespace MediaBrowser.Providers.Tmdb.TV { - public class MovieDbSeriesImageProvider : IRemoteImageProvider, IHasOrder + public class TmdbSeriesImageProvider : IRemoteImageProvider, IHasOrder { private readonly IJsonSerializer _jsonSerializer; private readonly IHttpClient _httpClient; private readonly IFileSystem _fileSystem; - public MovieDbSeriesImageProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, IFileSystem fileSystem) + public TmdbSeriesImageProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, IFileSystem fileSystem) { _jsonSerializer = jsonSerializer; _httpClient = httpClient; @@ -31,7 +34,7 @@ namespace MediaBrowser.Providers.TV.TheMovieDb public string Name => ProviderName; - public static string ProviderName => "TheMovieDb"; + public static string ProviderName => TmdbUtils.ProviderName; public bool Supports(BaseItem item) { @@ -58,7 +61,7 @@ namespace MediaBrowser.Providers.TV.TheMovieDb return list; } - var tmdbSettings = await MovieDbProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false); + var tmdbSettings = await TmdbMovieProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false); var tmdbImageUrl = tmdbSettings.images.GetImageUrl("original"); @@ -66,12 +69,12 @@ namespace MediaBrowser.Providers.TV.TheMovieDb list.AddRange(GetPosters(results).Select(i => new RemoteImageInfo { - Url = tmdbImageUrl + i.file_path, - CommunityRating = i.vote_average, - VoteCount = i.vote_count, - Width = i.width, - Height = i.height, - Language = MovieDbProvider.AdjustImageLanguage(i.iso_639_1, language), + Url = tmdbImageUrl + i.File_Path, + CommunityRating = i.Vote_Average, + VoteCount = i.Vote_Count, + Width = i.Width, + Height = i.Height, + Language = TmdbMovieProvider.AdjustImageLanguage(i.Iso_639_1, language), ProviderName = Name, Type = ImageType.Primary, RatingType = RatingType.Score @@ -79,11 +82,11 @@ namespace MediaBrowser.Providers.TV.TheMovieDb list.AddRange(GetBackdrops(results).Select(i => new RemoteImageInfo { - Url = tmdbImageUrl + i.file_path, - CommunityRating = i.vote_average, - VoteCount = i.vote_count, - Width = i.width, - Height = i.height, + Url = tmdbImageUrl + i.File_Path, + CommunityRating = i.Vote_Average, + VoteCount = i.Vote_Count, + Width = i.Width, + Height = i.Height, ProviderName = Name, Type = ImageType.Backdrop, RatingType = RatingType.Score @@ -118,22 +121,21 @@ namespace MediaBrowser.Providers.TV.TheMovieDb /// Gets the posters. /// </summary> /// <param name="images">The images.</param> - private IEnumerable<MovieDbSeriesProvider.Poster> GetPosters(MovieDbSeriesProvider.Images images) + private IEnumerable<Poster> GetPosters(Images images) { - return images.posters ?? new List<MovieDbSeriesProvider.Poster>(); + return images.Posters ?? new List<Poster>(); } /// <summary> /// Gets the backdrops. /// </summary> /// <param name="images">The images.</param> - private IEnumerable<MovieDbSeriesProvider.Backdrop> GetBackdrops(MovieDbSeriesProvider.Images images) + private IEnumerable<Backdrop> GetBackdrops(Images images) { - var eligibleBackdrops = images.backdrops == null ? new List<MovieDbSeriesProvider.Backdrop>() : - images.backdrops; + var eligibleBackdrops = images.Backdrops ?? new List<Backdrop>(); - return eligibleBackdrops.OrderByDescending(i => i.vote_average) - .ThenByDescending(i => i.vote_count); + return eligibleBackdrops.OrderByDescending(i => i.Vote_Average) + .ThenByDescending(i => i.Vote_Count); } /// <summary> @@ -144,7 +146,7 @@ namespace MediaBrowser.Providers.TV.TheMovieDb /// <param name="jsonSerializer">The json serializer.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task{MovieImages}.</returns> - private async Task<MovieDbSeriesProvider.Images> FetchImages(BaseItem item, string language, IJsonSerializer jsonSerializer, + private async Task<Images> FetchImages(BaseItem item, string language, IJsonSerializer jsonSerializer, CancellationToken cancellationToken) { var tmdbId = item.GetProviderId(MetadataProviders.Tmdb); @@ -154,9 +156,9 @@ namespace MediaBrowser.Providers.TV.TheMovieDb return null; } - await MovieDbSeriesProvider.Current.EnsureSeriesInfo(tmdbId, language, cancellationToken).ConfigureAwait(false); + await TmdbSeriesProvider.Current.EnsureSeriesInfo(tmdbId, language, cancellationToken).ConfigureAwait(false); - var path = MovieDbSeriesProvider.Current.GetDataFilePath(tmdbId, language); + var path = TmdbSeriesProvider.Current.GetDataFilePath(tmdbId, language); if (!string.IsNullOrEmpty(path)) { @@ -164,7 +166,7 @@ namespace MediaBrowser.Providers.TV.TheMovieDb if (fileInfo.Exists) { - return jsonSerializer.DeserializeFromFile<MovieDbSeriesProvider.RootObject>(path).images; + return jsonSerializer.DeserializeFromFile<SeriesResult>(path).Images; } } diff --git a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeriesProvider.cs b/MediaBrowser.Providers/Tmdb/TV/TmdbSeriesProvider.cs index b51fb6af8..304f34c25 100644 --- a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeriesProvider.cs +++ b/MediaBrowser.Providers/Tmdb/TV/TmdbSeriesProvider.cs @@ -18,16 +18,19 @@ using MediaBrowser.Model.IO; using MediaBrowser.Model.Providers; using MediaBrowser.Model.Serialization; using MediaBrowser.Providers.Movies; +using MediaBrowser.Providers.Tmdb.Models.Search; +using MediaBrowser.Providers.Tmdb.Models.TV; +using MediaBrowser.Providers.Tmdb.Movies; using Microsoft.Extensions.Logging; -namespace MediaBrowser.Providers.TV.TheMovieDb +namespace MediaBrowser.Providers.Tmdb.TV { - public class MovieDbSeriesProvider : IRemoteMetadataProvider<Series, SeriesInfo>, IHasOrder + public class TmdbSeriesProvider : IRemoteMetadataProvider<Series, SeriesInfo>, IHasOrder { - private const string GetTvInfo3 = MovieDbProvider.BaseMovieDbUrl + @"3/tv/{0}?api_key={1}&append_to_response=credits,images,keywords,external_ids,videos,content_ratings"; + private const string GetTvInfo3 = TmdbUtils.BaseTmdbApiUrl + @"3/tv/{0}?api_key={1}&append_to_response=credits,images,keywords,external_ids,videos,content_ratings"; private readonly CultureInfo _usCulture = new CultureInfo("en-US"); - internal static MovieDbSeriesProvider Current { get; private set; } + internal static TmdbSeriesProvider Current { get; private set; } private readonly IJsonSerializer _jsonSerializer; private readonly IFileSystem _fileSystem; @@ -37,7 +40,7 @@ namespace MediaBrowser.Providers.TV.TheMovieDb private readonly IHttpClient _httpClient; private readonly ILibraryManager _libraryManager; - public MovieDbSeriesProvider(IJsonSerializer jsonSerializer, IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILogger logger, ILocalizationManager localization, IHttpClient httpClient, ILibraryManager libraryManager) + public TmdbSeriesProvider(IJsonSerializer jsonSerializer, IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILogger logger, ILocalizationManager localization, IHttpClient httpClient, ILibraryManager libraryManager) { _jsonSerializer = jsonSerializer; _fileSystem = fileSystem; @@ -49,7 +52,7 @@ namespace MediaBrowser.Providers.TV.TheMovieDb Current = this; } - public string Name => "TheMovieDb"; + public string Name => TmdbUtils.ProviderName; public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(SeriesInfo searchInfo, CancellationToken cancellationToken) { @@ -63,24 +66,24 @@ namespace MediaBrowser.Providers.TV.TheMovieDb var dataFilePath = GetDataFilePath(tmdbId, searchInfo.MetadataLanguage); - var obj = _jsonSerializer.DeserializeFromFile<RootObject>(dataFilePath); + var obj = _jsonSerializer.DeserializeFromFile<SeriesResult>(dataFilePath); - var tmdbSettings = await MovieDbProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false); + var tmdbSettings = await TmdbMovieProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false); var tmdbImageUrl = tmdbSettings.images.GetImageUrl("original"); var remoteResult = new RemoteSearchResult { - Name = obj.name, + Name = obj.Name, SearchProviderName = Name, - ImageUrl = string.IsNullOrWhiteSpace(obj.poster_path) ? null : tmdbImageUrl + obj.poster_path + ImageUrl = string.IsNullOrWhiteSpace(obj.Poster_Path) ? null : tmdbImageUrl + obj.Poster_Path }; - remoteResult.SetProviderId(MetadataProviders.Tmdb, obj.id.ToString(_usCulture)); - remoteResult.SetProviderId(MetadataProviders.Imdb, obj.external_ids.imdb_id); + remoteResult.SetProviderId(MetadataProviders.Tmdb, obj.Id.ToString(_usCulture)); + remoteResult.SetProviderId(MetadataProviders.Imdb, obj.External_Ids.Imdb_Id); - if (obj.external_ids.tvdb_id > 0) + if (obj.External_Ids.Tvdb_Id > 0) { - remoteResult.SetProviderId(MetadataProviders.Tvdb, obj.external_ids.tvdb_id.ToString(_usCulture)); + remoteResult.SetProviderId(MetadataProviders.Tvdb, obj.External_Ids.Tvdb_Id.ToString(_usCulture)); } return new[] { remoteResult }; @@ -110,7 +113,7 @@ namespace MediaBrowser.Providers.TV.TheMovieDb } } - return await new MovieDbSearch(_logger, _jsonSerializer, _libraryManager).GetSearchResults(searchInfo, cancellationToken).ConfigureAwait(false); + return await new TmdbSearch(_logger, _jsonSerializer, _libraryManager).GetSearchResults(searchInfo, cancellationToken).ConfigureAwait(false); } public async Task<MetadataResult<Series>> GetMetadata(SeriesInfo info, CancellationToken cancellationToken) @@ -153,7 +156,7 @@ namespace MediaBrowser.Providers.TV.TheMovieDb if (string.IsNullOrEmpty(tmdbId)) { result.QueriedById = false; - var searchResults = await new MovieDbSearch(_logger, _jsonSerializer, _libraryManager).GetSearchResults(info, cancellationToken).ConfigureAwait(false); + var searchResults = await new TmdbSearch(_logger, _jsonSerializer, _libraryManager).GetSearchResults(info, cancellationToken).ConfigureAwait(false); var searchResult = searchResults.FirstOrDefault(); @@ -177,14 +180,14 @@ namespace MediaBrowser.Providers.TV.TheMovieDb private async Task<MetadataResult<Series>> FetchMovieData(string tmdbId, string language, string preferredCountryCode, CancellationToken cancellationToken) { - RootObject seriesInfo = await FetchMainResult(tmdbId, language, cancellationToken).ConfigureAwait(false); + SeriesResult seriesInfo = await FetchMainResult(tmdbId, language, cancellationToken).ConfigureAwait(false); if (seriesInfo == null) { return null; } - tmdbId = seriesInfo.id.ToString(_usCulture); + tmdbId = seriesInfo.Id.ToString(_usCulture); string dataFilePath = GetDataFilePath(tmdbId, language); Directory.CreateDirectory(Path.GetDirectoryName(dataFilePath)); @@ -196,102 +199,102 @@ namespace MediaBrowser.Providers.TV.TheMovieDb result.Item = new Series(); result.ResultLanguage = seriesInfo.ResultLanguage; - var settings = await MovieDbProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false); + var settings = await TmdbMovieProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false); ProcessMainInfo(result, seriesInfo, preferredCountryCode, settings); return result; } - private void ProcessMainInfo(MetadataResult<Series> seriesResult, RootObject seriesInfo, string preferredCountryCode, TmdbSettingsResult settings) + private void ProcessMainInfo(MetadataResult<Series> seriesResult, SeriesResult seriesInfo, string preferredCountryCode, TmdbSettingsResult settings) { var series = seriesResult.Item; - series.Name = seriesInfo.name; - series.SetProviderId(MetadataProviders.Tmdb, seriesInfo.id.ToString(_usCulture)); + series.Name = seriesInfo.Name; + series.SetProviderId(MetadataProviders.Tmdb, seriesInfo.Id.ToString(_usCulture)); //series.VoteCount = seriesInfo.vote_count; - string voteAvg = seriesInfo.vote_average.ToString(CultureInfo.InvariantCulture); + string voteAvg = seriesInfo.Vote_Average.ToString(CultureInfo.InvariantCulture); if (float.TryParse(voteAvg, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out float rating)) { series.CommunityRating = rating; } - series.Overview = seriesInfo.overview; + series.Overview = seriesInfo.Overview; - if (seriesInfo.networks != null) + if (seriesInfo.Networks != null) { - series.Studios = seriesInfo.networks.Select(i => i.name).ToArray(); + series.Studios = seriesInfo.Networks.Select(i => i.Name).ToArray(); } - if (seriesInfo.genres != null) + if (seriesInfo.Genres != null) { - series.Genres = seriesInfo.genres.Select(i => i.name).ToArray(); + series.Genres = seriesInfo.Genres.Select(i => i.Name).ToArray(); } //series.HomePageUrl = seriesInfo.homepage; - series.RunTimeTicks = seriesInfo.episode_run_time.Select(i => TimeSpan.FromMinutes(i).Ticks).FirstOrDefault(); + series.RunTimeTicks = seriesInfo.Episode_Run_Time.Select(i => TimeSpan.FromMinutes(i).Ticks).FirstOrDefault(); - if (string.Equals(seriesInfo.status, "Ended", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(seriesInfo.Status, "Ended", StringComparison.OrdinalIgnoreCase)) { series.Status = SeriesStatus.Ended; - series.EndDate = seriesInfo.last_air_date; + series.EndDate = seriesInfo.Last_Air_Date; } else { series.Status = SeriesStatus.Continuing; } - series.PremiereDate = seriesInfo.first_air_date; + series.PremiereDate = seriesInfo.First_Air_Date; - var ids = seriesInfo.external_ids; + var ids = seriesInfo.External_Ids; if (ids != null) { - if (!string.IsNullOrWhiteSpace(ids.imdb_id)) + if (!string.IsNullOrWhiteSpace(ids.Imdb_Id)) { - series.SetProviderId(MetadataProviders.Imdb, ids.imdb_id); + series.SetProviderId(MetadataProviders.Imdb, ids.Imdb_Id); } - if (ids.tvrage_id > 0) + if (ids.Tvrage_Id > 0) { - series.SetProviderId(MetadataProviders.TvRage, ids.tvrage_id.ToString(_usCulture)); + series.SetProviderId(MetadataProviders.TvRage, ids.Tvrage_Id.ToString(_usCulture)); } - if (ids.tvdb_id > 0) + if (ids.Tvdb_Id > 0) { - series.SetProviderId(MetadataProviders.Tvdb, ids.tvdb_id.ToString(_usCulture)); + series.SetProviderId(MetadataProviders.Tvdb, ids.Tvdb_Id.ToString(_usCulture)); } } - var contentRatings = (seriesInfo.content_ratings ?? new ContentRatings()).results ?? new List<ContentRating>(); + var contentRatings = (seriesInfo.Content_Ratings ?? new ContentRatings()).Results ?? new List<ContentRating>(); - var ourRelease = contentRatings.FirstOrDefault(c => string.Equals(c.iso_3166_1, preferredCountryCode, StringComparison.OrdinalIgnoreCase)); - var usRelease = contentRatings.FirstOrDefault(c => string.Equals(c.iso_3166_1, "US", StringComparison.OrdinalIgnoreCase)); + var ourRelease = contentRatings.FirstOrDefault(c => string.Equals(c.Iso_3166_1, preferredCountryCode, StringComparison.OrdinalIgnoreCase)); + var usRelease = contentRatings.FirstOrDefault(c => string.Equals(c.Iso_3166_1, "US", StringComparison.OrdinalIgnoreCase)); var minimumRelease = contentRatings.FirstOrDefault(); if (ourRelease != null) { - series.OfficialRating = ourRelease.rating; + series.OfficialRating = ourRelease.Rating; } else if (usRelease != null) { - series.OfficialRating = usRelease.rating; + series.OfficialRating = usRelease.Rating; } else if (minimumRelease != null) { - series.OfficialRating = minimumRelease.rating; + series.OfficialRating = minimumRelease.Rating; } - if (seriesInfo.videos != null && seriesInfo.videos.results != null) + if (seriesInfo.Videos != null && seriesInfo.Videos.Results != null) { - foreach (var video in seriesInfo.videos.results) + foreach (var video in seriesInfo.Videos.Results) { - if ((video.type.Equals("trailer", StringComparison.OrdinalIgnoreCase) - || video.type.Equals("clip", StringComparison.OrdinalIgnoreCase)) - && video.site.Equals("youtube", StringComparison.OrdinalIgnoreCase)) + if ((video.Type.Equals("trailer", StringComparison.OrdinalIgnoreCase) + || video.Type.Equals("clip", StringComparison.OrdinalIgnoreCase)) + && video.Site.Equals("youtube", StringComparison.OrdinalIgnoreCase)) { - series.AddTrailerUrl($"http://www.youtube.com/watch?v={video.key}"); + series.AddTrailerUrl($"http://www.youtube.com/watch?v={video.Key}"); } } } @@ -299,26 +302,26 @@ namespace MediaBrowser.Providers.TV.TheMovieDb seriesResult.ResetPeople(); var tmdbImageUrl = settings.images.GetImageUrl("original"); - if (seriesInfo.credits != null && seriesInfo.credits.cast != null) + if (seriesInfo.Credits != null && seriesInfo.Credits.Cast != null) { - foreach (var actor in seriesInfo.credits.cast.OrderBy(a => a.order)) + foreach (var actor in seriesInfo.Credits.Cast.OrderBy(a => a.Order)) { var personInfo = new PersonInfo { - Name = actor.name.Trim(), - Role = actor.character, + Name = actor.Name.Trim(), + Role = actor.Character, Type = PersonType.Actor, - SortOrder = actor.order + SortOrder = actor.Order }; - if (!string.IsNullOrWhiteSpace(actor.profile_path)) + if (!string.IsNullOrWhiteSpace(actor.Profile_Path)) { - personInfo.ImageUrl = tmdbImageUrl + actor.profile_path; + personInfo.ImageUrl = tmdbImageUrl + actor.Profile_Path; } - if (actor.id > 0) + if (actor.Id > 0) { - personInfo.SetProviderId(MetadataProviders.Tmdb, actor.id.ToString(CultureInfo.InvariantCulture)); + personInfo.SetProviderId(MetadataProviders.Tmdb, actor.Id.ToString(CultureInfo.InvariantCulture)); } seriesResult.AddPerson(personInfo); @@ -342,7 +345,7 @@ namespace MediaBrowser.Providers.TV.TheMovieDb internal async Task DownloadSeriesInfo(string id, string preferredMetadataLanguage, CancellationToken cancellationToken) { - RootObject mainResult = await FetchMainResult(id, preferredMetadataLanguage, cancellationToken).ConfigureAwait(false); + SeriesResult mainResult = await FetchMainResult(id, preferredMetadataLanguage, cancellationToken).ConfigureAwait(false); if (mainResult == null) { @@ -356,31 +359,31 @@ namespace MediaBrowser.Providers.TV.TheMovieDb _jsonSerializer.SerializeToFile(mainResult, dataFilePath); } - internal async Task<RootObject> FetchMainResult(string id, string language, CancellationToken cancellationToken) + internal async Task<SeriesResult> FetchMainResult(string id, string language, CancellationToken cancellationToken) { - var url = string.Format(GetTvInfo3, id, MovieDbProvider.ApiKey); + var url = string.Format(GetTvInfo3, id, TmdbUtils.ApiKey); if (!string.IsNullOrEmpty(language)) { - url += "&language=" + MovieDbProvider.NormalizeLanguage(language) - + "&include_image_language=" + MovieDbProvider.GetImageLanguagesParam(language); // Get images in english and with no language + url += "&language=" + TmdbMovieProvider.NormalizeLanguage(language) + + "&include_image_language=" + TmdbMovieProvider.GetImageLanguagesParam(language); // Get images in english and with no language } cancellationToken.ThrowIfCancellationRequested(); - RootObject mainResult; + SeriesResult mainResult; - using (var response = await MovieDbProvider.Current.GetMovieDbResponse(new HttpRequestOptions + using (var response = await TmdbMovieProvider.Current.GetMovieDbResponse(new HttpRequestOptions { Url = url, CancellationToken = cancellationToken, - AcceptHeader = MovieDbProvider.AcceptHeader + AcceptHeader = TmdbUtils.AcceptHeader }).ConfigureAwait(false)) { using (var json = response.Content) { - mainResult = await _jsonSerializer.DeserializeFromStreamAsync<RootObject>(json).ConfigureAwait(false); + mainResult = await _jsonSerializer.DeserializeFromStreamAsync<SeriesResult>(json).ConfigureAwait(false); if (!string.IsNullOrEmpty(language)) { @@ -393,33 +396,33 @@ namespace MediaBrowser.Providers.TV.TheMovieDb // If the language preference isn't english, then have the overview fallback to english if it's blank if (mainResult != null && - string.IsNullOrEmpty(mainResult.overview) && + string.IsNullOrEmpty(mainResult.Overview) && !string.IsNullOrEmpty(language) && !string.Equals(language, "en", StringComparison.OrdinalIgnoreCase)) { _logger.LogInformation("MovieDbSeriesProvider couldn't find meta for language {Language}. Trying English...", language); - url = string.Format(GetTvInfo3, id, MovieDbProvider.ApiKey) + "&language=en"; + url = string.Format(GetTvInfo3, id, TmdbUtils.ApiKey) + "&language=en"; if (!string.IsNullOrEmpty(language)) { // Get images in english and with no language - url += "&include_image_language=" + MovieDbProvider.GetImageLanguagesParam(language); + url += "&include_image_language=" + TmdbMovieProvider.GetImageLanguagesParam(language); } - using (var response = await MovieDbProvider.Current.GetMovieDbResponse(new HttpRequestOptions + using (var response = await TmdbMovieProvider.Current.GetMovieDbResponse(new HttpRequestOptions { Url = url, CancellationToken = cancellationToken, - AcceptHeader = MovieDbProvider.AcceptHeader + AcceptHeader = TmdbUtils.AcceptHeader }).ConfigureAwait(false)) { using (var json = response.Content) { - var englishResult = await _jsonSerializer.DeserializeFromStreamAsync<RootObject>(json).ConfigureAwait(false); + var englishResult = await _jsonSerializer.DeserializeFromStreamAsync<SeriesResult>(json).ConfigureAwait(false); - mainResult.overview = englishResult.overview; + mainResult.Overview = englishResult.Overview; mainResult.ResultLanguage = "en"; } } @@ -467,40 +470,40 @@ namespace MediaBrowser.Providers.TV.TheMovieDb private async Task<RemoteSearchResult> FindByExternalId(string id, string externalSource, CancellationToken cancellationToken) { - var url = string.Format(MovieDbProvider.BaseMovieDbUrl + @"3/find/{0}?api_key={1}&external_source={2}", + var url = string.Format(TmdbUtils.BaseTmdbApiUrl + @"3/find/{0}?api_key={1}&external_source={2}", id, - MovieDbProvider.ApiKey, + TmdbUtils.ApiKey, externalSource); - using (var response = await MovieDbProvider.Current.GetMovieDbResponse(new HttpRequestOptions + using (var response = await TmdbMovieProvider.Current.GetMovieDbResponse(new HttpRequestOptions { Url = url, CancellationToken = cancellationToken, - AcceptHeader = MovieDbProvider.AcceptHeader + AcceptHeader = TmdbUtils.AcceptHeader }).ConfigureAwait(false)) { using (var json = response.Content) { - var result = await _jsonSerializer.DeserializeFromStreamAsync<MovieDbSearch.ExternalIdLookupResult>(json).ConfigureAwait(false); + var result = await _jsonSerializer.DeserializeFromStreamAsync<ExternalIdLookupResult>(json).ConfigureAwait(false); - if (result != null && result.tv_results != null) + if (result != null && result.Tv_Results != null) { - var tv = result.tv_results.FirstOrDefault(); + var tv = result.Tv_Results.FirstOrDefault(); if (tv != null) { - var tmdbSettings = await MovieDbProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false); + var tmdbSettings = await TmdbMovieProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false); var tmdbImageUrl = tmdbSettings.images.GetImageUrl("original"); var remoteResult = new RemoteSearchResult { - Name = tv.name, + Name = tv.Name, SearchProviderName = Name, - ImageUrl = string.IsNullOrWhiteSpace(tv.poster_path) ? null : tmdbImageUrl + tv.poster_path + ImageUrl = string.IsNullOrWhiteSpace(tv.Poster_Path) ? null : tmdbImageUrl + tv.Poster_Path }; - remoteResult.SetProviderId(MetadataProviders.Tmdb, tv.id.ToString(_usCulture)); + remoteResult.SetProviderId(MetadataProviders.Tmdb, tv.Id.ToString(_usCulture)); return remoteResult; } @@ -511,163 +514,6 @@ namespace MediaBrowser.Providers.TV.TheMovieDb return null; } - public class CreatedBy - { - public int id { get; set; } - public string name { get; set; } - public string profile_path { get; set; } - } - - public class Genre - { - public int id { get; set; } - public string name { get; set; } - } - - public class Network - { - public int id { get; set; } - public string name { get; set; } - } - - public class Season - { - public string air_date { get; set; } - public int episode_count { get; set; } - public int id { get; set; } - public string poster_path { get; set; } - public int season_number { get; set; } - } - - public class Cast - { - public string character { get; set; } - public string credit_id { get; set; } - public int id { get; set; } - public string name { get; set; } - public string profile_path { get; set; } - public int order { get; set; } - } - - public class Crew - { - public string credit_id { get; set; } - public string department { get; set; } - public int id { get; set; } - public string name { get; set; } - public string job { get; set; } - public string profile_path { get; set; } - } - - public class Credits - { - public List<Cast> cast { get; set; } - public List<Crew> crew { get; set; } - } - - public class Backdrop - { - public double aspect_ratio { get; set; } - public string file_path { get; set; } - public int height { get; set; } - public string iso_639_1 { get; set; } - public double vote_average { get; set; } - public int vote_count { get; set; } - public int width { get; set; } - } - - public class Poster - { - public double aspect_ratio { get; set; } - public string file_path { get; set; } - public int height { get; set; } - public string iso_639_1 { get; set; } - public double vote_average { get; set; } - public int vote_count { get; set; } - public int width { get; set; } - } - - public class Images - { - public List<Backdrop> backdrops { get; set; } - public List<Poster> posters { get; set; } - } - - public class Keywords - { - public List<object> results { get; set; } - } - - public class ExternalIds - { - public string imdb_id { get; set; } - public string freebase_id { get; set; } - public string freebase_mid { get; set; } - public int tvdb_id { get; set; } - public int tvrage_id { get; set; } - } - - public class Videos - { - public List<Video> results { get; set; } - } - - public class Video - { - public string id { get; set; } - public string iso_639_1 { get; set; } - public string iso_3166_1 { get; set; } - public string key { get; set; } - public string name { get; set; } - public string site { get; set; } - public string size { get; set; } - public string type { get; set; } - } - - public class ContentRating - { - public string iso_3166_1 { get; set; } - public string rating { get; set; } - } - - public class ContentRatings - { - public List<ContentRating> results { get; set; } - } - - public class RootObject - { - public string backdrop_path { get; set; } - public List<CreatedBy> created_by { get; set; } - public List<int> episode_run_time { get; set; } - public DateTime first_air_date { get; set; } - public List<Genre> genres { get; set; } - public string homepage { get; set; } - public int id { get; set; } - public bool in_production { get; set; } - public List<string> languages { get; set; } - public DateTime last_air_date { get; set; } - public string name { get; set; } - public List<Network> networks { get; set; } - public int number_of_episodes { get; set; } - public int number_of_seasons { get; set; } - public string original_name { get; set; } - public List<string> origin_country { get; set; } - public string overview { get; set; } - public string popularity { get; set; } - public string poster_path { get; set; } - public List<Season> seasons { get; set; } - public string status { get; set; } - public double vote_average { get; set; } - public int vote_count { get; set; } - public Credits credits { get; set; } - public Images images { get; set; } - public Keywords keywords { get; set; } - public ExternalIds external_ids { get; set; } - public Videos videos { get; set; } - public ContentRatings content_ratings { get; set; } - public string ResultLanguage { get; set; } - } // After TheTVDB public int Order => 1; diff --git a/MediaBrowser.Providers/Tmdb/TmdbUtils.cs b/MediaBrowser.Providers/Tmdb/TmdbUtils.cs new file mode 100644 index 000000000..035b99c1a --- /dev/null +++ b/MediaBrowser.Providers/Tmdb/TmdbUtils.cs @@ -0,0 +1,31 @@ +using System; +using MediaBrowser.Model.Entities; +using MediaBrowser.Providers.Tmdb.Models.General; + +namespace MediaBrowser.Providers.Tmdb +{ + public static class TmdbUtils + { + public const string BaseTmdbUrl = "https://www.themoviedb.org/"; + public const string BaseTmdbApiUrl = "https://api.themoviedb.org/"; + public const string ProviderName = "TheMovieDb"; + public const string ApiKey = "4219e299c89411838049ab0dab19ebd5"; + public const string AcceptHeader = "application/json,image/*"; + + public static string MapCrewToPersonType(Crew crew) + { + if (crew.Department.Equals("production", StringComparison.InvariantCultureIgnoreCase) + && crew.Job.IndexOf("producer", StringComparison.InvariantCultureIgnoreCase) != -1) + { + return PersonType.Producer; + } + + if (crew.Department.Equals("writing", StringComparison.InvariantCultureIgnoreCase)) + { + return PersonType.Writer; + } + + return null; + } + } +} diff --git a/MediaBrowser.Providers/Movies/MovieDbTrailerProvider.cs b/MediaBrowser.Providers/Tmdb/Trailers/TmdbTrailerProvider.cs index 2a3cdf097..b0dec0245 100644 --- a/MediaBrowser.Providers/Movies/MovieDbTrailerProvider.cs +++ b/MediaBrowser.Providers/Tmdb/Trailers/TmdbTrailerProvider.cs @@ -5,29 +5,31 @@ using MediaBrowser.Common.Net; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Providers; +using MediaBrowser.Providers.Movies; +using MediaBrowser.Providers.Tmdb.Movies; -namespace MediaBrowser.Providers.Movies +namespace MediaBrowser.Providers.Tmdb.Trailers { - public class MovieDbTrailerProvider : IHasOrder, IRemoteMetadataProvider<Trailer, TrailerInfo> + public class TmdbTrailerProvider : IHasOrder, IRemoteMetadataProvider<Trailer, TrailerInfo> { private readonly IHttpClient _httpClient; - public MovieDbTrailerProvider(IHttpClient httpClient) + public TmdbTrailerProvider(IHttpClient httpClient) { _httpClient = httpClient; } public Task<IEnumerable<RemoteSearchResult>> GetSearchResults(TrailerInfo searchInfo, CancellationToken cancellationToken) { - return MovieDbProvider.Current.GetMovieSearchResults(searchInfo, cancellationToken); + return TmdbMovieProvider.Current.GetMovieSearchResults(searchInfo, cancellationToken); } public Task<MetadataResult<Trailer>> GetMetadata(TrailerInfo info, CancellationToken cancellationToken) { - return MovieDbProvider.Current.GetItemMetadata<Trailer>(info, cancellationToken); + return TmdbMovieProvider.Current.GetItemMetadata<Trailer>(info, cancellationToken); } - public string Name => MovieDbProvider.Current.Name; + public string Name => TmdbMovieProvider.Current.Name; public int Order => 0; diff --git a/MediaBrowser.WebDashboard/jellyfin-web b/MediaBrowser.WebDashboard/jellyfin-web -Subproject 1d0fd79eb1e4d0bf6a9f62f769a951970383bcf +Subproject 867a5e664cb968602b50dee4874fcb961eed480 diff --git a/MediaBrowser.XbmcMetadata/EntryPoint.cs b/MediaBrowser.XbmcMetadata/EntryPoint.cs index 992991a7e..fe4d50efa 100644 --- a/MediaBrowser.XbmcMetadata/EntryPoint.cs +++ b/MediaBrowser.XbmcMetadata/EntryPoint.cs @@ -16,27 +16,30 @@ namespace MediaBrowser.XbmcMetadata { private readonly IUserDataManager _userDataManager; private readonly ILogger _logger; - private readonly ILibraryManager _libraryManager; private readonly IProviderManager _providerManager; private readonly IConfigurationManager _config; - public EntryPoint(IUserDataManager userDataManager, ILibraryManager libraryManager, ILogger logger, IProviderManager providerManager, IConfigurationManager config) + public EntryPoint( + IUserDataManager userDataManager, + ILogger logger, + IProviderManager providerManager, + IConfigurationManager config) { _userDataManager = userDataManager; - _libraryManager = libraryManager; _logger = logger; _providerManager = providerManager; _config = config; } + /// <inheritdoc /> public Task RunAsync() { - _userDataManager.UserDataSaved += _userDataManager_UserDataSaved; + _userDataManager.UserDataSaved += OnUserDataSaved; return Task.CompletedTask; } - void _userDataManager_UserDataSaved(object sender, UserDataSaveEventArgs e) + private void OnUserDataSaved(object sender, UserDataSaveEventArgs e) { if (e.SaveReason == UserDataSaveReason.PlaybackFinished || e.SaveReason == UserDataSaveReason.TogglePlayed || e.SaveReason == UserDataSaveReason.UpdateUserRating) { @@ -47,9 +50,10 @@ namespace MediaBrowser.XbmcMetadata } } + /// <inheritdoc /> public void Dispose() { - _userDataManager.UserDataSaved -= _userDataManager_UserDataSaved; + _userDataManager.UserDataSaved -= OnUserDataSaved; } private void SaveMetadataForItem(BaseItem item, ItemUpdateType updateReason) diff --git a/MediaBrowser.XbmcMetadata/Savers/AlbumNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/AlbumNfoSaver.cs index 430b93199..a1905bf26 100644 --- a/MediaBrowser.XbmcMetadata/Savers/AlbumNfoSaver.cs +++ b/MediaBrowser.XbmcMetadata/Savers/AlbumNfoSaver.cs @@ -15,16 +15,30 @@ namespace MediaBrowser.XbmcMetadata.Savers { public class AlbumNfoSaver : BaseNfoSaver { + public AlbumNfoSaver( + IFileSystem fileSystem, + IServerConfigurationManager configurationManager, + ILibraryManager libraryManager, + IUserManager userManager, + IUserDataManager userDataManager, + ILogger logger) + : base(fileSystem, configurationManager, libraryManager, userManager, userDataManager, logger) + { + } + + /// <inheritdoc /> protected override string GetLocalSavePath(BaseItem item) { return Path.Combine(item.Path, "album.nfo"); } + /// <inheritdoc /> protected override string GetRootElementName(BaseItem item) { return "album"; } + /// <inheritdoc /> public override bool IsEnabledFor(BaseItem item, ItemUpdateType updateType) { if (!item.SupportsLocalMetadata) @@ -35,6 +49,7 @@ namespace MediaBrowser.XbmcMetadata.Savers return item is MusicAlbum && updateType >= MinimumUpdateType; } + /// <inheritdoc /> protected override void WriteCustomElements(BaseItem item, XmlWriter writer) { var album = (MusicAlbum)item; @@ -52,8 +67,6 @@ namespace MediaBrowser.XbmcMetadata.Savers AddTracks(album.Tracks, writer); } - private readonly CultureInfo UsCulture = new CultureInfo("en-US"); - private void AddTracks(IEnumerable<BaseItem> tracks, XmlWriter writer) { foreach (var track in tracks.OrderBy(i => i.ParentIndexNumber ?? 0).ThenBy(i => i.IndexNumber ?? 0)) @@ -62,7 +75,7 @@ namespace MediaBrowser.XbmcMetadata.Savers if (track.IndexNumber.HasValue) { - writer.WriteElementString("position", track.IndexNumber.Value.ToString(UsCulture)); + writer.WriteElementString("position", track.IndexNumber.Value.ToString(CultureInfo.InvariantCulture)); } if (!string.IsNullOrEmpty(track.Name)) @@ -81,21 +94,19 @@ namespace MediaBrowser.XbmcMetadata.Savers } } + /// <inheritdoc /> protected override List<string> GetTagsUsed(BaseItem item) { var list = base.GetTagsUsed(item); - list.AddRange(new string[] - { - "track", - "artist", - "albumartist" - }); - return list; - } + list.AddRange( + new string[] + { + "track", + "artist", + "albumartist" + }); - public AlbumNfoSaver(IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataManager, ILogger logger) - : base(fileSystem, configurationManager, libraryManager, userManager, userDataManager, logger) - { + return list; } } } diff --git a/MediaBrowser.sln b/MediaBrowser.sln index 3ed86d65c..39839e273 100644 --- a/MediaBrowser.sln +++ b/MediaBrowser.sln @@ -1,4 +1,4 @@ - + Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.26730.3 @@ -41,8 +41,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Emby.Naming", "Emby.Naming\ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Emby.XmlTv", "Emby.XmlTv\Emby.XmlTv\Emby.XmlTv.csproj", "{6EAFA7F0-8A82-49E6-B2FA-086C5CAEA95B}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IsoMounter", "Emby.IsoMounting\IsoMounter\IsoMounter.csproj", "{9BA471D2-6DB9-4DBF-B3A0-9FB3171F94A6}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MediaBrowser.MediaEncoding", "MediaBrowser.MediaEncoding\MediaBrowser.MediaEncoding.csproj", "{960295EE-4AF4-4440-A525-B4C295B01A61}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Server", "Jellyfin.Server\Jellyfin.Server.csproj", "{07E39F42-A2C6-4B32-AF8C-725F957A73FF}" @@ -141,10 +139,6 @@ Global {6EAFA7F0-8A82-49E6-B2FA-086C5CAEA95B}.Debug|Any CPU.Build.0 = Debug|Any CPU {6EAFA7F0-8A82-49E6-B2FA-086C5CAEA95B}.Release|Any CPU.ActiveCfg = Release|Any CPU {6EAFA7F0-8A82-49E6-B2FA-086C5CAEA95B}.Release|Any CPU.Build.0 = Release|Any CPU - {9BA471D2-6DB9-4DBF-B3A0-9FB3171F94A6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {9BA471D2-6DB9-4DBF-B3A0-9FB3171F94A6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {9BA471D2-6DB9-4DBF-B3A0-9FB3171F94A6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {9BA471D2-6DB9-4DBF-B3A0-9FB3171F94A6}.Release|Any CPU.Build.0 = Release|Any CPU {960295EE-4AF4-4440-A525-B4C295B01A61}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {960295EE-4AF4-4440-A525-B4C295B01A61}.Debug|Any CPU.Build.0 = Debug|Any CPU {960295EE-4AF4-4440-A525-B4C295B01A61}.Release|Any CPU.ActiveCfg = Release|Any CPU diff --git a/SharedVersion.cs b/SharedVersion.cs index 500e6fe55..5f3fd8e50 100644 --- a/SharedVersion.cs +++ b/SharedVersion.cs @@ -1,4 +1,4 @@ using System.Reflection; -[assembly: AssemblyVersion("10.3.7")] -[assembly: AssemblyFileVersion("10.3.7")] +[assembly: AssemblyVersion("10.4.0")] +[assembly: AssemblyFileVersion("10.4.0")] diff --git a/build.yaml b/build.yaml index 2d4bc29f4..3cfccd337 100644 --- a/build.yaml +++ b/build.yaml @@ -1,7 +1,7 @@ --- # We just wrap `build` so this is really it name: "jellyfin" -version: "10.3.7" +version: "10.4.0" packages: - debian-package-x64 - debian-package-armhf diff --git a/bump_version b/bump_version index 398caee15..590020864 100755 --- a/bump_version +++ b/bump_version @@ -24,7 +24,11 @@ fi shared_version_file="./SharedVersion.cs" build_file="./build.yaml" -web_branch="$( git branch 2>/dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/\1/' )" +if [[ -z $2 ]]; then + web_branch="$( git branch 2>/dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/\1/' )" +else + web_branch="$2" +fi # Initialize submodules git submodule update --init --recursive @@ -80,7 +84,7 @@ fi # Set the Dockerfile web version to the specified new_version old_version="$( grep "JELLYFIN_WEB_VERSION=" Dockerfile \ - | sed -E 's/ARG JELLYFIN_WEB_VERSION=([0-9\.]+[-a-z0-9]*)/\1/' + | sed -E 's/ARG JELLYFIN_WEB_VERSION=v([0-9\.]+[-a-z0-9]*)/\1/' )" echo $old_version diff --git a/deployment/debian-package-x64/pkg-src/changelog b/deployment/debian-package-x64/pkg-src/changelog index aa15827a7..54d4cffba 100644 --- a/deployment/debian-package-x64/pkg-src/changelog +++ b/deployment/debian-package-x64/pkg-src/changelog @@ -1,3 +1,9 @@ +jellyfin (10.4.0-1) unstable; urgency=medium + + * New upstream version 10.4.0; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.4.0 + + -- Jellyfin Packaging Team <packaging@jellyfin.org> Sat, 31 Aug 2019 21:38:56 -0400 + jellyfin (10.3.7-1) unstable; urgency=medium * New upstream version 10.3.7; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.7 diff --git a/deployment/fedora-package-x64/pkg-src/jellyfin.spec b/deployment/fedora-package-x64/pkg-src/jellyfin.spec index 91b74ffe1..d591a54e1 100644 --- a/deployment/fedora-package-x64/pkg-src/jellyfin.spec +++ b/deployment/fedora-package-x64/pkg-src/jellyfin.spec @@ -7,7 +7,7 @@ %endif Name: jellyfin -Version: 10.3.7 +Version: 10.4.0 Release: 1%{?dist} Summary: The Free Software Media Browser License: GPLv2 @@ -68,7 +68,7 @@ EOF %{__install} -D -m 0644 %{SOURCE2} %{buildroot}%{_sysconfdir}/sysconfig/%{name} %{__install} -D -m 0600 %{SOURCE3} %{buildroot}%{_sysconfdir}/sudoers.d/%{name}-sudoers %{__install} -D -m 0755 %{SOURCE4} %{buildroot}%{_libexecdir}/%{name}/restart.sh -%{__install} -D -m 0644 %{SOURCE6} %{buildroot}%{_prefix}/lib/firewalld/service/%{name}.xml +%{__install} -D -m 0644 %{SOURCE6} %{buildroot}%{_prefix}/lib/firewalld/services/%{name}.xml %files %{_libdir}/%{name}/jellyfin-web/* @@ -83,7 +83,7 @@ EOF %{_libdir}/%{name}/sosdocsunix.txt %{_unitdir}/%{name}.service %{_libexecdir}/%{name}/restart.sh -%{_prefix}/lib/firewalld/service/%{name}.xml +%{_prefix}/lib/firewalld/services/%{name}.xml %attr(755,jellyfin,jellyfin) %dir %{_sysconfdir}/%{name} %config %{_sysconfdir}/sysconfig/%{name} %config(noreplace) %attr(600,root,root) %{_sysconfdir}/sudoers.d/%{name}-sudoers @@ -140,6 +140,8 @@ fi %systemd_postun_with_restart jellyfin.service %changelog +* Sat Aug 31 2019 Jellyfin Packaging Team <packaging@jellyfin.org> +- New upstream version 10.4.0; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.4.0 * Wed Jul 24 2019 Jellyfin Packaging Team <packaging@jellyfin.org> - New upstream version 10.3.7; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.7 * Sat Jul 06 2019 Jellyfin Packaging Team <packaging@jellyfin.org> |
