public static Song ToSong(this LocalSong track)
        {
            var song = new Song
            {
                ProviderId  = "local." + track.Id,
                Name        = track.Title,
                ArtistName  = track.ArtistName,
                Duration    = track.Duration,
                AudioUrl    = track.FilePath,
                SongState   = SongState.Local,
                TrackNumber = track.TrackNumber,
                HeartState  = track.HeartState
            };

            if (!string.IsNullOrEmpty(track.ArtistId))
            {
                song.Artist = track.ToArtist();
                if (string.IsNullOrEmpty(song.ArtistName))
                {
                    song.ArtistName = song.Artist.Name;
                }
            }

            if (!string.IsNullOrEmpty(track.AlbumId))
            {
                song.Album = track.ToAlbum();
                song.Album.PrimaryArtist = song.Artist;
            }
            return(song);
        }
Exemple #2
0
            public async Task IsTrueForUpdatedLibrary()
            {
                var song = new LocalSong("C://Song.mp3", TimeSpan.Zero);

                var songFinder = Substitute.For <ILocalSongFinder>();

                songFinder.GetSongsAsync().Returns(Observable.Return(Tuple.Create(song, (byte[])null)));

                var fileSystem = new MockFileSystem(new Dictionary <string, MockFileData> {
                    { "C://Song.mp3", new MockFileData("Bla") }
                });

                using (Library library = new LibraryBuilder().WithFileSystem(fileSystem).WithSongFinder(songFinder).Build())
                {
                    Guid accessToken = library.LocalAccessControl.RegisterLocalAccessToken();

                    // NB: System.IO.Abstractions only likes backslashes
                    library.ChangeSongSourcePath("C:\\", accessToken);

                    var vm = new LocalViewModel(library, new ViewSettings(), new CoreSettings(), accessToken);

                    await library.AwaitInitializationAndUpdate();

                    Assert.False(vm.ShowAddSongsHelperMessage);
                }
            }
        public async Task <FileTransferStatus> QueueRemoteSong(LocalSong songMetadata, byte[] songData)
        {
            var song = new NetworkSong
            {
                Album    = songMetadata.Album,
                Artist   = songMetadata.Artist,
                Duration = songMetadata.Duration,
                Genre    = songMetadata.Genre,
                Source   = NetworkSongSource.Mobile,
                Title    = songMetadata.Title,
                Guid     = Guid.NewGuid()
            };

            Guid transferId = Guid.NewGuid();
            var  info       = new SongTransferInfo
            {
                TransferId = transferId,
                Metadata   = song
            };

            ResponseInfo response = await this.SendRequest(RequestAction.QueueRemoteSong, info);

            var message = new SongTransferMessage {
                Data = songData, TransferId = transferId
            };

            var progress = this.TransferFileAsync(message).Publish(0);

            progress.Connect();

            var status = new FileTransferStatus(response, progress);

            return(status);
        }
 public static Album ToAlbum(this LocalSong track)
 {
     return(new Album
     {
         ProviderId = "local." + track.AlbumId,
         Name = track.AlbumName.Trim()
     });
 }
Exemple #5
0
            public void SetsArtworkKey()
            {
                string key = BlobCacheKeys.GetKeyForArtwork(new byte[] { 0, 1 });

                var song = new LocalSong("C://Bla", TimeSpan.Zero, key);

                Assert.Equal(key, song.ArtworkKey);
            }
Exemple #6
0
        public LocalSongViewModel(LocalSong model)
        {
            if (model == null)
            {
                throw new ArgumentNullException("model");
            }

            this.model = model;
        }
 public static Artist ToArtist(this LocalSong track)
 {
     return(new Artist
     {
         ProviderId = "local." + track.ArtistId,
         Name = (string.IsNullOrEmpty(track.AlbumArtist)
             ? track.ArtistName
             : track.AlbumArtist).Trim()
     });
 }
Exemple #8
0
        private static (ILocalSong, byte[]) CreateSong(Tag tag, TimeSpan duration, string filePath)
        {
            var song = new LocalSong(filePath, duration)
            {
                Album  = PrepareTag(tag.Album, string.Empty),
                Artist =
                    PrepareTag(tag.FirstAlbumArtist ?? tag.FirstPerformer,
                               "Unknown Artist"), //HACK: In the future retrieve the string for an unkown artist from the view if we want to localize it
                Genre       = PrepareTag(tag.FirstGenre, string.Empty),
                Title       = PrepareTag(tag.Title, Path.GetFileNameWithoutExtension(filePath)),
                TrackNumber = (int)tag.Track
            };

            IPicture picture = tag.Pictures.FirstOrDefault();

            return(song, picture?.Data.Data);
        }
Exemple #9
0
            public void MultipleSongsWithSameArtistReturnsCommonArtist()
            {
                var song1 = new LocalSong("C://song1.mp3", TimeSpan.Zero)
                {
                    Artist = "The Artist"
                };

                var song2 = new LocalSong("C://song2.mp3", TimeSpan.Zero)
                {
                    Artist = "The Artist"
                };

                var songs   = new[] { song1, song2 };
                var fixture = new TagEditorViewModel(songs, () => Task.FromResult(true));

                Assert.Equal(song1.Artist, fixture.Artist);
            }
Exemple #10
0
        public async Task CanPlayAWholeBunchOfSongs()
        {
            var song = new LocalSong("C://", TimeSpan.Zero);

            using (Library library = new LibraryBuilder().WithPlaylist().Build())
            {
                var awaiter = library.PlaybackState.Where(x => x == AudioPlayerState.Playing)
                    .Select((x, i) => i + 1)
                    .FirstAsync(x => x == 10)
                    .PublishLast();
                awaiter.Connect();

                Guid token = library.LocalAccessControl.RegisterLocalAccessToken();

                await library.PlayInstantlyAsync(Enumerable.Repeat(song, 10).ToList(), token);

                await awaiter.Timeout(TimeSpan.FromSeconds(5));
            }
        }
Exemple #11
0
 public async Task <FileTransferStatus> QueueRemoteSong(LocalSong songMetadata, byte[] data)
 {
     return(new FileTransferStatus(await Success(), Observable.Never <int>()));
 }
Exemple #12
0
            public async Task UpdateRemovesArtworkWhenSongIsMissing()
            {
                var missingSong = new LocalSong("C://Missing.mp3", TimeSpan.Zero, "artwork-abcdefg");
                await BlobCache.LocalMachine.Insert("artwork-abcdefg", new byte[] { 0, 1 });

                var libraryReader = Substitute.For<ILibraryReader>();
                libraryReader.LibraryExists.Returns(true);
                libraryReader.ReadSongSourcePath().Returns("C://");
                libraryReader.ReadPlaylists().Returns(new List<Playlist>());
                libraryReader.ReadSongs().Returns(new[] { missingSong });

                var fileSystem = new MockFileSystem();
                fileSystem.Directory.CreateDirectory("C://");

                using (Library library = new LibraryBuilder().WithFileSystem(fileSystem).WithReader(libraryReader).Build())
                {
                    await library.AwaitInitializationAndUpdate();

                    Assert.Null(BlobCache.LocalMachine.GetAllKeys().FirstOrDefault(x => x == "artwork-abcdefg"));
                }
            }
        public static async Task <SavingError> SaveTrackAsync(StorageFile file)
        {
            var audioPath = file.Path;

            if (App.Locator.CollectionService.SongAlreadyExists(audioPath) != null)
            {
                return(SavingError.AlreadyExists);
            }

            #region Getting metadata

            #region id3 tags

            Tag      tags = null;
            TimeSpan duration;
            var      tryAsM4A   = false;
            var      fileStream = await file.OpenStreamForReadAsync();

            File tagFile = null;
            try
            {
                tagFile = File.Create(new StreamFileAbstraction(file.Name, fileStream, fileStream));
            }
            catch (Exception e)
            {
                tryAsM4A = e is CorruptFileException;
            }

            if (tryAsM4A)
            {
                //need to reopen (when it fails to open it disposes of the stream
                fileStream = await file.OpenStreamForReadAsync();

                try
                {
                    tagFile = File.Create(new StreamFileAbstraction(
                                              file.Name.Replace(".mp3", ".m4a"), fileStream, fileStream));
                }
                catch
                {
                }
            }

            tags = tagFile == null ? null : tagFile.Tag;
            if (tags != null)
            {
                duration = tagFile.Properties.Duration;
            }
            fileStream.Dispose();
            if (tagFile != null)
            {
                tagFile.Dispose();
            }

            #endregion

            LocalSong track;
            if (tags == null)
            {
                //if there aren't any id3tags, try using the properties from the file
                var prop = await file.Properties.GetMusicPropertiesAsync().AsTask().ConfigureAwait(false);

                track = new LocalSong(prop)
                {
                    FilePath = audioPath
                };
            }
            else
            {
                track = new LocalSong(tags.Title, tags.JoinedPerformers.Replace(";", ","), tags.Album,
                                      tags.FirstAlbumArtist)
                {
                    FilePath    = audioPath,
                    Genre       = tags.FirstGenre,
                    TrackNumber = (int)tags.Track,
                    Duration    = duration
                };
            }

            var song = track.ToSong();

            #endregion

            //if no metadata was obtain, then result to using the filename
            if (string.IsNullOrEmpty(song.Name))
            {
                song.Name        = file.DisplayName;
                song.ProviderId += Convert.ToBase64String(Encoding.UTF8.GetBytes(song.Name.ToLower()));
            }


            if (App.Locator.CollectionService.SongAlreadyExists(song.ProviderId,
                                                                track.Title, track.AlbumName, track.ArtistName) != null)
            {
                return(SavingError.AlreadyExists);
            }

            try
            {
                await App.Locator.CollectionService.AddSongAsync(song, tags)
                .ConfigureAwait(false);

                return(SavingError.None);
            }
            catch (NetworkException)
            {
                return(SavingError.Network);
            }
            catch
            {
                return(SavingError.Unknown);
            }
        }
Exemple #14
0
            public async Task ExternalTagChangesArePropagated()
            {
                var song = new LocalSong("C://Song.mp3", TimeSpan.Zero) { Title = "A" };
                var updatedSong = new LocalSong("C://Song.mp3", TimeSpan.Zero) { Title = "B" };

                var libraryReader = Substitute.For<ILibraryReader>();
                libraryReader.LibraryExists.Returns(true);
                libraryReader.ReadSongSourcePath().Returns("C://");
                libraryReader.ReadPlaylists().Returns(new List<Playlist>());
                libraryReader.ReadSongs().Returns(new[] { song });

                var fileSystem = new MockFileSystem(new Dictionary<string, MockFileData> { { song.OriginalPath, new MockFileData("DontCare") } });

                var songFinder = Substitute.For<ILocalSongFinder>();
                songFinder.GetSongsAsync().Returns(Observable.Return(Tuple.Create(updatedSong, (byte[])null)));

                using (Library library = new LibraryBuilder().WithFileSystem(fileSystem).WithReader(libraryReader).WithSongFinder(songFinder).Build())
                {
                    await library.AwaitInitializationAndUpdate();

                    Assert.Equal("B", library.Songs[0].Title);
                }
            }
Exemple #15
0
            public async Task UpdateRemovesMissingSongWithoutArtworkFromLibraryWhenOtherArtworksArePresent()
            {
                var existingSong = new LocalSong("C://Existing.mp3", TimeSpan.Zero, "artwork-abcdefg");
                var missingSong = new LocalSong("C://Missing.mp3", TimeSpan.Zero);

                var libraryReader = Substitute.For<ILibraryReader>();
                libraryReader.LibraryExists.Returns(true);
                libraryReader.ReadSongSourcePath().Returns("C://");
                libraryReader.ReadPlaylists().Returns(new List<Playlist>());
                libraryReader.ReadSongs().Returns(new[] { existingSong, missingSong });

                var fileSystem = new MockFileSystem(new Dictionary<string, MockFileData> { { existingSong.OriginalPath, new MockFileData("DontCare") } });

                using (Library library = new LibraryBuilder().WithFileSystem(fileSystem).WithReader(libraryReader).Build())
                {
                    await library.AwaitInitializationAndUpdate();

                    Assert.Equal(1, library.Songs.Count());
                }
            }
Exemple #16
0
            public async Task UpdateRemovesMissingSongsFromPlaylists()
            {
                var existingSong = new LocalSong("C://Existing.mp3", TimeSpan.Zero);
                var missingSong = new LocalSong("C://Missing.mp3", TimeSpan.Zero);

                var songs = new[] { existingSong, missingSong };

                var playlist1 = new Playlist("Playlist 1");
                playlist1.AddSongs(songs);

                var playlist2 = new Playlist("Playlist 2");
                playlist2.AddSongs(songs);

                var libraryReader = Substitute.For<ILibraryReader>();
                libraryReader.LibraryExists.Returns(true);
                libraryReader.ReadSongSourcePath().Returns("C://");
                libraryReader.ReadPlaylists().Returns(new[] { playlist1, playlist2 });
                libraryReader.ReadSongs().Returns(songs);

                var fileSystem = new MockFileSystem(new Dictionary<string, MockFileData> { { existingSong.OriginalPath, new MockFileData("DontCare") } });

                using (Library library = new LibraryBuilder().WithFileSystem(fileSystem).WithReader(libraryReader).Build())
                {
                    await library.AwaitInitializationAndUpdate();

                    Assert.Equal(1, library.Playlists[0].Count());
                    Assert.Equal(1, library.Playlists[1].Count());
                }
            }
Exemple #17
0
        private async Task UpdateSongsAsync(string path)
        {
            if (this.currentSongFinderSubscription != null)
            {
                this.currentSongFinderSubscription.Dispose();
                this.currentSongFinderSubscription = null;
            }

            this.IsUpdating = true;

            await this.RemoveMissingSongsAsync(path);

            ILocalSongFinder songFinder = this.localSongFinderFunc(path);

            this.currentSongFinderSubscription = songFinder.GetSongsAsync()
                                                 .ObserveOn(RxApp.TaskpoolScheduler)
                                                 .Subscribe(t =>
            {
                LocalSong song = t.Item1;

                this.songLock.EnterWriteLock();

                bool added = this.songs.Add(song);

                LocalSong realSong;
                bool needsUpdate = false;

                if (added)
                {
                    realSong    = song;
                    needsUpdate = true;
                }

                else
                {
                    LocalSong existing = this.songs.First(x => x.OriginalPath == song.OriginalPath);

                    if (existing.UpdateMetadataFrom(song))
                    {
                        needsUpdate = true;
                    }

                    realSong = existing;
                }

                this.songLock.ExitWriteLock();

                byte[] artworkData = t.Item2;

                if (artworkData != null)
                {
                    string key = BlobCacheKeys.GetKeyForArtwork(artworkData);

                    if (realSong.ArtworkKey != key)
                    {
                        ArtworkCache.Instance.Store(key, artworkData).ToObservable()
                        .Subscribe(x => realSong.ArtworkKey = key);
                    }
                }

                if (needsUpdate)
                {
                    this.songsUpdated.OnNext(Unit.Default);
                }
            }, () =>
            {
                this.Save();

                this.StartOnlineArtworkLookup();

                this.songLock.EnterReadLock();
                int songCount = this.songs.Count;
                this.songLock.ExitReadLock();

                AnalyticsClient.Instance.RecordLibrarySize(songCount);

                this.IsUpdating = false;
            });
        }
            public async Task InvokesSongsUpdatedObservableWhenSongAdded()
            {
                var song = new LocalSong("C://Song.mp3", TimeSpan.Zero);

                var libraryReader = Substitute.For<ILibraryReader>();
                libraryReader.LibraryExists.Returns(true);
                libraryReader.ReadSongSourcePath().Returns("C://");
                libraryReader.ReadPlaylists().Returns(new List<Playlist>());
                libraryReader.ReadSongs().Returns(new List<LocalSong>());

                var fileSystem = new MockFileSystem(new Dictionary<string, MockFileData> { { song.OriginalPath, new MockFileData("DontCare") } });

                var songFinder = Substitute.For<ILocalSongFinder>();
                songFinder.GetSongsAsync().Returns(Observable.Return(Tuple.Create(song, (byte[])null)));

                using (Library library = new LibraryBuilder().WithFileSystem(fileSystem).WithReader(libraryReader).WithSongFinder(songFinder).Build())
                {
                    var update = library.SongsUpdated.CreateCollection();

                    await library.AwaitInitializationAndUpdate();

                    Assert.Equal(1, update.Count);
                }
            }
Exemple #19
0
            public async Task UnavailableSongsSourceDoesntPurgeLibrary()
            {
                var settings = new CoreSettings
                {
                    EnableAutomaticLibraryUpdates = false
                };

                var song = new LocalSong("C://Song.mp3", TimeSpan.Zero) { Title = "A" };

                var libraryReader = Substitute.For<ILibraryReader>();
                libraryReader.LibraryExists.Returns(true);
                libraryReader.ReadSongSourcePath().Returns("C://");
                libraryReader.ReadPlaylists().Returns(new List<Playlist>());
                libraryReader.ReadSongs().Returns(new[] { song });

                var fileSystem = new MockFileSystem();

                using (Library library = Helpers.CreateLibrary(settings, libraryReader, fileSystem: fileSystem))
                {
                    library.Initialize();

                    var updateFinished = library.IsUpdating.FirstAsync(x => !x).PublishLast();
                    updateFinished.Connect();

                    library.UpdateNow();

                    await updateFinished.Timeout(TimeSpan.FromSeconds(5));

                    Assert.Equal(1, library.Songs.Count);
                }
            }
            public async Task InvokesSongsUpdatedObservableWhenSongMetadataChanged()
            {
                var artworkKey = BlobCacheKeys.GetKeyForArtwork(new byte[] { 0, 1 });
                var song = new LocalSong("C://Song.mp3", TimeSpan.Zero, artworkKey)
                {
                    Album = "Album-A",
                    Artist = "Artist-A",
                    Title = "Title-A",
                    Genre = "Genre-A"
                };

                byte[] updatedArtworkData = { 1, 0 };
                var updatedSong = new LocalSong("C://Song.mp3", TimeSpan.Zero)
                {
                    Album = "Album-B",
                    Artist = "Artist-B",
                    Title = "Title-B",
                    Genre = "Genre-B"
                };

                var libraryReader = Substitute.For<ILibraryReader>();
                libraryReader.LibraryExists.Returns(true);
                libraryReader.ReadSongSourcePath().Returns("C://");
                libraryReader.ReadPlaylists().Returns(new List<Playlist>());
                libraryReader.ReadSongs().Returns(new[] { song });

                var fileSystem = new MockFileSystem(new Dictionary<string, MockFileData> { { song.OriginalPath, new MockFileData("DontCare") } });

                var songFinder = Substitute.For<ILocalSongFinder>();
                songFinder.GetSongsAsync().Returns(Observable.Return(Tuple.Create(updatedSong, updatedArtworkData)));

                using (Library library = new LibraryBuilder().WithFileSystem(fileSystem).WithReader(libraryReader).WithSongFinder(songFinder).Build())
                {
                    var update = library.SongsUpdated.CreateCollection();

                    await library.AwaitInitializationAndUpdate();

                    Assert.Equal(1, update.Count); ;
                }
            }
Exemple #21
0
            public async Task UpdateRemovesArtworkOnlyWithoutReferenceToSong()
            {
                var existingSong = new LocalSong("C://Existing.mp3", TimeSpan.Zero, "artwork-abcdefg");
                var missingSong = new LocalSong("C://Missing.mp3", TimeSpan.Zero, "artwork-abcdefg");
                await BlobCache.LocalMachine.Insert("artwork-abcdefg", new byte[] { 0, 1 });

                var libraryReader = Substitute.For<ILibraryReader>();
                libraryReader.LibraryExists.Returns(true);
                libraryReader.ReadSongSourcePath().Returns("C://");
                libraryReader.ReadPlaylists().Returns(new List<Playlist>());
                libraryReader.ReadSongs().Returns(new[] { existingSong, missingSong });

                var fileSystem = new MockFileSystem(new Dictionary<string, MockFileData> { { existingSong.OriginalPath, new MockFileData("DontCare") } });

                using (Library library = new LibraryBuilder().WithFileSystem(fileSystem).WithReader(libraryReader).Build())
                {
                    await library.AwaitInitializationAndUpdate();

                    Assert.NotNull(BlobCache.LocalMachine.GetAllKeys().FirstOrDefault(x => x == "artwork-abcdefg"));
                }
            }
            public async Task ExternalTagChangesArePropagated()
            {
                var artworkKey = BlobCacheKeys.GetKeyForArtwork(new byte[] { 0, 1 });
                var song = new LocalSong("C://Song.mp3", TimeSpan.Zero, artworkKey)
                {
                    Album = "Album-A",
                    Artist = "Artist-A",
                    Title = "Title-A",
                    Genre = "Genre-A"
                };

                byte[] updatedArtworkData = { 1, 0 };
                var updatedSong = new LocalSong("C://Song.mp3", TimeSpan.Zero)
                {
                    Album = "Album-B",
                    Artist = "Artist-B",
                    Title = "Title-B",
                    Genre = "Genre-B"
                };

                var libraryReader = Substitute.For<ILibraryReader>();
                libraryReader.LibraryExists.Returns(true);
                libraryReader.ReadSongSourcePath().Returns("C://");
                libraryReader.ReadPlaylists().Returns(new List<Playlist>());
                libraryReader.ReadSongs().Returns(new[] { song });

                var fileSystem = new MockFileSystem(new Dictionary<string, MockFileData> { { song.OriginalPath, new MockFileData("DontCare") } });

                var songFinder = Substitute.For<ILocalSongFinder>();
                songFinder.GetSongsAsync().Returns(Observable.Return(Tuple.Create(updatedSong, updatedArtworkData)));

                using (Library library = new LibraryBuilder().WithFileSystem(fileSystem).WithReader(libraryReader).WithSongFinder(songFinder).Build())
                {
                    await library.AwaitInitializationAndUpdate();

                    Assert.True(ReferenceEquals(song, library.Songs[0]));

                    Assert.Equal(updatedSong.Album, song.Album);
                    Assert.Equal(updatedSong.Artist, song.Artist);
                    Assert.Equal(BlobCacheKeys.GetKeyForArtwork(updatedArtworkData), song.ArtworkKey);
                    Assert.Equal(updatedSong.Genre, song.Genre);
                    Assert.Equal(updatedSong.Title, song.Title);
                }
            }