aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--MediaBrowser.Api/Images/ImageByNameService.cs8
-rw-r--r--MediaBrowser.Api/Library/LibraryHelpers.cs2
-rw-r--r--MediaBrowser.Api/Sync/SyncService.cs67
-rw-r--r--MediaBrowser.Common.Implementations/IO/CommonFileSystem.cs15
-rw-r--r--MediaBrowser.Common/IO/IFileSystem.cs14
-rw-r--r--MediaBrowser.Controller/Entities/BaseItem.cs8
-rw-r--r--MediaBrowser.Controller/Library/TVUtils.cs14
-rw-r--r--MediaBrowser.Controller/MediaBrowser.Controller.csproj1
-rw-r--r--MediaBrowser.Controller/Sync/ISyncManager.cs22
-rw-r--r--MediaBrowser.Controller/Sync/ISyncRepository.cs58
-rw-r--r--MediaBrowser.Dlna/Channels/DlnaChannelFactory.cs112
-rw-r--r--MediaBrowser.Dlna/ContentDirectory/ContentDirectoryBrowser.cs126
-rw-r--r--MediaBrowser.Dlna/DlnaManager.cs2
-rw-r--r--MediaBrowser.Dlna/MediaBrowser.Dlna.csproj1
-rw-r--r--MediaBrowser.Dlna/PlayTo/Device.cs4
-rw-r--r--MediaBrowser.LocalMetadata/Images/CollectionFolderImageProvider.cs12
-rw-r--r--MediaBrowser.LocalMetadata/Images/EpisodeLocalImageProvider.cs14
-rw-r--r--MediaBrowser.LocalMetadata/Images/ImagesByNameImageProvider.cs2
-rw-r--r--MediaBrowser.LocalMetadata/Images/InternalMetadataFolderImageProvider.cs11
-rw-r--r--MediaBrowser.LocalMetadata/Images/LocalImageProvider.cs27
-rw-r--r--MediaBrowser.LocalMetadata/Providers/GameXmlProvider.cs2
-rw-r--r--MediaBrowser.LocalMetadata/Providers/MovieXmlProvider.cs10
-rw-r--r--MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj12
-rw-r--r--MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj12
-rw-r--r--MediaBrowser.Model/MediaBrowser.Model.csproj4
-rw-r--r--MediaBrowser.Model/Sync/SyncJob.cs80
-rw-r--r--MediaBrowser.Model/Sync/SyncJobCreationResult.cs8
-rw-r--r--MediaBrowser.Model/Sync/SyncJobItem.cs48
-rw-r--r--MediaBrowser.Model/Sync/SyncJobQuery.cs10
-rw-r--r--MediaBrowser.Model/Sync/SyncJobRequest.cs32
-rw-r--r--MediaBrowser.Model/Sync/SyncJobStatus.cs28
-rw-r--r--MediaBrowser.Model/Sync/SyncSchedule.cs12
-rw-r--r--MediaBrowser.Model/Sync/SyncScheduleQuery.cs7
-rw-r--r--MediaBrowser.Providers/BoxSets/MovieDbBoxSetProvider.cs6
-rw-r--r--MediaBrowser.Providers/Manager/ImageSaver.cs10
-rw-r--r--MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs2
-rw-r--r--MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs6
-rw-r--r--MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs19
-rw-r--r--MediaBrowser.Providers/Movies/MovieDbProvider.cs2
-rw-r--r--MediaBrowser.Providers/Subtitles/SubtitleManager.cs2
-rw-r--r--MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs6
-rw-r--r--MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs14
-rw-r--r--MediaBrowser.Server.Implementations/HttpServer/NetListener/HttpListenerServer.cs18
-rw-r--r--MediaBrowser.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpListener.cs21
-rw-r--r--MediaBrowser.Server.Implementations/Library/CoreResolutionIgnoreRule.cs2
-rw-r--r--MediaBrowser.Server.Implementations/Library/LibraryManager.cs17
-rw-r--r--MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs43
-rw-r--r--MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs15
-rw-r--r--MediaBrowser.Server.Implementations/Library/Resolvers/FolderResolver.cs12
-rw-r--r--MediaBrowser.Server.Implementations/Library/Resolvers/LocalTrailerResolver.cs12
-rw-r--r--MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs7
-rw-r--r--MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs12
-rw-r--r--MediaBrowser.Server.Implementations/Library/UserManager.cs2
-rw-r--r--MediaBrowser.Server.Implementations/Localization/JavaScript/es_MX.json8
-rw-r--r--MediaBrowser.Server.Implementations/Localization/JavaScript/fr.json2
-rw-r--r--MediaBrowser.Server.Implementations/Localization/JavaScript/nl.json14
-rw-r--r--MediaBrowser.Server.Implementations/Localization/JavaScript/pt_BR.json4
-rw-r--r--MediaBrowser.Server.Implementations/Localization/JavaScript/ru.json4
-rw-r--r--MediaBrowser.Server.Implementations/Localization/LocalizationManager.cs4
-rw-r--r--MediaBrowser.Server.Implementations/Localization/Server/ar.json12
-rw-r--r--MediaBrowser.Server.Implementations/Localization/Server/ca.json12
-rw-r--r--MediaBrowser.Server.Implementations/Localization/Server/cs.json12
-rw-r--r--MediaBrowser.Server.Implementations/Localization/Server/da.json12
-rw-r--r--MediaBrowser.Server.Implementations/Localization/Server/de.json12
-rw-r--r--MediaBrowser.Server.Implementations/Localization/Server/el.json12
-rw-r--r--MediaBrowser.Server.Implementations/Localization/Server/en_GB.json12
-rw-r--r--MediaBrowser.Server.Implementations/Localization/Server/en_US.json12
-rw-r--r--MediaBrowser.Server.Implementations/Localization/Server/es.json4
-rw-r--r--MediaBrowser.Server.Implementations/Localization/Server/es_MX.json28
-rw-r--r--MediaBrowser.Server.Implementations/Localization/Server/fr.json8
-rw-r--r--MediaBrowser.Server.Implementations/Localization/Server/he.json12
-rw-r--r--MediaBrowser.Server.Implementations/Localization/Server/it.json4
-rw-r--r--MediaBrowser.Server.Implementations/Localization/Server/kk.json4
-rw-r--r--MediaBrowser.Server.Implementations/Localization/Server/ko.json12
-rw-r--r--MediaBrowser.Server.Implementations/Localization/Server/ms.json12
-rw-r--r--MediaBrowser.Server.Implementations/Localization/Server/nb.json12
-rw-r--r--MediaBrowser.Server.Implementations/Localization/Server/nl.json48
-rw-r--r--MediaBrowser.Server.Implementations/Localization/Server/pl.json12
-rw-r--r--MediaBrowser.Server.Implementations/Localization/Server/pt_BR.json26
-rw-r--r--MediaBrowser.Server.Implementations/Localization/Server/pt_PT.json12
-rw-r--r--MediaBrowser.Server.Implementations/Localization/Server/ru.json18
-rw-r--r--MediaBrowser.Server.Implementations/Localization/Server/server.json24
-rw-r--r--MediaBrowser.Server.Implementations/Localization/Server/sv.json4
-rw-r--r--MediaBrowser.Server.Implementations/Localization/Server/vi.json12
-rw-r--r--MediaBrowser.Server.Implementations/Localization/Server/zh_TW.json12
-rw-r--r--MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj5
-rw-r--r--MediaBrowser.Server.Implementations/Sync/SyncManager.cs150
-rw-r--r--MediaBrowser.Server.Implementations/Sync/SyncRepository.cs429
-rw-r--r--MediaBrowser.Server.Implementations/packages.config2
-rw-r--r--MediaBrowser.ServerApplication/ApplicationHost.cs15
-rw-r--r--MediaBrowser.ServerApplication/FFMpeg/FFMpegDownloader.cs2
-rw-r--r--MediaBrowser.Tests/Resolvers/MovieResolverTests.cs2
-rw-r--r--MediaBrowser.WebDashboard/Api/DashboardService.cs2
-rw-r--r--MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj12
-rw-r--r--MediaBrowser.XbmcMetadata/Images/XbmcImageSaver.cs2
-rw-r--r--MediaBrowser.XbmcMetadata/Providers/BaseVideoNfoProvider.cs6
-rw-r--r--MediaBrowser.XbmcMetadata/Savers/MovieNfoSaver.cs6
97 files changed, 1537 insertions, 505 deletions
diff --git a/MediaBrowser.Api/Images/ImageByNameService.cs b/MediaBrowser.Api/Images/ImageByNameService.cs
index d40762964..99d2f144b 100644
--- a/MediaBrowser.Api/Images/ImageByNameService.cs
+++ b/MediaBrowser.Api/Images/ImageByNameService.cs
@@ -1,4 +1,5 @@
using MediaBrowser.Common.Extensions;
+using MediaBrowser.Common.IO;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Dto;
@@ -100,13 +101,16 @@ namespace MediaBrowser.Api.Images
/// </summary>
private readonly IServerApplicationPaths _appPaths;
+ private readonly IFileSystem _fileSystem;
+
/// <summary>
/// Initializes a new instance of the <see cref="ImageByNameService" /> class.
/// </summary>
/// <param name="appPaths">The app paths.</param>
- public ImageByNameService(IServerApplicationPaths appPaths)
+ public ImageByNameService(IServerApplicationPaths appPaths, IFileSystem fileSystem)
{
_appPaths = appPaths;
+ _fileSystem = fileSystem;
}
public object Get(GetMediaInfoImages request)
@@ -133,7 +137,7 @@ namespace MediaBrowser.Api.Images
.Where(i => BaseItem.SupportedImageExtensions.Contains(i.Extension, StringComparer.Ordinal))
.Select(i => new ImageByNameInfo
{
- Name = Path.GetFileNameWithoutExtension(i.FullName),
+ Name = _fileSystem.GetFileNameWithoutExtension(i),
FileLength = i.Length,
// For themeable images, use the Theme property
diff --git a/MediaBrowser.Api/Library/LibraryHelpers.cs b/MediaBrowser.Api/Library/LibraryHelpers.cs
index be9f00a61..e21dc4a73 100644
--- a/MediaBrowser.Api/Library/LibraryHelpers.cs
+++ b/MediaBrowser.Api/Library/LibraryHelpers.cs
@@ -65,7 +65,7 @@ namespace MediaBrowser.Api.Library
var rootFolderPath = appPaths.DefaultUserViewsPath;
var virtualFolderPath = Path.Combine(rootFolderPath, virtualFolderName);
- var shortcutFilename = Path.GetFileNameWithoutExtension(path);
+ var shortcutFilename = fileSystem.GetFileNameWithoutExtension(path);
var lnk = Path.Combine(virtualFolderPath, shortcutFilename + ShortcutFileExtension);
diff --git a/MediaBrowser.Api/Sync/SyncService.cs b/MediaBrowser.Api/Sync/SyncService.cs
index 929d0a463..e0dc07cdc 100644
--- a/MediaBrowser.Api/Sync/SyncService.cs
+++ b/MediaBrowser.Api/Sync/SyncService.cs
@@ -15,13 +15,6 @@ namespace MediaBrowser.Api.Sync
public string Id { get; set; }
}
- [Route("/Sync/Schedules/{Id}", "DELETE", Summary = "Cancels a sync job.")]
- public class CancelSyncSchedule : IReturnVoid
- {
- [ApiMember(Name = "Id", Description = "Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
- public string Id { get; set; }
- }
-
[Route("/Sync/Jobs/{Id}", "GET", Summary = "Gets a sync job.")]
public class GetSyncJob : IReturn<SyncJob>
{
@@ -29,25 +22,26 @@ namespace MediaBrowser.Api.Sync
public string Id { get; set; }
}
- [Route("/Sync/Schedules/{Id}", "GET", Summary = "Gets a sync job.")]
- public class GetSyncSchedule : IReturn<SyncSchedule>
- {
- [ApiMember(Name = "Id", Description = "Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
- public string Id { get; set; }
- }
-
[Route("/Sync/Jobs", "GET", Summary = "Gets sync jobs.")]
public class GetSyncJobs : IReturn<QueryResult<SyncJob>>
{
- }
-
- [Route("/Sync/Schedules", "GET", Summary = "Gets sync schedules.")]
- public class GetSyncSchedules : IReturn<QueryResult<SyncSchedule>>
- {
+ /// <summary>
+ /// Skips over a given number of items within the results. Use for paging.
+ /// </summary>
+ /// <value>The start index.</value>
+ [ApiMember(Name = "StartIndex", Description = "Optional. The record index to start at. All items with a lower index will be dropped from the results.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
+ public int? StartIndex { get; set; }
+
+ /// <summary>
+ /// The maximum number of items to return
+ /// </summary>
+ /// <value>The limit.</value>
+ [ApiMember(Name = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
+ public int? Limit { get; set; }
}
[Route("/Sync/Jobs", "POST", Summary = "Gets sync jobs.")]
- public class CreateSyncJob : SyncJobRequest
+ public class CreateSyncJob : SyncJobRequest, IReturn<SyncJobCreationResult>
{
}
@@ -79,22 +73,13 @@ namespace MediaBrowser.Api.Sync
{
var result = _syncManager.GetJobs(new SyncJobQuery
{
-
+ StartIndex = request.StartIndex,
+ Limit = request.Limit
});
return ToOptimizedResult(result);
}
- public object Get(GetSyncSchedules request)
- {
- var result = _syncManager.GetSchedules(new SyncScheduleQuery
- {
-
- });
-
- return ToOptimizedResult(result);
- }
-
public object Get(GetSyncJob request)
{
var result = _syncManager.GetJob(request.Id);
@@ -102,13 +87,6 @@ namespace MediaBrowser.Api.Sync
return ToOptimizedResult(result);
}
- public object Get(GetSyncSchedule request)
- {
- var result = _syncManager.GetSchedule(request.Id);
-
- return ToOptimizedResult(result);
- }
-
public void Delete(CancelSyncJob request)
{
var task = _syncManager.CancelJob(request.Id);
@@ -116,18 +94,11 @@ namespace MediaBrowser.Api.Sync
Task.WaitAll(task);
}
- public void Delete(CancelSyncSchedule request)
+ public object Post(CreateSyncJob request)
{
- var task = _syncManager.CancelSchedule(request.Id);
+ var result = _syncManager.CreateJob(request);
- Task.WaitAll(task);
- }
-
- public void Post(CreateSyncJob request)
- {
- var task = _syncManager.CreateJob(request);
-
- Task.WaitAll(task);
+ return ToOptimizedResult(result);
}
}
}
diff --git a/MediaBrowser.Common.Implementations/IO/CommonFileSystem.cs b/MediaBrowser.Common.Implementations/IO/CommonFileSystem.cs
index dfe93c5c9..2d67ec975 100644
--- a/MediaBrowser.Common.Implementations/IO/CommonFileSystem.cs
+++ b/MediaBrowser.Common.Implementations/IO/CommonFileSystem.cs
@@ -367,5 +367,20 @@ namespace MediaBrowser.Common.Implementations.IO
return newPath;
}
+
+ public string GetFileNameWithoutExtension(FileSystemInfo info)
+ {
+ if (info is DirectoryInfo)
+ {
+ return info.Name;
+ }
+
+ return Path.GetFileNameWithoutExtension(info.FullName);
+ }
+
+ public string GetFileNameWithoutExtension(string path)
+ {
+ return Path.GetFileNameWithoutExtension(path);
+ }
}
}
diff --git a/MediaBrowser.Common/IO/IFileSystem.cs b/MediaBrowser.Common/IO/IFileSystem.cs
index de9b7e88a..27307a0e9 100644
--- a/MediaBrowser.Common/IO/IFileSystem.cs
+++ b/MediaBrowser.Common/IO/IFileSystem.cs
@@ -112,5 +112,19 @@ namespace MediaBrowser.Common.IO
/// <param name="to">To.</param>
/// <returns>System.String.</returns>
string SubstitutePath(string path, string from, string to);
+
+ /// <summary>
+ /// Gets the file name without extension.
+ /// </summary>
+ /// <param name="info">The information.</param>
+ /// <returns>System.String.</returns>
+ string GetFileNameWithoutExtension(FileSystemInfo info);
+
+ /// <summary>
+ /// Gets the file name without extension.
+ /// </summary>
+ /// <param name="path">The path.</param>
+ /// <returns>System.String.</returns>
+ string GetFileNameWithoutExtension(string path);
}
}
diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs
index b891bcfb2..2bdbab084 100644
--- a/MediaBrowser.Controller/Entities/BaseItem.cs
+++ b/MediaBrowser.Controller/Entities/BaseItem.cs
@@ -529,10 +529,10 @@ namespace MediaBrowser.Controller.Entities
.Where(i => string.Equals(i.Name, TrailerFolderName, StringComparison.OrdinalIgnoreCase))
.SelectMany(i => i.EnumerateFiles("*", SearchOption.TopDirectoryOnly))
.ToList();
-
+
// Support plex/xbmc convention
files.AddRange(fileSystemChildren.OfType<FileInfo>()
- .Where(i => System.IO.Path.GetFileNameWithoutExtension(i.Name).EndsWith(XbmcTrailerFileSuffix, StringComparison.OrdinalIgnoreCase) && !string.Equals(Path, i.FullName, StringComparison.OrdinalIgnoreCase))
+ .Where(i => FileSystem.GetFileNameWithoutExtension(i).EndsWith(XbmcTrailerFileSuffix, StringComparison.OrdinalIgnoreCase) && !string.Equals(Path, i.FullName, StringComparison.OrdinalIgnoreCase))
);
return LibraryManager.ResolvePaths<Trailer>(files, directoryService, null).Select(video =>
@@ -564,7 +564,7 @@ namespace MediaBrowser.Controller.Entities
// Support plex/xbmc convention
files.AddRange(fileSystemChildren.OfType<FileInfo>()
- .Where(i => string.Equals(System.IO.Path.GetFileNameWithoutExtension(i.Name), ThemeSongFilename, StringComparison.OrdinalIgnoreCase))
+ .Where(i => string.Equals(FileSystem.GetFileNameWithoutExtension(i), ThemeSongFilename, StringComparison.OrdinalIgnoreCase))
);
return LibraryManager.ResolvePaths<Audio.Audio>(files, directoryService, null).Select(audio =>
@@ -1564,7 +1564,7 @@ namespace MediaBrowser.Controller.Entities
if (string.IsNullOrEmpty(Name) && !string.IsNullOrEmpty(Path))
{
- Name = System.IO.Path.GetFileNameWithoutExtension(Path);
+ Name = FileSystem.GetFileNameWithoutExtension(Path);
hasChanges = true;
}
diff --git a/MediaBrowser.Controller/Library/TVUtils.cs b/MediaBrowser.Controller/Library/TVUtils.cs
index af0ff8319..86699272e 100644
--- a/MediaBrowser.Controller/Library/TVUtils.cs
+++ b/MediaBrowser.Controller/Library/TVUtils.cs
@@ -1,4 +1,5 @@
-using MediaBrowser.Controller.Entities;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Resolvers;
using System;
@@ -189,11 +190,14 @@ namespace MediaBrowser.Controller.Library
/// </summary>
/// <param name="path">The path.</param>
/// <param name="directoryService">The directory service.</param>
+ /// <param name="fileSystem">The file system.</param>
/// <returns><c>true</c> if [is season folder] [the specified path]; otherwise, <c>false</c>.</returns>
- private static bool IsSeasonFolder(string path, IDirectoryService directoryService)
+ private static bool IsSeasonFolder(string path, IDirectoryService directoryService, IFileSystem fileSystem)
{
// It's a season folder if it's named as such and does not contain any audio files, apart from theme.mp3
- return GetSeasonNumberFromPath(path) != null && !directoryService.GetFiles(path).Any(i => EntityResolutionHelper.IsAudioFile(i.FullName) && !string.Equals(Path.GetFileNameWithoutExtension(i.FullName), BaseItem.ThemeSongFilename));
+ return GetSeasonNumberFromPath(path) != null &&
+ !directoryService.GetFiles(path)
+ .Any(i => EntityResolutionHelper.IsAudioFile(i.FullName) && !string.Equals(fileSystem.GetFileNameWithoutExtension(i), BaseItem.ThemeSongFilename));
}
/// <summary>
@@ -204,7 +208,7 @@ namespace MediaBrowser.Controller.Library
/// <param name="fileSystemChildren">The file system children.</param>
/// <param name="directoryService">The directory service.</param>
/// <returns><c>true</c> if [is series folder] [the specified path]; otherwise, <c>false</c>.</returns>
- public static bool IsSeriesFolder(string path, bool considerSeasonlessSeries, IEnumerable<FileSystemInfo> fileSystemChildren, IDirectoryService directoryService)
+ public static bool IsSeriesFolder(string path, bool considerSeasonlessSeries, IEnumerable<FileSystemInfo> fileSystemChildren, IDirectoryService directoryService, IFileSystem fileSystem)
{
// A folder with more than 3 non-season folders in will not becounted as a series
var nonSeriesFolders = 0;
@@ -225,7 +229,7 @@ namespace MediaBrowser.Controller.Library
if ((attributes & FileAttributes.Directory) == FileAttributes.Directory)
{
- if (IsSeasonFolder(child.FullName, directoryService))
+ if (IsSeasonFolder(child.FullName, directoryService, fileSystem))
{
return true;
}
diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj
index 5c3f10b54..aee118f9a 100644
--- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj
+++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj
@@ -322,6 +322,7 @@
<Compile Include="Sync\ICloudSyncProvider.cs" />
<Compile Include="Sync\ISyncManager.cs" />
<Compile Include="Sync\ISyncProvider.cs" />
+ <Compile Include="Sync\ISyncRepository.cs" />
<Compile Include="Themes\IAppThemeManager.cs" />
<Compile Include="Themes\InternalThemeImage.cs" />
</ItemGroup>
diff --git a/MediaBrowser.Controller/Sync/ISyncManager.cs b/MediaBrowser.Controller/Sync/ISyncManager.cs
index 714e71a1f..1d5ab7d3e 100644
--- a/MediaBrowser.Controller/Sync/ISyncManager.cs
+++ b/MediaBrowser.Controller/Sync/ISyncManager.cs
@@ -13,7 +13,7 @@ namespace MediaBrowser.Controller.Sync
/// </summary>
/// <param name="request">The request.</param>
/// <returns>Task.</returns>
- Task<List<SyncJob>> CreateJob(SyncJobRequest request);
+ Task<SyncJobCreationResult> CreateJob(SyncJobRequest request);
/// <summary>
/// Gets the jobs.
@@ -22,24 +22,11 @@ namespace MediaBrowser.Controller.Sync
QueryResult<SyncJob> GetJobs(SyncJobQuery query);
/// <summary>
- /// Gets the schedules.
- /// </summary>
- /// <returns>QueryResult&lt;SyncSchedule&gt;.</returns>
- QueryResult<SyncSchedule> GetSchedules(SyncScheduleQuery query);
-
- /// <summary>
/// Gets the job.
/// </summary>
/// <param name="id">The identifier.</param>
/// <returns>SyncJob.</returns>
SyncJob GetJob(string id);
-
- /// <summary>
- /// Gets the schedule.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <returns>SyncSchedule.</returns>
- SyncSchedule GetSchedule(string id);
/// <summary>
/// Cancels the job.
@@ -49,13 +36,6 @@ namespace MediaBrowser.Controller.Sync
Task CancelJob(string id);
/// <summary>
- /// Cancels the schedule.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <returns>Task.</returns>
- Task CancelSchedule(string id);
-
- /// <summary>
/// Adds the parts.
/// </summary>
void AddParts(IEnumerable<ISyncProvider> providers);
diff --git a/MediaBrowser.Controller/Sync/ISyncRepository.cs b/MediaBrowser.Controller/Sync/ISyncRepository.cs
new file mode 100644
index 000000000..9cce69bdc
--- /dev/null
+++ b/MediaBrowser.Controller/Sync/ISyncRepository.cs
@@ -0,0 +1,58 @@
+using MediaBrowser.Model.Querying;
+using MediaBrowser.Model.Sync;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Controller.Sync
+{
+ public interface ISyncRepository
+ {
+ /// <summary>
+ /// Gets the job.
+ /// </summary>
+ /// <param name="id">The identifier.</param>
+ /// <returns>SyncJob.</returns>
+ SyncJob GetJob(string id);
+
+ /// <summary>
+ /// Creates the specified job.
+ /// </summary>
+ /// <param name="job">The job.</param>
+ /// <returns>Task.</returns>
+ Task Create(SyncJob job);
+
+ /// <summary>
+ /// Updates the specified job.
+ /// </summary>
+ /// <param name="job">The job.</param>
+ /// <returns>Task.</returns>
+ Task Update(SyncJob job);
+
+ /// <summary>
+ /// Gets the jobs.
+ /// </summary>
+ /// <param name="query">The query.</param>
+ /// <returns>QueryResult&lt;SyncJob&gt;.</returns>
+ QueryResult<SyncJob> GetJobs(SyncJobQuery query);
+
+ /// <summary>
+ /// Gets the job item.
+ /// </summary>
+ /// <param name="id">The identifier.</param>
+ /// <returns>SyncJobItem.</returns>
+ SyncJobItem GetJobItem(string id);
+
+ /// <summary>
+ /// Creates the specified job item.
+ /// </summary>
+ /// <param name="jobItem">The job item.</param>
+ /// <returns>Task.</returns>
+ Task Create(SyncJobItem jobItem);
+
+ /// <summary>
+ /// Updates the specified job item.
+ /// </summary>
+ /// <param name="jobItem">The job item.</param>
+ /// <returns>Task.</returns>
+ Task Update(SyncJobItem jobItem);
+ }
+}
diff --git a/MediaBrowser.Dlna/Channels/DlnaChannelFactory.cs b/MediaBrowser.Dlna/Channels/DlnaChannelFactory.cs
index 230b5e145..fc5fd4061 100644
--- a/MediaBrowser.Dlna/Channels/DlnaChannelFactory.cs
+++ b/MediaBrowser.Dlna/Channels/DlnaChannelFactory.cs
@@ -1,7 +1,9 @@
-using MediaBrowser.Common.Net;
+using MediaBrowser.Common.Extensions;
+using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Providers;
+using MediaBrowser.Dlna.ContentDirectory;
using MediaBrowser.Dlna.PlayTo;
using MediaBrowser.Dlna.Ssdp;
using MediaBrowser.Model.Channels;
@@ -20,7 +22,7 @@ namespace MediaBrowser.Dlna.Channels
private readonly IServerConfigurationManager _config;
private readonly ILogger _logger;
private readonly IHttpClient _httpClient;
-
+
private DeviceDiscovery _deviceDiscovery;
private readonly SemaphoreSlim _syncLock = new SemaphoreSlim(1, 1);
@@ -96,7 +98,7 @@ namespace MediaBrowser.Dlna.Channels
}
catch (Exception ex)
{
-
+
}
finally
{
@@ -123,7 +125,7 @@ namespace MediaBrowser.Dlna.Channels
}
await _syncLock.WaitAsync().ConfigureAwait(false);
-
+
try
{
var list = _servers.ToList();
@@ -163,7 +165,23 @@ namespace MediaBrowser.Dlna.Channels
public IEnumerable<IChannel> GetChannels()
{
- return _servers.Select(i => new ServerChannel(i)).ToList();
+ //if (_servers.Count > 0)
+ //{
+ // var service = _servers[0].Properties.Services
+ // .FirstOrDefault(i => string.Equals(i.ServiceType, "urn:schemas-upnp-org:service:ContentDirectory:1", StringComparison.OrdinalIgnoreCase));
+
+ // var controlUrl = service == null ? null : (_servers[0].Properties.BaseUrl.TrimEnd('/') + "/" + service.ControlUrl.TrimStart('/'));
+
+ // if (!string.IsNullOrEmpty(controlUrl))
+ // {
+ // return new List<IChannel>
+ // {
+ // new ServerChannel(_servers.ToList(), _httpClient, _logger, controlUrl)
+ // };
+ // }
+ //}
+
+ return new List<IChannel>();
}
public void Dispose()
@@ -178,31 +196,37 @@ namespace MediaBrowser.Dlna.Channels
public class ServerChannel : IChannel, IFactoryChannel
{
- private readonly Device _device;
+ private readonly List<Device> _servers = new List<Device>();
+ private readonly IHttpClient _httpClient;
+ private readonly ILogger _logger;
+ private readonly string _controlUrl;
- public ServerChannel(Device device)
+ public ServerChannel(List<Device> servers, IHttpClient httpClient, ILogger logger, string controlUrl)
{
- _device = device;
+ _servers = servers;
+ _httpClient = httpClient;
+ _logger = logger;
+ _controlUrl = controlUrl;
}
public string Name
{
- get { return _device.Properties.Name; }
+ get { return "Devices"; }
}
public string Description
{
- get { return _device.Properties.ModelDescription; }
+ get { return string.Empty; }
}
public string DataVersion
{
- get { return "1"; }
+ get { return DateTime.UtcNow.Ticks.ToString(); }
}
public string HomePageUrl
{
- get { return _device.Properties.ModelUrl; }
+ get { return string.Empty; }
}
public ChannelParentalRating ParentalRating
@@ -234,10 +258,68 @@ namespace MediaBrowser.Dlna.Channels
return true;
}
- public Task<ChannelItemResult> GetChannelItems(InternalChannelItemQuery query, CancellationToken cancellationToken)
+ public async Task<ChannelItemResult> GetChannelItems(InternalChannelItemQuery query, CancellationToken cancellationToken)
{
- // TODO: Implement
- return Task.FromResult(new ChannelItemResult());
+ IEnumerable<ChannelItemInfo> items;
+
+ if (string.IsNullOrWhiteSpace(query.FolderId))
+ {
+ items = _servers.Select(i => new ChannelItemInfo
+ {
+ FolderType = ChannelFolderType.Container,
+ Id = GetServerId(i),
+ Name = i.Properties.Name,
+ Overview = i.Properties.ModelDescription,
+ Type = ChannelItemType.Folder
+ });
+ }
+ else
+ {
+ var idParts = query.FolderId.Split('|');
+ var folderId = idParts.Length == 2 ? idParts[1] : null;
+
+ var result = await new ContentDirectoryBrowser(_httpClient, _logger).Browse(new ContentDirectoryBrowseRequest
+ {
+ Limit = query.Limit,
+ StartIndex = query.StartIndex,
+ ParentId = folderId,
+ ContentDirectoryUrl = _controlUrl
+
+ }, cancellationToken).ConfigureAwait(false);
+
+ items = result.Items.ToList();
+ }
+
+ var list = items.ToList();
+ var count = list.Count;
+
+ list = ApplyPaging(list, query).ToList();
+
+ return new ChannelItemResult
+ {
+ Items = list,
+ TotalRecordCount = count
+ };
+ }
+
+ private string GetServerId(Device device)
+ {
+ return device.Properties.UUID.GetMD5().ToString("N");
+ }
+
+ private IEnumerable<T> ApplyPaging<T>(IEnumerable<T> items, InternalChannelItemQuery query)
+ {
+ if (query.StartIndex.HasValue)
+ {
+ items = items.Skip(query.StartIndex.Value);
+ }
+
+ if (query.Limit.HasValue)
+ {
+ items = items.Take(query.Limit.Value);
+ }
+
+ return items;
}
public Task<DynamicImageResponse> GetChannelImage(ImageType type, CancellationToken cancellationToken)
diff --git a/MediaBrowser.Dlna/ContentDirectory/ContentDirectoryBrowser.cs b/MediaBrowser.Dlna/ContentDirectory/ContentDirectoryBrowser.cs
new file mode 100644
index 000000000..0c62ada80
--- /dev/null
+++ b/MediaBrowser.Dlna/ContentDirectory/ContentDirectoryBrowser.cs
@@ -0,0 +1,126 @@
+using System.Linq;
+using System.Xml.Linq;
+using MediaBrowser.Common.Net;
+using MediaBrowser.Controller.Channels;
+using MediaBrowser.Dlna.PlayTo;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Querying;
+using System;
+using System.Globalization;
+using System.IO;
+using System.Security;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Dlna.ContentDirectory
+{
+ public class ContentDirectoryBrowser
+ {
+ private readonly IHttpClient _httpClient;
+ private readonly ILogger _logger;
+
+ public ContentDirectoryBrowser(IHttpClient httpClient, ILogger logger)
+ {
+ _httpClient = httpClient;
+ _logger = logger;
+ }
+
+ private static XNamespace UNamespace = "u";
+
+ public async Task<QueryResult<ChannelItemInfo>> Browse(ContentDirectoryBrowseRequest request, CancellationToken cancellationToken)
+ {
+ var options = new HttpRequestOptions
+ {
+ CancellationToken = cancellationToken,
+ UserAgent = "Media Browser",
+ RequestContentType = "text/xml; charset=\"utf-8\"",
+ LogErrorResponseBody = true,
+ Url = request.ContentDirectoryUrl
+ };
+
+ options.RequestHeaders["SOAPACTION"] = "urn:schemas-upnp-org:service:ContentDirectory:1#Browse";
+
+ options.RequestContent = GetRequestBody(request);
+
+ var response = await _httpClient.SendAsync(options, "POST");
+
+ using (var reader = new StreamReader(response.Content))
+ {
+ var doc = XDocument.Parse(reader.ReadToEnd(), LoadOptions.PreserveWhitespace);
+
+ var queryResult = new QueryResult<ChannelItemInfo>();
+
+ if (doc.Document == null)
+ return queryResult;
+
+ var responseElement = doc.Document.Descendants(UNamespace + "BrowseResponse").ToList();
+
+ var countElement = responseElement.Select(i => i.Element("TotalMatches")).FirstOrDefault(i => i != null);
+ var countValue = countElement == null ? null : countElement.Value;
+
+ int count;
+ if (!string.IsNullOrWhiteSpace(countValue) && int.TryParse(countValue, NumberStyles.Integer, CultureInfo.InvariantCulture, out count))
+ {
+ queryResult.TotalRecordCount = count;
+
+ var resultElement = responseElement.Select(i => i.Element("Result")).FirstOrDefault(i => i != null);
+ var resultString = (string)resultElement;
+
+ if (resultElement != null)
+ {
+ var xElement = XElement.Parse(resultString);
+ }
+ }
+
+ return queryResult;
+ }
+ }
+
+ private string GetRequestBody(ContentDirectoryBrowseRequest request)
+ {
+ var builder = new StringBuilder();
+
+ builder.Append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
+
+ builder.Append("<s:Envelope s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\" xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"><s:Body>");
+ builder.Append("<u:Browse xmlns:u=\"urn:schemas-upnp-org:service:ContentDirectory:1\">");
+
+ if (string.IsNullOrWhiteSpace(request.ParentId))
+ {
+ request.ParentId = "1";
+ }
+
+ builder.AppendFormat("<ObjectID>{0}</ObjectID>", SecurityElement.Escape(request.ParentId));
+ builder.Append("<BrowseFlag>BrowseDirectChildren</BrowseFlag>");
+
+ //builder.Append("<BrowseFlag>BrowseMetadata</BrowseFlag>");
+
+ builder.Append("<Filter>*</Filter>");
+
+ request.StartIndex = request.StartIndex ?? 0;
+ builder.AppendFormat("<StartingIndex>{0}</StartingIndex>", SecurityElement.Escape(request.StartIndex.Value.ToString(CultureInfo.InvariantCulture)));
+
+ request.Limit = request.Limit ?? 20;
+ if (request.Limit.HasValue)
+ {
+ builder.AppendFormat("<RequestedCount>{0}</RequestedCount>", SecurityElement.Escape(request.Limit.Value.ToString(CultureInfo.InvariantCulture)));
+ }
+
+ builder.Append("<SortCriteria></SortCriteria>");
+
+ builder.Append("</u:Browse>");
+ builder.Append("</s:Body></s:Envelope>");
+
+ return builder.ToString();
+ }
+ }
+
+ public class ContentDirectoryBrowseRequest
+ {
+ public int? StartIndex { get; set; }
+ public int? Limit { get; set; }
+ public string ParentId { get; set; }
+ public string ContentDirectoryUrl { get; set; }
+ }
+}
diff --git a/MediaBrowser.Dlna/DlnaManager.cs b/MediaBrowser.Dlna/DlnaManager.cs
index 333521bb5..29bd21407 100644
--- a/MediaBrowser.Dlna/DlnaManager.cs
+++ b/MediaBrowser.Dlna/DlnaManager.cs
@@ -372,7 +372,7 @@ namespace MediaBrowser.Dlna
Info = new DeviceProfileInfo
{
Id = i.FullName.ToLower().GetMD5().ToString("N"),
- Name = Path.GetFileNameWithoutExtension(i.FullName),
+ Name = _fileSystem.GetFileNameWithoutExtension(i),
Type = type
}
})
diff --git a/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj b/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj
index ec8527738..461470b7a 100644
--- a/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj
+++ b/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj
@@ -57,6 +57,7 @@
<Compile Include="ConnectionManager\ConnectionManagerXmlBuilder.cs" />
<Compile Include="ConnectionManager\ControlHandler.cs" />
<Compile Include="ConnectionManager\ServiceActionListBuilder.cs" />
+ <Compile Include="ContentDirectory\ContentDirectoryBrowser.cs" />
<Compile Include="Didl\Filter.cs" />
<Compile Include="DlnaManager.cs" />
<Compile Include="Common\Argument.cs" />
diff --git a/MediaBrowser.Dlna/PlayTo/Device.cs b/MediaBrowser.Dlna/PlayTo/Device.cs
index 0d7c450d6..85e18150e 100644
--- a/MediaBrowser.Dlna/PlayTo/Device.cs
+++ b/MediaBrowser.Dlna/PlayTo/Device.cs
@@ -819,12 +819,12 @@ namespace MediaBrowser.Dlna.PlayTo
foreach (var services in document.Descendants(uPnpNamespaces.ud.GetName("serviceList")))
{
if (services == null)
- return null;
+ continue;
var servicesList = services.Descendants(uPnpNamespaces.ud.GetName("service"));
if (servicesList == null)
- return null;
+ continue;
foreach (var element in servicesList)
{
diff --git a/MediaBrowser.LocalMetadata/Images/CollectionFolderImageProvider.cs b/MediaBrowser.LocalMetadata/Images/CollectionFolderImageProvider.cs
index 29fd76aa5..ccb2acd1d 100644
--- a/MediaBrowser.LocalMetadata/Images/CollectionFolderImageProvider.cs
+++ b/MediaBrowser.LocalMetadata/Images/CollectionFolderImageProvider.cs
@@ -1,11 +1,19 @@
-using System.Collections.Generic;
+using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Providers;
+using System.Collections.Generic;
namespace MediaBrowser.LocalMetadata.Images
{
public class CollectionFolderLocalImageProvider : ILocalImageFileProvider, IHasOrder
{
+ private readonly IFileSystem _fileSystem;
+
+ public CollectionFolderLocalImageProvider(IFileSystem fileSystem)
+ {
+ _fileSystem = fileSystem;
+ }
+
public string Name
{
get { return "Collection Folder Images"; }
@@ -29,7 +37,7 @@ namespace MediaBrowser.LocalMetadata.Images
{
var collectionFolder = (CollectionFolder)item;
- return new LocalImageProvider().GetImages(item, collectionFolder.PhysicalLocations, directoryService);
+ return new LocalImageProvider(_fileSystem).GetImages(item, collectionFolder.PhysicalLocations, directoryService);
}
}
}
diff --git a/MediaBrowser.LocalMetadata/Images/EpisodeLocalImageProvider.cs b/MediaBrowser.LocalMetadata/Images/EpisodeLocalImageProvider.cs
index f40020c84..cd9b78201 100644
--- a/MediaBrowser.LocalMetadata/Images/EpisodeLocalImageProvider.cs
+++ b/MediaBrowser.LocalMetadata/Images/EpisodeLocalImageProvider.cs
@@ -1,4 +1,5 @@
-using MediaBrowser.Controller.Entities;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
@@ -11,6 +12,13 @@ namespace MediaBrowser.LocalMetadata.Images
{
public class EpisodeLocalLocalImageProvider : ILocalImageFileProvider
{
+ private readonly IFileSystem _fileSystem;
+
+ public EpisodeLocalLocalImageProvider(IFileSystem fileSystem)
+ {
+ _fileSystem = fileSystem;
+ }
+
public string Name
{
get { return "Local Images"; }
@@ -27,7 +35,7 @@ namespace MediaBrowser.LocalMetadata.Images
var parentPathFiles = directoryService.GetFileSystemEntries(parentPath);
- var nameWithoutExtension = Path.GetFileNameWithoutExtension(item.Path);
+ var nameWithoutExtension = _fileSystem.GetFileNameWithoutExtension(item.Path);
var files = GetFilesFromParentFolder(nameWithoutExtension, parentPathFiles);
@@ -60,7 +68,7 @@ namespace MediaBrowser.LocalMetadata.Images
if (BaseItem.SupportedImageExtensions.Contains(i.Extension, StringComparer.OrdinalIgnoreCase))
{
- var currentNameWithoutExtension = Path.GetFileNameWithoutExtension(i.Name);
+ var currentNameWithoutExtension = _fileSystem.GetFileNameWithoutExtension(i);
if (string.Equals(filenameWithoutExtension, currentNameWithoutExtension, StringComparison.OrdinalIgnoreCase))
{
diff --git a/MediaBrowser.LocalMetadata/Images/ImagesByNameImageProvider.cs b/MediaBrowser.LocalMetadata/Images/ImagesByNameImageProvider.cs
index 3f84df462..d992e2026 100644
--- a/MediaBrowser.LocalMetadata/Images/ImagesByNameImageProvider.cs
+++ b/MediaBrowser.LocalMetadata/Images/ImagesByNameImageProvider.cs
@@ -45,7 +45,7 @@ namespace MediaBrowser.LocalMetadata.Images
try
{
- return new LocalImageProvider().GetImages(item, path, directoryService);
+ return new LocalImageProvider(_fileSystem).GetImages(item, path, directoryService);
}
catch (DirectoryNotFoundException)
{
diff --git a/MediaBrowser.LocalMetadata/Images/InternalMetadataFolderImageProvider.cs b/MediaBrowser.LocalMetadata/Images/InternalMetadataFolderImageProvider.cs
index 8c4f6247c..c126af884 100644
--- a/MediaBrowser.LocalMetadata/Images/InternalMetadataFolderImageProvider.cs
+++ b/MediaBrowser.LocalMetadata/Images/InternalMetadataFolderImageProvider.cs
@@ -1,19 +1,22 @@
-using System.Collections.Generic;
-using System.IO;
+using MediaBrowser.Common.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.LocalMetadata.Images
{
public class InternalMetadataFolderImageProvider : ILocalImageFileProvider, IHasOrder
{
private readonly IServerConfigurationManager _config;
+ private readonly IFileSystem _fileSystem;
- public InternalMetadataFolderImageProvider(IServerConfigurationManager config)
+ public InternalMetadataFolderImageProvider(IServerConfigurationManager config, IFileSystem fileSystem)
{
_config = config;
+ _fileSystem = fileSystem;
}
public string Name
@@ -57,7 +60,7 @@ namespace MediaBrowser.LocalMetadata.Images
try
{
- return new LocalImageProvider().GetImages(item, path, directoryService);
+ return new LocalImageProvider(_fileSystem).GetImages(item, path, directoryService);
}
catch (DirectoryNotFoundException)
{
diff --git a/MediaBrowser.LocalMetadata/Images/LocalImageProvider.cs b/MediaBrowser.LocalMetadata/Images/LocalImageProvider.cs
index 5d7762823..f69c261d8 100644
--- a/MediaBrowser.LocalMetadata/Images/LocalImageProvider.cs
+++ b/MediaBrowser.LocalMetadata/Images/LocalImageProvider.cs
@@ -1,19 +1,27 @@
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.Linq;
+using MediaBrowser.Common.IO;
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.LocalMetadata.Images
{
public class LocalImageProvider : ILocalImageFileProvider
{
+ private readonly IFileSystem _fileSystem;
+
+ public LocalImageProvider(IFileSystem fileSystem)
+ {
+ _fileSystem = fileSystem;
+ }
+
public string Name
{
get { return "Local Images"; }
@@ -117,7 +125,7 @@ namespace MediaBrowser.LocalMetadata.Images
var baseItem = item as BaseItem;
if (baseItem != null && baseItem.IsInMixedFolder)
{
- imagePrefix = Path.GetFileNameWithoutExtension(item.Path) + "-";
+ imagePrefix = _fileSystem.GetFileNameWithoutExtension(item.Path) + "-";
}
PopulatePrimaryImages(item, images, files, imagePrefix);
@@ -172,7 +180,7 @@ namespace MediaBrowser.LocalMetadata.Images
if (!string.IsNullOrEmpty(item.Path))
{
- var name = Path.GetFileNameWithoutExtension(item.Path);
+ var name = _fileSystem.GetFileNameWithoutExtension(item.Path);
if (!string.IsNullOrEmpty(name))
{
@@ -188,7 +196,7 @@ namespace MediaBrowser.LocalMetadata.Images
if (!string.IsNullOrEmpty(item.Path))
{
- var name = Path.GetFileNameWithoutExtension(item.Path);
+ var name = _fileSystem.GetFileNameWithoutExtension(item.Path);
if (!string.IsNullOrEmpty(name))
{
@@ -259,6 +267,7 @@ namespace MediaBrowser.LocalMetadata.Images
}
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
+
private void PopulateSeasonImagesFromSeriesFolder(Season season, List<LocalImageInfo> images, IDirectoryService directoryService)
{
var seasonNumber = season.IndexNumber;
@@ -316,7 +325,7 @@ namespace MediaBrowser.LocalMetadata.Images
private FileSystemInfo GetImage(IEnumerable<FileSystemInfo> files, string name)
{
var candidates = files
- .Where(i => string.Equals(name, Path.GetFileNameWithoutExtension(i.Name), StringComparison.OrdinalIgnoreCase))
+ .Where(i => string.Equals(name, _fileSystem.GetFileNameWithoutExtension(i), StringComparison.OrdinalIgnoreCase))
.ToList();
return BaseItem.SupportedImageExtensions
diff --git a/MediaBrowser.LocalMetadata/Providers/GameXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/GameXmlProvider.cs
index 681706321..83ef6e424 100644
--- a/MediaBrowser.LocalMetadata/Providers/GameXmlProvider.cs
+++ b/MediaBrowser.LocalMetadata/Providers/GameXmlProvider.cs
@@ -36,7 +36,7 @@ namespace MediaBrowser.LocalMetadata.Providers
var directoryPath = directoryInfo.FullName;
- var specificFile = Path.Combine(directoryPath, Path.GetFileNameWithoutExtension(info.Path) + ".xml");
+ var specificFile = Path.Combine(directoryPath, FileSystem.GetFileNameWithoutExtension(info.Path) + ".xml");
var file = new FileInfo(specificFile);
diff --git a/MediaBrowser.LocalMetadata/Providers/MovieXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/MovieXmlProvider.cs
index 6ba1912a5..fb3a01bf2 100644
--- a/MediaBrowser.LocalMetadata/Providers/MovieXmlProvider.cs
+++ b/MediaBrowser.LocalMetadata/Providers/MovieXmlProvider.cs
@@ -1,12 +1,12 @@
-using System.Collections.Generic;
-using System.IO;
-using System.Threading;
-using MediaBrowser.Common.IO;
+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.LocalMetadata.Providers
{
@@ -47,7 +47,7 @@ namespace MediaBrowser.LocalMetadata.Providers
var directoryPath = directoryInfo.FullName;
- var specificFile = Path.Combine(directoryPath, Path.GetFileNameWithoutExtension(info.Path) + ".xml");
+ var specificFile = Path.Combine(directoryPath, fileSystem.GetFileNameWithoutExtension(info.Path) + ".xml");
var file = new FileInfo(specificFile);
diff --git a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj
index a892f8bf1..c397cdcd5 100644
--- a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj
+++ b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj
@@ -848,6 +848,12 @@
<Compile Include="..\MediaBrowser.Model\Sync\SyncJob.cs">
<Link>Sync\SyncJob.cs</Link>
</Compile>
+ <Compile Include="..\MediaBrowser.Model\Sync\SyncJobCreationResult.cs">
+ <Link>Sync\SyncJobCreationResult.cs</Link>
+ </Compile>
+ <Compile Include="..\MediaBrowser.Model\Sync\SyncJobItem.cs">
+ <Link>Sync\SyncJobItem.cs</Link>
+ </Compile>
<Compile Include="..\MediaBrowser.Model\Sync\SyncJobQuery.cs">
<Link>Sync\SyncJobQuery.cs</Link>
</Compile>
@@ -860,12 +866,6 @@
<Compile Include="..\MediaBrowser.Model\Sync\SyncQuality.cs">
<Link>Sync\SyncQuality.cs</Link>
</Compile>
- <Compile Include="..\MediaBrowser.Model\Sync\SyncSchedule.cs">
- <Link>Sync\SyncSchedule.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Sync\SyncScheduleQuery.cs">
- <Link>Sync\SyncScheduleQuery.cs</Link>
- </Compile>
<Compile Include="..\MediaBrowser.Model\Sync\SyncTarget.cs">
<Link>Sync\SyncTarget.cs</Link>
</Compile>
diff --git a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj
index ab2a99109..1b74fcf51 100644
--- a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj
+++ b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj
@@ -805,6 +805,12 @@
<Compile Include="..\MediaBrowser.Model\Sync\SyncJob.cs">
<Link>Sync\SyncJob.cs</Link>
</Compile>
+ <Compile Include="..\MediaBrowser.Model\Sync\SyncJobCreationResult.cs">
+ <Link>Sync\SyncJobCreationResult.cs</Link>
+ </Compile>
+ <Compile Include="..\MediaBrowser.Model\Sync\SyncJobItem.cs">
+ <Link>Sync\SyncJobItem.cs</Link>
+ </Compile>
<Compile Include="..\MediaBrowser.Model\Sync\SyncJobQuery.cs">
<Link>Sync\SyncJobQuery.cs</Link>
</Compile>
@@ -817,12 +823,6 @@
<Compile Include="..\MediaBrowser.Model\Sync\SyncQuality.cs">
<Link>Sync\SyncQuality.cs</Link>
</Compile>
- <Compile Include="..\MediaBrowser.Model\Sync\SyncSchedule.cs">
- <Link>Sync\SyncSchedule.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Sync\SyncScheduleQuery.cs">
- <Link>Sync\SyncScheduleQuery.cs</Link>
- </Compile>
<Compile Include="..\MediaBrowser.Model\Sync\SyncTarget.cs">
<Link>Sync\SyncTarget.cs</Link>
</Compile>
diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj
index 6cdd2b8f5..fd38ff723 100644
--- a/MediaBrowser.Model/MediaBrowser.Model.csproj
+++ b/MediaBrowser.Model/MediaBrowser.Model.csproj
@@ -297,12 +297,12 @@
<Compile Include="Session\SessionUserInfo.cs" />
<Compile Include="Session\UserDataChangeInfo.cs" />
<Compile Include="Sync\SyncJob.cs" />
+ <Compile Include="Sync\SyncJobCreationResult.cs" />
+ <Compile Include="Sync\SyncJobItem.cs" />
<Compile Include="Sync\SyncJobQuery.cs" />
<Compile Include="Sync\SyncJobRequest.cs" />
<Compile Include="Sync\SyncJobStatus.cs" />
<Compile Include="Sync\SyncQuality.cs" />
- <Compile Include="Sync\SyncSchedule.cs" />
- <Compile Include="Sync\SyncScheduleQuery.cs" />
<Compile Include="Sync\SyncTarget.cs" />
<Compile Include="System\LogFile.cs" />
<Compile Include="Themes\AppTheme.cs" />
diff --git a/MediaBrowser.Model/Sync/SyncJob.cs b/MediaBrowser.Model/Sync/SyncJob.cs
index 74dd79497..f69fccae5 100644
--- a/MediaBrowser.Model/Sync/SyncJob.cs
+++ b/MediaBrowser.Model/Sync/SyncJob.cs
@@ -1,4 +1,6 @@
-
+using System;
+using System.Collections.Generic;
+
namespace MediaBrowser.Model.Sync
{
public class SyncJob
@@ -14,39 +16,79 @@ namespace MediaBrowser.Model.Sync
/// <value>The device identifier.</value>
public string TargetId { get; set; }
/// <summary>
- /// Gets or sets the item identifier.
- /// </summary>
- /// <value>The item identifier.</value>
- public string ItemId { get; set; }
- /// <summary>
/// Gets or sets the quality.
/// </summary>
/// <value>The quality.</value>
public SyncQuality Quality { get; set; }
/// <summary>
+ /// Gets or sets the current progress.
+ /// </summary>
+ /// <value>The current progress.</value>
+ public double? Progress { get; set; }
+ /// <summary>
+ /// Gets or sets the name.
+ /// </summary>
+ /// <value>The name.</value>
+ public string Name { get; set; }
+ /// <summary>
/// Gets or sets the status.
/// </summary>
/// <value>The status.</value>
- public SyncJobStatus Status { get; set; }
+ public SyncJobStatus Status { get; set; }
/// <summary>
- /// Gets or sets the current progress.
+ /// Gets or sets the user identifier.
/// </summary>
- /// <value>The current progress.</value>
- public double? CurrentProgress { get; set; }
+ /// <value>The user identifier.</value>
+ public string UserId { get; set; }
/// <summary>
- /// Gets or sets the synchronize rule identifier.
+ /// Gets or sets a value indicating whether [unwatched only].
/// </summary>
- /// <value>The synchronize rule identifier.</value>
- public string SyncScheduleId { get; set; }
+ /// <value><c>true</c> if [unwatched only]; otherwise, <c>false</c>.</value>
+ public bool UnwatchedOnly { get; set; }
/// <summary>
- /// Gets or sets the transcoded path.
+ /// Gets or sets the limit.
/// </summary>
- /// <value>The transcoded path.</value>
- public string TranscodedPath { get; set; }
+ /// <value>The limit.</value>
+ public long? Limit { get; set; }
/// <summary>
- /// Gets or sets the name.
+ /// Gets or sets the type of the limit.
/// </summary>
- /// <value>The name.</value>
- public string Name { get; set; }
+ /// <value>The type of the limit.</value>
+ public SyncLimitType? LimitType { get; set; }
+ /// <summary>
+ /// Gets or sets the requested item ids.
+ /// </summary>
+ /// <value>The requested item ids.</value>
+ public List<string> RequestedItemIds { get; set; }
+ /// <summary>
+ /// Gets or sets a value indicating whether this instance is dynamic.
+ /// </summary>
+ /// <value><c>true</c> if this instance is dynamic; otherwise, <c>false</c>.</value>
+ public bool IsDynamic { get; set; }
+ /// <summary>
+ /// Gets or sets the date created.
+ /// </summary>
+ /// <value>The date created.</value>
+ public DateTime DateCreated { get; set; }
+ /// <summary>
+ /// Gets or sets the date last modified.
+ /// </summary>
+ /// <value>The date last modified.</value>
+ public DateTime DateLastModified { get; set; }
+ /// <summary>
+ /// Gets or sets the item count.
+ /// </summary>
+ /// <value>The item count.</value>
+ public int ItemCount { get; set; }
+
+ public string ParentName { get; set; }
+ public string PrimaryImageItemId { get; set; }
+ public string PrimaryImageTag { get; set; }
+ public double? PrimaryImageAspectRatio { get; set; }
+
+ public SyncJob()
+ {
+ RequestedItemIds = new List<string>();
+ }
}
}
diff --git a/MediaBrowser.Model/Sync/SyncJobCreationResult.cs b/MediaBrowser.Model/Sync/SyncJobCreationResult.cs
new file mode 100644
index 000000000..797318b2a
--- /dev/null
+++ b/MediaBrowser.Model/Sync/SyncJobCreationResult.cs
@@ -0,0 +1,8 @@
+
+namespace MediaBrowser.Model.Sync
+{
+ public class SyncJobCreationResult
+ {
+ public SyncJob Job { get; set; }
+ }
+}
diff --git a/MediaBrowser.Model/Sync/SyncJobItem.cs b/MediaBrowser.Model/Sync/SyncJobItem.cs
new file mode 100644
index 000000000..141546eb5
--- /dev/null
+++ b/MediaBrowser.Model/Sync/SyncJobItem.cs
@@ -0,0 +1,48 @@
+
+namespace MediaBrowser.Model.Sync
+{
+ public class SyncJobItem
+ {
+ /// <summary>
+ /// Gets or sets the identifier.
+ /// </summary>
+ /// <value>The identifier.</value>
+ public string Id { get; set; }
+
+ /// <summary>
+ /// Gets or sets the job identifier.
+ /// </summary>
+ /// <value>The job identifier.</value>
+ public string JobId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the item identifier.
+ /// </summary>
+ /// <value>The item identifier.</value>
+ public string ItemId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the target identifier.
+ /// </summary>
+ /// <value>The target identifier.</value>
+ public string TargetId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the output path.
+ /// </summary>
+ /// <value>The output path.</value>
+ public string OutputPath { get; set; }
+
+ /// <summary>
+ /// Gets or sets the status.
+ /// </summary>
+ /// <value>The status.</value>
+ public SyncJobStatus Status { get; set; }
+
+ /// <summary>
+ /// Gets or sets the current progress.
+ /// </summary>
+ /// <value>The current progress.</value>
+ public double? CurrentProgress { get; set; }
+ }
+}
diff --git a/MediaBrowser.Model/Sync/SyncJobQuery.cs b/MediaBrowser.Model/Sync/SyncJobQuery.cs
index f41544db9..74b35186e 100644
--- a/MediaBrowser.Model/Sync/SyncJobQuery.cs
+++ b/MediaBrowser.Model/Sync/SyncJobQuery.cs
@@ -3,5 +3,15 @@ namespace MediaBrowser.Model.Sync
{
public class SyncJobQuery
{
+ /// <summary>
+ /// Gets or sets the start index.
+ /// </summary>
+ /// <value>The start index.</value>
+ public int? StartIndex { get; set; }
+ /// <summary>
+ /// Gets or sets the limit.
+ /// </summary>
+ /// <value>The limit.</value>
+ public int? Limit { get; set; }
}
}
diff --git a/MediaBrowser.Model/Sync/SyncJobRequest.cs b/MediaBrowser.Model/Sync/SyncJobRequest.cs
index cd833068e..7728aad9b 100644
--- a/MediaBrowser.Model/Sync/SyncJobRequest.cs
+++ b/MediaBrowser.Model/Sync/SyncJobRequest.cs
@@ -5,10 +5,10 @@ namespace MediaBrowser.Model.Sync
public class SyncJobRequest
{
/// <summary>
- /// Gets or sets the device identifier.
+ /// Gets or sets the target identifier.
/// </summary>
- /// <value>The device identifier.</value>
- public List<string> TargetIds { get; set; }
+ /// <value>The target identifier.</value>
+ public string TargetId { get; set; }
/// <summary>
/// Gets or sets the item ids.
/// </summary>
@@ -24,11 +24,35 @@ namespace MediaBrowser.Model.Sync
/// </summary>
/// <value>The name.</value>
public string Name { get; set; }
+ /// <summary>
+ /// Gets or sets the user identifier.
+ /// </summary>
+ /// <value>The user identifier.</value>
+ public string UserId { get; set; }
+ /// <summary>
+ /// Gets or sets a value indicating whether [unwatched only].
+ /// </summary>
+ /// <value><c>true</c> if [unwatched only]; otherwise, <c>false</c>.</value>
+ public bool UnwatchedOnly { get; set; }
+ /// <summary>
+ /// Gets or sets the limit.
+ /// </summary>
+ /// <value>The limit.</value>
+ public long? Limit { get; set; }
+ /// <summary>
+ /// Gets or sets the type of the limit.
+ /// </summary>
+ /// <value>The type of the limit.</value>
+ public SyncLimitType? LimitType { get; set; }
public SyncJobRequest()
{
- TargetIds = new List<string>();
ItemIds = new List<string>();
}
}
+
+ public enum SyncLimitType
+ {
+ ItemCount = 0
+ }
}
diff --git a/MediaBrowser.Model/Sync/SyncJobStatus.cs b/MediaBrowser.Model/Sync/SyncJobStatus.cs
index 2a216fe04..ef4d8d7bf 100644
--- a/MediaBrowser.Model/Sync/SyncJobStatus.cs
+++ b/MediaBrowser.Model/Sync/SyncJobStatus.cs
@@ -3,33 +3,11 @@ namespace MediaBrowser.Model.Sync
{
public enum SyncJobStatus
{
- /// <summary>
- /// The queued
- /// </summary>
Queued = 0,
- /// <summary>
- /// The transcoding
- /// </summary>
Transcoding = 1,
- /// <summary>
- /// The transcoding failed
- /// </summary>
TranscodingFailed = 2,
- /// <summary>
- /// The transcoding completed
- /// </summary>
- TranscodingCompleted = 3,
- /// <summary>
- /// The transfering
- /// </summary>
- Transfering = 4,
- /// <summary>
- /// The transfer failed
- /// </summary>
- TransferFailed = 4,
- /// <summary>
- /// The completed
- /// </summary>
- Completed = 6
+ Transfering = 3,
+ Completed = 4,
+ Cancelled = 5
}
}
diff --git a/MediaBrowser.Model/Sync/SyncSchedule.cs b/MediaBrowser.Model/Sync/SyncSchedule.cs
deleted file mode 100644
index 297cbd145..000000000
--- a/MediaBrowser.Model/Sync/SyncSchedule.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-
-namespace MediaBrowser.Model.Sync
-{
- public class SyncSchedule
- {
- /// <summary>
- /// Gets or sets the identifier.
- /// </summary>
- /// <value>The identifier.</value>
- public string Id { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Sync/SyncScheduleQuery.cs b/MediaBrowser.Model/Sync/SyncScheduleQuery.cs
deleted file mode 100644
index b704a358c..000000000
--- a/MediaBrowser.Model/Sync/SyncScheduleQuery.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-
-namespace MediaBrowser.Model.Sync
-{
- public class SyncScheduleQuery
- {
- }
-}
diff --git a/MediaBrowser.Providers/BoxSets/MovieDbBoxSetProvider.cs b/MediaBrowser.Providers/BoxSets/MovieDbBoxSetProvider.cs
index cf7904e5c..e17a96a43 100644
--- a/MediaBrowser.Providers/BoxSets/MovieDbBoxSetProvider.cs
+++ b/MediaBrowser.Providers/BoxSets/MovieDbBoxSetProvider.cs
@@ -216,12 +216,6 @@ namespace MediaBrowser.Providers.BoxSets
{
mainResult = _json.DeserializeFromStream<RootObject>(json);
}
-
- if (String.IsNullOrEmpty(mainResult.overview))
- {
- _logger.Error("Unable to find information for (id:" + id + ")");
- return null;
- }
}
}
return mainResult;
diff --git a/MediaBrowser.Providers/Manager/ImageSaver.cs b/MediaBrowser.Providers/Manager/ImageSaver.cs
index 1b2e9fa6d..d7110c1ec 100644
--- a/MediaBrowser.Providers/Manager/ImageSaver.cs
+++ b/MediaBrowser.Providers/Manager/ImageSaver.cs
@@ -292,7 +292,7 @@ namespace MediaBrowser.Providers.Manager
private string GetStandardSavePath(IHasImages item, ImageType type, int? imageIndex, string mimeType, bool saveLocally)
{
string filename;
-
+
switch (type)
{
case ImageType.Art:
@@ -305,7 +305,7 @@ namespace MediaBrowser.Providers.Manager
filename = item is MusicAlbum ? "cdart" : "disc";
break;
case ImageType.Primary:
- filename = item is Episode ? Path.GetFileNameWithoutExtension(item.Path) : "folder";
+ filename = item is Episode ? _fileSystem.GetFileNameWithoutExtension(item.Path) : "folder";
break;
case ImageType.Backdrop:
filename = GetBackdropSaveFilename(item.GetImages(type), "backdrop", "backdrop", imageIndex);
@@ -367,7 +367,7 @@ namespace MediaBrowser.Providers.Manager
return zeroIndexFilename;
}
- var filenames = images.Select(i => Path.GetFileNameWithoutExtension(i.Path)).ToList();
+ var filenames = images.Select(i => _fileSystem.GetFileNameWithoutExtension(i.Path)).ToList();
var current = 1;
while (filenames.Contains(numberedIndexPrefix + current.ToString(UsCulture), StringComparer.OrdinalIgnoreCase))
@@ -473,7 +473,7 @@ namespace MediaBrowser.Providers.Manager
{
var seasonFolder = Path.GetDirectoryName(item.Path);
- var imageFilename = Path.GetFileNameWithoutExtension(item.Path) + "-thumb" + extension;
+ var imageFilename = _fileSystem.GetFileNameWithoutExtension(item.Path) + "-thumb" + extension;
return new[] { Path.Combine(seasonFolder, imageFilename) };
}
@@ -560,7 +560,7 @@ namespace MediaBrowser.Providers.Manager
}
var folder = Path.GetDirectoryName(item.Path);
- return Path.Combine(folder, Path.GetFileNameWithoutExtension(item.Path) + "-" + imageFilename + extension);
+ return Path.Combine(folder, _fileSystem.GetFileNameWithoutExtension(item.Path) + "-" + imageFilename + extension);
}
}
}
diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs
index 9e9936cab..33b69d71a 100644
--- a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs
+++ b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs
@@ -175,7 +175,7 @@ namespace MediaBrowser.Providers.MediaInfo
if (video != null && !video.IsPlaceHolder)
{
return !video.SubtitleFiles
- .SequenceEqual(SubtitleResolver.GetSubtitleFiles(video, directoryService, false)
+ .SequenceEqual(SubtitleResolver.GetSubtitleFiles(video, directoryService, _fileSystem, false)
.Select(i => i.FullName)
.OrderBy(i => i), StringComparer.OrdinalIgnoreCase);
}
diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs
index c0313561a..a2e1ba05a 100644
--- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs
+++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs
@@ -477,7 +477,7 @@ namespace MediaBrowser.Providers.MediaInfo
MetadataRefreshOptions options,
CancellationToken cancellationToken)
{
- var subtitleResolver = new SubtitleResolver(_localization);
+ var subtitleResolver = new SubtitleResolver(_localization, _fileSystem);
var externalSubtitleStreams = subtitleResolver.GetExternalSubtitleStreams(video, currentStreams.Count, options.DirectoryService, false).ToList();
@@ -790,7 +790,7 @@ namespace MediaBrowser.Providers.MediaInfo
// Assuming they're named "vts_05_01", take all files whose second part matches that of the first file
if (files.Count > 0)
{
- var parts = Path.GetFileNameWithoutExtension(files[0].FullName).Split('_');
+ var parts = _fileSystem.GetFileNameWithoutExtension(files[0]).Split('_');
if (parts.Length == 3)
{
@@ -798,7 +798,7 @@ namespace MediaBrowser.Providers.MediaInfo
files = files.TakeWhile(f =>
{
- var fileParts = Path.GetFileNameWithoutExtension(f.FullName).Split('_');
+ var fileParts = _fileSystem.GetFileNameWithoutExtension(f).Split('_');
return fileParts.Length == 3 && string.Equals(title, fileParts[1], StringComparison.OrdinalIgnoreCase);
diff --git a/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs b/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs
index c12f74fa6..78d1fac47 100644
--- a/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs
+++ b/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs
@@ -1,4 +1,5 @@
using MediaBrowser.Common.Extensions;
+using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Localization;
using MediaBrowser.Controller.Providers;
@@ -13,10 +14,12 @@ namespace MediaBrowser.Providers.MediaInfo
public class SubtitleResolver
{
private readonly ILocalizationManager _localization;
+ private readonly IFileSystem _fileSystem;
- public SubtitleResolver(ILocalizationManager localization)
+ public SubtitleResolver(ILocalizationManager localization, IFileSystem fileSystem)
{
_localization = localization;
+ _fileSystem = fileSystem;
}
public IEnumerable<MediaStream> GetExternalSubtitleStreams(Video video,
@@ -24,17 +27,17 @@ namespace MediaBrowser.Providers.MediaInfo
IDirectoryService directoryService,
bool clearCache)
{
- var files = GetSubtitleFiles(video, directoryService, clearCache);
+ var files = GetSubtitleFiles(video, directoryService, _fileSystem, clearCache);
var streams = new List<MediaStream>();
- var videoFileNameWithoutExtension = Path.GetFileNameWithoutExtension(video.Path);
+ var videoFileNameWithoutExtension = _fileSystem.GetFileNameWithoutExtension(video.Path);
foreach (var file in files)
{
var fullName = file.FullName;
- var fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fullName);
+ var fileNameWithoutExtension = _fileSystem.GetFileNameWithoutExtension(file);
var codec = Path.GetExtension(fullName).ToLower().TrimStart('.');
@@ -96,7 +99,7 @@ namespace MediaBrowser.Providers.MediaInfo
}
}
- public static IEnumerable<FileSystemInfo> GetSubtitleFiles(Video video, IDirectoryService directoryService, bool clearCache)
+ public static IEnumerable<FileSystemInfo> GetSubtitleFiles(Video video, IDirectoryService directoryService, IFileSystem fileSystem, bool clearCache)
{
var containingPath = video.ContainingFolderPath;
@@ -107,16 +110,14 @@ namespace MediaBrowser.Providers.MediaInfo
var files = directoryService.GetFiles(containingPath, clearCache);
- var videoFileNameWithoutExtension = Path.GetFileNameWithoutExtension(video.Path);
+ var videoFileNameWithoutExtension = fileSystem.GetFileNameWithoutExtension(video.Path);
return files.Where(i =>
{
if (!i.Attributes.HasFlag(FileAttributes.Directory) &&
SubtitleExtensions.Contains(i.Extension, StringComparer.OrdinalIgnoreCase))
{
- var fullName = i.FullName;
-
- var fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fullName);
+ var fileNameWithoutExtension = fileSystem.GetFileNameWithoutExtension(i);
if (string.Equals(videoFileNameWithoutExtension, fileNameWithoutExtension, StringComparison.OrdinalIgnoreCase))
{
diff --git a/MediaBrowser.Providers/Movies/MovieDbProvider.cs b/MediaBrowser.Providers/Movies/MovieDbProvider.cs
index 65fe32a99..32d53db43 100644
--- a/MediaBrowser.Providers/Movies/MovieDbProvider.cs
+++ b/MediaBrowser.Providers/Movies/MovieDbProvider.cs
@@ -252,7 +252,7 @@ namespace MediaBrowser.Providers.Movies
var path = GetMovieDataPath(_configurationManager.ApplicationPaths, tmdbId);
var filename = string.Format("all-{0}.json",
- preferredLanguage ?? string.Empty);
+ preferredLanguage);
return Path.Combine(path, filename);
}
diff --git a/MediaBrowser.Providers/Subtitles/SubtitleManager.cs b/MediaBrowser.Providers/Subtitles/SubtitleManager.cs
index 08499a20b..34656f05b 100644
--- a/MediaBrowser.Providers/Subtitles/SubtitleManager.cs
+++ b/MediaBrowser.Providers/Subtitles/SubtitleManager.cs
@@ -105,7 +105,7 @@ namespace MediaBrowser.Providers.Subtitles
using (var stream = response.Stream)
{
var savePath = Path.Combine(Path.GetDirectoryName(video.Path),
- Path.GetFileNameWithoutExtension(video.Path) + "." + response.Language.ToLower());
+ _fileSystem.GetFileNameWithoutExtension(video.Path) + "." + response.Language.ToLower());
if (response.IsForced)
{
diff --git a/MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs b/MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs
index d44811886..594ba9d23 100644
--- a/MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs
+++ b/MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs
@@ -248,12 +248,12 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
.ToList();
var folder = Path.GetDirectoryName(targetPath);
- var targetFileNameWithoutExtension = Path.GetFileNameWithoutExtension(targetPath);
-
+ var targetFileNameWithoutExtension = _fileSystem.GetFileNameWithoutExtension(targetPath);
+
try
{
var filesOfOtherExtensions = Directory.EnumerateFiles(folder, "*", SearchOption.TopDirectoryOnly)
- .Where(i => EntityResolutionHelper.IsVideoFile(i) && string.Equals(Path.GetFileNameWithoutExtension(i), targetFileNameWithoutExtension, StringComparison.OrdinalIgnoreCase));
+ .Where(i => EntityResolutionHelper.IsVideoFile(i) && string.Equals(_fileSystem.GetFileNameWithoutExtension(i), targetFileNameWithoutExtension, StringComparison.OrdinalIgnoreCase));
episodePaths.AddRange(filesOfOtherExtensions);
}
diff --git a/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs b/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs
index d0ea07056..6e0b654fd 100644
--- a/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs
+++ b/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs
@@ -1,5 +1,4 @@
-using Amib.Threading;
-using Funq;
+using Funq;
using MediaBrowser.Common;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
@@ -15,7 +14,6 @@ using ServiceStack.Host.HttpListener;
using ServiceStack.Logging;
using ServiceStack.Web;
using System;
-using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -37,7 +35,6 @@ namespace MediaBrowser.Server.Implementations.HttpServer
private IHttpListener _listener;
- private readonly SmartThreadPool _threadPoolManager;
private const int IdleTimeout = 300;
private readonly ContainerAdapter _containerAdapter;
@@ -62,9 +59,6 @@ namespace MediaBrowser.Server.Implementations.HttpServer
_logger = logManager.GetLogger("HttpServer");
_containerAdapter = new ContainerAdapter(applicationHost);
-
- _threadPoolManager = new SmartThreadPool(IdleTimeout,
- maxWorkerThreads: Math.Max(16, Environment.ProcessorCount * 2));
}
public override void Configure(Container container)
@@ -158,8 +152,8 @@ namespace MediaBrowser.Server.Implementations.HttpServer
HostContext.Config.HandlerFactoryPath = ListenerRequest.GetHandlerPathIfAny(UrlPrefixes.First());
_listener = NativeWebSocket.IsSupported
- ? _listener = new HttpListenerServer(_logger, _threadPoolManager)
- : _listener = new WebSocketSharpListener(_logger, _threadPoolManager);
+ ? _listener = new HttpListenerServer(_logger)
+ : _listener = new WebSocketSharpListener(_logger);
_listener.WebSocketHandler = WebSocketHandler;
_listener.ErrorHandler = ErrorHandler;
@@ -356,8 +350,6 @@ namespace MediaBrowser.Server.Implementations.HttpServer
if (disposing)
{
- _threadPoolManager.Dispose();
-
Stop();
}
diff --git a/MediaBrowser.Server.Implementations/HttpServer/NetListener/HttpListenerServer.cs b/MediaBrowser.Server.Implementations/HttpServer/NetListener/HttpListenerServer.cs
index 7f766129e..12106c32e 100644
--- a/MediaBrowser.Server.Implementations/HttpServer/NetListener/HttpListenerServer.cs
+++ b/MediaBrowser.Server.Implementations/HttpServer/NetListener/HttpListenerServer.cs
@@ -1,6 +1,4 @@
-using System.Text;
-using Amib.Threading;
-using MediaBrowser.Common.Net;
+using MediaBrowser.Common.Net;
using MediaBrowser.Model.Logging;
using ServiceStack;
using ServiceStack.Host.HttpListener;
@@ -10,6 +8,7 @@ using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Net;
+using System.Text;
using System.Threading;
using System.Threading.Tasks;
@@ -20,7 +19,6 @@ namespace MediaBrowser.Server.Implementations.HttpServer.NetListener
private readonly ILogger _logger;
private HttpListener _listener;
private readonly AutoResetEvent _listenForNextRequest = new AutoResetEvent(false);
- private readonly SmartThreadPool _threadPoolManager;
public System.Action<Exception, IRequest> ErrorHandler { get; set; }
public Action<WebSocketConnectEventArgs> WebSocketHandler { get; set; }
@@ -28,11 +26,9 @@ namespace MediaBrowser.Server.Implementations.HttpServer.NetListener
private readonly ConcurrentDictionary<string, string> _localEndPoints = new ConcurrentDictionary<string, string>(StringComparer.OrdinalIgnoreCase);
- public HttpListenerServer(ILogger logger, SmartThreadPool threadPoolManager)
+ public HttpListenerServer(ILogger logger)
{
_logger = logger;
-
- _threadPoolManager = threadPoolManager;
}
/// <summary>
@@ -63,7 +59,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer.NetListener
_listener.Start();
- ThreadPool.QueueUserWorkItem(Listen);
+ Task.Factory.StartNew(Listen, TaskCreationOptions.LongRunning);
}
private bool IsListening
@@ -72,7 +68,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer.NetListener
}
// Loop here to begin processing of new requests.
- private void Listen(object state)
+ private void Listen()
{
while (IsListening)
{
@@ -130,7 +126,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer.NetListener
_listenForNextRequest.Set();
}
- _threadPoolManager.QueueWorkItem(() => InitTask(context));
+ Task.Factory.StartNew(() => InitTask(context));
}
private void InitTask(HttpListenerContext context)
@@ -287,8 +283,6 @@ namespace MediaBrowser.Server.Implementations.HttpServer.NetListener
if (disposing)
{
- _threadPoolManager.Dispose();
-
Stop();
}
diff --git a/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpListener.cs b/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpListener.cs
index cf756d9f2..b18d0df5e 100644
--- a/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpListener.cs
+++ b/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpListener.cs
@@ -1,14 +1,13 @@
-using System;
+using MediaBrowser.Common.Net;
+using MediaBrowser.Model.Logging;
+using ServiceStack;
+using ServiceStack.Web;
+using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
-using Amib.Threading;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Model.Logging;
-using ServiceStack;
-using ServiceStack.Web;
using WebSocketSharp.Net;
using WebSocketSharp.Server;
@@ -20,12 +19,10 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp
private WebSocketSharp.Server.HttpServer _httpsv;
private readonly ILogger _logger;
- private readonly SmartThreadPool _threadPoolManager;
- public WebSocketSharpListener(ILogger logger, SmartThreadPool threadPoolManager)
+ public WebSocketSharpListener(ILogger logger)
{
_logger = logger;
- _threadPoolManager = threadPoolManager;
}
public IEnumerable<string> LocalEndPoints
@@ -33,9 +30,9 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp
get { return _localEndPoints.Keys.ToList(); }
}
- public System.Action<Exception, IRequest> ErrorHandler { get; set; }
+ public Action<Exception, IRequest> ErrorHandler { get; set; }
- public System.Func<IHttpRequest, Uri, Task> RequestHandler { get; set; }
+ public Func<IHttpRequest, Uri, Task> RequestHandler { get; set; }
public Action<WebSocketConnectEventArgs> WebSocketHandler { get; set; }
@@ -50,7 +47,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp
void _httpsv_OnRequest(object sender, HttpRequestEventArgs e)
{
- _threadPoolManager.QueueWorkItem(() => InitTask(e.Context));
+ Task.Factory.StartNew(() => InitTask(e.Context));
}
private void InitTask(HttpListenerContext context)
diff --git a/MediaBrowser.Server.Implementations/Library/CoreResolutionIgnoreRule.cs b/MediaBrowser.Server.Implementations/Library/CoreResolutionIgnoreRule.cs
index 8229c8a2f..7b58dd7c4 100644
--- a/MediaBrowser.Server.Implementations/Library/CoreResolutionIgnoreRule.cs
+++ b/MediaBrowser.Server.Implementations/Library/CoreResolutionIgnoreRule.cs
@@ -90,7 +90,7 @@ namespace MediaBrowser.Server.Implementations.Library
if (args.Parent != null)
{
// Don't resolve these into audio files
- if (string.Equals(Path.GetFileNameWithoutExtension(filename), BaseItem.ThemeSongFilename) && EntityResolutionHelper.IsAudioFile(filename))
+ if (string.Equals(_fileSystem.GetFileNameWithoutExtension(filename), BaseItem.ThemeSongFilename) && EntityResolutionHelper.IsAudioFile(filename))
{
return true;
}
diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs
index 64e0a6662..8310895e6 100644
--- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs
+++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs
@@ -1157,7 +1157,7 @@ namespace MediaBrowser.Server.Implementations.Library
private string GetCollectionType(string path)
{
return new DirectoryInfo(path).EnumerateFiles("*.collection", SearchOption.TopDirectoryOnly)
- .Select(i => Path.GetFileNameWithoutExtension(i.FullName))
+ .Select(i => _fileSystem.GetFileNameWithoutExtension(i))
.FirstOrDefault();
}
@@ -1486,23 +1486,22 @@ namespace MediaBrowser.Server.Implementations.Library
public async Task<UserView> GetNamedView(string name, string type, string sortName, CancellationToken cancellationToken)
{
- var id = "namedview_3_" + name;
- var guid = id.GetMD5();
+ var path = Path.Combine(ConfigurationManager.ApplicationPaths.ItemsByNamePath,
+ "views",
+ _fileSystem.GetValidFilename(type));
- var item = GetItemById(guid) as UserView;
+ var id = (path + "_namedview_" + name).GetMBId(typeof(UserView));
+
+ var item = GetItemById(id) as UserView;
if (item == null)
{
- var path = Path.Combine(ConfigurationManager.ApplicationPaths.ItemsByNamePath,
- "views",
- _fileSystem.GetValidFilename(type));
-
Directory.CreateDirectory(Path.GetDirectoryName(path));
item = new UserView
{
Path = path,
- Id = guid,
+ Id = id,
DateCreated = DateTime.UtcNow,
Name = name,
ViewType = type,
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs
index 64d2db2e4..2ee3d49a4 100644
--- a/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs
+++ b/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs
@@ -1,4 +1,5 @@
-using MediaBrowser.Controller.Entities;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV;
@@ -6,6 +7,7 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Resolvers;
using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Logging;
using System;
using System.Collections.Generic;
using System.IO;
@@ -17,6 +19,15 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
/// </summary>
public class MusicAlbumResolver : ItemResolver<MusicAlbum>
{
+ private readonly ILogger _logger;
+ private readonly IFileSystem _fileSystem;
+
+ public MusicAlbumResolver(ILogger logger, IFileSystem fileSystem)
+ {
+ _logger = logger;
+ _fileSystem = fileSystem;
+ }
+
/// <summary>
/// Gets the priority.
/// </summary>
@@ -64,10 +75,12 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
/// </summary>
/// <param name="path">The path.</param>
/// <param name="directoryService">The directory service.</param>
+ /// <param name="logger">The logger.</param>
+ /// <param name="fileSystem">The file system.</param>
/// <returns><c>true</c> if [is music album] [the specified data]; otherwise, <c>false</c>.</returns>
- public static bool IsMusicAlbum(string path, IDirectoryService directoryService)
+ public static bool IsMusicAlbum(string path, IDirectoryService directoryService, ILogger logger, IFileSystem fileSystem)
{
- return ContainsMusic(directoryService.GetFileSystemEntries(path), true, directoryService);
+ return ContainsMusic(directoryService.GetFileSystemEntries(path), true, directoryService, logger, fileSystem);
}
/// <summary>
@@ -75,13 +88,13 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
/// </summary>
/// <param name="args">The args.</param>
/// <returns><c>true</c> if [is music album] [the specified args]; otherwise, <c>false</c>.</returns>
- public static bool IsMusicAlbum(ItemResolveArgs args)
+ private bool IsMusicAlbum(ItemResolveArgs args)
{
// Args points to an album if parent is an Artist folder or it directly contains music
if (args.IsDirectory)
{
//if (args.Parent is MusicArtist) return true; //saves us from testing children twice
- if (ContainsMusic(args.FileSystemChildren, true, args.DirectoryService)) return true;
+ if (ContainsMusic(args.FileSystemChildren, true, args.DirectoryService, _logger, _fileSystem)) return true;
}
return false;
@@ -93,19 +106,23 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
/// <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>
+ /// <param name="logger">The logger.</param>
+ /// <param name="fileSystem">The file system.</param>
/// <returns><c>true</c> if the specified list contains music; otherwise, <c>false</c>.</returns>
- private static bool ContainsMusic(IEnumerable<FileSystemInfo> list, bool allowSubfolders, IDirectoryService directoryService)
+ private static bool ContainsMusic(IEnumerable<FileSystemInfo> list, bool allowSubfolders, IDirectoryService directoryService, ILogger logger, IFileSystem fileSystem)
{
// If list contains at least 2 audio files or at least one and no video files consider it to contain music
var foundAudio = 0;
+ var discSubfolderCount = 0;
+
foreach (var fileSystemInfo in list)
{
if ((fileSystemInfo.Attributes & FileAttributes.Directory) == FileAttributes.Directory)
{
- if (allowSubfolders && IsAlbumSubfolder(fileSystemInfo, directoryService))
+ if (allowSubfolders && IsAlbumSubfolder(fileSystemInfo, directoryService, logger, fileSystem))
{
- return true;
+ discSubfolderCount++;
}
if (!IsAdditionalSubfolderAllowed(fileSystemInfo))
{
@@ -118,7 +135,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
if (EntityResolutionHelper.IsAudioFile(fullName))
{
// Don't resolve these into audio files
- if (string.Equals(Path.GetFileNameWithoutExtension(fullName), BaseItem.ThemeSongFilename) && EntityResolutionHelper.IsAudioFile(fullName))
+ if (string.Equals(fileSystem.GetFileNameWithoutExtension(fullName), BaseItem.ThemeSongFilename) && EntityResolutionHelper.IsAudioFile(fullName))
{
continue;
}
@@ -135,16 +152,18 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
}
// or a single audio file and no video files
- return foundAudio > 0;
+ return foundAudio > 0 || discSubfolderCount > 0;
}
- private static bool IsAlbumSubfolder(FileSystemInfo directory, IDirectoryService directoryService)
+ private static bool IsAlbumSubfolder(FileSystemInfo directory, IDirectoryService directoryService, ILogger logger, IFileSystem fileSystem)
{
var path = directory.FullName;
if (IsMultiDiscFolder(path))
{
- return ContainsMusic(directoryService.GetFileSystemEntries(path), false, directoryService);
+ logger.Debug("Found multi-disc folder: " + path);
+
+ return ContainsMusic(directoryService.GetFileSystemEntries(path), false, directoryService, logger, fileSystem);
}
return false;
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs
index 4fa97fc9d..19a284d15 100644
--- a/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs
+++ b/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs
@@ -1,9 +1,11 @@
-using MediaBrowser.Controller.Entities.Audio;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Resolvers;
using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Logging;
using System;
using System.IO;
using System.Linq;
@@ -15,6 +17,15 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
/// </summary>
public class MusicArtistResolver : ItemResolver<MusicArtist>
{
+ private readonly ILogger _logger;
+ private readonly IFileSystem _fileSystem;
+
+ public MusicArtistResolver(ILogger logger, IFileSystem fileSystem)
+ {
+ _logger = logger;
+ _fileSystem = fileSystem;
+ }
+
/// <summary>
/// Gets the priority.
/// </summary>
@@ -61,7 +72,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
var directoryService = args.DirectoryService;
// If we contain an album assume we are an artist folder
- return args.FileSystemChildren.Where(i => (i.Attributes & FileAttributes.Directory) == FileAttributes.Directory).Any(i => MusicAlbumResolver.IsMusicAlbum(i.FullName, directoryService)) ? new MusicArtist() : null;
+ return args.FileSystemChildren.Where(i => (i.Attributes & FileAttributes.Directory) == FileAttributes.Directory).Any(i => MusicAlbumResolver.IsMusicAlbum(i.FullName, directoryService, _logger, _fileSystem)) ? new MusicArtist() : null;
}
}
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/FolderResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/FolderResolver.cs
index 594277ef7..166465f72 100644
--- a/MediaBrowser.Server.Implementations/Library/Resolvers/FolderResolver.cs
+++ b/MediaBrowser.Server.Implementations/Library/Resolvers/FolderResolver.cs
@@ -1,4 +1,5 @@
-using MediaBrowser.Controller.Entities;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Resolvers;
using System;
@@ -12,6 +13,13 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers
/// </summary>
public class FolderResolver : FolderResolver<Folder>
{
+ private readonly IFileSystem _fileSystem;
+
+ public FolderResolver(IFileSystem fileSystem)
+ {
+ _fileSystem = fileSystem;
+ }
+
/// <summary>
/// Gets the priority.
/// </summary>
@@ -69,7 +77,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers
}
})
- .Select(i => Path.GetFileNameWithoutExtension(i.FullName))
+ .Select(i => _fileSystem.GetFileNameWithoutExtension(i))
.FirstOrDefault();
}
}
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/LocalTrailerResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/LocalTrailerResolver.cs
index 10ee3586d..b483f7c42 100644
--- a/MediaBrowser.Server.Implementations/Library/Resolvers/LocalTrailerResolver.cs
+++ b/MediaBrowser.Server.Implementations/Library/Resolvers/LocalTrailerResolver.cs
@@ -1,4 +1,5 @@
-using MediaBrowser.Controller.Entities;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Resolvers;
using System;
@@ -11,6 +12,13 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers
/// </summary>
public class LocalTrailerResolver : BaseVideoResolver<Trailer>
{
+ private readonly IFileSystem _fileSystem;
+
+ public LocalTrailerResolver(IFileSystem fileSystem)
+ {
+ _fileSystem = fileSystem;
+ }
+
/// <summary>
/// Resolves the specified args.
/// </summary>
@@ -33,7 +41,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers
}
// Support xbmc local trailer convention, but only when looking for local trailers (hence the parent == null check)
- if (args.Parent == null && Path.GetFileNameWithoutExtension(args.Path).EndsWith(BaseItem.XbmcTrailerFileSuffix, StringComparison.OrdinalIgnoreCase))
+ if (args.Parent == null && _fileSystem.GetFileNameWithoutExtension(args.Path).EndsWith(BaseItem.XbmcTrailerFileSuffix, StringComparison.OrdinalIgnoreCase))
{
return base.Resolve(args);
}
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
index f3e6fcdba..215cff22f 100644
--- a/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
+++ b/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
@@ -1,4 +1,5 @@
using MediaBrowser.Common.Extensions;
+using MediaBrowser.Common.IO;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Movies;
@@ -22,12 +23,14 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
private readonly IServerApplicationPaths _applicationPaths;
private readonly ILibraryManager _libraryManager;
private readonly ILogger _logger;
+ private readonly IFileSystem _fileSystem;
- public MovieResolver(IServerApplicationPaths appPaths, ILibraryManager libraryManager, ILogger logger)
+ public MovieResolver(IServerApplicationPaths appPaths, ILibraryManager libraryManager, ILogger logger, IFileSystem fileSystem)
{
_applicationPaths = appPaths;
_libraryManager = libraryManager;
_logger = logger;
+ _fileSystem = fileSystem;
}
/// <summary>
@@ -407,7 +410,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
if (!string.IsNullOrWhiteSpace(filenamePrefix))
{
- if (sortedMovies.All(i => Path.GetFileNameWithoutExtension(i.Path).StartsWith(filenamePrefix + " - ", StringComparison.OrdinalIgnoreCase)))
+ if (sortedMovies.All(i => _fileSystem.GetFileNameWithoutExtension(i.Path).StartsWith(filenamePrefix + " - ", StringComparison.OrdinalIgnoreCase)))
{
firstMovie.HasLocalAlternateVersions = true;
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs
index a756dc794..519d775de 100644
--- a/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs
+++ b/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs
@@ -1,4 +1,5 @@
using MediaBrowser.Common.Extensions;
+using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
@@ -14,6 +15,13 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV
/// </summary>
public class SeriesResolver : FolderResolver<Series>
{
+ private readonly IFileSystem _fileSystem;
+
+ public SeriesResolver(IFileSystem fileSystem)
+ {
+ _fileSystem = fileSystem;
+ }
+
/// <summary>
/// Gets the priority.
/// </summary>
@@ -67,8 +75,8 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV
{
return null;
}
-
- if (TVUtils.IsSeriesFolder(args.Path, collectionType == CollectionType.TvShows, args.FileSystemChildren, args.DirectoryService))
+
+ if (TVUtils.IsSeriesFolder(args.Path, collectionType == CollectionType.TvShows, args.FileSystemChildren, args.DirectoryService, _fileSystem))
{
return new Series();
}
diff --git a/MediaBrowser.Server.Implementations/Library/UserManager.cs b/MediaBrowser.Server.Implementations/Library/UserManager.cs
index dbe020908..43c9f3b9c 100644
--- a/MediaBrowser.Server.Implementations/Library/UserManager.cs
+++ b/MediaBrowser.Server.Implementations/Library/UserManager.cs
@@ -282,7 +282,7 @@ namespace MediaBrowser.Server.Implementations.Library
/// <exception cref="System.ArgumentException"></exception>
public async Task<User> CreateUser(string name)
{
- if (string.IsNullOrEmpty(name))
+ if (string.IsNullOrWhiteSpace(name))
{
throw new ArgumentNullException("name");
}
diff --git a/MediaBrowser.Server.Implementations/Localization/JavaScript/es_MX.json b/MediaBrowser.Server.Implementations/Localization/JavaScript/es_MX.json
index 53047104c..d9855c52f 100644
--- a/MediaBrowser.Server.Implementations/Localization/JavaScript/es_MX.json
+++ b/MediaBrowser.Server.Implementations/Localization/JavaScript/es_MX.json
@@ -314,10 +314,10 @@
"ButtonSubtitles": "Subt\u00edtulos",
"ButtonScenes": "Escenas",
"ButtonQuality": "Calidad",
- "HeaderNotifications": "Notifications",
- "HeaderSelectPlayer": "Select Player:",
+ "HeaderNotifications": "Notificaciones",
+ "HeaderSelectPlayer": "Seleccionar Reproductor:",
"ButtonSelect": "Seleccionar",
"ButtonNew": "Nuevo",
- "MessageInternetExplorerWebm": "For best results with Internet Explorer please install the WebM plugin for IE.",
- "HeaderVideoError": "Video Error"
+ "MessageInternetExplorerWebm": "Para mejores resultados con Internet Explorer por favor instale el complemento WebM para IE.",
+ "HeaderVideoError": "Error de Video"
} \ 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 6bcb5aed5..5086dcfac 100644
--- a/MediaBrowser.Server.Implementations/Localization/JavaScript/fr.json
+++ b/MediaBrowser.Server.Implementations/Localization/JavaScript/fr.json
@@ -77,7 +77,7 @@
"MessageInvalidUser": "Nom d'utilisateur et mot de passe invalides.",
"HeaderAllRecordings": "Tous les enregistrements",
"RecommendationBecauseYouLike": "Parce-que vous aimez {0}",
- "RecommendationBecauseYouWatched": "Parece-que vous avez regard\u00e9 {0}",
+ "RecommendationBecauseYouWatched": "Parce que vous avez regard\u00e9 {0}",
"RecommendationDirectedBy": "R\u00e9alis\u00e9 par {0}",
"RecommendationStarring": "Mettant en vedette {0}",
"HeaderConfirmRecordingCancellation": "Confirmer l'annulation de l'enregistrement.",
diff --git a/MediaBrowser.Server.Implementations/Localization/JavaScript/nl.json b/MediaBrowser.Server.Implementations/Localization/JavaScript/nl.json
index 191a89000..37cff9888 100644
--- a/MediaBrowser.Server.Implementations/Localization/JavaScript/nl.json
+++ b/MediaBrowser.Server.Implementations/Localization/JavaScript/nl.json
@@ -20,11 +20,11 @@
"OptionRelease": "Offici\u00eble Release",
"OptionBeta": "Beta",
"OptionDev": "Dev (Instabiel)",
- "UninstallPluginHeader": "Invoegtoepassing de\u00efnstalleren",
+ "UninstallPluginHeader": "Plug-in de\u00efnstalleren",
"UninstallPluginConfirmation": "Weet u zeker dat u {0} wilt de\u00efnstalleren?",
- "NoPluginConfigurationMessage": "Deze Invoegtoepassing heeft niets in te stellen",
- "NoPluginsInstalledMessage": "U heeft geen Invoegtoepassingen ge\u00efnstalleerd",
- "BrowsePluginCatalogMessage": "Bekijk de Invoegtoepassings catalogus voor beschikbare Invoegtoepassingen.",
+ "NoPluginConfigurationMessage": "Deze Plug-in heeft niets in te stellen",
+ "NoPluginsInstalledMessage": "U heeft geen Plug-in ge\u00efnstalleerd",
+ "BrowsePluginCatalogMessage": "Bekijk de Plug-in catalogus voor beschikbare Plug-ins.",
"MessageKeyEmailedTo": "Sleutel gemaild naar {0}.",
"MessageKeysLinked": "Sleutels gekoppeld.",
"HeaderConfirmation": "Bevestiging",
@@ -45,7 +45,7 @@
"HeaderDeleteTaskTrigger": "Verwijderen Taak Trigger",
"HeaderTaskTriggers": "Taak Triggers",
"MessageDeleteTaskTrigger": "Weet u zeker dat u deze taak trigger wilt verwijderen?",
- "MessageNoPluginsInstalled": "U heeft geen Invoegtoepassingen ge\u00efnstalleerd.",
+ "MessageNoPluginsInstalled": "U heeft geen Plug-ins ge\u00efnstalleerd.",
"LabelVersionInstalled": "{0} ge\u00efnstalleerd",
"LabelNumberReviews": "{0} Recensies",
"LabelFree": "Gratis",
@@ -305,7 +305,7 @@
"TabDLNA": "DLNA",
"TabLiveTV": "Live TV",
"TabAutoOrganize": "Automatisch-Organiseren",
- "TabPlugins": "Invoegtoepassingen",
+ "TabPlugins": "Plug-ins",
"TabAdvanced": "Geavanceerd",
"TabHelp": "Hulp",
"TabScheduledTasks": "Geplande taken",
@@ -318,6 +318,6 @@
"HeaderSelectPlayer": "Selecteer Speler:",
"ButtonSelect": "Selecteer",
"ButtonNew": "Nieuw",
- "MessageInternetExplorerWebm": "For best results with Internet Explorer please install the WebM plugin for IE.",
+ "MessageInternetExplorerWebm": "Voor het beste resultaat met Internet Explorer installeert u de WebM plugin voor IE.",
"HeaderVideoError": "Video Fout"
} \ 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 51559053d..622ad2c05 100644
--- a/MediaBrowser.Server.Implementations/Localization/JavaScript/pt_BR.json
+++ b/MediaBrowser.Server.Implementations/Localization/JavaScript/pt_BR.json
@@ -318,6 +318,6 @@
"HeaderSelectPlayer": "Selecione onde executar:",
"ButtonSelect": "Selecionar",
"ButtonNew": "Nova",
- "MessageInternetExplorerWebm": "For best results with Internet Explorer please install the WebM plugin for IE.",
- "HeaderVideoError": "Video Error"
+ "MessageInternetExplorerWebm": "Para melhores resultados com o Internet Explorer, por favor instale o plugin WebM para IE.",
+ "HeaderVideoError": "Erro de V\u00eddeo"
} \ 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 2fa7f2ea7..80bdc43f7 100644
--- a/MediaBrowser.Server.Implementations/Localization/JavaScript/ru.json
+++ b/MediaBrowser.Server.Implementations/Localization/JavaScript/ru.json
@@ -60,7 +60,7 @@
"ButtonStop": "\u041e\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c",
"ButtonNextTrack": "\u0421\u043b\u0435\u0434\u0443\u044e\u0449\u0430\u044f \u0434\u043e\u0440\u043e\u0436\u043a\u0430",
"ButtonPause": "\u041f\u0430\u0443\u0437\u0430",
- "ButtonPlay": "\u0412\u043e\u0441\u043f\u0440",
+ "ButtonPlay": "\u0412\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0441\u0442\u0438",
"ButtonEdit": "\u041f\u0440\u0430\u0432\u0438\u0442\u044c",
"ButtonQueue": "\u041e\u0447\u0435\u0440\u0435\u0434\u044c",
"ButtonPlayTrailer": "\u0412\u043e\u0441\u043f\u0440 \u0442\u0440\u0435\u0439\u043b\u0435\u0440",
@@ -276,7 +276,7 @@
"OptionParentalRating": "\u0412\u043e\u0437\u0440\u0430\u0441\u0442\u043d\u0430\u044f \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u044f",
"OptionPeople": "\u041b\u044e\u0434\u0438",
"OptionRuntime": "\u0414\u043b\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c",
- "OptionProductionLocations": "\u041c\u0435\u0441\u0442\u0430 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0441\u0442\u0432\u0430",
+ "OptionProductionLocations": "\u041c\u0435\u0441\u0442\u0430 \u0441\u044a\u0451\u043c\u043e\u043a",
"OptionBirthLocation": "\u041c\u0435\u0441\u0442\u043e \u0440\u043e\u0436\u0434\u0435\u043d\u0438\u044f",
"LabelAllChannels": "\u0412\u0441\u0435 \u043a\u0430\u043d\u0430\u043b\u044b",
"LabelLiveProgram": "\u041f\u0420\u042f\u041c\u041e\u0419 \u042d\u0424\u0418\u0420",
diff --git a/MediaBrowser.Server.Implementations/Localization/LocalizationManager.cs b/MediaBrowser.Server.Implementations/Localization/LocalizationManager.cs
index 05de4d65a..1f993f0d4 100644
--- a/MediaBrowser.Server.Implementations/Localization/LocalizationManager.cs
+++ b/MediaBrowser.Server.Implementations/Localization/LocalizationManager.cs
@@ -235,7 +235,9 @@ namespace MediaBrowser.Server.Implementations.Localization
.Where(i => i != null)
.ToDictionary(i => i.Name, StringComparer.OrdinalIgnoreCase);
- var countryCode = Path.GetFileNameWithoutExtension(file).Split('-').Last();
+ var countryCode = _fileSystem.GetFileNameWithoutExtension(file)
+ .Split('-')
+ .Last();
_allParentalRatings.TryAdd(countryCode, dict);
}
diff --git a/MediaBrowser.Server.Implementations/Localization/Server/ar.json b/MediaBrowser.Server.Implementations/Localization/Server/ar.json
index 81922f713..667245839 100644
--- a/MediaBrowser.Server.Implementations/Localization/Server/ar.json
+++ b/MediaBrowser.Server.Implementations/Localization/Server/ar.json
@@ -767,10 +767,10 @@
"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:",
+ "LabelHomePageSection1": "Home page section 1:",
+ "LabelHomePageSection2": "Home page section 2:",
+ "LabelHomePageSection3": "Home page section 3:",
+ "LabelHomePageSection4": "Home page section 4:",
"OptionMyViewsButtons": "My views (buttons)",
"OptionMyViews": "My views",
"OptionMyViewsSmall": "My views (small)",
@@ -888,5 +888,7 @@
"ButtonSync": "Sync",
"TabScheduledTasks": "Scheduled Tasks",
"HeaderChapters": "Chapters",
- "HeaderResumeSettings": "Resume Settings"
+ "HeaderResumeSettings": "Resume Settings",
+ "TabSync": "Sync",
+ "TitleUsers": "Users"
} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/Localization/Server/ca.json b/MediaBrowser.Server.Implementations/Localization/Server/ca.json
index a56ccf4de..bcca3aeb9 100644
--- a/MediaBrowser.Server.Implementations/Localization/Server/ca.json
+++ b/MediaBrowser.Server.Implementations/Localization/Server/ca.json
@@ -767,10 +767,10 @@
"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:",
+ "LabelHomePageSection1": "Home page section 1:",
+ "LabelHomePageSection2": "Home page section 2:",
+ "LabelHomePageSection3": "Home page section 3:",
+ "LabelHomePageSection4": "Home page section 4:",
"OptionMyViewsButtons": "My views (buttons)",
"OptionMyViews": "My views",
"OptionMyViewsSmall": "My views (small)",
@@ -888,5 +888,7 @@
"ButtonSync": "Sync",
"TabScheduledTasks": "Scheduled Tasks",
"HeaderChapters": "Chapters",
- "HeaderResumeSettings": "Resume Settings"
+ "HeaderResumeSettings": "Resume Settings",
+ "TabSync": "Sync",
+ "TitleUsers": "Users"
} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/Localization/Server/cs.json b/MediaBrowser.Server.Implementations/Localization/Server/cs.json
index 729768b78..71ca6fc49 100644
--- a/MediaBrowser.Server.Implementations/Localization/Server/cs.json
+++ b/MediaBrowser.Server.Implementations/Localization/Server/cs.json
@@ -767,10 +767,10 @@
"OptionAuto": "Auto",
"OptionYes": "Ano",
"OptionNo": "Ne",
- "LabelHomePageSection1": "Home page section one:",
- "LabelHomePageSection2": "Home page section two:",
- "LabelHomePageSection3": "Home page section three:",
- "LabelHomePageSection4": "Home page section four:",
+ "LabelHomePageSection1": "Home page section 1:",
+ "LabelHomePageSection2": "Home page section 2:",
+ "LabelHomePageSection3": "Home page section 3:",
+ "LabelHomePageSection4": "Home page section 4:",
"OptionMyViewsButtons": "My views (buttons)",
"OptionMyViews": "My views",
"OptionMyViewsSmall": "My views (small)",
@@ -888,5 +888,7 @@
"ButtonSync": "Sync",
"TabScheduledTasks": "Scheduled Tasks",
"HeaderChapters": "Chapters",
- "HeaderResumeSettings": "Resume Settings"
+ "HeaderResumeSettings": "Resume Settings",
+ "TabSync": "Sync",
+ "TitleUsers": "Users"
} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/Localization/Server/da.json b/MediaBrowser.Server.Implementations/Localization/Server/da.json
index c2fafba45..7a5bd7632 100644
--- a/MediaBrowser.Server.Implementations/Localization/Server/da.json
+++ b/MediaBrowser.Server.Implementations/Localization/Server/da.json
@@ -767,10 +767,10 @@
"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:",
+ "LabelHomePageSection1": "Home page section 1:",
+ "LabelHomePageSection2": "Home page section 2:",
+ "LabelHomePageSection3": "Home page section 3:",
+ "LabelHomePageSection4": "Home page section 4:",
"OptionMyViewsButtons": "My views (buttons)",
"OptionMyViews": "My views",
"OptionMyViewsSmall": "My views (small)",
@@ -888,5 +888,7 @@
"ButtonSync": "Sync",
"TabScheduledTasks": "Scheduled Tasks",
"HeaderChapters": "Chapters",
- "HeaderResumeSettings": "Resume Settings"
+ "HeaderResumeSettings": "Resume Settings",
+ "TabSync": "Sync",
+ "TitleUsers": "Users"
} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/Localization/Server/de.json b/MediaBrowser.Server.Implementations/Localization/Server/de.json
index 5ffff96dd..ea1b4a1bc 100644
--- a/MediaBrowser.Server.Implementations/Localization/Server/de.json
+++ b/MediaBrowser.Server.Implementations/Localization/Server/de.json
@@ -767,10 +767,10 @@
"OptionAuto": "Auto",
"OptionYes": "Ja",
"OptionNo": "Nein",
- "LabelHomePageSection1": "Home page section one:",
- "LabelHomePageSection2": "Home page section two:",
- "LabelHomePageSection3": "Home page section three:",
- "LabelHomePageSection4": "Home page section four:",
+ "LabelHomePageSection1": "Home page section 1:",
+ "LabelHomePageSection2": "Home page section 2:",
+ "LabelHomePageSection3": "Home page section 3:",
+ "LabelHomePageSection4": "Home page section 4:",
"OptionMyViewsButtons": "My views (buttons)",
"OptionMyViews": "My views",
"OptionMyViewsSmall": "My views (small)",
@@ -888,5 +888,7 @@
"ButtonSync": "Sync",
"TabScheduledTasks": "Scheduled Tasks",
"HeaderChapters": "Chapters",
- "HeaderResumeSettings": "Resume Settings"
+ "HeaderResumeSettings": "Resume Settings",
+ "TabSync": "Sync",
+ "TitleUsers": "Users"
} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/Localization/Server/el.json b/MediaBrowser.Server.Implementations/Localization/Server/el.json
index fe7451fed..2edf37326 100644
--- a/MediaBrowser.Server.Implementations/Localization/Server/el.json
+++ b/MediaBrowser.Server.Implementations/Localization/Server/el.json
@@ -767,10 +767,10 @@
"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:",
+ "LabelHomePageSection1": "Home page section 1:",
+ "LabelHomePageSection2": "Home page section 2:",
+ "LabelHomePageSection3": "Home page section 3:",
+ "LabelHomePageSection4": "Home page section 4:",
"OptionMyViewsButtons": "My views (buttons)",
"OptionMyViews": "My views",
"OptionMyViewsSmall": "My views (small)",
@@ -888,5 +888,7 @@
"ButtonSync": "Sync",
"TabScheduledTasks": "Scheduled Tasks",
"HeaderChapters": "Chapters",
- "HeaderResumeSettings": "Resume Settings"
+ "HeaderResumeSettings": "Resume Settings",
+ "TabSync": "Sync",
+ "TitleUsers": "Users"
} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/Localization/Server/en_GB.json b/MediaBrowser.Server.Implementations/Localization/Server/en_GB.json
index 5044e6d95..7ff88733a 100644
--- a/MediaBrowser.Server.Implementations/Localization/Server/en_GB.json
+++ b/MediaBrowser.Server.Implementations/Localization/Server/en_GB.json
@@ -767,10 +767,10 @@
"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:",
+ "LabelHomePageSection1": "Home page section 1:",
+ "LabelHomePageSection2": "Home page section 2:",
+ "LabelHomePageSection3": "Home page section 3:",
+ "LabelHomePageSection4": "Home page section 4:",
"OptionMyViewsButtons": "My views (buttons)",
"OptionMyViews": "My views",
"OptionMyViewsSmall": "My views (small)",
@@ -888,5 +888,7 @@
"ButtonSync": "Sync",
"TabScheduledTasks": "Scheduled Tasks",
"HeaderChapters": "Chapters",
- "HeaderResumeSettings": "Resume Settings"
+ "HeaderResumeSettings": "Resume Settings",
+ "TabSync": "Sync",
+ "TitleUsers": "Users"
} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/Localization/Server/en_US.json b/MediaBrowser.Server.Implementations/Localization/Server/en_US.json
index dc4ac80cb..930401e5c 100644
--- a/MediaBrowser.Server.Implementations/Localization/Server/en_US.json
+++ b/MediaBrowser.Server.Implementations/Localization/Server/en_US.json
@@ -767,10 +767,10 @@
"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:",
+ "LabelHomePageSection1": "Home page section 1:",
+ "LabelHomePageSection2": "Home page section 2:",
+ "LabelHomePageSection3": "Home page section 3:",
+ "LabelHomePageSection4": "Home page section 4:",
"OptionMyViewsButtons": "My views (buttons)",
"OptionMyViews": "My views",
"OptionMyViewsSmall": "My views (small)",
@@ -888,5 +888,7 @@
"ButtonSync": "Sync",
"TabScheduledTasks": "Scheduled Tasks",
"HeaderChapters": "Chapters",
- "HeaderResumeSettings": "Resume Settings"
+ "HeaderResumeSettings": "Resume Settings",
+ "TabSync": "Sync",
+ "TitleUsers": "Users"
} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/Localization/Server/es.json b/MediaBrowser.Server.Implementations/Localization/Server/es.json
index 9c43a9c9e..94c6c9ec4 100644
--- a/MediaBrowser.Server.Implementations/Localization/Server/es.json
+++ b/MediaBrowser.Server.Implementations/Localization/Server/es.json
@@ -888,5 +888,7 @@
"ButtonSync": "Sync",
"TabScheduledTasks": "Scheduled Tasks",
"HeaderChapters": "Chapters",
- "HeaderResumeSettings": "Resume Settings"
+ "HeaderResumeSettings": "Resume Settings",
+ "TabSync": "Sync",
+ "TitleUsers": "Users"
} \ 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 98a36f8dd..f1a323e6d 100644
--- a/MediaBrowser.Server.Implementations/Localization/Server/es_MX.json
+++ b/MediaBrowser.Server.Implementations/Localization/Server/es_MX.json
@@ -190,7 +190,7 @@
"HeaderStatus": "Estado",
"OptionContinuing": "Continuando",
"OptionEnded": "Finalizado",
- "HeaderAirDays": "D\u00edas de Emisi\u00f3n:",
+ "HeaderAirDays": "D\u00edas de Emisi\u00f3n",
"OptionSunday": "Domingo",
"OptionMonday": "Lunes",
"OptionTuesday": "Martes",
@@ -198,8 +198,8 @@
"OptionThursday": "Jueves",
"OptionFriday": "Viernes",
"OptionSaturday": "S\u00e1bado",
- "HeaderManagement": "Administraci\u00f3n:",
- "LabelManagement": "Management:",
+ "HeaderManagement": "Administraci\u00f3n",
+ "LabelManagement": "Administraci\u00f3n:",
"OptionMissingImdbId": "Falta Id de IMDb",
"OptionMissingTvdbId": "Falta Id de TheTVDB",
"OptionMissingOverview": "Falta Sinopsis",
@@ -257,11 +257,11 @@
"ButtonSelectDirectory": "Seleccionar Carpeta",
"LabelCustomPaths": "Especificar rutas personalizadas cuando se desee. Deje los campos vac\u00edos para usar los valores predeterminados.",
"LabelCachePath": "Ruta para el Cach\u00e9:",
- "LabelCachePathHelp": "Esta carpeta contiene los archivos de cach\u00e9 del servidor, por ejemplo im\u00e1genes.",
+ "LabelCachePathHelp": "Especifique una ubicaci\u00f3n personalizada para los archivos de cach\u00e9 del servidor, tales como im\u00e1genes.",
"LabelImagesByNamePath": "Ruta para Im\u00e1genes por nombre:",
- "LabelImagesByNamePathHelp": "Esta carpeta contiene im\u00e1genes descargadas de actores, artistas, g\u00e9neros y estudios.",
+ "LabelImagesByNamePathHelp": "Especifique una ubicaci\u00f3n personalizada para im\u00e1genes descargadas de actores, artistas, g\u00e9neros y estudios.",
"LabelMetadataPath": "Ruta para metadatos:",
- "LabelMetadataPathHelp": "Esta ubicaci\u00f3n contiene ilustraciones descargadas y metadatos cuando no han sido configurados para almacenarse en carpetas de medios.",
+ "LabelMetadataPathHelp": "Especifique una ubicaci\u00f3n personalizada para ilustraciones descargadas y metadatos cuando no han sido configurados para almacenarse en carpetas de medios.",
"LabelTranscodingTempPath": "Ruta para transcodificaci\u00f3n temporal:",
"LabelTranscodingTempPathHelp": "Esta carpeta contiene archivos de trabajo usados por el transcodificador. Especifique una trayectoria personalizada, o d\u00e9jela vac\u00eda para utilizar su valor por omisi\u00f3n en la carpeta de datos del servidor.",
"TabBasics": "B\u00e1sicos",
@@ -627,10 +627,10 @@
"TabNowPlaying": "Reproduci\u00e9ndo Ahora",
"TabNavigation": "Navegaci\u00f3n",
"TabControls": "Controles",
- "ButtonFullscreen": "Toggle fullscreen",
+ "ButtonFullscreen": "Cambiar a pantalla completa",
"ButtonScenes": "Escenas",
"ButtonSubtitles": "Subt\u00edtulos",
- "ButtonAudioTracks": "Audio tracks",
+ "ButtonAudioTracks": "Pistas de audio",
"ButtonPreviousTrack": "Pista anterior",
"ButtonNextTrack": "Pista siguiente",
"ButtonStop": "Detener",
@@ -880,13 +880,13 @@
"TabSort": "Ordenaci\u00f3n",
"TabFilter": "Filtro",
"ButtonView": "Vista",
- "LabelPageSize": "Tama\u00f1o de Pantalla:",
+ "LabelPageSize": "Cantidad de \u00cdtems:",
"LabelView": "Vista:",
"TabUsers": "Usuarios",
- "HeaderFeatures": "Features",
- "HeaderAdvanced": "Advanced",
- "ButtonSync": "Sync",
+ "HeaderFeatures": "Caracter\u00edsticas",
+ "HeaderAdvanced": "Avanzado",
+ "ButtonSync": "Sincronizar",
"TabScheduledTasks": "Tareas Programadas",
- "HeaderChapters": "Chapters",
- "HeaderResumeSettings": "Resume Settings"
+ "HeaderChapters": "Cap\u00edtulos",
+ "HeaderResumeSettings": "Configuraci\u00f3n para Continuar"
} \ 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 f5f3d9387..275cd20c3 100644
--- a/MediaBrowser.Server.Implementations/Localization/Server/fr.json
+++ b/MediaBrowser.Server.Implementations/Localization/Server/fr.json
@@ -824,8 +824,8 @@
"LabelProtocolInfoHelp": "La valeur sera utilis\u00e9e par le p\u00e9riph\u00e9rique pour r\u00e9pondre aux requ\u00eates GetProtocolInfo.",
"TabXbmcMetadata": "Xbmc",
"HeaderXbmcMetadataHelp": "Media Browser inclut un support natif pour les m\u00e9tadonn\u00e9es Nfo Xbmc et les images. Pour activer ou d\u00e9sactiver les m\u00e9tadonn\u00e9es Xbmc, utiliser l'onglet Avanc\u00e9 pour configurer les options de vos types de m\u00e9dia.",
- "LabelXbmcMetadataUser": "Add user watch data to nfo's for:",
- "LabelXbmcMetadataUserHelp": "Activer ceci pour synchroniser en permanence Media Browser et Xbmc",
+ "LabelXbmcMetadataUser": "Ajouter aux nfo, les donn\u00e9es de surveillance de l'utilisateur :",
+ "LabelXbmcMetadataUserHelp": "Activer ceci pour synchroniser les donn\u00e9es de surveillance entre Media Browser et Xbmc",
"LabelXbmcMetadataDateFormat": "Format de la date de sortie :",
"LabelXbmcMetadataDateFormatHelp": "Toutes les dates provenant des nfo seront lues et \u00e9crites en utilisant ce format.",
"LabelXbmcMetadataSaveImagePaths": "Sauvegarder les chemins d'images dans les fichiers nfo.",
@@ -888,5 +888,7 @@
"ButtonSync": "Sync",
"TabScheduledTasks": "T\u00e2ches planifi\u00e9es",
"HeaderChapters": "Chapitres",
- "HeaderResumeSettings": "Reprendre les param\u00e8tres"
+ "HeaderResumeSettings": "Reprendre les param\u00e8tres",
+ "TabSync": "Sync",
+ "TitleUsers": "Users"
} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/Localization/Server/he.json b/MediaBrowser.Server.Implementations/Localization/Server/he.json
index 43135ec81..51a27549e 100644
--- a/MediaBrowser.Server.Implementations/Localization/Server/he.json
+++ b/MediaBrowser.Server.Implementations/Localization/Server/he.json
@@ -767,10 +767,10 @@
"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:",
+ "LabelHomePageSection1": "Home page section 1:",
+ "LabelHomePageSection2": "Home page section 2:",
+ "LabelHomePageSection3": "Home page section 3:",
+ "LabelHomePageSection4": "Home page section 4:",
"OptionMyViewsButtons": "My views (buttons)",
"OptionMyViews": "My views",
"OptionMyViewsSmall": "My views (small)",
@@ -888,5 +888,7 @@
"ButtonSync": "Sync",
"TabScheduledTasks": "Scheduled Tasks",
"HeaderChapters": "Chapters",
- "HeaderResumeSettings": "Resume Settings"
+ "HeaderResumeSettings": "Resume Settings",
+ "TabSync": "Sync",
+ "TitleUsers": "Users"
} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/Localization/Server/it.json b/MediaBrowser.Server.Implementations/Localization/Server/it.json
index e76d47a04..dba80e2b4 100644
--- a/MediaBrowser.Server.Implementations/Localization/Server/it.json
+++ b/MediaBrowser.Server.Implementations/Localization/Server/it.json
@@ -888,5 +888,7 @@
"ButtonSync": "Sync",
"TabScheduledTasks": "Operazioni pianificate",
"HeaderChapters": "Chapters",
- "HeaderResumeSettings": "Resume Settings"
+ "HeaderResumeSettings": "Resume Settings",
+ "TabSync": "Sync",
+ "TitleUsers": "Users"
} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/Localization/Server/kk.json b/MediaBrowser.Server.Implementations/Localization/Server/kk.json
index 9a1847ea4..a3120ffe4 100644
--- a/MediaBrowser.Server.Implementations/Localization/Server/kk.json
+++ b/MediaBrowser.Server.Implementations/Localization/Server/kk.json
@@ -888,5 +888,7 @@
"ButtonSync": "\u0421\u0438\u043d\u0445\u0440\u043e\u043d\u0434\u0430\u0443",
"TabScheduledTasks": "\u0416\u043e\u0441\u043f\u0430\u0440\u043b\u0430\u0443\u0448\u044b",
"HeaderChapters": "\u0421\u0430\u0445\u043d\u0430\u043b\u0430\u0440",
- "HeaderResumeSettings": "\u0416\u0430\u043b\u0493\u0430\u0441\u0442\u044b\u0440\u0443 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043b\u0435\u0440\u0456"
+ "HeaderResumeSettings": "\u0416\u0430\u043b\u0493\u0430\u0441\u0442\u044b\u0440\u0443 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043b\u0435\u0440\u0456",
+ "TabSync": "Sync",
+ "TitleUsers": "Users"
} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/Localization/Server/ko.json b/MediaBrowser.Server.Implementations/Localization/Server/ko.json
index f474225fd..c558cbe8d 100644
--- a/MediaBrowser.Server.Implementations/Localization/Server/ko.json
+++ b/MediaBrowser.Server.Implementations/Localization/Server/ko.json
@@ -767,10 +767,10 @@
"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:",
+ "LabelHomePageSection1": "Home page section 1:",
+ "LabelHomePageSection2": "Home page section 2:",
+ "LabelHomePageSection3": "Home page section 3:",
+ "LabelHomePageSection4": "Home page section 4:",
"OptionMyViewsButtons": "My views (buttons)",
"OptionMyViews": "My views",
"OptionMyViewsSmall": "My views (small)",
@@ -888,5 +888,7 @@
"ButtonSync": "Sync",
"TabScheduledTasks": "Scheduled Tasks",
"HeaderChapters": "Chapters",
- "HeaderResumeSettings": "Resume Settings"
+ "HeaderResumeSettings": "Resume Settings",
+ "TabSync": "Sync",
+ "TitleUsers": "Users"
} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/Localization/Server/ms.json b/MediaBrowser.Server.Implementations/Localization/Server/ms.json
index 232e94a72..134ff44d8 100644
--- a/MediaBrowser.Server.Implementations/Localization/Server/ms.json
+++ b/MediaBrowser.Server.Implementations/Localization/Server/ms.json
@@ -767,10 +767,10 @@
"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:",
+ "LabelHomePageSection1": "Home page section 1:",
+ "LabelHomePageSection2": "Home page section 2:",
+ "LabelHomePageSection3": "Home page section 3:",
+ "LabelHomePageSection4": "Home page section 4:",
"OptionMyViewsButtons": "My views (buttons)",
"OptionMyViews": "My views",
"OptionMyViewsSmall": "My views (small)",
@@ -888,5 +888,7 @@
"ButtonSync": "Sync",
"TabScheduledTasks": "Scheduled Tasks",
"HeaderChapters": "Chapters",
- "HeaderResumeSettings": "Resume Settings"
+ "HeaderResumeSettings": "Resume Settings",
+ "TabSync": "Sync",
+ "TitleUsers": "Users"
} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/Localization/Server/nb.json b/MediaBrowser.Server.Implementations/Localization/Server/nb.json
index c10e19102..165bf3f66 100644
--- a/MediaBrowser.Server.Implementations/Localization/Server/nb.json
+++ b/MediaBrowser.Server.Implementations/Localization/Server/nb.json
@@ -767,10 +767,10 @@
"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:",
+ "LabelHomePageSection1": "Home page section 1:",
+ "LabelHomePageSection2": "Home page section 2:",
+ "LabelHomePageSection3": "Home page section 3:",
+ "LabelHomePageSection4": "Home page section 4:",
"OptionMyViewsButtons": "My views (buttons)",
"OptionMyViews": "My views",
"OptionMyViewsSmall": "My views (small)",
@@ -888,5 +888,7 @@
"ButtonSync": "Sync",
"TabScheduledTasks": "Scheduled Tasks",
"HeaderChapters": "Chapters",
- "HeaderResumeSettings": "Resume Settings"
+ "HeaderResumeSettings": "Resume Settings",
+ "TabSync": "Sync",
+ "TitleUsers": "Users"
} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/Localization/Server/nl.json b/MediaBrowser.Server.Implementations/Localization/Server/nl.json
index c1a871095..5862688a6 100644
--- a/MediaBrowser.Server.Implementations/Localization/Server/nl.json
+++ b/MediaBrowser.Server.Implementations/Localization/Server/nl.json
@@ -39,7 +39,7 @@
"HeaderSetupLibrary": "Stel uw mediabibliotheek in",
"ButtonAddMediaFolder": "Mediamap toevoegen",
"LabelFolderType": "Maptype:",
- "MediaFolderHelpPluginRequired": "* Hiervoor is het gebruik van een Invoegtoepassing vereist, bijvoorbeeld GameBrowser of MB Bookshelf.",
+ "MediaFolderHelpPluginRequired": "* Hiervoor is het gebruik van een Plug-in vereist, bijvoorbeeld GameBrowser of MB Bookshelf.",
"ReferToMediaLibraryWiki": "Raadpleeg de mediabibliotheek wiki.",
"LabelCountry": "Land:",
"LabelLanguage": "Taal:",
@@ -152,16 +152,16 @@
"OptionResumable": "Hervatbaar",
"ScheduledTasksHelp": "Klik op een taak om het schema aan te passen.",
"ScheduledTasksTitle": "Geplande taken",
- "TabMyPlugins": "Mijn Invoegtoepassingen",
+ "TabMyPlugins": "Mijn Plug-ins",
"TabCatalog": "Catalogus",
- "PluginsTitle": "Invoegtoepassingen",
+ "PluginsTitle": "Plug-ins",
"HeaderAutomaticUpdates": "Automatische updates",
"HeaderNowPlaying": "Wordt nu afgespeeld",
"HeaderLatestAlbums": "Nieuwste Albums",
"HeaderLatestSongs": "Nieuwste Songs",
"HeaderRecentlyPlayed": "Recent afgespeeld",
"HeaderFrequentlyPlayed": "Vaak afgespeeld",
- "DevBuildWarning": "Development versies zijn voor eigen risico. Ze worden vaak uitgegeven, deze versies zijn niet getest!. De Applicatie kan crashen en sommige functies kunnen mogelijk niet meer werken.",
+ "DevBuildWarning": "Development versies zijn geheel voor eigen risico. Deze versies worden vaak vrijgegeven en zijn niet getest!. De Applicatie kan crashen en sommige functies kunnen mogelijk niet meer werken.",
"LabelVideoType": "Video Type:",
"OptionBluray": "Blu-ray",
"OptionDvd": "Dvd",
@@ -249,11 +249,11 @@
"OptionRelease": "Offici\u00eble Release",
"OptionBeta": "Beta",
"OptionDev": "Dev (Instabiel)",
- "LabelAllowServerAutoRestart": "Sta de server toe automatisch te herstarten om updates toe te passen",
- "LabelAllowServerAutoRestartHelp": "De server zal alleen opnieuw op tijdens inactieve perioden, wanneer er geen gebruikers actief zijn.",
+ "LabelAllowServerAutoRestart": "Automatisch herstarten van de server toestaan om updates toe te passen",
+ "LabelAllowServerAutoRestartHelp": "De server zal alleen opnieuw opstarten tijdens inactieve perioden, wanneer er geen gebruikers actief zijn.",
"LabelEnableDebugLogging": "Foutopsporings logboek inschakelen",
"LabelRunServerAtStartup": "Start server bij het aanmelden",
- "LabelRunServerAtStartupHelp": "Dit zal de applicatie starten als pictogram in het systeemvak tijdens het aanmelden van Windows. Om de Windows-service te starten, schakelt u deze uit en start u de service via het Configuratiescherm van Windows. Houd er rekening mee dat u de service en de applicatie niet tegelijk kunt uitvoeren, u moet dus eerst de applicatie sluiten alvorens u de service start.",
+ "LabelRunServerAtStartupHelp": "Dit start de applicatie als pictogram in het systeemvak tijdens het aanmelden van Windows. Om de Windows-service te starten, schakelt u deze uit en start u de service via het Configuratiescherm van Windows. Houd er rekening mee dat u de service en de applicatie niet tegelijk kunt uitvoeren, u moet dus eerst de applicatie sluiten alvorens u de service start.",
"ButtonSelectDirectory": "Selecteer map",
"LabelCustomPaths": "Geef aangepaste paden op waar gewenst. Laat velden leeg om de standaardinstellingen te gebruiken.",
"LabelCachePath": "Cache pad:",
@@ -333,10 +333,10 @@
"LabelNumberOfGuideDays": "Aantal dagen van de gids om te downloaden:",
"LabelNumberOfGuideDaysHelp": "Het downloaden van meer dagen van de gids gegevens biedt de mogelijkheid verder vooruit te plannen en een beter overzicht geven, maar het zal ook langer duren om te downloaden. Auto kiest op basis van het aantal kanalen.",
"LabelActiveService": "Actieve Service:",
- "LabelActiveServiceHelp": "Er kunnen meerdere tv Invoegtoepassingen worden ge\u00efnstalleerd, maar er kan er slechts een per keer actief zijn.",
+ "LabelActiveServiceHelp": "Er kunnen meerdere tv Plug-ins worden ge\u00efnstalleerd, maar er kan er slechts een per keer actief zijn.",
"OptionAutomatic": "Automatisch",
- "LiveTvPluginRequired": "Een Live TV service provider Invoegtoepassing is vereist om door te gaan.",
- "LiveTvPluginRequiredHelp": "Installeer a.u b een van onze beschikbare Invoegtoepassingen, zoals Next PVR of ServerWmc.",
+ "LiveTvPluginRequired": "Een Live TV service provider Plug-in is vereist om door te gaan.",
+ "LiveTvPluginRequiredHelp": "Installeer a.u b een van onze beschikbare Plug-ins, zoals Next PVR of ServerWmc.",
"LabelCustomizeOptionsPerMediaType": "Aanpassen voor mediatype",
"OptionDownloadThumbImage": "Miniatuur",
"OptionDownloadMenuImage": "Menu",
@@ -577,9 +577,9 @@
"HeaderNotificationList": "Klik op een melding om de opties voor het versturen ervan te configureren .",
"NotificationOptionApplicationUpdateAvailable": "Programma-update beschikbaar",
"NotificationOptionApplicationUpdateInstalled": "Programma-update ge\u00efnstalleerd",
- "NotificationOptionPluginUpdateInstalled": "Invoegtoepassings-update ge\u00efnstalleerd",
- "NotificationOptionPluginInstalled": "Invoegtoepassing ge\u00efnstalleerd",
- "NotificationOptionPluginUninstalled": "Invoegtoepassing verwijderd",
+ "NotificationOptionPluginUpdateInstalled": "Plug-in-update ge\u00efnstalleerd",
+ "NotificationOptionPluginInstalled": "Plug-in ge\u00efnstalleerd",
+ "NotificationOptionPluginUninstalled": "Plug-in verwijderd",
"NotificationOptionVideoPlayback": "Video afspelen gestart",
"NotificationOptionAudioPlayback": "Audio afspelen gestart",
"NotificationOptionGamePlayback": "Game gestart",
@@ -590,7 +590,7 @@
"NotificationOptionInstallationFailed": "Mislukken van de installatie",
"NotificationOptionNewLibraryContent": "Nieuwe content toegevoegd",
"NotificationOptionNewLibraryContentMultiple": "Nieuwe content toegevoegd (meerdere)",
- "SendNotificationHelp": "Meldingen worden geplaatst in de inbox op het dashboard. Blader door de Invoegtoepassings catalogus om aanvullende opties voor meldingen te installeren.",
+ "SendNotificationHelp": "Meldingen worden geplaatst in de inbox op het dashboard. Blader door de Plug-in catalogus om aanvullende opties voor meldingen te installeren.",
"NotificationOptionServerRestartRequired": "Server herstart nodig",
"LabelNotificationEnabled": "Deze melding inschakelen",
"LabelMonitorUsers": "Monitor activiteit van:",
@@ -600,10 +600,10 @@
"CategoryUser": "Gebruiker",
"CategorySystem": "Systeem",
"CategoryApplication": "Toepassing",
- "CategoryPlugin": "Invoegtoepassing",
+ "CategoryPlugin": "Plug-in",
"LabelMessageTitle": "Titel van het bericht:",
"LabelAvailableTokens": "Beschikbaar tokens:",
- "AdditionalNotificationServices": "Blader door de Invoegtoepassings catalogus om aanvullende meldingsdiensten te installeren.",
+ "AdditionalNotificationServices": "Blader door de Plug-in catalogus om aanvullende meldingsdiensten te installeren.",
"OptionAllUsers": "Alle gebruikers",
"OptionAdminUsers": "Beheerders",
"OptionCustomUsers": "Aangepast",
@@ -637,7 +637,7 @@
"ButtonPause": "Pauze",
"LabelGroupMoviesIntoCollections": "Groepeer films in verzamelingen",
"LabelGroupMoviesIntoCollectionsHelp": "Bij de weergave van film lijsten, zullen films die behoren tot een verzameling worden weergegeven als een gegroepeerd object.",
- "NotificationOptionPluginError": "Invoegtoepassings fout",
+ "NotificationOptionPluginError": "Plug-in fout",
"ButtonVolumeUp": "Volume omhoog",
"ButtonVolumeDown": "Volume omlaag",
"ButtonMute": "Dempen",
@@ -723,7 +723,7 @@
"OptionReportByteRangeSeekingWhenTranscodingHelp": "Dit is vereist voor bepaalde apparaten die zo goed op tijd zoeken.",
"HeaderSubtitleDownloadingHelp": "Bij het scannen van uw videobestanden kan Media Browser naar ontbrekende ondertiteling zoeken en deze downloaden bij ondertiteling providers zoals OpenSubtitles.org.",
"HeaderDownloadSubtitlesFor": "Download ondertiteling voor:",
- "MessageNoChapterProviders": "Installeer een hoofdstuk provider Invoegtoepassing zoals ChapterDb om extra hoofdstuk opties in te schakelen.",
+ "MessageNoChapterProviders": "Installeer een hoofdstuk provider Plug-in zoals ChapterDb om extra hoofdstuk opties in te schakelen.",
"LabelSkipIfGraphicalSubsPresent": "Overslaan als de video al grafische ondertitels bevat",
"LabelSkipIfGraphicalSubsPresentHelp": "Tekstversies houden van ondertitels zal resulteren in meer effici\u00ebnte levering aan mobiele clients.",
"TabSubtitles": "Ondertiteling",
@@ -731,7 +731,7 @@
"HeaderDownloadChaptersFor": "Download hoofdstuk namen voor:",
"LabelOpenSubtitlesUsername": "Gebruikersnaam Open Subtitles:",
"LabelOpenSubtitlesPassword": "Wachtwoord Open Subtitles:",
- "HeaderChapterDownloadingHelp": "Wanneer Media Browser uw videobestanden scant kan het gebruiksvriendelijke namen voor hoofdstukken downloaden van het internet met behulp van hoofdstuk Invoegtoepassing zoals ChapterDb.",
+ "HeaderChapterDownloadingHelp": "Wanneer Media Browser uw videobestanden scant kan het gebruiksvriendelijke namen voor hoofdstukken downloaden van het internet met behulp van hoofdstuk Plug-in zoals ChapterDb.",
"LabelPlayDefaultAudioTrack": "Speel standaard audio spoor ongeacht taal",
"LabelSubtitlePlaybackMode": "Ondertitelingsmode:",
"LabelDownloadLanguages": "Download talen:",
@@ -741,8 +741,8 @@
"HeaderSendMessage": "Stuur bericht",
"ButtonSend": "Stuur",
"LabelMessageText": "Bericht tekst:",
- "MessageNoAvailablePlugins": "Geen beschikbare Invoegtoepassingen.",
- "LabelDisplayPluginsFor": "Toon Invoegtoepassingen voor:",
+ "MessageNoAvailablePlugins": "Geen beschikbare Plug-ins.",
+ "LabelDisplayPluginsFor": "Toon Plug-ins voor:",
"PluginTabMediaBrowserClassic": "MB Classic",
"PluginTabMediaBrowserTheater": "MB Theater",
"LabelEpisodeName": "Naam aflevering",
@@ -803,7 +803,7 @@
"LabelChannelDownloadPathHelp": "Geef een eigen download pad op als dit gewenst is, leeglaten voor dowloaden naar de interne program data map.",
"LabelChannelDownloadAge": "Verwijder inhoud na: (dagen)",
"LabelChannelDownloadAgeHelp": "Gedownloade inhoud die ouder is zal worden verwijderd. Afspelen via internet streaming blijft mogelijk.",
- "ChannelSettingsFormHelp": "Installeer kanalen zoals Trailers en Vimeo in de Invoegtoepassings catalogus.",
+ "ChannelSettingsFormHelp": "Installeer kanalen zoals Trailers en Vimeo in de Plug-in catalogus.",
"LabelSelectCollection": "Selecteer verzameling:",
"ViewTypeMovies": "Films",
"ViewTypeTvShows": "TV",
@@ -888,5 +888,7 @@
"ButtonSync": "Sync",
"TabScheduledTasks": "Geplande taken",
"HeaderChapters": "Hoofdstukken",
- "HeaderResumeSettings": "Resume Settings"
+ "HeaderResumeSettings": "Instellingen voor Hervatten",
+ "TabSync": "Sync",
+ "TitleUsers": "Users"
} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/Localization/Server/pl.json b/MediaBrowser.Server.Implementations/Localization/Server/pl.json
index 3032f71f0..135b640f2 100644
--- a/MediaBrowser.Server.Implementations/Localization/Server/pl.json
+++ b/MediaBrowser.Server.Implementations/Localization/Server/pl.json
@@ -767,10 +767,10 @@
"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:",
+ "LabelHomePageSection1": "Home page section 1:",
+ "LabelHomePageSection2": "Home page section 2:",
+ "LabelHomePageSection3": "Home page section 3:",
+ "LabelHomePageSection4": "Home page section 4:",
"OptionMyViewsButtons": "My views (buttons)",
"OptionMyViews": "My views",
"OptionMyViewsSmall": "My views (small)",
@@ -888,5 +888,7 @@
"ButtonSync": "Sync",
"TabScheduledTasks": "Scheduled Tasks",
"HeaderChapters": "Chapters",
- "HeaderResumeSettings": "Resume Settings"
+ "HeaderResumeSettings": "Resume Settings",
+ "TabSync": "Sync",
+ "TitleUsers": "Users"
} \ 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 a80d352b9..55d76aa8b 100644
--- a/MediaBrowser.Server.Implementations/Localization/Server/pt_BR.json
+++ b/MediaBrowser.Server.Implementations/Localization/Server/pt_BR.json
@@ -190,7 +190,7 @@
"HeaderStatus": "Status",
"OptionContinuing": "Em Exibi\u00e7\u00e3o",
"OptionEnded": "Finalizada",
- "HeaderAirDays": "Dias de Exibi\u00e7\u00e3o:",
+ "HeaderAirDays": "Dias de Exibi\u00e7\u00e3o",
"OptionSunday": "Domingo",
"OptionMonday": "Segunda-feira",
"OptionTuesday": "Ter\u00e7a-feira",
@@ -198,7 +198,7 @@
"OptionThursday": "Quinta-feira",
"OptionFriday": "Sexta-feira",
"OptionSaturday": "S\u00e1bado",
- "HeaderManagement": "Gerenciamento:",
+ "HeaderManagement": "Gerenciamento",
"LabelManagement": "Administra\u00e7\u00e3o:",
"OptionMissingImdbId": "Faltando Id IMDb",
"OptionMissingTvdbId": "Faltando Id TheTVDB",
@@ -257,11 +257,11 @@
"ButtonSelectDirectory": "Selecionar Diret\u00f3rio",
"LabelCustomPaths": "Defina caminhos personalizados. Deixe os campos em branco para usar o padr\u00e3o.",
"LabelCachePath": "Caminho do cache:",
- "LabelCachePathHelp": "Esta pasta cont\u00e9m arquivos de cache do servidor como, por exemplo, imagens.",
+ "LabelCachePathHelp": "Defina uma localiza\u00e7\u00e3o para os arquivos de cache como, por exemplo, imagens.",
"LabelImagesByNamePath": "Caminho do Images by name:",
- "LabelImagesByNamePathHelp": "Esta pasta cont\u00e9m imagens transferidas para ator, artista, g\u00eanero e est\u00fadio.",
+ "LabelImagesByNamePathHelp": "Defina uma localiza\u00e7\u00e3o para imagens baixadas para ator, artista, g\u00eanero e est\u00fadio.",
"LabelMetadataPath": "Caminho dos Metadados:",
- "LabelMetadataPathHelp": "Esta localiza\u00e7\u00e3o cont\u00e9m artwork e metadados transferidos que n\u00e3o foram configurados para serem armazenados nas pastas de m\u00eddia.",
+ "LabelMetadataPathHelp": "Defina uma localiza\u00e7\u00e3o para artwork e metadados baixados, caso n\u00e3o sejam salvos dentro das pastas de m\u00eddia.",
"LabelTranscodingTempPath": "Caminho tempor\u00e1rio para transcodifica\u00e7\u00e3o:",
"LabelTranscodingTempPathHelp": "Esta pasta cont\u00e9m arquivos ativos usados pelo transcodificador. Especifique um caminho personalizado ou deixe em branco para usar o padr\u00e3o dentro da pasta de dados do servidor.",
"TabBasics": "B\u00e1sico",
@@ -692,7 +692,7 @@
"LabelMaxBitrate": "Taxa de bits m\u00e1xima:",
"LabelMaxBitrateHelp": "Especifique uma taxa de bits m\u00e1xima para ambientes com restri\u00e7\u00e3o de tamanho de banda, ou se o dispositivo imp\u00f5e esse limite.",
"OptionIgnoreTranscodeByteRangeRequests": "Ignorar requisi\u00e7\u00f5es de extens\u00e3o do byte de transcodifica\u00e7\u00e3o",
- "OptionIgnoreTranscodeByteRangeRequestsHelp": "Se ativado, estas requisi\u00e7\u00f5es ser\u00e3o honradas mas ir\u00e3o ignorar o cabe\u00e7alho da extens\u00e3o do byte.",
+ "OptionIgnoreTranscodeByteRangeRequestsHelp": "Se ativadas, estas requisi\u00e7\u00f5es ser\u00e3o honradas mas ir\u00e3o ignorar o cabe\u00e7alho da extens\u00e3o do byte.",
"LabelFriendlyName": "Nome amig\u00e1vel",
"LabelManufacturer": "Fabricante",
"LabelManufacturerUrl": "Url do fabricante",
@@ -834,7 +834,7 @@
"LabelXbmcMetadataEnablePathSubstitutionHelp": "Ativa a substitui\u00e7\u00e3o do caminho da imagem usando as configura\u00e7\u00f5es de suvbstitui\u00e7\u00e3o de caminho do servidor.",
"LabelXbmcMetadataEnablePathSubstitutionHelp2": "Ver substitui\u00e7\u00e3o de caminho.",
"LabelGroupChannelsIntoViews": "Exibir os seguintes canais diretamente dentro de minhas visualiza\u00e7\u00f5es:",
- "LabelGroupChannelsIntoViewsHelp": "Se ativado, estes canais ser\u00e3o exibidos imediatamente ao lado de outras visualiza\u00e7\u00f5es. Se desativado, eles ser\u00e3o exibidos dentro de uma visualiza\u00e7\u00e3o separada de Canais.",
+ "LabelGroupChannelsIntoViewsHelp": "Se ativados, estes canais ser\u00e3o exibidos imediatamente ao lado de outras visualiza\u00e7\u00f5es. Se desativado, eles ser\u00e3o exibidos dentro de uma visualiza\u00e7\u00e3o separada de Canais.",
"LabelDisplayCollectionsView": "Exibir uma visualiza\u00e7\u00e3o de cole\u00e7\u00f5es para mostrar colet\u00e2neas de filmes",
"LabelXbmcMetadataEnableExtraThumbs": "Copiar extrafanart para dentro de extrathumbs",
"LabelXbmcMetadataEnableExtraThumbsHelp": "Ao fazer o download de imagens elas podem ser salvas em ambas extrafanart e extrathumbs para uma maior compatibilidade com a skin do Xbmc.",
@@ -842,7 +842,7 @@
"TabLogs": "Logs",
"HeaderServerLogFiles": "Arquivos de log do servidor:",
"TabBranding": "Marca",
- "HeaderBrandingHelp": "Personalizar a apar\u00eancia do Media Browser para as necessidades de seu grupo ou organiza\u00e7\u00e3o.",
+ "HeaderBrandingHelp": "Personalize a apar\u00eancia do Media Browser para as necessidades de seu grupo ou organiza\u00e7\u00e3o.",
"LabelLoginDisclaimer": "Aviso legal no login:",
"LabelLoginDisclaimerHelp": "Isto ser\u00e1 exibido na parte inferior da p\u00e1gina de login.",
"LabelAutomaticallyDonate": "Doar automaticamente esta quantidade a cada seis meses",
@@ -880,13 +880,15 @@
"TabSort": "Ordenar",
"TabFilter": "Filtro",
"ButtonView": "Visualizar",
- "LabelPageSize": "Tamanho de exibi\u00e7\u00e3o:",
+ "LabelPageSize": "Limite do item:",
"LabelView": "Visualizar:",
"TabUsers": "Usu\u00e1rios",
"HeaderFeatures": "Inclui",
"HeaderAdvanced": "Avan\u00e7ado",
- "ButtonSync": "Sync",
+ "ButtonSync": "Sincronizar",
"TabScheduledTasks": "Tarefas Agendadas",
- "HeaderChapters": "Chapters",
- "HeaderResumeSettings": "Resume Settings"
+ "HeaderChapters": "Cap\u00edtulos",
+ "HeaderResumeSettings": "Ajustes para Retomar",
+ "TabSync": "Sync",
+ "TitleUsers": "Users"
} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/Localization/Server/pt_PT.json b/MediaBrowser.Server.Implementations/Localization/Server/pt_PT.json
index 83e82070a..966365f7c 100644
--- a/MediaBrowser.Server.Implementations/Localization/Server/pt_PT.json
+++ b/MediaBrowser.Server.Implementations/Localization/Server/pt_PT.json
@@ -767,10 +767,10 @@
"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:",
+ "LabelHomePageSection1": "Home page section 1:",
+ "LabelHomePageSection2": "Home page section 2:",
+ "LabelHomePageSection3": "Home page section 3:",
+ "LabelHomePageSection4": "Home page section 4:",
"OptionMyViewsButtons": "My views (buttons)",
"OptionMyViews": "My views",
"OptionMyViewsSmall": "My views (small)",
@@ -888,5 +888,7 @@
"ButtonSync": "Sync",
"TabScheduledTasks": "Scheduled Tasks",
"HeaderChapters": "Chapters",
- "HeaderResumeSettings": "Resume Settings"
+ "HeaderResumeSettings": "Resume Settings",
+ "TabSync": "Sync",
+ "TitleUsers": "Users"
} \ 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 abdb98e97..c5ea589e8 100644
--- a/MediaBrowser.Server.Implementations/Localization/Server/ru.json
+++ b/MediaBrowser.Server.Implementations/Localization/Server/ru.json
@@ -322,7 +322,7 @@
"HeaderActiveRecordings": "\u0410\u043a\u0442\u0438\u0432\u043d\u044b\u0435 \u0437\u0430\u043f\u0438\u0441\u0438",
"HeaderLatestRecordings": "\u041d\u043e\u0432\u0438\u043d\u043a\u0438 \u0437\u0430\u043f\u0438\u0441\u0435\u0439",
"HeaderAllRecordings": "\u0412\u0441\u0435 \u0437\u0430\u043f\u0438\u0441\u0438",
- "ButtonPlay": "\u0412\u043e\u0441\u043f\u0440",
+ "ButtonPlay": "\u0412\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0441\u0442\u0438",
"ButtonEdit": "\u041f\u0440\u0430\u0432\u0438\u0442\u044c",
"ButtonRecord": "\u0417\u0430\u043f\u0438\u0441\u0430\u0442\u044c",
"ButtonDelete": "\u0423\u0434\u0430\u043b\u0438\u0442\u044c",
@@ -551,7 +551,7 @@
"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.",
+ "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 \u0434\u043e\u043b\u0436\u043d\u044b \u0431\u044b\u0442\u044c \u0442\u0430\u043a\u0436\u0435 \u0441\u043f\u043e\u043d\u0441\u043e\u0440\u043e\u043c Media Browser. \u0416\u0435\u0440\u0442\u0432\u0443\u0439\u0442\u0435 \u0438 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0439\u0442\u0435 \u0434\u0430\u043b\u044c\u043d\u0435\u0439\u0448\u0443\u044e \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \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.",
"HeaderDisplaySettings": "\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f",
"TabPlayTo": "\u0412\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0441\u0442\u0438 \u041d\u0430",
"LabelEnableDlnaServer": "\u0412\u043a\u043b\u044e\u0447\u0438\u0442\u044c DLNA-\u0441\u0435\u0440\u0432\u0435\u0440",
@@ -845,8 +845,8 @@
"HeaderBrandingHelp": "\u041f\u043e\u0434\u0433\u043e\u043d\u043a\u0430 \u0432\u043d\u0435\u0448\u043d\u0435\u0433\u043e \u0432\u0438\u0434\u0430 Media Browser \u0432 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0438\u0438 \u0441 \u043f\u043e\u0442\u0440\u0435\u0431\u043d\u043e\u0441\u0442\u044f\u043c\u0438 \u0432\u0430\u0448\u0435\u0439 \u0433\u0440\u0443\u043f\u043f\u044b \u0438\u043b\u0438 \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0446\u0438\u0438.",
"LabelLoginDisclaimer": "\u041e\u0433\u043e\u0432\u043e\u0440\u043a\u0430 \u043d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435 \u0432\u0445\u043e\u0434\u0430:",
"LabelLoginDisclaimerHelp": "\u042d\u0442\u043e \u0431\u0443\u0434\u0435\u0442 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0442\u044c\u0441\u044f \u0432 \u043d\u0438\u0436\u043d\u0435\u0439 \u0447\u0430\u0441\u0442\u0438 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b \u0432\u0445\u043e\u0434\u0430 \u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u0443.",
- "LabelAutomaticallyDonate": "\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0434\u0430\u0440\u0438\u0442\u044c \u0434\u0430\u043d\u043d\u0443\u044e \u0441\u0443\u043c\u043c\u0443 \u043a\u0430\u0436\u0434\u044b\u0435 \u0448\u0435\u0441\u0442\u044c \u043c\u0435\u0441\u044f\u0446\u0435\u0432",
- "LabelAutomaticallyDonateHelp": "\u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0432 \u043b\u044e\u0431\u043e\u0435 \u0432\u0440\u0435\u043c\u044f \u043f\u0440\u0435\u043a\u0440\u0430\u0442\u0438\u0442\u044c \u044d\u0442\u043e \u0447\u0435\u0440\u0435\u0437 \u0441\u0432\u043e\u044e \u0443\u0447\u0435\u0442\u043d\u0443\u044e \u0437\u0430\u043f\u0438\u0441\u044c PayPal.",
+ "LabelAutomaticallyDonate": "\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0436\u0435\u0440\u0442\u0432\u043e\u0432\u0430\u0442\u044c \u0434\u0430\u043d\u043d\u0443\u044e \u0441\u0443\u043c\u043c\u0443 \u043a\u0430\u0436\u0434\u044b\u0435 \u0448\u0435\u0441\u0442\u044c \u043c\u0435\u0441\u044f\u0446\u0435\u0432",
+ "LabelAutomaticallyDonateHelp": "\u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0432 \u043b\u044e\u0431\u043e\u0435 \u0432\u0440\u0435\u043c\u044f \u043e\u0442\u043c\u0435\u043d\u0438\u0442\u044c \u044d\u0442\u043e \u0447\u0435\u0440\u0435\u0437 \u0441\u0432\u043e\u044e \u0443\u0447\u0435\u0442\u043d\u0443\u044e \u0437\u0430\u043f\u0438\u0441\u044c PayPal.",
"OptionList": "\u0421\u043f\u0438\u0441\u043e\u043a",
"TabDashboard": "\u0418\u043d\u0444\u043e\u043f\u0430\u043d\u0435\u043b\u044c",
"TitleServer": "\u0421\u0435\u0440\u0432\u0435\u0440",
@@ -878,15 +878,17 @@
"OptionSubstring": "\u041f\u043e\u0434\u0441\u0442\u0440\u043e\u043a\u0430",
"TabView": "\u0412\u0438\u0434",
"TabSort": "\u0421\u043e\u0440\u0442-\u043a\u0430",
- "TabFilter": "\u0424\u0438\u043b\u044c\u0442\u0440-\u043a\u0430",
+ "TabFilter": "\u0424\u0438\u043b\u044c\u0442\u0440\u044b",
"ButtonView": "\u041f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c",
"LabelPageSize": "\u041c\u0430\u043a\u0441. \u0447\u0438\u0441\u043b\u043e \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432:",
- "LabelView": "\u041f\u0440\u043e\u0441\u043c\u043e\u0442\u0440:",
+ "LabelView": "\u041f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0435:",
"TabUsers": "\u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438",
"HeaderFeatures": "\u041c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u044b",
"HeaderAdvanced": "\u0414\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e",
- "ButtonSync": "\u0421\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c",
+ "ButtonSync": "\u0421\u0438\u043d\u0445\u0440\u043e",
"TabScheduledTasks": "\u041f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0449\u0438\u043a",
"HeaderChapters": "\u0421\u0446\u0435\u043d\u044b",
- "HeaderResumeSettings": "\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0432\u043e\u0437\u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f"
+ "HeaderResumeSettings": "\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0432\u043e\u0437\u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f",
+ "TabSync": "Sync",
+ "TitleUsers": "Users"
} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/Localization/Server/server.json b/MediaBrowser.Server.Implementations/Localization/Server/server.json
index 3a49024f9..8b3a5e8c9 100644
--- a/MediaBrowser.Server.Implementations/Localization/Server/server.json
+++ b/MediaBrowser.Server.Implementations/Localization/Server/server.json
@@ -781,10 +781,10 @@
"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:",
+ "LabelHomePageSection1": "Home page section 1:",
+ "LabelHomePageSection2": "Home page section 2:",
+ "LabelHomePageSection3": "Home page section 3:",
+ "LabelHomePageSection4": "Home page section 4:",
"OptionMyViewsButtons": "My views (buttons)",
"OptionMyViews": "My views",
"OptionMyViewsSmall": "My views (small)",
@@ -897,11 +897,13 @@
"ButtonView": "View",
"LabelPageSize": "Item limit:",
"LabelView": "View:",
- "TabUsers": "Users",
- "HeaderFeatures": "Features",
- "HeaderAdvanced": "Advanced",
- "ButtonSync": "Sync",
- "TabScheduledTasks": "Scheduled Tasks",
- "HeaderChapters": "Chapters",
- "HeaderResumeSettings": "Resume Settings"
+ "TabUsers": "Users",
+ "HeaderFeatures": "Features",
+ "HeaderAdvanced": "Advanced",
+ "ButtonSync": "Sync",
+ "TabScheduledTasks": "Scheduled Tasks",
+ "HeaderChapters": "Chapters",
+ "HeaderResumeSettings": "Resume Settings",
+ "TabSync": "Sync",
+ "TitleUsers": "Users"
}
diff --git a/MediaBrowser.Server.Implementations/Localization/Server/sv.json b/MediaBrowser.Server.Implementations/Localization/Server/sv.json
index 9988af0fa..c6c042640 100644
--- a/MediaBrowser.Server.Implementations/Localization/Server/sv.json
+++ b/MediaBrowser.Server.Implementations/Localization/Server/sv.json
@@ -888,5 +888,7 @@
"ButtonSync": "Sync",
"TabScheduledTasks": "Scheduled Tasks",
"HeaderChapters": "Chapters",
- "HeaderResumeSettings": "Resume Settings"
+ "HeaderResumeSettings": "Resume Settings",
+ "TabSync": "Sync",
+ "TitleUsers": "Users"
} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/Localization/Server/vi.json b/MediaBrowser.Server.Implementations/Localization/Server/vi.json
index 98f108421..17748ecc7 100644
--- a/MediaBrowser.Server.Implementations/Localization/Server/vi.json
+++ b/MediaBrowser.Server.Implementations/Localization/Server/vi.json
@@ -767,10 +767,10 @@
"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:",
+ "LabelHomePageSection1": "Home page section 1:",
+ "LabelHomePageSection2": "Home page section 2:",
+ "LabelHomePageSection3": "Home page section 3:",
+ "LabelHomePageSection4": "Home page section 4:",
"OptionMyViewsButtons": "My views (buttons)",
"OptionMyViews": "My views",
"OptionMyViewsSmall": "My views (small)",
@@ -888,5 +888,7 @@
"ButtonSync": "Sync",
"TabScheduledTasks": "Scheduled Tasks",
"HeaderChapters": "Chapters",
- "HeaderResumeSettings": "Resume Settings"
+ "HeaderResumeSettings": "Resume Settings",
+ "TabSync": "Sync",
+ "TitleUsers": "Users"
} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/Localization/Server/zh_TW.json b/MediaBrowser.Server.Implementations/Localization/Server/zh_TW.json
index 6511dd7d0..352a031e1 100644
--- a/MediaBrowser.Server.Implementations/Localization/Server/zh_TW.json
+++ b/MediaBrowser.Server.Implementations/Localization/Server/zh_TW.json
@@ -767,10 +767,10 @@
"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:",
+ "LabelHomePageSection1": "Home page section 1:",
+ "LabelHomePageSection2": "Home page section 2:",
+ "LabelHomePageSection3": "Home page section 3:",
+ "LabelHomePageSection4": "Home page section 4:",
"OptionMyViewsButtons": "My views (buttons)",
"OptionMyViews": "My views",
"OptionMyViewsSmall": "My views (small)",
@@ -888,5 +888,7 @@
"ButtonSync": "Sync",
"TabScheduledTasks": "Scheduled Tasks",
"HeaderChapters": "Chapters",
- "HeaderResumeSettings": "Resume Settings"
+ "HeaderResumeSettings": "Resume Settings",
+ "TabSync": "Sync",
+ "TitleUsers": "Users"
} \ 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 9655771c6..ef6730b75 100644
--- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj
+++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj
@@ -45,9 +45,9 @@
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
</PropertyGroup>
<ItemGroup>
- <Reference Include="Mono.Nat, Version=1.2.20.0, Culture=neutral, processorArchitecture=MSIL">
+ <Reference Include="Mono.Nat, Version=1.2.21.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
- <HintPath>..\packages\Mono.Nat.1.2.20.0\lib\net40\Mono.Nat.dll</HintPath>
+ <HintPath>..\packages\Mono.Nat.1.2.21.0\lib\net40\Mono.Nat.dll</HintPath>
</Reference>
<Reference Include="Nowin">
<HintPath>..\ThirdParty\Nowin\Nowin.dll</HintPath>
@@ -274,6 +274,7 @@
<Compile Include="Sync\CloudSyncProvider.cs" />
<Compile Include="Sync\MockSyncProvider.cs" />
<Compile Include="Sync\SyncManager.cs" />
+ <Compile Include="Sync\SyncRepository.cs" />
<Compile Include="Themes\AppThemeManager.cs" />
<Compile Include="Udp\UdpMessageReceivedEventArgs.cs" />
<Compile Include="Udp\UdpServer.cs" />
diff --git a/MediaBrowser.Server.Implementations/Sync/SyncManager.cs b/MediaBrowser.Server.Implementations/Sync/SyncManager.cs
index 50f1030f3..a8d723ce3 100644
--- a/MediaBrowser.Server.Implementations/Sync/SyncManager.cs
+++ b/MediaBrowser.Server.Implementations/Sync/SyncManager.cs
@@ -1,9 +1,14 @@
using MediaBrowser.Common.Extensions;
+using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities.Audio;
+using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Sync;
using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Sync;
+using MoreLinq;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -13,56 +18,131 @@ namespace MediaBrowser.Server.Implementations.Sync
{
public class SyncManager : ISyncManager
{
- private ISyncProvider[] _providers = new ISyncProvider[] { };
+ private readonly ILibraryManager _libraryManager;
+ private readonly ISyncRepository _repo;
+ private readonly IImageProcessor _imageProcessor;
+ private readonly ILogger _logger;
+
+ private ISyncProvider[] _providers = { };
+
+ public SyncManager(ILibraryManager libraryManager, ISyncRepository repo, IImageProcessor imageProcessor, ILogger logger)
+ {
+ _libraryManager = libraryManager;
+ _repo = repo;
+ _imageProcessor = imageProcessor;
+ _logger = logger;
+ }
public void AddParts(IEnumerable<ISyncProvider> providers)
{
_providers = providers.ToArray();
}
- public Task<List<SyncJob>> CreateJob(SyncJobRequest request)
+ public async Task<SyncJobCreationResult> CreateJob(SyncJobRequest request)
{
- throw new NotImplementedException();
+ var items = GetItemsForSync(request.ItemIds).ToList();
+
+ if (items.Count == 1)
+ {
+ request.Name = GetDefaultName(items[0]);
+ }
+
+ if (string.IsNullOrWhiteSpace(request.Name))
+ {
+ throw new ArgumentException("Please supply a name for the sync job.");
+ }
+
+ var target = GetSyncTargets(request.UserId)
+ .First(i => string.Equals(request.TargetId, i.Id));
+
+ var jobId = Guid.NewGuid().ToString("N");
+
+ var job = new SyncJob
+ {
+ Id = jobId,
+ Name = request.Name,
+ TargetId = target.Id,
+ UserId = request.UserId,
+ UnwatchedOnly = request.UnwatchedOnly,
+ Limit = request.Limit,
+ LimitType = request.LimitType,
+ RequestedItemIds = request.ItemIds,
+ DateCreated = DateTime.UtcNow,
+ DateLastModified = DateTime.UtcNow
+ };
+
+ await _repo.Create(job).ConfigureAwait(false);
+
+ return new SyncJobCreationResult
+ {
+ Job = GetJob(jobId)
+ };
}
public QueryResult<SyncJob> GetJobs(SyncJobQuery query)
{
- throw new NotImplementedException();
- }
+ var result = _repo.GetJobs(query);
- public QueryResult<SyncSchedule> GetSchedules(SyncScheduleQuery query)
- {
- throw new NotImplementedException();
- }
+ result.Items.ForEach(FillMetadata);
- public Task CancelJob(string id)
- {
- throw new NotImplementedException();
+ return result;
}
- public Task CancelSchedule(string id)
+ private void FillMetadata(SyncJob job)
{
- throw new NotImplementedException();
+ var item = GetItemsForSync(job.RequestedItemIds)
+ .FirstOrDefault();
+
+ if (item != null)
+ {
+ var hasSeries = item as IHasSeries;
+ if (hasSeries != null)
+ {
+ job.ParentName = hasSeries.SeriesName;
+ }
+
+ var hasAlbumArtist = item as IHasAlbumArtist;
+ if (hasAlbumArtist != null)
+ {
+ job.ParentName = hasAlbumArtist.AlbumArtists.FirstOrDefault();
+ }
+
+ var primaryImage = item.GetImageInfo(ImageType.Primary, 0);
+
+ if (primaryImage != null)
+ {
+ try
+ {
+ job.PrimaryImageTag = _imageProcessor.GetImageCacheTag(item, ImageType.Primary);
+ job.PrimaryImageItemId = item.Id.ToString("N");
+
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error getting image info", ex);
+ }
+ }
+ }
}
- public SyncJob GetJob(string id)
+ public Task CancelJob(string id)
{
throw new NotImplementedException();
}
- public SyncSchedule GetSchedule(string id)
+ public SyncJob GetJob(string id)
{
- throw new NotImplementedException();
+ return _repo.GetJob(id);
}
public IEnumerable<SyncTarget> GetSyncTargets(string userId)
{
return _providers
- .SelectMany(GetSyncTargets)
+ .SelectMany(i => GetSyncTargets(i, userId))
.OrderBy(i => i.Name);
}
- private IEnumerable<SyncTarget> GetSyncTargets(ISyncProvider provider)
+ private IEnumerable<SyncTarget> GetSyncTargets(ISyncProvider provider, string userId)
{
var providerId = GetSyncProviderId(provider);
@@ -120,5 +200,37 @@ namespace MediaBrowser.Server.Implementations.Sync
return false;
}
+
+ private IEnumerable<BaseItem> GetItemsForSync(IEnumerable<string> itemIds)
+ {
+ return itemIds.SelectMany(GetItemsForSync).DistinctBy(i => i.Id);
+ }
+
+ private IEnumerable<BaseItem> GetItemsForSync(string id)
+ {
+ var item = _libraryManager.GetItemById(id);
+
+ if (item == null)
+ {
+ throw new ArgumentException("Item with Id " + id + " not found.");
+ }
+
+ if (!SupportsSync(item))
+ {
+ throw new ArgumentException("Item with Id " + id + " does not support sync.");
+ }
+
+ return GetItemsForSync(item);
+ }
+
+ private IEnumerable<BaseItem> GetItemsForSync(BaseItem item)
+ {
+ return new[] { item };
+ }
+
+ private string GetDefaultName(BaseItem item)
+ {
+ return item.Name;
+ }
}
}
diff --git a/MediaBrowser.Server.Implementations/Sync/SyncRepository.cs b/MediaBrowser.Server.Implementations/Sync/SyncRepository.cs
new file mode 100644
index 000000000..bb22e992b
--- /dev/null
+++ b/MediaBrowser.Server.Implementations/Sync/SyncRepository.cs
@@ -0,0 +1,429 @@
+using MediaBrowser.Controller;
+using MediaBrowser.Controller.Sync;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Querying;
+using MediaBrowser.Model.Sync;
+using MediaBrowser.Server.Implementations.Persistence;
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Server.Implementations.Sync
+{
+ public class SyncRepository : ISyncRepository
+ {
+ private IDbConnection _connection;
+ private readonly ILogger _logger;
+ private readonly SemaphoreSlim _writeLock = new SemaphoreSlim(1, 1);
+ private readonly IServerApplicationPaths _appPaths;
+ private readonly CultureInfo _usCulture = new CultureInfo("en-US");
+
+ private IDbCommand _saveJobCommand;
+ private IDbCommand _saveJobItemCommand;
+
+ public SyncRepository(ILogger logger, IServerApplicationPaths appPaths)
+ {
+ _logger = logger;
+ _appPaths = appPaths;
+ }
+
+ public async Task Initialize()
+ {
+ var dbFile = Path.Combine(_appPaths.DataPath, "sync.db");
+
+ _connection = await SqliteExtensions.ConnectToDb(dbFile, _logger).ConfigureAwait(false);
+
+ string[] queries = {
+
+ "create table if not exists SyncJobs (Id GUID PRIMARY KEY, TargetId TEXT NOT NULL, Name TEXT NOT NULL, Quality TEXT NOT NULL, Status TEXT NOT NULL, Progress FLOAT, UserId TEXT NOT NULL, ItemIds TEXT NOT NULL, UnwatchedOnly BIT, SyncLimit BigInt, LimitType TEXT, IsDynamic BIT, DateCreated DateTime, DateLastModified DateTime, ItemCount int)",
+ "create index if not exists idx_SyncJobs on SyncJobs(Id)",
+
+ "create table if not exists SyncJobItems (Id GUID PRIMARY KEY, ItemId TEXT, JobId TEXT, OutputPath TEXT, Status TEXT, TargetId TEXT)",
+ "create index if not exists idx_SyncJobItems on SyncJobs(Id)",
+
+ //pragmas
+ "pragma temp_store = memory",
+
+ "pragma shrink_memory"
+ };
+
+ _connection.RunQueries(queries, _logger);
+
+ PrepareStatements();
+ }
+
+ private void PrepareStatements()
+ {
+ _saveJobCommand = _connection.CreateCommand();
+ _saveJobCommand.CommandText = "replace into SyncJobs (Id, TargetId, Name, Quality, Status, Progress, UserId, ItemIds, UnwatchedOnly, SyncLimit, LimitType, IsDynamic, DateCreated, DateLastModified, ItemCount) values (@Id, @TargetId, @Name, @Quality, @Status, @Progress, @UserId, @ItemIds, @UnwatchedOnly, @SyncLimit, @LimitType, @IsDynamic, @DateCreated, @DateLastModified, @ItemCount)";
+
+ _saveJobCommand.Parameters.Add(_saveJobCommand, "@Id");
+ _saveJobCommand.Parameters.Add(_saveJobCommand, "@TargetId");
+ _saveJobCommand.Parameters.Add(_saveJobCommand, "@Name");
+ _saveJobCommand.Parameters.Add(_saveJobCommand, "@Quality");
+ _saveJobCommand.Parameters.Add(_saveJobCommand, "@Status");
+ _saveJobCommand.Parameters.Add(_saveJobCommand, "@Progress");
+ _saveJobCommand.Parameters.Add(_saveJobCommand, "@UserId");
+ _saveJobCommand.Parameters.Add(_saveJobCommand, "@ItemIds");
+ _saveJobCommand.Parameters.Add(_saveJobCommand, "@UnwatchedOnly");
+ _saveJobCommand.Parameters.Add(_saveJobCommand, "@SyncLimit");
+ _saveJobCommand.Parameters.Add(_saveJobCommand, "@LimitType");
+ _saveJobCommand.Parameters.Add(_saveJobCommand, "@IsDynamic");
+ _saveJobCommand.Parameters.Add(_saveJobCommand, "@DateCreated");
+ _saveJobCommand.Parameters.Add(_saveJobCommand, "@DateLastModified");
+ _saveJobCommand.Parameters.Add(_saveJobCommand, "@ItemCount");
+
+ _saveJobItemCommand = _connection.CreateCommand();
+ _saveJobItemCommand.CommandText = "replace into SyncJobItems (Id, ItemId, JobId, OutputPath, Status, TargetId) values (@Id, @ItemId, @JobId, @OutputPath, @Status, @TargetId)";
+
+ _saveJobItemCommand.Parameters.Add(_saveJobCommand, "@Id");
+ _saveJobItemCommand.Parameters.Add(_saveJobCommand, "@ItemId");
+ _saveJobItemCommand.Parameters.Add(_saveJobCommand, "@JobId");
+ _saveJobItemCommand.Parameters.Add(_saveJobCommand, "@OutputPath");
+ _saveJobItemCommand.Parameters.Add(_saveJobCommand, "@Status");
+ }
+
+ private const string BaseJobSelectText = "select Id, TargetId, Name, Quality, Status, Progress, UserId, ItemIds, UnwatchedOnly, SyncLimit, LimitType, IsDynamic, DateCreated, DateLastModified, ItemCount from SyncJobs";
+ private const string BaseJobItemSelectText = "select Id, ItemId, JobId, OutputPath, Status, TargetId from SyncJobItems";
+
+ public SyncJob GetJob(string id)
+ {
+ if (string.IsNullOrEmpty(id))
+ {
+ throw new ArgumentNullException("id");
+ }
+
+ var guid = new Guid(id);
+
+ using (var cmd = _connection.CreateCommand())
+ {
+ cmd.CommandText = BaseJobSelectText + " where Id=@Id";
+
+ cmd.Parameters.Add(cmd, "@Id", DbType.Guid).Value = guid;
+
+ using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult | CommandBehavior.SingleRow))
+ {
+ if (reader.Read())
+ {
+ return GetJob(reader);
+ }
+ }
+ }
+
+ return null;
+ }
+
+ private SyncJob GetJob(IDataReader reader)
+ {
+ var info = new SyncJob
+ {
+ Id = reader.GetGuid(0).ToString("N"),
+ TargetId = reader.GetString(1),
+ Name = reader.GetString(2)
+ };
+
+ if (!reader.IsDBNull(3))
+ {
+ info.Quality = (SyncQuality)Enum.Parse(typeof(SyncQuality), reader.GetString(3), true);
+ }
+
+ if (!reader.IsDBNull(4))
+ {
+ info.Status = (SyncJobStatus)Enum.Parse(typeof(SyncJobStatus), reader.GetString(4), true);
+ }
+
+ if (!reader.IsDBNull(5))
+ {
+ info.Progress = reader.GetDouble(5);
+ }
+
+ if (!reader.IsDBNull(6))
+ {
+ info.UserId = reader.GetString(6);
+ }
+
+ if (!reader.IsDBNull(7))
+ {
+ info.RequestedItemIds = reader.GetString(7).Split(',').ToList();
+ }
+
+ if (!reader.IsDBNull(8))
+ {
+ info.UnwatchedOnly = reader.GetBoolean(8);
+ }
+
+ if (!reader.IsDBNull(9))
+ {
+ info.Limit = reader.GetInt64(9);
+ }
+
+ if (!reader.IsDBNull(10))
+ {
+ info.LimitType = (SyncLimitType)Enum.Parse(typeof(SyncLimitType), reader.GetString(10), true);
+ }
+
+ info.IsDynamic = reader.GetBoolean(11);
+ info.DateCreated = reader.GetDateTime(12).ToUniversalTime();
+ info.DateLastModified = reader.GetDateTime(13).ToUniversalTime();
+ info.ItemCount = reader.GetInt32(14);
+
+ return info;
+ }
+
+ public Task Create(SyncJob job)
+ {
+ return Update(job);
+ }
+
+ public async Task Update(SyncJob job)
+ {
+ if (job == null)
+ {
+ throw new ArgumentNullException("job");
+ }
+
+ await _writeLock.WaitAsync().ConfigureAwait(false);
+
+ IDbTransaction transaction = null;
+
+ try
+ {
+ transaction = _connection.BeginTransaction();
+
+ var index = 0;
+
+ _saveJobCommand.GetParameter(index++).Value = new Guid(job.Id);
+ _saveJobCommand.GetParameter(index++).Value = job.TargetId;
+ _saveJobCommand.GetParameter(index++).Value = job.Name;
+ _saveJobCommand.GetParameter(index++).Value = job.Quality;
+ _saveJobCommand.GetParameter(index++).Value = job.Status;
+ _saveJobCommand.GetParameter(index++).Value = job.Progress;
+ _saveJobCommand.GetParameter(index++).Value = job.UserId;
+ _saveJobCommand.GetParameter(index++).Value = string.Join(",", job.RequestedItemIds.ToArray());
+ _saveJobCommand.GetParameter(index++).Value = job.UnwatchedOnly;
+ _saveJobCommand.GetParameter(index++).Value = job.Limit;
+ _saveJobCommand.GetParameter(index++).Value = job.LimitType;
+ _saveJobCommand.GetParameter(index++).Value = job.IsDynamic;
+ _saveJobCommand.GetParameter(index++).Value = job.DateCreated;
+ _saveJobCommand.GetParameter(index++).Value = job.DateLastModified;
+ _saveJobCommand.GetParameter(index++).Value = job.ItemCount;
+
+ _saveJobCommand.Transaction = transaction;
+
+ _saveJobCommand.ExecuteNonQuery();
+
+ transaction.Commit();
+ }
+ catch (OperationCanceledException)
+ {
+ if (transaction != null)
+ {
+ transaction.Rollback();
+ }
+
+ throw;
+ }
+ catch (Exception e)
+ {
+ _logger.ErrorException("Failed to save record:", e);
+
+ if (transaction != null)
+ {
+ transaction.Rollback();
+ }
+
+ throw;
+ }
+ finally
+ {
+ if (transaction != null)
+ {
+ transaction.Dispose();
+ }
+
+ _writeLock.Release();
+ }
+ }
+
+ public QueryResult<SyncJob> GetJobs(SyncJobQuery query)
+ {
+ if (query == null)
+ {
+ throw new ArgumentNullException("query");
+ }
+
+ using (var cmd = _connection.CreateCommand())
+ {
+ cmd.CommandText = BaseJobSelectText;
+
+ var whereClauses = new List<string>();
+
+ var startIndex = query.StartIndex ?? 0;
+
+ if (startIndex > 0)
+ {
+ whereClauses.Add(string.Format("Id NOT IN (SELECT Id FROM SyncJobs ORDER BY DateLastModified DESC LIMIT {0})",
+ startIndex.ToString(_usCulture)));
+ }
+
+ if (whereClauses.Count > 0)
+ {
+ cmd.CommandText += " where " + string.Join(" AND ", whereClauses.ToArray());
+ }
+
+ cmd.CommandText += " ORDER BY DateLastModified DESC";
+
+ if (query.Limit.HasValue)
+ {
+ cmd.CommandText += " LIMIT " + query.Limit.Value.ToString(_usCulture);
+ }
+
+ cmd.CommandText += "; select count (Id) from SyncJobs";
+
+ var list = new List<SyncJob>();
+ var count = 0;
+
+ using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess))
+ {
+ while (reader.Read())
+ {
+ list.Add(GetJob(reader));
+ }
+
+ if (reader.NextResult() && reader.Read())
+ {
+ count = reader.GetInt32(0);
+ }
+ }
+
+ return new QueryResult<SyncJob>()
+ {
+ Items = list.ToArray(),
+ TotalRecordCount = count
+ };
+ }
+ }
+
+ public SyncJobItem GetJobItem(string id)
+ {
+ if (string.IsNullOrEmpty(id))
+ {
+ throw new ArgumentNullException("id");
+ }
+
+ var guid = new Guid(id);
+
+ using (var cmd = _connection.CreateCommand())
+ {
+ cmd.CommandText = BaseJobItemSelectText + " where Id=@Id";
+
+ cmd.Parameters.Add(cmd, "@Id", DbType.Guid).Value = guid;
+
+ using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult | CommandBehavior.SingleRow))
+ {
+ if (reader.Read())
+ {
+ return GetSyncJobItem(reader);
+ }
+ }
+ }
+
+ return null;
+ }
+
+ public Task Create(SyncJobItem jobItem)
+ {
+ return Update(jobItem);
+ }
+
+ public async Task Update(SyncJobItem jobItem)
+ {
+ if (jobItem == null)
+ {
+ throw new ArgumentNullException("jobItem");
+ }
+
+ await _writeLock.WaitAsync().ConfigureAwait(false);
+
+ IDbTransaction transaction = null;
+
+ try
+ {
+ transaction = _connection.BeginTransaction();
+
+ var index = 0;
+
+ _saveJobItemCommand.GetParameter(index++).Value = new Guid(jobItem.Id);
+ _saveJobItemCommand.GetParameter(index++).Value = jobItem.ItemId;
+ _saveJobItemCommand.GetParameter(index++).Value = jobItem.JobId;
+ _saveJobItemCommand.GetParameter(index++).Value = jobItem.OutputPath;
+ _saveJobItemCommand.GetParameter(index++).Value = jobItem.Status;
+ _saveJobItemCommand.GetParameter(index++).Value = jobItem.TargetId;
+
+ _saveJobItemCommand.Transaction = transaction;
+
+ _saveJobItemCommand.ExecuteNonQuery();
+
+ transaction.Commit();
+ }
+ catch (OperationCanceledException)
+ {
+ if (transaction != null)
+ {
+ transaction.Rollback();
+ }
+
+ throw;
+ }
+ catch (Exception e)
+ {
+ _logger.ErrorException("Failed to save record:", e);
+
+ if (transaction != null)
+ {
+ transaction.Rollback();
+ }
+
+ throw;
+ }
+ finally
+ {
+ if (transaction != null)
+ {
+ transaction.Dispose();
+ }
+
+ _writeLock.Release();
+ }
+ }
+
+ private SyncJobItem GetSyncJobItem(IDataReader reader)
+ {
+ var info = new SyncJobItem
+ {
+ Id = reader.GetGuid(0).ToString("N"),
+ ItemId = reader.GetString(1),
+ JobId = reader.GetString(2)
+ };
+
+ if (!reader.IsDBNull(3))
+ {
+ info.OutputPath = reader.GetString(3);
+ }
+
+ if (!reader.IsDBNull(4))
+ {
+ info.Status = (SyncJobStatus)Enum.Parse(typeof(SyncJobStatus), reader.GetString(4), true);
+ }
+
+ info.TargetId = reader.GetString(5);
+
+ return info;
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/packages.config b/MediaBrowser.Server.Implementations/packages.config
index 907500ae3..18739ddb3 100644
--- a/MediaBrowser.Server.Implementations/packages.config
+++ b/MediaBrowser.Server.Implementations/packages.config
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
- <package id="Mono.Nat" version="1.2.20.0" targetFramework="net45" />
+ <package id="Mono.Nat" version="1.2.21.0" targetFramework="net45" />
<package id="morelinq" version="1.0.16006" targetFramework="net45" />
<package id="System.Data.SQLite.Core" version="1.0.91.3" targetFramework="net45" />
</packages> \ No newline at end of file
diff --git a/MediaBrowser.ServerApplication/ApplicationHost.cs b/MediaBrowser.ServerApplication/ApplicationHost.cs
index 55843bbdb..5c086c8cc 100644
--- a/MediaBrowser.ServerApplication/ApplicationHost.cs
+++ b/MediaBrowser.ServerApplication/ApplicationHost.cs
@@ -209,6 +209,7 @@ namespace MediaBrowser.ServerApplication
private IUserViewManager UserViewManager { get; set; }
private IAuthenticationRepository AuthenticationRepository { get; set; }
+ private ISyncRepository SyncRepository { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="ApplicationHost" /> class.
@@ -525,6 +526,9 @@ namespace MediaBrowser.ServerApplication
AuthenticationRepository = await GetAuthenticationRepository().ConfigureAwait(false);
RegisterSingleInstance(AuthenticationRepository);
+ SyncRepository = await GetSyncRepository().ConfigureAwait(false);
+ RegisterSingleInstance(SyncRepository);
+
UserManager = new UserManager(LogManager.GetLogger("UserManager"), ServerConfigurationManager, UserRepository, XmlSerializer);
RegisterSingleInstance(UserManager);
@@ -561,7 +565,7 @@ namespace MediaBrowser.ServerApplication
ImageProcessor = new ImageProcessor(LogManager.GetLogger("ImageProcessor"), ServerConfigurationManager.ApplicationPaths, FileSystemManager, JsonSerializer, MediaEncoder);
RegisterSingleInstance(ImageProcessor);
- SyncManager = new SyncManager();
+ SyncManager = new SyncManager(LibraryManager, SyncRepository, ImageProcessor, LogManager.GetLogger("SyncManager"));
RegisterSingleInstance(SyncManager);
DtoService = new DtoService(Logger, LibraryManager, UserDataManager, ItemRepository, ImageProcessor, ServerConfigurationManager, FileSystemManager, ProviderManager, () => ChannelManager, SyncManager);
@@ -706,6 +710,15 @@ namespace MediaBrowser.ServerApplication
return repo;
}
+ private async Task<ISyncRepository> GetSyncRepository()
+ {
+ var repo = new SyncRepository(LogManager.GetLogger("SyncRepository"), ServerConfigurationManager.ApplicationPaths);
+
+ await repo.Initialize().ConfigureAwait(false);
+
+ return repo;
+ }
+
/// <summary>
/// Configures the repositories.
/// </summary>
diff --git a/MediaBrowser.ServerApplication/FFMpeg/FFMpegDownloader.cs b/MediaBrowser.ServerApplication/FFMpeg/FFMpegDownloader.cs
index a93ed9196..2642ffde7 100644
--- a/MediaBrowser.ServerApplication/FFMpeg/FFMpegDownloader.cs
+++ b/MediaBrowser.ServerApplication/FFMpeg/FFMpegDownloader.cs
@@ -101,7 +101,7 @@ namespace MediaBrowser.ServerApplication.FFMpeg
{
EncoderPath = encoder,
ProbePath = probe,
- Version = Path.GetFileNameWithoutExtension(Path.GetDirectoryName(probe))
+ Version = Path.GetFileName(Path.GetDirectoryName(probe))
};
}
}
diff --git a/MediaBrowser.Tests/Resolvers/MovieResolverTests.cs b/MediaBrowser.Tests/Resolvers/MovieResolverTests.cs
index 768e4ee5c..af24609e8 100644
--- a/MediaBrowser.Tests/Resolvers/MovieResolverTests.cs
+++ b/MediaBrowser.Tests/Resolvers/MovieResolverTests.cs
@@ -38,6 +38,8 @@ namespace MediaBrowser.Tests.Resolvers
public void TestMultiPartFolders()
{
Assert.IsFalse(EntityResolutionHelper.IsMultiPartFolder(@"blah blah"));
+ Assert.IsFalse(EntityResolutionHelper.IsMultiPartFolder(@"d:\\music\weezer\\03 Pinkerton"));
+ Assert.IsFalse(EntityResolutionHelper.IsMultiPartFolder(@"d:\\music\\michael jackson\\Bad (2012 Remaster)"));
Assert.IsTrue(EntityResolutionHelper.IsMultiPartFolder(@"blah blah - cd1"));
Assert.IsTrue(EntityResolutionHelper.IsMultiPartFolder(@"blah blah - disc1"));
diff --git a/MediaBrowser.WebDashboard/Api/DashboardService.cs b/MediaBrowser.WebDashboard/Api/DashboardService.cs
index 92487959d..9235beacf 100644
--- a/MediaBrowser.WebDashboard/Api/DashboardService.cs
+++ b/MediaBrowser.WebDashboard/Api/DashboardService.cs
@@ -548,6 +548,7 @@ namespace MediaBrowser.WebDashboard.Api
"channelsettings.js",
"dashboardgeneral.js",
"dashboardpage.js",
+ "dashboardsync.js",
"directorybrowser.js",
"dlnaprofile.js",
"dlnaprofiles.js",
@@ -676,6 +677,7 @@ namespace MediaBrowser.WebDashboard.Api
"librarybrowser.css",
"detailtable.css",
"posteritem.css",
+ "card.css",
"tileitem.css",
"metadataeditor.css",
"notifications.css",
diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj
index 25cff11cf..73174dacf 100644
--- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj
+++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj
@@ -287,9 +287,15 @@
<Content Include="dashboard-ui\css\nowplaying.css">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
+ <Content Include="dashboard-ui\css\card.css">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
<Content Include="dashboard-ui\dashboardgeneral.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
+ <Content Include="dashboard-ui\dashboardsync.html">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
<Content Include="dashboard-ui\dlnaprofile.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -602,6 +608,9 @@
<Content Include="dashboard-ui\scripts\dashboardgeneral.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
+ <Content Include="dashboard-ui\scripts\dashboardsync.js">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
<Content Include="dashboard-ui\scripts\dlnaprofile.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -2074,6 +2083,9 @@
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
+ <None Include="dashboard-ui\css\fonts\RobotoBold.woff">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </None>
<None Include="dashboard-ui\css\fonts\OpenSans-ExtraBold.woff">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
diff --git a/MediaBrowser.XbmcMetadata/Images/XbmcImageSaver.cs b/MediaBrowser.XbmcMetadata/Images/XbmcImageSaver.cs
index a45a44904..6071db6b5 100644
--- a/MediaBrowser.XbmcMetadata/Images/XbmcImageSaver.cs
+++ b/MediaBrowser.XbmcMetadata/Images/XbmcImageSaver.cs
@@ -88,7 +88,7 @@ namespace MediaBrowser.XbmcMetadata.Images
if (item is Episode)
{
var seasonFolder = Path.GetDirectoryName(item.Path);
-
+
var imageFilename = Path.GetFileNameWithoutExtension(item.Path) + "-thumb" + extension;
return new[] { Path.Combine(seasonFolder, imageFilename) };
diff --git a/MediaBrowser.XbmcMetadata/Providers/BaseVideoNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/BaseVideoNfoProvider.cs
index d51c44ad4..c4edfb461 100644
--- a/MediaBrowser.XbmcMetadata/Providers/BaseVideoNfoProvider.cs
+++ b/MediaBrowser.XbmcMetadata/Providers/BaseVideoNfoProvider.cs
@@ -35,18 +35,18 @@ namespace MediaBrowser.XbmcMetadata.Providers
protected override FileSystemInfo GetXmlFile(ItemInfo info, IDirectoryService directoryService)
{
- var path = GetMovieSavePath(info);
+ var path = GetMovieSavePath(info, FileSystem);
return directoryService.GetFile(path);
}
- public static string GetMovieSavePath(ItemInfo item)
+ public static string GetMovieSavePath(ItemInfo item, IFileSystem fileSystem)
{
if (Directory.Exists(item.Path))
{
var path = item.Path;
- return Path.Combine(path, Path.GetFileNameWithoutExtension(path) + ".nfo");
+ return Path.Combine(path, fileSystem.GetFileNameWithoutExtension(path) + ".nfo");
}
return Path.ChangeExtension(item.Path, ".nfo");
diff --git a/MediaBrowser.XbmcMetadata/Savers/MovieNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/MovieNfoSaver.cs
index b23473295..210c743bf 100644
--- a/MediaBrowser.XbmcMetadata/Savers/MovieNfoSaver.cs
+++ b/MediaBrowser.XbmcMetadata/Savers/MovieNfoSaver.cs
@@ -21,10 +21,10 @@ namespace MediaBrowser.XbmcMetadata.Savers
public override string GetSavePath(IHasMetadata item)
{
- return GetMovieSavePath(item);
+ return GetMovieSavePath(item, FileSystem);
}
- public static string GetMovieSavePath(IHasMetadata item)
+ public static string GetMovieSavePath(IHasMetadata item, IFileSystem fileSystem)
{
var video = (Video)item;
@@ -32,7 +32,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
{
var path = item.ContainingFolderPath;
- return Path.Combine(path, Path.GetFileNameWithoutExtension(path) + ".nfo");
+ return Path.Combine(path, fileSystem.GetFileNameWithoutExtension(path) + ".nfo");
}
return Path.ChangeExtension(item.Path, ".nfo");