private static void ApplyChangeSet(string root, ChangeSet pendingChange, List<string> playlist) { using (log4net.ThreadContext.Stacks["NDC"].Push("Deferred Removing iTrack")) { foreach (var track in pendingChange.RemoveChanges) { playlist.Remove(track.GetPlaylistLine(root)); } } using (log4net.ThreadContext.Stacks["NDC"].Push("Deferred Adding iTrack")) { foreach (var addChange in pendingChange.AddChanges) { if(!playlist.Contains(addChange)) playlist.Add(addChange); } } }
public Response Query([RequestBody]PlaylistRequest request) { SongDb.Clear(); PlaylistDb.Clear(); Response errorResponse; if (!request.CheckValidate(out errorResponse)) return errorResponse; using (ThreadContext.Stacks["NDC"].Push("Client " + request.DeviceId)) { Status = "Client " + request.DeviceId + " connected."; Log.Info("Client " + request.DeviceId + " connected."); Log.Debug("Received Data:" + Environment.NewLine + request); string playlistName = Path.GetFileNameWithoutExtension(request.PlaylistDevicePath); Subscription subscription = DataManager.GetSubscription(request.SafeDeviceId); SubscriptionManager subManager = subscription != null ? new SubscriptionManager(subscription) : null; string playlistPath = DataManager.GetDevicePlaylistPath(request); List<string> reconciledPlaylist; // Read and sort the playlists Log.Info("Loading Phone Playlist..."); List<string> devicePlaylist = new List<string>(request.PlaylistData); Status = string.Format("Loading iTunes Library ({0})...", playlistName); Log.InfoFormat("Loading iTunes Library ({0})...", playlistName); List<string> desktopPlaylist; XmliTunesLibrary library = CachedXmlLibrary.Library; IPlaylist playlist = library.GetFirstPlaylistByName(playlistName); if (playlist != null) { desktopPlaylist = library.GeneratePlaylist(playlist, request.DeviceMediaRoot); } else { // Playlist was deleted on the server Log.WarnFormat("Fail. Playlist ({0}) does not exist", playlistName); // We're sending an error message, along with a list of track that are no longer subscribed and // can be safely removed. var wipeActions = request.PlaylistData.Select(s => new SyncAction {DeviceLocation = s, Type = SyncType.Remove}); if (subManager != null) wipeActions = subManager.FixProposedDeletes(library, wipeActions); return new SyncResponse { ErrorMessage = "Playlist does not exist", Error = (int) SyncResponseError.PlaylistNotFound, PlaylistDevicePath = request.PlaylistDevicePath, Actions = wipeActions.ToArray() }; } // changes that were made on the DESKTOP. Apply to DEVICE. IEnumerable<SyncAction> changesOnDesktop = DiffHandler.Diff(desktopPlaylist, devicePlaylist); // Make sure we don't delete any tracks still present in other playlists. if (subManager != null) changesOnDesktop = subManager.FixProposedDeletes(library, changesOnDesktop); // We use different files for the serving and referencing playlists because this allows us to delay // committing a playlist as reference playlist until the client has retrieved the playlist. // In situations where the sync is interrupted, it can otherwise lead to the server having incorrect // state information and results in improper deletion of tracks from iTunes playlist. string playlistRefPath = DataManager.GetReferencePlaylistPath(playlistPath); string pendingChangesPath = DataManager.GetChangeSetCollectionPath(playlistPath); #if ENABLE_SYNC_TO_ITUNES if (File.Exists(playlistRefPath) && !Helper.IsAlbumOrArtistPlaylist(playlistName)) { // load any pending changes ChangeSetCollection pendingChanges = ChangeSetCollection.DeserializeOrCreate(pendingChangesPath); // apply those changes to the desktopPlaylist, so we can move into the expected state ApplyChangeSet(request.DeviceMediaRoot, pendingChanges, desktopPlaylist); Log.InfoFormat("Loading reference playlist {0}...", playlistRefPath); List<string> referencePlaylist = Helper.LoadPlaylist(playlistRefPath); changesOnDesktop = DiffHandler.Diff(desktopPlaylist, referencePlaylist); IEnumerable<SyncAction> changesOnDevice = DiffHandler.Diff(devicePlaylist, referencePlaylist); // changes that were made on the DEVICE. Apply to DESKTOP. reconciledPlaylist = new List<string>(desktopPlaylist); var currentChangeSet = new ChangeSet { AddChanges = (from change in changesOnDevice where change.Type == SyncType.Add select change.DeviceLocation).ToList(), RemoveChanges = ((from change in changesOnDevice where change.Type == SyncType.Remove select library.GetTrack(change.DeviceLocation) as Track).TakeWhile(t => t != null)).ToList() }; if(!currentChangeSet.IsEmpty) pendingChanges.Add(currentChangeSet); if (ComiTunesLibrary.IsItunesRunning()) { ComiTunesLibrary comLibrary = new ComiTunesLibrary(); foreach (var pendingChange in pendingChanges) { using (log4net.ThreadContext.Stacks["NDC"].Push("Removing iTrack")) { foreach (var track in comLibrary.RemoveTracks(playlist, pendingChange.RemoveChanges)) { reconciledPlaylist.Remove(track.GetPlaylistLine(request.DeviceMediaRoot)); Log.Info(track.Location); } } using (log4net.ThreadContext.Stacks["NDC"].Push("Adding iTrack")) { foreach (var deviceLocation in comLibrary.AddTracks( playlist, pendingChange.AddChanges, subscription ?? Subscription.GetQuickSubscription(playlistName, request.DeviceMediaRoot, request.DeviceId), request.DeviceMediaRoot)) { reconciledPlaylist.Add(deviceLocation); Log.Info(deviceLocation); } } } pendingChanges.Clear(); }else { if(!currentChangeSet.IsEmpty) ApplyChangeSet(request.DeviceMediaRoot, currentChangeSet, reconciledPlaylist); } ChangeSetCollection.Serialize(pendingChanges, pendingChangesPath); } else { #endif reconciledPlaylist = desktopPlaylist; #if ENABLE_SYNC_TO_ITUNES } #endif foreach (var change in changesOnDesktop) { if (change.Type != SyncType.Add) continue; // Remember songs, we we can serve them when client requests string id = Helper.GetSha1Hash(change.DeviceLocation); change.TrackPath = "/songs/" + id; SongDb.Add(id, library.GetTrack(change.DeviceLocation).Location); } string playlistKey = Helper.GetSha1Hash(playlistPath); Helper.SavePlaylist(reconciledPlaylist, playlistPath); PlaylistDb.Add(playlistKey, playlistPath); SyncResponse syncResponse = new SyncResponse { PlaylistServerPath = "/playlists/" + playlistKey, PlaylistDevicePath = request.PlaylistDevicePath, Actions = changesOnDesktop.ToArray() }; syncResponse.Actions.UnEscapeAllDeviceLocations(); Log.Debug("Sending Data:" + Environment.NewLine + syncResponse.ToString()); ResetStatusLater(); return syncResponse; } }