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); }
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() }); }
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); }
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() }); }
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); }
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); }
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)); } }
public async Task <FileTransferStatus> QueueRemoteSong(LocalSong songMetadata, byte[] data) { return(new FileTransferStatus(await Success(), Observable.Never <int>())); }
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); } }
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); } }
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()); } }
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()); } }
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); } }
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); ; } }
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); } }