Pārlūkot izejas kodu

rework server sync

Luke Pulverenti 10 gadi atpakaļ
vecāks
revīzija
65fb01bbe2

+ 36 - 17
MediaBrowser.Controller/Entities/UserViewBuilder.cs

@@ -50,8 +50,8 @@ namespace MediaBrowser.Controller.Entities
         {
             var user = query.User;
 
-            if (query.IncludeItemTypes != null && 
-                query.IncludeItemTypes.Length == 1 && 
+            if (query.IncludeItemTypes != null &&
+                query.IncludeItemTypes.Length == 1 &&
                 string.Equals(query.IncludeItemTypes[0], "Playlist", StringComparison.OrdinalIgnoreCase))
             {
                 if (!string.Equals(viewType, CollectionType.Playlists, StringComparison.OrdinalIgnoreCase))
@@ -117,9 +117,7 @@ namespace MediaBrowser.Controller.Entities
 
                 case CollectionType.LiveTv:
                     {
-                        var result = await GetLiveTvFolders(user).ConfigureAwait(false);
-
-                        return GetResult(result, queryParent, query);
+                        return await GetLiveTvView(queryParent, user, query).ConfigureAwait(false);
                     }
 
                 case CollectionType.Books:
@@ -215,6 +213,9 @@ namespace MediaBrowser.Controller.Entities
                 case SpecialFolder.MusicLatest:
                     return GetMusicLatest(queryParent, user, query);
 
+                case SpecialFolder.MusicPlaylists:
+                    return await GetMusicPlaylists(queryParent, user, query).ConfigureAwait(false);
+
                 case SpecialFolder.MusicAlbums:
                     return GetMusicAlbums(queryParent, user, query);
 
@@ -277,12 +278,13 @@ namespace MediaBrowser.Controller.Entities
             var list = new List<BaseItem>();
 
             list.Add(await GetUserView(SpecialFolder.MusicLatest, user, "0", parent).ConfigureAwait(false));
-            list.Add(await GetUserView(SpecialFolder.MusicAlbums, user, "1", parent).ConfigureAwait(false));
-            list.Add(await GetUserView(SpecialFolder.MusicAlbumArtists, user, "2", parent).ConfigureAwait(false));
-            //list.Add(await GetUserView(SpecialFolder.MusicArtists, user, "3", parent).ConfigureAwait(false));
-            list.Add(await GetUserView(SpecialFolder.MusicSongs, user, "4", parent).ConfigureAwait(false));
-            list.Add(await GetUserView(SpecialFolder.MusicGenres, user, "5", parent).ConfigureAwait(false));
-            list.Add(await GetUserView(SpecialFolder.MusicFavorites, user, "6", parent).ConfigureAwait(false));
+            list.Add(await GetUserView(SpecialFolder.MusicPlaylists, user, "1", parent).ConfigureAwait(false));
+            list.Add(await GetUserView(SpecialFolder.MusicAlbums, user, "2", parent).ConfigureAwait(false));
+            list.Add(await GetUserView(SpecialFolder.MusicAlbumArtists, user, "3", parent).ConfigureAwait(false));
+            //list.Add(await GetUserView(SpecialFolder.MusicArtists, user, "4", parent).ConfigureAwait(false));
+            list.Add(await GetUserView(SpecialFolder.MusicSongs, user, "5", parent).ConfigureAwait(false));
+            list.Add(await GetUserView(SpecialFolder.MusicGenres, user, "6", parent).ConfigureAwait(false));
+            list.Add(await GetUserView(SpecialFolder.MusicFavorites, user, "7", parent).ConfigureAwait(false));
 
             return GetResult(list, parent, query);
         }
@@ -423,6 +425,14 @@ namespace MediaBrowser.Controller.Entities
             return GetResult(artists, parent, query);
         }
 
+        private Task<QueryResult<BaseItem>> GetMusicPlaylists(Folder parent, User user, InternalItemsQuery query)
+        {
+            query.IncludeItemTypes = new[] { "Playlist" };
+            query.Recursive = true;
+
+            return parent.GetItems(query);
+        }
+
         private QueryResult<BaseItem> GetMusicAlbums(Folder parent, User user, InternalItemsQuery query)
         {
             var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Music, CollectionType.MusicVideos }, i => (i is MusicAlbum) && FilterItem(i, query));
@@ -1769,17 +1779,26 @@ namespace MediaBrowser.Controller.Entities
             return parent.GetRecursiveChildren(user, filter);
         }
 
-        private async Task<IEnumerable<BaseItem>> GetLiveTvFolders(User user)
+        private async Task<QueryResult<BaseItem>> GetLiveTvView(Folder queryParent, User user, InternalItemsQuery query)
         {
-            var list = new List<BaseItem>();
+            if (query.Recursive)
+            {
+                return await _liveTvManager.GetInternalRecordings(new RecordingQuery
+                {
+                    IsInProgress = false,
+                    Status = RecordingStatus.Completed,
+                    UserId = user.Id.ToString("N")
 
-            var parent = user.RootFolder;
+                }, CancellationToken.None).ConfigureAwait(false);
+            }
+
+            var list = new List<BaseItem>();
 
             //list.Add(await GetUserSubView(SpecialFolder.LiveTvNowPlaying, user, "0", parent).ConfigureAwait(false));
-            list.Add(await GetUserView(SpecialFolder.LiveTvChannels, user, string.Empty, parent).ConfigureAwait(false));
-            list.Add(await GetUserView(SpecialFolder.LiveTvRecordingGroups, user, string.Empty, parent).ConfigureAwait(false));
+            list.Add(await GetUserView(SpecialFolder.LiveTvChannels, user, string.Empty, user.RootFolder).ConfigureAwait(false));
+            list.Add(await GetUserView(SpecialFolder.LiveTvRecordingGroups, user, string.Empty, user.RootFolder).ConfigureAwait(false));
 
-            return list;
+            return GetResult(list, queryParent, query);
         }
 
         private async Task<UserView> GetUserView(string name, string type, User user, string sortName, BaseItem parent)

+ 4 - 0
MediaBrowser.Controller/MediaBrowser.Controller.csproj

@@ -52,6 +52,10 @@
       <SpecificVersion>False</SpecificVersion>
       <HintPath>..\packages\morelinq.1.1.0\lib\net35\MoreLinq.dll</HintPath>
     </Reference>
+    <Reference Include="Patterns.IO, Version=1.0.5580.36861, Culture=neutral, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>..\packages\Patterns.IO.1.0.0.3\lib\portable-net45+sl4+wp71+win8+wpa81\Patterns.IO.dll</HintPath>
+    </Reference>
     <Reference Include="System" />
     <Reference Include="System.Core" />
     <Reference Include="System.Data" />

+ 2 - 2
MediaBrowser.Controller/Sync/IHasDynamicAccess.cs

@@ -9,10 +9,10 @@ namespace MediaBrowser.Controller.Sync
         /// <summary>
         /// Gets the synced file information.
         /// </summary>
-        /// <param name="remotePath">The remote path.</param>
+        /// <param name="id">The identifier.</param>
         /// <param name="target">The target.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task&lt;SyncedFileInfo&gt;.</returns>
-        Task<SyncedFileInfo> GetSyncedFileInfo(string remotePath, SyncTarget target, CancellationToken cancellationToken);
+        Task<SyncedFileInfo> GetSyncedFileInfo(string id, SyncTarget target, CancellationToken cancellationToken);
     }
 }

+ 14 - 20
MediaBrowser.Controller/Sync/IServerSyncProvider.cs

@@ -1,6 +1,7 @@
-using MediaBrowser.Model.Sync;
+using MediaBrowser.Model.Querying;
+using MediaBrowser.Model.Sync;
+using Patterns.IO;
 using System;
-using System.Collections.Generic;
 using System.IO;
 using System.Threading;
 using System.Threading.Tasks;
@@ -13,46 +14,39 @@ namespace MediaBrowser.Controller.Sync
         /// Transfers the file.
         /// </summary>
         /// <param name="stream">The stream.</param>
-        /// <param name="remotePath">The remote path.</param>
+        /// <param name="pathParts">The path parts.</param>
         /// <param name="target">The target.</param>
         /// <param name="progress">The progress.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task.</returns>
-        Task<SyncedFileInfo> SendFile(Stream stream, string remotePath, SyncTarget target, IProgress<double> progress, CancellationToken cancellationToken);
+        Task<SyncedFileInfo> SendFile(Stream stream, string[] pathParts, SyncTarget target, IProgress<double> progress, CancellationToken cancellationToken);
 
         /// <summary>
         /// Deletes the file.
         /// </summary>
-        /// <param name="path">The path.</param>
+        /// <param name="id">The identifier.</param>
         /// <param name="target">The target.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task.</returns>
-        Task DeleteFile(string path, SyncTarget target, CancellationToken cancellationToken);
+        Task DeleteFile(string id, SyncTarget target, CancellationToken cancellationToken);
 
         /// <summary>
         /// Gets the file.
         /// </summary>
-        /// <param name="path">The path.</param>
+        /// <param name="id">The identifier.</param>
         /// <param name="target">The target.</param>
         /// <param name="progress">The progress.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task&lt;Stream&gt;.</returns>
-        Task<Stream> GetFile(string path, SyncTarget target, IProgress<double> progress, CancellationToken cancellationToken);
+        Task<Stream> GetFile(string id, SyncTarget target, IProgress<double> progress, CancellationToken cancellationToken);
 
         /// <summary>
-        /// Gets the full path.
+        /// Gets the files.
         /// </summary>
-        /// <param name="path">The path.</param>
+        /// <param name="query">The query.</param>
         /// <param name="target">The target.</param>
-        /// <returns>System.String.</returns>
-        string GetFullPath(IEnumerable<string> path, SyncTarget target);
-
-        /// <summary>
-        /// Gets the parent directory path.
-        /// </summary>
-        /// <param name="path">The path.</param>
-        /// <param name="target">The target.</param>
-        /// <returns>System.String.</returns>
-        string GetParentDirectoryPath(string path, SyncTarget target);
+        /// <param name="cancellationToken">The cancellation token.</param>
+        /// <returns>Task&lt;QueryResult&lt;FileMetadata&gt;&gt;.</returns>
+        Task<QueryResult<FileMetadata>> GetFiles(FileQuery query, SyncTarget target, CancellationToken cancellationToken);
     }
 }

+ 3 - 11
MediaBrowser.Controller/Sync/ISyncDataProvider.cs

@@ -7,20 +7,12 @@ namespace MediaBrowser.Controller.Sync
     public interface ISyncDataProvider
     {
         /// <summary>
-        /// Gets the server item ids.
+        /// Gets the local items.
         /// </summary>
         /// <param name="target">The target.</param>
         /// <param name="serverId">The server identifier.</param>
-        /// <returns>Task&lt;List&lt;System.String&gt;&gt;.</returns>
-        Task<List<string>> GetServerItemIds(SyncTarget target, string serverId);
-
-        /// <summary>
-        /// Gets the synchronize job item ids.
-        /// </summary>
-        /// <param name="target">The target.</param>
-        /// <param name="serverId">The server identifier.</param>
-        /// <returns>Task&lt;List&lt;System.String&gt;&gt;.</returns>
-        Task<List<string>> GetSyncJobItemIds(SyncTarget target, string serverId);
+        /// <returns>Task&lt;List&lt;LocalItem&gt;&gt;.</returns>
+        Task<List<LocalItem>> GetLocalItems(SyncTarget target, string serverId);
 
         /// <summary>
         /// Adds the or update.

+ 5 - 0
MediaBrowser.Controller/Sync/SyncedFileInfo.cs

@@ -20,6 +20,11 @@ namespace MediaBrowser.Controller.Sync
         /// </summary>
         /// <value>The required HTTP headers.</value>
         public Dictionary<string, string> RequiredHttpHeaders { get; set; }
+        /// <summary>
+        /// Gets or sets the identifier.
+        /// </summary>
+        /// <value>The identifier.</value>
+        public string Id { get; set; }
 
         public SyncedFileInfo()
         {

+ 1 - 0
MediaBrowser.Controller/packages.config

@@ -1,4 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
   <package id="morelinq" version="1.1.0" targetFramework="net45" />
+  <package id="Patterns.IO" version="1.0.0.3" targetFramework="net45" />
 </packages>

+ 8 - 4
MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs

@@ -732,7 +732,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
 
                 if (enableForceKill)
                 {
-                    process.Process .Kill();
+                    process.Process.Kill();
                 }
             }
             catch (Exception ex)
@@ -748,10 +748,14 @@ namespace MediaBrowser.MediaEncoding.Encoder
             {
                 proceses = _runningProcesses.ToList();
             }
+            _runningProcesses.Clear();
 
             foreach (var process in proceses)
             {
-                StopProcess(process, 500, true);
+                if (!process.HasExited)
+                {
+                    StopProcess(process, 500, true);
+                }
             }
         }
 
@@ -801,8 +805,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
                 lock (_mediaEncoder._runningProcesses)
                 {
                     _mediaEncoder._runningProcesses.Remove(this);
-                } 
-                
+                }
+
                 process.Dispose();
             }
         }

+ 1 - 0
MediaBrowser.Model/Entities/CollectionType.cs

@@ -61,6 +61,7 @@
         public const string MusicGenres = "MusicGenres";
         public const string MusicGenre = "MusicGenre";
         public const string MusicLatest = "MusicLatest";
+        public const string MusicPlaylists = "MusicPlaylists";
         public const string MusicSongs = "MusicSongs";
         public const string MusicFavorites = "MusicFavorites";
         public const string MusicFavoriteArtists = "MusicFavoriteArtists";

+ 3 - 0
MediaBrowser.Model/Session/ClientCapabilities.cs

@@ -21,6 +21,9 @@ namespace MediaBrowser.Model.Session
         public DeviceProfile DeviceProfile { get; set; }
         public List<string> SupportedLiveMediaTypes { get; set; }
 
+        public string AppUrl { get; set; }
+        public string AppImageUrl { get; set; }
+
         public ClientCapabilities()
         {
             PlayableMediaTypes = new List<string>();

+ 5 - 0
MediaBrowser.Model/Sync/LocalItem.cs

@@ -26,6 +26,11 @@ namespace MediaBrowser.Model.Sync
         /// <value>The unique identifier.</value>
         public string Id { get; set; }
         /// <summary>
+        /// Gets or sets the file identifier.
+        /// </summary>
+        /// <value>The file identifier.</value>
+        public string FileId { get; set; }
+        /// <summary>
         /// Gets or sets the item identifier.
         /// </summary>
         /// <value>The item identifier.</value>

+ 1 - 0
MediaBrowser.Server.Implementations/Localization/Server/server.json

@@ -964,6 +964,7 @@
     "ViewTypeMovieFavorites": "Favorites",
     "ViewTypeMovieGenres": "Genres",
     "ViewTypeMusicLatest": "Latest",
+    "ViewTypeMusicPlaylists": "Playlists",
     "ViewTypeMusicAlbums": "Albums",
     "ViewTypeMusicAlbumArtists": "Album Artists",
     "HeaderOtherDisplaySettings": "Display Settings",

+ 4 - 0
MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj

@@ -57,6 +57,10 @@
       <SpecificVersion>False</SpecificVersion>
       <HintPath>..\packages\morelinq.1.1.0\lib\net35\MoreLinq.dll</HintPath>
     </Reference>
+    <Reference Include="Patterns.IO, Version=1.0.5580.36861, Culture=neutral, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>..\packages\Patterns.IO.1.0.0.3\lib\portable-net45+sl4+wp71+win8+wpa81\Patterns.IO.dll</HintPath>
+    </Reference>
     <Reference Include="Patterns.Logging">
       <HintPath>..\packages\Patterns.Logging.1.0.0.2\lib\portable-net45+sl4+wp71+win8+wpa81\Patterns.Logging.dll</HintPath>
       <Private>True</Private>

+ 44 - 19
MediaBrowser.Server.Implementations/Sync/MediaSync.cs

@@ -1,5 +1,4 @@
-using System.Globalization;
-using MediaBrowser.Common.Configuration;
+using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.IO;
 using MediaBrowser.Common.Progress;
 using MediaBrowser.Controller;
@@ -12,12 +11,14 @@ using MediaBrowser.Model.MediaInfo;
 using MediaBrowser.Model.Sync;
 using System;
 using System.Collections.Generic;
+using System.Globalization;
 using System.IO;
 using System.Linq;
 using System.Security.Cryptography;
 using System.Text;
 using System.Threading;
 using System.Threading.Tasks;
+using Patterns.IO;
 
 namespace MediaBrowser.Server.Implementations.Sync
 {
@@ -29,6 +30,9 @@ namespace MediaBrowser.Server.Implementations.Sync
         private readonly IFileSystem _fileSystem;
         private readonly IConfigurationManager _config;
 
+        public const string PathSeparatorString = "/";
+        public const char PathSeparatorChar = '/';
+
         public MediaSync(ILogger logger, ISyncManager syncManager, IServerApplicationHost appHost, IFileSystem fileSystem, IConfigurationManager config)
         {
             _logger = logger;
@@ -71,7 +75,24 @@ namespace MediaBrowser.Server.Implementations.Sync
             SyncTarget target,
             CancellationToken cancellationToken)
         {
-            var jobItemIds = await dataProvider.GetSyncJobItemIds(target, serverId).ConfigureAwait(false);
+            var localItems = await dataProvider.GetLocalItems(target, serverId).ConfigureAwait(false);
+            var remoteFiles = await provider.GetFiles(new FileQuery(), target, cancellationToken).ConfigureAwait(false);
+            var remoteIds = remoteFiles.Items.Select(i => i.Id).ToList();
+
+            var jobItemIds = new List<string>();
+
+            foreach (var localItem in localItems)
+            {
+                // TODO: Remove this after a while
+                if (string.IsNullOrWhiteSpace(localItem.FileId))
+                {
+                    jobItemIds.Add(localItem.SyncJobItemId);
+                }
+                else if (remoteIds.Contains(localItem.FileId, StringComparer.OrdinalIgnoreCase))
+                {
+                    jobItemIds.Add(localItem.SyncJobItemId);
+                }
+            }
 
             var result = await _syncManager.SyncData(new SyncDataRequest
             {
@@ -163,7 +184,7 @@ namespace MediaBrowser.Server.Implementations.Sync
                 var fileTransferProgress = new ActionableProgress<double>();
                 fileTransferProgress.RegisterAction(pct => progress.Report(pct * .92));
 
-                var sendFileResult = await SendFile(provider, internalSyncJobItem.OutputPath, localItem.LocalPath, target, options, fileTransferProgress, cancellationToken).ConfigureAwait(false);
+                var sendFileResult = await SendFile(provider, internalSyncJobItem.OutputPath, localItem.LocalPath.Split(PathSeparatorChar), target, options, fileTransferProgress, cancellationToken).ConfigureAwait(false);
 
                 if (localItem.Item.MediaSources != null)
                 {
@@ -177,6 +198,8 @@ namespace MediaBrowser.Server.Implementations.Sync
                     }
                 }
 
+                localItem.FileId = sendFileResult.Id;
+
                 // Create db record
                 await dataProvider.AddOrUpdate(target, localItem).ConfigureAwait(false);
 
@@ -228,10 +251,10 @@ namespace MediaBrowser.Server.Implementations.Sync
                     var sendFileResult = await SendFile(provider, mediaStream.Path, remotePath, target, options, new Progress<double>(), cancellationToken).ConfigureAwait(false);
 
                     // This is the path that will be used when talking to the provider
-                    mediaStream.ExternalId = remotePath;
+                    mediaStream.ExternalId = sendFileResult.Id;
 
                     // Keep track of all additional files for cleanup later.
-                    localItem.AdditionalFiles.Add(remotePath);
+                    localItem.AdditionalFiles.Add(sendFileResult.Id);
 
                     // This is the public path clients will use
                     mediaStream.Path = sendFileResult.Path;
@@ -256,17 +279,15 @@ namespace MediaBrowser.Server.Implementations.Sync
             }
         }
 
-        private string GetRemoteSubtitlePath(LocalItem item, MediaStream stream, IServerSyncProvider provider, SyncTarget target)
+        private string[] GetRemoteSubtitlePath(LocalItem item, MediaStream stream, IServerSyncProvider provider, SyncTarget target)
         {
-            var path = item.LocalPath;
-
             var filename = GetSubtitleSaveFileName(item, stream.Language, stream.IsForced) + "." + stream.Codec.ToLower();
 
-            var parentPath = provider.GetParentDirectoryPath(path, target);
-
-            path = Path.Combine(parentPath, filename);
+            var pathParts = item.LocalPath.Split(PathSeparatorChar);
+            var list = pathParts.Take(pathParts.Length - 1).ToList();
+            list.Add(filename);
 
-            return path;
+            return list.ToArray();
         }
 
         private string GetSubtitleSaveFileName(LocalItem item, string language, bool isForced)
@@ -300,12 +321,16 @@ namespace MediaBrowser.Server.Implementations.Sync
             foreach (var localItem in localItems)
             {
                 var files = localItem.AdditionalFiles.ToList();
-                files.Insert(0, localItem.LocalPath);
+
+                // TODO: Remove this. Have to check it for now since this is a new property
+                if (!string.IsNullOrWhiteSpace(localItem.FileId))
+                {
+                    files.Insert(0, localItem.FileId);
+                }
 
                 foreach (var file in files)
                 {
                     _logger.Debug("Removing {0} from {1}.", file, target.Name);
-
                     await provider.DeleteFile(file, target, cancellationToken).ConfigureAwait(false);
                 }
 
@@ -313,9 +338,9 @@ namespace MediaBrowser.Server.Implementations.Sync
             }
         }
 
-        private async Task<SyncedFileInfo> SendFile(IServerSyncProvider provider, string inputPath, string remotePath, SyncTarget target, SyncOptions options, IProgress<double> progress, CancellationToken cancellationToken)
+        private async Task<SyncedFileInfo> SendFile(IServerSyncProvider provider, string inputPath, string[] pathParts, SyncTarget target, SyncOptions options, IProgress<double> progress, CancellationToken cancellationToken)
         {
-            _logger.Debug("Sending {0} to {1}. Remote path: {2}", inputPath, provider.Name, remotePath);
+            _logger.Debug("Sending {0} to {1}. Remote path: {2}", inputPath, provider.Name, string.Join("/", pathParts));
             using (var fileStream = _fileSystem.GetFileStream(inputPath, FileMode.Open, FileAccess.Read, FileShare.Read, true))
             {
                 Stream stream = fileStream;
@@ -325,7 +350,7 @@ namespace MediaBrowser.Server.Implementations.Sync
                     stream = new ThrottledStream(stream, options.UploadSpeedLimitBytes);
                 }
 
-                return await provider.SendFile(stream, remotePath, target, progress, cancellationToken).ConfigureAwait(false);
+                return await provider.SendFile(stream, pathParts, target, progress, cancellationToken).ConfigureAwait(false);
             }
         }
 
@@ -349,7 +374,7 @@ namespace MediaBrowser.Server.Implementations.Sync
             var path = GetDirectoryPath(provider, job, syncedItem, libraryItem, serverName);
             path.Add(GetLocalFileName(provider, libraryItem, originalFileName));
 
-            var localPath = provider.GetFullPath(path, target);
+            var localPath = string.Join(PathSeparatorString, path.ToArray());
 
             foreach (var mediaSource in libraryItem.MediaSources)
             {

+ 2 - 4
MediaBrowser.Server.Implementations/Sync/SyncManager.cs

@@ -793,8 +793,6 @@ namespace MediaBrowser.Server.Implementations.Sync
                 }
                 else
                 {
-                    _logger.Debug("Setting status to Queued for {0} because it is no longer on the device.", jobItem.ItemId);
-
                     // Content is no longer on the device
                     if (jobItem.IsMarkedForRemoval)
                     {
@@ -802,6 +800,7 @@ namespace MediaBrowser.Server.Implementations.Sync
                     }
                     else
                     {
+                        _logger.Debug("Setting status to Queued for {0} because it is no longer on the device.", jobItem.ItemId);
                         jobItem.Status = SyncJobItemStatus.Queued;
                     }
                     requiresSaving = true;
@@ -902,8 +901,6 @@ namespace MediaBrowser.Server.Implementations.Sync
                 }
                 else
                 {
-                    _logger.Debug("Setting status to Queued for {0} because it is no longer on the device.", jobItem.Id);
-
                     // Content is no longer on the device
                     if (jobItem.IsMarkedForRemoval)
                     {
@@ -911,6 +908,7 @@ namespace MediaBrowser.Server.Implementations.Sync
                     }
                     else
                     {
+                        _logger.Debug("Setting status to Queued for {0} because it is no longer on the device.", jobItem.Id);
                         jobItem.Status = SyncJobItemStatus.Queued;
                     }
                     requiresSaving = true;

+ 6 - 1
MediaBrowser.Server.Implementations/Sync/SyncedMediaSourceProvider.cs

@@ -109,8 +109,13 @@ namespace MediaBrowser.Server.Implementations.Sync
             var dataProvider = _syncManager.GetDataProvider(provider, target);
             var localItem = await dataProvider.Get(target, openKeys[2]).ConfigureAwait(false);
 
+            var fileId = localItem.FileId;
+            if (string.IsNullOrWhiteSpace(fileId))
+            {
+            }
+
             var requiresDynamicAccess = (IHasDynamicAccess)provider;
-            var dynamicInfo = await requiresDynamicAccess.GetSyncedFileInfo(localItem.LocalPath, target, cancellationToken).ConfigureAwait(false);
+            var dynamicInfo = await requiresDynamicAccess.GetSyncedFileInfo(fileId, target, cancellationToken).ConfigureAwait(false);
 
             var mediaSource = localItem.Item.MediaSources.First();
             mediaSource.LiveStreamId = Guid.NewGuid().ToString();

+ 14 - 21
MediaBrowser.Server.Implementations/Sync/TargetDataProvider.cs

@@ -1,5 +1,4 @@
 using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.IO;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller.Sync;
@@ -12,6 +11,7 @@ using System.IO;
 using System.Linq;
 using System.Threading;
 using System.Threading.Tasks;
+using Patterns.IO;
 
 namespace MediaBrowser.Server.Implementations.Sync
 {
@@ -29,8 +29,6 @@ namespace MediaBrowser.Server.Implementations.Sync
         private readonly IApplicationPaths _appPaths;
         private readonly IServerApplicationHost _appHost;
 
-        private readonly SemaphoreSlim _cacheFileLock = new SemaphoreSlim(1, 1);
-
         public TargetDataProvider(IServerSyncProvider provider, SyncTarget target, IServerApplicationHost appHost, ILogger logger, IJsonSerializer json, IFileSystem fileSystem, IApplicationPaths appPaths)
         {
             _logger = logger;
@@ -42,7 +40,7 @@ namespace MediaBrowser.Server.Implementations.Sync
             _appHost = appHost;
         }
 
-        private string GetRemotePath()
+        private string[] GetRemotePath()
         {
             var parts = new List<string>
             {
@@ -52,7 +50,7 @@ namespace MediaBrowser.Server.Implementations.Sync
 
             parts = parts.Select(i => GetValidFilename(_provider, i)).ToList();
 
-            return _provider.GetFullPath(parts, _target);
+            return parts.ToArray();
         }
 
         private string GetValidFilename(IServerSyncProvider provider, string filename)
@@ -65,22 +63,22 @@ namespace MediaBrowser.Server.Implementations.Sync
         {
             if (_items == null)
             {
-                try
+                _logger.Debug("Getting {0} from {1}", string.Join(MediaSync.PathSeparatorString, GetRemotePath().ToArray()), _provider.Name);
+
+                var fileResult = await _provider.GetFiles(new FileQuery
                 {
-                    var path = GetRemotePath();
+                    FullPath = GetRemotePath().ToArray()
 
-                    _logger.Debug("Getting {0} from {1}", path, _provider.Name);
+                }, _target, cancellationToken).ConfigureAwait(false);
 
-                    using (var stream = await _provider.GetFile(path, _target, new Progress<double>(), cancellationToken))
+                if (fileResult.Items.Length > 0)
+                {
+                    using (var stream = await _provider.GetFile(fileResult.Items[0].Id, _target, new Progress<double>(), cancellationToken))
                     {
                         _items = _json.DeserializeFromStream<List<LocalItem>>(stream);
                     }
                 }
-                catch (FileNotFoundException)
-                {
-                    _items = new List<LocalItem>();
-                }
-                catch (DirectoryNotFoundException)
+                else
                 {
                     _items = new List<LocalItem>();
                 }
@@ -133,14 +131,9 @@ namespace MediaBrowser.Server.Implementations.Sync
             }
         }
 
-        public Task<List<string>> GetServerItemIds(SyncTarget target, string serverId)
-        {
-            return GetData(items => items.Where(i => string.Equals(i.ServerId, serverId, StringComparison.OrdinalIgnoreCase)).Select(i => i.ItemId).ToList());
-        }
-
-        public Task<List<string>> GetSyncJobItemIds(SyncTarget target, string serverId)
+        public Task<List<LocalItem>> GetLocalItems(SyncTarget target, string serverId)
         {
-            return GetData(items => items.Where(i => string.Equals(i.ServerId, serverId, StringComparison.OrdinalIgnoreCase)).Select(i => i.SyncJobItemId).Where(i => !string.IsNullOrWhiteSpace(i)).ToList());
+            return GetData(items => items.Where(i => string.Equals(i.ServerId, serverId, StringComparison.OrdinalIgnoreCase)).ToList());
         }
 
         public Task AddOrUpdate(SyncTarget target, LocalItem item)

+ 1 - 0
MediaBrowser.Server.Implementations/UserViews/DynamicImageProvider.cs

@@ -203,6 +203,7 @@ namespace MediaBrowser.Server.Implementations.UserViews
                     SpecialFolder.MusicGenres,
                     SpecialFolder.MusicGenre,
                     SpecialFolder.MusicLatest,
+                    SpecialFolder.MusicPlaylists,
                     SpecialFolder.MusicSongs,
                     SpecialFolder.MusicFavorites,
                     SpecialFolder.MusicFavoriteArtists,

+ 1 - 0
MediaBrowser.Server.Implementations/packages.config

@@ -3,6 +3,7 @@
   <package id="MediaBrowser.Naming" version="1.0.0.32" targetFramework="net45" />
   <package id="Mono.Nat" version="1.2.23.0" targetFramework="net45" />
   <package id="morelinq" version="1.1.0" targetFramework="net45" />
+  <package id="Patterns.IO" version="1.0.0.3" targetFramework="net45" />
   <package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" />
   <package id="SocketHttpListener" version="1.0.0.3" targetFramework="net45" />
 </packages>

+ 2 - 2
Nuget/MediaBrowser.Common.Internal.nuspec

@@ -2,7 +2,7 @@
 <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
     <metadata>
         <id>MediaBrowser.Common.Internal</id>
-        <version>3.0.616</version>
+        <version>3.0.619</version>
         <title>MediaBrowser.Common.Internal</title>
         <authors>Luke</authors>
         <owners>ebr,Luke,scottisafool</owners>
@@ -12,7 +12,7 @@
         <description>Contains common components shared by Emby Theater and Emby Server. Not intended for plugin developer consumption.</description>
         <copyright>Copyright © Emby 2013</copyright>
         <dependencies>
-            <dependency id="MediaBrowser.Common" version="3.0.616" />
+            <dependency id="MediaBrowser.Common" version="3.0.619" />
             <dependency id="NLog" version="3.2.0.0" />
             <dependency id="SimpleInjector" version="2.7.0" />
         </dependencies>

+ 1 - 1
Nuget/MediaBrowser.Common.nuspec

@@ -2,7 +2,7 @@
 <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
     <metadata>
         <id>MediaBrowser.Common</id>
-        <version>3.0.616</version>
+        <version>3.0.619</version>
         <title>MediaBrowser.Common</title>
         <authors>Emby Team</authors>
         <owners>ebr,Luke,scottisafool</owners>

+ 1 - 1
Nuget/MediaBrowser.Model.Signed.nuspec

@@ -2,7 +2,7 @@
 <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
     <metadata>
         <id>MediaBrowser.Model.Signed</id>
-        <version>3.0.616</version>
+        <version>3.0.619</version>
         <title>MediaBrowser.Model - Signed Edition</title>
         <authors>Emby Team</authors>
         <owners>ebr,Luke,scottisafool</owners>

+ 3 - 2
Nuget/MediaBrowser.Server.Core.nuspec

@@ -2,7 +2,7 @@
 <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
     <metadata>
         <id>MediaBrowser.Server.Core</id>
-        <version>3.0.616</version>
+        <version>3.0.619</version>
         <title>Media Browser.Server.Core</title>
         <authors>Emby Team</authors>
         <owners>ebr,Luke,scottisafool</owners>
@@ -12,7 +12,8 @@
         <description>Contains core components required to build plugins for Emby Server.</description>
         <copyright>Copyright © Emby 2013</copyright>
         <dependencies>
-            <dependency id="MediaBrowser.Common" version="3.0.616" />
+            <dependency id="MediaBrowser.Common" version="3.0.619" />
+			<dependency id="Patterns.IO" version="1.0.0.3" />
         </dependencies>
     </metadata>
     <files>