diff options
89 files changed, 4349 insertions, 306 deletions
diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 3cb7b914a..bdd1b76d0 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -1447,6 +1447,16 @@ namespace MediaBrowser.Api.Playback state.MediaPath = mediaUrl; state.InputProtocol = MediaProtocol.Http; } + else + { + // No media info, so this is probably needed + state.DeInterlace = true; + } + + if (recording.RecordingInfo.Status == RecordingStatus.InProgress) + { + state.ReadInputAtNativeFramerate = true; + } state.RunTimeTicks = recording.RunTimeTicks; @@ -1455,9 +1465,7 @@ namespace MediaBrowser.Api.Playback await Task.Delay(1000, cancellationToken).ConfigureAwait(false); } - state.ReadInputAtNativeFramerate = recording.RecordingInfo.Status == RecordingStatus.InProgress; state.OutputAudioSync = "1000"; - state.DeInterlace = true; state.InputVideoSync = "-1"; state.InputAudioSync = "1"; state.InputContainer = recording.Container; @@ -1524,7 +1532,9 @@ namespace MediaBrowser.Api.Playback state.RunTimeTicks = mediaSource.RunTimeTicks; } - if (string.Equals(state.InputContainer, "wtv", StringComparison.OrdinalIgnoreCase)) + // If it's a wtv and we don't have media info, we will probably need to deinterlace + if (string.Equals(state.InputContainer, "wtv", StringComparison.OrdinalIgnoreCase) && + mediaStreams.Count == 0) { state.DeInterlace = true; } diff --git a/MediaBrowser.Api/Playback/BifService.cs b/MediaBrowser.Api/Playback/BifService.cs index 7a3a7d32d..057d81441 100644 --- a/MediaBrowser.Api/Playback/BifService.cs +++ b/MediaBrowser.Api/Playback/BifService.cs @@ -72,6 +72,11 @@ namespace MediaBrowser.Api.Playback try { + if (File.Exists(path)) + { + return path; + } + await _mediaEncoder.ExtractVideoImagesOnInterval(inputPath, protocol, mediaSource.Video3DFormat, TimeSpan.FromSeconds(10), Path.GetDirectoryName(path), "img_", request.MaxWidth, CancellationToken.None) .ConfigureAwait(false); diff --git a/MediaBrowser.Api/Playback/Progressive/VideoService.cs b/MediaBrowser.Api/Playback/Progressive/VideoService.cs index 937df513e..bedacc0d2 100644 --- a/MediaBrowser.Api/Playback/Progressive/VideoService.cs +++ b/MediaBrowser.Api/Playback/Progressive/VideoService.cs @@ -144,7 +144,8 @@ namespace MediaBrowser.Api.Playback.Progressive return state.VideoStream != null && IsH264(state.VideoStream) ? args + " -bsf h264_mp4toannexb" : args; } - const string keyFrameArg = " -force_key_frames expr:if(isnan(prev_forced_t),gte(t,.1),gte(t,prev_forced_t+5))"; + var keyFrameArg = string.Format(" -force_key_frames expr:gte(t,n_forced*{0})", + 5.ToString(UsCulture)); args += keyFrameArg; diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index 87b1cc7a3..f503a5ff4 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -445,11 +445,6 @@ namespace MediaBrowser.Controller.Entities cancellationToken.ThrowIfCancellationRequested(); - if (this is UserRootFolder) - { - var b = true; - } - foreach (var child in nonCachedChildren) { BaseItem currentChild; diff --git a/MediaBrowser.Dlna/PlayTo/PlayToController.cs b/MediaBrowser.Dlna/PlayTo/PlayToController.cs index 89d61bb21..0bc921508 100644 --- a/MediaBrowser.Dlna/PlayTo/PlayToController.cs +++ b/MediaBrowser.Dlna/PlayTo/PlayToController.cs @@ -80,7 +80,7 @@ namespace MediaBrowser.Dlna.PlayTo _updateTimer = new Timer(updateTimer_Elapsed, null, 60000, 60000); } - private async void updateTimer_Elapsed(object state) + private void updateTimer_Elapsed(object state) { if (DateTime.UtcNow >= _device.DateLastActivity.AddSeconds(120)) { diff --git a/MediaBrowser.Providers/BaseXmlProvider.cs b/MediaBrowser.LocalMetadata/BaseXmlProvider.cs index a5a3ba146..cc9bc7bed 100644 --- a/MediaBrowser.Providers/BaseXmlProvider.cs +++ b/MediaBrowser.LocalMetadata/BaseXmlProvider.cs @@ -1,13 +1,13 @@ -using MediaBrowser.Common.IO; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.Logging; -using System; +using System; using System.IO; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Logging; -namespace MediaBrowser.Providers +namespace MediaBrowser.LocalMetadata { public abstract class BaseXmlProvider<T> : ILocalMetadataProvider<T>, IHasChangeMonitor where T : IHasMetadata, new() diff --git a/MediaBrowser.Providers/Folders/CollectionFolderImageProvider.cs b/MediaBrowser.LocalMetadata/Images/CollectionFolderImageProvider.cs index fa5946bd5..29fd76aa5 100644 --- a/MediaBrowser.Providers/Folders/CollectionFolderImageProvider.cs +++ b/MediaBrowser.LocalMetadata/Images/CollectionFolderImageProvider.cs @@ -1,10 +1,8 @@ -using MediaBrowser.Controller.Entities; +using System.Collections.Generic; +using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.Entities; -using MediaBrowser.Providers.All; -using System.Collections.Generic; -namespace MediaBrowser.Providers.Folders +namespace MediaBrowser.LocalMetadata.Images { public class CollectionFolderLocalImageProvider : ILocalImageFileProvider, IHasOrder { diff --git a/MediaBrowser.Providers/TV/EpisodeLocalImageProvider.cs b/MediaBrowser.LocalMetadata/Images/EpisodeLocalImageProvider.cs index 1ec0e0f48..f1e7426aa 100644 --- a/MediaBrowser.Providers/TV/EpisodeLocalImageProvider.cs +++ b/MediaBrowser.LocalMetadata/Images/EpisodeLocalImageProvider.cs @@ -1,13 +1,13 @@ -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.TV; -using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.Entities; -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Entities; -namespace MediaBrowser.Providers.TV +namespace MediaBrowser.LocalMetadata.Images { public class EpisodeLocalLocalImageProvider : ILocalImageFileProvider { diff --git a/MediaBrowser.Providers/Folders/ImagesByNameImageProvider.cs b/MediaBrowser.LocalMetadata/Images/ImagesByNameImageProvider.cs index 5dcf3bc59..3f84df462 100644 --- a/MediaBrowser.Providers/Folders/ImagesByNameImageProvider.cs +++ b/MediaBrowser.LocalMetadata/Images/ImagesByNameImageProvider.cs @@ -1,12 +1,11 @@ -using MediaBrowser.Common.IO; +using System.Collections.Generic; +using System.IO; +using MediaBrowser.Common.IO; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Providers; -using MediaBrowser.Providers.All; -using System.Collections.Generic; -using System.IO; -namespace MediaBrowser.Providers.Folders +namespace MediaBrowser.LocalMetadata.Images { public class ImagesByNameImageProvider : ILocalImageFileProvider, IHasOrder { diff --git a/MediaBrowser.Providers/All/InternalMetadataFolderImageProvider.cs b/MediaBrowser.LocalMetadata/Images/InternalMetadataFolderImageProvider.cs index edaa5edaf..8c4f6247c 100644 --- a/MediaBrowser.Providers/All/InternalMetadataFolderImageProvider.cs +++ b/MediaBrowser.LocalMetadata/Images/InternalMetadataFolderImageProvider.cs @@ -1,11 +1,11 @@ -using MediaBrowser.Controller.Configuration; +using System.Collections.Generic; +using System.IO; +using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Providers; -using System.Collections.Generic; -using System.IO; -namespace MediaBrowser.Providers.All +namespace MediaBrowser.LocalMetadata.Images { public class InternalMetadataFolderImageProvider : ILocalImageFileProvider, IHasOrder { diff --git a/MediaBrowser.Providers/All/LocalImageProvider.cs b/MediaBrowser.LocalMetadata/Images/LocalImageProvider.cs index 1d10fcfa3..a5ef7977b 100644 --- a/MediaBrowser.Providers/All/LocalImageProvider.cs +++ b/MediaBrowser.LocalMetadata/Images/LocalImageProvider.cs @@ -1,16 +1,16 @@ -using MediaBrowser.Controller.Entities; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Linq; -namespace MediaBrowser.Providers.All +namespace MediaBrowser.LocalMetadata.Images { public class LocalImageProvider : ILocalImageFileProvider { diff --git a/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj b/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj new file mode 100644 index 000000000..0d2c0b97f --- /dev/null +++ b/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj @@ -0,0 +1,110 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <ProjectGuid>{7EF9F3E0-697D-42F3-A08F-19DEB5F84392}</ProjectGuid> + <OutputType>Library</OutputType> + <AppDesignerFolder>Properties</AppDesignerFolder> + <RootNamespace>MediaBrowser.LocalMetadata</RootNamespace> + <AssemblyName>MediaBrowser.LocalMetadata</AssemblyName> + <TargetFrameworkVersion>v4.5</TargetFrameworkVersion> + <FileAlignment>512</FileAlignment> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + <DebugSymbols>true</DebugSymbols> + <DebugType>full</DebugType> + <Optimize>false</Optimize> + <OutputPath>bin\Debug\</OutputPath> + <DefineConstants>DEBUG;TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <OutputPath>bin\Release\</OutputPath> + <DefineConstants>TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <ItemGroup> + <Reference Include="System" /> + <Reference Include="System.Core" /> + <Reference Include="System.Xml.Linq" /> + <Reference Include="System.Data.DataSetExtensions" /> + <Reference Include="Microsoft.CSharp" /> + <Reference Include="System.Data" /> + <Reference Include="System.Xml" /> + </ItemGroup> + <ItemGroup> + <Compile Include="BaseXmlProvider.cs" /> + <Compile Include="Images\CollectionFolderImageProvider.cs" /> + <Compile Include="Images\EpisodeLocalImageProvider.cs" /> + <Compile Include="Images\ImagesByNameImageProvider.cs" /> + <Compile Include="Images\InternalMetadataFolderImageProvider.cs" /> + <Compile Include="Images\LocalImageProvider.cs" /> + <Compile Include="Parsers\BoxSetXmlParser.cs" /> + <Compile Include="Parsers\EpisodeXmlParser.cs" /> + <Compile Include="Parsers\GameSystemXmlParser.cs" /> + <Compile Include="Parsers\GameXmlParser.cs" /> + <Compile Include="Parsers\MovieXmlParser.cs" /> + <Compile Include="Parsers\MusicVideoXmlParser.cs" /> + <Compile Include="Parsers\SeasonXmlParser.cs" /> + <Compile Include="Parsers\SeriesXmlParser.cs" /> + <Compile Include="Properties\AssemblyInfo.cs" /> + <Compile Include="Providers\AdultVideoXmlProvider.cs" /> + <Compile Include="Providers\AlbumXmlProvider.cs" /> + <Compile Include="Providers\ArtistXmlProvider.cs" /> + <Compile Include="Providers\BoxSetXmlProvider.cs" /> + <Compile Include="Providers\ChannelXmlProvider.cs" /> + <Compile Include="Providers\EpisodeXmlProvider.cs" /> + <Compile Include="Providers\FolderXmlProvider.cs" /> + <Compile Include="Providers\GameSystemXmlProvider.cs" /> + <Compile Include="Providers\GameXmlProvider.cs" /> + <Compile Include="Providers\MovieXmlProvider.cs" /> + <Compile Include="Providers\MusicVideoXmlProvider.cs" /> + <Compile Include="Providers\PersonXmlProvider.cs" /> + <Compile Include="Providers\SeasonXmlProvider.cs" /> + <Compile Include="Providers\SeriesXmlProvider.cs" /> + <Compile Include="Providers\TrailerXmlProvider.cs" /> + <Compile Include="Providers\VideoXmlProvider.cs" /> + <Compile Include="Savers\AlbumXmlSaver.cs" /> + <Compile Include="Savers\ArtistXmlSaver.cs" /> + <Compile Include="Savers\BoxSetXmlSaver.cs" /> + <Compile Include="Savers\ChannelXmlSaver.cs" /> + <Compile Include="Savers\EpisodeXmlSaver.cs" /> + <Compile Include="Savers\FolderXmlSaver.cs" /> + <Compile Include="Savers\GameSystemXmlSaver.cs" /> + <Compile Include="Savers\GameXmlSaver.cs" /> + <Compile Include="Savers\MovieXmlSaver.cs" /> + <Compile Include="Savers\PersonXmlSaver.cs" /> + <Compile Include="Savers\SeasonXmlSaver.cs" /> + <Compile Include="Savers\SeriesXmlSaver.cs" /> + <Compile Include="Savers\XmlSaverHelpers.cs" /> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj"> + <Project>{9142eefa-7570-41e1-bfcc-468bb571af2f}</Project> + <Name>MediaBrowser.Common</Name> + </ProjectReference> + <ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj"> + <Project>{17e1f4e6-8abd-4fe5-9ecf-43d4b6087ba2}</Project> + <Name>MediaBrowser.Controller</Name> + </ProjectReference> + <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj"> + <Project>{7eeeb4bb-f3e8-48fc-b4c5-70f0fff8329b}</Project> + <Name>MediaBrowser.Model</Name> + </ProjectReference> + </ItemGroup> + <ItemGroup /> + <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> + <!-- To modify your build process, add your task inside one of the targets below and uncomment it. + Other similar extension points exist, see Microsoft.Common.targets. + <Target Name="BeforeBuild"> + </Target> + <Target Name="AfterBuild"> + </Target> + --> +</Project>
\ No newline at end of file diff --git a/MediaBrowser.Providers/BoxSets/BoxSetXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/BoxSetXmlParser.cs index eb3c99cef..51a4684d7 100644 --- a/MediaBrowser.Providers/BoxSets/BoxSetXmlParser.cs +++ b/MediaBrowser.LocalMetadata/Parsers/BoxSetXmlParser.cs @@ -1,12 +1,12 @@ -using MediaBrowser.Controller.Entities; +using System.Collections.Generic; +using System.Globalization; +using System.Xml; +using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Logging; -using System.Collections.Generic; -using System.Globalization; -using System.Xml; -namespace MediaBrowser.Providers.BoxSets +namespace MediaBrowser.LocalMetadata.Parsers { public class BoxSetXmlParser : BaseItemXmlParser<BoxSet> { diff --git a/MediaBrowser.Providers/TV/EpisodeXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/EpisodeXmlParser.cs index 20f267885..8430f3b3c 100644 --- a/MediaBrowser.Providers/TV/EpisodeXmlParser.cs +++ b/MediaBrowser.LocalMetadata/Parsers/EpisodeXmlParser.cs @@ -1,15 +1,15 @@ -using MediaBrowser.Controller.Entities.TV; -using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Logging; -using System; +using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Threading; using System.Xml; +using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Logging; -namespace MediaBrowser.Providers.TV +namespace MediaBrowser.LocalMetadata.Parsers { /// <summary> /// Class EpisodeXmlParser diff --git a/MediaBrowser.Providers/Games/GameSystemXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/GameSystemXmlParser.cs index 85ee509ca..d449108c4 100644 --- a/MediaBrowser.Providers/Games/GameSystemXmlParser.cs +++ b/MediaBrowser.LocalMetadata/Parsers/GameSystemXmlParser.cs @@ -1,12 +1,12 @@ -using MediaBrowser.Controller.Entities; +using System.Threading; +using System.Threading.Tasks; +using System.Xml; +using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; -using System.Threading; -using System.Threading.Tasks; -using System.Xml; -namespace MediaBrowser.Providers.Games +namespace MediaBrowser.LocalMetadata.Parsers { public class GameSystemXmlParser : BaseItemXmlParser<GameSystem> { diff --git a/MediaBrowser.Providers/Games/GameXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/GameXmlParser.cs index dfa71e5f4..2caced8a9 100644 --- a/MediaBrowser.Providers/Games/GameXmlParser.cs +++ b/MediaBrowser.LocalMetadata/Parsers/GameXmlParser.cs @@ -1,13 +1,13 @@ -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Logging; -using System.Globalization; +using System.Globalization; using System.Threading; using System.Threading.Tasks; using System.Xml; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Logging; -namespace MediaBrowser.Providers.Games +namespace MediaBrowser.LocalMetadata.Parsers { /// <summary> /// Class EpisodeXmlParser diff --git a/MediaBrowser.Providers/Movies/MovieXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/MovieXmlParser.cs index 52795ac2d..388a0d20d 100644 --- a/MediaBrowser.Providers/Movies/MovieXmlParser.cs +++ b/MediaBrowser.LocalMetadata/Parsers/MovieXmlParser.cs @@ -1,13 +1,13 @@ -using MediaBrowser.Controller.Entities; +using System.Collections.Generic; +using System.Threading; +using System.Xml; +using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; -using System.Collections.Generic; -using System.Threading; -using System.Xml; -namespace MediaBrowser.Providers.Movies +namespace MediaBrowser.LocalMetadata.Parsers { /// <summary> /// Class EpisodeXmlParser diff --git a/MediaBrowser.Providers/Music/MusicVideoXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/MusicVideoXmlParser.cs index 0c160ff66..b88ff6c3a 100644 --- a/MediaBrowser.Providers/Music/MusicVideoXmlParser.cs +++ b/MediaBrowser.LocalMetadata/Parsers/MusicVideoXmlParser.cs @@ -1,10 +1,9 @@ -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Audio; +using System.Xml; +using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Logging; -using System.Xml; -namespace MediaBrowser.Providers.Music +namespace MediaBrowser.LocalMetadata.Parsers { public class MusicVideoXmlParser : BaseItemXmlParser<MusicVideo> { diff --git a/MediaBrowser.Providers/TV/SeasonXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/SeasonXmlParser.cs index 44b73e8e1..62a7d37cf 100644 --- a/MediaBrowser.Providers/TV/SeasonXmlParser.cs +++ b/MediaBrowser.LocalMetadata/Parsers/SeasonXmlParser.cs @@ -1,9 +1,9 @@ -using MediaBrowser.Controller.Entities.TV; +using System.Xml; +using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Logging; -using System.Xml; -namespace MediaBrowser.Providers.TV +namespace MediaBrowser.LocalMetadata.Parsers { public class SeasonXmlParser : BaseItemXmlParser<Season> { diff --git a/MediaBrowser.Providers/TV/SeriesXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/SeriesXmlParser.cs index 237fcf73a..a3d45034e 100644 --- a/MediaBrowser.Providers/TV/SeriesXmlParser.cs +++ b/MediaBrowser.LocalMetadata/Parsers/SeriesXmlParser.cs @@ -1,12 +1,12 @@ -using MediaBrowser.Controller.Entities.TV; +using System; +using System.Xml; +using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; -using System; -using System.Xml; -namespace MediaBrowser.Providers.TV +namespace MediaBrowser.LocalMetadata.Parsers { /// <summary> /// Class SeriesXmlParser diff --git a/MediaBrowser.LocalMetadata/Properties/AssemblyInfo.cs b/MediaBrowser.LocalMetadata/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..dd1c89579 --- /dev/null +++ b/MediaBrowser.LocalMetadata/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +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("MediaBrowser.LocalMetadata")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("MediaBrowser.LocalMetadata")] +[assembly: AssemblyCopyright("Copyright © 2014")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// 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)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("1c669501-2113-493a-b0ed-f8fd26311941")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/MediaBrowser.Providers/AdultVideos/AdultVideoXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/AdultVideoXmlProvider.cs index 07b24c57d..fa17d597d 100644 --- a/MediaBrowser.Providers/AdultVideos/AdultVideoXmlProvider.cs +++ b/MediaBrowser.LocalMetadata/Providers/AdultVideoXmlProvider.cs @@ -1,14 +1,14 @@ -using MediaBrowser.Common.IO; +using System.Collections.Generic; +using System.IO; +using System.Threading; +using MediaBrowser.Common.IO; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Providers; +using MediaBrowser.LocalMetadata.Parsers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; -using MediaBrowser.Providers.Movies; -using System.Collections.Generic; -using System.IO; -using System.Threading; -namespace MediaBrowser.Providers.AdultVideos +namespace MediaBrowser.LocalMetadata.Providers { class AdultVideoXmlProvider : BaseXmlProvider<AdultVideo> { diff --git a/MediaBrowser.Providers/Music/AlbumXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/AlbumXmlProvider.cs index 73b914090..646922769 100644 --- a/MediaBrowser.Providers/Music/AlbumXmlProvider.cs +++ b/MediaBrowser.LocalMetadata/Providers/AlbumXmlProvider.cs @@ -1,13 +1,13 @@ -using MediaBrowser.Common.IO; +using System.IO; +using System.Threading; +using MediaBrowser.Common.IO; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Logging; -using System.IO; -using System.Threading; -namespace MediaBrowser.Providers.Music +namespace MediaBrowser.LocalMetadata.Providers { - class AlbumXmlProvider : BaseXmlProvider<MusicAlbum> + public class AlbumXmlProvider : BaseXmlProvider<MusicAlbum> { private readonly ILogger _logger; diff --git a/MediaBrowser.Providers/Music/ArtistXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/ArtistXmlProvider.cs index b221fde1e..0b5ebfb11 100644 --- a/MediaBrowser.Providers/Music/ArtistXmlProvider.cs +++ b/MediaBrowser.LocalMetadata/Providers/ArtistXmlProvider.cs @@ -1,11 +1,11 @@ -using MediaBrowser.Common.IO; +using System.IO; +using System.Threading; +using MediaBrowser.Common.IO; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Logging; -using System.IO; -using System.Threading; -namespace MediaBrowser.Providers.Music +namespace MediaBrowser.LocalMetadata.Providers { class ArtistXmlProvider : BaseXmlProvider<MusicArtist> { diff --git a/MediaBrowser.Providers/BoxSets/BoxSetXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/BoxSetXmlProvider.cs index 1d4d893ed..871c2bd92 100644 --- a/MediaBrowser.Providers/BoxSets/BoxSetXmlProvider.cs +++ b/MediaBrowser.LocalMetadata/Providers/BoxSetXmlProvider.cs @@ -1,11 +1,12 @@ -using MediaBrowser.Common.IO; +using System.IO; +using System.Threading; +using MediaBrowser.Common.IO; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Providers; +using MediaBrowser.LocalMetadata.Parsers; using MediaBrowser.Model.Logging; -using System.IO; -using System.Threading; -namespace MediaBrowser.Providers.BoxSets +namespace MediaBrowser.LocalMetadata.Providers { /// <summary> /// Class BoxSetXmlProvider. diff --git a/MediaBrowser.Providers/LiveTv/ChannelXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/ChannelXmlProvider.cs index 44a312e24..78845487a 100644 --- a/MediaBrowser.Providers/LiveTv/ChannelXmlProvider.cs +++ b/MediaBrowser.LocalMetadata/Providers/ChannelXmlProvider.cs @@ -1,11 +1,11 @@ -using MediaBrowser.Common.IO; +using System.IO; +using System.Threading; +using MediaBrowser.Common.IO; using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Logging; -using System.IO; -using System.Threading; -namespace MediaBrowser.Providers.LiveTv +namespace MediaBrowser.LocalMetadata.Providers { public class ChannelXmlProvider : BaseXmlProvider<LiveTvChannel> { diff --git a/MediaBrowser.Providers/TV/EpisodeXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/EpisodeXmlProvider.cs index 3def06bfe..dff3c1c07 100644 --- a/MediaBrowser.Providers/TV/EpisodeXmlProvider.cs +++ b/MediaBrowser.LocalMetadata/Providers/EpisodeXmlProvider.cs @@ -1,15 +1,16 @@ -using MediaBrowser.Common.IO; +using System.Collections.Generic; +using System.IO; +using System.Threading; +using MediaBrowser.Common.IO; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Providers; +using MediaBrowser.LocalMetadata.Parsers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; -using System.Collections.Generic; -using System.IO; -using System.Threading; -namespace MediaBrowser.Providers.TV +namespace MediaBrowser.LocalMetadata.Providers { - public class EpisodeXmlProvider : BaseXmlProvider<Episode> + public class EpisodeXmlProvider : BaseXmlProvider<Episode>, IHasOrder { private readonly ILogger _logger; @@ -39,5 +40,14 @@ namespace MediaBrowser.Providers.TV return directoryService.GetFile(metadataFile); } + + public int Order + { + get + { + // After Xbmc + return 1; + } + } } } diff --git a/MediaBrowser.Providers/Folders/FolderXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/FolderXmlProvider.cs index 144d1b752..0a2b33744 100644 --- a/MediaBrowser.Providers/Folders/FolderXmlProvider.cs +++ b/MediaBrowser.LocalMetadata/Providers/FolderXmlProvider.cs @@ -1,11 +1,11 @@ -using MediaBrowser.Common.IO; +using System.IO; +using System.Threading; +using MediaBrowser.Common.IO; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Logging; -using System.IO; -using System.Threading; -namespace MediaBrowser.Providers.Folders +namespace MediaBrowser.LocalMetadata.Providers { /// <summary> /// Provides metadata for Folders and all subclasses by parsing folder.xml diff --git a/MediaBrowser.Providers/Games/GameSystemXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/GameSystemXmlProvider.cs index db9c8f063..dd486da1d 100644 --- a/MediaBrowser.Providers/Games/GameSystemXmlProvider.cs +++ b/MediaBrowser.LocalMetadata/Providers/GameSystemXmlProvider.cs @@ -1,11 +1,12 @@ -using MediaBrowser.Common.IO; +using System.IO; +using System.Threading; +using MediaBrowser.Common.IO; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Providers; +using MediaBrowser.LocalMetadata.Parsers; using MediaBrowser.Model.Logging; -using System.IO; -using System.Threading; -namespace MediaBrowser.Providers.Games +namespace MediaBrowser.LocalMetadata.Providers { public class GameSystemXmlProvider : BaseXmlProvider<GameSystem> { diff --git a/MediaBrowser.Providers/Games/GameXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/GameXmlProvider.cs index 6609f9d5e..681706321 100644 --- a/MediaBrowser.Providers/Games/GameXmlProvider.cs +++ b/MediaBrowser.LocalMetadata/Providers/GameXmlProvider.cs @@ -1,11 +1,12 @@ -using MediaBrowser.Common.IO; +using System.IO; +using System.Threading; +using MediaBrowser.Common.IO; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Providers; +using MediaBrowser.LocalMetadata.Parsers; using MediaBrowser.Model.Logging; -using System.IO; -using System.Threading; -namespace MediaBrowser.Providers.Games +namespace MediaBrowser.LocalMetadata.Providers { public class GameXmlProvider : BaseXmlProvider<Game> { diff --git a/MediaBrowser.Providers/Movies/MovieXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/MovieXmlProvider.cs index cc7293f53..6ba1912a5 100644 --- a/MediaBrowser.Providers/Movies/MovieXmlProvider.cs +++ b/MediaBrowser.LocalMetadata/Providers/MovieXmlProvider.cs @@ -1,13 +1,14 @@ -using MediaBrowser.Common.IO; +using System.Collections.Generic; +using System.IO; +using System.Threading; +using MediaBrowser.Common.IO; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Providers; +using MediaBrowser.LocalMetadata.Parsers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; -using System.Collections.Generic; -using System.IO; -using System.Threading; -namespace MediaBrowser.Providers.Movies +namespace MediaBrowser.LocalMetadata.Providers { public class MovieXmlProvider : BaseXmlProvider<Movie> { diff --git a/MediaBrowser.Providers/Music/MusicVideoXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/MusicVideoXmlProvider.cs index 93d9031c3..6289dcb56 100644 --- a/MediaBrowser.Providers/Music/MusicVideoXmlProvider.cs +++ b/MediaBrowser.LocalMetadata/Providers/MusicVideoXmlProvider.cs @@ -1,13 +1,12 @@ -using MediaBrowser.Common.IO; +using System.IO; +using System.Threading; +using MediaBrowser.Common.IO; using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Providers; +using MediaBrowser.LocalMetadata.Parsers; using MediaBrowser.Model.Logging; -using MediaBrowser.Providers.Movies; -using System.IO; -using System.Threading; -namespace MediaBrowser.Providers.Music +namespace MediaBrowser.LocalMetadata.Providers { class MusicVideoXmlProvider : BaseXmlProvider<MusicVideo> { diff --git a/MediaBrowser.Providers/People/PersonXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/PersonXmlProvider.cs index b120c4830..9f27d6c7d 100644 --- a/MediaBrowser.Providers/People/PersonXmlProvider.cs +++ b/MediaBrowser.LocalMetadata/Providers/PersonXmlProvider.cs @@ -1,11 +1,11 @@ -using MediaBrowser.Common.IO; +using System.IO; +using System.Threading; +using MediaBrowser.Common.IO; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Logging; -using System.IO; -using System.Threading; -namespace MediaBrowser.Providers.People +namespace MediaBrowser.LocalMetadata.Providers { public class PersonXmlProvider : BaseXmlProvider<Person> { diff --git a/MediaBrowser.Providers/TV/SeasonXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/SeasonXmlProvider.cs index 6a41988de..2320982c3 100644 --- a/MediaBrowser.Providers/TV/SeasonXmlProvider.cs +++ b/MediaBrowser.LocalMetadata/Providers/SeasonXmlProvider.cs @@ -1,16 +1,17 @@ -using MediaBrowser.Common.IO; +using System.IO; +using System.Threading; +using MediaBrowser.Common.IO; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Providers; +using MediaBrowser.LocalMetadata.Parsers; using MediaBrowser.Model.Logging; -using System.IO; -using System.Threading; -namespace MediaBrowser.Providers.TV +namespace MediaBrowser.LocalMetadata.Providers { /// <summary> /// Class SeriesProviderFromXml /// </summary> - public class SeasonXmlProvider : BaseXmlProvider<Season> + public class SeasonXmlProvider : BaseXmlProvider<Season>, IHasOrder { private readonly ILogger _logger; @@ -29,6 +30,15 @@ namespace MediaBrowser.Providers.TV { return directoryService.GetFile(Path.Combine(info.Path, "season.xml")); } + + public int Order + { + get + { + // After Xbmc + return 1; + } + } } } diff --git a/MediaBrowser.Providers/TV/SeriesXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/SeriesXmlProvider.cs index f32afbd96..311c10287 100644 --- a/MediaBrowser.Providers/TV/SeriesXmlProvider.cs +++ b/MediaBrowser.LocalMetadata/Providers/SeriesXmlProvider.cs @@ -1,16 +1,17 @@ -using MediaBrowser.Common.IO; +using System.IO; +using System.Threading; +using MediaBrowser.Common.IO; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Providers; +using MediaBrowser.LocalMetadata.Parsers; using MediaBrowser.Model.Logging; -using System.IO; -using System.Threading; -namespace MediaBrowser.Providers.TV +namespace MediaBrowser.LocalMetadata.Providers { /// <summary> /// Class SeriesProviderFromXml /// </summary> - public class SeriesXmlProvider : BaseXmlProvider<Series> + public class SeriesXmlProvider : BaseXmlProvider<Series>, IHasOrder { private readonly ILogger _logger; @@ -29,5 +30,14 @@ namespace MediaBrowser.Providers.TV { return directoryService.GetFile(Path.Combine(info.Path, "series.xml")); } + + public int Order + { + get + { + // After Xbmc + return 1; + } + } } } diff --git a/MediaBrowser.Providers/Movies/TrailerXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/TrailerXmlProvider.cs index d24bdb431..db3b2fcf0 100644 --- a/MediaBrowser.Providers/Movies/TrailerXmlProvider.cs +++ b/MediaBrowser.LocalMetadata/Providers/TrailerXmlProvider.cs @@ -1,13 +1,14 @@ -using MediaBrowser.Common.IO; +using System.Collections.Generic; +using System.IO; +using System.Threading; +using MediaBrowser.Common.IO; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Providers; +using MediaBrowser.LocalMetadata.Parsers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; -using System.Collections.Generic; -using System.IO; -using System.Threading; -namespace MediaBrowser.Providers.Movies +namespace MediaBrowser.LocalMetadata.Providers { public class TrailerXmlProvider : BaseXmlProvider<Trailer> { diff --git a/MediaBrowser.Providers/Videos/VideoXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/VideoXmlProvider.cs index 779f3faa7..25aa61baf 100644 --- a/MediaBrowser.Providers/Videos/VideoXmlProvider.cs +++ b/MediaBrowser.LocalMetadata/Providers/VideoXmlProvider.cs @@ -1,14 +1,14 @@ -using MediaBrowser.Common.IO; +using System.Collections.Generic; +using System.IO; +using System.Threading; +using MediaBrowser.Common.IO; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Providers; +using MediaBrowser.LocalMetadata.Parsers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; -using MediaBrowser.Providers.Movies; -using System.Collections.Generic; -using System.IO; -using System.Threading; -namespace MediaBrowser.Providers.Videos +namespace MediaBrowser.LocalMetadata.Providers { class VideoXmlProvider : BaseXmlProvider<Video> { diff --git a/MediaBrowser.Providers/Savers/AlbumXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/AlbumXmlSaver.cs index 4f08f0d82..05022464d 100644 --- a/MediaBrowser.Providers/Savers/AlbumXmlSaver.cs +++ b/MediaBrowser.LocalMetadata/Savers/AlbumXmlSaver.cs @@ -1,12 +1,12 @@ -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Audio; -using MediaBrowser.Controller.Library; -using System.Collections.Generic; +using System.Collections.Generic; using System.IO; using System.Text; using System.Threading; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.Audio; +using MediaBrowser.Controller.Library; -namespace MediaBrowser.Providers.Savers +namespace MediaBrowser.LocalMetadata.Savers { class AlbumXmlSaver : IMetadataFileSaver { diff --git a/MediaBrowser.Providers/Savers/ArtistXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/ArtistXmlSaver.cs index 5f9c06d25..b932c5c7c 100644 --- a/MediaBrowser.Providers/Savers/ArtistXmlSaver.cs +++ b/MediaBrowser.LocalMetadata/Savers/ArtistXmlSaver.cs @@ -1,12 +1,12 @@ -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Audio; -using MediaBrowser.Controller.Library; -using System.Collections.Generic; +using System.Collections.Generic; using System.IO; using System.Text; using System.Threading; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.Audio; +using MediaBrowser.Controller.Library; -namespace MediaBrowser.Providers.Savers +namespace MediaBrowser.LocalMetadata.Savers { class ArtistXmlSaver : IMetadataFileSaver { diff --git a/MediaBrowser.Providers/Savers/BoxSetXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/BoxSetXmlSaver.cs index dcf789b30..db7b40c7d 100644 --- a/MediaBrowser.Providers/Savers/BoxSetXmlSaver.cs +++ b/MediaBrowser.LocalMetadata/Savers/BoxSetXmlSaver.cs @@ -1,13 +1,12 @@ -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Movies; -using MediaBrowser.Controller.Library; -using MediaBrowser.Model.Entities; -using System.Collections.Generic; +using System.Collections.Generic; using System.IO; using System.Text; using System.Threading; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.Movies; +using MediaBrowser.Controller.Library; -namespace MediaBrowser.Providers.Savers +namespace MediaBrowser.LocalMetadata.Savers { public class BoxSetXmlSaver : IMetadataFileSaver { diff --git a/MediaBrowser.Providers/Savers/ChannelXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/ChannelXmlSaver.cs index 2d4221bda..3b7783012 100644 --- a/MediaBrowser.Providers/Savers/ChannelXmlSaver.cs +++ b/MediaBrowser.LocalMetadata/Savers/ChannelXmlSaver.cs @@ -1,13 +1,12 @@ -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.LiveTv; -using MediaBrowser.Model.Entities; -using System.Collections.Generic; +using System.Collections.Generic; using System.IO; using System.Text; using System.Threading; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.LiveTv; -namespace MediaBrowser.Providers.Savers +namespace MediaBrowser.LocalMetadata.Savers { /// <summary> /// Class PersonXmlSaver diff --git a/MediaBrowser.Providers/Savers/EpisodeXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/EpisodeXmlSaver.cs index 98a298620..275ec2fe8 100644 --- a/MediaBrowser.Providers/Savers/EpisodeXmlSaver.cs +++ b/MediaBrowser.LocalMetadata/Savers/EpisodeXmlSaver.cs @@ -1,15 +1,15 @@ -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.TV; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Persistence; -using System.Collections.Generic; +using System.Collections.Generic; using System.Globalization; using System.IO; using System.Security; using System.Text; using System.Threading; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Persistence; -namespace MediaBrowser.Providers.Savers +namespace MediaBrowser.LocalMetadata.Savers { public class EpisodeXmlSaver : IMetadataFileSaver { diff --git a/MediaBrowser.Providers/Savers/FolderXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/FolderXmlSaver.cs index db08eafe3..6dd65b69c 100644 --- a/MediaBrowser.Providers/Savers/FolderXmlSaver.cs +++ b/MediaBrowser.LocalMetadata/Savers/FolderXmlSaver.cs @@ -1,15 +1,14 @@ -using MediaBrowser.Controller.Entities; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Threading; +using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; -using MediaBrowser.Model.Entities; -using System.Collections.Generic; -using System.IO; -using System.Text; -using System.Threading; -namespace MediaBrowser.Providers.Savers +namespace MediaBrowser.LocalMetadata.Savers { public class FolderXmlSaver : IMetadataFileSaver { diff --git a/MediaBrowser.Providers/Savers/GameSystemXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/GameSystemXmlSaver.cs index 98a2d03be..163c79ce2 100644 --- a/MediaBrowser.Providers/Savers/GameSystemXmlSaver.cs +++ b/MediaBrowser.LocalMetadata/Savers/GameSystemXmlSaver.cs @@ -1,12 +1,12 @@ -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Library; -using System.Collections.Generic; +using System.Collections.Generic; using System.IO; using System.Security; using System.Text; using System.Threading; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Library; -namespace MediaBrowser.Providers.Savers +namespace MediaBrowser.LocalMetadata.Savers { public class GameSystemXmlSaver : IMetadataFileSaver { diff --git a/MediaBrowser.Providers/Savers/GameXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/GameXmlSaver.cs index 959041a8c..7eeaa211f 100644 --- a/MediaBrowser.Providers/Savers/GameXmlSaver.cs +++ b/MediaBrowser.LocalMetadata/Savers/GameXmlSaver.cs @@ -1,14 +1,14 @@ -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Library; -using MediaBrowser.Model.Entities; -using System.Collections.Generic; +using System.Collections.Generic; using System.Globalization; using System.IO; using System.Security; using System.Text; using System.Threading; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Library; +using MediaBrowser.Model.Entities; -namespace MediaBrowser.Providers.Savers +namespace MediaBrowser.LocalMetadata.Savers { /// <summary> /// Saves game.xml for games diff --git a/MediaBrowser.Providers/Savers/MovieXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/MovieXmlSaver.cs index cd5f2faec..ef81790a6 100644 --- a/MediaBrowser.Providers/Savers/MovieXmlSaver.cs +++ b/MediaBrowser.LocalMetadata/Savers/MovieXmlSaver.cs @@ -1,16 +1,15 @@ -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Movies; -using MediaBrowser.Controller.Entities.TV; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Persistence; -using System.Collections.Generic; -using System.Globalization; +using System.Collections.Generic; using System.IO; using System.Security; using System.Text; using System.Threading; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.Movies; +using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Persistence; -namespace MediaBrowser.Providers.Savers +namespace MediaBrowser.LocalMetadata.Savers { /// <summary> /// Saves movie.xml for movies, trailers and music videos diff --git a/MediaBrowser.Providers/Savers/PersonXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/PersonXmlSaver.cs index 9bbe5b5dc..2ea60f47c 100644 --- a/MediaBrowser.Providers/Savers/PersonXmlSaver.cs +++ b/MediaBrowser.LocalMetadata/Savers/PersonXmlSaver.cs @@ -1,14 +1,12 @@ -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Providers; -using System.Collections.Generic; +using System.Collections.Generic; using System.IO; using System.Security; using System.Text; using System.Threading; -using MediaBrowser.Model.Entities; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Library; -namespace MediaBrowser.Providers.Savers +namespace MediaBrowser.LocalMetadata.Savers { /// <summary> /// Class PersonXmlSaver diff --git a/MediaBrowser.Providers/Savers/SeasonXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/SeasonXmlSaver.cs index ee3e18fd7..b9908875d 100644 --- a/MediaBrowser.Providers/Savers/SeasonXmlSaver.cs +++ b/MediaBrowser.LocalMetadata/Savers/SeasonXmlSaver.cs @@ -1,14 +1,14 @@ -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.TV; -using MediaBrowser.Controller.Library; -using System.Collections.Generic; +using System.Collections.Generic; using System.Globalization; using System.IO; using System.Security; using System.Text; using System.Threading; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.Library; -namespace MediaBrowser.Providers.Savers +namespace MediaBrowser.LocalMetadata.Savers { public class SeasonXmlSaver : IMetadataFileSaver { diff --git a/MediaBrowser.Providers/Savers/SeriesXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/SeriesXmlSaver.cs index a7ed55e5d..23ea52820 100644 --- a/MediaBrowser.Providers/Savers/SeriesXmlSaver.cs +++ b/MediaBrowser.LocalMetadata/Savers/SeriesXmlSaver.cs @@ -1,15 +1,15 @@ -using System.Globalization; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.TV; -using MediaBrowser.Controller.Library; -using MediaBrowser.Model.Entities; -using System.Collections.Generic; +using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Security; using System.Text; using System.Threading; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.Library; +using MediaBrowser.Model.Entities; -namespace MediaBrowser.Providers.Savers +namespace MediaBrowser.LocalMetadata.Savers { public class SeriesXmlSaver : IMetadataFileSaver { diff --git a/MediaBrowser.Providers/Savers/XmlSaverHelpers.cs b/MediaBrowser.LocalMetadata/Savers/XmlSaverHelpers.cs index d10422f78..c43875b04 100644 --- a/MediaBrowser.Providers/Savers/XmlSaverHelpers.cs +++ b/MediaBrowser.LocalMetadata/Savers/XmlSaverHelpers.cs @@ -1,9 +1,4 @@ -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Movies; -using MediaBrowser.Controller.Entities.TV; -using MediaBrowser.Controller.Persistence; -using MediaBrowser.Model.Entities; -using System; +using System; using System.Collections.Generic; using System.Globalization; using System.IO; @@ -11,8 +6,13 @@ using System.Linq; using System.Security; using System.Text; using System.Xml; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.Movies; +using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.Persistence; +using MediaBrowser.Model.Entities; -namespace MediaBrowser.Providers.Savers +namespace MediaBrowser.LocalMetadata.Savers { /// <summary> /// Class XmlHelpers diff --git a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj index 4237c2974..2d19d335d 100644 --- a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj +++ b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj @@ -170,6 +170,9 @@ <Compile Include="..\MediaBrowser.Model\Configuration\UserConfiguration.cs"> <Link>Configuration\UserConfiguration.cs</Link> </Compile> + <Compile Include="..\MediaBrowser.Model\Configuration\XbmcMetadataOptions.cs"> + <Link>Configuration\XbmcMetadataOptions.cs</Link> + </Compile> <Compile Include="..\MediaBrowser.Model\Dlna\AudioOptions.cs"> <Link>Dlna\AudioOptions.cs</Link> </Compile> diff --git a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj index 262ad6267..c0c886c8a 100644 --- a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj +++ b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj @@ -157,6 +157,9 @@ <Compile Include="..\MediaBrowser.Model\Configuration\UserConfiguration.cs"> <Link>Configuration\UserConfiguration.cs</Link> </Compile> + <Compile Include="..\MediaBrowser.Model\Configuration\XbmcMetadataOptions.cs"> + <Link>Configuration\XbmcMetadataOptions.cs</Link> + </Compile> <Compile Include="..\MediaBrowser.Model\Dlna\AudioOptions.cs"> <Link>Dlna\AudioOptions.cs</Link> </Compile> diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs index 542020483..af09a25ac 100644 --- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs +++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Model.Weather; +using System.Linq; +using MediaBrowser.Model.Weather; using System; namespace MediaBrowser.Model.Configuration @@ -69,24 +70,12 @@ namespace MediaBrowser.Model.Configuration public string SeasonZeroDisplayName { get; set; } /// <summary> - /// Gets or sets the metadata refresh days. - /// </summary> - /// <value>The metadata refresh days.</value> - public int MetadataRefreshDays { get; set; } - - /// <summary> /// Gets or sets a value indicating whether [save local meta]. /// </summary> /// <value><c>true</c> if [save local meta]; otherwise, <c>false</c>.</value> public bool SaveLocalMeta { get; set; } /// <summary> - /// Gets or sets a value indicating whether [refresh item images]. - /// </summary> - /// <value><c>true</c> if [refresh item images]; otherwise, <c>false</c>.</value> - public bool RefreshItemImages { get; set; } - - /// <summary> /// Gets or sets the preferred metadata language. /// </summary> /// <value>The preferred metadata language.</value> @@ -227,6 +216,9 @@ namespace MediaBrowser.Model.Configuration [Obsolete] public ChapterOptions ChapterOptions { get; set; } + [Obsolete] + public bool DefaultMetadataSettingsApplied { get; set; } + /// <summary> /// Initializes a new instance of the <see cref="ServerConfiguration" /> class. /// </summary> @@ -258,7 +250,6 @@ namespace MediaBrowser.Model.Configuration PathSubstitutions = new PathSubstitution[] { }; - MetadataRefreshDays = 30; PreferredMetadataLanguage = "en"; MetadataCountryCode = "US"; diff --git a/MediaBrowser.Model/Configuration/XbmcMetadataOptions.cs b/MediaBrowser.Model/Configuration/XbmcMetadataOptions.cs new file mode 100644 index 000000000..db8b69951 --- /dev/null +++ b/MediaBrowser.Model/Configuration/XbmcMetadataOptions.cs @@ -0,0 +1,21 @@ + +namespace MediaBrowser.Model.Configuration +{ + public class XbmcMetadataOptions + { + public string UserId { get; set; } + + public string ReleaseDateFormat { get; set; } + + public bool SaveImagePathsInNfo { get; set; } + public bool EnablePathSubstitution { get; set; } + + public XbmcMetadataOptions() + { + ReleaseDateFormat = "yyyy-MM-dd"; + + SaveImagePathsInNfo = true; + EnablePathSubstitution = true; + } + } +} diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj index fc79b7e75..d2b0a8caf 100644 --- a/MediaBrowser.Model/MediaBrowser.Model.csproj +++ b/MediaBrowser.Model/MediaBrowser.Model.csproj @@ -75,6 +75,7 @@ <Compile Include="Chapters\RemoteChapterResult.cs" /> <Compile Include="Configuration\ChannelOptions.cs" /> <Compile Include="Configuration\ChapterOptions.cs" /> + <Compile Include="Configuration\XbmcMetadataOptions.cs" /> <Compile Include="Configuration\SubtitlePlaybackMode.cs" /> <Compile Include="Configuration\TvFileOrganizationOptions.cs" /> <Compile Include="Configuration\BaseApplicationConfiguration.cs" /> diff --git a/MediaBrowser.Providers/GameGenres/AudioChannelItemMetadataService.cs b/MediaBrowser.Providers/Channels/AudioChannelItemMetadataService.cs index c8e496b71..42b1ea4de 100644 --- a/MediaBrowser.Providers/GameGenres/AudioChannelItemMetadataService.cs +++ b/MediaBrowser.Providers/Channels/AudioChannelItemMetadataService.cs @@ -1,13 +1,13 @@ -using MediaBrowser.Common.IO; +using System.Collections.Generic; +using MediaBrowser.Common.IO; using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using MediaBrowser.Providers.Manager; -using System.Collections.Generic; -namespace MediaBrowser.Providers.GameGenres +namespace MediaBrowser.Providers.Channels { public class AudioChannelItemMetadataService : MetadataService<ChannelAudioItem, ItemLookupInfo> { diff --git a/MediaBrowser.Providers/GameGenres/VideoChannelItemMetadataService.cs b/MediaBrowser.Providers/Channels/VideoChannelItemMetadataService.cs index 9dd37c959..ddd112207 100644 --- a/MediaBrowser.Providers/GameGenres/VideoChannelItemMetadataService.cs +++ b/MediaBrowser.Providers/Channels/VideoChannelItemMetadataService.cs @@ -1,13 +1,13 @@ -using MediaBrowser.Common.IO; +using System.Collections.Generic; +using MediaBrowser.Common.IO; using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using MediaBrowser.Providers.Manager; -using System.Collections.Generic; -namespace MediaBrowser.Providers.GameGenres +namespace MediaBrowser.Providers.Channels { public class VideoChannelItemMetadataService : MetadataService<ChannelVideoItem, ItemLookupInfo> { diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index 3234a8ae0..4c9d2416f 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -70,41 +70,28 @@ <Link>Properties\SharedVersion.cs</Link> </Compile> <Compile Include="AdultVideos\AdultVideoMetadataService.cs" /> - <Compile Include="AdultVideos\AdultVideoXmlProvider.cs" /> - <Compile Include="All\InternalMetadataFolderImageProvider.cs" /> - <Compile Include="All\LocalImageProvider.cs" /> <Compile Include="Books\BookMetadataService.cs" /> <Compile Include="BoxSets\BoxSetMetadataService.cs" /> - <Compile Include="BoxSets\BoxSetXmlParser.cs" /> <Compile Include="BoxSets\MovieDbBoxSetImageProvider.cs" /> <Compile Include="BoxSets\MovieDbBoxSetProvider.cs" /> <Compile Include="Channels\ChannelMetadataService.cs" /> <Compile Include="Chapters\ChapterManager.cs" /> <Compile Include="FolderImages\DefaultImageProvider.cs" /> - <Compile Include="Folders\CollectionFolderImageProvider.cs" /> <Compile Include="Folders\FolderMetadataService.cs" /> - <Compile Include="Folders\ImagesByNameImageProvider.cs" /> - <Compile Include="GameGenres\AudioChannelItemMetadataService.cs" /> + <Compile Include="Channels\AudioChannelItemMetadataService.cs" /> <Compile Include="GameGenres\GameGenreMetadataService.cs" /> - <Compile Include="GameGenres\VideoChannelItemMetadataService.cs" /> + <Compile Include="Channels\VideoChannelItemMetadataService.cs" /> <Compile Include="Games\GameMetadataService.cs" /> <Compile Include="Games\GameSystemMetadataService.cs" /> - <Compile Include="Games\GameSystemXmlParser.cs" /> <Compile Include="Genres\GenreMetadataService.cs" /> <Compile Include="LiveTv\AudioRecordingService.cs" /> <Compile Include="LiveTv\ChannelMetadataService.cs" /> - <Compile Include="LiveTv\ChannelXmlProvider.cs" /> <Compile Include="LiveTv\ProgramMetadataService.cs" /> <Compile Include="LiveTv\VideoRecordingService.cs" /> <Compile Include="Manager\ImageSaver.cs" /> <Compile Include="Manager\ItemImageProvider.cs" /> <Compile Include="Manager\ProviderManager.cs" /> <Compile Include="Manager\MetadataService.cs" /> - <Compile Include="BaseXmlProvider.cs" /> - <Compile Include="Folders\FolderXmlProvider.cs" /> - <Compile Include="Games\GameXmlParser.cs" /> - <Compile Include="Games\GameXmlProvider.cs" /> - <Compile Include="Games\GameSystemXmlProvider.cs" /> <Compile Include="Manager\SeriesOrderManager.cs" /> <Compile Include="MediaInfo\FFProbeAudioInfo.cs" /> <Compile Include="MediaInfo\FFProbeHelpers.cs" /> @@ -118,16 +105,13 @@ <Compile Include="Movies\GenericMovieDbInfo.cs" /> <Compile Include="Movies\MovieDbSearch.cs" /> <Compile Include="Movies\MovieMetadataService.cs" /> - <Compile Include="Movies\MovieXmlProvider.cs" /> <Compile Include="Movies\TmdbSettings.cs" /> - <Compile Include="Movies\TrailerXmlProvider.cs" /> <Compile Include="MusicGenres\MusicGenreImageProvider.cs" /> <Compile Include="GameGenres\GameGenreImageProvider.cs" /> <Compile Include="Genres\GenreImageProvider.cs" /> <Compile Include="ImagesByName\ImageUtils.cs" /> <Compile Include="MediaInfo\AudioImageProvider.cs" /> <Compile Include="MediaInfo\VideoImageProvider.cs" /> - <Compile Include="BoxSets\BoxSetXmlProvider.cs" /> <Compile Include="Movies\MovieDbImageProvider.cs" /> <Compile Include="Movies\FanartMovieImageProvider.cs" /> <Compile Include="MusicGenres\MusicGenreMetadataService.cs" /> @@ -146,16 +130,12 @@ <Compile Include="Music\MusicBrainzArtistProvider.cs" /> <Compile Include="Music\MusicExternalIds.cs" /> <Compile Include="Music\MusicVideoMetadataService.cs" /> - <Compile Include="Music\MusicVideoXmlProvider.cs" /> <Compile Include="Omdb\OmdbProvider.cs" /> <Compile Include="Omdb\OmdbItemProvider.cs" /> <Compile Include="People\MovieDbPersonImageProvider.cs" /> <Compile Include="Movies\MovieUpdatesPrescanTask.cs" /> - <Compile Include="Movies\MovieXmlParser.cs" /> <Compile Include="Movies\FanArtMovieUpdatesPostScanTask.cs" /> <Compile Include="Movies\MovieDbProvider.cs" /> - <Compile Include="Music\AlbumXmlProvider.cs" /> - <Compile Include="Music\ArtistXmlProvider.cs" /> <Compile Include="Music\FanArtUpdatesPostScanTask.cs" /> <Compile Include="Music\LastfmAlbumProvider.cs" /> <Compile Include="Music\LastfmHelper.cs" /> @@ -163,10 +143,8 @@ <Compile Include="Music\FanArtArtistProvider.cs" /> <Compile Include="Music\LastFmImageProvider.cs" /> <Compile Include="Music\MusicBrainzAlbumProvider.cs" /> - <Compile Include="Music\MusicVideoXmlParser.cs" /> <Compile Include="Music\SoundtrackPostScanTask.cs" /> <Compile Include="People\PersonMetadataService.cs" /> - <Compile Include="People\PersonXmlProvider.cs" /> <Compile Include="People\MovieDbPersonProvider.cs" /> <Compile Include="Photos\ExifReader.cs" /> <Compile Include="Photos\ExifTags.cs" /> @@ -175,34 +153,17 @@ <Compile Include="Photos\PhotoProvider.cs" /> <Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Manager\ProviderUtils.cs" /> - <Compile Include="Savers\AlbumXmlSaver.cs" /> - <Compile Include="Savers\ArtistXmlSaver.cs" /> - <Compile Include="Savers\BoxSetXmlSaver.cs" /> - <Compile Include="Savers\ChannelXmlSaver.cs" /> - <Compile Include="Savers\EpisodeXmlSaver.cs" /> - <Compile Include="Savers\FolderXmlSaver.cs" /> - <Compile Include="Savers\GameSystemXmlSaver.cs" /> - <Compile Include="Savers\GameXmlSaver.cs" /> - <Compile Include="Savers\MovieXmlSaver.cs" /> - <Compile Include="Savers\PersonXmlSaver.cs" /> - <Compile Include="Savers\SeasonXmlSaver.cs" /> - <Compile Include="Savers\SeriesXmlSaver.cs" /> - <Compile Include="Savers\XmlSaverHelpers.cs" /> <Compile Include="Studios\StudiosImageProvider.cs" /> <Compile Include="Studios\StudioMetadataService.cs" /> <Compile Include="Subtitles\OpenSubtitleDownloader.cs" /> <Compile Include="Subtitles\SubtitleManager.cs" /> - <Compile Include="TV\EpisodeLocalImageProvider.cs" /> <Compile Include="TV\EpisodeMetadataService.cs" /> - <Compile Include="TV\EpisodeXmlProvider.cs" /> - <Compile Include="TV\EpisodeXmlParser.cs" /> <Compile Include="TV\FanArtTvUpdatesPostScanTask.cs" /> <Compile Include="TV\FanArtSeasonProvider.cs" /> <Compile Include="TV\FanartSeriesProvider.cs" /> <Compile Include="TV\MissingEpisodeProvider.cs" /> <Compile Include="TV\MovieDbSeriesImageProvider.cs" /> <Compile Include="TV\MovieDbSeriesProvider.cs" /> - <Compile Include="TV\SeasonXmlParser.cs" /> <Compile Include="TV\SeriesMetadataService.cs" /> <Compile Include="TV\TvdbEpisodeImageProvider.cs" /> <Compile Include="People\TvdbPersonImageProvider.cs" /> @@ -212,16 +173,11 @@ <Compile Include="TV\SeasonMetadataService.cs" /> <Compile Include="TV\TvdbEpisodeProvider.cs" /> <Compile Include="TV\TvdbSeriesProvider.cs" /> - <Compile Include="TV\SeasonXmlProvider.cs" /> <Compile Include="TV\SeriesPostScanTask.cs" /> - <Compile Include="TV\SeriesXmlProvider.cs" /> - <Compile Include="TV\SeriesXmlParser.cs" /> <Compile Include="TV\TvdbPrescanTask.cs" /> <Compile Include="TV\TvExternalIds.cs" /> <Compile Include="Users\UserMetadataService.cs" /> <Compile Include="Videos\VideoMetadataService.cs" /> - <Compile Include="Videos\VideoXmlProvider.cs" /> - <Compile Include="Xbmc\XbmcImageSaver.cs" /> <Compile Include="Years\YearMetadataService.cs" /> </ItemGroup> <ItemGroup> @@ -248,6 +204,7 @@ <ItemGroup> <EmbeddedResource Include="MediaInfo\whitelist.txt" /> </ItemGroup> + <ItemGroup /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition=" '$(ConfigurationName)' != 'Release Mono' " /> <!-- To modify your build process, add your task inside one of the targets below and uncomment it. diff --git a/MediaBrowser.Server.Implementations/Localization/Server/server.json b/MediaBrowser.Server.Implementations/Localization/Server/server.json index fc050ec2d..9063ebc4b 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/server.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/server.json @@ -835,5 +835,16 @@ "TitleRemoteControl": "Remote Control", "OptionLatestTvRecordings": "Latest recordings", "LabelProtocolInfo": "Protocol info:", - "LabelProtocolInfoHelp": "The value that will be used when responding to GetProtocolInfo requests from the device." + "LabelProtocolInfoHelp": "The value that will be used when responding to GetProtocolInfo requests from the device.", + "TabXbmcMetadata": "Xbmc", + "HeaderXbmcMetadataHelp": "Media Browser includes native support for Xbmc Nfo metadata and images. To enable or disable Xbmc metadata, use the Advanced tab to configure options for your media types.", + "LabelXbmcMetadataUser": "Add user watch data to nfo's for:", + "LabelXbmcMetadataUserHelp": "Enable this to keep watch data in sync between Media Browser and Xbmc.", + "LabelXbmcMetadataDateFormat": "Release date format:", + "LabelXbmcMetadataDateFormatHelp": "All dates within nfo's will be read and written to using this format.", + "LabelXbmcMetadataSaveImagePaths": "Save image paths within nfo files", + "LabelXbmcMetadataSaveImagePathsHelp": "This is recommended if you have image file names that don't conform to Xbmc guidelines.", + "LabelXbmcMetadataEnablePathSubstitution": "Enable path substitution", + "LabelXbmcMetadataEnablePathSubstitutionHelp": "Enables path substitution of image paths using the server's path substitution settings.", + "LabelXbmcMetadataEnablePathSubstitutionHelp2": "See path substitution." }
\ No newline at end of file diff --git a/MediaBrowser.ServerApplication/ApplicationHost.cs b/MediaBrowser.ServerApplication/ApplicationHost.cs index 470786ba2..eeab8ee2e 100644 --- a/MediaBrowser.ServerApplication/ApplicationHost.cs +++ b/MediaBrowser.ServerApplication/ApplicationHost.cs @@ -17,6 +17,9 @@ using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.Audio; +using MediaBrowser.Controller.Entities.Movies; +using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.FileOrganization; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.LiveTv; @@ -38,9 +41,11 @@ using MediaBrowser.Dlna; using MediaBrowser.Dlna.ConnectionManager; using MediaBrowser.Dlna.ContentDirectory; using MediaBrowser.Dlna.Main; +using MediaBrowser.LocalMetadata.Providers; using MediaBrowser.MediaEncoding.BdInfo; using MediaBrowser.MediaEncoding.Encoder; using MediaBrowser.MediaEncoding.Subtitles; +using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Logging; using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.System; @@ -75,6 +80,7 @@ using MediaBrowser.ServerApplication.IO; using MediaBrowser.ServerApplication.Native; using MediaBrowser.ServerApplication.Networking; using MediaBrowser.WebDashboard.Api; +using MediaBrowser.XbmcMetadata.Providers; using System; using System.Collections.Generic; using System.Globalization; @@ -283,6 +289,7 @@ namespace MediaBrowser.ServerApplication DeleteDeprecatedModules(); MigrateModularConfigurations(); + ApplyDefaultXbmcSettings(); } private void MigrateModularConfigurations() @@ -309,6 +316,68 @@ namespace MediaBrowser.ServerApplication } } + private void ApplyDefaultXbmcSettings() + { + if (!ServerConfigurationManager.Configuration.DefaultMetadataSettingsApplied) + { + // Make sure xbmc metadata is disabled for existing users. + // New users can just take the defaults. + var service = ServerConfigurationManager.Configuration.IsStartupWizardCompleted + ? "Xbmc Nfo" + : "Media Browser Xml"; + + DisableMetadataService(typeof(Movie), ServerConfigurationManager.Configuration, service); + DisableMetadataService(typeof(MusicAlbum), ServerConfigurationManager.Configuration, service); + DisableMetadataService(typeof(MusicArtist), ServerConfigurationManager.Configuration, service); + DisableMetadataService(typeof(Episode), ServerConfigurationManager.Configuration, service); + DisableMetadataService(typeof(Season), ServerConfigurationManager.Configuration, service); + DisableMetadataService(typeof(Series), ServerConfigurationManager.Configuration, service); + DisableMetadataService(typeof(MusicVideo), ServerConfigurationManager.Configuration, service); + DisableMetadataService(typeof(Trailer), ServerConfigurationManager.Configuration, service); + DisableMetadataService(typeof(AdultVideo), ServerConfigurationManager.Configuration, service); + DisableMetadataService(typeof(Video), ServerConfigurationManager.Configuration, service); + } + + ServerConfigurationManager.Configuration.DefaultMetadataSettingsApplied = true; + ServerConfigurationManager.SaveConfiguration(); + } + + private void DisableMetadataService(Type type, ServerConfiguration config, string service) + { + var options = GetMetadataOptions(type, config); + + if (!options.DisabledMetadataSavers.Contains(service, StringComparer.OrdinalIgnoreCase)) + { + var list = options.DisabledMetadataSavers.ToList(); + + list.Add(service); + + options.DisabledMetadataSavers = list.ToArray(); + } + } + + private MetadataOptions GetMetadataOptions(Type type, ServerConfiguration config) + { + var options = config.MetadataOptions + .FirstOrDefault(i => string.Equals(i.ItemType, type.Name, StringComparison.OrdinalIgnoreCase)); + + if (options == null) + { + var list = config.MetadataOptions.ToList(); + + options = new MetadataOptions + { + ItemType = type.Name + }; + + list.Add(options); + + config.MetadataOptions = list.ToArray(); + } + + return options; + } + private void DeleteDeprecatedModules() { try @@ -328,6 +397,15 @@ namespace MediaBrowser.ServerApplication // Not there, no big deal } + try + { + File.Delete(Path.Combine(ApplicationPaths.PluginsPath, "MediaBrowser.Plugins.XbmcMetadata.dll")); + } + catch (IOException) + { + // Not there, no big deal + } + Task.Run(() => { try @@ -912,6 +990,12 @@ namespace MediaBrowser.ServerApplication // Dlna list.Add(typeof(DlnaEntryPoint).Assembly); + // Local metadata + list.Add(typeof(AlbumXmlProvider).Assembly); + + // Xbmc + list.Add(typeof(ArtistNfoProvider).Assembly); + list.AddRange(Assemblies.GetAssembliesWithParts()); // Include composable parts in the running assembly diff --git a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj index 7657fc091..fee2081cf 100644 --- a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj +++ b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj @@ -198,6 +198,10 @@ <Project>{734098eb-6dc1-4dd0-a1ca-3140dcd2737c}</Project> <Name>MediaBrowser.Dlna</Name> </ProjectReference> + <ProjectReference Include="..\MediaBrowser.LocalMetadata\MediaBrowser.LocalMetadata.csproj"> + <Project>{7ef9f3e0-697d-42f3-a08f-19deb5f84392}</Project> + <Name>MediaBrowser.LocalMetadata</Name> + </ProjectReference> <ProjectReference Include="..\MediaBrowser.MediaEncoding\MediaBrowser.MediaEncoding.csproj"> <Project>{0bd82fa6-eb8a-4452-8af5-74f9c3849451}</Project> <Name>MediaBrowser.MediaEncoding</Name> @@ -218,6 +222,10 @@ <Project>{5624b7b5-b5a7-41d8-9f10-cc5611109619}</Project> <Name>MediaBrowser.WebDashboard</Name> </ProjectReference> + <ProjectReference Include="..\MediaBrowser.XbmcMetadata\MediaBrowser.XbmcMetadata.csproj"> + <Project>{23499896-b135-4527-8574-c26e926ea99e}</Project> + <Name>MediaBrowser.XbmcMetadata</Name> + </ProjectReference> </ItemGroup> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" /> diff --git a/MediaBrowser.WebDashboard/Api/DashboardService.cs b/MediaBrowser.WebDashboard/Api/DashboardService.cs index acdbc9d65..2ca0669cd 100644 --- a/MediaBrowser.WebDashboard/Api/DashboardService.cs +++ b/MediaBrowser.WebDashboard/Api/DashboardService.cs @@ -594,6 +594,7 @@ namespace MediaBrowser.WebDashboard.Api "metadataimagespage.js", "metadatasubtitles.js", "metadatachapters.js", + "metadataxbmc.js", "moviegenres.js", "moviecollections.js", "movies.js", diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj index be05261b7..cbdae6d08 100644 --- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj +++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj @@ -338,6 +338,9 @@ <Content Include="dashboard-ui\metadatachapters.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
+ <Content Include="dashboard-ui\metadataxbmc.html">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
<Content Include="dashboard-ui\mypreferencesdisplay.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -683,6 +686,9 @@ <Content Include="dashboard-ui\scripts\metadatachapters.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
+ <Content Include="dashboard-ui\scripts\metadataxbmc.js">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
<Content Include="dashboard-ui\scripts\mypreferencesdisplay.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
diff --git a/MediaBrowser.XbmcMetadata/Configuration/NfoOptions.cs b/MediaBrowser.XbmcMetadata/Configuration/NfoOptions.cs new file mode 100644 index 000000000..d0e2dbc6d --- /dev/null +++ b/MediaBrowser.XbmcMetadata/Configuration/NfoOptions.cs @@ -0,0 +1,29 @@ +using MediaBrowser.Common.Configuration; +using MediaBrowser.Model.Configuration; +using System.Collections.Generic; + +namespace MediaBrowser.XbmcMetadata.Configuration +{ + public class ConfigurationFactory : IConfigurationFactory + { + public IEnumerable<ConfigurationStore> GetConfigurations() + { + return new[] + { + new ConfigurationStore + { + ConfigurationType = typeof(XbmcMetadataOptions), + Key = "xbmcmetadata" + } + }; + } + } + + public static class ConfigurationExtension + { + public static XbmcMetadataOptions GetNfoConfiguration(this IConfigurationManager manager) + { + return manager.GetConfiguration<XbmcMetadataOptions>("xbmcmetadata"); + } + } +} diff --git a/MediaBrowser.XbmcMetadata/EntryPoint.cs b/MediaBrowser.XbmcMetadata/EntryPoint.cs new file mode 100644 index 000000000..2d978781f --- /dev/null +++ b/MediaBrowser.XbmcMetadata/EntryPoint.cs @@ -0,0 +1,99 @@ +using MediaBrowser.Common.Configuration; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Plugins; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Logging; +using MediaBrowser.XbmcMetadata.Configuration; +using System; +using System.Linq; + +namespace MediaBrowser.XbmcMetadata +{ + public class EntryPoint : IServerEntryPoint + { + 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) + { + _userDataManager = userDataManager; + _libraryManager = libraryManager; + _logger = logger; + _providerManager = providerManager; + _config = config; + } + + public void Run() + { + _userDataManager.UserDataSaved += _userDataManager_UserDataSaved; + _libraryManager.ItemUpdated += _libraryManager_ItemUpdated; + } + + void _libraryManager_ItemUpdated(object sender, ItemChangeEventArgs e) + { + if (e.UpdateReason == ItemUpdateType.ImageUpdate && e.Item is Person) + { + var person = e.Item.Name; + + var items = _libraryManager.RootFolder + .GetRecursiveChildren(i => !i.IsFolder && i.People.Any(p => string.Equals(p.Name, person, StringComparison.OrdinalIgnoreCase))); + + foreach (var item in items) + { + SaveMetadataForItem(item, ItemUpdateType.MetadataEdit); + } + } + } + + void _userDataManager_UserDataSaved(object sender, UserDataSaveEventArgs e) + { + if (e.SaveReason == UserDataSaveReason.PlaybackFinished || e.SaveReason == UserDataSaveReason.TogglePlayed) + { + var item = e.Item as BaseItem; + + if (item != null) + { + if (!item.IsFolder && !(item is IItemByName)) + { + SaveMetadataForItem(item, ItemUpdateType.MetadataEdit); + } + } + } + } + + public void Dispose() + { + _userDataManager.UserDataSaved -= _userDataManager_UserDataSaved; + } + + private async void SaveMetadataForItem(BaseItem item, ItemUpdateType updateReason) + { + var userId = _config.GetNfoConfiguration().UserId; + if (string.IsNullOrWhiteSpace(userId)) + { + return; + } + + var locationType = item.LocationType; + if (locationType == LocationType.Remote || + locationType == LocationType.Virtual) + { + return; + } + + try + { + await _providerManager.SaveMetadata(item, updateReason).ConfigureAwait(false); + } + catch (Exception ex) + { + _logger.ErrorException("Error saving metadata for {0}", ex, item.Path ?? item.Name); + } + } + } +} diff --git a/MediaBrowser.Providers/Xbmc/XbmcImageSaver.cs b/MediaBrowser.XbmcMetadata/Images/XbmcImageSaver.cs index 93231e1e3..a45a44904 100644 --- a/MediaBrowser.Providers/Xbmc/XbmcImageSaver.cs +++ b/MediaBrowser.XbmcMetadata/Images/XbmcImageSaver.cs @@ -10,7 +10,7 @@ using System.Globalization; using System.IO; using System.Linq; -namespace MediaBrowser.Providers.Xbmc +namespace MediaBrowser.XbmcMetadata.Images { public class XbmcImageSaver : IImageFileSaver { diff --git a/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj b/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj new file mode 100644 index 000000000..a3928d774 --- /dev/null +++ b/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj @@ -0,0 +1,89 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <ProjectGuid>{23499896-B135-4527-8574-C26E926EA99E}</ProjectGuid> + <OutputType>Library</OutputType> + <AppDesignerFolder>Properties</AppDesignerFolder> + <RootNamespace>MediaBrowser.XbmcMetadata</RootNamespace> + <AssemblyName>MediaBrowser.XbmcMetadata</AssemblyName> + <TargetFrameworkVersion>v4.5</TargetFrameworkVersion> + <FileAlignment>512</FileAlignment> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + <DebugSymbols>true</DebugSymbols> + <DebugType>full</DebugType> + <Optimize>false</Optimize> + <OutputPath>bin\Debug\</OutputPath> + <DefineConstants>DEBUG;TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <OutputPath>bin\Release\</OutputPath> + <DefineConstants>TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <ItemGroup> + <Reference Include="System" /> + <Reference Include="System.Core" /> + <Reference Include="System.Xml.Linq" /> + <Reference Include="System.Data.DataSetExtensions" /> + <Reference Include="Microsoft.CSharp" /> + <Reference Include="System.Data" /> + <Reference Include="System.Xml" /> + </ItemGroup> + <ItemGroup> + <Compile Include="Configuration\NfoOptions.cs" /> + <Compile Include="EntryPoint.cs" /> + <Compile Include="Images\XbmcImageSaver.cs" /> + <Compile Include="Parsers\BaseNfoParser.cs" /> + <Compile Include="Parsers\EpisodeNfoParser.cs" /> + <Compile Include="Parsers\MovieNfoParser.cs" /> + <Compile Include="Parsers\SeasonNfoParser.cs" /> + <Compile Include="Parsers\SeriesNfoParser.cs" /> + <Compile Include="Properties\AssemblyInfo.cs" /> + <Compile Include="Providers\AlbumNfoProvider.cs" /> + <Compile Include="Providers\ArtistNfoProvider.cs" /> + <Compile Include="Providers\BaseNfoProvider.cs" /> + <Compile Include="Providers\BaseVideoNfoProvider.cs" /> + <Compile Include="Providers\EpisodeNfoProvider.cs" /> + <Compile Include="Providers\MovieNfoProvider.cs" /> + <Compile Include="Providers\SeasonNfoProvider.cs" /> + <Compile Include="Providers\SeriesNfoProvider.cs" /> + <Compile Include="Savers\AlbumXmlSaver.cs" /> + <Compile Include="Savers\ArtistXmlSaver.cs" /> + <Compile Include="Savers\EpisodeXmlSaver.cs" /> + <Compile Include="Savers\MovieXmlSaver.cs" /> + <Compile Include="Savers\SeasonXmlSaver.cs" /> + <Compile Include="Savers\SeriesXmlSaver.cs" /> + <Compile Include="Savers\XmlSaverHelpers.cs" /> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj"> + <Project>{9142eefa-7570-41e1-bfcc-468bb571af2f}</Project> + <Name>MediaBrowser.Common</Name> + </ProjectReference> + <ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj"> + <Project>{17e1f4e6-8abd-4fe5-9ecf-43d4b6087ba2}</Project> + <Name>MediaBrowser.Controller</Name> + </ProjectReference> + <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj"> + <Project>{7eeeb4bb-f3e8-48fc-b4c5-70f0fff8329b}</Project> + <Name>MediaBrowser.Model</Name> + </ProjectReference> + </ItemGroup> + <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> + <!-- To modify your build process, add your task inside one of the targets below and uncomment it. + Other similar extension points exist, see Microsoft.Common.targets. + <Target Name="BeforeBuild"> + </Target> + <Target Name="AfterBuild"> + </Target> + --> +</Project>
\ No newline at end of file diff --git a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs new file mode 100644 index 000000000..0515148f0 --- /dev/null +++ b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs @@ -0,0 +1,992 @@ +using MediaBrowser.Common.Configuration; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Logging; +using MediaBrowser.XbmcMetadata.Configuration; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading; +using System.Xml; + +namespace MediaBrowser.XbmcMetadata.Parsers +{ + public class BaseNfoParser<T> + where T : BaseItem + { + /// <summary> + /// The logger + /// </summary> + protected ILogger Logger { get; private set; } + + private readonly CultureInfo _usCulture = new CultureInfo("en-US"); + private readonly IConfigurationManager _config; + + /// <summary> + /// Initializes a new instance of the <see cref="BaseNfoParser{T}" /> class. + /// </summary> + /// <param name="logger">The logger.</param> + /// <param name="config">The configuration.</param> + public BaseNfoParser(ILogger logger, IConfigurationManager config) + { + Logger = logger; + _config = config; + } + + /// <summary> + /// Fetches metadata for an item from one xml file + /// </summary> + /// <param name="item">The item.</param> + /// <param name="metadataFile">The metadata file.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <exception cref="System.ArgumentNullException"></exception> + public void Fetch(T item, string metadataFile, CancellationToken cancellationToken) + { + if (item == null) + { + throw new ArgumentNullException(); + } + + if (string.IsNullOrEmpty(metadataFile)) + { + throw new ArgumentNullException(); + } + + var settings = new XmlReaderSettings + { + CheckCharacters = false, + IgnoreProcessingInstructions = true, + IgnoreComments = true, + ValidationType = ValidationType.None + }; + + //Fetch(item, metadataFile, settings, Encoding.GetEncoding("ISO-8859-1"), cancellationToken); + Fetch(item, metadataFile, settings, Encoding.UTF8, cancellationToken); + } + + /// <summary> + /// Fetches the specified item. + /// </summary> + /// <param name="item">The item.</param> + /// <param name="metadataFile">The metadata file.</param> + /// <param name="settings">The settings.</param> + /// <param name="encoding">The encoding.</param> + /// <param name="cancellationToken">The cancellation token.</param> + private void Fetch(T item, string metadataFile, XmlReaderSettings settings, Encoding encoding, CancellationToken cancellationToken) + { + using (var streamReader = new StreamReader(metadataFile, encoding)) + { + // Use XmlReader for best performance + using (var reader = XmlReader.Create(streamReader, settings)) + { + reader.MoveToContent(); + + // Loop through each element + while (reader.Read()) + { + cancellationToken.ThrowIfCancellationRequested(); + + if (reader.NodeType == XmlNodeType.Element) + { + FetchDataFromXmlNode(reader, item); + } + } + } + } + } + + protected virtual void FetchDataFromXmlNode(XmlReader reader, T item) + { + switch (reader.Name) + { + // DateCreated + case "dateadded": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + DateTime added; + if (DateTime.TryParse(val, out added)) + { + item.DateCreated = added.ToUniversalTime(); + } + else + { + Logger.Warn("Invalid Added value found: " + val); + } + } + break; + } + + case "title": + case "localtitle": + item.Name = reader.ReadElementContentAsString(); + break; + + case "criticrating": + { + var text = reader.ReadElementContentAsString(); + + var hasCriticRating = item as IHasCriticRating; + + if (hasCriticRating != null && !string.IsNullOrEmpty(text)) + { + float value; + if (float.TryParse(text, NumberStyles.Any, _usCulture, out value)) + { + hasCriticRating.CriticRating = value; + } + } + + break; + } + + case "budget": + { + var text = reader.ReadElementContentAsString(); + var hasBudget = item as IHasBudget; + if (hasBudget != null) + { + double value; + if (double.TryParse(text, NumberStyles.Any, _usCulture, out value)) + { + hasBudget.Budget = value; + } + } + + break; + } + + case "revenue": + { + var text = reader.ReadElementContentAsString(); + var hasBudget = item as IHasBudget; + if (hasBudget != null) + { + double value; + if (double.TryParse(text, NumberStyles.Any, _usCulture, out value)) + { + hasBudget.Revenue = value; + } + } + + break; + } + + case "metascore": + { + var text = reader.ReadElementContentAsString(); + var hasMetascore = item as IHasMetascore; + if (hasMetascore != null) + { + float value; + if (float.TryParse(text, NumberStyles.Any, _usCulture, out value)) + { + hasMetascore.Metascore = value; + } + } + + break; + } + + case "awardsummary": + { + var text = reader.ReadElementContentAsString(); + var hasAwards = item as IHasAwards; + if (hasAwards != null) + { + if (!string.IsNullOrWhiteSpace(text)) + { + hasAwards.AwardSummary = text; + } + } + + break; + } + + case "sorttitle": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + item.ForcedSortName = val; + } + break; + } + + case "outline": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + var hasShortOverview = item as IHasShortOverview; + + if (hasShortOverview != null) + { + hasShortOverview.ShortOverview = val; + } + } + break; + } + + case "biography": + case "plot": + case "review": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + item.Overview = val; + } + + break; + } + + case "criticratingsummary": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + var hasCriticRating = item as IHasCriticRating; + + if (hasCriticRating != null) + { + hasCriticRating.CriticRatingSummary = val; + } + } + + break; + } + + case "language": + { + var val = reader.ReadElementContentAsString(); + + var hasLanguage = item as IHasPreferredMetadataLanguage; + if (hasLanguage != null) + { + hasLanguage.PreferredMetadataLanguage = val; + } + + break; + } + + case "website": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + item.HomePageUrl = val; + } + + break; + } + + case "lockedfields": + { + var fields = new List<MetadataFields>(); + + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + var list = val.Split('|').Select(i => + { + MetadataFields field; + + if (Enum.TryParse<MetadataFields>(i, true, out field)) + { + return (MetadataFields?)field; + } + + return null; + + }).Where(i => i.HasValue).Select(i => i.Value); + + fields.AddRange(list); + } + + item.LockedFields = fields; + + break; + } + + case "tagline": + { + var val = reader.ReadElementContentAsString(); + + var hasTagline = item as IHasTaglines; + if (hasTagline != null) + { + if (!string.IsNullOrWhiteSpace(val)) + { + hasTagline.AddTagline(val); + } + } + break; + } + + case "country": + { + var val = reader.ReadElementContentAsString(); + + var hasProductionLocations = item as IHasProductionLocations; + if (hasProductionLocations != null) + { + if (!string.IsNullOrWhiteSpace(val)) + { + hasProductionLocations.AddProductionLocation(val); + } + } + break; + } + + case "mpaa": + { + var rating = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(rating)) + { + item.OfficialRating = rating; + } + break; + } + + case "mpaadescription": + { + var rating = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(rating)) + { + item.OfficialRatingDescription = rating; + } + break; + } + + case "customrating": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + item.CustomRating = val; + } + break; + } + + case "runtime": + { + var text = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(text)) + { + int runtime; + if (int.TryParse(text.Split(' ')[0], NumberStyles.Integer, _usCulture, out runtime)) + { + item.RunTimeTicks = TimeSpan.FromMinutes(runtime).Ticks; + } + } + break; + } + + case "aspectratio": + { + var val = reader.ReadElementContentAsString(); + + var hasAspectRatio = item as IHasAspectRatio; + if (!string.IsNullOrWhiteSpace(val) && hasAspectRatio != null) + { + hasAspectRatio.AspectRatio = val; + } + break; + } + + case "lockdata": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + item.IsLocked = string.Equals("true", val, StringComparison.OrdinalIgnoreCase); + } + break; + } + + case "studio": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + item.AddStudio(val); + } + break; + } + + case "director": + { + foreach (var p in SplitNames(reader.ReadElementContentAsString()).Select(v => new PersonInfo { Name = v.Trim(), Type = PersonType.Director })) + { + if (string.IsNullOrWhiteSpace(p.Name)) + { + continue; + } + item.AddPerson(p); + } + break; + } + case "credits": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + var parts = val.Split('/').Select(i => i.Trim()) + .Where(i => !string.IsNullOrEmpty(i)); + + foreach (var p in parts.Select(v => new PersonInfo { Name = v.Trim(), Type = PersonType.Writer })) + { + if (string.IsNullOrWhiteSpace(p.Name)) + { + continue; + } + item.AddPerson(p); + } + } + break; + } + + case "writer": + { + foreach (var p in SplitNames(reader.ReadElementContentAsString()).Select(v => new PersonInfo { Name = v.Trim(), Type = PersonType.Writer })) + { + if (string.IsNullOrWhiteSpace(p.Name)) + { + continue; + } + item.AddPerson(p); + } + break; + } + + case "actor": + { + using (var subtree = reader.ReadSubtree()) + { + var person = GetPersonFromXmlNode(subtree); + + item.AddPerson(person); + } + break; + } + + case "trailer": + { + var val = reader.ReadElementContentAsString(); + + var hasTrailer = item as IHasTrailers; + if (hasTrailer != null) + { + if (!string.IsNullOrWhiteSpace(val)) + { + hasTrailer.AddTrailerUrl(val, false); + } + } + break; + } + + case "displayorder": + { + var val = reader.ReadElementContentAsString(); + + var hasDisplayOrder = item as IHasDisplayOrder; + if (hasDisplayOrder != null) + { + if (!string.IsNullOrWhiteSpace(val)) + { + hasDisplayOrder.DisplayOrder = val; + } + } + break; + } + + case "year": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + int productionYear; + if (int.TryParse(val, out productionYear) && productionYear > 1850) + { + item.ProductionYear = productionYear; + } + } + + break; + } + + case "rating": + { + + var rating = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(rating)) + { + float val; + // All external meta is saving this as '.' for decimal I believe...but just to be sure + if (float.TryParse(rating.Replace(',', '.'), NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out val)) + { + item.CommunityRating = val; + } + } + break; + } + + case "aired": + case "formed": + case "premiered": + case "releasedate": + { + var formatString = _config.GetNfoConfiguration().ReleaseDateFormat; + + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + DateTime date; + + if (DateTime.TryParseExact(val, formatString, CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out date) && date.Year > 1850) + { + item.PremiereDate = date.ToUniversalTime(); + item.ProductionYear = date.Year; + } + } + + break; + } + + case "enddate": + { + var formatString = _config.GetNfoConfiguration().ReleaseDateFormat; + + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + DateTime date; + + if (DateTime.TryParseExact(val, formatString, CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out date) && date.Year > 1850) + { + item.EndDate = date.ToUniversalTime(); + } + } + + break; + } + + case "tvdbid": + var tvdbId = reader.ReadElementContentAsString(); + if (!string.IsNullOrWhiteSpace(tvdbId)) + { + item.SetProviderId(MetadataProviders.Tvdb, tvdbId); + } + break; + + case "votes": + { + var val = reader.ReadElementContentAsString(); + if (!string.IsNullOrWhiteSpace(val)) + { + int num; + + if (int.TryParse(val, NumberStyles.Integer, _usCulture, out num)) + { + item.VoteCount = num; + } + } + break; + } + case "musicbrainzalbumid": + { + var mbz = reader.ReadElementContentAsString(); + if (!string.IsNullOrWhiteSpace(mbz)) + { + item.SetProviderId(MetadataProviders.MusicBrainzAlbum, mbz); + } + break; + } + case "musicbrainzalbumartistid": + { + var mbz = reader.ReadElementContentAsString(); + if (!string.IsNullOrWhiteSpace(mbz)) + { + item.SetProviderId(MetadataProviders.MusicBrainzAlbumArtist, mbz); + } + break; + } + case "musicbrainzartistid": + { + var mbz = reader.ReadElementContentAsString(); + if (!string.IsNullOrWhiteSpace(mbz)) + { + item.SetProviderId(MetadataProviders.MusicBrainzArtist, mbz); + } + break; + } + case "musicbrainzreleasegroupid": + { + var mbz = reader.ReadElementContentAsString(); + if (!string.IsNullOrWhiteSpace(mbz)) + { + item.SetProviderId(MetadataProviders.MusicBrainzReleaseGroup, mbz); + } + break; + } + case "tvrageid": + { + var id = reader.ReadElementContentAsString(); + if (!string.IsNullOrWhiteSpace(id)) + { + item.SetProviderId(MetadataProviders.TvRage, id); + } + break; + } + case "audiodbartistid": + { + var id = reader.ReadElementContentAsString(); + if (!string.IsNullOrWhiteSpace(id)) + { + item.SetProviderId(MetadataProviders.AudioDbArtist, id); + } + break; + } + case "audiodbalbumid": + { + var id = reader.ReadElementContentAsString(); + if (!string.IsNullOrWhiteSpace(id)) + { + item.SetProviderId(MetadataProviders.AudioDbAlbum, id); + } + break; + } + case "rottentomatoesid": + var rtId = reader.ReadElementContentAsString(); + if (!string.IsNullOrWhiteSpace(rtId)) + { + item.SetProviderId(MetadataProviders.RottenTomatoes, rtId); + } + break; + + case "tmdbid": + var tmdb = reader.ReadElementContentAsString(); + if (!string.IsNullOrWhiteSpace(tmdb)) + { + item.SetProviderId(MetadataProviders.Tmdb, tmdb); + } + break; + + case "collectionnumber": + var tmdbCollection = reader.ReadElementContentAsString(); + if (!string.IsNullOrWhiteSpace(tmdbCollection)) + { + item.SetProviderId(MetadataProviders.TmdbCollection, tmdbCollection); + } + break; + + case "tvcomid": + var TVcomId = reader.ReadElementContentAsString(); + if (!string.IsNullOrWhiteSpace(TVcomId)) + { + item.SetProviderId(MetadataProviders.Tvcom, TVcomId); + } + break; + + case "zap2itid": + var zap2ItId = reader.ReadElementContentAsString(); + if (!string.IsNullOrWhiteSpace(zap2ItId)) + { + item.SetProviderId(MetadataProviders.Zap2It, zap2ItId); + } + break; + + case "imdb_id": + case "imdbid": + var imDbId = reader.ReadElementContentAsString(); + if (!string.IsNullOrWhiteSpace(imDbId)) + { + item.SetProviderId(MetadataProviders.Imdb, imDbId); + } + break; + + case "genre": + { + var val = reader.ReadElementContentAsString(); + if (!string.IsNullOrWhiteSpace(val)) + { + item.AddGenre(val); + } + break; + } + + case "style": + case "tag": + { + var val = reader.ReadElementContentAsString(); + if (!string.IsNullOrWhiteSpace(val)) + { + var hasTags = item as IHasTags; + if (hasTags != null) + { + hasTags.AddTag(val); + } + } + break; + } + + case "plotkeyword": + { + var val = reader.ReadElementContentAsString(); + + var hasKeywords = item as IHasKeywords; + if (hasKeywords != null) + { + if (!string.IsNullOrWhiteSpace(val)) + { + hasKeywords.AddKeyword(val); + } + } + break; + } + + case "fileinfo": + { + using (var subtree = reader.ReadSubtree()) + { + FetchFromFileInfoNode(subtree, item); + } + break; + } + + default: + reader.Skip(); + break; + } + } + + private void FetchFromFileInfoNode(XmlReader reader, T item) + { + reader.MoveToContent(); + + while (reader.Read()) + { + if (reader.NodeType == XmlNodeType.Element) + { + switch (reader.Name) + { + case "streamdetails": + { + using (var subtree = reader.ReadSubtree()) + { + FetchFromStreamDetailsNode(subtree, item); + } + break; + } + + default: + reader.Skip(); + break; + } + } + } + } + + private void FetchFromStreamDetailsNode(XmlReader reader, T item) + { + reader.MoveToContent(); + + while (reader.Read()) + { + if (reader.NodeType == XmlNodeType.Element) + { + switch (reader.Name) + { + case "video": + { + using (var subtree = reader.ReadSubtree()) + { + FetchFromVideoNode(subtree, item); + } + break; + } + + default: + reader.Skip(); + break; + } + } + } + } + + private void FetchFromVideoNode(XmlReader reader, T item) + { + reader.MoveToContent(); + + while (reader.Read()) + { + if (reader.NodeType == XmlNodeType.Element) + { + switch (reader.Name) + { + case "format3d": + { + var video = item as Video; + + if (video != null) + { + var val = reader.ReadElementContentAsString(); + + if (string.Equals("HSBS", val, StringComparison.CurrentCulture)) + { + video.Video3DFormat = Video3DFormat.HalfSideBySide; + } + else if (string.Equals("HTAB", val, StringComparison.CurrentCulture)) + { + video.Video3DFormat = Video3DFormat.HalfTopAndBottom; + } + else if (string.Equals("FTAB", val, StringComparison.CurrentCulture)) + { + video.Video3DFormat = Video3DFormat.FullTopAndBottom; + } + else if (string.Equals("FSBS", val, StringComparison.CurrentCulture)) + { + video.Video3DFormat = Video3DFormat.FullSideBySide; + } + } + break; + } + + default: + reader.Skip(); + break; + } + } + } + } + + /// <summary> + /// Gets the persons from XML node. + /// </summary> + /// <param name="reader">The reader.</param> + /// <returns>IEnumerable{PersonInfo}.</returns> + private PersonInfo GetPersonFromXmlNode(XmlReader reader) + { + var name = string.Empty; + var type = PersonType.Actor; // If type is not specified assume actor + var role = string.Empty; + int? sortOrder = null; + + reader.MoveToContent(); + + while (reader.Read()) + { + if (reader.NodeType == XmlNodeType.Element) + { + switch (reader.Name) + { + case "name": + name = reader.ReadElementContentAsString() ?? string.Empty; + break; + + case "type": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + type = val; + } + break; + } + + case "role": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + role = val; + } + break; + } + case "sortorder": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + int intVal; + if (int.TryParse(val, NumberStyles.Integer, _usCulture, out intVal)) + { + sortOrder = intVal; + } + } + break; + } + + default: + reader.Skip(); + break; + } + } + } + + return new PersonInfo + { + Name = name.Trim(), + Role = role, + Type = type, + SortOrder = sortOrder + }; + } + + /// <summary> + /// Used to split names of comma or pipe delimeted genres and people + /// </summary> + /// <param name="value">The value.</param> + /// <returns>IEnumerable{System.String}.</returns> + private IEnumerable<string> SplitNames(string value) + { + value = value ?? string.Empty; + + // Only split by comma if there is no pipe in the string + // We have to be careful to not split names like Matthew, Jr. + var separator = value.IndexOf('|') == -1 && value.IndexOf(';') == -1 ? new[] { ',' } : new[] { '|', ';' }; + + value = value.Trim().Trim(separator); + + return string.IsNullOrWhiteSpace(value) ? new string[] { } : Split(value, separator, StringSplitOptions.RemoveEmptyEntries); + } + + /// <summary> + /// Provides an additional overload for string.split + /// </summary> + /// <param name="val">The val.</param> + /// <param name="separators">The separators.</param> + /// <param name="options">The options.</param> + /// <returns>System.String[][].</returns> + private static string[] Split(string val, char[] separators, StringSplitOptions options) + { + return val.Split(separators, options); + } + } +} diff --git a/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs new file mode 100644 index 000000000..ba7b48b1f --- /dev/null +++ b/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs @@ -0,0 +1,211 @@ +using MediaBrowser.Common.Configuration; +using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Logging; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Threading; +using System.Xml; + +namespace MediaBrowser.XbmcMetadata.Parsers +{ + public class EpisodeNfoParser : BaseNfoParser<Episode> + { + private List<LocalImageInfo> _imagesFound; + private List<ChapterInfo> _chaptersFound; + private string _xmlPath; + + public EpisodeNfoParser(ILogger logger, IConfigurationManager config) : base(logger, config) + { + } + + public void Fetch(Episode item, + List<LocalImageInfo> images, + List<ChapterInfo> chapters, + string metadataFile, + CancellationToken cancellationToken) + { + _imagesFound = images; + _chaptersFound = chapters; + _xmlPath = metadataFile; + + Fetch(item, metadataFile, cancellationToken); + } + + private static readonly CultureInfo UsCulture = new CultureInfo("en-US"); + + /// <summary> + /// Fetches the data from XML node. + /// </summary> + /// <param name="reader">The reader.</param> + /// <param name="item">The item.</param> + protected override void FetchDataFromXmlNode(XmlReader reader, Episode item) + { + switch (reader.Name) + { + //case "Chapters": + + // _chaptersFound.AddRange(FetchChaptersFromXmlNode(item, reader.ReadSubtree())); + // break; + + case "season": + { + var number = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(number)) + { + int num; + + if (int.TryParse(number, out num)) + { + item.ParentIndexNumber = num; + } + } + break; + } + + case "episode": + { + var number = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(number)) + { + int num; + + if (int.TryParse(number, out num)) + { + item.IndexNumber = num; + } + } + break; + } + + case "episodenumberend": + { + var number = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(number)) + { + int num; + + if (int.TryParse(number, out num)) + { + item.IndexNumberEnd = num; + } + } + break; + } + + case "absolute_number": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + int rval; + + // int.TryParse is local aware, so it can be probamatic, force us culture + if (int.TryParse(val, NumberStyles.Integer, UsCulture, out rval)) + { + item.AbsoluteEpisodeNumber = rval; + } + } + + break; + } + case "DVD_episodenumber": + { + var number = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(number)) + { + float num; + + if (float.TryParse(number, NumberStyles.Any, UsCulture, out num)) + { + item.DvdEpisodeNumber = num; + } + } + break; + } + + case "DVD_season": + { + var number = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(number)) + { + float num; + + if (float.TryParse(number, NumberStyles.Any, UsCulture, out num)) + { + item.DvdSeasonNumber = Convert.ToInt32(num); + } + } + break; + } + + case "airsbefore_episode": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + int rval; + + // int.TryParse is local aware, so it can be probamatic, force us culture + if (int.TryParse(val, NumberStyles.Integer, UsCulture, out rval)) + { + item.AirsBeforeEpisodeNumber = rval; + } + } + + break; + } + + case "airsafter_season": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + int rval; + + // int.TryParse is local aware, so it can be probamatic, force us culture + if (int.TryParse(val, NumberStyles.Integer, UsCulture, out rval)) + { + item.AirsAfterSeasonNumber = rval; + } + } + + break; + } + + case "airsbefore_season": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + int rval; + + // int.TryParse is local aware, so it can be probamatic, force us culture + if (int.TryParse(val, NumberStyles.Integer, UsCulture, out rval)) + { + item.AirsBeforeSeasonNumber = rval; + } + } + + break; + } + + + default: + base.FetchDataFromXmlNode(reader, item); + break; + } + } + } +} diff --git a/MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs new file mode 100644 index 000000000..9b90b2bb3 --- /dev/null +++ b/MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs @@ -0,0 +1,97 @@ +using MediaBrowser.Common.Configuration; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.Movies; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Logging; +using System.Collections.Generic; +using System.Threading; +using System.Xml; + +namespace MediaBrowser.XbmcMetadata.Parsers +{ + class MovieNfoParser : BaseNfoParser<Video> + { + private List<ChapterInfo> _chaptersFound; + + public MovieNfoParser(ILogger logger, IConfigurationManager config) : base(logger, config) + { + } + + public void Fetch(Video item, + List<ChapterInfo> chapters, + string metadataFile, + CancellationToken cancellationToken) + { + _chaptersFound = chapters; + + Fetch(item, metadataFile, cancellationToken); + } + + /// <summary> + /// Fetches the data from XML node. + /// </summary> + /// <param name="reader">The reader.</param> + /// <param name="item">The item.</param> + protected override void FetchDataFromXmlNode(XmlReader reader, Video item) + { + switch (reader.Name) + { + case "id": + var id = reader.ReadElementContentAsString(); + if (!string.IsNullOrWhiteSpace(id)) + { + item.SetProviderId(MetadataProviders.Imdb, id); + } + break; + + case "set": + { + var val = reader.ReadElementContentAsString(); + var movie = item as Movie; + + if (!string.IsNullOrWhiteSpace(val) && movie != null) + { + movie.TmdbCollectionName = val; + } + + break; + } + + case "artist": + { + var val = reader.ReadElementContentAsString(); + var movie = item as MusicVideo; + + if (!string.IsNullOrWhiteSpace(val) && movie != null) + { + movie.Artist = val; + } + + break; + } + + case "album": + { + var val = reader.ReadElementContentAsString(); + var movie = item as MusicVideo; + + if (!string.IsNullOrWhiteSpace(val) && movie != null) + { + movie.Album = val; + } + + break; + } + + //case "chapter": + + // _chaptersFound.AddRange(FetchChaptersFromXmlNode(item, reader.ReadSubtree())); + // break; + + default: + base.FetchDataFromXmlNode(reader, item); + break; + } + } + } +} diff --git a/MediaBrowser.XbmcMetadata/Parsers/SeasonNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/SeasonNfoParser.cs new file mode 100644 index 000000000..14abb74c6 --- /dev/null +++ b/MediaBrowser.XbmcMetadata/Parsers/SeasonNfoParser.cs @@ -0,0 +1,45 @@ +using MediaBrowser.Common.Configuration; +using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Model.Logging; +using System.Xml; + +namespace MediaBrowser.XbmcMetadata.Parsers +{ + public class SeasonNfoParser : BaseNfoParser<Season> + { + public SeasonNfoParser(ILogger logger, IConfigurationManager config) : base(logger, config) + { + } + + /// <summary> + /// Fetches the data from XML node. + /// </summary> + /// <param name="reader">The reader.</param> + /// <param name="item">The item.</param> + protected override void FetchDataFromXmlNode(XmlReader reader, Season item) + { + switch (reader.Name) + { + case "seasonnumber": + { + var number = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(number)) + { + int num; + + if (int.TryParse(number, out num)) + { + item.IndexNumber = num; + } + } + break; + } + + default: + base.FetchDataFromXmlNode(reader, item); + break; + } + } + } +} diff --git a/MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs new file mode 100644 index 000000000..d72a64b5b --- /dev/null +++ b/MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs @@ -0,0 +1,93 @@ +using MediaBrowser.Common.Configuration; +using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.Library; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Logging; +using System; +using System.Xml; + +namespace MediaBrowser.XbmcMetadata.Parsers +{ + public class SeriesNfoParser : BaseNfoParser<Series> + { + public SeriesNfoParser(ILogger logger, IConfigurationManager config) : base(logger, config) + { + } + + /// <summary> + /// Fetches the data from XML node. + /// </summary> + /// <param name="reader">The reader.</param> + /// <param name="item">The item.</param> + protected override void FetchDataFromXmlNode(XmlReader reader, Series item) + { + switch (reader.Name) + { + case "id": + string id = reader.ReadElementContentAsString(); + if (!string.IsNullOrWhiteSpace(id)) + { + item.SetProviderId(MetadataProviders.Tvdb, id); + } + break; + + case "airs_dayofweek": + { + item.AirDays = TVUtils.GetAirDays(reader.ReadElementContentAsString()); + break; + } + + case "airs_time": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + item.AirTime = val; + } + break; + } + + case "animeseriesindex": + { + var number = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(number)) + { + int num; + + if (int.TryParse(number, out num)) + { + item.AnimeSeriesIndex = num; + } + } + break; + } + + case "status": + { + var status = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(status)) + { + SeriesStatus seriesStatus; + if (Enum.TryParse(status, true, out seriesStatus)) + { + item.Status = seriesStatus; + } + else + { + Logger.Info("Unrecognized series status: " + status); + } + } + + break; + } + + default: + base.FetchDataFromXmlNode(reader, item); + break; + } + } + } +} diff --git a/MediaBrowser.XbmcMetadata/Properties/AssemblyInfo.cs b/MediaBrowser.XbmcMetadata/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..03829798e --- /dev/null +++ b/MediaBrowser.XbmcMetadata/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +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("MediaBrowser.XbmcMetadata")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("MediaBrowser.XbmcMetadata")] +[assembly: AssemblyCopyright("Copyright © 2014")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// 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)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("bbdf434b-65f1-4178-8ddf-067447df3e20")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/MediaBrowser.XbmcMetadata/Providers/AlbumNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/AlbumNfoProvider.cs new file mode 100644 index 000000000..535fa28b8 --- /dev/null +++ b/MediaBrowser.XbmcMetadata/Providers/AlbumNfoProvider.cs @@ -0,0 +1,34 @@ +using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.Entities.Audio; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Logging; +using MediaBrowser.XbmcMetadata.Parsers; +using System.IO; +using System.Threading; + +namespace MediaBrowser.XbmcMetadata.Providers +{ + public class AlbumNfoProvider : BaseNfoProvider<MusicAlbum> + { + private readonly ILogger _logger; + private readonly IConfigurationManager _config; + + public AlbumNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config) + : base(fileSystem) + { + _logger = logger; + _config = config; + } + + protected override void Fetch(LocalMetadataResult<MusicAlbum> result, string path, CancellationToken cancellationToken) + { + new BaseNfoParser<MusicAlbum>(_logger, _config).Fetch(result.Item, path, cancellationToken); + } + + protected override FileSystemInfo GetXmlFile(ItemInfo info, IDirectoryService directoryService) + { + return directoryService.GetFile(Path.Combine(info.Path, "album.nfo")); + } + } +} diff --git a/MediaBrowser.XbmcMetadata/Providers/ArtistNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/ArtistNfoProvider.cs new file mode 100644 index 000000000..f281c1048 --- /dev/null +++ b/MediaBrowser.XbmcMetadata/Providers/ArtistNfoProvider.cs @@ -0,0 +1,34 @@ +using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.Entities.Audio; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Logging; +using MediaBrowser.XbmcMetadata.Parsers; +using System.IO; +using System.Threading; + +namespace MediaBrowser.XbmcMetadata.Providers +{ + public class ArtistNfoProvider : BaseNfoProvider<MusicArtist> + { + private readonly ILogger _logger; + private readonly IConfigurationManager _config; + + public ArtistNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config) + : base(fileSystem) + { + _logger = logger; + _config = config; + } + + protected override void Fetch(LocalMetadataResult<MusicArtist> result, string path, CancellationToken cancellationToken) + { + new BaseNfoParser<MusicArtist>(_logger, _config).Fetch(result.Item, path, cancellationToken); + } + + protected override FileSystemInfo GetXmlFile(ItemInfo info, IDirectoryService directoryService) + { + return directoryService.GetFile(Path.Combine(info.Path, "artist.nfo")); + } + } +} diff --git a/MediaBrowser.XbmcMetadata/Providers/BaseNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/BaseNfoProvider.cs new file mode 100644 index 000000000..65648e1d0 --- /dev/null +++ b/MediaBrowser.XbmcMetadata/Providers/BaseNfoProvider.cs @@ -0,0 +1,89 @@ +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Logging; +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.XbmcMetadata.Providers +{ + public abstract class BaseNfoProvider<T> : ILocalMetadataProvider<T>, IHasChangeMonitor + where T : IHasMetadata, new() + { + protected IFileSystem FileSystem; + + public async Task<LocalMetadataResult<T>> GetMetadata(ItemInfo info, CancellationToken cancellationToken) + { + var result = new LocalMetadataResult<T>(); + + var file = GetXmlFile(info, new DirectoryService(new NullLogger())); + + if (file == null) + { + return result; + } + + var path = file.FullName; + + await XmlProviderUtils.XmlParsingResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false); + + try + { + result.Item = new T(); + + Fetch(result, path, cancellationToken); + result.HasMetadata = true; + } + catch (FileNotFoundException) + { + result.HasMetadata = false; + } + catch (DirectoryNotFoundException) + { + result.HasMetadata = false; + } + finally + { + XmlProviderUtils.XmlParsingResourcePool.Release(); + } + + return result; + } + + protected abstract void Fetch(LocalMetadataResult<T> result, string path, CancellationToken cancellationToken); + + protected BaseNfoProvider(IFileSystem fileSystem) + { + FileSystem = fileSystem; + } + + protected abstract FileSystemInfo GetXmlFile(ItemInfo info, IDirectoryService directoryService); + + public bool HasChanged(IHasMetadata item, IDirectoryService directoryService, DateTime date) + { + var file = GetXmlFile(new ItemInfo { IsInMixedFolder = item.IsInMixedFolder, Path = item.Path }, directoryService); + + if (file == null) + { + return false; + } + + return file.Exists && FileSystem.GetLastWriteTimeUtc(file) > date; + } + + public string Name + { + get + { + return "Xbmc Nfo"; + } + } + } + + static class XmlProviderUtils + { + internal static readonly SemaphoreSlim XmlParsingResourcePool = new SemaphoreSlim(4, 4); + } +} diff --git a/MediaBrowser.XbmcMetadata/Providers/BaseVideoNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/BaseVideoNfoProvider.cs new file mode 100644 index 000000000..d51c44ad4 --- /dev/null +++ b/MediaBrowser.XbmcMetadata/Providers/BaseVideoNfoProvider.cs @@ -0,0 +1,55 @@ +using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Logging; +using MediaBrowser.XbmcMetadata.Parsers; +using System.Collections.Generic; +using System.IO; +using System.Threading; + +namespace MediaBrowser.XbmcMetadata.Providers +{ + public class BaseVideoNfoProvider<T> : BaseNfoProvider<T> + where T : Video, new () + { + private readonly ILogger _logger; + private readonly IConfigurationManager _config; + + public BaseVideoNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config) + : base(fileSystem) + { + _logger = logger; + _config = config; + } + + protected override void Fetch(LocalMetadataResult<T> result, string path, CancellationToken cancellationToken) + { + var chapters = new List<ChapterInfo>(); + + new MovieNfoParser(_logger, _config).Fetch(result.Item, chapters, path, cancellationToken); + + result.Chapters = chapters; + } + + protected override FileSystemInfo GetXmlFile(ItemInfo info, IDirectoryService directoryService) + { + var path = GetMovieSavePath(info); + + return directoryService.GetFile(path); + } + + public static string GetMovieSavePath(ItemInfo item) + { + if (Directory.Exists(item.Path)) + { + var path = item.Path; + + return Path.Combine(path, Path.GetFileNameWithoutExtension(path) + ".nfo"); + } + + return Path.ChangeExtension(item.Path, ".nfo"); + } + } +}
\ No newline at end of file diff --git a/MediaBrowser.XbmcMetadata/Providers/EpisodeNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/EpisodeNfoProvider.cs new file mode 100644 index 000000000..7899305e3 --- /dev/null +++ b/MediaBrowser.XbmcMetadata/Providers/EpisodeNfoProvider.cs @@ -0,0 +1,44 @@ +using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Logging; +using MediaBrowser.XbmcMetadata.Parsers; +using System.Collections.Generic; +using System.IO; +using System.Threading; + +namespace MediaBrowser.XbmcMetadata.Providers +{ + public class EpisodeNfoProvider : BaseNfoProvider<Episode> + { + private readonly ILogger _logger; + private readonly IConfigurationManager _config; + + public EpisodeNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config) + : base(fileSystem) + { + _logger = logger; + _config = config; + } + + protected override void Fetch(LocalMetadataResult<Episode> result, string path, CancellationToken cancellationToken) + { + var images = new List<LocalImageInfo>(); + var chapters = new List<ChapterInfo>(); + + new EpisodeNfoParser(_logger, _config).Fetch(result.Item, images, chapters, path, cancellationToken); + + result.Images = images; + result.Chapters = chapters; + } + + protected override FileSystemInfo GetXmlFile(ItemInfo info, IDirectoryService directoryService) + { + var path = Path.ChangeExtension(info.Path, ".nfo"); + + return directoryService.GetFile(path); + } + } +} diff --git a/MediaBrowser.XbmcMetadata/Providers/MovieNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/MovieNfoProvider.cs new file mode 100644 index 000000000..bd17c8539 --- /dev/null +++ b/MediaBrowser.XbmcMetadata/Providers/MovieNfoProvider.cs @@ -0,0 +1,45 @@ +using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.Movies; +using MediaBrowser.Model.Logging; + +namespace MediaBrowser.XbmcMetadata.Providers +{ + public class MovieNfoProvider : BaseVideoNfoProvider<Movie> + { + public MovieNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config) : base(fileSystem, logger, config) + { + } + } + + public class MusicVideoNfoProvider : BaseVideoNfoProvider<MusicVideo> + { + public MusicVideoNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config) : base(fileSystem, logger, config) + { + } + } + + public class AdultVideoNfoProvider : BaseVideoNfoProvider<AdultVideo> + { + public AdultVideoNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config) : base(fileSystem, logger, config) + { + } + } + + public class VideoNfoProvider : BaseVideoNfoProvider<Video> + { + public VideoNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config) : base(fileSystem, logger, config) + { + } + } + + public class TrailerNfoProvider : BaseVideoNfoProvider<Trailer> + { + public TrailerNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config) + : base(fileSystem, logger, config) + { + } + } + +}
\ No newline at end of file diff --git a/MediaBrowser.XbmcMetadata/Providers/SeasonNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/SeasonNfoProvider.cs new file mode 100644 index 000000000..7b68ed94b --- /dev/null +++ b/MediaBrowser.XbmcMetadata/Providers/SeasonNfoProvider.cs @@ -0,0 +1,35 @@ +using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Logging; +using MediaBrowser.XbmcMetadata.Parsers; +using System.IO; +using System.Threading; + +namespace MediaBrowser.XbmcMetadata.Providers +{ + public class SeasonNfoProvider : BaseNfoProvider<Season> + { + private readonly ILogger _logger; + private readonly IConfigurationManager _config; + + public SeasonNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config) + : base(fileSystem) + { + _logger = logger; + _config = config; + } + + protected override void Fetch(LocalMetadataResult<Season> result, string path, CancellationToken cancellationToken) + { + new SeasonNfoParser(_logger, _config).Fetch(result.Item, path, cancellationToken); + } + + protected override FileSystemInfo GetXmlFile(ItemInfo info, IDirectoryService directoryService) + { + return directoryService.GetFile(Path.Combine(info.Path, "season.nfo")); + } + } +} + diff --git a/MediaBrowser.XbmcMetadata/Providers/SeriesNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/SeriesNfoProvider.cs new file mode 100644 index 000000000..4a71d9fd3 --- /dev/null +++ b/MediaBrowser.XbmcMetadata/Providers/SeriesNfoProvider.cs @@ -0,0 +1,34 @@ +using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Logging; +using MediaBrowser.XbmcMetadata.Parsers; +using System.IO; +using System.Threading; + +namespace MediaBrowser.XbmcMetadata.Providers +{ + public class SeriesNfoProvider : BaseNfoProvider<Series> + { + private readonly ILogger _logger; + private readonly IConfigurationManager _config; + + public SeriesNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config) + : base(fileSystem) + { + _logger = logger; + _config = config; + } + + protected override void Fetch(LocalMetadataResult<Series> result, string path, CancellationToken cancellationToken) + { + new SeriesNfoParser(_logger, _config).Fetch(result.Item, path, cancellationToken); + } + + protected override FileSystemInfo GetXmlFile(ItemInfo info, IDirectoryService directoryService) + { + return directoryService.GetFile(Path.Combine(info.Path, "series.nfo")); + } + } +} diff --git a/MediaBrowser.XbmcMetadata/Savers/AlbumXmlSaver.cs b/MediaBrowser.XbmcMetadata/Savers/AlbumXmlSaver.cs new file mode 100644 index 000000000..121514cdd --- /dev/null +++ b/MediaBrowser.XbmcMetadata/Savers/AlbumXmlSaver.cs @@ -0,0 +1,143 @@ +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.Audio; +using MediaBrowser.Controller.Library; +using MediaBrowser.Model.Entities; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Security; +using System.Text; +using System.Threading; + +namespace MediaBrowser.XbmcMetadata.Savers +{ + public class AlbumXmlSaver : IMetadataFileSaver + { + private readonly ILibraryManager _libraryManager; + private readonly IUserManager _userManager; + private readonly IUserDataManager _userDataRepo; + + private readonly IFileSystem _fileSystem; + private readonly IServerConfigurationManager _config; + + public AlbumXmlSaver(ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataRepo, IFileSystem fileSystem, IServerConfigurationManager config) + { + _libraryManager = libraryManager; + _userManager = userManager; + _userDataRepo = userDataRepo; + _fileSystem = fileSystem; + _config = config; + } + + public string Name + { + get + { + return "Xbmc Nfo"; + } + } + + public string GetSavePath(IHasMetadata item) + { + return Path.Combine(item.Path, "album.nfo"); + } + + public void Save(IHasMetadata item, CancellationToken cancellationToken) + { + var album = (MusicAlbum)item; + + var builder = new StringBuilder(); + + builder.Append("<album>"); + + XmlSaverHelpers.AddCommonNodes(album, builder, _libraryManager, _userManager, _userDataRepo, _fileSystem, _config); + + var tracks = album.RecursiveChildren + .OfType<Audio>() + .ToList(); + + var artists = tracks + .SelectMany(i => + { + var list = new List<string>(); + + if (!string.IsNullOrEmpty(i.AlbumArtist)) + { + list.Add(i.AlbumArtist); + } + list.AddRange(i.Artists); + + return list; + }) + .Distinct(StringComparer.OrdinalIgnoreCase); + + foreach (var artist in artists) + { + builder.Append("<artist>" + SecurityElement.Escape(artist) + "</artist>"); + } + + AddTracks(tracks, builder); + + builder.Append("</album>"); + + var xmlFilePath = GetSavePath(item); + + XmlSaverHelpers.Save(builder, xmlFilePath, new List<string> + { + "track", + "artist" + }); + } + + public bool IsEnabledFor(IHasMetadata item, ItemUpdateType updateType) + { + var locationType = item.LocationType; + if (locationType == LocationType.Remote || locationType == LocationType.Virtual) + { + return false; + } + + // If new metadata has been downloaded or metadata was manually edited, proceed + if ((updateType & ItemUpdateType.MetadataDownload) == ItemUpdateType.MetadataDownload + || (updateType & ItemUpdateType.MetadataEdit) == ItemUpdateType.MetadataEdit) + { + return item is MusicAlbum; + } + + return false; + } + + private static readonly CultureInfo UsCulture = new CultureInfo("en-US"); + + private void AddTracks(IEnumerable<Audio> tracks, StringBuilder builder) + { + foreach (var track in tracks.OrderBy(i => i.ParentIndexNumber ?? 0).ThenBy(i => i.IndexNumber ?? 0)) + { + builder.Append("<track>"); + + if (track.IndexNumber.HasValue) + { + builder.Append("<position>" + SecurityElement.Escape(track.IndexNumber.Value.ToString(UsCulture)) + "</position>"); + } + + if (!string.IsNullOrEmpty(track.Name)) + { + builder.Append("<title>" + SecurityElement.Escape(track.Name) + "</title>"); + } + + if (track.RunTimeTicks.HasValue) + { + var time = TimeSpan.FromTicks(track.RunTimeTicks.Value).ToString(@"mm\:ss"); + + builder.Append("<duration>" + SecurityElement.Escape(time) + "</duration>"); + } + + builder.Append("</track>"); + } + } + } +} diff --git a/MediaBrowser.XbmcMetadata/Savers/ArtistXmlSaver.cs b/MediaBrowser.XbmcMetadata/Savers/ArtistXmlSaver.cs new file mode 100644 index 000000000..f47785155 --- /dev/null +++ b/MediaBrowser.XbmcMetadata/Savers/ArtistXmlSaver.cs @@ -0,0 +1,124 @@ +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.Audio; +using MediaBrowser.Controller.Library; +using MediaBrowser.Model.Entities; +using MediaBrowser.XbmcMetadata.Configuration; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Security; +using System.Text; +using System.Threading; + +namespace MediaBrowser.XbmcMetadata.Savers +{ + public class ArtistXmlSaver : IMetadataFileSaver + { + private readonly ILibraryManager _libraryManager; + private readonly IUserManager _userManager; + private readonly IUserDataManager _userDataRepo; + + private readonly IFileSystem _fileSystem; + private readonly IServerConfigurationManager _config; + + private static readonly CultureInfo UsCulture = new CultureInfo("en-US"); + + public ArtistXmlSaver(ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataRepo, IFileSystem fileSystem, IServerConfigurationManager config) + { + _libraryManager = libraryManager; + _userManager = userManager; + _userDataRepo = userDataRepo; + _fileSystem = fileSystem; + _config = config; + } + + public string GetSavePath(IHasMetadata item) + { + return Path.Combine(item.Path, "artist.nfo"); + } + + public string Name + { + get + { + return "Xbmc Nfo"; + } + } + + public void Save(IHasMetadata item, CancellationToken cancellationToken) + { + var artist = (MusicArtist)item; + + var builder = new StringBuilder(); + + builder.Append("<artist>"); + + XmlSaverHelpers.AddCommonNodes(artist, builder, _libraryManager, _userManager, _userDataRepo, _fileSystem, _config); + + if (artist.EndDate.HasValue) + { + var formatString = _config.GetNfoConfiguration().ReleaseDateFormat; + + builder.Append("<disbanded>" + SecurityElement.Escape(artist.EndDate.Value.ToString(formatString)) + "</disbanded>"); + } + + var albums = artist + .RecursiveChildren + .OfType<MusicAlbum>() + .ToList(); + + AddAlbums(albums, builder); + + builder.Append("</artist>"); + + var xmlFilePath = GetSavePath(item); + + XmlSaverHelpers.Save(builder, xmlFilePath, new List<string> + { + "album", + "disbanded" + }); + } + + public bool IsEnabledFor(IHasMetadata item, ItemUpdateType updateType) + { + var locationType = item.LocationType; + if (locationType == LocationType.Remote || locationType == LocationType.Virtual) + { + return false; + } + + // If new metadata has been downloaded or metadata was manually edited, proceed + if ((updateType & ItemUpdateType.MetadataDownload) == ItemUpdateType.MetadataDownload + || (updateType & ItemUpdateType.MetadataEdit) == ItemUpdateType.MetadataEdit) + { + return item is MusicArtist; + } + + return false; + } + + private void AddAlbums(IEnumerable<MusicAlbum> albums, StringBuilder builder) + { + foreach (var album in albums) + { + builder.Append("<album>"); + + if (!string.IsNullOrEmpty(album.Name)) + { + builder.Append("<title>" + SecurityElement.Escape(album.Name) + "</title>"); + } + + if (album.ProductionYear.HasValue) + { + builder.Append("<year>" + SecurityElement.Escape(album.ProductionYear.Value.ToString(UsCulture)) + "</year>"); + } + + builder.Append("</album>"); + } + } + } +} diff --git a/MediaBrowser.XbmcMetadata/Savers/EpisodeXmlSaver.cs b/MediaBrowser.XbmcMetadata/Savers/EpisodeXmlSaver.cs new file mode 100644 index 000000000..a03eacdc4 --- /dev/null +++ b/MediaBrowser.XbmcMetadata/Savers/EpisodeXmlSaver.cs @@ -0,0 +1,149 @@ +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Security; +using System.Text; +using System.Threading; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.Library; +using MediaBrowser.Model.Entities; +using MediaBrowser.XbmcMetadata.Configuration; + +namespace MediaBrowser.XbmcMetadata.Savers +{ + public class EpisodeXmlSaver : IMetadataFileSaver + { + private readonly ILibraryManager _libraryManager; + private readonly IUserManager _userManager; + private readonly IUserDataManager _userDataRepo; + + private readonly CultureInfo _usCulture = new CultureInfo("en-US"); + + private readonly IFileSystem _fileSystem; + private readonly IServerConfigurationManager _config; + + public EpisodeXmlSaver(ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataRepo, IFileSystem fileSystem, IServerConfigurationManager config) + { + _libraryManager = libraryManager; + _userManager = userManager; + _userDataRepo = userDataRepo; + _fileSystem = fileSystem; + _config = config; + } + + public string Name + { + get + { + return "Xbmc Nfo"; + } + } + + public string GetSavePath(IHasMetadata item) + { + return Path.ChangeExtension(item.Path, ".nfo"); + } + + public void Save(IHasMetadata item, CancellationToken cancellationToken) + { + var episode = (Episode)item; + + var builder = new StringBuilder(); + + builder.Append("<episodedetails>"); + + XmlSaverHelpers.AddCommonNodes(episode, builder, _libraryManager, _userManager, _userDataRepo, _fileSystem, _config); + + if (episode.IndexNumber.HasValue) + { + builder.Append("<episode>" + episode.IndexNumber.Value.ToString(_usCulture) + "</episode>"); + } + + if (episode.IndexNumberEnd.HasValue) + { + builder.Append("<episodenumberend>" + SecurityElement.Escape(episode.IndexNumberEnd.Value.ToString(_usCulture)) + "</episodenumberend>"); + } + + if (episode.ParentIndexNumber.HasValue) + { + builder.Append("<season>" + episode.ParentIndexNumber.Value.ToString(_usCulture) + "</season>"); + } + + if (episode.PremiereDate.HasValue) + { + var formatString = _config.GetNfoConfiguration().ReleaseDateFormat; + + builder.Append("<aired>" + SecurityElement.Escape(episode.PremiereDate.Value.ToString(formatString)) + "</aired>"); + } + + if (episode.AirsAfterSeasonNumber.HasValue) + { + builder.Append("<airsafter_season>" + SecurityElement.Escape(episode.AirsAfterSeasonNumber.Value.ToString(_usCulture)) + "</airsafter_season>"); + } + if (episode.AirsBeforeEpisodeNumber.HasValue) + { + builder.Append("<airsbefore_episode>" + SecurityElement.Escape(episode.AirsBeforeEpisodeNumber.Value.ToString(_usCulture)) + "</airsbefore_episode>"); + } + if (episode.AirsBeforeSeasonNumber.HasValue) + { + builder.Append("<airsbefore_season>" + SecurityElement.Escape(episode.AirsBeforeSeasonNumber.Value.ToString(_usCulture)) + "</airsbefore_season>"); + } + + if (episode.DvdEpisodeNumber.HasValue) + { + builder.Append("<DVD_episodenumber>" + SecurityElement.Escape(episode.DvdEpisodeNumber.Value.ToString(_usCulture)) + "</DVD_episodenumber>"); + } + + if (episode.DvdSeasonNumber.HasValue) + { + builder.Append("<DVD_season>" + SecurityElement.Escape(episode.DvdSeasonNumber.Value.ToString(_usCulture)) + "</DVD_season>"); + } + + if (episode.AbsoluteEpisodeNumber.HasValue) + { + builder.Append("<absolute_number>" + SecurityElement.Escape(episode.AbsoluteEpisodeNumber.Value.ToString(_usCulture)) + "</absolute_number>"); + } + + XmlSaverHelpers.AddMediaInfo((Episode)item, builder); + + builder.Append("</episodedetails>"); + + var xmlFilePath = GetSavePath(item); + + XmlSaverHelpers.Save(builder, xmlFilePath, new List<string> + { + "aired", + "season", + "episode", + "episodenumberend", + "airsafter_season", + "airsbefore_episode", + "airsbefore_season", + "DVD_episodenumber", + "DVD_season", + "absolute_number" + }); + } + + public bool IsEnabledFor(IHasMetadata item, ItemUpdateType updateType) + { + var locationType = item.LocationType; + if (locationType == LocationType.Remote || locationType == LocationType.Virtual) + { + return false; + } + + // If new metadata has been downloaded or metadata was manually edited, proceed + if ((updateType & ItemUpdateType.MetadataDownload) == ItemUpdateType.MetadataDownload + || (updateType & ItemUpdateType.MetadataEdit) == ItemUpdateType.MetadataEdit) + { + return item is Episode; + } + + return false; + } + } +} diff --git a/MediaBrowser.XbmcMetadata/Savers/MovieXmlSaver.cs b/MediaBrowser.XbmcMetadata/Savers/MovieXmlSaver.cs new file mode 100644 index 000000000..586cefe65 --- /dev/null +++ b/MediaBrowser.XbmcMetadata/Savers/MovieXmlSaver.cs @@ -0,0 +1,143 @@ +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.Movies; +using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.Library; +using MediaBrowser.Model.Entities; +using System.Collections.Generic; +using System.IO; +using System.Security; +using System.Text; +using System.Threading; + +namespace MediaBrowser.XbmcMetadata.Savers +{ + public class MovieXmlSaver : IMetadataFileSaver + { + private readonly ILibraryManager _libraryManager; + private readonly IUserManager _userManager; + private readonly IUserDataManager _userDataRepo; + + private readonly IFileSystem _fileSystem; + private readonly IServerConfigurationManager _config; + + public MovieXmlSaver(ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataRepo, IFileSystem fileSystem, IServerConfigurationManager config) + { + _libraryManager = libraryManager; + _userManager = userManager; + _userDataRepo = userDataRepo; + _fileSystem = fileSystem; + _config = config; + } + + public string Name + { + get + { + return "Xbmc Nfo"; + } + } + + public string GetSavePath(IHasMetadata item) + { + return GetMovieSavePath(item); + } + + public static string GetMovieSavePath(IHasMetadata item) + { + var video = (Video)item; + + if (video.VideoType == VideoType.Dvd || video.VideoType == VideoType.BluRay || video.VideoType == VideoType.HdDvd) + { + var path = item.ContainingFolderPath; + + return Path.Combine(path, Path.GetFileNameWithoutExtension(path) + ".nfo"); + } + + return Path.ChangeExtension(item.Path, ".nfo"); + } + + public void Save(IHasMetadata item, CancellationToken cancellationToken) + { + var video = (Video)item; + + var builder = new StringBuilder(); + + var tag = item is MusicVideo ? "musicvideo" : "movie"; + + builder.Append("<" + tag + ">"); + + XmlSaverHelpers.AddCommonNodes(video, builder, _libraryManager, _userManager, _userDataRepo, _fileSystem, _config); + + var imdb = item.GetProviderId(MetadataProviders.Imdb); + + if (!string.IsNullOrEmpty(imdb)) + { + builder.Append("<id>" + SecurityElement.Escape(imdb) + "</id>"); + } + + var musicVideo = item as MusicVideo; + + if (musicVideo != null) + { + if (!string.IsNullOrEmpty(musicVideo.Artist)) + { + builder.Append("<artist>" + SecurityElement.Escape(musicVideo.Artist) + "</artist>"); + } + if (!string.IsNullOrEmpty(musicVideo.Album)) + { + builder.Append("<album>" + SecurityElement.Escape(musicVideo.Album) + "</album>"); + } + } + + var movie = item as Movie; + + if (movie != null) + { + if (!string.IsNullOrEmpty(movie.TmdbCollectionName)) + { + builder.Append("<set>" + SecurityElement.Escape(movie.TmdbCollectionName) + "</set>"); + } + } + + XmlSaverHelpers.AddMediaInfo((Video)item, builder); + + builder.Append("</" + tag + ">"); + + var xmlFilePath = GetSavePath(item); + + XmlSaverHelpers.Save(builder, xmlFilePath, new List<string> + { + "album", + "artist", + "set", + "id" + }); + } + + public bool IsEnabledFor(IHasMetadata item, ItemUpdateType updateType) + { + var locationType = item.LocationType; + if (locationType == LocationType.Remote || locationType == LocationType.Virtual) + { + return false; + } + + // If new metadata has been downloaded or metadata was manually edited, proceed + if ((updateType & ItemUpdateType.MetadataDownload) == ItemUpdateType.MetadataDownload + || (updateType & ItemUpdateType.MetadataEdit) == ItemUpdateType.MetadataEdit) + { + var video = item as Video; + + // Check parent for null to avoid running this against things like video backdrops + if (video != null && !(item is Episode) && !video.IsOwnedItem) + { + return true; + } + } + + return false; + } + } +} diff --git a/MediaBrowser.XbmcMetadata/Savers/SeasonXmlSaver.cs b/MediaBrowser.XbmcMetadata/Savers/SeasonXmlSaver.cs new file mode 100644 index 000000000..9de589e55 --- /dev/null +++ b/MediaBrowser.XbmcMetadata/Savers/SeasonXmlSaver.cs @@ -0,0 +1,90 @@ +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Security; +using System.Text; +using System.Threading; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.Library; +using MediaBrowser.Model.Entities; + +namespace MediaBrowser.XbmcMetadata.Savers +{ + public class SeasonXmlSaver : IMetadataFileSaver + { + private readonly ILibraryManager _libraryManager; + private readonly IUserManager _userManager; + private readonly IUserDataManager _userDataRepo; + + private readonly IFileSystem _fileSystem; + private readonly IServerConfigurationManager _config; + + public SeasonXmlSaver(ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataRepo, IFileSystem fileSystem, IServerConfigurationManager config) + { + _libraryManager = libraryManager; + _userManager = userManager; + _userDataRepo = userDataRepo; + _fileSystem = fileSystem; + _config = config; + } + + public string Name + { + get + { + return "Xbmc Nfo"; + } + } + + public string GetSavePath(IHasMetadata item) + { + return Path.Combine(item.Path, "season.nfo"); + } + + public void Save(IHasMetadata item, CancellationToken cancellationToken) + { + var builder = new StringBuilder(); + + builder.Append("<season>"); + + var season = (Season)item; + + if (season.IndexNumber.HasValue) + { + builder.Append("<seasonnumber>" + SecurityElement.Escape(season.IndexNumber.Value.ToString(CultureInfo.InvariantCulture)) + "</seasonnumber>"); + } + + XmlSaverHelpers.AddCommonNodes((Season)item, builder, _libraryManager, _userManager, _userDataRepo, _fileSystem, _config); + + builder.Append("</season>"); + + var xmlFilePath = GetSavePath(item); + + XmlSaverHelpers.Save(builder, xmlFilePath, new List<string> + { + "seasonnumber" + }); + } + + public bool IsEnabledFor(IHasMetadata item, ItemUpdateType updateType) + { + var locationType = item.LocationType; + if (locationType == LocationType.Remote || locationType == LocationType.Virtual) + { + return false; + } + + // If new metadata has been downloaded or metadata was manually edited, proceed + if ((updateType & ItemUpdateType.MetadataDownload) == ItemUpdateType.MetadataDownload + || (updateType & ItemUpdateType.MetadataEdit) == ItemUpdateType.MetadataEdit) + { + return item is Season; + } + + return false; + } + } +} diff --git a/MediaBrowser.XbmcMetadata/Savers/SeriesXmlSaver.cs b/MediaBrowser.XbmcMetadata/Savers/SeriesXmlSaver.cs new file mode 100644 index 000000000..9c99cc33d --- /dev/null +++ b/MediaBrowser.XbmcMetadata/Savers/SeriesXmlSaver.cs @@ -0,0 +1,130 @@ +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Security; +using System.Text; +using System.Threading; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.Library; +using MediaBrowser.Model.Entities; + +namespace MediaBrowser.XbmcMetadata.Savers +{ + public class SeriesXmlSaver : IMetadataFileSaver + { + private readonly ILibraryManager _libraryManager; + private readonly IUserManager _userManager; + private readonly IUserDataManager _userDataRepo; + + private readonly IFileSystem _fileSystem; + private readonly IServerConfigurationManager _config; + + public SeriesXmlSaver(IServerConfigurationManager config, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataRepo, IFileSystem fileSystem) + { + _config = config; + _libraryManager = libraryManager; + _userManager = userManager; + _userDataRepo = userDataRepo; + _fileSystem = fileSystem; + } + + public string Name + { + get + { + return "Xbmc Nfo"; + } + } + + public string GetSavePath(IHasMetadata item) + { + return Path.Combine(item.Path, "tvshow.nfo"); + } + + public void Save(IHasMetadata item, CancellationToken cancellationToken) + { + var series = (Series)item; + + var builder = new StringBuilder(); + + builder.Append("<tvshow>"); + + XmlSaverHelpers.AddCommonNodes(series, builder, _libraryManager, _userManager, _userDataRepo, _fileSystem, _config); + + var tvdb = item.GetProviderId(MetadataProviders.Tvdb); + + if (!string.IsNullOrEmpty(tvdb)) + { + builder.Append("<id>" + SecurityElement.Escape(tvdb) + "</id>"); + + builder.AppendFormat("<episodeguide><url cache=\"{0}.xml\">http://www.thetvdb.com/api/1D62F2F90030C444/series/{0}/all/{1}.zip</url></episodeguide>", + tvdb, + string.IsNullOrEmpty(_config.Configuration.PreferredMetadataLanguage) ? "en" : _config.Configuration.PreferredMetadataLanguage); + } + + builder.Append("<season>-1</season>"); + builder.Append("<episode>-1</episode>"); + + if (series.Status.HasValue) + { + builder.Append("<status>" + SecurityElement.Escape(series.Status.Value.ToString()) + "</status>"); + } + + if (!string.IsNullOrEmpty(series.AirTime)) + { + builder.Append("<airs_time>" + SecurityElement.Escape(series.AirTime) + "</airs_time>"); + } + + if (series.AirDays.Count == 7) + { + builder.Append("<airs_dayofweek>" + SecurityElement.Escape("Daily") + "</airs_dayofweek>"); + } + else if (series.AirDays.Count > 0) + { + builder.Append("<airs_dayofweek>" + SecurityElement.Escape(series.AirDays[0].ToString()) + "</airs_dayofweek>"); + } + + if (series.AnimeSeriesIndex.HasValue) + { + builder.Append("<animeseriesindex>" + SecurityElement.Escape(series.AnimeSeriesIndex.Value.ToString(CultureInfo.InvariantCulture)) + "</animeseriesindex>"); + } + + builder.Append("</tvshow>"); + + var xmlFilePath = GetSavePath(item); + + XmlSaverHelpers.Save(builder, xmlFilePath, new List<string> + { + "id", + "episodeguide", + "season", + "episode", + "status", + "airs_time", + "airs_dayofweek", + "animeseriesindex" + }); + } + + public bool IsEnabledFor(IHasMetadata item, ItemUpdateType updateType) + { + var locationType = item.LocationType; + if (locationType == LocationType.Remote || locationType == LocationType.Virtual) + { + return false; + } + + // If new metadata has been downloaded or metadata was manually edited, proceed + if ((updateType & ItemUpdateType.MetadataDownload) == ItemUpdateType.MetadataDownload + || (updateType & ItemUpdateType.MetadataEdit) == ItemUpdateType.MetadataEdit) + { + return item is Series; + } + + return false; + } + } +} diff --git a/MediaBrowser.XbmcMetadata/Savers/XmlSaverHelpers.cs b/MediaBrowser.XbmcMetadata/Savers/XmlSaverHelpers.cs new file mode 100644 index 000000000..85829b155 --- /dev/null +++ b/MediaBrowser.XbmcMetadata/Savers/XmlSaverHelpers.cs @@ -0,0 +1,906 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Security; +using System.Text; +using System.Xml; +using MediaBrowser.Common.Extensions; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.Audio; +using MediaBrowser.Controller.Entities.Movies; +using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Persistence; +using MediaBrowser.Model.Configuration; +using MediaBrowser.Model.Entities; +using MediaBrowser.XbmcMetadata.Configuration; + +namespace MediaBrowser.XbmcMetadata.Savers +{ + public static class XmlSaverHelpers + { + private static readonly CultureInfo UsCulture = new CultureInfo("en-US"); + + private static readonly Dictionary<string, string> CommonTags = new[] { + + "plot", + "customrating", + "lockdata", + "type", + "dateadded", + "title", + "rating", + "year", + "sorttitle", + "mpaa", + "mpaadescription", + "aspectratio", + "website", + "collectionnumber", + "tmdbid", + "rottentomatoesid", + "language", + "tvcomid", + "budget", + "revenue", + "tagline", + "studio", + "genre", + "tag", + "runtime", + "actor", + "criticratingsummary", + "criticrating", + "fileinfo", + "director", + "writer", + "trailer", + "premiered", + "releasedate", + "outline", + "id", + "votes", + "credits", + "originaltitle", + "watched", + "playcount", + "lastplayed", + "art", + "resume", + "biography", + "formed", + "review", + "style", + "imdbid", + "imdb_id", + "plotkeyword", + "country", + "audiodbalbumid", + "audiodbartistid", + "awardsummary", + "enddate", + "lockedfields", + "metascore", + "zap2itid", + "tvrageid", + "gamesdbid", + + "musicbrainzartistid", + "musicbrainzalbumartistid", + "musicbrainzalbumid", + "musicbrainzreleasegroupid", + "tvdbid", + "collectionitem" + + }.ToDictionary(i => i, StringComparer.OrdinalIgnoreCase); + + /// <summary> + /// Saves the specified XML. + /// </summary> + /// <param name="xml">The XML.</param> + /// <param name="path">The path.</param> + /// <param name="xmlTagsUsed">The XML tags used.</param> + public static void Save(StringBuilder xml, string path, List<string> xmlTagsUsed) + { + if (File.Exists(path)) + { + var tags = xmlTagsUsed.ToList(); + + var position = xml.ToString().LastIndexOf("</", StringComparison.OrdinalIgnoreCase); + xml.Insert(position, GetCustomTags(path, tags)); + } + + var xmlDocument = new XmlDocument(); + xmlDocument.LoadXml(xml.ToString()); + + //Add the new node to the document. + xmlDocument.InsertBefore(xmlDocument.CreateXmlDeclaration("1.0", "UTF-8", "yes"), xmlDocument.DocumentElement); + + Directory.CreateDirectory(Path.GetDirectoryName(path)); + + var wasHidden = false; + + var file = new FileInfo(path); + + // This will fail if the file is hidden + if (file.Exists) + { + if ((file.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden) + { + file.Attributes &= ~FileAttributes.Hidden; + + wasHidden = true; + } + } + + using (var filestream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read)) + { + using (var streamWriter = new StreamWriter(filestream, Encoding.UTF8)) + { + xmlDocument.Save(streamWriter); + } + } + + if (wasHidden) + { + file.Refresh(); + + // Add back the attribute + file.Attributes |= FileAttributes.Hidden; + } + } + + /// <summary> + /// Gets the custom tags. + /// </summary> + /// <param name="path">The path.</param> + /// <param name="xmlTagsUsed">The XML tags used.</param> + /// <returns>System.String.</returns> + private static string GetCustomTags(string path, List<string> xmlTagsUsed) + { + var settings = new XmlReaderSettings + { + CheckCharacters = false, + IgnoreProcessingInstructions = true, + IgnoreComments = true, + ValidationType = ValidationType.None + }; + + var builder = new StringBuilder(); + + using (var streamReader = new StreamReader(path, Encoding.UTF8)) + { + // Use XmlReader for best performance + using (var reader = XmlReader.Create(streamReader, settings)) + { + reader.MoveToContent(); + + // Loop through each element + while (reader.Read()) + { + if (reader.NodeType == XmlNodeType.Element) + { + var name = reader.Name; + + if (!CommonTags.ContainsKey(name) && !xmlTagsUsed.Contains(name, StringComparer.OrdinalIgnoreCase)) + { + builder.AppendLine(reader.ReadOuterXml()); + } + else + { + reader.Skip(); + } + } + } + } + } + + return builder.ToString(); + } + + public static void AddMediaInfo<T>(T item, StringBuilder builder) + where T : BaseItem, IHasMediaSources + { + builder.Append("<fileinfo>"); + builder.Append("<streamdetails>"); + + foreach (var stream in item.GetMediaSources(false).First().MediaStreams) + { + builder.Append("<" + stream.Type.ToString().ToLower() + ">"); + + if (!string.IsNullOrEmpty(stream.Codec)) + { + builder.Append("<codec>" + SecurityElement.Escape(stream.Codec) + "</codec>"); + builder.Append("<micodec>" + SecurityElement.Escape(stream.Codec) + "</micodec>"); + } + + if (stream.BitRate.HasValue) + { + builder.Append("<bitrate>" + stream.BitRate.Value.ToString(UsCulture) + "</bitrate>"); + } + + if (stream.Width.HasValue) + { + builder.Append("<width>" + stream.Width.Value.ToString(UsCulture) + "</width>"); + } + + if (stream.Height.HasValue) + { + builder.Append("<height>" + stream.Height.Value.ToString(UsCulture) + "</height>"); + } + + if (!string.IsNullOrEmpty(stream.AspectRatio)) + { + builder.Append("<aspect>" + SecurityElement.Escape(stream.AspectRatio) + "</aspect>"); + builder.Append("<aspectratio>" + SecurityElement.Escape(stream.AspectRatio) + "</aspectratio>"); + } + + var framerate = stream.AverageFrameRate ?? stream.RealFrameRate; + + if (framerate.HasValue) + { + builder.Append("<framerate>" + framerate.Value.ToString(UsCulture) + "</framerate>"); + } + + if (!string.IsNullOrEmpty(stream.Language)) + { + builder.Append("<language>" + SecurityElement.Escape(stream.Language) + "</language>"); + } + + var scanType = stream.IsInterlaced ? "interlaced" : "progressive"; + if (!string.IsNullOrEmpty(scanType)) + { + builder.Append("<scantype>" + SecurityElement.Escape(scanType) + "</scantype>"); + } + + if (stream.Channels.HasValue) + { + builder.Append("<channels>" + stream.Channels.Value.ToString(UsCulture) + "</channels>"); + } + + if (stream.SampleRate.HasValue) + { + builder.Append("<samplingrate>" + stream.SampleRate.Value.ToString(UsCulture) + "</samplingrate>"); + } + + builder.Append("<default>" + SecurityElement.Escape(stream.IsDefault.ToString()) + "</default>"); + builder.Append("<forced>" + SecurityElement.Escape(stream.IsForced.ToString()) + "</forced>"); + + if (stream.Type == MediaStreamType.Video) + { + if (item.RunTimeTicks.HasValue) + { + var timespan = TimeSpan.FromTicks(item.RunTimeTicks.Value); + + builder.Append("<duration>" + Convert.ToInt32(timespan.TotalMinutes).ToString(UsCulture) + "</duration>"); + builder.Append("<durationinseconds>" + Convert.ToInt32(timespan.TotalSeconds).ToString(UsCulture) + "</durationinseconds>"); + } + + var video = item as Video; + + if (video != null) + { + //AddChapters(video, builder, itemRepository); + + if (video.Video3DFormat.HasValue) + { + switch (video.Video3DFormat.Value) + { + case Video3DFormat.FullSideBySide: + builder.Append("<format3d>FSBS</format3d>"); + break; + case Video3DFormat.FullTopAndBottom: + builder.Append("<format3d>FTAB</format3d>"); + break; + case Video3DFormat.HalfSideBySide: + builder.Append("<format3d>HSBS</format3d>"); + break; + case Video3DFormat.HalfTopAndBottom: + builder.Append("<format3d>HTAB</format3d>"); + break; + } + } + } + } + + builder.Append("</" + stream.Type.ToString().ToLower() + ">"); + } + + builder.Append("</streamdetails>"); + builder.Append("</fileinfo>"); + } + + /// <summary> + /// Adds the common nodes. + /// </summary> + /// <returns>Task.</returns> + public static void AddCommonNodes(BaseItem item, StringBuilder builder, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataRepo, IFileSystem fileSystem, IServerConfigurationManager config) + { + var overview = (item.Overview ?? string.Empty) + .StripHtml() + .Replace(""", "'"); + + var options = config.GetNfoConfiguration(); + + if (item is MusicArtist) + { + builder.Append("<biography><![CDATA[" + overview + "]]></biography>"); + } + else if (item is MusicAlbum) + { + builder.Append("<review><![CDATA[" + overview + "]]></review>"); + } + else + { + builder.Append("<plot><![CDATA[" + overview + "]]></plot>"); + } + + var hasShortOverview = item as IHasShortOverview; + if (hasShortOverview != null) + { + var outline = (hasShortOverview.ShortOverview ?? string.Empty) + .StripHtml() + .Replace(""", "'"); + + builder.Append("<outline><![CDATA[" + outline + "]]></outline>"); + } + else + { + builder.Append("<outline><![CDATA[" + overview + "]]></outline>"); + } + + builder.Append("<customrating>" + SecurityElement.Escape(item.CustomRating ?? string.Empty) + "</customrating>"); + builder.Append("<lockdata>" + item.IsLocked.ToString().ToLower() + "</lockdata>"); + + if (item.LockedFields.Count > 0) + { + builder.Append("<lockedfields>" + string.Join("|", item.LockedFields.Select(i => i.ToString()).ToArray()) + "</lockedfields>"); + } + + if (!string.IsNullOrEmpty(item.DisplayMediaType)) + { + builder.Append("<type>" + SecurityElement.Escape(item.DisplayMediaType) + "</type>"); + } + + builder.Append("<dateadded>" + SecurityElement.Escape(item.DateCreated.ToString("yyyy-MM-dd HH:mm:ss")) + "</dateadded>"); + + builder.Append("<title>" + SecurityElement.Escape(item.Name ?? string.Empty) + "</title>"); + builder.Append("<originaltitle>" + SecurityElement.Escape(item.Name ?? string.Empty) + "</originaltitle>"); + + var directors = item.People + .Where(i => IsPersonType(i, PersonType.Director)) + .Select(i => i.Name) + .ToList(); + + foreach (var person in directors) + { + builder.Append("<director>" + SecurityElement.Escape(person) + "</director>"); + } + + var writers = item.People + .Where(i => IsPersonType(i, PersonType.Director)) + .Select(i => i.Name) + .ToList(); + + foreach (var person in writers) + { + builder.Append("<writer>" + SecurityElement.Escape(person) + "</writer>"); + } + + var credits = writers.Distinct(StringComparer.OrdinalIgnoreCase).ToList(); + + if (credits.Count > 0) + { + builder.Append("<credits>" + SecurityElement.Escape(string.Join(" / ", credits.ToArray())) + "</credits>"); + } + + var hasTrailer = item as IHasTrailers; + if (hasTrailer != null) + { + foreach (var trailer in hasTrailer.RemoteTrailers) + { + builder.Append("<trailer>" + SecurityElement.Escape(GetOutputTrailerUrl(trailer.Url)) + "</trailer>"); + } + } + + if (item.CommunityRating.HasValue) + { + builder.Append("<rating>" + SecurityElement.Escape(item.CommunityRating.Value.ToString(UsCulture)) + "</rating>"); + } + + if (item.ProductionYear.HasValue) + { + builder.Append("<year>" + SecurityElement.Escape(item.ProductionYear.Value.ToString(UsCulture)) + "</year>"); + } + + if (!string.IsNullOrEmpty(item.ForcedSortName)) + { + builder.Append("<sorttitle>" + SecurityElement.Escape(item.ForcedSortName) + "</sorttitle>"); + } + + if (!string.IsNullOrEmpty(item.OfficialRating)) + { + builder.Append("<mpaa>" + SecurityElement.Escape(item.OfficialRating) + "</mpaa>"); + } + + if (!string.IsNullOrEmpty(item.OfficialRatingDescription)) + { + builder.Append("<mpaadescription>" + SecurityElement.Escape(item.OfficialRatingDescription) + "</mpaadescription>"); + } + + var hasAspectRatio = item as IHasAspectRatio; + if (hasAspectRatio != null) + { + if (!string.IsNullOrEmpty(hasAspectRatio.AspectRatio)) + { + builder.Append("<aspectratio>" + SecurityElement.Escape(hasAspectRatio.AspectRatio) + "</aspectratio>"); + } + } + + if (!string.IsNullOrEmpty(item.HomePageUrl)) + { + builder.Append("<website>" + SecurityElement.Escape(item.HomePageUrl) + "</website>"); + } + + var rt = item.GetProviderId(MetadataProviders.RottenTomatoes); + + if (!string.IsNullOrEmpty(rt)) + { + builder.Append("<rottentomatoesid>" + SecurityElement.Escape(rt) + "</rottentomatoesid>"); + } + + var tmdbCollection = item.GetProviderId(MetadataProviders.TmdbCollection); + + if (!string.IsNullOrEmpty(tmdbCollection)) + { + builder.Append("<collectionnumber>" + SecurityElement.Escape(tmdbCollection) + "</collectionnumber>"); + } + + var imdb = item.GetProviderId(MetadataProviders.Imdb); + if (!string.IsNullOrEmpty(imdb)) + { + if (item is Series) + { + builder.Append("<imdb_id>" + SecurityElement.Escape(imdb) + "</imdb_id>"); + } + else + { + builder.Append("<imdbid>" + SecurityElement.Escape(imdb) + "</imdbid>"); + } + } + + // Series xml saver already saves this + if (!(item is Series)) + { + var tvdb = item.GetProviderId(MetadataProviders.Tvdb); + if (!string.IsNullOrEmpty(tvdb)) + { + builder.Append("<tvdbid>" + SecurityElement.Escape(tvdb) + "</tvdbid>"); + } + } + + var tmdb = item.GetProviderId(MetadataProviders.Tmdb); + if (!string.IsNullOrEmpty(tmdb)) + { + builder.Append("<tmdbid>" + SecurityElement.Escape(tmdb) + "</tmdbid>"); + } + + var tvcom = item.GetProviderId(MetadataProviders.Tvcom); + if (!string.IsNullOrEmpty(tvcom)) + { + builder.Append("<tvcomid>" + SecurityElement.Escape(tvcom) + "</tvcomid>"); + } + + var hasLanguage = item as IHasPreferredMetadataLanguage; + if (hasLanguage != null) + { + if (!string.IsNullOrEmpty(hasLanguage.PreferredMetadataLanguage)) + { + builder.Append("<language>" + SecurityElement.Escape(hasLanguage.PreferredMetadataLanguage) + "</language>"); + } + } + + if (item.PremiereDate.HasValue && !(item is Episode)) + { + var formatString = options.ReleaseDateFormat; + + if (item is MusicArtist) + { + builder.Append("<formed>" + SecurityElement.Escape(item.PremiereDate.Value.ToString(formatString)) + "</formed>"); + } + else + { + builder.Append("<premiered>" + SecurityElement.Escape(item.PremiereDate.Value.ToString(formatString)) + "</premiered>"); + builder.Append("<releasedate>" + SecurityElement.Escape(item.PremiereDate.Value.ToString(formatString)) + "</releasedate>"); + } + } + + if (item.EndDate.HasValue) + { + if (!(item is Episode)) + { + var formatString = options.ReleaseDateFormat; + + builder.Append("<enddate>" + SecurityElement.Escape(item.EndDate.Value.ToString(formatString)) + "</enddate>"); + } + } + + var hasCriticRating = item as IHasCriticRating; + + if (hasCriticRating != null) + { + if (hasCriticRating.CriticRating.HasValue) + { + builder.Append("<criticrating>" + SecurityElement.Escape(hasCriticRating.CriticRating.Value.ToString(UsCulture)) + "</criticrating>"); + } + + if (!string.IsNullOrEmpty(hasCriticRating.CriticRatingSummary)) + { + builder.Append("<criticratingsummary><![CDATA[" + hasCriticRating.CriticRatingSummary + "]]></criticratingsummary>"); + } + } + + var hasDisplayOrder = item as IHasDisplayOrder; + + if (hasDisplayOrder != null) + { + if (!string.IsNullOrEmpty(hasDisplayOrder.DisplayOrder)) + { + builder.Append("<displayorder>" + SecurityElement.Escape(hasDisplayOrder.DisplayOrder) + "</displayorder>"); + } + } + + if (item.VoteCount.HasValue) + { + builder.Append("<votes>" + SecurityElement.Escape(item.VoteCount.Value.ToString(UsCulture)) + "</votes>"); + } + + var hasBudget = item as IHasBudget; + if (hasBudget != null) + { + if (hasBudget.Budget.HasValue) + { + builder.Append("<budget>" + SecurityElement.Escape(hasBudget.Budget.Value.ToString(UsCulture)) + "</budget>"); + } + + if (hasBudget.Revenue.HasValue) + { + builder.Append("<revenue>" + SecurityElement.Escape(hasBudget.Revenue.Value.ToString(UsCulture)) + "</revenue>"); + } + } + + var hasMetascore = item as IHasMetascore; + if (hasMetascore != null && hasMetascore.Metascore.HasValue) + { + builder.Append("<metascore>" + SecurityElement.Escape(hasMetascore.Metascore.Value.ToString(UsCulture)) + "</metascore>"); + } + + // Use original runtime here, actual file runtime later in MediaInfo + var runTimeTicks = item.RunTimeTicks; + + if (runTimeTicks.HasValue) + { + var timespan = TimeSpan.FromTicks(runTimeTicks.Value); + + builder.Append("<runtime>" + Convert.ToInt32(timespan.TotalMinutes).ToString(UsCulture) + "</runtime>"); + } + + var hasTaglines = item as IHasTaglines; + if (hasTaglines != null) + { + foreach (var tagline in hasTaglines.Taglines) + { + builder.Append("<tagline>" + SecurityElement.Escape(tagline) + "</tagline>"); + } + } + + var hasProductionLocations = item as IHasProductionLocations; + if (hasProductionLocations != null) + { + foreach (var country in hasProductionLocations.ProductionLocations) + { + builder.Append("<country>" + SecurityElement.Escape(country) + "</country>"); + } + } + + foreach (var genre in item.Genres) + { + builder.Append("<genre>" + SecurityElement.Escape(genre) + "</genre>"); + } + + foreach (var studio in item.Studios) + { + builder.Append("<studio>" + SecurityElement.Escape(studio) + "</studio>"); + } + + var hasTags = item as IHasTags; + if (hasTags != null) + { + foreach (var tag in hasTags.Tags) + { + if (item is MusicAlbum || item is MusicArtist) + { + builder.Append("<style>" + SecurityElement.Escape(tag) + "</style>"); + } + else + { + builder.Append("<tag>" + SecurityElement.Escape(tag) + "</tag>"); + } + } + } + + var hasKeywords = item as IHasKeywords; + if (hasKeywords != null) + { + foreach (var tag in hasKeywords.Keywords) + { + builder.Append("<plotkeyword>" + SecurityElement.Escape(tag) + "</plotkeyword>"); + } + } + + var hasAwards = item as IHasAwards; + if (hasAwards != null && !string.IsNullOrEmpty(hasAwards.AwardSummary)) + { + builder.Append("<awardsummary>" + SecurityElement.Escape(hasAwards.AwardSummary) + "</awardsummary>"); + } + + var externalId = item.GetProviderId(MetadataProviders.AudioDbArtist); + + if (!string.IsNullOrEmpty(externalId)) + { + builder.Append("<audiodbartistid>" + SecurityElement.Escape(externalId) + "</audiodbartistid>"); + } + + externalId = item.GetProviderId(MetadataProviders.AudioDbAlbum); + + if (!string.IsNullOrEmpty(externalId)) + { + builder.Append("<audiodbalbumid>" + SecurityElement.Escape(externalId) + "</audiodbalbumid>"); + } + + externalId = item.GetProviderId(MetadataProviders.Zap2It); + + if (!string.IsNullOrEmpty(externalId)) + { + builder.Append("<zap2itid>" + SecurityElement.Escape(externalId) + "</zap2itid>"); + } + + externalId = item.GetProviderId(MetadataProviders.MusicBrainzAlbum); + + if (!string.IsNullOrEmpty(externalId)) + { + builder.Append("<musicbrainzalbumid>" + SecurityElement.Escape(externalId) + "</musicbrainzalbumid>"); + } + + externalId = item.GetProviderId(MetadataProviders.MusicBrainzAlbumArtist); + + if (!string.IsNullOrEmpty(externalId)) + { + builder.Append("<musicbrainzalbumartistid>" + SecurityElement.Escape(externalId) + "</musicbrainzalbumartistid>"); + } + + externalId = item.GetProviderId(MetadataProviders.MusicBrainzArtist); + + if (!string.IsNullOrEmpty(externalId)) + { + builder.Append("<musicbrainzartistid>" + SecurityElement.Escape(externalId) + "</musicbrainzartistid>"); + } + + externalId = item.GetProviderId(MetadataProviders.MusicBrainzReleaseGroup); + + if (!string.IsNullOrEmpty(externalId)) + { + builder.Append("<musicbrainzreleasegroupid>" + SecurityElement.Escape(externalId) + "</musicbrainzreleasegroupid>"); + } + + externalId = item.GetProviderId(MetadataProviders.Gamesdb); + if (!string.IsNullOrEmpty(externalId)) + { + builder.Append("<gamesdbid>" + SecurityElement.Escape(externalId) + "</gamesdbid>"); + } + + externalId = item.GetProviderId(MetadataProviders.TvRage); + if (!string.IsNullOrEmpty(externalId)) + { + builder.Append("<tvrageid>" + SecurityElement.Escape(externalId) + "</tvrageid>"); + } + + if (options.SaveImagePathsInNfo) + { + AddImages(item, builder, fileSystem, config); + } + + AddUserData(item, builder, userManager, userDataRepo, options); + + AddActors(item, builder, libraryManager, fileSystem, config); + + var folder = item as BoxSet; + if (folder != null) + { + AddCollectionItems(folder, builder); + } + } + + public static void AddChapters(Video item, StringBuilder builder, IItemRepository repository) + { + var chapters = repository.GetChapters(item.Id); + + foreach (var chapter in chapters) + { + builder.Append("<chapter>"); + builder.Append("<name>" + SecurityElement.Escape(chapter.Name) + "</name>"); + + var time = TimeSpan.FromTicks(chapter.StartPositionTicks); + var ms = Convert.ToInt64(time.TotalMilliseconds); + + builder.Append("<startpositionms>" + SecurityElement.Escape(ms.ToString(UsCulture)) + "</startpositionms>"); + builder.Append("</chapter>"); + } + } + + public static void AddCollectionItems(Folder item, StringBuilder builder) + { + var items = item.LinkedChildren + .Where(i => i.Type == LinkedChildType.Manual && !string.IsNullOrWhiteSpace(i.ItemName)) + .ToList(); + + foreach (var link in items) + { + builder.Append("<collectionitem>"); + + builder.Append("<name>" + SecurityElement.Escape(link.ItemName) + "</name>"); + builder.Append("<type>" + SecurityElement.Escape(link.ItemType) + "</type>"); + + if (link.ItemYear.HasValue) + { + builder.Append("<year>" + SecurityElement.Escape(link.ItemYear.Value.ToString(UsCulture)) + "</year>"); + } + + builder.Append("</collectionitem>"); + } + } + + /// <summary> + /// Gets the output trailer URL. + /// </summary> + /// <param name="url">The URL.</param> + /// <returns>System.String.</returns> + private static string GetOutputTrailerUrl(string url) + { + // This is what xbmc expects + + return url.Replace("http://www.youtube.com/watch?v=", + "plugin://plugin.video.youtube/?action=play_video&videoid=", + StringComparison.OrdinalIgnoreCase); + } + + private static void AddImages(BaseItem item, StringBuilder builder, IFileSystem fileSystem, IServerConfigurationManager config) + { + builder.Append("<art>"); + + var poster = item.PrimaryImagePath; + + if (!string.IsNullOrEmpty(poster)) + { + builder.Append("<poster>" + SecurityElement.Escape(GetPathToSave(item.PrimaryImagePath, fileSystem, config)) + "</poster>"); + } + + foreach (var backdrop in item.GetImages(ImageType.Backdrop)) + { + builder.Append("<fanart>" + SecurityElement.Escape(GetPathToSave(backdrop.Path, fileSystem, config)) + "</fanart>"); + } + + builder.Append("</art>"); + } + + private static void AddUserData(BaseItem item, StringBuilder builder, IUserManager userManager, IUserDataManager userDataRepo, XbmcMetadataOptions options) + { + var userId = options.UserId; + if (string.IsNullOrWhiteSpace(userId)) + { + return; + } + + var user = userManager.GetUserById(new Guid(userId)); + + if (user == null) + { + return; + } + + if (item.IsFolder) + { + return; + } + + var userdata = userDataRepo.GetUserData(user.Id, item.GetUserDataKey()); + + builder.Append("<playcount>" + userdata.PlayCount.ToString(UsCulture) + "</playcount>"); + builder.Append("<watched>" + userdata.Played.ToString().ToLower() + "</watched>"); + + if (userdata.LastPlayedDate.HasValue) + { + builder.Append("<lastplayed>" + SecurityElement.Escape(userdata.LastPlayedDate.Value.ToString("yyyy-MM-dd HH:mm:ss")) + "</lastplayed>"); + } + + builder.Append("<resume>"); + + var runTimeTicks = item.RunTimeTicks ?? 0; + + builder.Append("<position>" + TimeSpan.FromTicks(userdata.PlaybackPositionTicks).TotalSeconds.ToString(UsCulture) + "</position>"); + builder.Append("<total>" + TimeSpan.FromTicks(runTimeTicks).TotalSeconds.ToString(UsCulture) + "</total>"); + + builder.Append("</resume>"); + } + + public static void AddActors(BaseItem item, StringBuilder builder, ILibraryManager libraryManager, IFileSystem fileSystem, IServerConfigurationManager config) + { + var actors = item.People + .Where(i => !IsPersonType(i, PersonType.Director) && !IsPersonType(i, PersonType.Writer)) + .ToList(); + + foreach (var person in actors) + { + builder.Append("<actor>"); + builder.Append("<name>" + SecurityElement.Escape(person.Name ?? string.Empty) + "</name>"); + builder.Append("<role>" + SecurityElement.Escape(person.Role ?? string.Empty) + "</role>"); + builder.Append("<type>" + SecurityElement.Escape(person.Type ?? string.Empty) + "</type>"); + + try + { + var personEntity = libraryManager.GetPerson(person.Name); + + if (!string.IsNullOrEmpty(personEntity.PrimaryImagePath)) + { + builder.Append("<thumb>" + SecurityElement.Escape(GetPathToSave(personEntity.PrimaryImagePath, fileSystem, config)) + "</thumb>"); + } + } + catch (Exception) + { + // Already logged in core + } + + builder.Append("</actor>"); + } + } + + private static bool IsPersonType(PersonInfo person, string type) + { + return string.Equals(person.Type, type, StringComparison.OrdinalIgnoreCase) || string.Equals(person.Role, type, StringComparison.OrdinalIgnoreCase); + } + + private static string GetPathToSave(string path, IFileSystem fileSystem, IServerConfigurationManager config) + { + foreach (var map in config.Configuration.PathSubstitutions) + { + path = fileSystem.SubstitutePath(path, map.From, map.To); + } + + return path; + } + + public static string ReplaceString(string str, string oldValue, string newValue, StringComparison comparison) + { + var sb = new StringBuilder(); + + int previousIndex = 0; + int index = str.IndexOf(oldValue, comparison); + while (index != -1) + { + sb.Append(str.Substring(previousIndex, index - previousIndex)); + sb.Append(newValue); + index += oldValue.Length; + + previousIndex = index; + index = str.IndexOf(oldValue, index, comparison); + } + sb.Append(str.Substring(previousIndex)); + + return sb.ToString(); + } + } +} diff --git a/MediaBrowser.sln b/MediaBrowser.sln index 1b132e07f..f8c1701bb 100644 --- a/MediaBrowser.sln +++ b/MediaBrowser.sln @@ -1,8 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2013 -VisualStudioVersion = 12.0.21005.1 -MinimumVisualStudioVersion = 10.0.40219.1 +# Visual Studio 2012 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Controller", "MediaBrowser.Controller\MediaBrowser.Controller.csproj", "{17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Api", "MediaBrowser.Api\MediaBrowser.Api.csproj", "{4FD51AC5-2C16-4308-A993-C3A84F3B4582}" @@ -47,6 +45,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.MediaEncoding" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenSubtitlesHandler", "OpenSubtitlesHandler\OpenSubtitlesHandler.csproj", "{4A4402D4-E910-443B-B8FC-2C18286A2CA0}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.XbmcMetadata", "MediaBrowser.XbmcMetadata\MediaBrowser.XbmcMetadata.csproj", "{23499896-B135-4527-8574-C26E926EA99E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.LocalMetadata", "MediaBrowser.LocalMetadata\MediaBrowser.LocalMetadata.csproj", "{7EF9F3E0-697D-42F3-A08F-19DEB5F84392}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -281,6 +283,34 @@ Global {4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Release|Win32.ActiveCfg = Release|Any CPU {4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Release|x64.ActiveCfg = Release|Any CPU {4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Release|x86.ActiveCfg = Release|Any CPU + {23499896-B135-4527-8574-C26E926EA99E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {23499896-B135-4527-8574-C26E926EA99E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {23499896-B135-4527-8574-C26E926EA99E}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {23499896-B135-4527-8574-C26E926EA99E}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {23499896-B135-4527-8574-C26E926EA99E}.Debug|Win32.ActiveCfg = Debug|Any CPU + {23499896-B135-4527-8574-C26E926EA99E}.Debug|x64.ActiveCfg = Debug|Any CPU + {23499896-B135-4527-8574-C26E926EA99E}.Debug|x86.ActiveCfg = Debug|Any CPU + {23499896-B135-4527-8574-C26E926EA99E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {23499896-B135-4527-8574-C26E926EA99E}.Release|Any CPU.Build.0 = Release|Any CPU + {23499896-B135-4527-8574-C26E926EA99E}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {23499896-B135-4527-8574-C26E926EA99E}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {23499896-B135-4527-8574-C26E926EA99E}.Release|Win32.ActiveCfg = Release|Any CPU + {23499896-B135-4527-8574-C26E926EA99E}.Release|x64.ActiveCfg = Release|Any CPU + {23499896-B135-4527-8574-C26E926EA99E}.Release|x86.ActiveCfg = Release|Any CPU + {7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.Debug|Win32.ActiveCfg = Debug|Any CPU + {7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.Debug|x64.ActiveCfg = Debug|Any CPU + {7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.Debug|x86.ActiveCfg = Debug|Any CPU + {7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.Release|Any CPU.Build.0 = Release|Any CPU + {7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.Release|Win32.ActiveCfg = Release|Any CPU + {7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.Release|x64.ActiveCfg = Release|Any CPU + {7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.Release|x86.ActiveCfg = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE |
