aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStephen <snazy2000@btinternet.com>2014-06-30 06:20:24 +0100
committerStephen <snazy2000@btinternet.com>2014-06-30 06:20:24 +0100
commit2a9e4778124de3103bd4a50f806a3694b57d3c6a (patch)
tree0275788f3b9706b26441edf48a0b4975cc02ee5d
parent608ebf4829e7e394170bb2dec8a33c83600e9c08 (diff)
parent62d98551edf421ba50f2eadf95d6c3d1fb1d20ae (diff)
Merge pull request #1 from MediaBrowser/master
update
-rw-r--r--MediaBrowser.Api/ApiEntryPoint.cs2
-rw-r--r--MediaBrowser.Api/ConfigurationService.cs38
-rw-r--r--MediaBrowser.Api/Dlna/DlnaServerService.cs9
-rw-r--r--MediaBrowser.Api/ItemUpdateService.cs6
-rw-r--r--MediaBrowser.Api/MediaBrowser.Api.csproj1
-rw-r--r--MediaBrowser.Api/Playback/BaseStreamingService.cs16
-rw-r--r--MediaBrowser.Api/Playback/BifService.cs186
-rw-r--r--MediaBrowser.Api/Playback/Progressive/VideoService.cs3
-rw-r--r--MediaBrowser.Common.Implementations/BaseApplicationHost.cs1
-rw-r--r--MediaBrowser.Common.Implementations/Configuration/BaseConfigurationManager.cs91
-rw-r--r--MediaBrowser.Common.Implementations/Updates/InstallationManager.cs24
-rw-r--r--MediaBrowser.Common/Configuration/ConfigurationUpdateEventArgs.cs18
-rw-r--r--MediaBrowser.Common/Configuration/IConfigurationFactory.cs17
-rw-r--r--MediaBrowser.Common/Configuration/IConfigurationManager.cs43
-rw-r--r--MediaBrowser.Common/MediaBrowser.Common.csproj2
-rw-r--r--MediaBrowser.Common/Net/MimeTypes.cs5
-rw-r--r--MediaBrowser.Controller/Chapters/IChapterManager.cs7
-rw-r--r--MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs18
-rw-r--r--MediaBrowser.Controller/Entities/Folder.cs5
-rw-r--r--MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs21
-rw-r--r--MediaBrowser.Dlna/ConfigurationExtension.cs29
-rw-r--r--MediaBrowser.Dlna/ContentDirectory/ContentDirectory.cs6
-rw-r--r--MediaBrowser.Dlna/Didl/Filter.cs (renamed from MediaBrowser.Model/Dlna/Filter.cs)8
-rw-r--r--MediaBrowser.Dlna/Eventing/EventSubscription.cs (renamed from MediaBrowser.Model/Dlna/EventSubscription.cs)2
-rw-r--r--MediaBrowser.Dlna/Main/DlnaEntryPoint.cs22
-rw-r--r--MediaBrowser.Dlna/MediaBrowser.Dlna.csproj3
-rw-r--r--MediaBrowser.Dlna/PlayTo/PlayToController.cs2
-rw-r--r--MediaBrowser.Dlna/PlayTo/PlayToManager.cs6
-rw-r--r--MediaBrowser.Dlna/PlayTo/SsdpHttpClient.cs6
-rw-r--r--MediaBrowser.Dlna/Service/BaseControlHandler.cs2
-rw-r--r--MediaBrowser.Dlna/Ssdp/SsdpHandler.cs30
-rw-r--r--MediaBrowser.LocalMetadata/BaseXmlProvider.cs (renamed from MediaBrowser.Providers/BaseXmlProvider.cs)12
-rw-r--r--MediaBrowser.LocalMetadata/Images/CollectionFolderImageProvider.cs (renamed from MediaBrowser.Providers/Folders/CollectionFolderImageProvider.cs)8
-rw-r--r--MediaBrowser.LocalMetadata/Images/EpisodeLocalImageProvider.cs (renamed from MediaBrowser.Providers/TV/EpisodeLocalImageProvider.cs)12
-rw-r--r--MediaBrowser.LocalMetadata/Images/ImagesByNameImageProvider.cs (renamed from MediaBrowser.Providers/Folders/ImagesByNameImageProvider.cs)9
-rw-r--r--MediaBrowser.LocalMetadata/Images/InternalMetadataFolderImageProvider.cs (renamed from MediaBrowser.Providers/All/InternalMetadataFolderImageProvider.cs)8
-rw-r--r--MediaBrowser.LocalMetadata/Images/LocalImageProvider.cs (renamed from MediaBrowser.Providers/All/LocalImageProvider.cs)14
-rw-r--r--MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj110
-rw-r--r--MediaBrowser.LocalMetadata/Parsers/BoxSetXmlParser.cs (renamed from MediaBrowser.Providers/BoxSets/BoxSetXmlParser.cs)10
-rw-r--r--MediaBrowser.LocalMetadata/Parsers/EpisodeXmlParser.cs (renamed from MediaBrowser.Providers/TV/EpisodeXmlParser.cs)12
-rw-r--r--MediaBrowser.LocalMetadata/Parsers/GameSystemXmlParser.cs (renamed from MediaBrowser.Providers/Games/GameSystemXmlParser.cs)10
-rw-r--r--MediaBrowser.LocalMetadata/Parsers/GameXmlParser.cs (renamed from MediaBrowser.Providers/Games/GameXmlParser.cs)12
-rw-r--r--MediaBrowser.LocalMetadata/Parsers/MovieXmlParser.cs (renamed from MediaBrowser.Providers/Movies/MovieXmlParser.cs)10
-rw-r--r--MediaBrowser.LocalMetadata/Parsers/MusicVideoXmlParser.cs (renamed from MediaBrowser.Providers/Music/MusicVideoXmlParser.cs)7
-rw-r--r--MediaBrowser.LocalMetadata/Parsers/SeasonXmlParser.cs (renamed from MediaBrowser.Providers/TV/SeasonXmlParser.cs)6
-rw-r--r--MediaBrowser.LocalMetadata/Parsers/SeriesXmlParser.cs (renamed from MediaBrowser.Providers/TV/SeriesXmlParser.cs)8
-rw-r--r--MediaBrowser.LocalMetadata/Properties/AssemblyInfo.cs36
-rw-r--r--MediaBrowser.LocalMetadata/Providers/AdultVideoXmlProvider.cs (renamed from MediaBrowser.Providers/AdultVideos/AdultVideoXmlProvider.cs)12
-rw-r--r--MediaBrowser.LocalMetadata/Providers/AlbumXmlProvider.cs (renamed from MediaBrowser.Providers/Music/AlbumXmlProvider.cs)10
-rw-r--r--MediaBrowser.LocalMetadata/Providers/ArtistXmlProvider.cs (renamed from MediaBrowser.Providers/Music/ArtistXmlProvider.cs)8
-rw-r--r--MediaBrowser.LocalMetadata/Providers/BoxSetXmlProvider.cs (renamed from MediaBrowser.Providers/BoxSets/BoxSetXmlProvider.cs)9
-rw-r--r--MediaBrowser.LocalMetadata/Providers/ChannelXmlProvider.cs (renamed from MediaBrowser.Providers/LiveTv/ChannelXmlProvider.cs)8
-rw-r--r--MediaBrowser.LocalMetadata/Providers/EpisodeXmlProvider.cs (renamed from MediaBrowser.Providers/TV/EpisodeXmlProvider.cs)22
-rw-r--r--MediaBrowser.LocalMetadata/Providers/FolderXmlProvider.cs (renamed from MediaBrowser.Providers/Folders/FolderXmlProvider.cs)8
-rw-r--r--MediaBrowser.LocalMetadata/Providers/GameSystemXmlProvider.cs (renamed from MediaBrowser.Providers/Games/GameSystemXmlProvider.cs)9
-rw-r--r--MediaBrowser.LocalMetadata/Providers/GameXmlProvider.cs (renamed from MediaBrowser.Providers/Games/GameXmlProvider.cs)9
-rw-r--r--MediaBrowser.LocalMetadata/Providers/MovieXmlProvider.cs (renamed from MediaBrowser.Providers/Movies/MovieXmlProvider.cs)11
-rw-r--r--MediaBrowser.LocalMetadata/Providers/MusicVideoXmlProvider.cs (renamed from MediaBrowser.Providers/Music/MusicVideoXmlProvider.cs)11
-rw-r--r--MediaBrowser.LocalMetadata/Providers/PersonXmlProvider.cs (renamed from MediaBrowser.Providers/People/PersonXmlProvider.cs)8
-rw-r--r--MediaBrowser.LocalMetadata/Providers/SeasonXmlProvider.cs (renamed from MediaBrowser.Providers/TV/SeasonXmlProvider.cs)20
-rw-r--r--MediaBrowser.LocalMetadata/Providers/SeriesXmlProvider.cs (renamed from MediaBrowser.Providers/TV/SeriesXmlProvider.cs)20
-rw-r--r--MediaBrowser.LocalMetadata/Providers/TrailerXmlProvider.cs (renamed from MediaBrowser.Providers/Movies/TrailerXmlProvider.cs)11
-rw-r--r--MediaBrowser.LocalMetadata/Providers/VideoXmlProvider.cs (renamed from MediaBrowser.Providers/Videos/VideoXmlProvider.cs)12
-rw-r--r--MediaBrowser.LocalMetadata/Savers/AlbumXmlSaver.cs (renamed from MediaBrowser.Providers/Savers/AlbumXmlSaver.cs)10
-rw-r--r--MediaBrowser.LocalMetadata/Savers/ArtistXmlSaver.cs (renamed from MediaBrowser.Providers/Savers/ArtistXmlSaver.cs)10
-rw-r--r--MediaBrowser.LocalMetadata/Savers/BoxSetXmlSaver.cs (renamed from MediaBrowser.Providers/Savers/BoxSetXmlSaver.cs)11
-rw-r--r--MediaBrowser.LocalMetadata/Savers/ChannelXmlSaver.cs (renamed from MediaBrowser.Providers/Savers/ChannelXmlSaver.cs)11
-rw-r--r--MediaBrowser.LocalMetadata/Savers/EpisodeXmlSaver.cs (renamed from MediaBrowser.Providers/Savers/EpisodeXmlSaver.cs)12
-rw-r--r--MediaBrowser.LocalMetadata/Savers/FolderXmlSaver.cs (renamed from MediaBrowser.Providers/Savers/FolderXmlSaver.cs)13
-rw-r--r--MediaBrowser.LocalMetadata/Savers/GameSystemXmlSaver.cs (renamed from MediaBrowser.Providers/Savers/GameSystemXmlSaver.cs)8
-rw-r--r--MediaBrowser.LocalMetadata/Savers/GameXmlSaver.cs (renamed from MediaBrowser.Providers/Savers/GameXmlSaver.cs)10
-rw-r--r--MediaBrowser.LocalMetadata/Savers/MovieXmlSaver.cs (renamed from MediaBrowser.Providers/Savers/MovieXmlSaver.cs)15
-rw-r--r--MediaBrowser.LocalMetadata/Savers/PersonXmlSaver.cs (renamed from MediaBrowser.Providers/Savers/PersonXmlSaver.cs)10
-rw-r--r--MediaBrowser.LocalMetadata/Savers/SeasonXmlSaver.cs (renamed from MediaBrowser.Providers/Savers/SeasonXmlSaver.cs)10
-rw-r--r--MediaBrowser.LocalMetadata/Savers/SeriesXmlSaver.cs (renamed from MediaBrowser.Providers/Savers/SeriesXmlSaver.cs)14
-rw-r--r--MediaBrowser.LocalMetadata/Savers/XmlSaverHelpers.cs (renamed from MediaBrowser.Providers/Savers/XmlSaverHelpers.cs)14
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs99
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs11
-rw-r--r--MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj21
-rw-r--r--MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj21
-rw-r--r--MediaBrowser.Model/Configuration/ChannelOptions.cs18
-rw-r--r--MediaBrowser.Model/Configuration/ChapterOptions.cs27
-rw-r--r--MediaBrowser.Model/Configuration/ServerConfiguration.cs77
-rw-r--r--MediaBrowser.Model/Configuration/SubtitlePlaybackMode.cs10
-rw-r--r--MediaBrowser.Model/Configuration/UserConfiguration.cs8
-rw-r--r--MediaBrowser.Model/Configuration/XbmcMetadataOptions.cs21
-rw-r--r--MediaBrowser.Model/Dlna/ConditionProcessor.cs29
-rw-r--r--MediaBrowser.Model/Dto/BaseItemDto.cs8
-rw-r--r--MediaBrowser.Model/Dto/BaseItemPerson.cs3
-rw-r--r--MediaBrowser.Model/Dto/ChapterInfoDto.cs3
-rw-r--r--MediaBrowser.Model/Dto/UserDto.cs3
-rw-r--r--MediaBrowser.Model/Dto/UserItemDataDto.cs3
-rw-r--r--MediaBrowser.Model/Entities/DisplayPreferences.cs3
-rw-r--r--MediaBrowser.Model/Entities/MediaStream.cs2
-rw-r--r--MediaBrowser.Model/Extensions/IHasPropertyChangedEvent.cs8
-rw-r--r--MediaBrowser.Model/Extensions/ListHelper.cs11
-rw-r--r--MediaBrowser.Model/LiveTv/BaseTimerInfoDto.cs3
-rw-r--r--MediaBrowser.Model/LiveTv/ChannelInfoDto.cs3
-rw-r--r--MediaBrowser.Model/LiveTv/ProgramInfoDto.cs3
-rw-r--r--MediaBrowser.Model/LiveTv/RecordingGroupDto.cs3
-rw-r--r--MediaBrowser.Model/LiveTv/RecordingInfoDto.cs3
-rw-r--r--MediaBrowser.Model/MediaBrowser.Model.csproj7
-rw-r--r--MediaBrowser.Model/Notifications/NotificationRequest.cs6
-rw-r--r--MediaBrowser.Model/Session/GeneralCommand.cs5
-rw-r--r--MediaBrowser.Model/Session/SessionInfoDto.cs3
-rw-r--r--MediaBrowser.Model/Themes/AppTheme.cs5
-rw-r--r--MediaBrowser.Model/Updates/CheckForUpdateResult.cs7
-rw-r--r--MediaBrowser.Model/Updates/PackageVersionInfo.cs30
-rw-r--r--MediaBrowser.Providers/Channels/AudioChannelItemMetadataService.cs (renamed from MediaBrowser.Providers/GameGenres/AudioChannelItemMetadataService.cs)6
-rw-r--r--MediaBrowser.Providers/Channels/VideoChannelItemMetadataService.cs (renamed from MediaBrowser.Providers/GameGenres/VideoChannelItemMetadataService.cs)6
-rw-r--r--MediaBrowser.Providers/Chapters/ChapterManager.cs32
-rw-r--r--MediaBrowser.Providers/MediaBrowser.Providers.csproj49
-rw-r--r--MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs6
-rw-r--r--MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs45
-rw-r--r--MediaBrowser.Server.Implementations/Localization/JavaScript/de.json20
-rw-r--r--MediaBrowser.Server.Implementations/Localization/JavaScript/es.json26
-rw-r--r--MediaBrowser.Server.Implementations/Localization/JavaScript/es_MX.json10
-rw-r--r--MediaBrowser.Server.Implementations/Localization/JavaScript/fr.json8
-rw-r--r--MediaBrowser.Server.Implementations/Localization/JavaScript/pl.json198
-rw-r--r--MediaBrowser.Server.Implementations/Localization/JavaScript/pt_BR.json10
-rw-r--r--MediaBrowser.Server.Implementations/Localization/JavaScript/ru.json4
-rw-r--r--MediaBrowser.Server.Implementations/Localization/JavaScript/sv.json10
-rw-r--r--MediaBrowser.Server.Implementations/Localization/LocalizationManager.cs1
-rw-r--r--MediaBrowser.Server.Implementations/Localization/Server/de.json64
-rw-r--r--MediaBrowser.Server.Implementations/Localization/Server/es.json22
-rw-r--r--MediaBrowser.Server.Implementations/Localization/Server/es_MX.json12
-rw-r--r--MediaBrowser.Server.Implementations/Localization/Server/fr.json6
-rw-r--r--MediaBrowser.Server.Implementations/Localization/Server/pl.json825
-rw-r--r--MediaBrowser.Server.Implementations/Localization/Server/pt_BR.json12
-rw-r--r--MediaBrowser.Server.Implementations/Localization/Server/ru.json18
-rw-r--r--MediaBrowser.Server.Implementations/Localization/Server/server.json13
-rw-r--r--MediaBrowser.Server.Implementations/Localization/Server/sv.json10
-rw-r--r--MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj2
-rw-r--r--MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs14
-rw-r--r--MediaBrowser.ServerApplication/ApplicationHost.cs125
-rw-r--r--MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj8
-rw-r--r--MediaBrowser.WebDashboard/Api/DashboardService.cs1
-rw-r--r--MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj6
-rw-r--r--MediaBrowser.XbmcMetadata/Configuration/NfoOptions.cs29
-rw-r--r--MediaBrowser.XbmcMetadata/EntryPoint.cs110
-rw-r--r--MediaBrowser.XbmcMetadata/Images/XbmcImageSaver.cs (renamed from MediaBrowser.Providers/Xbmc/XbmcImageSaver.cs)2
-rw-r--r--MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj89
-rw-r--r--MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs992
-rw-r--r--MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs211
-rw-r--r--MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs97
-rw-r--r--MediaBrowser.XbmcMetadata/Parsers/SeasonNfoParser.cs45
-rw-r--r--MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs93
-rw-r--r--MediaBrowser.XbmcMetadata/Properties/AssemblyInfo.cs36
-rw-r--r--MediaBrowser.XbmcMetadata/Providers/AlbumNfoProvider.cs34
-rw-r--r--MediaBrowser.XbmcMetadata/Providers/ArtistNfoProvider.cs34
-rw-r--r--MediaBrowser.XbmcMetadata/Providers/BaseNfoProvider.cs89
-rw-r--r--MediaBrowser.XbmcMetadata/Providers/BaseVideoNfoProvider.cs55
-rw-r--r--MediaBrowser.XbmcMetadata/Providers/EpisodeNfoProvider.cs44
-rw-r--r--MediaBrowser.XbmcMetadata/Providers/MovieNfoProvider.cs45
-rw-r--r--MediaBrowser.XbmcMetadata/Providers/SeasonNfoProvider.cs35
-rw-r--r--MediaBrowser.XbmcMetadata/Providers/SeriesNfoProvider.cs34
-rw-r--r--MediaBrowser.XbmcMetadata/Savers/AlbumXmlSaver.cs135
-rw-r--r--MediaBrowser.XbmcMetadata/Savers/ArtistXmlSaver.cs116
-rw-r--r--MediaBrowser.XbmcMetadata/Savers/EpisodeXmlSaver.cs141
-rw-r--r--MediaBrowser.XbmcMetadata/Savers/MovieXmlSaver.cs137
-rw-r--r--MediaBrowser.XbmcMetadata/Savers/SeasonXmlSaver.cs87
-rw-r--r--MediaBrowser.XbmcMetadata/Savers/SeriesXmlSaver.cs122
-rw-r--r--MediaBrowser.XbmcMetadata/Savers/XmlSaverHelpers.cs906
-rw-r--r--MediaBrowser.sln36
164 files changed, 6391 insertions, 645 deletions
diff --git a/MediaBrowser.Api/ApiEntryPoint.cs b/MediaBrowser.Api/ApiEntryPoint.cs
index 577554610..2177f90c2 100644
--- a/MediaBrowser.Api/ApiEntryPoint.cs
+++ b/MediaBrowser.Api/ApiEntryPoint.cs
@@ -418,7 +418,7 @@ namespace MediaBrowser.Api
private async void DeletePartialStreamFiles(string path, TranscodingJobType jobType, int retryCount, int delayMs)
{
- if (retryCount >= 8)
+ if (retryCount >= 10)
{
return;
}
diff --git a/MediaBrowser.Api/ConfigurationService.cs b/MediaBrowser.Api/ConfigurationService.cs
index b3191cd4b..39fcc50d8 100644
--- a/MediaBrowser.Api/ConfigurationService.cs
+++ b/MediaBrowser.Api/ConfigurationService.cs
@@ -8,8 +8,11 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Serialization;
using ServiceStack;
+using ServiceStack.Text.Controller;
+using ServiceStack.Web;
using System;
using System.Collections.Generic;
+using System.IO;
using System.Linq;
namespace MediaBrowser.Api
@@ -23,6 +26,13 @@ namespace MediaBrowser.Api
}
+ [Route("/System/Configuration/{Key}", "GET", Summary = "Gets a named configuration")]
+ public class GetNamedConfiguration
+ {
+ [ApiMember(Name = "Key", Description = "Key", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
+ public string Key { get; set; }
+ }
+
/// <summary>
/// Class UpdateConfiguration
/// </summary>
@@ -31,6 +41,15 @@ namespace MediaBrowser.Api
{
}
+ [Route("/System/Configuration/{Key}", "POST", Summary = "Updates named configuration")]
+ public class UpdateNamedConfiguration : IReturnVoid, IRequiresRequestStream
+ {
+ [ApiMember(Name = "Key", Description = "Key", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
+ public string Key { get; set; }
+
+ public Stream RequestStream { get; set; }
+ }
+
[Route("/System/Configuration/MetadataOptions/Default", "GET", Summary = "Gets a default MetadataOptions object")]
public class GetDefaultMetadataOptions : IReturn<MetadataOptions>
{
@@ -88,6 +107,13 @@ namespace MediaBrowser.Api
return ToOptimizedResultUsingCache(cacheKey, dateModified, null, () => _configurationManager.Configuration);
}
+ public object Get(GetNamedConfiguration request)
+ {
+ var result = _configurationManager.GetConfiguration(request.Key);
+
+ return ToOptimizedResult(result);
+ }
+
/// <summary>
/// Posts the specified configuraiton.
/// </summary>
@@ -95,7 +121,6 @@ namespace MediaBrowser.Api
public void Post(UpdateConfiguration request)
{
// Silly, but we need to serialize and deserialize or the XmlSerializer will write the xml with an element name of UpdateConfiguration
-
var json = _jsonSerializer.SerializeToString(request);
var config = _jsonSerializer.DeserializeFromString<ServerConfiguration>(json);
@@ -103,6 +128,17 @@ namespace MediaBrowser.Api
_configurationManager.ReplaceConfiguration(config);
}
+ public void Post(UpdateNamedConfiguration request)
+ {
+ var pathInfo = PathInfo.Parse(Request.PathInfo);
+ var key = pathInfo.GetArgumentValue<string>(2);
+
+ var configurationType = _configurationManager.GetConfigurationType(key);
+ var configuration = _jsonSerializer.DeserializeFromStream(request.RequestStream, configurationType);
+
+ _configurationManager.SaveConfiguration(key, configuration);
+ }
+
public object Get(GetDefaultMetadataOptions request)
{
return ToOptimizedSerializedResultUsingCache(new MetadataOptions());
diff --git a/MediaBrowser.Api/Dlna/DlnaServerService.cs b/MediaBrowser.Api/Dlna/DlnaServerService.cs
index 28de8ee17..82bd394f0 100644
--- a/MediaBrowser.Api/Dlna/DlnaServerService.cs
+++ b/MediaBrowser.Api/Dlna/DlnaServerService.cs
@@ -1,4 +1,6 @@
-using MediaBrowser.Controller.Dlna;
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Controller.Dlna;
+using MediaBrowser.Model.Configuration;
using ServiceStack;
using ServiceStack.Text.Controller;
using ServiceStack.Web;
@@ -76,11 +78,14 @@ namespace MediaBrowser.Api.Dlna
private readonly IContentDirectory _contentDirectory;
private readonly IConnectionManager _connectionManager;
- public DlnaServerService(IDlnaManager dlnaManager, IContentDirectory contentDirectory, IConnectionManager connectionManager)
+ private readonly IConfigurationManager _config;
+
+ public DlnaServerService(IDlnaManager dlnaManager, IContentDirectory contentDirectory, IConnectionManager connectionManager, IConfigurationManager config)
{
_dlnaManager = dlnaManager;
_contentDirectory = contentDirectory;
_connectionManager = connectionManager;
+ _config = config;
}
public object Get(GetDescriptionXml request)
diff --git a/MediaBrowser.Api/ItemUpdateService.cs b/MediaBrowser.Api/ItemUpdateService.cs
index 4feba82b0..ad7da8e3c 100644
--- a/MediaBrowser.Api/ItemUpdateService.cs
+++ b/MediaBrowser.Api/ItemUpdateService.cs
@@ -108,6 +108,12 @@ namespace MediaBrowser.Api
hasTags.Tags = request.Tags;
}
+ var hasTaglines = item as IHasTaglines;
+ if (hasTaglines != null)
+ {
+ hasTaglines.Taglines = request.Taglines;
+ }
+
var hasShortOverview = item as IHasShortOverview;
if (hasShortOverview != null)
{
diff --git a/MediaBrowser.Api/MediaBrowser.Api.csproj b/MediaBrowser.Api/MediaBrowser.Api.csproj
index 1e9ff9199..3f1d9fe67 100644
--- a/MediaBrowser.Api/MediaBrowser.Api.csproj
+++ b/MediaBrowser.Api/MediaBrowser.Api.csproj
@@ -101,6 +101,7 @@
<Compile Include="NotificationsService.cs" />
<Compile Include="PackageReviewService.cs" />
<Compile Include="PackageService.cs" />
+ <Compile Include="Playback\BifService.cs" />
<Compile Include="Playback\EndlessStreamCopy.cs" />
<Compile Include="Playback\Hls\BaseHlsService.cs" />
<Compile Include="Playback\Hls\DynamicHlsService.cs" />
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
new file mode 100644
index 000000000..057d81441
--- /dev/null
+++ b/MediaBrowser.Api/Playback/BifService.cs
@@ -0,0 +1,186 @@
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.MediaEncoding;
+using ServiceStack;
+using System;
+using System.Collections.Concurrent;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Api.Playback
+{
+ [Route("/Videos/{Id}/index.bif", "GET")]
+ public class GetBifFile
+ {
+ [ApiMember(Name = "MediaSourceId", Description = "The media version id, if playing an alternate version", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
+ public string MediaSourceId { get; set; }
+
+ [ApiMember(Name = "MaxWidth", Description = "Optional. The maximum horizontal resolution of the encoded video.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
+ public int? MaxWidth { get; set; }
+
+ [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
+ public string Id { get; set; }
+ }
+
+ public class BifService : BaseApiService
+ {
+ private readonly IServerApplicationPaths _appPaths;
+ private readonly ILibraryManager _libraryManager;
+ private readonly IMediaEncoder _mediaEncoder;
+ private readonly IFileSystem _fileSystem;
+
+ public BifService(IServerApplicationPaths appPaths, ILibraryManager libraryManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem)
+ {
+ _appPaths = appPaths;
+ _libraryManager = libraryManager;
+ _mediaEncoder = mediaEncoder;
+ _fileSystem = fileSystem;
+ }
+
+ public object Get(GetBifFile request)
+ {
+ return ToStaticFileResult(GetBifFile(request).Result);
+ }
+
+ private async Task<string> GetBifFile(GetBifFile request)
+ {
+ var widthVal = request.MaxWidth.HasValue ? request.MaxWidth.Value.ToString(CultureInfo.InvariantCulture) : string.Empty;
+
+ var item = _libraryManager.GetItemById(request.Id);
+ var mediaSources = ((IHasMediaSources)item).GetMediaSources(false).ToList();
+ var mediaSource = mediaSources.FirstOrDefault(i => string.Equals(i.Id, request.MediaSourceId)) ?? mediaSources.First();
+
+ var path = Path.Combine(_appPaths.ImageCachePath, "bif", request.Id, request.MediaSourceId, widthVal, "index.bif");
+
+ if (File.Exists(path))
+ {
+ return path;
+ }
+
+ var protocol = mediaSource.Protocol;
+
+ var inputPath = MediaEncoderHelpers.GetInputArgument(mediaSource.Path, protocol, null, mediaSource.PlayableStreamFileNames);
+
+ var semaphore = GetLock(path);
+
+ await semaphore.WaitAsync().ConfigureAwait(false);
+
+ 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);
+
+ var images = new DirectoryInfo(Path.GetDirectoryName(path))
+ .EnumerateFiles()
+ .Where(img => string.Equals(img.Extension, ".jpg", StringComparison.Ordinal))
+ .OrderBy(i => i.FullName)
+ .ToList();
+
+ using (var fs = _fileSystem.GetFileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, true))
+ {
+ var magicNumber = new byte[] { 0x89, 0x42, 0x49, 0x46, 0x0d, 0x0a, 0x1a, 0x0a };
+ await fs.WriteAsync(magicNumber, 0, magicNumber.Length);
+
+ // version
+ var bytes = GetBytes(0);
+ await fs.WriteAsync(bytes, 0, bytes.Length);
+
+ // image count
+ bytes = GetBytes(images.Count);
+ await fs.WriteAsync(bytes, 0, bytes.Length);
+
+ // interval in ms
+ bytes = GetBytes(10000);
+ await fs.WriteAsync(bytes, 0, bytes.Length);
+
+ // reserved
+ for (var i = 20; i <= 63; i++)
+ {
+ bytes = new byte[] { 0x00 };
+ await fs.WriteAsync(bytes, 0, bytes.Length);
+ }
+
+ // write the bif index
+ var index = 0;
+ long imageOffset = 64 + (8 * images.Count) + 8;
+
+ foreach (var img in images)
+ {
+ bytes = GetBytes(index);
+ await fs.WriteAsync(bytes, 0, bytes.Length);
+
+ bytes = GetBytes(imageOffset);
+ await fs.WriteAsync(bytes, 0, bytes.Length);
+
+ imageOffset += img.Length;
+
+ index++;
+ }
+
+ bytes = new byte[] { 0xff, 0xff, 0xff, 0xff };
+ await fs.WriteAsync(bytes, 0, bytes.Length);
+
+ bytes = GetBytes(imageOffset);
+ await fs.WriteAsync(bytes, 0, bytes.Length);
+
+ // write the images
+ foreach (var img in images)
+ {
+ using (var imgStream = _fileSystem.GetFileStream(img.FullName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, true))
+ {
+ await imgStream.CopyToAsync(fs).ConfigureAwait(false);
+ }
+ }
+ }
+
+ return path;
+ }
+ finally
+ {
+ semaphore.Release();
+ }
+ }
+
+ private byte[] GetBytes(int value)
+ {
+ byte[] bytes = BitConverter.GetBytes(value);
+ if (BitConverter.IsLittleEndian)
+ Array.Reverse(bytes);
+ return bytes;
+ }
+
+ private byte[] GetBytes(long value)
+ {
+ var intVal = Convert.ToInt32(value);
+ return GetBytes(intVal);
+
+ //byte[] bytes = BitConverter.GetBytes(value);
+ //if (BitConverter.IsLittleEndian)
+ // Array.Reverse(bytes);
+ //return bytes;
+ }
+
+ private static readonly ConcurrentDictionary<string, SemaphoreSlim> SemaphoreLocks = new ConcurrentDictionary<string, SemaphoreSlim>();
+
+ /// <summary>
+ /// Gets the lock.
+ /// </summary>
+ /// <param name="filename">The filename.</param>
+ /// <returns>System.Object.</returns>
+ private static SemaphoreSlim GetLock(string filename)
+ {
+ return SemaphoreLocks.GetOrAdd(filename, key => new SemaphoreSlim(1, 1));
+ }
+ }
+}
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.Common.Implementations/BaseApplicationHost.cs b/MediaBrowser.Common.Implementations/BaseApplicationHost.cs
index 7bd0ca748..ebd6c6b59 100644
--- a/MediaBrowser.Common.Implementations/BaseApplicationHost.cs
+++ b/MediaBrowser.Common.Implementations/BaseApplicationHost.cs
@@ -342,6 +342,7 @@ namespace MediaBrowser.Common.Implementations
/// </summary>
protected virtual void FindParts()
{
+ ConfigurationManager.AddParts(GetExports<IConfigurationFactory>());
Plugins = GetExports<IPlugin>();
}
diff --git a/MediaBrowser.Common.Implementations/Configuration/BaseConfigurationManager.cs b/MediaBrowser.Common.Implementations/Configuration/BaseConfigurationManager.cs
index 8c4840ea7..60abc14f1 100644
--- a/MediaBrowser.Common.Implementations/Configuration/BaseConfigurationManager.cs
+++ b/MediaBrowser.Common.Implementations/Configuration/BaseConfigurationManager.cs
@@ -1,10 +1,13 @@
-using System.IO;
-using MediaBrowser.Common.Configuration;
+using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Events;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Serialization;
using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
using System.Threading;
namespace MediaBrowser.Common.Implementations.Configuration
@@ -26,6 +29,11 @@ namespace MediaBrowser.Common.Implementations.Configuration
public event EventHandler<EventArgs> ConfigurationUpdated;
/// <summary>
+ /// Occurs when [named configuration updated].
+ /// </summary>
+ public event EventHandler<ConfigurationUpdateEventArgs> NamedConfigurationUpdated;
+
+ /// <summary>
/// Gets the logger.
/// </summary>
/// <value>The logger.</value>
@@ -74,6 +82,9 @@ namespace MediaBrowser.Common.Implementations.Configuration
}
}
+ private ConfigurationStore[] _configurationStores = {};
+ private IConfigurationFactory[] _configurationFactories;
+
/// <summary>
/// Initializes a new instance of the <see cref="BaseConfigurationManager" /> class.
/// </summary>
@@ -89,10 +100,14 @@ namespace MediaBrowser.Common.Implementations.Configuration
UpdateCachePath();
}
- /// <summary>
- /// The _save lock
- /// </summary>
- private readonly object _configurationSaveLock = new object();
+ public void AddParts(IEnumerable<IConfigurationFactory> factories)
+ {
+ _configurationFactories = factories.ToArray();
+
+ _configurationStores = _configurationFactories
+ .SelectMany(i => i.GetConfigurations())
+ .ToArray();
+ }
/// <summary>
/// Saves the configuration.
@@ -103,7 +118,7 @@ namespace MediaBrowser.Common.Implementations.Configuration
Directory.CreateDirectory(Path.GetDirectoryName(path));
- lock (_configurationSaveLock)
+ lock (_configurationSyncLock)
{
XmlSerializer.SerializeToFile(CommonConfiguration, path);
}
@@ -144,8 +159,8 @@ namespace MediaBrowser.Common.Implementations.Configuration
/// </summary>
private void UpdateCachePath()
{
- ((BaseApplicationPaths)CommonApplicationPaths).CachePath = string.IsNullOrEmpty(CommonConfiguration.CachePath) ?
- null :
+ ((BaseApplicationPaths)CommonApplicationPaths).CachePath = string.IsNullOrEmpty(CommonConfiguration.CachePath) ?
+ null :
CommonConfiguration.CachePath;
}
@@ -168,5 +183,63 @@ namespace MediaBrowser.Common.Implementations.Configuration
}
}
}
+
+ private readonly ConcurrentDictionary<string, object> _configurations = new ConcurrentDictionary<string, object>();
+
+ private string GetConfigurationFile(string key)
+ {
+ return Path.Combine(CommonApplicationPaths.ConfigurationDirectoryPath, key.ToLower() + ".xml");
+ }
+
+ public object GetConfiguration(string key)
+ {
+ return _configurations.GetOrAdd(key, k =>
+ {
+ var file = GetConfigurationFile(key);
+
+ var configurationType = _configurationStores
+ .First(i => string.Equals(i.Key, key, StringComparison.OrdinalIgnoreCase))
+ .ConfigurationType;
+
+ lock (_configurationSyncLock)
+ {
+ return ConfigurationHelper.GetXmlConfiguration(configurationType, file, XmlSerializer);
+ }
+ });
+ }
+
+ public void SaveConfiguration(string key, object configuration)
+ {
+ var configurationType = GetConfigurationType(key);
+
+ if (configuration.GetType() != configurationType)
+ {
+ throw new ArgumentException("Expected configuration type is " + configurationType.Name);
+ }
+
+ _configurations.AddOrUpdate(key, configuration, (k, v) => configuration);
+
+ var path = GetConfigurationFile(key);
+ Directory.CreateDirectory(Path.GetDirectoryName(path));
+
+ lock (_configurationSyncLock)
+ {
+ XmlSerializer.SerializeToFile(configuration, path);
+ }
+
+ EventHelper.FireEventIfNotNull(NamedConfigurationUpdated, this, new ConfigurationUpdateEventArgs
+ {
+ Key = key,
+ NewConfiguration = configuration
+
+ }, Logger);
+ }
+
+ public Type GetConfigurationType(string key)
+ {
+ return _configurationStores
+ .First(i => string.Equals(i.Key, key, StringComparison.OrdinalIgnoreCase))
+ .ConfigurationType;
+ }
}
}
diff --git a/MediaBrowser.Common.Implementations/Updates/InstallationManager.cs b/MediaBrowser.Common.Implementations/Updates/InstallationManager.cs
index 895c43076..751ff55a5 100644
--- a/MediaBrowser.Common.Implementations/Updates/InstallationManager.cs
+++ b/MediaBrowser.Common.Implementations/Updates/InstallationManager.cs
@@ -68,7 +68,7 @@ namespace MediaBrowser.Common.Implementations.Updates
/// <param name="newVersion">The new version.</param>
private void OnPluginUpdated(IPlugin plugin, PackageVersionInfo newVersion)
{
- _logger.Info("Plugin updated: {0} {1} {2}", newVersion.name, newVersion.version, newVersion.classification);
+ _logger.Info("Plugin updated: {0} {1} {2}", newVersion.name, newVersion.versionStr ?? string.Empty, newVersion.classification);
EventHelper.FireEventIfNotNull(PluginUpdated, this, new GenericEventArgs<Tuple<IPlugin, PackageVersionInfo>> { Argument = new Tuple<IPlugin, PackageVersionInfo>(plugin, newVersion) }, _logger);
@@ -87,7 +87,7 @@ namespace MediaBrowser.Common.Implementations.Updates
/// <param name="package">The package.</param>
private void OnPluginInstalled(PackageVersionInfo package)
{
- _logger.Info("New plugin installed: {0} {1} {2}", package.name, package.version, package.classification);
+ _logger.Info("New plugin installed: {0} {1} {2}", package.name, package.versionStr ?? string.Empty, package.classification);
EventHelper.FireEventIfNotNull(PluginInstalled, this, new GenericEventArgs<PackageVersionInfo> { Argument = package }, _logger);
@@ -133,6 +133,16 @@ namespace MediaBrowser.Common.Implementations.Updates
_logger = logger;
}
+ private Version GetPackageVersion(PackageVersionInfo version)
+ {
+ return new Version(ValueOrDefault(version.versionStr, "0.0.0.1"));
+ }
+
+ private static string ValueOrDefault(string str, string def)
+ {
+ return string.IsNullOrEmpty(str) ? def : str;
+ }
+
/// <summary>
/// Gets all available packages.
/// </summary>
@@ -197,7 +207,7 @@ namespace MediaBrowser.Common.Implementations.Updates
foreach (var package in packages)
{
package.versions = package.versions.Where(v => !string.IsNullOrWhiteSpace(v.sourceUrl))
- .OrderByDescending(v => v.version).ToList();
+ .OrderByDescending(GetPackageVersion).ToList();
}
// Remove packages with no versions
@@ -211,7 +221,7 @@ namespace MediaBrowser.Common.Implementations.Updates
foreach (var package in packages)
{
package.versions = package.versions.Where(v => !string.IsNullOrWhiteSpace(v.sourceUrl))
- .OrderByDescending(v => v.version).ToList();
+ .OrderByDescending(GetPackageVersion).ToList();
}
if (packageType.HasValue)
@@ -272,7 +282,7 @@ namespace MediaBrowser.Common.Implementations.Updates
return null;
}
- return package.versions.FirstOrDefault(v => v.version.Equals(version) && v.classification == classification);
+ return package.versions.FirstOrDefault(v => GetPackageVersion(v).Equals(version) && v.classification == classification);
}
/// <summary>
@@ -309,7 +319,7 @@ namespace MediaBrowser.Common.Implementations.Updates
}
return package.versions
- .OrderByDescending(v => v.version)
+ .OrderByDescending(GetPackageVersion)
.FirstOrDefault(v => v.classification <= classification && IsPackageVersionUpToDate(v, currentServerVersion));
}
@@ -338,7 +348,7 @@ namespace MediaBrowser.Common.Implementations.Updates
{
var latestPluginInfo = GetLatestCompatibleVersion(catalog, p.Name, p.Id.ToString(), applicationVersion, _config.CommonConfiguration.SystemUpdateLevel);
- return latestPluginInfo != null && latestPluginInfo.version != null && latestPluginInfo.version > p.Version ? latestPluginInfo : null;
+ return latestPluginInfo != null && GetPackageVersion(latestPluginInfo) > p.Version ? latestPluginInfo : null;
}).Where(i => i != null).ToList();
diff --git a/MediaBrowser.Common/Configuration/ConfigurationUpdateEventArgs.cs b/MediaBrowser.Common/Configuration/ConfigurationUpdateEventArgs.cs
new file mode 100644
index 000000000..310e2aa63
--- /dev/null
+++ b/MediaBrowser.Common/Configuration/ConfigurationUpdateEventArgs.cs
@@ -0,0 +1,18 @@
+using System;
+
+namespace MediaBrowser.Common.Configuration
+{
+ public class ConfigurationUpdateEventArgs : EventArgs
+ {
+ /// <summary>
+ /// Gets or sets the key.
+ /// </summary>
+ /// <value>The key.</value>
+ public string Key { get; set; }
+ /// <summary>
+ /// Gets or sets the new configuration.
+ /// </summary>
+ /// <value>The new configuration.</value>
+ public object NewConfiguration { get; set; }
+ }
+}
diff --git a/MediaBrowser.Common/Configuration/IConfigurationFactory.cs b/MediaBrowser.Common/Configuration/IConfigurationFactory.cs
new file mode 100644
index 000000000..d418d0a42
--- /dev/null
+++ b/MediaBrowser.Common/Configuration/IConfigurationFactory.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+
+namespace MediaBrowser.Common.Configuration
+{
+ public interface IConfigurationFactory
+ {
+ IEnumerable<ConfigurationStore> GetConfigurations();
+ }
+
+ public class ConfigurationStore
+ {
+ public string Key { get; set; }
+
+ public Type ConfigurationType { get; set; }
+ }
+}
diff --git a/MediaBrowser.Common/Configuration/IConfigurationManager.cs b/MediaBrowser.Common/Configuration/IConfigurationManager.cs
index 0d0759b66..25698d972 100644
--- a/MediaBrowser.Common/Configuration/IConfigurationManager.cs
+++ b/MediaBrowser.Common/Configuration/IConfigurationManager.cs
@@ -1,5 +1,6 @@
using MediaBrowser.Model.Configuration;
using System;
+using System.Collections.Generic;
namespace MediaBrowser.Common.Configuration
{
@@ -9,7 +10,12 @@ namespace MediaBrowser.Common.Configuration
/// Occurs when [configuration updated].
/// </summary>
event EventHandler<EventArgs> ConfigurationUpdated;
-
+
+ /// <summary>
+ /// Occurs when [named configuration updated].
+ /// </summary>
+ event EventHandler<ConfigurationUpdateEventArgs> NamedConfigurationUpdated;
+
/// <summary>
/// Gets or sets the application paths.
/// </summary>
@@ -32,5 +38,40 @@ namespace MediaBrowser.Common.Configuration
/// </summary>
/// <param name="newConfiguration">The new configuration.</param>
void ReplaceConfiguration(BaseApplicationConfiguration newConfiguration);
+
+ /// <summary>
+ /// Gets the configuration.
+ /// </summary>
+ /// <param name="key">The key.</param>
+ /// <returns>System.Object.</returns>
+ object GetConfiguration(string key);
+
+ /// <summary>
+ /// Gets the type of the configuration.
+ /// </summary>
+ /// <param name="key">The key.</param>
+ /// <returns>Type.</returns>
+ Type GetConfigurationType(string key);
+
+ /// <summary>
+ /// Saves the configuration.
+ /// </summary>
+ /// <param name="key">The key.</param>
+ /// <param name="configuration">The configuration.</param>
+ void SaveConfiguration(string key, object configuration);
+
+ /// <summary>
+ /// Adds the parts.
+ /// </summary>
+ /// <param name="factories">The factories.</param>
+ void AddParts(IEnumerable<IConfigurationFactory> factories);
+ }
+
+ public static class ConfigurationManagerExtensions
+ {
+ public static T GetConfiguration<T>(this IConfigurationManager manager, string key)
+ {
+ return (T)manager.GetConfiguration(key);
+ }
}
}
diff --git a/MediaBrowser.Common/MediaBrowser.Common.csproj b/MediaBrowser.Common/MediaBrowser.Common.csproj
index 2e7db5c35..9d5984317 100644
--- a/MediaBrowser.Common/MediaBrowser.Common.csproj
+++ b/MediaBrowser.Common/MediaBrowser.Common.csproj
@@ -55,7 +55,9 @@
<Link>Properties\SharedVersion.cs</Link>
</Compile>
<Compile Include="Configuration\ConfigurationHelper.cs" />
+ <Compile Include="Configuration\ConfigurationUpdateEventArgs.cs" />
<Compile Include="Configuration\IConfigurationManager.cs" />
+ <Compile Include="Configuration\IConfigurationFactory.cs" />
<Compile Include="Constants\Constants.cs" />
<Compile Include="Events\EventHelper.cs" />
<Compile Include="Extensions\BaseExtensions.cs" />
diff --git a/MediaBrowser.Common/Net/MimeTypes.cs b/MediaBrowser.Common/Net/MimeTypes.cs
index d85a2fd1e..0cc4fc6b4 100644
--- a/MediaBrowser.Common/Net/MimeTypes.cs
+++ b/MediaBrowser.Common/Net/MimeTypes.cs
@@ -228,6 +228,11 @@ namespace MediaBrowser.Common.Net
return "text/vtt";
}
+ if (ext.Equals(".bif", StringComparison.OrdinalIgnoreCase))
+ {
+ return "application/octet-stream";
+ }
+
throw new ArgumentException("Argument not supported: " + path);
}
}
diff --git a/MediaBrowser.Controller/Chapters/IChapterManager.cs b/MediaBrowser.Controller/Chapters/IChapterManager.cs
index b8f29d1ce..676ef9c56 100644
--- a/MediaBrowser.Controller/Chapters/IChapterManager.cs
+++ b/MediaBrowser.Controller/Chapters/IChapterManager.cs
@@ -3,6 +3,7 @@ using MediaBrowser.Model.Chapters;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
namespace MediaBrowser.Controller.Chapters
@@ -70,5 +71,11 @@ namespace MediaBrowser.Controller.Chapters
/// </summary>
/// <returns>IEnumerable{ChapterProviderInfo}.</returns>
IEnumerable<ChapterProviderInfo> GetProviders();
+
+ /// <summary>
+ /// Gets the configuration.
+ /// </summary>
+ /// <returns>ChapterOptions.</returns>
+ ChapterOptions GetConfiguration();
}
}
diff --git a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs
index a4d9278e5..9ddd10f4a 100644
--- a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs
+++ b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs
@@ -72,6 +72,23 @@ namespace MediaBrowser.Controller.Entities.Audio
public List<string> Tags { get; set; }
/// <summary>
+ /// Gets the tracks.
+ /// </summary>
+ /// <value>The tracks.</value>
+ public IEnumerable<Audio> Tracks
+ {
+ get
+ {
+ return RecursiveChildren.OfType<Audio>();
+ }
+ }
+
+ protected override IEnumerable<BaseItem> GetEligibleChildrenForRecursiveChildren(User user)
+ {
+ return Tracks;
+ }
+
+ /// <summary>
/// Songs will group into us so don't also include us in the index
/// </summary>
/// <value><c>true</c> if [include in index]; otherwise, <c>false</c>.</value>
@@ -177,6 +194,7 @@ namespace MediaBrowser.Controller.Entities.Audio
}
}
+ [Obsolete]
public class MusicAlbumDisc : Folder
{
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.Controller/MediaEncoding/IMediaEncoder.cs b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
index 9468fd987..38c2c83c4 100644
--- a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
+++ b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
@@ -44,6 +44,27 @@ namespace MediaBrowser.Controller.MediaEncoding
Task<Stream> ExtractVideoImage(string[] inputFiles, MediaProtocol protocol, Video3DFormat? threedFormat, TimeSpan? offset, CancellationToken cancellationToken);
/// <summary>
+ /// Extracts the video images on interval.
+ /// </summary>
+ /// <param name="inputFiles">The input files.</param>
+ /// <param name="protocol">The protocol.</param>
+ /// <param name="threedFormat">The threed format.</param>
+ /// <param name="interval">The interval.</param>
+ /// <param name="targetDirectory">The target directory.</param>
+ /// <param name="filenamePrefix">The filename prefix.</param>
+ /// <param name="maxWidth">The maximum width.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>Task.</returns>
+ Task ExtractVideoImagesOnInterval(string[] inputFiles,
+ MediaProtocol protocol,
+ Video3DFormat? threedFormat,
+ TimeSpan interval,
+ string targetDirectory,
+ string filenamePrefix,
+ int? maxWidth,
+ CancellationToken cancellationToken);
+
+ /// <summary>
/// Gets the media info.
/// </summary>
/// <param name="inputFiles">The input files.</param>
diff --git a/MediaBrowser.Dlna/ConfigurationExtension.cs b/MediaBrowser.Dlna/ConfigurationExtension.cs
new file mode 100644
index 000000000..821e21ccf
--- /dev/null
+++ b/MediaBrowser.Dlna/ConfigurationExtension.cs
@@ -0,0 +1,29 @@
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Model.Configuration;
+using System.Collections.Generic;
+
+namespace MediaBrowser.Dlna
+{
+ public static class ConfigurationExtension
+ {
+ public static DlnaOptions GetDlnaConfiguration(this IConfigurationManager manager)
+ {
+ return manager.GetConfiguration<DlnaOptions>("dlna");
+ }
+ }
+
+ public class DlnaConfigurationFactory : IConfigurationFactory
+ {
+ public IEnumerable<ConfigurationStore> GetConfigurations()
+ {
+ return new List<ConfigurationStore>
+ {
+ new ConfigurationStore
+ {
+ Key = "dlna",
+ ConfigurationType = typeof (DlnaOptions)
+ }
+ };
+ }
+ }
+}
diff --git a/MediaBrowser.Dlna/ContentDirectory/ContentDirectory.cs b/MediaBrowser.Dlna/ContentDirectory/ContentDirectory.cs
index bb65f422c..f594b4471 100644
--- a/MediaBrowser.Dlna/ContentDirectory/ContentDirectory.cs
+++ b/MediaBrowser.Dlna/ContentDirectory/ContentDirectory.cs
@@ -89,9 +89,11 @@ namespace MediaBrowser.Dlna.ContentDirectory
}
}
- if (!string.IsNullOrEmpty(_config.Configuration.DlnaOptions.DefaultUserId))
+ var userId = _config.GetDlnaConfiguration().DefaultUserId;
+
+ if (!string.IsNullOrEmpty(userId))
{
- var user = _userManager.GetUserById(new Guid(_config.Configuration.DlnaOptions.DefaultUserId));
+ var user = _userManager.GetUserById(new Guid(userId));
if (user != null)
{
diff --git a/MediaBrowser.Model/Dlna/Filter.cs b/MediaBrowser.Dlna/Didl/Filter.cs
index a41997010..c980a2a2e 100644
--- a/MediaBrowser.Model/Dlna/Filter.cs
+++ b/MediaBrowser.Dlna/Didl/Filter.cs
@@ -1,8 +1,9 @@
using MediaBrowser.Model.Extensions;
using System;
using System.Collections.Generic;
+using System.Linq;
-namespace MediaBrowser.Model.Dlna
+namespace MediaBrowser.Dlna.Didl
{
public class Filter
{
@@ -19,9 +20,8 @@ namespace MediaBrowser.Model.Dlna
{
_all = StringHelper.EqualsIgnoreCase(filter, "*");
- List<string> list = new List<string>();
- foreach (string s in (filter ?? string.Empty).Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries))
- list.Add(s);
+ var list = (filter ?? string.Empty).Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries).ToList();
+
_fields = list;
}
diff --git a/MediaBrowser.Model/Dlna/EventSubscription.cs b/MediaBrowser.Dlna/Eventing/EventSubscription.cs
index 863ea508a..bd4cbdd55 100644
--- a/MediaBrowser.Model/Dlna/EventSubscription.cs
+++ b/MediaBrowser.Dlna/Eventing/EventSubscription.cs
@@ -1,6 +1,6 @@
using System;
-namespace MediaBrowser.Model.Dlna
+namespace MediaBrowser.Dlna.Eventing
{
public class EventSubscription
{
diff --git a/MediaBrowser.Dlna/Main/DlnaEntryPoint.cs b/MediaBrowser.Dlna/Main/DlnaEntryPoint.cs
index 048525588..5f2c1c49a 100644
--- a/MediaBrowser.Dlna/Main/DlnaEntryPoint.cs
+++ b/MediaBrowser.Dlna/Main/DlnaEntryPoint.cs
@@ -1,4 +1,5 @@
-using MediaBrowser.Common.Extensions;
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
@@ -58,34 +59,39 @@ namespace MediaBrowser.Dlna.Main
StartSsdpHandler();
ReloadComponents();
- _config.ConfigurationUpdated += ConfigurationUpdated;
+ _config.NamedConfigurationUpdated += _config_NamedConfigurationUpdated;
}
- void ConfigurationUpdated(object sender, EventArgs e)
+ void _config_NamedConfigurationUpdated(object sender, ConfigurationUpdateEventArgs e)
{
- ReloadComponents();
+ if (string.Equals(e.Key, "dlna", StringComparison.OrdinalIgnoreCase))
+ {
+ ReloadComponents();
+ }
}
private void ReloadComponents()
{
var isServerStarted = _dlnaServerStarted;
- if (_config.Configuration.DlnaOptions.EnableServer && !isServerStarted)
+ var options = _config.GetDlnaConfiguration();
+
+ if (options.EnableServer && !isServerStarted)
{
StartDlnaServer();
}
- else if (!_config.Configuration.DlnaOptions.EnableServer && isServerStarted)
+ else if (!options.EnableServer && isServerStarted)
{
DisposeDlnaServer();
}
var isPlayToStarted = _manager != null;
- if (_config.Configuration.DlnaOptions.EnablePlayTo && !isPlayToStarted)
+ if (options.EnablePlayTo && !isPlayToStarted)
{
StartPlayToManager();
}
- else if (!_config.Configuration.DlnaOptions.EnablePlayTo && isPlayToStarted)
+ else if (!options.EnablePlayTo && isPlayToStarted)
{
DisposePlayToManager();
}
diff --git a/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj b/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj
index 10da21e52..94f9c0391 100644
--- a/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj
+++ b/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj
@@ -51,13 +51,16 @@
<Compile Include="..\SharedVersion.cs">
<Link>Properties\SharedVersion.cs</Link>
</Compile>
+ <Compile Include="ConfigurationExtension.cs" />
<Compile Include="ConnectionManager\ConnectionManager.cs" />
<Compile Include="ConnectionManager\ConnectionManagerXmlBuilder.cs" />
<Compile Include="ConnectionManager\ControlHandler.cs" />
<Compile Include="ConnectionManager\ServiceActionListBuilder.cs" />
+ <Compile Include="Didl\Filter.cs" />
<Compile Include="DlnaManager.cs" />
<Compile Include="Common\Argument.cs" />
<Compile Include="Eventing\EventManager.cs" />
+ <Compile Include="Eventing\EventSubscription.cs" />
<Compile Include="Main\DlnaEntryPoint.cs" />
<Compile Include="PlayTo\CurrentIdEventArgs.cs" />
<Compile Include="PlayTo\Device.cs">
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.Dlna/PlayTo/PlayToManager.cs b/MediaBrowser.Dlna/PlayTo/PlayToManager.cs
index 095c6a893..1f8c33ee7 100644
--- a/MediaBrowser.Dlna/PlayTo/PlayToManager.cs
+++ b/MediaBrowser.Dlna/PlayTo/PlayToManager.cs
@@ -181,7 +181,7 @@ namespace MediaBrowser.Dlna.PlayTo
return;
}
- if (_config.Configuration.DlnaOptions.EnableDebugLogging)
+ if (_config.GetDlnaConfiguration().EnableDebugLogging)
{
var headerTexts = args.Headers.Select(i => string.Format("{0}={1}", i.Key, i.Value));
var headerText = string.Join(",", headerTexts.ToArray());
@@ -220,7 +220,7 @@ namespace MediaBrowser.Dlna.PlayTo
{
_ssdpHandler.SendRendererSearchMessage(new IPEndPoint(localIp, 1900));
- var delay = _config.Configuration.DlnaOptions.ClientDiscoveryIntervalSeconds * 1000;
+ var delay = _config.GetDlnaConfiguration().ClientDiscoveryIntervalSeconds * 1000;
await Task.Delay(delay, _tokenSource.Token).ConfigureAwait(false);
}
@@ -250,7 +250,7 @@ namespace MediaBrowser.Dlna.PlayTo
{
socket.SendTo(request, new IPEndPoint(IPAddress.Parse("239.255.255.250"), 1900));
- var delay = _config.Configuration.DlnaOptions.ClientDiscoveryIntervalSeconds * 1000;
+ var delay = _config.GetDlnaConfiguration().ClientDiscoveryIntervalSeconds * 1000;
await Task.Delay(delay).ConfigureAwait(false);
}
diff --git a/MediaBrowser.Dlna/PlayTo/SsdpHttpClient.cs b/MediaBrowser.Dlna/PlayTo/SsdpHttpClient.cs
index 22d4797a3..ccc7d46e6 100644
--- a/MediaBrowser.Dlna/PlayTo/SsdpHttpClient.cs
+++ b/MediaBrowser.Dlna/PlayTo/SsdpHttpClient.cs
@@ -58,7 +58,7 @@ namespace MediaBrowser.Dlna.PlayTo
{
Url = url,
UserAgent = USERAGENT,
- LogRequest = _config.Configuration.DlnaOptions.EnableDebugLogging,
+ LogRequest = _config.GetDlnaConfiguration().EnableDebugLogging,
LogErrorResponseBody = true
};
@@ -76,7 +76,7 @@ namespace MediaBrowser.Dlna.PlayTo
{
Url = url,
UserAgent = USERAGENT,
- LogRequest = _config.Configuration.DlnaOptions.EnableDebugLogging,
+ LogRequest = _config.GetDlnaConfiguration().EnableDebugLogging,
LogErrorResponseBody = true
};
@@ -103,7 +103,7 @@ namespace MediaBrowser.Dlna.PlayTo
{
Url = url,
UserAgent = USERAGENT,
- LogRequest = _config.Configuration.DlnaOptions.EnableDebugLogging,
+ LogRequest = _config.GetDlnaConfiguration().EnableDebugLogging,
LogErrorResponseBody = true
};
diff --git a/MediaBrowser.Dlna/Service/BaseControlHandler.cs b/MediaBrowser.Dlna/Service/BaseControlHandler.cs
index 9f7e87088..c2af1f5a1 100644
--- a/MediaBrowser.Dlna/Service/BaseControlHandler.cs
+++ b/MediaBrowser.Dlna/Service/BaseControlHandler.cs
@@ -28,7 +28,7 @@ namespace MediaBrowser.Dlna.Service
{
try
{
- if (Config.Configuration.DlnaOptions.EnableDebugLogging)
+ if (Config.GetDlnaConfiguration().EnableDebugLogging)
{
LogRequest(request);
}
diff --git a/MediaBrowser.Dlna/Ssdp/SsdpHandler.cs b/MediaBrowser.Dlna/Ssdp/SsdpHandler.cs
index 0fc7c579b..0455dd674 100644
--- a/MediaBrowser.Dlna/Ssdp/SsdpHandler.cs
+++ b/MediaBrowser.Dlna/Ssdp/SsdpHandler.cs
@@ -1,4 +1,4 @@
-using System.Text;
+using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Events;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Dlna.Server;
@@ -9,6 +9,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
+using System.Text;
using System.Threading;
namespace MediaBrowser.Dlna.Ssdp
@@ -42,12 +43,15 @@ namespace MediaBrowser.Dlna.Ssdp
_config = config;
_serverSignature = serverSignature;
- _config.ConfigurationUpdated += _config_ConfigurationUpdated;
+ _config.NamedConfigurationUpdated += _config_ConfigurationUpdated;
}
- void _config_ConfigurationUpdated(object sender, EventArgs e)
+ void _config_ConfigurationUpdated(object sender, ConfigurationUpdateEventArgs e)
{
- ReloadAliveNotifier();
+ if (string.Equals(e.Key, "dlna", StringComparison.OrdinalIgnoreCase))
+ {
+ ReloadAliveNotifier();
+ }
}
public event EventHandler<SsdpMessageEventArgs> MessageReceived;
@@ -142,7 +146,7 @@ namespace MediaBrowser.Dlna.Ssdp
private void RespondToSearch(IPEndPoint endpoint, string deviceType)
{
- if (_config.Configuration.DlnaOptions.EnableDebugLogging)
+ if (_config.GetDlnaConfiguration().EnableDebugLogging)
{
_logger.Debug("RespondToSearch");
}
@@ -166,7 +170,7 @@ namespace MediaBrowser.Dlna.Ssdp
SendDatagram(header, values, endpoint, null);
- if (_config.Configuration.DlnaOptions.EnableDebugLogging)
+ if (_config.GetDlnaConfiguration().EnableDebugLogging)
{
_logger.Debug("{1} - Responded to a {0} request to {2}", d.Type, endpoint, d.Address.ToString());
}
@@ -255,14 +259,14 @@ namespace MediaBrowser.Dlna.Ssdp
var received = (byte[])result.AsyncState;
- if (_config.Configuration.DlnaOptions.EnableDebugLogging)
+ if (_config.GetDlnaConfiguration().EnableDebugLogging)
{
_logger.Debug(Encoding.ASCII.GetString(received));
}
var args = SsdpHelper.ParseSsdpResponse(received, (IPEndPoint)endpoint);
- if (_config.Configuration.DlnaOptions.EnableDebugLogging)
+ if (_config.GetDlnaConfiguration().EnableDebugLogging)
{
var headerTexts = args.Headers.Select(i => string.Format("{0}={1}", i.Key, i.Value));
var headerText = string.Join(",", headerTexts.ToArray());
@@ -285,7 +289,7 @@ namespace MediaBrowser.Dlna.Ssdp
public void Dispose()
{
- _config.ConfigurationUpdated -= _config_ConfigurationUpdated;
+ _config.NamedConfigurationUpdated -= _config_ConfigurationUpdated;
_isDisposed = true;
while (_messageQueue.Count != 0)
@@ -337,7 +341,7 @@ namespace MediaBrowser.Dlna.Ssdp
private void NotifyAll()
{
- if (_config.Configuration.DlnaOptions.EnableDebugLogging)
+ if (_config.GetDlnaConfiguration().EnableDebugLogging)
{
_logger.Debug("Sending alive notifications");
}
@@ -362,7 +366,7 @@ namespace MediaBrowser.Dlna.Ssdp
values["NT"] = dev.Type;
values["USN"] = dev.USN;
- if (_config.Configuration.DlnaOptions.EnableDebugLogging)
+ if (_config.GetDlnaConfiguration().EnableDebugLogging)
{
_logger.Debug("{0} said {1}", dev.USN, type);
}
@@ -406,13 +410,13 @@ namespace MediaBrowser.Dlna.Ssdp
private int _aliveNotifierIntervalMs;
private void ReloadAliveNotifier()
{
- if (!_config.Configuration.DlnaOptions.BlastAliveMessages)
+ if (!_config.GetDlnaConfiguration().BlastAliveMessages)
{
DisposeNotificationTimer();
return;
}
- var intervalMs = _config.Configuration.DlnaOptions.BlastAliveMessageIntervalSeconds * 1000;
+ var intervalMs = _config.GetDlnaConfiguration().BlastAliveMessageIntervalSeconds * 1000;
if (_notificationTimer == null || _aliveNotifierIntervalMs != intervalMs)
{
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.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
index 1087c905c..ce761ff9c 100644
--- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
@@ -152,7 +152,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
RedirectStandardError = true,
FileName = FFProbePath,
Arguments = string.Format(args,
- probeSizeArgument, inputPath).Trim(),
+ probeSizeArgument, inputPath).Trim(),
WindowStyle = ProcessWindowStyle.Hidden,
ErrorDialog = false
@@ -186,8 +186,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
{
process.BeginErrorReadLine();
- result =
- _jsonSerializer.DeserializeFromStream<InternalMediaInfoResult>(process.StandardOutput.BaseStream);
+ result = _jsonSerializer.DeserializeFromStream<InternalMediaInfoResult>(process.StandardOutput.BaseStream);
}
catch
{
@@ -292,7 +291,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
// apply some filters to thumbnail extracted below (below) crop any black lines that we made and get the correct ar then scale to width 600.
// This filter chain may have adverse effects on recorded tv thumbnails if ar changes during presentation ex. commercials @ diff ar
var vf = "scale=600:trunc(600/dar/2)*2";
- //crop=min(iw\,ih*dar):min(ih\,iw/dar):(iw-min(iw\,iw*sar))/2:(ih - min (ih\,ih/sar))/2,scale=600:(600/dar),thumbnail" -f image2
if (threedFormat.HasValue)
{
@@ -344,7 +342,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
WindowStyle = ProcessWindowStyle.Hidden,
ErrorDialog = false,
RedirectStandardOutput = true,
- RedirectStandardError = true
+ RedirectStandardError = true,
+ RedirectStandardInput = true
}
};
@@ -370,7 +369,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
{
_logger.Info("Killing ffmpeg process");
- process.Kill();
+ process.StandardInput.WriteLine("q");
process.WaitForExit(1000);
}
@@ -437,5 +436,93 @@ namespace MediaBrowser.MediaEncoding.Encoder
{
return time.ToString(@"hh\:mm\:ss\.fff", UsCulture);
}
+
+ public async Task ExtractVideoImagesOnInterval(string[] inputFiles,
+ MediaProtocol protocol,
+ Video3DFormat? threedFormat,
+ TimeSpan interval,
+ string targetDirectory,
+ string filenamePrefix,
+ int? maxWidth,
+ CancellationToken cancellationToken)
+ {
+ var resourcePool = _videoImageResourcePool;
+
+ var inputArgument = GetInputArgument(inputFiles, protocol);
+
+ var vf = "fps=fps=1/" + interval.TotalSeconds.ToString(UsCulture);
+
+ if (maxWidth.HasValue)
+ {
+ var maxWidthParam = maxWidth.Value.ToString(UsCulture);
+
+ vf += string.Format(",scale=min(iw\\,{0}):trunc(ow/dar/2)*2", maxWidthParam);
+ }
+
+ Directory.CreateDirectory(targetDirectory);
+ var outputPath = Path.Combine(targetDirectory, filenamePrefix + "%05d.jpg");
+
+ var args = string.Format("-i {0} -threads 0 -v quiet -vf \"{2}\" -f image2 \"{1}\"", inputArgument, outputPath, vf);
+
+ var probeSize = GetProbeSizeArgument(new[] { inputArgument }, protocol);
+
+ if (!string.IsNullOrEmpty(probeSize))
+ {
+ args = probeSize + " " + args;
+ }
+
+ var process = new Process
+ {
+ StartInfo = new ProcessStartInfo
+ {
+ CreateNoWindow = true,
+ UseShellExecute = false,
+ FileName = FFMpegPath,
+ Arguments = args,
+ WindowStyle = ProcessWindowStyle.Hidden,
+ ErrorDialog = false,
+ RedirectStandardInput = true
+ }
+ };
+
+ _logger.Info(process.StartInfo.FileName + " " + process.StartInfo.Arguments);
+
+ await resourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
+
+ process.Start();
+
+ var ranToCompletion = process.WaitForExit(120000);
+
+ if (!ranToCompletion)
+ {
+ try
+ {
+ _logger.Info("Killing ffmpeg process");
+
+ process.StandardInput.WriteLine("q");
+
+ process.WaitForExit(1000);
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error killing process", ex);
+ }
+ }
+
+ resourcePool.Release();
+
+ var exitCode = ranToCompletion ? process.ExitCode : -1;
+
+ process.Dispose();
+
+ if (exitCode == -1)
+ {
+ var msg = string.Format("ffmpeg image extraction failed for {0}", inputArgument);
+
+ _logger.Error(msg);
+
+ throw new ApplicationException(msg);
+ }
+ }
}
}
diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
index 154541316..ab9cd546a 100644
--- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
@@ -342,12 +342,12 @@ namespace MediaBrowser.MediaEncoding.Subtitles
{
RedirectStandardOutput = false,
RedirectStandardError = true,
+ RedirectStandardInput = true,
CreateNoWindow = true,
UseShellExecute = false,
FileName = _mediaEncoder.EncoderPath,
- Arguments =
- string.Format("{0} -i \"{1}\" -c:s ass \"{2}\"", encodingParam, inputPath, outputPath),
+ Arguments = string.Format("{0} -i \"{1}\" -c:s ass \"{2}\"", encodingParam, inputPath, outputPath),
WindowStyle = ProcessWindowStyle.Hidden,
ErrorDialog = false
@@ -385,8 +385,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
{
_logger.Info("Killing ffmpeg subtitle conversion process");
- process.Kill();
-
+ process.StandardInput.WriteLine("q");
process.WaitForExit(1000);
await logTask.ConfigureAwait(false);
@@ -520,6 +519,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
RedirectStandardOutput = false,
RedirectStandardError = true,
+ RedirectStandardInput = true,
FileName = _mediaEncoder.EncoderPath,
Arguments = processArgs,
@@ -559,8 +559,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
{
_logger.Info("Killing ffmpeg subtitle extraction process");
- process.Kill();
-
+ process.StandardInput.WriteLine("q");
process.WaitForExit(1000);
}
catch (Exception ex)
diff --git a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj
index ee6f05cc1..2d19d335d 100644
--- a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj
+++ b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj
@@ -104,6 +104,12 @@
<Compile Include="..\MediaBrowser.Model\Configuration\BaseApplicationConfiguration.cs">
<Link>Configuration\BaseApplicationConfiguration.cs</Link>
</Compile>
+ <Compile Include="..\MediaBrowser.Model\Configuration\ChannelOptions.cs">
+ <Link>Configuration\ChannelOptions.cs</Link>
+ </Compile>
+ <Compile Include="..\MediaBrowser.Model\Configuration\ChapterOptions.cs">
+ <Link>Configuration\ChapterOptions.cs</Link>
+ </Compile>
<Compile Include="..\MediaBrowser.Model\Configuration\DlnaOptions.cs">
<Link>Configuration\DlnaOptions.cs</Link>
</Compile>
@@ -152,6 +158,9 @@
<Compile Include="..\MediaBrowser.Model\Configuration\SubtitleOptions.cs">
<Link>Configuration\SubtitleOptions.cs</Link>
</Compile>
+ <Compile Include="..\MediaBrowser.Model\Configuration\SubtitlePlaybackMode.cs">
+ <Link>Configuration\SubtitlePlaybackMode.cs</Link>
+ </Compile>
<Compile Include="..\MediaBrowser.Model\Configuration\TvFileOrganizationOptions.cs">
<Link>Configuration\TvFileOrganizationOptions.cs</Link>
</Compile>
@@ -161,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>
@@ -203,12 +215,6 @@
<Compile Include="..\MediaBrowser.Model\Dlna\DlnaProfileType.cs">
<Link>Dlna\DlnaProfileType.cs</Link>
</Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\EventSubscription.cs">
- <Link>Dlna\EventSubscription.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\Filter.cs">
- <Link>Dlna\Filter.cs</Link>
- </Compile>
<Compile Include="..\MediaBrowser.Model\Dlna\HeaderMatchType.cs">
<Link>Dlna\HeaderMatchType.cs</Link>
</Compile>
@@ -434,6 +440,9 @@
<Compile Include="..\MediaBrowser.Model\Extensions\DoubleHelper.cs">
<Link>Extensions\DoubleHelper.cs</Link>
</Compile>
+ <Compile Include="..\MediaBrowser.Model\Extensions\IHasPropertyChangedEvent.cs">
+ <Link>Extensions\IHasPropertyChangedEvent.cs</Link>
+ </Compile>
<Compile Include="..\MediaBrowser.Model\Extensions\IntHelper.cs">
<Link>Extensions\IntHelper.cs</Link>
</Compile>
diff --git a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj
index a2fa1aa94..c0c886c8a 100644
--- a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj
+++ b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj
@@ -91,6 +91,12 @@
<Compile Include="..\MediaBrowser.Model\Configuration\BaseApplicationConfiguration.cs">
<Link>Configuration\BaseApplicationConfiguration.cs</Link>
</Compile>
+ <Compile Include="..\MediaBrowser.Model\Configuration\ChannelOptions.cs">
+ <Link>Configuration\ChannelOptions.cs</Link>
+ </Compile>
+ <Compile Include="..\MediaBrowser.Model\Configuration\ChapterOptions.cs">
+ <Link>Configuration\ChapterOptions.cs</Link>
+ </Compile>
<Compile Include="..\MediaBrowser.Model\Configuration\DlnaOptions.cs">
<Link>Configuration\DlnaOptions.cs</Link>
</Compile>
@@ -139,6 +145,9 @@
<Compile Include="..\MediaBrowser.Model\Configuration\SubtitleOptions.cs">
<Link>Configuration\SubtitleOptions.cs</Link>
</Compile>
+ <Compile Include="..\MediaBrowser.Model\Configuration\SubtitlePlaybackMode.cs">
+ <Link>Configuration\SubtitlePlaybackMode.cs</Link>
+ </Compile>
<Compile Include="..\MediaBrowser.Model\Configuration\TvFileOrganizationOptions.cs">
<Link>Configuration\TvFileOrganizationOptions.cs</Link>
</Compile>
@@ -148,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>
@@ -190,12 +202,6 @@
<Compile Include="..\MediaBrowser.Model\Dlna\DlnaProfileType.cs">
<Link>Dlna\DlnaProfileType.cs</Link>
</Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\EventSubscription.cs">
- <Link>Dlna\EventSubscription.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\Filter.cs">
- <Link>Dlna\Filter.cs</Link>
- </Compile>
<Compile Include="..\MediaBrowser.Model\Dlna\HeaderMatchType.cs">
<Link>Dlna\HeaderMatchType.cs</Link>
</Compile>
@@ -421,6 +427,9 @@
<Compile Include="..\MediaBrowser.Model\Extensions\DoubleHelper.cs">
<Link>Extensions\DoubleHelper.cs</Link>
</Compile>
+ <Compile Include="..\MediaBrowser.Model\Extensions\IHasPropertyChangedEvent.cs">
+ <Link>Extensions\IHasPropertyChangedEvent.cs</Link>
+ </Compile>
<Compile Include="..\MediaBrowser.Model\Extensions\IntHelper.cs">
<Link>Extensions\IntHelper.cs</Link>
</Compile>
diff --git a/MediaBrowser.Model/Configuration/ChannelOptions.cs b/MediaBrowser.Model/Configuration/ChannelOptions.cs
new file mode 100644
index 000000000..f0fc4d47c
--- /dev/null
+++ b/MediaBrowser.Model/Configuration/ChannelOptions.cs
@@ -0,0 +1,18 @@
+namespace MediaBrowser.Model.Configuration
+{
+ public class ChannelOptions
+ {
+ public int? PreferredStreamingWidth { get; set; }
+
+ public string DownloadPath { get; set; }
+ public int? MaxDownloadAge { get; set; }
+
+ public string[] DownloadingChannels { get; set; }
+
+ public ChannelOptions()
+ {
+ DownloadingChannels = new string[] { };
+ MaxDownloadAge = 30;
+ }
+ }
+} \ No newline at end of file
diff --git a/MediaBrowser.Model/Configuration/ChapterOptions.cs b/MediaBrowser.Model/Configuration/ChapterOptions.cs
new file mode 100644
index 000000000..8a059a0a4
--- /dev/null
+++ b/MediaBrowser.Model/Configuration/ChapterOptions.cs
@@ -0,0 +1,27 @@
+namespace MediaBrowser.Model.Configuration
+{
+ public class ChapterOptions
+ {
+ public bool EnableMovieChapterImageExtraction { get; set; }
+ public bool EnableEpisodeChapterImageExtraction { get; set; }
+ public bool EnableOtherVideoChapterImageExtraction { get; set; }
+
+ public bool DownloadMovieChapters { get; set; }
+ public bool DownloadEpisodeChapters { get; set; }
+
+ public string[] FetcherOrder { get; set; }
+ public string[] DisabledFetchers { get; set; }
+
+ public ChapterOptions()
+ {
+ EnableMovieChapterImageExtraction = true;
+ EnableEpisodeChapterImageExtraction = false;
+ EnableOtherVideoChapterImageExtraction = false;
+
+ DownloadMovieChapters = true;
+
+ DisabledFetchers = new string[] { };
+ FetcherOrder = new string[] { };
+ }
+ }
+} \ No newline at end of file
diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs
index 0d728ec75..a5d19b5cb 100644
--- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs
+++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs
@@ -1,6 +1,6 @@
-using MediaBrowser.Model.Weather;
+using System.Linq;
+using MediaBrowser.Model.Weather;
using System;
-using System.Collections.Generic;
namespace MediaBrowser.Model.Configuration
{
@@ -70,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>
@@ -223,8 +211,11 @@ namespace MediaBrowser.Model.Configuration
public string[] ManualLoginClients { get; set; }
public ChannelOptions ChannelOptions { get; set; }
+
public ChapterOptions ChapterOptions { get; set; }
+ public bool DefaultMetadataSettingsApplied { get; set; }
+
/// <summary>
/// Initializes a new instance of the <see cref="ServerConfiguration" /> class.
/// </summary>
@@ -256,7 +247,6 @@ namespace MediaBrowser.Model.Configuration
PathSubstitutions = new PathSubstitution[] { };
- MetadataRefreshDays = 30;
PreferredMetadataLanguage = "en";
MetadataCountryCode = "US";
@@ -268,13 +258,11 @@ namespace MediaBrowser.Model.Configuration
SeasonZeroDisplayName = "Specials";
- LiveTvOptions = new LiveTvOptions();
-
- TvFileOrganizationOptions = new TvFileOrganizationOptions();
-
EnableRealtimeMonitor = true;
- List<MetadataOptions> options = new List<MetadataOptions>
+ UICulture = "en-us";
+
+ MetadataOptions = new[]
{
new MetadataOptions(1, 1280) {ItemType = "Book"},
new MetadataOptions(1, 1280) {ItemType = "MusicAlbum"},
@@ -282,59 +270,14 @@ namespace MediaBrowser.Model.Configuration
new MetadataOptions(0, 1280) {ItemType = "Season"}
};
- MetadataOptions = options.ToArray();
-
- DlnaOptions = new DlnaOptions();
-
- UICulture = "en-us";
-
NotificationOptions = new NotificationOptions();
SubtitleOptions = new SubtitleOptions();
ChannelOptions = new ChannelOptions();
- ChapterOptions = new ChapterOptions();
- }
- }
-
- public class ChannelOptions
- {
- public int? PreferredStreamingWidth { get; set; }
-
- public string DownloadPath { get; set; }
- public int? MaxDownloadAge { get; set; }
-
- public string[] DownloadingChannels { get; set; }
-
- public ChannelOptions()
- {
- DownloadingChannels = new string[] { };
- MaxDownloadAge = 30;
- }
- }
- public class ChapterOptions
- {
- public bool EnableMovieChapterImageExtraction { get; set; }
- public bool EnableEpisodeChapterImageExtraction { get; set; }
- public bool EnableOtherVideoChapterImageExtraction { get; set; }
-
- public bool DownloadMovieChapters { get; set; }
- public bool DownloadEpisodeChapters { get; set; }
-
- public string[] FetcherOrder { get; set; }
- public string[] DisabledFetchers { get; set; }
-
- public ChapterOptions()
- {
- EnableMovieChapterImageExtraction = true;
- EnableEpisodeChapterImageExtraction = false;
- EnableOtherVideoChapterImageExtraction = false;
-
- DownloadMovieChapters = true;
-
- DisabledFetchers = new string[] { };
- FetcherOrder = new string[] { };
+ LiveTvOptions = new LiveTvOptions();
+ TvFileOrganizationOptions = new TvFileOrganizationOptions();
}
}
}
diff --git a/MediaBrowser.Model/Configuration/SubtitlePlaybackMode.cs b/MediaBrowser.Model/Configuration/SubtitlePlaybackMode.cs
new file mode 100644
index 000000000..e6a3c3091
--- /dev/null
+++ b/MediaBrowser.Model/Configuration/SubtitlePlaybackMode.cs
@@ -0,0 +1,10 @@
+namespace MediaBrowser.Model.Configuration
+{
+ public enum SubtitlePlaybackMode
+ {
+ Default = 0,
+ Always = 1,
+ OnlyForced = 2,
+ None = 3
+ }
+} \ No newline at end of file
diff --git a/MediaBrowser.Model/Configuration/UserConfiguration.cs b/MediaBrowser.Model/Configuration/UserConfiguration.cs
index 885172bed..94a41bdda 100644
--- a/MediaBrowser.Model/Configuration/UserConfiguration.cs
+++ b/MediaBrowser.Model/Configuration/UserConfiguration.cs
@@ -91,12 +91,4 @@ namespace MediaBrowser.Model.Configuration
ExcludeFoldersFromGrouping = new string[] { };
}
}
-
- public enum SubtitlePlaybackMode
- {
- Default = 0,
- Always = 1,
- OnlyForced = 2,
- None = 3
- }
}
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/Dlna/ConditionProcessor.cs b/MediaBrowser.Model/Dlna/ConditionProcessor.cs
index 66c3e0b19..89b844ae5 100644
--- a/MediaBrowser.Model/Dlna/ConditionProcessor.cs
+++ b/MediaBrowser.Model/Dlna/ConditionProcessor.cs
@@ -175,6 +175,35 @@ namespace MediaBrowser.Model.Dlna
return false;
}
+
+ private bool IsConditionSatisfied(ProfileCondition condition, float? currentValue)
+ {
+ if (!currentValue.HasValue)
+ {
+ // If the value is unknown, it satisfies if not marked as required
+ return !condition.IsRequired;
+ }
+
+ float expected;
+ if (FloatHelper.TryParseCultureInvariant(condition.Value, out expected))
+ {
+ switch (condition.Condition)
+ {
+ case ProfileConditionType.Equals:
+ return currentValue.Value.Equals(expected);
+ case ProfileConditionType.GreaterThanEqual:
+ return currentValue.Value >= expected;
+ case ProfileConditionType.LessThanEqual:
+ return currentValue.Value <= expected;
+ case ProfileConditionType.NotEquals:
+ return !currentValue.Value.Equals(expected);
+ default:
+ throw new InvalidOperationException("Unexpected ProfileConditionType");
+ }
+ }
+
+ return false;
+ }
private bool IsConditionSatisfied(ProfileCondition condition, double? currentValue)
{
diff --git a/MediaBrowser.Model/Dto/BaseItemDto.cs b/MediaBrowser.Model/Dto/BaseItemDto.cs
index 1d20080dd..7f7a4b8f0 100644
--- a/MediaBrowser.Model/Dto/BaseItemDto.cs
+++ b/MediaBrowser.Model/Dto/BaseItemDto.cs
@@ -15,7 +15,7 @@ namespace MediaBrowser.Model.Dto
/// This holds information about a BaseItem in a format that is convenient for the client.
/// </summary>
[DebuggerDisplay("Name = {Name}, ID = {Id}, Type = {Type}")]
- public class BaseItemDto : IHasProviderIds, INotifyPropertyChanged, IItemDto
+ public class BaseItemDto : IHasProviderIds, IHasPropertyChangedEvent, IItemDto
{
/// <summary>
/// Gets or sets the name.
@@ -844,7 +844,7 @@ namespace MediaBrowser.Model.Dto
[IgnoreDataMember]
public bool IsVideo
{
- get { return StringHelper.EqualsIgnoreCase(MediaType, Entities.MediaType.Video); }
+ get { return StringHelper.EqualsIgnoreCase(MediaType, MediaBrowser.Model.Entities.MediaType.Video); }
}
/// <summary>
@@ -854,7 +854,7 @@ namespace MediaBrowser.Model.Dto
[IgnoreDataMember]
public bool IsAudio
{
- get { return StringHelper.EqualsIgnoreCase(MediaType, Entities.MediaType.Audio); }
+ get { return StringHelper.EqualsIgnoreCase(MediaType, MediaBrowser.Model.Entities.MediaType.Audio); }
}
/// <summary>
@@ -864,7 +864,7 @@ namespace MediaBrowser.Model.Dto
[IgnoreDataMember]
public bool IsGame
{
- get { return StringHelper.EqualsIgnoreCase(MediaType, Entities.MediaType.Game); }
+ get { return StringHelper.EqualsIgnoreCase(MediaType, MediaBrowser.Model.Entities.MediaType.Game); }
}
/// <summary>
diff --git a/MediaBrowser.Model/Dto/BaseItemPerson.cs b/MediaBrowser.Model/Dto/BaseItemPerson.cs
index b8393f444..46485316e 100644
--- a/MediaBrowser.Model/Dto/BaseItemPerson.cs
+++ b/MediaBrowser.Model/Dto/BaseItemPerson.cs
@@ -1,6 +1,7 @@
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.Serialization;
+using MediaBrowser.Model.Extensions;
namespace MediaBrowser.Model.Dto
{
@@ -8,7 +9,7 @@ namespace MediaBrowser.Model.Dto
/// This is used by the api to get information about a Person within a BaseItem
/// </summary>
[DebuggerDisplay("Name = {Name}, Role = {Role}, Type = {Type}")]
- public class BaseItemPerson : INotifyPropertyChanged
+ public class BaseItemPerson : IHasPropertyChangedEvent
{
/// <summary>
/// Gets or sets the name.
diff --git a/MediaBrowser.Model/Dto/ChapterInfoDto.cs b/MediaBrowser.Model/Dto/ChapterInfoDto.cs
index 1ec425169..62b1839d4 100644
--- a/MediaBrowser.Model/Dto/ChapterInfoDto.cs
+++ b/MediaBrowser.Model/Dto/ChapterInfoDto.cs
@@ -1,6 +1,7 @@
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.Serialization;
+using MediaBrowser.Model.Extensions;
namespace MediaBrowser.Model.Dto
{
@@ -8,7 +9,7 @@ namespace MediaBrowser.Model.Dto
/// Class ChapterInfo
/// </summary>
[DebuggerDisplay("Name = {Name}")]
- public class ChapterInfoDto : INotifyPropertyChanged
+ public class ChapterInfoDto : IHasPropertyChangedEvent
{
/// <summary>
/// Gets or sets the start position ticks.
diff --git a/MediaBrowser.Model/Dto/UserDto.cs b/MediaBrowser.Model/Dto/UserDto.cs
index c4a43c512..fec22e11c 100644
--- a/MediaBrowser.Model/Dto/UserDto.cs
+++ b/MediaBrowser.Model/Dto/UserDto.cs
@@ -3,6 +3,7 @@ using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.Serialization;
+using MediaBrowser.Model.Extensions;
namespace MediaBrowser.Model.Dto
{
@@ -10,7 +11,7 @@ namespace MediaBrowser.Model.Dto
/// Class UserDto
/// </summary>
[DebuggerDisplay("Name = {Name}, ID = {Id}, HasPassword = {HasPassword}")]
- public class UserDto : INotifyPropertyChanged, IItemDto
+ public class UserDto : IHasPropertyChangedEvent, IItemDto
{
/// <summary>
/// Gets or sets the name.
diff --git a/MediaBrowser.Model/Dto/UserItemDataDto.cs b/MediaBrowser.Model/Dto/UserItemDataDto.cs
index 8e8e7adbe..26b0e9d9e 100644
--- a/MediaBrowser.Model/Dto/UserItemDataDto.cs
+++ b/MediaBrowser.Model/Dto/UserItemDataDto.cs
@@ -1,12 +1,13 @@
using System;
using System.ComponentModel;
+using MediaBrowser.Model.Extensions;
namespace MediaBrowser.Model.Dto
{
/// <summary>
/// Class UserItemDataDto
/// </summary>
- public class UserItemDataDto : INotifyPropertyChanged
+ public class UserItemDataDto : IHasPropertyChangedEvent
{
/// <summary>
/// Gets or sets the rating.
diff --git a/MediaBrowser.Model/Entities/DisplayPreferences.cs b/MediaBrowser.Model/Entities/DisplayPreferences.cs
index 56a2c6194..cac703c91 100644
--- a/MediaBrowser.Model/Entities/DisplayPreferences.cs
+++ b/MediaBrowser.Model/Entities/DisplayPreferences.cs
@@ -2,13 +2,14 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
+using MediaBrowser.Model.Extensions;
namespace MediaBrowser.Model.Entities
{
/// <summary>
/// Defines the display preferences for any item that supports them (usually Folders)
/// </summary>
- public class DisplayPreferences : INotifyPropertyChanged
+ public class DisplayPreferences : IHasPropertyChangedEvent
{
/// <summary>
/// Occurs when [property changed].
diff --git a/MediaBrowser.Model/Entities/MediaStream.cs b/MediaBrowser.Model/Entities/MediaStream.cs
index 7be8a64b7..fe05483e7 100644
--- a/MediaBrowser.Model/Entities/MediaStream.cs
+++ b/MediaBrowser.Model/Entities/MediaStream.cs
@@ -136,7 +136,7 @@ namespace MediaBrowser.Model.Entities
{
if (Type != MediaStreamType.Subtitle) return false;
- var codec = Codec ?? string.Empty;
+ string codec = Codec ?? string.Empty;
return StringHelper.IndexOfIgnoreCase(codec, "pgs") == -1 &&
StringHelper.IndexOfIgnoreCase(codec, "dvd") == -1;
diff --git a/MediaBrowser.Model/Extensions/IHasPropertyChangedEvent.cs b/MediaBrowser.Model/Extensions/IHasPropertyChangedEvent.cs
new file mode 100644
index 000000000..c87550620
--- /dev/null
+++ b/MediaBrowser.Model/Extensions/IHasPropertyChangedEvent.cs
@@ -0,0 +1,8 @@
+using System.ComponentModel;
+
+namespace MediaBrowser.Model.Extensions
+{
+ public interface IHasPropertyChangedEvent : INotifyPropertyChanged
+ {
+ }
+}
diff --git a/MediaBrowser.Model/Extensions/ListHelper.cs b/MediaBrowser.Model/Extensions/ListHelper.cs
index 6c5d471c0..d929866bd 100644
--- a/MediaBrowser.Model/Extensions/ListHelper.cs
+++ b/MediaBrowser.Model/Extensions/ListHelper.cs
@@ -6,7 +6,16 @@ namespace MediaBrowser.Model.Extensions
{
public static class ListHelper
{
- public static bool ContainsIgnoreCase(IEnumerable<string> list, string value)
+ public static bool ContainsIgnoreCase(List<string> list, string value)
+ {
+ if (value == null)
+ {
+ throw new ArgumentNullException("value");
+ }
+
+ return list.Contains(value, StringComparer.OrdinalIgnoreCase);
+ }
+ public static bool ContainsIgnoreCase(string[] list, string value)
{
if (value == null)
{
diff --git a/MediaBrowser.Model/LiveTv/BaseTimerInfoDto.cs b/MediaBrowser.Model/LiveTv/BaseTimerInfoDto.cs
index 2d88215bb..27001f721 100644
--- a/MediaBrowser.Model/LiveTv/BaseTimerInfoDto.cs
+++ b/MediaBrowser.Model/LiveTv/BaseTimerInfoDto.cs
@@ -1,9 +1,10 @@
using System;
using System.ComponentModel;
+using MediaBrowser.Model.Extensions;
namespace MediaBrowser.Model.LiveTv
{
- public class BaseTimerInfoDto : INotifyPropertyChanged
+ public class BaseTimerInfoDto : IHasPropertyChangedEvent
{
/// <summary>
/// Occurs when a property value changes.
diff --git a/MediaBrowser.Model/LiveTv/ChannelInfoDto.cs b/MediaBrowser.Model/LiveTv/ChannelInfoDto.cs
index a48006755..351378eea 100644
--- a/MediaBrowser.Model/LiveTv/ChannelInfoDto.cs
+++ b/MediaBrowser.Model/LiveTv/ChannelInfoDto.cs
@@ -1,5 +1,6 @@
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.Library;
using System.Collections.Generic;
using System.ComponentModel;
@@ -12,7 +13,7 @@ namespace MediaBrowser.Model.LiveTv
/// Class ChannelInfoDto
/// </summary>
[DebuggerDisplay("Name = {Name}, Number = {Number}")]
- public class ChannelInfoDto : INotifyPropertyChanged, IItemDto
+ public class ChannelInfoDto : IHasPropertyChangedEvent, IItemDto
{
/// <summary>
/// Gets or sets the name.
diff --git a/MediaBrowser.Model/LiveTv/ProgramInfoDto.cs b/MediaBrowser.Model/LiveTv/ProgramInfoDto.cs
index 644af027d..50881f72f 100644
--- a/MediaBrowser.Model/LiveTv/ProgramInfoDto.cs
+++ b/MediaBrowser.Model/LiveTv/ProgramInfoDto.cs
@@ -5,12 +5,13 @@ using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using System;
using System.Collections.Generic;
+using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.Library;
namespace MediaBrowser.Model.LiveTv
{
[DebuggerDisplay("Name = {Name}, StartTime = {StartDate}, EndTime = {EndDate}")]
- public class ProgramInfoDto : INotifyPropertyChanged, IItemDto
+ public class ProgramInfoDto : IHasPropertyChangedEvent, IItemDto
{
/// <summary>
/// Id of the program.
diff --git a/MediaBrowser.Model/LiveTv/RecordingGroupDto.cs b/MediaBrowser.Model/LiveTv/RecordingGroupDto.cs
index db96d56b4..07c5ac32f 100644
--- a/MediaBrowser.Model/LiveTv/RecordingGroupDto.cs
+++ b/MediaBrowser.Model/LiveTv/RecordingGroupDto.cs
@@ -1,5 +1,6 @@
using System.ComponentModel;
using System.Diagnostics;
+using MediaBrowser.Model.Extensions;
namespace MediaBrowser.Model.LiveTv
{
@@ -7,7 +8,7 @@ namespace MediaBrowser.Model.LiveTv
/// Class RecordingGroupDto.
/// </summary>
[DebuggerDisplay("Name = {Name}, Count = {RecordingCount}")]
- public class RecordingGroupDto : INotifyPropertyChanged
+ public class RecordingGroupDto : IHasPropertyChangedEvent
{
/// <summary>
/// Gets or sets the name.
diff --git a/MediaBrowser.Model/LiveTv/RecordingInfoDto.cs b/MediaBrowser.Model/LiveTv/RecordingInfoDto.cs
index 58bca06bd..43ae9681e 100644
--- a/MediaBrowser.Model/LiveTv/RecordingInfoDto.cs
+++ b/MediaBrowser.Model/LiveTv/RecordingInfoDto.cs
@@ -1,5 +1,6 @@
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.Library;
using System;
using System.Collections.Generic;
@@ -10,7 +11,7 @@ using System.Runtime.Serialization;
namespace MediaBrowser.Model.LiveTv
{
[DebuggerDisplay("Name = {Name}, ChannelName = {ChannelName}")]
- public class RecordingInfoDto : INotifyPropertyChanged, IItemDto
+ public class RecordingInfoDto : IHasPropertyChangedEvent, IItemDto
{
/// <summary>
/// Id of the recording.
diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj
index 9efa63283..d2b0a8caf 100644
--- a/MediaBrowser.Model/MediaBrowser.Model.csproj
+++ b/MediaBrowser.Model/MediaBrowser.Model.csproj
@@ -73,6 +73,10 @@
<Compile Include="Channels\ChannelQuery.cs" />
<Compile Include="Chapters\RemoteChapterInfo.cs" />
<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" />
<Compile Include="Configuration\DlnaOptions.cs" />
@@ -106,8 +110,6 @@
<Compile Include="Dlna\DlnaFlags.cs" />
<Compile Include="Dlna\DlnaMaps.cs" />
<Compile Include="Dlna\DlnaProfileType.cs" />
- <Compile Include="Dlna\EventSubscription.cs" />
- <Compile Include="Dlna\Filter.cs" />
<Compile Include="Dlna\HeaderMatchType.cs" />
<Compile Include="Dlna\HttpHeaderInfo.cs" />
<Compile Include="Dlna\MediaFormatProfile.cs" />
@@ -152,6 +154,7 @@
<Compile Include="Entities\VideoSize.cs" />
<Compile Include="Events\GenericEventArgs.cs" />
<Compile Include="Extensions\DoubleHelper.cs" />
+ <Compile Include="Extensions\IHasPropertyChangedEvent.cs" />
<Compile Include="Extensions\IntHelper.cs" />
<Compile Include="Extensions\ListHelper.cs" />
<Compile Include="Extensions\StringHelper.cs" />
diff --git a/MediaBrowser.Model/Notifications/NotificationRequest.cs b/MediaBrowser.Model/Notifications/NotificationRequest.cs
index d47e9c4f2..f511d41a9 100644
--- a/MediaBrowser.Model/Notifications/NotificationRequest.cs
+++ b/MediaBrowser.Model/Notifications/NotificationRequest.cs
@@ -1,6 +1,6 @@
-using System;
+using MediaBrowser.Model.Configuration;
+using System;
using System.Collections.Generic;
-using MediaBrowser.Model.Configuration;
namespace MediaBrowser.Model.Notifications
{
@@ -34,7 +34,7 @@ namespace MediaBrowser.Model.Notifications
UserIds = new List<string>();
Date = DateTime.UtcNow;
- Variables = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
+ Variables = new Dictionary<string, string>();
ExcludeUserIds = new List<string>();
}
diff --git a/MediaBrowser.Model/Session/GeneralCommand.cs b/MediaBrowser.Model/Session/GeneralCommand.cs
index 7e818245d..ae9ab3adc 100644
--- a/MediaBrowser.Model/Session/GeneralCommand.cs
+++ b/MediaBrowser.Model/Session/GeneralCommand.cs
@@ -1,5 +1,4 @@
-using System;
-using System.Collections.Generic;
+using System.Collections.Generic;
namespace MediaBrowser.Model.Session
{
@@ -13,7 +12,7 @@ namespace MediaBrowser.Model.Session
public GeneralCommand()
{
- Arguments = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
+ Arguments = new Dictionary<string, string>();
}
}
}
diff --git a/MediaBrowser.Model/Session/SessionInfoDto.cs b/MediaBrowser.Model/Session/SessionInfoDto.cs
index 7217e28f4..40723eff8 100644
--- a/MediaBrowser.Model/Session/SessionInfoDto.cs
+++ b/MediaBrowser.Model/Session/SessionInfoDto.cs
@@ -3,11 +3,12 @@ using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
+using MediaBrowser.Model.Extensions;
namespace MediaBrowser.Model.Session
{
[DebuggerDisplay("Client = {Client}, Username = {UserName}")]
- public class SessionInfoDto : INotifyPropertyChanged
+ public class SessionInfoDto : IHasPropertyChangedEvent
{
/// <summary>
/// Gets or sets a value indicating whether this instance can seek.
diff --git a/MediaBrowser.Model/Themes/AppTheme.cs b/MediaBrowser.Model/Themes/AppTheme.cs
index 40a729963..527f1de72 100644
--- a/MediaBrowser.Model/Themes/AppTheme.cs
+++ b/MediaBrowser.Model/Themes/AppTheme.cs
@@ -1,5 +1,4 @@
-using System;
-using System.Collections.Generic;
+using System.Collections.Generic;
namespace MediaBrowser.Model.Themes
{
@@ -15,7 +14,7 @@ namespace MediaBrowser.Model.Themes
public AppTheme()
{
- Options = new Dictionary<string, string>(StringComparer.Ordinal);
+ Options = new Dictionary<string, string>();
Images = new List<ThemeImage>();
}
diff --git a/MediaBrowser.Model/Updates/CheckForUpdateResult.cs b/MediaBrowser.Model/Updates/CheckForUpdateResult.cs
index c9bc2d6b9..ff0bba197 100644
--- a/MediaBrowser.Model/Updates/CheckForUpdateResult.cs
+++ b/MediaBrowser.Model/Updates/CheckForUpdateResult.cs
@@ -1,5 +1,4 @@
-using System;
-
+
namespace MediaBrowser.Model.Updates
{
/// <summary>
@@ -17,9 +16,9 @@ namespace MediaBrowser.Model.Updates
/// Gets or sets the available version.
/// </summary>
/// <value>The available version.</value>
- public Version AvailableVersion
+ public string AvailableVersion
{
- get { return Package != null ? Package.version : new Version(0, 0); }
+ get { return Package != null ? Package.versionStr : "0.0.0.1"; }
set { } // need this for the serializer
}
diff --git a/MediaBrowser.Model/Updates/PackageVersionInfo.cs b/MediaBrowser.Model/Updates/PackageVersionInfo.cs
index b3d297e8e..de8f4e8b8 100644
--- a/MediaBrowser.Model/Updates/PackageVersionInfo.cs
+++ b/MediaBrowser.Model/Updates/PackageVersionInfo.cs
@@ -1,6 +1,4 @@
-using System;
-using System.Runtime.Serialization;
-
+
namespace MediaBrowser.Model.Updates
{
/// <summary>
@@ -27,32 +25,6 @@ namespace MediaBrowser.Model.Updates
public string versionStr { get; set; }
/// <summary>
- /// The _version
- /// </summary>
- private Version _version;
- /// <summary>
- /// Gets or sets the version.
- /// Had to make this an interpreted property since Protobuf can't handle Version
- /// </summary>
- /// <value>The version.</value>
- [IgnoreDataMember]
- public Version version
- {
- get { return _version ?? (_version = new Version(ValueOrDefault(versionStr, "0.0.0.1"))); }
- }
-
- /// <summary>
- /// Values the or default.
- /// </summary>
- /// <param name="str">The STR.</param>
- /// <param name="def">The def.</param>
- /// <returns>System.String.</returns>
- private static string ValueOrDefault(string str, string def)
- {
- return string.IsNullOrEmpty(str) ? def : str;
- }
-
- /// <summary>
/// Gets or sets the classification.
/// </summary>
/// <value>The classification.</value>
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/Chapters/ChapterManager.cs b/MediaBrowser.Providers/Chapters/ChapterManager.cs
index 5f8664bec..6e2cd77eb 100644
--- a/MediaBrowser.Providers/Chapters/ChapterManager.cs
+++ b/MediaBrowser.Providers/Chapters/ChapterManager.cs
@@ -1,4 +1,5 @@
-using MediaBrowser.Common.Extensions;
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Chapters;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
@@ -8,6 +9,7 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Chapters;
+using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using System;
@@ -192,8 +194,10 @@ namespace MediaBrowser.Providers.Chapters
if (!includeDisabledProviders)
{
+ var options = GetConfiguration();
+
providers = providers
- .Where(i => !_config.Configuration.ChapterOptions.DisabledFetchers.Contains(i.Name))
+ .Where(i => !options.DisabledFetchers.Contains(i.Name))
.ToArray();
}
@@ -224,8 +228,10 @@ namespace MediaBrowser.Providers.Chapters
private int GetConfiguredOrder(IChapterProvider provider)
{
+ var options = GetConfiguration();
+
// See if there's a user-defined order
- var index = Array.IndexOf(_config.Configuration.ChapterOptions.FetcherOrder, provider.Name);
+ var index = Array.IndexOf(options.FetcherOrder, provider.Name);
if (index != -1)
{
@@ -257,5 +263,25 @@ namespace MediaBrowser.Providers.Chapters
{
return _itemRepo.SaveChapters(new Guid(itemId), chapters, cancellationToken);
}
+
+ public ChapterOptions GetConfiguration()
+ {
+ return _config.GetConfiguration<ChapterOptions>("chapters");
+ }
+ }
+
+ public class ChapterConfigurationStore : IConfigurationFactory
+ {
+ public IEnumerable<ConfigurationStore> GetConfigurations()
+ {
+ return new List<ConfigurationStore>
+ {
+ new ConfigurationStore
+ {
+ Key = "chapters",
+ ConfigurationType = typeof (ChapterOptions)
+ }
+ };
+ }
}
}
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.Providers/MediaInfo/FFProbeVideoInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs
index 65faae327..92b4616e7 100644
--- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs
+++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs
@@ -492,9 +492,11 @@ namespace MediaBrowser.Providers.MediaInfo
private async Task<List<ChapterInfo>> DownloadChapters(Video video, List<ChapterInfo> currentChapters, CancellationToken cancellationToken)
{
- if ((_config.Configuration.ChapterOptions.DownloadEpisodeChapters &&
+ var options = _chapterManager.GetConfiguration();
+
+ if ((options.DownloadEpisodeChapters &&
video is Episode) ||
- (_config.Configuration.ChapterOptions.DownloadMovieChapters &&
+ (options.DownloadMovieChapters &&
video is Movie))
{
var results = await _chapterManager.Search(video, cancellationToken).ConfigureAwait(false);
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs
index 034894670..64d2db2e4 100644
--- a/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs
+++ b/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs
@@ -45,7 +45,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
{
return null;
}
-
+
var collectionType = args.GetCollectionType();
// If there's a collection type and it's not music, don't allow it.
@@ -54,7 +54,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
{
return null;
}
-
+
return IsMusicAlbum(args) ? new MusicAlbum() : null;
}
@@ -67,7 +67,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
/// <returns><c>true</c> if [is music album] [the specified data]; otherwise, <c>false</c>.</returns>
public static bool IsMusicAlbum(string path, IDirectoryService directoryService)
{
- return ContainsMusic(directoryService.GetFileSystemEntries(path));
+ return ContainsMusic(directoryService.GetFileSystemEntries(path), true, directoryService);
}
/// <summary>
@@ -81,7 +81,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
if (args.IsDirectory)
{
//if (args.Parent is MusicArtist) return true; //saves us from testing children twice
- if (ContainsMusic(args.FileSystemChildren)) return true;
+ if (ContainsMusic(args.FileSystemChildren, true, args.DirectoryService)) return true;
}
return false;
@@ -91,18 +91,26 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
/// Determine if the supplied list contains what we should consider music
/// </summary>
/// <param name="list">The list.</param>
+ /// <param name="allowSubfolders">if set to <c>true</c> [allow subfolders].</param>
+ /// <param name="directoryService">The directory service.</param>
/// <returns><c>true</c> if the specified list contains music; otherwise, <c>false</c>.</returns>
- private static bool ContainsMusic(IEnumerable<FileSystemInfo> list)
+ private static bool ContainsMusic(IEnumerable<FileSystemInfo> list, bool allowSubfolders, IDirectoryService directoryService)
{
// If list contains at least 2 audio files or at least one and no video files consider it to contain music
var foundAudio = 0;
foreach (var fileSystemInfo in list)
{
- // TODO: Support disc 1, disc 2, etc
if ((fileSystemInfo.Attributes & FileAttributes.Directory) == FileAttributes.Directory)
{
- continue;
+ if (allowSubfolders && IsAlbumSubfolder(fileSystemInfo, directoryService))
+ {
+ return true;
+ }
+ if (!IsAdditionalSubfolderAllowed(fileSystemInfo))
+ {
+ return false;
+ }
}
var fullName = fileSystemInfo.FullName;
@@ -129,5 +137,28 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
// or a single audio file and no video files
return foundAudio > 0;
}
+
+ private static bool IsAlbumSubfolder(FileSystemInfo directory, IDirectoryService directoryService)
+ {
+ var path = directory.FullName;
+
+ if (IsMultiDiscFolder(path))
+ {
+ return ContainsMusic(directoryService.GetFileSystemEntries(path), false, directoryService);
+ }
+
+ return false;
+ }
+
+ private static bool IsMultiDiscFolder(string path)
+ {
+ return EntityResolutionHelper.IsMultiPartFolder(path);
+ }
+
+ private static bool IsAdditionalSubfolderAllowed(FileSystemInfo directory)
+ {
+ // Resolver will ignore them based on rules engine
+ return true;
+ }
}
}
diff --git a/MediaBrowser.Server.Implementations/Localization/JavaScript/de.json b/MediaBrowser.Server.Implementations/Localization/JavaScript/de.json
index e48b788b0..9fa6b0ca2 100644
--- a/MediaBrowser.Server.Implementations/Localization/JavaScript/de.json
+++ b/MediaBrowser.Server.Implementations/Localization/JavaScript/de.json
@@ -55,22 +55,22 @@
"LabelDefaultStream": "(Default)",
"LabelForcedStream": "(Forced)",
"LabelDefaultForcedStream": "(Default\/Forced)",
- "LabelUnknownLanguage": "Unknown language",
+ "LabelUnknownLanguage": "Unbekannte Sprache",
"ButtonMute": "Stumm",
"ButtonUnmute": "Unmute",
"ButtonNextTrack": "N\u00e4chster Track",
"ButtonPause": "Pause",
"ButtonPlay": "Abspielen",
"ButtonEdit": "Bearbeiten",
- "ButtonQueue": "Queue",
+ "ButtonQueue": "Warteschlange",
"ButtonPlayTrailer": "Spiele Trailer",
"ButtonPlaylist": "Playlist",
"ButtonPreviousTrack": "Vorheriger Track",
- "LabelEnabled": "Enabled",
- "LabelDisabled": "Disabled",
- "ButtonMoreInformation": "More Information",
- "LabelNoUnreadNotifications": "No unread notifications.",
- "ButtonViewNotifications": "View notifications",
+ "LabelEnabled": "Aktivieren",
+ "LabelDisabled": "Deaktivieren",
+ "ButtonMoreInformation": "mehr Informationen",
+ "LabelNoUnreadNotifications": "keine ungelesenen Benachrichtigungen",
+ "ButtonViewNotifications": "Benachrichtigungen anschauen",
"ButtonMarkTheseRead": "Mark these read",
"ButtonClose": "Close",
"LabelAllPlaysSentToPlayer": "All plays will be sent to the selected player.",
@@ -100,7 +100,7 @@
"OptionSaturday": "Samstag",
"HeaderConfirmDeletion": "Confirm Deletion",
"MessageConfirmPathSubstitutionDeletion": "Are you sure you wish to delete this path substitution?",
- "LiveTvUpdateAvailable": "(Update available)",
+ "LiveTvUpdateAvailable": "(Update verf\u00fcgbar)",
"LabelVersionUpToDate": "Auf dem neuesten Stand!",
"ButtonResetTuner": "Tuner zur\u00fccksetzen",
"HeaderResetTuner": "Tuner zur\u00fccksetzen",
@@ -109,8 +109,8 @@
"LabelAllChannels": "Alle Channel",
"HeaderSeriesRecordings": "Series Recordings",
"LabelAnytime": "Jederzeit",
- "StatusRecording": "Recording",
- "StatusWatching": "Watching",
+ "StatusRecording": "Aufnehmen",
+ "StatusWatching": "Anschauing",
"StatusRecordingProgram": "Recording {0}",
"StatusWatchingProgram": "Watching {0}",
"HeaderSplitMedia": "Trenne Medien ab",
diff --git a/MediaBrowser.Server.Implementations/Localization/JavaScript/es.json b/MediaBrowser.Server.Implementations/Localization/JavaScript/es.json
index f4f126422..3873a10fc 100644
--- a/MediaBrowser.Server.Implementations/Localization/JavaScript/es.json
+++ b/MediaBrowser.Server.Implementations/Localization/JavaScript/es.json
@@ -121,7 +121,7 @@
"MessageTheFollowingItemsWillBeGrouped": "Los siguientes t\u00edtulos se agrupar\u00e1n en un elemento.",
"MessageConfirmItemGrouping": "Los clientes de Media Browser elegir\u00e1n autom\u00e1ticamente la mejor forma de reproduccion sobre la base de dispositivo y rendimiento de la red. \u00bfEst\u00e1 seguro que desea continuar?",
"HeaderResume": "Continuar",
- "HeaderMyViews": "My Views",
+ "HeaderMyViews": "Mis vistas",
"HeaderLibraryFolders": "Vista de carpeta",
"HeaderLatestMedia": "\u00daltimos medios",
"ButtonMore": "M\u00e1s...",
@@ -181,18 +181,18 @@
"LabelRunningOnPort": "Ejecut\u00e1ndose en el puerto {0}.",
"LabelRunningOnPorts": "Ejecut\u00e1ndose en los puertos {0} y {1}.",
"HeaderLatestFromChannel": "Lo \u00faltimo de {0}",
- "ButtonDownload": "Download",
- "LabelUnknownLanaguage": "Unknown language",
- "HeaderCurrentSubtitles": "Current Subtitles",
- "MessageDownloadQueued": "The download has been queued.",
- "MessageAreYouSureDeleteSubtitles": "Are you sure you wish to delete this subtitle file?",
- "ButtonRemoteControl": "Remote Control",
- "HeaderLatestTvRecordings": "Latest Recordings",
+ "ButtonDownload": "Descargar",
+ "LabelUnknownLanaguage": "Idioma desconocido",
+ "HeaderCurrentSubtitles": "Subt\u00edtulos actuales",
+ "MessageDownloadQueued": "La descarga se ha a\u00f1adido a la cola",
+ "MessageAreYouSureDeleteSubtitles": "\u00bfEst\u00e1 seguro que desea eliminar este archivo de subt\u00edtulos?",
+ "ButtonRemoteControl": "Control remoto",
+ "HeaderLatestTvRecordings": "\u00daltimas grabaciones",
"ButtonOk": "OK",
"ButtonCancel": "Cancelar",
- "ButtonRefresh": "Refresh",
- "LabelCurrentPath": "Current path:",
- "HeaderSelectMediaPath": "Select Media Path",
- "ButtonNetwork": "Network",
- "MessageDirectoryPickerInstruction": "Network paths can be entered manually in the event the Network button fails to locate your devices. For example, {0} or {1}."
+ "ButtonRefresh": "Refrescar",
+ "LabelCurrentPath": "Ruta actual:",
+ "HeaderSelectMediaPath": "Seleccionar la ruta para Medios",
+ "ButtonNetwork": "Red",
+ "MessageDirectoryPickerInstruction": "Rutas de red pueden ser introducidas manualmente en el caso de que el bot\u00f3n de la red no pueda localizar sus dispositivos. Por ejemplo, {0} o {1}."
} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/Localization/JavaScript/es_MX.json b/MediaBrowser.Server.Implementations/Localization/JavaScript/es_MX.json
index 6b609dde1..d72c295e3 100644
--- a/MediaBrowser.Server.Implementations/Localization/JavaScript/es_MX.json
+++ b/MediaBrowser.Server.Implementations/Localization/JavaScript/es_MX.json
@@ -190,9 +190,9 @@
"HeaderLatestTvRecordings": "\u00daltimas Grabaciones",
"ButtonOk": "Ok",
"ButtonCancel": "Cancelar",
- "ButtonRefresh": "Refresh",
- "LabelCurrentPath": "Current path:",
- "HeaderSelectMediaPath": "Select Media Path",
- "ButtonNetwork": "Network",
- "MessageDirectoryPickerInstruction": "Network paths can be entered manually in the event the Network button fails to locate your devices. For example, {0} or {1}."
+ "ButtonRefresh": "Refrescar",
+ "LabelCurrentPath": "Trayectoria actual:",
+ "HeaderSelectMediaPath": "Seleccionar trayectoria a medios",
+ "ButtonNetwork": "Red",
+ "MessageDirectoryPickerInstruction": "Las trayectorias de red pueden ser ingresadas manualmente en caso de que el bot\u00f3n de Red no pueda localizar sus dispositivos. Por ejemplo, {0} or {1}."
} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/Localization/JavaScript/fr.json b/MediaBrowser.Server.Implementations/Localization/JavaScript/fr.json
index 4ba25ce7a..a452eb02d 100644
--- a/MediaBrowser.Server.Implementations/Localization/JavaScript/fr.json
+++ b/MediaBrowser.Server.Implementations/Localization/JavaScript/fr.json
@@ -136,7 +136,7 @@
"HeaderSelectTranscodingPath": "S\u00e9lectionner le chemin d'acc\u00e8s du transcodage temporaire",
"HeaderSelectImagesByNamePath": "S\u00e9lectionner le chemin d'acc\u00e8s du \"Images By Name\"",
"HeaderSelectMetadataPath": "S\u00e9lectionner le chemin d'acc\u00e8s des m\u00e9tadonn\u00e9es",
- "HeaderSelectServerCachePathHelp": "Browse or enter the path to use for server cache files. The folder must be writeable. The location of this folder will directly impact server performance and should ideally be placed on a solid state drive.",
+ "HeaderSelectServerCachePathHelp": "Parcourez ou entrez le chemin \u00e0 utiliser pour les fichiers caches du serveur. Le dossier doit \u00eatre accessible en \u00e9criture. L'emplacement de ce fichier aura un impact direct sur les performances du serveur et devrait \u00eatre plac\u00e9 id\u00e9alement sur un SSD.",
"HeaderSelectTranscodingPathHelp": "Browse or enter the path to use for transcoding temporary files. The folder must be writeable.",
"HeaderSelectImagesByNamePathHelp": "Browse or enter the path to your items by name folder. The folder must be writeable.",
"HeaderSelectMetadataPathHelp": "Browse or enter the path you'd like to store metadata within. The folder must be writeable.",
@@ -190,9 +190,9 @@
"HeaderLatestTvRecordings": "Les plus r\u00e9cents enregistrements",
"ButtonOk": "Ok",
"ButtonCancel": "Annuler",
- "ButtonRefresh": "Refresh",
- "LabelCurrentPath": "Current path:",
+ "ButtonRefresh": "Actualiser",
+ "LabelCurrentPath": "Chemin actuel:",
"HeaderSelectMediaPath": "Select Media Path",
- "ButtonNetwork": "Network",
+ "ButtonNetwork": "R\u00e9seau",
"MessageDirectoryPickerInstruction": "Network paths can be entered manually in the event the Network button fails to locate your devices. For example, {0} or {1}."
} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/Localization/JavaScript/pl.json b/MediaBrowser.Server.Implementations/Localization/JavaScript/pl.json
new file mode 100644
index 000000000..b3ab8d3b9
--- /dev/null
+++ b/MediaBrowser.Server.Implementations/Localization/JavaScript/pl.json
@@ -0,0 +1,198 @@
+{
+ "SettingsSaved": "Ustawienia zapisane.",
+ "AddUser": "Dodaj u\u017cytkownika",
+ "Users": "U\u017cytkownicy",
+ "Delete": "Usu\u0144",
+ "Administrator": "Administrator",
+ "Password": "Has\u0142o",
+ "DeleteImage": "Usu\u0144 zdj\u0119cie",
+ "DeleteImageConfirmation": "Jeste\u015b pewien \u017ce chcesz usun\u0105\u0107 to zdj\u0119cie?",
+ "FileReadCancelled": "Odczytywanie pliku zosta\u0142o anulowane.",
+ "FileNotFound": "Plik nie znaleziony.",
+ "FileReadError": "Wyst\u0105pi\u0142 b\u0142\u0105d podczas odczytywania pliku.",
+ "DeleteUser": "Usu\u0144 u\u017cytkownika",
+ "DeleteUserConfirmation": "Jeste\u015b pewien \u017ce chcesz usun\u0105\u0107 {0}?",
+ "PasswordResetHeader": "Zresetuj has\u0142o",
+ "PasswordResetComplete": "Has\u0142o zosta\u0142o zresetowane.",
+ "PasswordResetConfirmation": "Jeste\u015b pewien \u017ce chcesz zresetowa\u0107 has\u0142o?",
+ "PasswordSaved": "Has\u0142o zapisane.",
+ "PasswordMatchError": "Has\u0142o i potwierdzenie has\u0142a musz\u0105 si\u0119 zgadza\u0107.",
+ "OptionRelease": "Oficjalne wydanie",
+ "OptionBeta": "Beta",
+ "OptionDev": "Dev (Niestabilne)",
+ "UninstallPluginHeader": "Usu\u0144 wtyczk\u0119",
+ "UninstallPluginConfirmation": "Czy na pewno chcesz usun\u0105\u0107 {0}?",
+ "NoPluginConfigurationMessage": "Ta wtyczka nie ma \u017cadnych ustawie\u0144.",
+ "NoPluginsInstalledMessage": "Nie masz \u017cadnych wtyczek zainstalowanych.",
+ "BrowsePluginCatalogMessage": "Przejrzyj nasz katalog wtyczek \u017ceby zobaczy\u0107 dost\u0119pne wtyczki.",
+ "MessageKeyEmailedTo": "Key emailed to {0}.",
+ "MessageKeysLinked": "Keys linked.",
+ "HeaderConfirmation": "Confirmation",
+ "MessageKeyUpdated": "Thank you. Your supporter key has been updated.",
+ "MessageKeyRemoved": "Thank you. Your supporter key has been removed.",
+ "ErrorLaunchingChromecast": "There was an error launching chromecast. Please ensure your device is connected to your wireless network.",
+ "HeaderSearch": "Search",
+ "LabelArtist": "Artist",
+ "LabelMovie": "Movie",
+ "LabelMusicVideo": "Music Video",
+ "LabelEpisode": "Episode",
+ "LabelSeries": "Series",
+ "LabelStopping": "Stopping",
+ "ButtonStop": "Stop",
+ "LabelCancelled": "(cancelled)",
+ "LabelFailed": "(failed)",
+ "LabelAbortedByServerShutdown": "(Aborted by server shutdown)",
+ "LabelScheduledTaskLastRan": "Last ran {0}, taking {1}.",
+ "HeaderDeleteTaskTrigger": "Delete Task Trigger",
+ "HeaderTaskTriggers": "Task Triggers",
+ "MessageDeleteTaskTrigger": "Are you sure you wish to delete this task trigger?",
+ "MessageNoPluginsInstalled": "You have no plugins installed.",
+ "LabelVersionInstalled": "{0} installed",
+ "LabelNumberReviews": "{0} Reviews",
+ "LabelFree": "Free",
+ "HeaderSelectAudio": "Select Audio",
+ "HeaderSelectSubtitles": "Select Subtitles",
+ "LabelDefaultStream": "(Default)",
+ "LabelForcedStream": "(Forced)",
+ "LabelDefaultForcedStream": "(Default\/Forced)",
+ "LabelUnknownLanguage": "Unknown language",
+ "ButtonMute": "Mute",
+ "ButtonUnmute": "Unmute",
+ "ButtonNextTrack": "Next track",
+ "ButtonPause": "Pause",
+ "ButtonPlay": "Play",
+ "ButtonEdit": "Edit",
+ "ButtonQueue": "Queue",
+ "ButtonPlayTrailer": "PlayTrailer",
+ "ButtonPlaylist": "Playlist",
+ "ButtonPreviousTrack": "Previous track",
+ "LabelEnabled": "Enabled",
+ "LabelDisabled": "Disabled",
+ "ButtonMoreInformation": "More Information",
+ "LabelNoUnreadNotifications": "No unread notifications.",
+ "ButtonViewNotifications": "View notifications",
+ "ButtonMarkTheseRead": "Mark these read",
+ "ButtonClose": "Close",
+ "LabelAllPlaysSentToPlayer": "All plays will be sent to the selected player.",
+ "MessageInvalidUser": "Invalid user or password.",
+ "HeaderAllRecordings": "All Recordings",
+ "RecommendationBecauseYouLike": "Because you like {0}",
+ "RecommendationBecauseYouWatched": "Because you watched {0}",
+ "RecommendationDirectedBy": "Directed by {0}",
+ "RecommendationStarring": "Starring {0}",
+ "HeaderConfirmRecordingCancellation": "Confirm Recording Cancellation",
+ "MessageConfirmRecordingCancellation": "Are you sure you wish to cancel this recording?",
+ "MessageRecordingCancelled": "Recording cancelled.",
+ "HeaderConfirmSeriesCancellation": "Confirm Series Cancellation",
+ "MessageConfirmSeriesCancellation": "Are you sure you wish to cancel this series?",
+ "MessageSeriesCancelled": "Series cancelled.",
+ "HeaderConfirmRecordingDeletion": "Confirm Recording Deletion",
+ "MessageConfirmRecordingDeletion": "Are you sure you wish to delete this recording?",
+ "MessageRecordingDeleted": "Recording deleted.",
+ "ButonCancelRecording": "Cancel Recording",
+ "MessageRecordingSaved": "Recording saved.",
+ "OptionSunday": "Niedziela",
+ "OptionMonday": "Poniedzia\u0142ek",
+ "OptionTuesday": "Wtorek",
+ "OptionWednesday": "\u015aroda",
+ "OptionThursday": "Czwartek",
+ "OptionFriday": "Pi\u0105tek",
+ "OptionSaturday": "Sobota",
+ "HeaderConfirmDeletion": "Confirm Deletion",
+ "MessageConfirmPathSubstitutionDeletion": "Are you sure you wish to delete this path substitution?",
+ "LiveTvUpdateAvailable": "(Update available)",
+ "LabelVersionUpToDate": "Up to date!",
+ "ButtonResetTuner": "Reset tuner",
+ "HeaderResetTuner": "Reset Tuner",
+ "MessageConfirmResetTuner": "Are you sure you wish to reset this tuner? Any active players or recordings will be abruptly stopped.",
+ "ButtonCancelSeries": "Cancel Series",
+ "LabelAllChannels": "All channels",
+ "HeaderSeriesRecordings": "Series Recordings",
+ "LabelAnytime": "Any time",
+ "StatusRecording": "Recording",
+ "StatusWatching": "Watching",
+ "StatusRecordingProgram": "Recording {0}",
+ "StatusWatchingProgram": "Watching {0}",
+ "HeaderSplitMedia": "Split Media Apart",
+ "MessageConfirmSplitMedia": "Are you sure you wish to split the media sources into separate items?",
+ "HeaderError": "Error",
+ "MessagePleaseSelectOneItem": "Please select at least one item.",
+ "MessagePleaseSelectTwoItems": "Please select at least two items.",
+ "MessageTheFollowingItemsWillBeGrouped": "The following titles will be grouped into one item:",
+ "MessageConfirmItemGrouping": "Media Browser clients will automatically choose the optimal version to play based on device and network performance. Are you sure you wish to continue?",
+ "HeaderResume": "Wzn\u00f3w",
+ "HeaderMyViews": "My Views",
+ "HeaderLibraryFolders": "Media Folders",
+ "HeaderLatestMedia": "Latest Media",
+ "ButtonMore": "More...",
+ "HeaderFavoriteMovies": "Favorite Movies",
+ "HeaderFavoriteShows": "Favorite Shows",
+ "HeaderFavoriteEpisodes": "Favorite Episodes",
+ "HeaderFavoriteGames": "Favorite Games",
+ "HeaderRatingsDownloads": "Rating \/ Downloads",
+ "HeaderConfirmProfileDeletion": "Confirm Profile Deletion",
+ "MessageConfirmProfileDeletion": "Are you sure you wish to delete this profile?",
+ "HeaderSelectServerCachePath": "Select Server Cache Path",
+ "HeaderSelectTranscodingPath": "Select Transcoding Temporary Path",
+ "HeaderSelectImagesByNamePath": "Select Images By Name Path",
+ "HeaderSelectMetadataPath": "Select Metadata Path",
+ "HeaderSelectServerCachePathHelp": "Browse or enter the path to use for server cache files. The folder must be writeable. The location of this folder will directly impact server performance and should ideally be placed on a solid state drive.",
+ "HeaderSelectTranscodingPathHelp": "Browse or enter the path to use for transcoding temporary files. The folder must be writeable.",
+ "HeaderSelectImagesByNamePathHelp": "Browse or enter the path to your items by name folder. The folder must be writeable.",
+ "HeaderSelectMetadataPathHelp": "Browse or enter the path you'd like to store metadata within. The folder must be writeable.",
+ "HeaderSelectChannelDownloadPath": "Select Channel Download Path",
+ "HeaderSelectChannelDownloadPathHelp": "Browse or enter the path to use for storing channel cache files. The folder must be writeable.",
+ "OptionNewCollection": "New...",
+ "ButtonAdd": "Add",
+ "ButtonRemove": "Remove",
+ "LabelChapterDownloaders": "Chapter downloaders:",
+ "LabelChapterDownloadersHelp": "Enable and rank your preferred chapter downloaders in order of priority. Lower priority downloaders will only be used to fill in missing information.",
+ "HeaderFavoriteAlbums": "Favorite Albums",
+ "HeaderLatestChannelMedia": "Latest Channel Items",
+ "ButtonOrganizeFile": "Organize File",
+ "ButtonDeleteFile": "Delete File",
+ "HeaderOrganizeFile": "Organize File",
+ "HeaderDeleteFile": "Delete File",
+ "StatusSkipped": "Skipped",
+ "StatusFailed": "Failed",
+ "StatusSuccess": "Success",
+ "MessageFileWillBeDeleted": "The following file will be deleted:",
+ "MessageSureYouWishToProceed": "Are you sure you wish to proceed?",
+ "MessageDuplicatesWillBeDeleted": "In addition the following dupliates will be deleted:",
+ "MessageFollowingFileWillBeMovedFrom": "The following file will be moved from:",
+ "MessageDestinationTo": "to:",
+ "HeaderSelectWatchFolder": "Select Watch Folder",
+ "HeaderSelectWatchFolderHelp": "Browse or enter the path to your watch folder. The folder must be writeable.",
+ "OrganizePatternResult": "Result: {0}",
+ "HeaderRestart": "Restart",
+ "HeaderShutdown": "Shutdown",
+ "MessageConfirmRestart": "Are you sure you wish to restart Media Browser Server?",
+ "MessageConfirmShutdown": "Are you sure you wish to shutdown Media Browser Server?",
+ "ButtonUpdateNow": "Update Now",
+ "NewVersionOfSomethingAvailable": "A new version of {0} is available!",
+ "VersionXIsAvailableForDownload": "Version {0} is now available for download.",
+ "LabelVersionNumber": "Version {0}",
+ "LabelPlayMethodTranscoding": "Transcoding",
+ "LabelPlayMethodDirectStream": "Direct Streaming",
+ "LabelPlayMethodDirectPlay": "Direct Playing",
+ "LabelAudioCodec": "Audio: {0}",
+ "LabelVideoCodec": "Video: {0}",
+ "LabelRemoteAccessUrl": "Remote access: {0}",
+ "LabelRunningOnPort": "Running on port {0}.",
+ "LabelRunningOnPorts": "Running on ports {0} and {1}.",
+ "HeaderLatestFromChannel": "Latest from {0}",
+ "ButtonDownload": "Download",
+ "LabelUnknownLanaguage": "Unknown language",
+ "HeaderCurrentSubtitles": "Current Subtitles",
+ "MessageDownloadQueued": "The download has been queued.",
+ "MessageAreYouSureDeleteSubtitles": "Are you sure you wish to delete this subtitle file?",
+ "ButtonRemoteControl": "Remote Control",
+ "HeaderLatestTvRecordings": "Latest Recordings",
+ "ButtonOk": "Ok",
+ "ButtonCancel": "Anuluj",
+ "ButtonRefresh": "Refresh",
+ "LabelCurrentPath": "Current path:",
+ "HeaderSelectMediaPath": "Select Media Path",
+ "ButtonNetwork": "Network",
+ "MessageDirectoryPickerInstruction": "Network paths can be entered manually in the event the Network button fails to locate your devices. For example, {0} or {1}."
+} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/Localization/JavaScript/pt_BR.json b/MediaBrowser.Server.Implementations/Localization/JavaScript/pt_BR.json
index 5b1667db6..5443451d6 100644
--- a/MediaBrowser.Server.Implementations/Localization/JavaScript/pt_BR.json
+++ b/MediaBrowser.Server.Implementations/Localization/JavaScript/pt_BR.json
@@ -190,9 +190,9 @@
"HeaderLatestTvRecordings": "\u00daltimas Grava\u00e7\u00f5es",
"ButtonOk": "Ok",
"ButtonCancel": "Cancelar",
- "ButtonRefresh": "Refresh",
- "LabelCurrentPath": "Current path:",
- "HeaderSelectMediaPath": "Select Media Path",
- "ButtonNetwork": "Network",
- "MessageDirectoryPickerInstruction": "Network paths can be entered manually in the event the Network button fails to locate your devices. For example, {0} or {1}."
+ "ButtonRefresh": "Atualizar",
+ "LabelCurrentPath": "Caminho atual:",
+ "HeaderSelectMediaPath": "Selecionar o Caminho da M\u00eddia",
+ "ButtonNetwork": "Rede",
+ "MessageDirectoryPickerInstruction": "Os caminhos da rede podem ser digitados manualmente caso o bot\u00e3o de Rede n\u00e3o consiga localizar seus dispositivos. Por exemplo, {0} ou {1}."
} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/Localization/JavaScript/ru.json b/MediaBrowser.Server.Implementations/Localization/JavaScript/ru.json
index 3661e5f60..afabb17e7 100644
--- a/MediaBrowser.Server.Implementations/Localization/JavaScript/ru.json
+++ b/MediaBrowser.Server.Implementations/Localization/JavaScript/ru.json
@@ -25,7 +25,7 @@
"NoPluginConfigurationMessage": "\u041d\u0435 \u0438\u043c\u0435\u0435\u0442\u0441\u044f \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 \u0434\u043b\u044f \u0434\u0430\u043d\u043d\u043e\u0433\u043e \u043f\u043b\u0430\u0433\u0438\u043d\u0430.",
"NoPluginsInstalledMessage": "\u041d\u0435 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u043e \u043d\u0438 \u043e\u0434\u043d\u043e\u0433\u043e \u043f\u043b\u0430\u0433\u0438\u043d\u0430.",
"BrowsePluginCatalogMessage": "\u041f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u0442\u0435 \u043a\u0430\u0442\u0430\u043b\u043e\u0433, \u0447\u0442\u043e\u0431\u044b \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u0438\u0442\u044c\u0441\u044f \u0441 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u043c\u0438 \u043f\u043b\u0430\u0433\u0438\u043d\u0430\u043c\u0438.",
- "MessageKeyEmailedTo": "\u041a\u043b\u044e\u0447 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u0435\u043d \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u043e\u0439 \u043f\u043e\u0447\u0442\u043e\u044e \u0434\u043b\u044f {0}.",
+ "MessageKeyEmailedTo": "\u041a\u043b\u044e\u0447 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u0435\u043d \u042d-\u043f\u043e\u0447\u0442\u043e\u044e \u0434\u043b\u044f {0}.",
"MessageKeysLinked": "\u041a\u043b\u044e\u0447\u0438 \u0431\u044b\u043b\u0438 \u043f\u0440\u0438\u0432\u044f\u0437\u0430\u043d\u044b.",
"HeaderConfirmation": "\u041f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435",
"MessageKeyUpdated": "\u041a\u043b\u044e\u0447 \u0441\u043f\u043e\u043d\u0441\u043e\u0440\u0430 \u0431\u044b\u043b \u043e\u0431\u043d\u043e\u0432\u043b\u0451\u043d.",
@@ -73,7 +73,7 @@
"ButtonViewNotifications": "\u041f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u044f",
"ButtonMarkTheseRead": "\u041e\u0442\u043c\u0435\u0442\u0438\u0442\u044c \u043a\u0430\u043a \u043f\u0440\u043e\u0447\u0442\u0451\u043d\u043d\u044b\u0435",
"ButtonClose": "\u0417\u0430\u043a\u0440\u044b\u0442\u044c",
- "LabelAllPlaysSentToPlayer": "\u0412\u0441\u0451 \u0432\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u043c\u043e\u0435 \u0431\u0443\u0434\u0435\u0442 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u043e \u043d\u0430 \u0432\u044b\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u0439 \u043f\u043b\u0435\u0439\u0435\u0440",
+ "LabelAllPlaysSentToPlayer": "\u0412\u0441\u0451 \u0432\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u043c\u043e\u0435 \u0431\u0443\u0434\u0435\u0442 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c\u0441\u044f \u043d\u0430 \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0439 \u043f\u043b\u0435\u0439\u0435\u0440",
"MessageInvalidUser": "\u041d\u0435\u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u043e\u0435 \u0438\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438\u043b\u0438 \u043f\u0430\u0440\u043e\u043b\u044c.",
"HeaderAllRecordings": "\u0412\u0441\u0435 \u0437\u0430\u043f\u0438\u0441\u0438",
"RecommendationBecauseYouLike": "\u0422\u0430\u043a \u043a\u0430\u043a \u0432\u0430\u043c \u043d\u0440\u0430\u0432\u0438\u0442\u0441\u044f {0}",
diff --git a/MediaBrowser.Server.Implementations/Localization/JavaScript/sv.json b/MediaBrowser.Server.Implementations/Localization/JavaScript/sv.json
index 7bb4f5409..b41dba810 100644
--- a/MediaBrowser.Server.Implementations/Localization/JavaScript/sv.json
+++ b/MediaBrowser.Server.Implementations/Localization/JavaScript/sv.json
@@ -190,9 +190,9 @@
"HeaderLatestTvRecordings": "Senaste inspelningar",
"ButtonOk": "OK",
"ButtonCancel": "Avbryt",
- "ButtonRefresh": "Refresh",
- "LabelCurrentPath": "Current path:",
- "HeaderSelectMediaPath": "Select Media Path",
- "ButtonNetwork": "Network",
- "MessageDirectoryPickerInstruction": "Network paths can be entered manually in the event the Network button fails to locate your devices. For example, {0} or {1}."
+ "ButtonRefresh": "Uppdatera",
+ "LabelCurrentPath": "Aktuell s\u00f6kv\u00e4g:",
+ "HeaderSelectMediaPath": "V\u00e4lj s\u00f6kv\u00e4g till media",
+ "ButtonNetwork": "N\u00e4tverk",
+ "MessageDirectoryPickerInstruction": "N\u00e4tverkss\u00f6kv\u00e4gar kan anges manuellt om \"N\u00e4tverk\" inte hittar dina enheter. T ex {0} eller {1}."
} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/Localization/LocalizationManager.cs b/MediaBrowser.Server.Implementations/Localization/LocalizationManager.cs
index e1f5bcfdc..c21c9fa17 100644
--- a/MediaBrowser.Server.Implementations/Localization/LocalizationManager.cs
+++ b/MediaBrowser.Server.Implementations/Localization/LocalizationManager.cs
@@ -371,6 +371,7 @@ namespace MediaBrowser.Server.Implementations.Localization
new LocalizatonOption{ Name="Italian", Value="it"},
new LocalizatonOption{ Name="Kazakh", Value="kk"},
new LocalizatonOption{ Name="Norwegian Bokmål", Value="nb"},
+ new LocalizatonOption{ Name="Polish", Value="pl"},
new LocalizatonOption{ Name="Portuguese (Brazil)", Value="pt-BR"},
new LocalizatonOption{ Name="Portuguese (Portugal)", Value="pt-PT"},
new LocalizatonOption{ Name="Russian", Value="ru"},
diff --git a/MediaBrowser.Server.Implementations/Localization/Server/de.json b/MediaBrowser.Server.Implementations/Localization/Server/de.json
index c5fc62bed..05e6e7202 100644
--- a/MediaBrowser.Server.Implementations/Localization/Server/de.json
+++ b/MediaBrowser.Server.Implementations/Localization/Server/de.json
@@ -63,14 +63,14 @@
"HeaderPlaybackSettings": "Wiedergabe Einstellungen",
"LabelAudioLanguagePreference": "Audiosprache Einstellungen:",
"LabelSubtitleLanguagePreference": "Untertitelsprache Einstellungen:",
- "OptionDefaultSubtitles": "Default",
+ "OptionDefaultSubtitles": "Standard",
"OptionOnlyForcedSubtitles": "Only forced subtitles",
- "OptionAlwaysPlaySubtitles": "Always play subtitles",
+ "OptionAlwaysPlaySubtitles": "Untertitel immer anzeigen",
"OptionNoSubtitles": "Keine Untertitel",
"OptionDefaultSubtitlesHelp": "Subtitles matching the language preference will be loaded when the audio is in a foreign language.",
"OptionOnlyForcedSubtitlesHelp": "Only subtitles marked as forced will be loaded.",
"OptionAlwaysPlaySubtitlesHelp": "Subtitles matching the language preference will be loaded regardless of the audio language.",
- "OptionNoSubtitlesHelp": "Subtitles will not be loaded by default.",
+ "OptionNoSubtitlesHelp": "Untertitel wird standardm\u00e4\u00dfig nicht geladen.",
"TabProfiles": "Profile",
"TabSecurity": "Sicherheit",
"ButtonAddUser": "User hinzuf\u00fcgen",
@@ -295,13 +295,13 @@
"PasswordLocalhostMessage": "Passw\u00f6rter werden nich gebraucht, wenn Sie sich vom Localhost aus einloggen.",
"TabGuide": "Programm",
"TabChannels": "Kan\u00e4le",
- "TabCollections": "Collections",
+ "TabCollections": "Sammlungen",
"HeaderChannels": "Kan\u00e4le",
"TabRecordings": "Aufnahmen",
"TabScheduled": "Geplant",
"TabSeries": "Serie",
- "TabFavorites": "Favorites",
- "TabMyLibrary": "My Library",
+ "TabFavorites": "Favoriten",
+ "TabMyLibrary": "Meine Bibliothek",
"ButtonCancelRecording": "Aufnahme abbrechen",
"HeaderPrePostPadding": "Pufferzeit vor\/nach der Aufnahme",
"LabelPrePaddingMinutes": "Minuten vor der Aufnahme",
@@ -470,7 +470,7 @@
"LabelMinResumePercentageHelp": "Titel werden als nicht abgespielt angenommen, wenn sie vor dieser Zeit gestoppt werden",
"LabelMaxResumePercentageHelp": "Titel werden als komplett abgespielt angenommen, wenn sie nach dieser Zeit gestoppt werden",
"LabelMinResumeDurationHelp": "Titel k\u00fcrzer als dies werden nicht fortsetzbar sein",
- "TitleAutoOrganize": "Auto-Organize",
+ "TitleAutoOrganize": "automatische Sortierung",
"TabActivityLog": "Aktivit\u00e4tsverlauf",
"HeaderName": "Name",
"HeaderDate": "Datum",
@@ -481,7 +481,7 @@
"LabelCompleted": "Fertiggestellt",
"LabelFailed": "Gescheitert",
"LabelSkipped": "\u00dcbersprungen",
- "HeaderEpisodeOrganization": "Episode Organization",
+ "HeaderEpisodeOrganization": "Episodensortierung",
"LabelSeries": "Serien:",
"LabelSeasonNumber": "Staffelnummer",
"LabelEpisodeNumber": "Episodennummer",
@@ -494,7 +494,7 @@
"DonationNextStep": "Sobald abgeschlossen, kehren Sie bitte hierher zur\u00fcck und tragen Sie den Unterst\u00fctzerschl\u00fcssel ein, den Sie per E-Mail erhalten haben.",
"AutoOrganizeHelp": "Auto-organize monitors your download folders for new files and moves them to your media directories.",
"AutoOrganizeTvHelp": "TV Dateien Organisation wird nur Episoden zu bereits vorhandenen Serien hinzuf\u00fcgen. Es werden keine neuen Serien angelegt.",
- "OptionEnableEpisodeOrganization": "Enable new episode organization",
+ "OptionEnableEpisodeOrganization": "Aktiviere die Sortierung neuer Episoden",
"LabelWatchFolder": "\u00dcberwachungsordner:",
"LabelWatchFolderHelp": "Der Server wird diesen Ordner, w\u00e4hrend des geplanten Tasks \"Organisiere neue Mediendateien\", abfragen.",
"ButtonViewScheduledTasks": "Zeige Geplante Aufgaben",
@@ -573,7 +573,7 @@
"HeaderRequireManualLoginHelp": "Wenn deaktiviert k\u00f6nnen Clients einen Loginbildschirm mit einer visuellen Auswahl der User anzeigen.",
"OptionOtherApps": "Andere Apps",
"OptionMobileApps": "Mobile Apps",
- "HeaderNotificationList": "Click on a notification to configure it's sending options.",
+ "HeaderNotificationList": "Klicke auf eine Benachrichtigung um die Benachrichtigungseinstellungen zu bearbeiten",
"NotificationOptionApplicationUpdateAvailable": "Anwendungsaktualisierung verf\u00fcgbar",
"NotificationOptionApplicationUpdateInstalled": "Anwendungsaktualisierung installiert",
"NotificationOptionPluginUpdateInstalled": "Pluginaktualisierung installiert",
@@ -588,7 +588,7 @@
"NotificationOptionTaskFailed": "Scheduled task failure",
"NotificationOptionInstallationFailed": "Installationsfehler",
"NotificationOptionNewLibraryContent": "Neuer Inhalt hinzugef\u00fcgt",
- "NotificationOptionNewLibraryContentMultiple": "New content added (multiple)",
+ "NotificationOptionNewLibraryContentMultiple": "Neuen Inhalte hinzugef\u00fcgt (mehrere)",
"SendNotificationHelp": "By default, notifications are delivered to the dashboard inbox. Browse the plugin catalog to install additional notification options.",
"NotificationOptionServerRestartRequired": "Serverneustart notwendig",
"LabelNotificationEnabled": "Aktiviere diese Benachrichtigung",
@@ -721,11 +721,11 @@
"OptionReportByteRangeSeekingWhenTranscoding": "Report that the server supports byte seeking when transcoding",
"OptionReportByteRangeSeekingWhenTranscodingHelp": "This is required for some devices that don't time seek very well.",
"HeaderSubtitleDownloadingHelp": "When Media Browser scans your video files it can search for missing subtitles, and download them using a subtitle provider such as OpenSubtitles.org.",
- "HeaderDownloadSubtitlesFor": "Download subtitles for:",
+ "HeaderDownloadSubtitlesFor": "Lade Untertitel runter f\u00fcr",
"MessageNoChapterProviders": "Install a chapter provider plugin such as ChapterDb to enable additional chapter options.",
- "LabelSkipIfGraphicalSubsPresent": "Skip if the video already contains graphical subtitles",
+ "LabelSkipIfGraphicalSubsPresent": "\u00dcberspringen, falls das Video bereits grafische Untertitel enth\u00e4lt",
"LabelSkipIfGraphicalSubsPresentHelp": "Keeping text versions of subtitles will result in more efficient delivery to mobile clients.",
- "TabSubtitles": "Subtitles",
+ "TabSubtitles": "Untertitel",
"TabChapters": "Chapters",
"HeaderDownloadChaptersFor": "Download chapter names for:",
"LabelOpenSubtitlesUsername": "Open Subtitles username:",
@@ -734,33 +734,33 @@
"LabelPlayDefaultAudioTrack": "Play default audio track regardless of language",
"LabelSubtitlePlaybackMode": "Subtitle mode:",
"LabelDownloadLanguages": "Download languages:",
- "ButtonRegister": "Register",
- "LabelSkipIfAudioTrackPresent": "Skip if the default audio track matches the download language",
+ "ButtonRegister": "Registrierung",
+ "LabelSkipIfAudioTrackPresent": "\u00dcberspringen, falls der Ton bereits der herunterladbaren Sprache entspricht",
"LabelSkipIfAudioTrackPresentHelp": "Uncheck this to ensure all videos have subtitles, regardless of audio language.",
- "HeaderSendMessage": "Send Message",
- "ButtonSend": "Send",
- "LabelMessageText": "Message text:",
- "MessageNoAvailablePlugins": "No available plugins.",
+ "HeaderSendMessage": "sende Nachricht",
+ "ButtonSend": "senden",
+ "LabelMessageText": "Inhalt der Nachricht",
+ "MessageNoAvailablePlugins": "Keine verf\u00fcgbaren Erweiterungen.",
"LabelDisplayPluginsFor": "Display plugins for:",
"PluginTabMediaBrowserClassic": "MB Classic",
"PluginTabMediaBrowserTheater": "MB Theater",
- "TabOtherPlugins": "Others",
- "LabelEpisodeName": "Episode name",
- "LabelSeriesName": "Series name",
+ "TabOtherPlugins": "Andere",
+ "LabelEpisodeName": "Episodentitel",
+ "LabelSeriesName": "Serientitel",
"ValueSeriesNamePeriod": "Series.name",
"ValueSeriesNameUnderscore": "Series_name",
- "ValueEpisodeNamePeriod": "Episode.name",
+ "ValueEpisodeNamePeriod": "Episodentitel",
"ValueEpisodeNameUnderscore": "Episode_name",
"HeaderTypeText": "Enter Text",
"LabelTypeText": "Text",
"HeaderSearchForSubtitles": "Search for Subtitles",
"MessageNoSubtitleSearchResultsFound": "No search results founds.",
- "TabDisplay": "Display",
- "TabLanguages": "Languages",
- "TabWebClient": "Web Client",
- "LabelEnableThemeSongs": "Enable theme songs",
+ "TabDisplay": "Anzeige",
+ "TabLanguages": "Sprachen",
+ "TabWebClient": "Webclient",
+ "LabelEnableThemeSongs": "Aktiviere Titelmelodie",
"LabelEnableBackdrops": "Aktiviere Backdrops",
- "LabelEnableThemeSongsHelp": "If enabled, theme songs will be played in the background while browsing the library.",
+ "LabelEnableThemeSongsHelp": "Wenn aktiviert, wird die Titelmusik w\u00e4hrend dem Durchsuchen durch die Bibliothek im Hintergrund abgespielt",
"LabelEnableBackdropsHelp": "If enabled, backdrops will be displayed in the background of some pages while browsing the library.",
"HeaderHomePage": "Home Page",
"HeaderSettingsForThisDevice": "Einstellungen f\u00fcr dieses Ger\u00e4t",
@@ -775,13 +775,13 @@
"OptionMyViews": "My views",
"OptionMyViewsSmall": "My views (small)",
"OptionResumablemedia": "Resume",
- "OptionLatestMedia": "Latest media",
+ "OptionLatestMedia": "Neuste Medien",
"OptionLatestChannelMedia": "Latest channel items",
"HeaderLatestChannelItems": "Latest Channel Items",
"OptionNone": "None",
- "HeaderLiveTv": "Live TV",
+ "HeaderLiveTv": "Live Fernsehn",
"HeaderReports": "Meldungen",
- "HeaderMetadataManager": "Metadata Manager",
+ "HeaderMetadataManager": "Metadaten-Manager",
"HeaderPreferences": "Einstellungen",
"MessageLoadingChannels": "Lade Channel Inhalt...",
"ButtonMarkRead": "Als gelesen markieren",
diff --git a/MediaBrowser.Server.Implementations/Localization/Server/es.json b/MediaBrowser.Server.Implementations/Localization/Server/es.json
index 9811cb951..125693f34 100644
--- a/MediaBrowser.Server.Implementations/Localization/Server/es.json
+++ b/MediaBrowser.Server.Implementations/Localization/Server/es.json
@@ -770,14 +770,14 @@
"LabelHomePageSection1": "Secci\u00f3n uno de la p\u00e1gina de inicio:",
"LabelHomePageSection2": "Secci\u00f3n dos de la p\u00e1gina de inicio:",
"LabelHomePageSection3": "Secci\u00f3n tres de la p\u00e1gina de inicio:",
- "LabelHomePageSection4": "Home page section four:",
- "OptionMyViewsButtons": "My views (buttons)",
- "OptionMyViews": "My views",
- "OptionMyViewsSmall": "My views (small)",
+ "LabelHomePageSection4": "Secci\u00f3n cuarta de la p\u00e1gina de inicio",
+ "OptionMyViewsButtons": "Mis vistas (botones)",
+ "OptionMyViews": "Mis vistas",
+ "OptionMyViewsSmall": "Mis vistas (peque\u00f1o)",
"OptionResumablemedia": "Continuar",
"OptionLatestMedia": "\u00daltimos medios",
- "OptionLatestChannelMedia": "Latest channel items",
- "HeaderLatestChannelItems": "Latest Channel Items",
+ "OptionLatestChannelMedia": "Ultimos elementos de canales",
+ "HeaderLatestChannelItems": "Ultimos elementos de canales",
"OptionNone": "Nada",
"HeaderLiveTv": "TV en vivo",
"HeaderReports": "Informes",
@@ -813,13 +813,13 @@
"ViewTypeChannels": "Canales",
"ViewTypeLiveTV": "Tv en vivo",
"HeaderOtherDisplaySettings": "Configuraci\u00f3n de pantalla",
- "HeaderMyViews": "My Views",
+ "HeaderMyViews": "Mis vistas",
"LabelSelectFolderGroups": "Agrupar autom\u00e1ticamente las siguientes carpetas en vistas tales como pel\u00edculas, m\u00fasica y televisi\u00f3n",
"LabelSelectFolderGroupsHelp": "Las carpetas que no est\u00e9n marcadas se mostrar\u00e1n por s\u00ed mismas en su propia secci\u00f3n.",
"OptionDisplayAdultContent": "Mostrar contenido para adultos",
"OptionLibraryFolders": "Vista de carpeta",
- "TitleRemoteControl": "Remote Control",
- "OptionLatestTvRecordings": "Latest recordings",
- "LabelProtocolInfo": "Protocol info:",
- "LabelProtocolInfoHelp": "The value that will be used when responding to GetProtocolInfo requests from the device."
+ "TitleRemoteControl": "Control remoto",
+ "OptionLatestTvRecordings": "\u00daltimas grabaciones",
+ "LabelProtocolInfo": "Informaci\u00f3n de protocolo:",
+ "LabelProtocolInfoHelp": "El valor que se utilizar\u00e1 cuando se responde a una solicitud GetProtocolInfo desde el dispositivo."
} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/Localization/Server/es_MX.json b/MediaBrowser.Server.Implementations/Localization/Server/es_MX.json
index 407cd6ace..7ddd4c987 100644
--- a/MediaBrowser.Server.Implementations/Localization/Server/es_MX.json
+++ b/MediaBrowser.Server.Implementations/Localization/Server/es_MX.json
@@ -94,7 +94,7 @@
"MessagePleaseEnsureInternetMetadata": "Por favor aseg\u00farese que la descarga de metadatos de internet esta habilitada.",
"TabSuggested": "Sugerencias",
"TabLatest": "Recientes",
- "TabUpcoming": "Pr\u00f3ximo Estreno",
+ "TabUpcoming": "Por Estrenar",
"TabShows": "Programas",
"TabEpisodes": "Episodios",
"TabGenres": "G\u00e9neros",
@@ -771,9 +771,9 @@
"LabelHomePageSection2": "Pagina principal secci\u00f3n dos:",
"LabelHomePageSection3": "Pagina principal secci\u00f3n tres:",
"LabelHomePageSection4": "Pagina principal secci\u00f3n cuatro:",
- "OptionMyViewsButtons": "My views (buttons)",
- "OptionMyViews": "My views",
- "OptionMyViewsSmall": "My views (small)",
+ "OptionMyViewsButtons": "Mis vistas (botones)",
+ "OptionMyViews": "Mis vistas",
+ "OptionMyViewsSmall": "Mis vistas (peque\u00f1o)",
"OptionResumablemedia": "Reanudar",
"OptionLatestMedia": "Agregados recientemente",
"OptionLatestChannelMedia": "Elementos recientes de canales",
@@ -820,6 +820,6 @@
"OptionLibraryFolders": "Carpetas de medios",
"TitleRemoteControl": "Control Remoto",
"OptionLatestTvRecordings": "\u00daltimas grabaciones",
- "LabelProtocolInfo": "Protocol info:",
- "LabelProtocolInfoHelp": "The value that will be used when responding to GetProtocolInfo requests from the device."
+ "LabelProtocolInfo": "Informaci\u00f3n del protocolo:",
+ "LabelProtocolInfoHelp": "El valor que ser\u00e1 utilizado cuando se responde a solicitudes GetProtocolInfo desde el dispositivo."
} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/Localization/Server/fr.json b/MediaBrowser.Server.Implementations/Localization/Server/fr.json
index 299868a54..69f75dd94 100644
--- a/MediaBrowser.Server.Implementations/Localization/Server/fr.json
+++ b/MediaBrowser.Server.Implementations/Localization/Server/fr.json
@@ -772,8 +772,8 @@
"LabelHomePageSection3": "Section 3 de la page de d\u00e9marrage:",
"LabelHomePageSection4": "Section 4 de la page de d\u00e9marrage:",
"OptionMyViewsButtons": "My views (buttons)",
- "OptionMyViews": "My views",
- "OptionMyViewsSmall": "My views (small)",
+ "OptionMyViews": "Mes vues",
+ "OptionMyViewsSmall": "Mes vues (petit)",
"OptionResumablemedia": "Reprendre",
"OptionLatestMedia": "Les plus r\u00e9cents",
"OptionLatestChannelMedia": "Items de cha\u00eene les plus r\u00e9cents",
@@ -820,6 +820,6 @@
"OptionLibraryFolders": "R\u00e9pertoires de m\u00e9dias",
"TitleRemoteControl": "Acc\u00e8s \u00e0 distance",
"OptionLatestTvRecordings": "Les plus r\u00e9cents enregistrements",
- "LabelProtocolInfo": "Protocol info:",
+ "LabelProtocolInfo": "Infos sur le protocol:",
"LabelProtocolInfoHelp": "The value that will be used when responding to GetProtocolInfo requests from the device."
} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/Localization/Server/pl.json b/MediaBrowser.Server.Implementations/Localization/Server/pl.json
new file mode 100644
index 000000000..4970fdccc
--- /dev/null
+++ b/MediaBrowser.Server.Implementations/Localization/Server/pl.json
@@ -0,0 +1,825 @@
+{
+ "LabelExit": "Wyj\u015b\u0107",
+ "LabelVisitCommunity": "Odwied\u017a spo\u0142eczno\u015b\u0107",
+ "LabelGithubWiki": "Wiki Github",
+ "LabelSwagger": "Swagger",
+ "LabelStandard": "Standardowy",
+ "LabelViewApiDocumentation": "Zobacz dokumentacj\u0119 Api",
+ "LabelBrowseLibrary": "Przejrzyj bibliotek\u0119",
+ "LabelConfigureMediaBrowser": "Skonfiguruj Media Browser",
+ "LabelOpenLibraryViewer": "Otw\u00f3rz przegl\u0105dark\u0119 biblioteki",
+ "LabelRestartServer": "Uruchom serwer ponownie",
+ "LabelShowLogWindow": "Show Log Window",
+ "LabelPrevious": "Wstecz",
+ "LabelFinish": "Koniec",
+ "LabelNext": "Dalej",
+ "LabelYoureDone": "Sko\u0144czy\u0142e\u015b!",
+ "WelcomeToMediaBrowser": "Witaj w Media Browser!",
+ "TitleMediaBrowser": "Media Browser",
+ "ThisWizardWillGuideYou": "Asystent pomo\u017ce Ci podczas instalacji. Na pocz\u0105tku, wybierz tw\u00f3j preferowany j\u0119zyk.",
+ "TellUsAboutYourself": "Opowiedz nam o sobie",
+ "LabelYourFirstName": "Twoje imi\u0119:",
+ "MoreUsersCanBeAddedLater": "Mo\u017cesz doda\u0107 wi\u0119cej u\u017cytkownik\u00f3w p\u00f3\u017aniej przez tablic\u0119 rozdzielcz\u0105.",
+ "UserProfilesIntro": "Media Browser includes built-in support for user profiles, enabling each user to have their own display settings, playstate and parental controls.",
+ "LabelWindowsService": "Serwis Windows",
+ "AWindowsServiceHasBeenInstalled": "Serwis Windows zosta\u0142 zainstalowany.",
+ "WindowsServiceIntro1": "Media Browser Server normally runs as a desktop application with a tray icon, but if you prefer to run it as a background service, it can be started from the windows services control panel instead.",
+ "WindowsServiceIntro2": "If using the windows service, please note that it cannot be run at the same time as the tray icon, so you'll need to exit the tray in order to run the service. The service will also need to be configured with administrative privileges via the control panel. Please note that at this time the service is unable to self-update, so new versions will require manual interaction.",
+ "WizardCompleted": "To wszystko na teraz. Media Browser zacz\u0105\u0142 zbiera\u0107 informacje o twojej bibliotece. Zapoznaj si\u0119 z aplikacjami po czym kliknij <b>Koniec<\/b> \u017ceby zobaczy\u0107 <b>Tablic\u0119 rozdzielcz\u0105<\/b>.",
+ "LabelConfigureSettings": "Skonfiguruj ustawienia",
+ "LabelEnableVideoImageExtraction": "Enable video image extraction",
+ "VideoImageExtractionHelp": "Dla filmik\u00f3w kt\u00f3re nie maj\u0105 jeszcze obraz\u00f3w i dla kt\u00f3rych nie mo\u017cemy \u017cadnych znale\u017a\u0107 na internecie. Zwi\u0119kszy to czas wst\u0119pnego skanowania biblioteki ale wynikiem b\u0119dzie \u0142adniejsza prezentacja.",
+ "LabelEnableChapterImageExtractionForMovies": "Extract chapter image extraction for Movies",
+ "LabelChapterImageExtractionForMoviesHelp": "Extracting chapter images will allow clients to display graphical scene selection menus. The process can be slow, cpu-intensive and may require several gigabytes of space. It runs as a nightly scheduled task at 4am, although this is configurable in the scheduled tasks area. It is not recommended to run this task during peak usage hours.",
+ "LabelEnableAutomaticPortMapping": "Enable automatic port mapping",
+ "LabelEnableAutomaticPortMappingHelp": "UPnP allows automated router configuration for easy remote access. This may not work with some router models.",
+ "ButtonOk": "Ok",
+ "ButtonCancel": "Anuluj",
+ "ButtonNew": "New",
+ "HeaderSetupLibrary": "Ustaw swoj\u0105 bibliotek\u0119",
+ "ButtonAddMediaFolder": "Dodaj folder",
+ "LabelFolderType": "Typ folderu:",
+ "MediaFolderHelpPluginRequired": "* Wymaga u\u017cycia wtyczki tak jak GameBrowser lub MB Bookshelf.",
+ "ReferToMediaLibraryWiki": "Odnie\u015b si\u0119 do wiki biblioteki.",
+ "LabelCountry": "Kraj:",
+ "LabelLanguage": "J\u0119zyk:",
+ "HeaderPreferredMetadataLanguage": "Preferowany j\u0119zyk metadanych:",
+ "LabelSaveLocalMetadata": "Save artwork and metadata into media folders",
+ "LabelSaveLocalMetadataHelp": "Saving artwork and metadata directly into media folders will put them in a place where they can be easily edited.",
+ "LabelDownloadInternetMetadata": "Download artwork and metadata from the internet",
+ "LabelDownloadInternetMetadataHelp": "Media Browser can download information about your media to enable rich presentations.",
+ "TabPreferences": "Preferencje",
+ "TabPassword": "Has\u0142o",
+ "TabLibraryAccess": "Dost\u0119p do biblioteki",
+ "TabImage": "Obraz",
+ "TabProfile": "Profil",
+ "TabMetadata": "Metadata",
+ "TabImages": "Images",
+ "TabNotifications": "Notifications",
+ "TabCollectionTitles": "Titles",
+ "LabelDisplayMissingEpisodesWithinSeasons": "Wy\u015bwietl brakuj\u0105ce odcinki w sezonach",
+ "LabelUnairedMissingEpisodesWithinSeasons": "Wy\u015bwietl nie wydanie odcinki w sezonach",
+ "HeaderVideoPlaybackSettings": "Ustawienia odtwarzania wideo",
+ "HeaderPlaybackSettings": "Playback Settings",
+ "LabelAudioLanguagePreference": "Preferencje j\u0119zyka audio:",
+ "LabelSubtitleLanguagePreference": "Preferencje j\u0119zyka napis\u00f3w:",
+ "OptionDefaultSubtitles": "Default",
+ "OptionOnlyForcedSubtitles": "Only forced subtitles",
+ "OptionAlwaysPlaySubtitles": "Always play subtitles",
+ "OptionNoSubtitles": "No Subtitles",
+ "OptionDefaultSubtitlesHelp": "Subtitles matching the language preference will be loaded when the audio is in a foreign language.",
+ "OptionOnlyForcedSubtitlesHelp": "Only subtitles marked as forced will be loaded.",
+ "OptionAlwaysPlaySubtitlesHelp": "Subtitles matching the language preference will be loaded regardless of the audio language.",
+ "OptionNoSubtitlesHelp": "Subtitles will not be loaded by default.",
+ "TabProfiles": "Profile",
+ "TabSecurity": "Zabezpieczenie",
+ "ButtonAddUser": "Dodaj u\u017cytkownika",
+ "ButtonSave": "Zapisz",
+ "ButtonResetPassword": "Zresetuj has\u0142o",
+ "LabelNewPassword": "Nowe has\u0142o:",
+ "LabelNewPasswordConfirm": "Potwierd\u017a nowe has\u0142o:",
+ "HeaderCreatePassword": "Stw\u00f3rz has\u0142o:",
+ "LabelCurrentPassword": "Bie\u017c\u0105ce has\u0142o:",
+ "LabelMaxParentalRating": "Maksymalna dozwolona ocena rodzicielska:",
+ "MaxParentalRatingHelp": "Zawarto\u015b\u0107 z wy\u017csz\u0105 ocen\u0105 b\u0119dzie schowana dla tego u\u017cytkownika.",
+ "LibraryAccessHelp": "Select the media folders to share with this user. Administrators will be able to edit all folders using the metadata manager.",
+ "ChannelAccessHelp": "Select the channels to share with this user. Administrators will be able to edit all channels using the metadata manager.",
+ "ButtonDeleteImage": "Usu\u0144 obrazek",
+ "LabelSelectUsers": "Select users:",
+ "ButtonUpload": "Wy\u015blij",
+ "HeaderUploadNewImage": "Wy\u015blij nowy obrazek",
+ "LabelDropImageHere": "Wrzu\u0107 obrazek tutaj",
+ "ImageUploadAspectRatioHelp": "1:1 Aspect Ratio Recommended. JPG\/PNG only.",
+ "MessageNothingHere": "Nic tutaj nie ma.",
+ "MessagePleaseEnsureInternetMetadata": "Upewnij si\u0119 \u017ce pobieranie metadanych z internetu jest w\u0142\u0105czone.",
+ "TabSuggested": "Sugerowane",
+ "TabLatest": "Ostatnie",
+ "TabUpcoming": "Upcoming",
+ "TabShows": "Seriale",
+ "TabEpisodes": "Odcinki",
+ "TabGenres": "Rodzaje",
+ "TabPeople": "Osoby",
+ "TabNetworks": "Sieci",
+ "HeaderUsers": "U\u017cytkownicy",
+ "HeaderFilters": "Filtry:",
+ "ButtonFilter": "Filtr",
+ "OptionFavorite": "Ulubione",
+ "OptionLikes": "Likes",
+ "OptionDislikes": "Dislikes",
+ "OptionActors": "Aktorzy",
+ "OptionGuestStars": "Guest Stars",
+ "OptionDirectors": "Dyrektorzy",
+ "OptionWriters": "Pisarze",
+ "OptionProducers": "Producenci",
+ "HeaderResume": "Wzn\u00f3w",
+ "HeaderNextUp": "Next Up",
+ "NoNextUpItemsMessage": "Nie znaleziono \u017cadnego. Zacznij ogl\u0105da\u0107 Twoje seriale!",
+ "HeaderLatestEpisodes": "Ostanie odcinki",
+ "HeaderPersonTypes": "Person Types:",
+ "TabSongs": "Utwory",
+ "TabAlbums": "Albumy",
+ "TabArtists": "Arty\u015bci",
+ "TabAlbumArtists": "Arty\u015bci albumu",
+ "TabMusicVideos": "Teledyski",
+ "ButtonSort": "Sortuj",
+ "HeaderSortBy": "Sortuj wed\u0142ug:",
+ "HeaderSortOrder": "Kolejno\u015b\u0107 sortowania:",
+ "OptionPlayed": "Played",
+ "OptionUnplayed": "Unplayed",
+ "OptionAscending": "Rosn\u0105co",
+ "OptionDescending": "Malej\u0105co",
+ "OptionRuntime": "D\u0142ugo\u015b\u0107 filmu",
+ "OptionReleaseDate": "Data wydania",
+ "OptionPlayCount": "Ilo\u015b\u0107 odtworze\u0144",
+ "OptionDatePlayed": "Data odtworzenia",
+ "OptionDateAdded": "Data dodania",
+ "OptionAlbumArtist": "Artysta albumu",
+ "OptionArtist": "Artysta",
+ "OptionAlbum": "Album",
+ "OptionTrackName": "Nazwa utworu",
+ "OptionCommunityRating": "Ocena spo\u0142eczno\u015bci",
+ "OptionNameSort": "Nazwa",
+ "OptionFolderSort": "Folders",
+ "OptionBudget": "Bud\u017cet",
+ "OptionRevenue": "Doch\u00f3d",
+ "OptionPoster": "Plakat",
+ "OptionBackdrop": "Backdrop",
+ "OptionTimeline": "Timeline",
+ "OptionThumb": "Thumb",
+ "OptionBanner": "Banner",
+ "OptionCriticRating": "Ocena krytyk\u00f3w",
+ "OptionVideoBitrate": "Video Bitrate",
+ "OptionResumable": "Resumable",
+ "ScheduledTasksHelp": "Click a task to adjust its schedule.",
+ "ScheduledTasksTitle": "Zaplanowane zadania",
+ "TabMyPlugins": "Moje wtyczki",
+ "TabCatalog": "Katalog",
+ "PluginsTitle": "Wtyczki",
+ "HeaderAutomaticUpdates": "Automatyczne aktualizacje",
+ "HeaderNowPlaying": "Now Playing",
+ "HeaderLatestAlbums": "Ostatnie albumy",
+ "HeaderLatestSongs": "Ostatnie utwory",
+ "HeaderRecentlyPlayed": "Ostatnio grane",
+ "HeaderFrequentlyPlayed": "Cz\u0119sto grane",
+ "DevBuildWarning": "Dev builds are the bleeding edge. Released often, these build have not been tested. The application may crash and entire features may not work at all.",
+ "LabelVideoType": "Type widea",
+ "OptionBluray": "Bluray",
+ "OptionDvd": "Dvd",
+ "OptionIso": "Iso",
+ "Option3D": "3D",
+ "LabelFeatures": "W\u0142a\u015bciwo\u015bci",
+ "LabelService": "Service:",
+ "LabelStatus": "Status:",
+ "LabelVersion": "Version:",
+ "LabelLastResult": "Last result:",
+ "OptionHasSubtitles": "Napisy",
+ "OptionHasTrailer": "Zwiastun",
+ "OptionHasThemeSong": "Theme Song",
+ "OptionHasThemeVideo": "Theme Video",
+ "TabMovies": "Filmy",
+ "TabStudios": "Studia",
+ "TabTrailers": "Zwiastuny",
+ "HeaderLatestMovies": "Ostatnie filmy",
+ "HeaderLatestTrailers": "Ostatnie zwiastuny",
+ "OptionHasSpecialFeatures": "Special Features",
+ "OptionImdbRating": "Ocena IMDb",
+ "OptionParentalRating": "Ocena rodzicielska",
+ "OptionPremiereDate": "Data premiery",
+ "TabBasic": "Podstawowe",
+ "TabAdvanced": "Zaawansowane",
+ "HeaderStatus": "Status",
+ "OptionContinuing": "Continuing",
+ "OptionEnded": "Ended",
+ "HeaderAirDays": "Air Days:",
+ "OptionSunday": "Niedziela",
+ "OptionMonday": "Poniedzia\u0142ek",
+ "OptionTuesday": "Wtorek",
+ "OptionWednesday": "\u015aroda",
+ "OptionThursday": "Czwartek",
+ "OptionFriday": "Pi\u0105tek",
+ "OptionSaturday": "Sobota",
+ "HeaderManagement": "Management:",
+ "OptionMissingImdbId": "Brakuje Id IMDb",
+ "OptionMissingTvdbId": "Brakuje Id TheTVDB",
+ "OptionMissingOverview": "Missing Overview",
+ "OptionFileMetadataYearMismatch": "File\/Metadata Years Mismatched",
+ "TabGeneral": "Og\u00f3lne",
+ "TitleSupport": "Wesprzyj",
+ "TabLog": "Log",
+ "TabAbout": "A propos",
+ "TabSupporterKey": "Supporter Key",
+ "TabBecomeSupporter": "Become a Supporter",
+ "MediaBrowserHasCommunity": "Media Browser has a thriving community of users and contributors.",
+ "CheckoutKnowledgeBase": "Check out our knowledge base to help you get the most out of Media Browser.",
+ "SearchKnowledgeBase": "Search the Knowledge Base",
+ "VisitTheCommunity": "Odwied\u017a spo\u0142eczno\u015b\u0107",
+ "VisitMediaBrowserWebsite": "Odwied\u017a stron\u0119 Media Browser",
+ "VisitMediaBrowserWebsiteLong": "Visit the Media Browser Web site to catch the latest news and keep up with the developer blog.",
+ "OptionHideUser": "Hide this user from login screens",
+ "OptionDisableUser": "Disable this user",
+ "OptionDisableUserHelp": "If disabled the server will not allow any connections from this user. Existing connections will be abruptly terminated.",
+ "HeaderAdvancedControl": "Advanced Control",
+ "LabelName": "Name:",
+ "OptionAllowUserToManageServer": "Allow this user to manage the server",
+ "HeaderFeatureAccess": "Feature Access",
+ "OptionAllowMediaPlayback": "Allow media playback",
+ "OptionAllowBrowsingLiveTv": "Allow browsing of live tv",
+ "OptionAllowDeleteLibraryContent": "Allow this user to delete library content",
+ "OptionAllowManageLiveTv": "Allow management of live tv recordings",
+ "OptionAllowRemoteControlOthers": "Allow this user to remote control other users",
+ "OptionMissingTmdbId": "Missing Tmdb Id",
+ "OptionIsHD": "HD",
+ "OptionIsSD": "SD",
+ "OptionMetascore": "Metascore",
+ "ButtonSelect": "Select",
+ "ButtonSearch": "Search",
+ "ButtonGroupVersions": "Group Versions",
+ "ButtonAddToCollection": "Add to Collection",
+ "PismoMessage": "Utilizing Pismo File Mount through a donated license.",
+ "TangibleSoftwareMessage": "Utilizing Tangible Solutions Java\/C# converters through a donated license.",
+ "HeaderCredits": "Credits",
+ "PleaseSupportOtherProduces": "Please support other free products we utilize:",
+ "VersionNumber": "Version {0}",
+ "TabPaths": "Paths",
+ "TabServer": "Server",
+ "TabTranscoding": "Transcoding",
+ "TitleAdvanced": "Advanced",
+ "LabelAutomaticUpdateLevel": "Automatic update level",
+ "OptionRelease": "Oficjalne wydanie",
+ "OptionBeta": "Beta",
+ "OptionDev": "Dev (Niestabilne)",
+ "LabelAllowServerAutoRestart": "Allow the server to restart automatically to apply updates",
+ "LabelAllowServerAutoRestartHelp": "The server will only restart during idle periods, when no users are active.",
+ "LabelEnableDebugLogging": "Enable debug logging",
+ "LabelRunServerAtStartup": "Run server at startup",
+ "LabelRunServerAtStartupHelp": "This will start the tray icon on windows startup. To start the windows service, uncheck this and run the service from the windows control panel. Please note that you cannot run both at the same time, so you will need to exit the tray icon before starting the service.",
+ "ButtonSelectDirectory": "Select Directory",
+ "LabelCustomPaths": "Specify custom paths where desired. Leave fields empty to use the defaults.",
+ "LabelCachePath": "Cache path:",
+ "LabelCachePathHelp": "This folder contains server cache files, such as images.",
+ "LabelImagesByNamePath": "Images by name path:",
+ "LabelImagesByNamePathHelp": "This folder contains actor, artist, genre and studio images.",
+ "LabelMetadataPath": "Metadata path:",
+ "LabelMetadataPathHelp": "This location contains downloaded artwork and metadata that is not configured to be stored in media folders.",
+ "LabelTranscodingTempPath": "Transcoding temporary path:",
+ "LabelTranscodingTempPathHelp": "This folder contains working files used by the transcoder.",
+ "TabBasics": "Basics",
+ "TabTV": "TV",
+ "TabGames": "Games",
+ "TabMusic": "Music",
+ "TabOthers": "Others",
+ "HeaderExtractChapterImagesFor": "Extract chapter images for:",
+ "OptionMovies": "Movies",
+ "OptionEpisodes": "Episodes",
+ "OptionOtherVideos": "Other Videos",
+ "TitleMetadata": "Metadata",
+ "LabelAutomaticUpdatesFanart": "Enable automatic updates from FanArt.tv",
+ "LabelAutomaticUpdatesTmdb": "Enable automatic updates from TheMovieDB.org",
+ "LabelAutomaticUpdatesTvdb": "Enable automatic updates from TheTVDB.com",
+ "LabelAutomaticUpdatesFanartHelp": "If enabled, new images will be downloaded automatically as they're added to fanart.tv. Existing images will not be replaced.",
+ "LabelAutomaticUpdatesTmdbHelp": "If enabled, new images will be downloaded automatically as they're added to TheMovieDB.org. Existing images will not be replaced.",
+ "LabelAutomaticUpdatesTvdbHelp": "If enabled, new images will be downloaded automatically as they're added to TheTVDB.com. Existing images will not be replaced.",
+ "ExtractChapterImagesHelp": "Extracting chapter images will allow clients to display graphical scene selection menus. The process can be slow, cpu-intensive and may require several gigabytes of space. It runs as a nightly scheduled task at 4am, although this is configurable in the scheduled tasks area. It is not recommended to run this task during peak usage hours.",
+ "LabelMetadataDownloadLanguage": "Preferred download language:",
+ "ButtonAutoScroll": "Auto-scroll",
+ "LabelImageSavingConvention": "Image saving convention:",
+ "LabelImageSavingConventionHelp": "Media Browser recognizes images from most major media applications. Choosing your downloading convention is useful if you also use other products.",
+ "OptionImageSavingCompatible": "Compatible - Media Browser\/Plex\/Xbmc",
+ "OptionImageSavingStandard": "Standard - MB2",
+ "ButtonSignIn": "Sign In",
+ "TitleSignIn": "Sign In",
+ "HeaderPleaseSignIn": "Please sign in",
+ "LabelUser": "User:",
+ "LabelPassword": "Password:",
+ "ButtonManualLogin": "Manual Login",
+ "PasswordLocalhostMessage": "Passwords are not required when logging in from localhost.",
+ "TabGuide": "Guide",
+ "TabChannels": "Channels",
+ "TabCollections": "Collections",
+ "HeaderChannels": "Channels",
+ "TabRecordings": "Recordings",
+ "TabScheduled": "Scheduled",
+ "TabSeries": "Series",
+ "TabFavorites": "Favorites",
+ "TabMyLibrary": "My Library",
+ "ButtonCancelRecording": "Cancel Recording",
+ "HeaderPrePostPadding": "Pre\/Post Padding",
+ "LabelPrePaddingMinutes": "Pre-padding minutes:",
+ "OptionPrePaddingRequired": "Pre-padding is required in order to record.",
+ "LabelPostPaddingMinutes": "Post-padding minutes:",
+ "OptionPostPaddingRequired": "Post-padding is required in order to record.",
+ "HeaderWhatsOnTV": "What's On",
+ "HeaderUpcomingTV": "Upcoming TV",
+ "TabStatus": "Status",
+ "TabSettings": "Settings",
+ "ButtonRefreshGuideData": "Refresh Guide Data",
+ "OptionPriority": "Priority",
+ "OptionRecordOnAllChannels": "Record program on all channels",
+ "OptionRecordAnytime": "Record program at any time",
+ "OptionRecordOnlyNewEpisodes": "Record only new episodes",
+ "HeaderDays": "Days",
+ "HeaderActiveRecordings": "Active Recordings",
+ "HeaderLatestRecordings": "Latest Recordings",
+ "HeaderAllRecordings": "All Recordings",
+ "ButtonPlay": "Play",
+ "ButtonEdit": "Edit",
+ "ButtonRecord": "Record",
+ "ButtonDelete": "Delete",
+ "ButtonRemove": "Remove",
+ "OptionRecordSeries": "Record Series",
+ "HeaderDetails": "Details",
+ "TitleLiveTV": "Live TV",
+ "LabelNumberOfGuideDays": "Number of days of guide data to download:",
+ "LabelNumberOfGuideDaysHelp": "Downloading more days worth of guide data provides the ability to schedule out further in advance and view more listings, but it will also take longer to download. Auto will choose based on the number of channels.",
+ "LabelActiveService": "Active Service:",
+ "LabelActiveServiceHelp": "Multiple tv plugins can be installed but only one can be active at a time.",
+ "OptionAutomatic": "Auto",
+ "LiveTvPluginRequired": "A Live TV service provider plugin is required in order to continue.",
+ "LiveTvPluginRequiredHelp": "Please install one of our available plugins, such as Next Pvr or ServerWmc.",
+ "LabelCustomizeOptionsPerMediaType": "Customize for media type:",
+ "OptionDownloadThumbImage": "Thumb",
+ "OptionDownloadMenuImage": "Menu",
+ "OptionDownloadLogoImage": "Logo",
+ "OptionDownloadBoxImage": "Box",
+ "OptionDownloadDiscImage": "Disc",
+ "OptionDownloadBannerImage": "Banner",
+ "OptionDownloadBackImage": "Back",
+ "OptionDownloadArtImage": "Art",
+ "OptionDownloadPrimaryImage": "Primary",
+ "HeaderFetchImages": "Fetch Images:",
+ "HeaderImageSettings": "Image Settings",
+ "TabOther": "Other",
+ "LabelMaxBackdropsPerItem": "Maximum number of backdrops per item:",
+ "LabelMaxScreenshotsPerItem": "Maximum number of screenshots per item:",
+ "LabelMinBackdropDownloadWidth": "Minimum backdrop download width:",
+ "LabelMinScreenshotDownloadWidth": "Minimum screenshot download width:",
+ "ButtonAddScheduledTaskTrigger": "Add Task Trigger",
+ "HeaderAddScheduledTaskTrigger": "Add Task Trigger",
+ "ButtonAdd": "Add",
+ "LabelTriggerType": "Trigger Type:",
+ "OptionDaily": "Daily",
+ "OptionWeekly": "Weekly",
+ "OptionOnInterval": "On an interval",
+ "OptionOnAppStartup": "On application startup",
+ "OptionAfterSystemEvent": "After a system event",
+ "LabelDay": "Day:",
+ "LabelTime": "Time:",
+ "LabelEvent": "Event:",
+ "OptionWakeFromSleep": "Wake from sleep",
+ "LabelEveryXMinutes": "Every:",
+ "HeaderTvTuners": "Tuners",
+ "HeaderGallery": "Gallery",
+ "HeaderLatestGames": "Latest Games",
+ "HeaderRecentlyPlayedGames": "Recently Played Games",
+ "TabGameSystems": "Game Systems",
+ "TitleMediaLibrary": "Media Library",
+ "TabFolders": "Folders",
+ "TabPathSubstitution": "Path Substitution",
+ "LabelSeasonZeroDisplayName": "Season 0 display name:",
+ "LabelEnableRealtimeMonitor": "Enable real time monitoring",
+ "LabelEnableRealtimeMonitorHelp": "Changes will be processed immediately, on supported file systems.",
+ "ButtonScanLibrary": "Scan Library",
+ "HeaderNumberOfPlayers": "Players:",
+ "OptionAnyNumberOfPlayers": "Any",
+ "Option1Player": "1+",
+ "Option2Player": "2+",
+ "Option3Player": "3+",
+ "Option4Player": "4+",
+ "HeaderMediaFolders": "Media Folders",
+ "HeaderThemeVideos": "Theme Videos",
+ "HeaderThemeSongs": "Theme Songs",
+ "HeaderScenes": "Scenes",
+ "HeaderAwardsAndReviews": "Awards and Reviews",
+ "HeaderSoundtracks": "Soundtracks",
+ "HeaderMusicVideos": "Music Videos",
+ "HeaderSpecialFeatures": "Special Features",
+ "HeaderCastCrew": "Cast & Crew",
+ "HeaderAdditionalParts": "Additional Parts",
+ "ButtonSplitVersionsApart": "Split Versions Apart",
+ "ButtonPlayTrailer": "Trailer",
+ "LabelMissing": "Missing",
+ "LabelOffline": "Offline",
+ "PathSubstitutionHelp": "Path substitutions are used for mapping a path on the server to a path that clients are able to access. By allowing clients direct access to media on the server they may be able to play them directly over the network and avoid using server resources to stream and transcode them.",
+ "HeaderFrom": "From",
+ "HeaderTo": "To",
+ "LabelFrom": "From:",
+ "LabelFromHelp": "Example: D:\\Movies (on the server)",
+ "LabelTo": "To:",
+ "LabelToHelp": "Example: \\\\MyServer\\Movies (a path clients can access)",
+ "ButtonAddPathSubstitution": "Add Substitution",
+ "OptionSpecialEpisode": "Specials",
+ "OptionMissingEpisode": "Missing Episodes",
+ "OptionUnairedEpisode": "Unaired Episodes",
+ "OptionEpisodeSortName": "Episode Sort Name",
+ "OptionSeriesSortName": "Series Name",
+ "OptionTvdbRating": "Tvdb Rating",
+ "HeaderTranscodingQualityPreference": "Transcoding Quality Preference:",
+ "OptionAutomaticTranscodingHelp": "The server will decide quality and speed",
+ "OptionHighSpeedTranscodingHelp": "Lower quality, but faster encoding",
+ "OptionHighQualityTranscodingHelp": "Higher quality, but slower encoding",
+ "OptionMaxQualityTranscodingHelp": "Best quality with slower encoding and high CPU usage",
+ "OptionHighSpeedTranscoding": "Higher speed",
+ "OptionHighQualityTranscoding": "Higher quality",
+ "OptionMaxQualityTranscoding": "Max quality",
+ "OptionEnableDebugTranscodingLogging": "Enable debug transcoding logging",
+ "OptionEnableDebugTranscodingLoggingHelp": "This will create very large log files and is only recommended as needed for troubleshooting purposes.",
+ "OptionUpscaling": "Allow clients to request upscaled video",
+ "OptionUpscalingHelp": "In some cases this will result in improved video quality but will increase CPU usage.",
+ "EditCollectionItemsHelp": "Add or remove any movies, series, albums, books or games you wish to group within this collection.",
+ "HeaderAddTitles": "Add Titles",
+ "LabelEnableDlnaPlayTo": "Enable DLNA Play To",
+ "LabelEnableDlnaPlayToHelp": "Media Browser can detect devices within your network and offer the ability to remote control them.",
+ "LabelEnableDlnaDebugLogging": "Enable DLNA debug logging",
+ "LabelEnableDlnaDebugLoggingHelp": "This will create large log files and should only be used as needed for troubleshooting purposes.",
+ "LabelEnableDlnaClientDiscoveryInterval": "Client discovery interval (seconds)",
+ "LabelEnableDlnaClientDiscoveryIntervalHelp": "Determines the duration in seconds between SSDP searches performed by Media Browser.",
+ "HeaderCustomDlnaProfiles": "Custom Profiles",
+ "HeaderSystemDlnaProfiles": "System Profiles",
+ "CustomDlnaProfilesHelp": "Create a custom profile to target a new device or override a system profile.",
+ "SystemDlnaProfilesHelp": "System profiles are read-only. Changes to a system profile will be saved to a new custom profile.",
+ "TitleDashboard": "Dashboard",
+ "TabHome": "Home",
+ "TabInfo": "Info",
+ "HeaderLinks": "Links",
+ "HeaderSystemPaths": "System Paths",
+ "LinkCommunity": "Community",
+ "LinkGithub": "Github",
+ "LinkApiDocumentation": "Api Documentation",
+ "LabelFriendlyServerName": "Friendly server name:",
+ "LabelFriendlyServerNameHelp": "This name will be used to identify this server. If left blank, the computer name will be used.",
+ "LabelPreferredDisplayLanguage": "Preferred display language",
+ "LabelPreferredDisplayLanguageHelp": "Translating Media Browser is an ongoing project and is not yet complete.",
+ "LabelReadHowYouCanContribute": "Read about how you can contribute.",
+ "HeaderNewCollection": "New Collection",
+ "HeaderAddToCollection": "Add to Collection",
+ "ButtonSubmit": "Submit",
+ "NewCollectionNameExample": "Example: Star Wars Collection",
+ "OptionSearchForInternetMetadata": "Search the internet for artwork and metadata",
+ "ButtonCreate": "Create",
+ "LabelHttpServerPortNumber": "Http server port number:",
+ "LabelWebSocketPortNumber": "Web socket port number:",
+ "LabelEnableAutomaticPortHelp": "UPnP allows automated router configuration for remote access. This may not work with some router models.",
+ "LabelExternalDDNS": "External DDNS:",
+ "LabelExternalDDNSHelp": "If you have a dynamic DNS enter it here. Media Browser apps will use it when connecting remotely.",
+ "TabResume": "Resume",
+ "TabWeather": "Weather",
+ "TitleAppSettings": "App Settings",
+ "LabelMinResumePercentage": "Min resume percentage:",
+ "LabelMaxResumePercentage": "Max resume percentage:",
+ "LabelMinResumeDuration": "Min resume duration (seconds):",
+ "LabelMinResumePercentageHelp": "Titles are assumed unplayed if stopped before this time",
+ "LabelMaxResumePercentageHelp": "Titles are assumed fully played if stopped after this time",
+ "LabelMinResumeDurationHelp": "Titles shorter than this will not be resumable",
+ "TitleAutoOrganize": "Auto-Organize",
+ "TabActivityLog": "Activity Log",
+ "HeaderName": "Name",
+ "HeaderDate": "Date",
+ "HeaderSource": "Source",
+ "HeaderDestination": "Destination",
+ "HeaderProgram": "Program",
+ "HeaderClients": "Clients",
+ "LabelCompleted": "Completed",
+ "LabelFailed": "Failed",
+ "LabelSkipped": "Skipped",
+ "HeaderEpisodeOrganization": "Episode Organization",
+ "LabelSeries": "Series:",
+ "LabelSeasonNumber": "Season number",
+ "LabelEpisodeNumber": "Episode number",
+ "LabelEndingEpisodeNumber": "Ending episode number",
+ "LabelEndingEpisodeNumberHelp": "Only required for multi-episode files",
+ "HeaderSupportTheTeam": "Support the Media Browser Team",
+ "LabelSupportAmount": "Amount (USD)",
+ "HeaderSupportTheTeamHelp": "Help ensure the continued development of this project by donating. A portion of all donations will be contributed to other free tools we depend on.",
+ "ButtonEnterSupporterKey": "Enter supporter key",
+ "DonationNextStep": "Once complete, please return and enter your supporter key, which you will receive by email.",
+ "AutoOrganizeHelp": "Auto-organize monitors your download folders for new files and moves them to your media directories.",
+ "AutoOrganizeTvHelp": "TV file organizing will only add episodes to existing series. It will not create new series folders.",
+ "OptionEnableEpisodeOrganization": "Enable new episode organization",
+ "LabelWatchFolder": "Watch folder:",
+ "LabelWatchFolderHelp": "The server will poll this folder during the 'Organize new media files' scheduled task.",
+ "ButtonViewScheduledTasks": "View scheduled tasks",
+ "LabelMinFileSizeForOrganize": "Minimum file size (MB):",
+ "LabelMinFileSizeForOrganizeHelp": "Files under this size will be ignored.",
+ "LabelSeasonFolderPattern": "Season folder pattern:",
+ "LabelSeasonZeroFolderName": "Season zero folder name:",
+ "HeaderEpisodeFilePattern": "Episode file pattern",
+ "LabelEpisodePattern": "Episode pattern:",
+ "LabelMultiEpisodePattern": "Multi-Episode pattern:",
+ "HeaderSupportedPatterns": "Supported Patterns",
+ "HeaderTerm": "Term",
+ "HeaderPattern": "Pattern",
+ "HeaderResult": "Result",
+ "LabelDeleteEmptyFolders": "Delete empty folders after organizing",
+ "LabelDeleteEmptyFoldersHelp": "Enable this to keep the download directory clean.",
+ "LabelDeleteLeftOverFiles": "Delete left over files with the following extensions:",
+ "LabelDeleteLeftOverFilesHelp": "Separate with ;. For example: .nfo;.txt",
+ "OptionOverwriteExistingEpisodes": "Overwrite existing episodes",
+ "LabelTransferMethod": "Transfer method",
+ "OptionCopy": "Copy",
+ "OptionMove": "Move",
+ "LabelTransferMethodHelp": "Copy or move files from the watch folder",
+ "HeaderLatestNews": "Latest News",
+ "HeaderHelpImproveMediaBrowser": "Help Improve Media Browser",
+ "HeaderRunningTasks": "Running Tasks",
+ "HeaderActiveDevices": "Active Devices",
+ "HeaderPendingInstallations": "Pending Installations",
+ "HeaerServerInformation": "Server Information",
+ "ButtonRestartNow": "Restart Now",
+ "ButtonRestart": "Restart",
+ "ButtonShutdown": "Shutdown",
+ "ButtonUpdateNow": "Update Now",
+ "PleaseUpdateManually": "Please shutdown the server and update manually.",
+ "NewServerVersionAvailable": "A new version of Media Browser Server is available!",
+ "ServerUpToDate": "Media Browser Server is up to date",
+ "ErrorConnectingToMediaBrowserRepository": "There was an error connecting to the remote Media Browser repository.",
+ "LabelComponentsUpdated": "The following components have been installed or updated:",
+ "MessagePleaseRestartServerToFinishUpdating": "Please restart the server to finish applying updates.",
+ "LabelDownMixAudioScale": "Audio boost when downmixing:",
+ "LabelDownMixAudioScaleHelp": "Boost audio when downmixing. Set to 1 to preserve original volume value.",
+ "ButtonLinkKeys": "Link Keys",
+ "LabelOldSupporterKey": "Old supporter key",
+ "LabelNewSupporterKey": "New supporter key",
+ "HeaderMultipleKeyLinking": "Multiple Key Linking",
+ "MultipleKeyLinkingHelp": "If you have more than one supporter key, use this form to link the old key's registrations with your new one.",
+ "LabelCurrentEmailAddress": "Current email address",
+ "LabelCurrentEmailAddressHelp": "The current email address to which your new key was sent.",
+ "HeaderForgotKey": "Forgot Key",
+ "LabelEmailAddress": "Email address",
+ "LabelSupporterEmailAddress": "The email address that was used to purchase the key.",
+ "ButtonRetrieveKey": "Retrieve Key",
+ "LabelSupporterKey": "Supporter Key (paste from email)",
+ "LabelSupporterKeyHelp": "Enter your supporter key to start enjoying additional benefits the community has developed for Media Browser.",
+ "MessageInvalidKey": "Supporter key is missing or invalid.",
+ "ErrorMessageInvalidKey": "In order for any premium content to be registered, you must also be a Media Browser Supporter. Please donate and support the continued development of the core product. Thank you.",
+ "HeaderDisplaySettings": "Display Settings",
+ "TabPlayTo": "Play To",
+ "LabelEnableDlnaServer": "Enable Dlna server",
+ "LabelEnableDlnaServerHelp": "Allows UPnP devices on your network to browse and play Media Browser content.",
+ "LabelEnableBlastAliveMessages": "Blast alive messages",
+ "LabelEnableBlastAliveMessagesHelp": "Enable this if the server is not detected reliably by other UPnP devices on your network.",
+ "LabelBlastMessageInterval": "Alive message interval (seconds)",
+ "LabelBlastMessageIntervalHelp": "Determines the duration in seconds between server alive messages.",
+ "LabelDefaultUser": "Default user:",
+ "LabelDefaultUserHelp": "Determines which user library should be displayed on connected devices. This can be overridden for each device using profiles.",
+ "TitleDlna": "DLNA",
+ "TitleChannels": "Channels",
+ "HeaderServerSettings": "Server Settings",
+ "LabelWeatherDisplayLocation": "Weather display location:",
+ "LabelWeatherDisplayLocationHelp": "US zip code \/ City, State, Country \/ City, Country",
+ "LabelWeatherDisplayUnit": "Weather display unit:",
+ "OptionCelsius": "Celsius",
+ "OptionFahrenheit": "Fahrenheit",
+ "HeaderRequireManualLogin": "Require manual username entry for:",
+ "HeaderRequireManualLoginHelp": "When disabled clients may present a login screen with a visual selection of users.",
+ "OptionOtherApps": "Other apps",
+ "OptionMobileApps": "Mobile apps",
+ "HeaderNotificationList": "Click on a notification to configure it's sending options.",
+ "NotificationOptionApplicationUpdateAvailable": "Application update available",
+ "NotificationOptionApplicationUpdateInstalled": "Application update installed",
+ "NotificationOptionPluginUpdateInstalled": "Plugin update installed",
+ "NotificationOptionPluginInstalled": "Plugin installed",
+ "NotificationOptionPluginUninstalled": "Plugin uninstalled",
+ "NotificationOptionVideoPlayback": "Video playback started",
+ "NotificationOptionAudioPlayback": "Audio playback started",
+ "NotificationOptionGamePlayback": "Game playback started",
+ "NotificationOptionVideoPlaybackStopped": "Video playback stopped",
+ "NotificationOptionAudioPlaybackStopped": "Audio playback stopped",
+ "NotificationOptionGamePlaybackStopped": "Game playback stopped",
+ "NotificationOptionTaskFailed": "Scheduled task failure",
+ "NotificationOptionInstallationFailed": "Installation failure",
+ "NotificationOptionNewLibraryContent": "New content added",
+ "NotificationOptionNewLibraryContentMultiple": "New content added (multiple)",
+ "SendNotificationHelp": "By default, notifications are delivered to the dashboard inbox. Browse the plugin catalog to install additional notification options.",
+ "NotificationOptionServerRestartRequired": "Server restart required",
+ "LabelNotificationEnabled": "Enable this notification",
+ "LabelMonitorUsers": "Monitor activity from:",
+ "LabelSendNotificationToUsers": "Send the notification to:",
+ "UsersNotNotifiedAboutSelfActivity": "Users will not be notified about their own activities.",
+ "LabelUseNotificationServices": "Use the following services:",
+ "CategoryUser": "User",
+ "CategorySystem": "System",
+ "CategoryApplication": "Application",
+ "CategoryPlugin": "Plugin",
+ "LabelMessageTitle": "Message title:",
+ "LabelAvailableTokens": "Available tokens:",
+ "AdditionalNotificationServices": "Browse the plugin catalog to install additional notification services.",
+ "OptionAllUsers": "All users",
+ "OptionAdminUsers": "Administrators",
+ "OptionCustomUsers": "Custom",
+ "ButtonArrowUp": "Up",
+ "ButtonArrowDown": "Down",
+ "ButtonArrowLeft": "Left",
+ "ButtonArrowRight": "Right",
+ "ButtonBack": "Back",
+ "ButtonInfo": "Info",
+ "ButtonOsd": "On screen display",
+ "ButtonPageUp": "Page Up",
+ "ButtonPageDown": "Page Down",
+ "PageAbbreviation": "PG",
+ "ButtonHome": "Home",
+ "ButtonSettings": "Settings",
+ "ButtonTakeScreenshot": "Capture Screenshot",
+ "ButtonLetterUp": "Letter Up",
+ "ButtonLetterDown": "Letter Down",
+ "PageButtonAbbreviation": "PG",
+ "LetterButtonAbbreviation": "A",
+ "TabNowPlaying": "Now Playing",
+ "TabNavigation": "Navigation",
+ "TabControls": "Controls",
+ "ButtonFullscreen": "Toggle fullscreen",
+ "ButtonScenes": "Scenes",
+ "ButtonSubtitles": "Subtitles",
+ "ButtonAudioTracks": "Audio tracks",
+ "ButtonPreviousTrack": "Previous track",
+ "ButtonNextTrack": "Next track",
+ "ButtonStop": "Stop",
+ "ButtonPause": "Pause",
+ "LabelGroupMoviesIntoCollections": "Group movies into collections",
+ "LabelGroupMoviesIntoCollectionsHelp": "When displaying movie lists, movies belonging to a collection will be displayed as one grouped item.",
+ "NotificationOptionPluginError": "Plugin failure",
+ "ButtonVolumeUp": "Volume up",
+ "ButtonVolumeDown": "Volume down",
+ "ButtonMute": "Mute",
+ "HeaderLatestMedia": "Latest Media",
+ "OptionSpecialFeatures": "Special Features",
+ "HeaderCollections": "Collections",
+ "LabelProfileCodecsHelp": "Separated by comma. This can be left empty to apply to all codecs.",
+ "LabelProfileContainersHelp": "Separated by comma. This can be left empty to apply to all containers.",
+ "HeaderResponseProfile": "Response Profile",
+ "LabelType": "Type:",
+ "LabelProfileContainer": "Container:",
+ "LabelProfileVideoCodecs": "Video codecs:",
+ "LabelProfileAudioCodecs": "Audio codecs:",
+ "LabelProfileCodecs": "Codecs:",
+ "HeaderDirectPlayProfile": "Direct Play Profile",
+ "HeaderTranscodingProfile": "Transcoding Profile",
+ "HeaderCodecProfile": "Codec Profile",
+ "HeaderCodecProfileHelp": "Codec profiles indicate the limitations of a device when playing specific codecs. If a limitation applies then the media will be transcoded, even if the codec is configured for direct play.",
+ "HeaderContainerProfile": "Container Profile",
+ "HeaderContainerProfileHelp": "Container profiles indicate the limitations of a device when playing specific formats. If a limitation applies then the media will be transcoded, even if the format is configured for direct play.",
+ "OptionProfileVideo": "Video",
+ "OptionProfileAudio": "Audio",
+ "OptionProfileVideoAudio": "Video Audio",
+ "OptionProfilePhoto": "Photo",
+ "LabelUserLibrary": "User library:",
+ "LabelUserLibraryHelp": "Select which user library to display to the device. Leave empty to inherit the default setting.",
+ "OptionPlainStorageFolders": "Display all folders as plain storage folders",
+ "OptionPlainStorageFoldersHelp": "If enabled, all folders are represented in DIDL as \"object.container.storageFolder\" instead of a more specific type, such as \"object.container.person.musicArtist\".",
+ "OptionPlainVideoItems": "Display all videos as plain video items",
+ "OptionPlainVideoItemsHelp": "If enabled, all videos are represented in DIDL as \"object.item.videoItem\" instead of a more specific type, such as \"object.item.videoItem.movie\".",
+ "LabelSupportedMediaTypes": "Supported Media Types:",
+ "TabIdentification": "Identification",
+ "TabDirectPlay": "Direct Play",
+ "TabContainers": "Containers",
+ "TabCodecs": "Codecs",
+ "TabResponses": "Responses",
+ "HeaderProfileInformation": "Profile Information",
+ "LabelEmbedAlbumArtDidl": "Embed album art in Didl",
+ "LabelEmbedAlbumArtDidlHelp": "Some devices prefer this method for obtaining album art. Others may fail to play with this option enabled.",
+ "LabelAlbumArtPN": "Album art PN:",
+ "LabelAlbumArtHelp": "PN used for album art, within the dlna:profileID attribute on upnp:albumArtURI. Some clients require a specific value, regardless of the size of the image.",
+ "LabelAlbumArtMaxWidth": "Album art max width:",
+ "LabelAlbumArtMaxWidthHelp": "Max resolution of album art exposed via upnp:albumArtURI.",
+ "LabelAlbumArtMaxHeight": "Album art max height:",
+ "LabelAlbumArtMaxHeightHelp": "Max resolution of album art exposed via upnp:albumArtURI.",
+ "LabelIconMaxWidth": "Icon max width:",
+ "LabelIconMaxWidthHelp": "Max resolution of icons exposed via upnp:icon.",
+ "LabelIconMaxHeight": "Icon max height:",
+ "LabelIconMaxHeightHelp": "Max resolution of icons exposed via upnp:icon.",
+ "LabelIdentificationFieldHelp": "A case-insensitive substring or regex expression.",
+ "HeaderProfileServerSettingsHelp": "These values control how Media Browser will present itself to the device.",
+ "LabelMaxBitrate": "Max bitrate:",
+ "LabelMaxBitrateHelp": "Specify a max bitrate in bandwidth constrained environments, or if the device imposes it's own limit.",
+ "OptionIgnoreTranscodeByteRangeRequests": "Ignore transcode byte range requests",
+ "OptionIgnoreTranscodeByteRangeRequestsHelp": "If enabled, these requests will be honored but will ignore the byte range header.",
+ "LabelFriendlyName": "Friendly name",
+ "LabelManufacturer": "Manufacturer",
+ "LabelManufacturerUrl": "Manufacturer url",
+ "LabelModelName": "Model name",
+ "LabelModelNumber": "Model number",
+ "LabelModelDescription": "Model description",
+ "LabelModelUrl": "Model url",
+ "LabelSerialNumber": "Serial number",
+ "LabelDeviceDescription": "Device description",
+ "HeaderIdentificationCriteriaHelp": "Enter at least one identification criteria.",
+ "HeaderDirectPlayProfileHelp": "Add direct play profiles to indicate which formats the device can handle natively.",
+ "HeaderTranscodingProfileHelp": "Add transcoding profiles to indicate which formats should be used when transcoding is required.",
+ "HeaderResponseProfileHelp": "Response profiles provide a way to customize information sent to the device when playing certain kinds of media.",
+ "LabelXDlnaCap": "X-Dlna cap:",
+ "LabelXDlnaCapHelp": "Determines the content of the X_DLNACAP element in the urn:schemas-dlna-org:device-1-0 namespace.",
+ "LabelXDlnaDoc": "X-Dlna doc:",
+ "LabelXDlnaDocHelp": "Determines the content of the X_DLNADOC element in the urn:schemas-dlna-org:device-1-0 namespace.",
+ "LabelSonyAggregationFlags": "Sony aggregation flags:",
+ "LabelSonyAggregationFlagsHelp": "Determines the content of the aggregationFlags element in the urn:schemas-sonycom:av namespace.",
+ "LabelTranscodingContainer": "Container:",
+ "LabelTranscodingVideoCodec": "Video codec:",
+ "LabelTranscodingVideoProfile": "Video profile:",
+ "LabelTranscodingAudioCodec": "Audio codec:",
+ "OptionEnableM2tsMode": "Enable M2ts mode",
+ "OptionEnableM2tsModeHelp": "Enable m2ts mode when encoding to mpegts.",
+ "OptionEstimateContentLength": "Estimate content length when transcoding",
+ "OptionReportByteRangeSeekingWhenTranscoding": "Report that the server supports byte seeking when transcoding",
+ "OptionReportByteRangeSeekingWhenTranscodingHelp": "This is required for some devices that don't time seek very well.",
+ "HeaderSubtitleDownloadingHelp": "When Media Browser scans your video files it can search for missing subtitles, and download them using a subtitle provider such as OpenSubtitles.org.",
+ "HeaderDownloadSubtitlesFor": "Download subtitles for:",
+ "MessageNoChapterProviders": "Install a chapter provider plugin such as ChapterDb to enable additional chapter options.",
+ "LabelSkipIfGraphicalSubsPresent": "Skip if the video already contains graphical subtitles",
+ "LabelSkipIfGraphicalSubsPresentHelp": "Keeping text versions of subtitles will result in more efficient delivery to mobile clients.",
+ "TabSubtitles": "Subtitles",
+ "TabChapters": "Chapters",
+ "HeaderDownloadChaptersFor": "Download chapter names for:",
+ "LabelOpenSubtitlesUsername": "Open Subtitles username:",
+ "LabelOpenSubtitlesPassword": "Open Subtitles password:",
+ "HeaderChapterDownloadingHelp": "When Media Browser scans your video files it can download friendly chapter names from the internet using chapter plugins such as ChapterDb.",
+ "LabelPlayDefaultAudioTrack": "Play default audio track regardless of language",
+ "LabelSubtitlePlaybackMode": "Subtitle mode:",
+ "LabelDownloadLanguages": "Download languages:",
+ "ButtonRegister": "Register",
+ "LabelSkipIfAudioTrackPresent": "Skip if the default audio track matches the download language",
+ "LabelSkipIfAudioTrackPresentHelp": "Uncheck this to ensure all videos have subtitles, regardless of audio language.",
+ "HeaderSendMessage": "Send Message",
+ "ButtonSend": "Send",
+ "LabelMessageText": "Message text:",
+ "MessageNoAvailablePlugins": "No available plugins.",
+ "LabelDisplayPluginsFor": "Display plugins for:",
+ "PluginTabMediaBrowserClassic": "MB Classic",
+ "PluginTabMediaBrowserTheater": "MB Theater",
+ "TabOtherPlugins": "Others",
+ "LabelEpisodeName": "Episode name",
+ "LabelSeriesName": "Series name",
+ "ValueSeriesNamePeriod": "Series.name",
+ "ValueSeriesNameUnderscore": "Series_name",
+ "ValueEpisodeNamePeriod": "Episode.name",
+ "ValueEpisodeNameUnderscore": "Episode_name",
+ "HeaderTypeText": "Enter Text",
+ "LabelTypeText": "Text",
+ "HeaderSearchForSubtitles": "Search for Subtitles",
+ "MessageNoSubtitleSearchResultsFound": "No search results founds.",
+ "TabDisplay": "Display",
+ "TabLanguages": "Languages",
+ "TabWebClient": "Web Client",
+ "LabelEnableThemeSongs": "Enable theme songs",
+ "LabelEnableBackdrops": "Enable backdrops",
+ "LabelEnableThemeSongsHelp": "If enabled, theme songs will be played in the background while browsing the library.",
+ "LabelEnableBackdropsHelp": "If enabled, backdrops will be displayed in the background of some pages while browsing the library.",
+ "HeaderHomePage": "Home Page",
+ "HeaderSettingsForThisDevice": "Settings for This Device",
+ "OptionAuto": "Auto",
+ "OptionYes": "Yes",
+ "OptionNo": "No",
+ "LabelHomePageSection1": "Home page section one:",
+ "LabelHomePageSection2": "Home page section two:",
+ "LabelHomePageSection3": "Home page section three:",
+ "LabelHomePageSection4": "Home page section four:",
+ "OptionMyViewsButtons": "My views (buttons)",
+ "OptionMyViews": "My views",
+ "OptionMyViewsSmall": "My views (small)",
+ "OptionResumablemedia": "Resume",
+ "OptionLatestMedia": "Latest media",
+ "OptionLatestChannelMedia": "Latest channel items",
+ "HeaderLatestChannelItems": "Latest Channel Items",
+ "OptionNone": "None",
+ "HeaderLiveTv": "Live TV",
+ "HeaderReports": "Reports",
+ "HeaderMetadataManager": "Metadata Manager",
+ "HeaderPreferences": "Preferences",
+ "MessageLoadingChannels": "Loading channel content...",
+ "ButtonMarkRead": "Mark Read",
+ "OptionDefaultSort": "Default",
+ "OptionCommunityMostWatchedSort": "Most Watched",
+ "TabNextUp": "Next Up",
+ "MessageNoMovieSuggestionsAvailable": "No movie suggestions are currently available. Start watching and rating your movies, and then come back to view your recommendations.",
+ "MessageNoCollectionsAvailable": "Collections allow you to enjoy personalized groupings of Movies, Series, Albums, Books and Games. Click the New button to start creating Collections.",
+ "HeaderWelcomeToMediaBrowserWebClient": "Welcome to the Media Browser Web Client",
+ "ButtonDismiss": "Dismiss",
+ "MessageLearnHowToCustomize": "Learn how to customize this page to your own personal tastes. Click your user icon in the top right corner of the screen to view and update your preferences.",
+ "ButtonEditOtherUserPreferences": "Edit this user's personal preferences.",
+ "LabelChannelStreamQuality": "Preferred internet stream quality:",
+ "LabelChannelStreamQualityHelp": "In a low bandwidth environment, limiting quality can help ensure a smooth streaming experience.",
+ "OptionBestAvailableStreamQuality": "Best available",
+ "LabelEnableChannelContentDownloadingFor": "Enable channel content downloading for:",
+ "LabelEnableChannelContentDownloadingForHelp": "Some channels support downloading content prior to viewing. Enable this in low bandwidth enviornments to download channel content during off hours. Content is downloaded as part of the channel download scheduled task.",
+ "LabelChannelDownloadPath": "Channel content download path:",
+ "LabelChannelDownloadPathHelp": "Specify a custom download path if desired. Leave empty to download to an internal program data folder.",
+ "LabelChannelDownloadAge": "Delete content after: (days)",
+ "LabelChannelDownloadAgeHelp": "Downloaded content older than this will be deleted. It will remain playable via internet streaming.",
+ "ChannelSettingsFormHelp": "Install channels such as Trailers and Vimeo in the plugin catalog.",
+ "LabelSelectCollection": "Select collection:",
+ "ViewTypeMovies": "Movies",
+ "ViewTypeTvShows": "TV",
+ "ViewTypeGames": "Games",
+ "ViewTypeMusic": "Music",
+ "ViewTypeBoxSets": "Collections",
+ "ViewTypeChannels": "Channels",
+ "ViewTypeLiveTV": "Live TV",
+ "HeaderOtherDisplaySettings": "Display Settings",
+ "HeaderMyViews": "My Views",
+ "LabelSelectFolderGroups": "Automatically group content from the following folders into views such as Movies, Music and TV:",
+ "LabelSelectFolderGroupsHelp": "Folders that are unchecked will be displayed by themselves in their own view.",
+ "OptionDisplayAdultContent": "Display adult content",
+ "OptionLibraryFolders": "Media folders",
+ "TitleRemoteControl": "Remote Control",
+ "OptionLatestTvRecordings": "Latest recordings",
+ "LabelProtocolInfo": "Protocol info:",
+ "LabelProtocolInfoHelp": "The value that will be used when responding to GetProtocolInfo requests from the device."
+} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/Localization/Server/pt_BR.json b/MediaBrowser.Server.Implementations/Localization/Server/pt_BR.json
index 31282dfee..9dbd946fe 100644
--- a/MediaBrowser.Server.Implementations/Localization/Server/pt_BR.json
+++ b/MediaBrowser.Server.Implementations/Localization/Server/pt_BR.json
@@ -770,10 +770,10 @@
"LabelHomePageSection1": "Se\u00e7\u00e3o um da tela de in\u00edcio:",
"LabelHomePageSection2": "Se\u00e7\u00e3o dois da tela de in\u00edcio:",
"LabelHomePageSection3": "Se\u00e7\u00e3o tr\u00eas da tela de in\u00edcio:",
- "LabelHomePageSection4": "Se\u00e7\u00e3o quatro da tela de inicio",
- "OptionMyViewsButtons": "My views (buttons)",
- "OptionMyViews": "My views",
- "OptionMyViewsSmall": "My views (small)",
+ "LabelHomePageSection4": "Se\u00e7\u00e3o quatro da tela de in\u00edcio",
+ "OptionMyViewsButtons": "Minhas visualiza\u00e7\u00f5es (bot\u00f5es)",
+ "OptionMyViews": "Minhas visualiza\u00e7\u00f5es",
+ "OptionMyViewsSmall": "Minhas visualiza\u00e7\u00f5es (pequeno)",
"OptionResumablemedia": "Retomar",
"OptionLatestMedia": "M\u00eddias recentes",
"OptionLatestChannelMedia": "Itens recentes de canal",
@@ -820,6 +820,6 @@
"OptionLibraryFolders": "Pastas de m\u00eddias",
"TitleRemoteControl": "Controle Remoto",
"OptionLatestTvRecordings": "\u00daltimas grava\u00e7\u00f5es",
- "LabelProtocolInfo": "Protocol info:",
- "LabelProtocolInfoHelp": "The value that will be used when responding to GetProtocolInfo requests from the device."
+ "LabelProtocolInfo": "Informa\u00e7\u00e3o do protocolo:",
+ "LabelProtocolInfoHelp": "O valor que ser\u00e1 usado ao responder os pedidos GetProtocolInfo do dispositivo."
} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/Localization/Server/ru.json b/MediaBrowser.Server.Implementations/Localization/Server/ru.json
index 203454e3f..e92a23417 100644
--- a/MediaBrowser.Server.Implementations/Localization/Server/ru.json
+++ b/MediaBrowser.Server.Implementations/Localization/Server/ru.json
@@ -491,7 +491,7 @@
"LabelSupportAmount": "\u0421\u0443\u043c\u043c\u0430 (USD)",
"HeaderSupportTheTeamHelp": "\u041f\u043e\u043c\u043e\u0433\u0438\u0442\u0435 \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0442\u044c \u0434\u0430\u043b\u044c\u043d\u0435\u0439\u0448\u0435\u0435 \u0440\u0430\u0437\u0432\u0438\u0442\u0438\u0435 \u044d\u0442\u043e\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u043f\u0443\u0442\u0451\u043c \u0434\u0430\u0440\u0435\u043d\u0438\u044f. \u041d\u0435\u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0447\u0430\u0441\u0442\u044c \u0432\u0441\u0435\u0445 \u043f\u043e\u0436\u0435\u0440\u0442\u0432\u043e\u0432\u0430\u043d\u0438\u0439 \u0431\u0443\u0434\u0435\u0442 \u0432\u043b\u043e\u0436\u0435\u043d\u0430 \u0432 \u0434\u0440\u0443\u0433\u043e\u0435 \u043e\u0442\u043a\u0440\u044b\u0442\u043e\u0435 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u043d\u043e\u0435 \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0435\u043d\u0438\u0435, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u043d\u0430\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e.",
"ButtonEnterSupporterKey": "\u0412\u0432\u0435\u0441\u0442\u0438 \u043a\u043b\u044e\u0447 \u0441\u043f\u043e\u043d\u0441\u043e\u0440\u0430",
- "DonationNextStep": "\u041f\u043e\u0441\u043b\u0435 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u044f \u0432\u0435\u0440\u043d\u0438\u0442\u0435\u0441\u044c, \u0438 \u0432\u0432\u0435\u0434\u0438\u0442\u0435 \u043a\u043b\u044e\u0447 \u0441\u043f\u043e\u043d\u0441\u043e\u0440\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u0435 \u043f\u043e \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u043e\u0439 \u043f\u043e\u0447\u0442\u0435.",
+ "DonationNextStep": "\u041f\u043e\u0441\u043b\u0435 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u044f \u0432\u0435\u0440\u043d\u0438\u0442\u0435\u0441\u044c, \u0438 \u0432\u0432\u0435\u0434\u0438\u0442\u0435 \u043a\u043b\u044e\u0447 \u0441\u043f\u043e\u043d\u0441\u043e\u0440\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u0435 \u043f\u043e \u042d-\u043f\u043e\u0447\u0442\u0435.",
"AutoOrganizeHelp": "\u041f\u0440\u0438 \u0430\u0432\u0442\u043e\u0440\u0435\u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0446\u0438\u0438 \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u043f\u0430\u043f\u043a\u0430, \u043a\u0443\u0434\u0430 \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u044e\u0442\u0441\u044f \u043d\u043e\u0432\u044b\u0435 \u0444\u0430\u0439\u043b\u044b, \u0430 \u0437\u0430\u0442\u0435\u043c \u0442\u0435 \u0431\u0443\u0434\u0443\u0442 \u043f\u0435\u0440\u0435\u043d\u0435\u0441\u0435\u043d\u044b \u0432 \u0432\u0430\u0448\u0438 \u043c\u0435\u0434\u0438\u0430\u043f\u0430\u043f\u043a\u0438.",
"AutoOrganizeTvHelp": "\u041f\u0440\u0438 \u0440\u0435\u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0446\u0438\u0438 \u0422\u0412 \u0444\u0430\u0439\u043b\u043e\u0432 \u044d\u043f\u0438\u0437\u043e\u0434\u044b \u0431\u0443\u0434\u0443\u0442 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0442\u044c\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u0432 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0435 \u0441\u0435\u0440\u0438\u0430\u043b\u044b. \u041d\u043e\u0432\u044b\u0435 \u043f\u0430\u043f\u043a\u0438 \u0441\u0435\u0440\u0438\u0430\u043b\u043e\u0432 \u043d\u0435 \u0431\u0443\u0434\u0443\u0442 \u0441\u043e\u0437\u0434\u0430\u043d\u044b.",
"OptionEnableEpisodeOrganization": "\u0412\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0440\u0435\u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0446\u0438\u044e \u043d\u043e\u0432\u044b\u0445 \u044d\u043f\u0438\u0437\u043e\u0434\u043e\u0432",
@@ -532,7 +532,7 @@
"NewServerVersionAvailable": "\u0414\u043e\u0441\u0442\u0443\u043f\u043d\u0430 \u043d\u043e\u0432\u0430\u044f \u0432\u0435\u0440\u0441\u0438\u044f Media Browser Server!",
"ServerUpToDate": "Media Browser Server - \u043e\u0431\u043d\u043e\u0432\u043b\u0451\u043d",
"ErrorConnectingToMediaBrowserRepository": "\u041f\u0440\u043e\u0438\u0437\u043e\u0448\u043b\u0430 \u043e\u0448\u0438\u0431\u043a\u0430 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a \u0443\u0434\u0430\u043b\u0451\u043d\u043d\u043e\u043c\u0443 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u044e Media Browser.",
- "LabelComponentsUpdated": "\u0411\u044b\u043b\u0438 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u044b \u0438\u043b\u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u044b \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b :",
+ "LabelComponentsUpdated": "\u0411\u044b\u043b\u0438 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u044b \u0438\u043b\u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u044b \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b:",
"MessagePleaseRestartServerToFinishUpdating": "\u041f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 \u0441\u0435\u0440\u0432\u0435\u0440, \u0447\u0442\u043e\u0431\u044b \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u044c \u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0439.",
"LabelDownMixAudioScale": "\u0423\u0441\u0438\u043b\u0435\u043d\u0438\u0435 \u043f\u0440\u0438 \u043f\u043e\u043d\u0438\u0436\u0430\u044e\u0449\u0435\u043c \u043c\u0438\u043a\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0438:",
"LabelDownMixAudioScaleHelp": "\u0423\u0441\u0438\u043b\u0435\u043d\u0438\u0435 \u0437\u0432\u0443\u043a\u0430 \u043f\u0440\u0438 \u043f\u043e\u043d\u0438\u0436\u0430\u044e\u0449\u0435\u043c \u043c\u0438\u043a\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0438. \u0412\u0432\u0435\u0434\u0438\u0442\u0435 1, \u0447\u0442\u043e\u0431\u044b \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0443\u0440\u043e\u0432\u043d\u044f.",
@@ -541,13 +541,13 @@
"LabelNewSupporterKey": "\u041d\u043e\u0432\u044b\u0439 \u043a\u043b\u044e\u0447 \u0441\u043f\u043e\u043d\u0441\u043e\u0440\u0430",
"HeaderMultipleKeyLinking": "\u0421\u0432\u044f\u0437\u044b\u0432\u0430\u043d\u0438\u0435 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u043a\u043b\u044e\u0447\u0435\u0439",
"MultipleKeyLinkingHelp": "\u0415\u0441\u043b\u0438 \u0438\u043c\u0435\u0435\u0442\u0441\u044f \u0431\u043e\u043b\u0435\u0435 \u0447\u0435\u043c \u043e\u0434\u0438\u043d \u043a\u043b\u044e\u0447 \u0441\u043f\u043e\u043d\u0441\u043e\u0440\u0430, \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435\u0441\u044c \u044d\u0442\u043e\u0439 \u0444\u043e\u0440\u043c\u043e\u0439, \u0447\u0442\u043e\u0431\u044b \u0441\u0432\u044f\u0437\u0430\u0442\u044c \u0441\u0442\u0430\u0440\u044b\u0435 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438 \u043a\u043b\u044e\u0447\u0435\u0439 \u0441 \u043d\u043e\u0432\u043e\u0439.",
- "LabelCurrentEmailAddress": "\u0422\u0435\u043a\u0443\u0449\u0438\u0439 \u0430\u0434\u0440\u0435\u0441 \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u043e\u0439 \u043f\u043e\u0447\u0442\u044b",
- "LabelCurrentEmailAddressHelp": "\u0422\u0435\u043a\u0443\u0449\u0438\u0439 \u0430\u0434\u0440\u0435\u0441 \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u043e\u0439 \u043f\u043e\u0447\u0442\u044b, \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u044b\u043b \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u0435\u043d \u043d\u043e\u0432\u044b\u0439 \u043a\u043b\u044e\u0447.",
+ "LabelCurrentEmailAddress": "\u0422\u0435\u043a\u0443\u0449\u0438\u0439 \u0430\u0434\u0440\u0435\u0441 \u042d-\u043f\u043e\u0447\u0442\u044b",
+ "LabelCurrentEmailAddressHelp": "\u0422\u0435\u043a\u0443\u0449\u0438\u0439 \u0430\u0434\u0440\u0435\u0441 \u042d-\u043f\u043e\u0447\u0442\u044b, \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u044b\u043b \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u0435\u043d \u043d\u043e\u0432\u044b\u0439 \u043a\u043b\u044e\u0447.",
"HeaderForgotKey": "\u041a\u043b\u044e\u0447 \u0431\u044b\u043b \u0437\u0430\u0431\u044b\u0442",
- "LabelEmailAddress": "\u0410\u0434\u0440\u0435\u0441 \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u043e\u0439 \u043f\u043e\u0447\u0442\u044b",
- "LabelSupporterEmailAddress": "\u0410\u0434\u0440\u0435\u0441 \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u043e\u0439 \u043f\u043e\u0447\u0442\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u044b\u043b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d \u0434\u043b\u044f \u043f\u0440\u0438\u043e\u0431\u0440\u0435\u0442\u0435\u043d\u0438\u044f \u043a\u043b\u044e\u0447\u0430.",
+ "LabelEmailAddress": "\u0410\u0434\u0440\u0435\u0441 \u042d-\u043f\u043e\u0447\u0442\u044b",
+ "LabelSupporterEmailAddress": "\u0410\u0434\u0440\u0435\u0441 \u042d-\u043f\u043e\u0447\u0442\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u044b\u043b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d \u0434\u043b\u044f \u043f\u0440\u0438\u043e\u0431\u0440\u0435\u0442\u0435\u043d\u0438\u044f \u043a\u043b\u044e\u0447\u0430.",
"ButtonRetrieveKey": "\u0412\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u043a\u043b\u044e\u0447",
- "LabelSupporterKey": "\u041a\u043b\u044e\u0447 \u0441\u043f\u043e\u043d\u0441\u043e\u0440\u0430 (\u0432\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0438\u0437 \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u043e\u0439 \u043f\u043e\u0447\u0442\u044b)",
+ "LabelSupporterKey": "\u041a\u043b\u044e\u0447 \u0441\u043f\u043e\u043d\u0441\u043e\u0440\u0430 (\u0432\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0438\u0437 \u043f\u0438\u0441\u044c\u043c\u0430 \u043f\u043e \u042d-\u043f\u043e\u0447\u0442\u0435)",
"LabelSupporterKeyHelp": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u043a\u043b\u044e\u0447 \u0441\u043f\u043e\u043d\u0441\u043e\u0440\u0430, \u0447\u0442\u043e\u0431\u044b \u043d\u0430\u0447\u0430\u0442\u044c \u043d\u0430\u0441\u043b\u0430\u0436\u0434\u0430\u0442\u044c\u0441\u044f \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u043c\u0438 \u043f\u0440\u0435\u0438\u043c\u0443\u0449\u0435\u0441\u0442\u0432\u0430\u043c\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0431\u044b\u043b\u0438 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0430\u043d\u044b \u0441\u043e\u043e\u0431\u0449\u0435\u0441\u0442\u0432\u043e\u043c \u0434\u043b\u044f Media Browser.",
"MessageInvalidKey": "\u041a\u043b\u044e\u0447 \u0441\u043f\u043e\u043d\u0441\u043e\u0440\u0430 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u0435\u0442 \u0438\u043b\u0438 \u043d\u0435\u0432\u0435\u0440\u0435\u043d",
"ErrorMessageInvalidKey": "\u0414\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043b\u044e\u0431\u043e\u0435 \u043f\u0440\u0435\u043c\u0438\u0443\u043c \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u0435, \u0432\u044b \u0442\u0430\u043a\u0436\u0435 \u0434\u043e\u043b\u0436\u043d\u044b \u0431\u044b\u0442\u044c \u0441\u043f\u043e\u043d\u0441\u043e\u0440\u043e\u043c Media Browser. \u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u0434\u0430\u0440\u0441\u0442\u0432\u0443\u0439\u0442\u0435 \u0438 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0439\u0442\u0435 \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0430\u044e\u0449\u0435\u0435\u0441\u044f \u0440\u0430\u0437\u0432\u0438\u0442\u0438\u0435 \u043e\u0441\u043d\u043e\u0432\u043e\u043f\u043e\u043b\u0430\u0433\u0430\u044e\u0449\u0435\u0433\u043e \u043f\u0440\u043e\u0434\u0443\u043a\u0442\u0430. \u0411\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u0438\u043c \u0432\u0430\u0441.",
@@ -589,7 +589,7 @@
"NotificationOptionInstallationFailed": "\u0421\u0431\u043e\u0439 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438",
"NotificationOptionNewLibraryContent": "\u041d\u043e\u0432\u043e\u0435 \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e",
"NotificationOptionNewLibraryContentMultiple": "\u041d\u043e\u0432\u043e\u0435 \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e (\u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e)",
- "SendNotificationHelp": "\u041f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u044f \u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u0432 \u044f\u0449\u0438\u043a \u0432\u0445\u043e\u0434\u044f\u0449\u0438\u0445 \u043f\u0430\u043d\u0435\u043b\u0438 \u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u043d\u0433\u0430. \u041f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u0432 \u043a\u0430\u0442\u0430\u043b\u043e\u0433 \u043f\u043b\u0430\u0433\u0438\u043d\u043e\u0432, \u0447\u0442\u043e\u0431\u044b \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0441\u0440\u0435\u0434\u0441\u0442\u0432\u0430 \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u044f.",
+ "SendNotificationHelp": "\u041f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u044f \u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u0432 \u044f\u0449\u0438\u043a \u0432\u0445\u043e\u0434\u044f\u0449\u0438\u0445 \u043f\u0430\u043d\u0435\u043b\u0438 \u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u043d\u0433\u0430. \u041f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u0442\u0435 \u043a\u0430\u0442\u0430\u043b\u043e\u0433 \u043f\u043b\u0430\u0433\u0438\u043d\u043e\u0432, \u0447\u0442\u043e\u0431\u044b \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0441\u0440\u0435\u0434\u0441\u0442\u0432\u0430 \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u0439.",
"NotificationOptionServerRestartRequired": "\u0422\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u043a \u0441\u0435\u0440\u0432\u0435\u0440\u0430",
"LabelNotificationEnabled": "\u0412\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0434\u0430\u043d\u043d\u043e\u0435 \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u0435",
"LabelMonitorUsers": "\u041e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u043d\u0438\u0435 \u0434\u0435\u044f\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u043e\u0442:",
@@ -778,7 +778,7 @@
"OptionLatestMedia": "\u041d\u043e\u0432\u0438\u043d\u043a\u0438 \u043d\u043e\u0441\u0438\u0442\u0435\u043b\u0435\u0439",
"OptionLatestChannelMedia": "\u041d\u043e\u0432\u0438\u043d\u043a\u0438 \u043a\u0430\u043d\u0430\u043b\u043e\u0432",
"HeaderLatestChannelItems": "\u041d\u043e\u0432\u0438\u043d\u043a\u0438 \u043a\u0430\u043d\u0430\u043b\u043e\u0432",
- "OptionNone": "\u041d\u0438\u043a\u0430\u043a\u043e\u0435",
+ "OptionNone": "\u041d\u0438\u0447\u0435\u0433\u043e",
"HeaderLiveTv": "\u0422\u0412 \u044d\u0444\u0438\u0440",
"HeaderReports": "\u041e\u0442\u0447\u0451\u0442\u044b",
"HeaderMetadataManager": "\u0414\u0438\u0441\u043f\u0435\u0442\u0447\u0435\u0440 \u043c\u0435\u0442\u0430\u0434\u0430\u043d\u043d\u044b\u0445",
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.Server.Implementations/Localization/Server/sv.json b/MediaBrowser.Server.Implementations/Localization/Server/sv.json
index 8d14dbf8e..79f1f5826 100644
--- a/MediaBrowser.Server.Implementations/Localization/Server/sv.json
+++ b/MediaBrowser.Server.Implementations/Localization/Server/sv.json
@@ -771,9 +771,9 @@
"LabelHomePageSection2": "Hemsk\u00e4rmens del 2:",
"LabelHomePageSection3": "Hemsk\u00e4rmens del 3:",
"LabelHomePageSection4": "Hemsidans del 4:",
- "OptionMyViewsButtons": "My views (buttons)",
- "OptionMyViews": "My views",
- "OptionMyViewsSmall": "My views (small)",
+ "OptionMyViewsButtons": "Mina vyer (knappar)",
+ "OptionMyViews": "Mina vyer",
+ "OptionMyViewsSmall": "Mina vyer (liten)",
"OptionResumablemedia": "\u00c5teruppta",
"OptionLatestMedia": "Nytillkommet",
"OptionLatestChannelMedia": "Senaste objekten i Kanaler",
@@ -820,6 +820,6 @@
"OptionLibraryFolders": "Mappvy",
"TitleRemoteControl": "Fj\u00e4rrkontroll",
"OptionLatestTvRecordings": "Senaste inspelningar",
- "LabelProtocolInfo": "Protocol info:",
- "LabelProtocolInfoHelp": "The value that will be used when responding to GetProtocolInfo requests from the device."
+ "LabelProtocolInfo": "Protokollinfo:",
+ "LabelProtocolInfoHelp": "V\u00e4rde att anv\u00e4nda vid svar p\u00e5 GetProtocolInfo-beg\u00e4ran fr\u00e5n enheter."
} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj
index c6a105e22..7410f6377 100644
--- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj
+++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj
@@ -344,6 +344,8 @@
<EmbeddedResource Include="Localization\JavaScript\vi.json" />
<EmbeddedResource Include="Localization\Server\da.json" />
<EmbeddedResource Include="Localization\Server\vi.json" />
+ <EmbeddedResource Include="Localization\JavaScript\pl.json" />
+ <EmbeddedResource Include="Localization\Server\pl.json" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
diff --git a/MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs b/MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs
index 056a526f6..d35282e55 100644
--- a/MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs
+++ b/MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs
@@ -1,12 +1,15 @@
-using MediaBrowser.Common.IO;
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Chapters;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.MediaEncoding;
+using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.MediaInfo;
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -14,7 +17,6 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using MediaBrowser.Model.MediaInfo;
namespace MediaBrowser.Server.Implementations.MediaEncoder
{
@@ -61,23 +63,25 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder
return false;
}
+ var options = _chapterManager.GetConfiguration();
+
if (video is Movie)
{
- if (!_config.Configuration.ChapterOptions.EnableMovieChapterImageExtraction)
+ if (!options.EnableMovieChapterImageExtraction)
{
return false;
}
}
else if (video is Episode)
{
- if (!_config.Configuration.ChapterOptions.EnableEpisodeChapterImageExtraction)
+ if (!options.EnableEpisodeChapterImageExtraction)
{
return false;
}
}
else
{
- if (!_config.Configuration.ChapterOptions.EnableOtherVideoChapterImageExtraction)
+ if (!options.EnableOtherVideoChapterImageExtraction)
{
return false;
}
diff --git a/MediaBrowser.ServerApplication/ApplicationHost.cs b/MediaBrowser.ServerApplication/ApplicationHost.cs
index d03c5fe3d..5c84b92af 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;
@@ -273,11 +279,105 @@ namespace MediaBrowser.ServerApplication
public override Task Init(IProgress<double> progress)
{
- DeleteDeprecatedModules();
+ PerformVersionMigration();
return base.Init(progress);
}
+ private void PerformVersionMigration()
+ {
+ DeleteDeprecatedModules();
+
+ MigrateModularConfigurations();
+ ApplyDefaultMetadataSettings();
+ }
+
+ private void MigrateModularConfigurations()
+ {
+ var saveConfig = false;
+
+ if (ServerConfigurationManager.Configuration.DlnaOptions != null)
+ {
+ ServerConfigurationManager.SaveConfiguration("dlna", ServerConfigurationManager.Configuration.DlnaOptions);
+ ServerConfigurationManager.Configuration.DlnaOptions = null;
+ saveConfig = true;
+ }
+
+ if (ServerConfigurationManager.Configuration.ChapterOptions != null)
+ {
+ ServerConfigurationManager.SaveConfiguration("chapters", ServerConfigurationManager.Configuration.ChapterOptions);
+ ServerConfigurationManager.Configuration.ChapterOptions = null;
+ saveConfig = true;
+ }
+
+ if (saveConfig)
+ {
+ ServerConfigurationManager.SaveConfiguration();
+ }
+ }
+
+ private void ApplyDefaultMetadataSettings()
+ {
+ 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
@@ -297,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
@@ -881,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
@@ -1063,10 +1178,12 @@ namespace MediaBrowser.ServerApplication
var version = InstallationManager.GetLatestCompatibleVersion(availablePackages, "MBServer", null, ApplicationVersion,
ConfigurationManager.CommonConfiguration.SystemUpdateLevel);
- HasUpdateAvailable = version != null && version.version >= ApplicationVersion;
+ var versionObject = version == null || string.IsNullOrWhiteSpace(version.versionStr) ? null : new Version(version.versionStr);
+
+ HasUpdateAvailable = versionObject != null && versionObject >= ApplicationVersion;
- return version != null ? new CheckForUpdateResult { AvailableVersion = version.version, IsUpdateAvailable = version.version > ApplicationVersion, Package = version } :
- new CheckForUpdateResult { AvailableVersion = ApplicationVersion, IsUpdateAvailable = false };
+ return versionObject != null ? new CheckForUpdateResult { AvailableVersion = versionObject.ToString(), IsUpdateAvailable = versionObject > ApplicationVersion, Package = version } :
+ new CheckForUpdateResult { AvailableVersion = ApplicationVersion.ToString(), IsUpdateAvailable = false };
}
/// <summary>
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..a7015e41f
--- /dev/null
+++ b/MediaBrowser.XbmcMetadata/EntryPoint.cs
@@ -0,0 +1,110 @@
+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)
+ {
+ // TODO: Need a more accurate check here to see if xbmc metadata saving is enabled.
+ // This is probably good enough, but no guarantee
+ var userId = _config.GetNfoConfiguration().UserId;
+ if (string.IsNullOrWhiteSpace(userId))
+ {
+ return;
+ }
+
+ if (e.UpdateReason == ItemUpdateType.ImageUpdate)
+ {
+ var person = e.Item as Person;
+
+ if (person != null)
+ {
+ var items = _libraryManager.RootFolder.RecursiveChildren;
+ items = person.GetTaggedItems(items).ToList();
+
+ foreach (var item in items)
+ {
+ SaveMetadataForItem(item, ItemUpdateType.MetadataEdit);
+ }
+ }
+ }
+ }
+
+ void _userDataManager_UserDataSaved(object sender, UserDataSaveEventArgs e)
+ {
+ var userId = _config.GetNfoConfiguration().UserId;
+ if (string.IsNullOrWhiteSpace(userId))
+ {
+ return;
+ }
+
+ 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 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..0f4d25dde
--- /dev/null
+++ b/MediaBrowser.XbmcMetadata/Savers/AlbumXmlSaver.cs
@@ -0,0 +1,135 @@
+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)
+ {
+ if (!item.SupportsLocalMetadata)
+ {
+ return false;
+ }
+
+ return item is MusicAlbum && updateType >= ItemUpdateType.MetadataDownload;
+ }
+
+ 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..937d702d3
--- /dev/null
+++ b/MediaBrowser.XbmcMetadata/Savers/ArtistXmlSaver.cs
@@ -0,0 +1,116 @@
+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)
+ {
+ if (!item.SupportsLocalMetadata)
+ {
+ return false;
+ }
+
+ return item is MusicArtist && updateType >= ItemUpdateType.MetadataDownload;
+ }
+
+ 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..1b5c24aad
--- /dev/null
+++ b/MediaBrowser.XbmcMetadata/Savers/EpisodeXmlSaver.cs
@@ -0,0 +1,141 @@
+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)
+ {
+ if (!item.SupportsLocalMetadata)
+ {
+ return false;
+ }
+
+ return item is Episode && updateType >= ItemUpdateType.MetadataDownload;
+ }
+ }
+}
diff --git a/MediaBrowser.XbmcMetadata/Savers/MovieXmlSaver.cs b/MediaBrowser.XbmcMetadata/Savers/MovieXmlSaver.cs
new file mode 100644
index 000000000..fa2155e30
--- /dev/null
+++ b/MediaBrowser.XbmcMetadata/Savers/MovieXmlSaver.cs
@@ -0,0 +1,137 @@
+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)
+ {
+ if (!item.SupportsLocalMetadata)
+ {
+ return false;
+ }
+
+ 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 updateType >= ItemUpdateType.MetadataDownload;
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/MediaBrowser.XbmcMetadata/Savers/SeasonXmlSaver.cs b/MediaBrowser.XbmcMetadata/Savers/SeasonXmlSaver.cs
new file mode 100644
index 000000000..1b6c7a340
--- /dev/null
+++ b/MediaBrowser.XbmcMetadata/Savers/SeasonXmlSaver.cs
@@ -0,0 +1,87 @@
+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)
+ {
+ if (!item.SupportsLocalMetadata)
+ {
+ return false;
+ }
+
+ if (!(item is Season))
+ {
+ return false;
+ }
+
+ return updateType >= ItemUpdateType.MetadataDownload || (updateType >= ItemUpdateType.MetadataImport && File.Exists(GetSavePath(item)));
+ }
+ }
+}
diff --git a/MediaBrowser.XbmcMetadata/Savers/SeriesXmlSaver.cs b/MediaBrowser.XbmcMetadata/Savers/SeriesXmlSaver.cs
new file mode 100644
index 000000000..94d46f61d
--- /dev/null
+++ b/MediaBrowser.XbmcMetadata/Savers/SeriesXmlSaver.cs
@@ -0,0 +1,122 @@
+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)
+ {
+ if (!item.SupportsLocalMetadata)
+ {
+ return false;
+ }
+
+ return item is Series && updateType >= ItemUpdateType.MetadataDownload;
+ }
+ }
+}
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("&quot;", "'");
+
+ 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("&quot;", "'");
+
+ 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