/// <summary> /// Keeps the download progress properties of the music items up-to-date /// so that the progress can be displayed under each item in the UI. /// /// The music items in the library and the playlist do not point to the /// same object even if it's the same track so we need to update both places separately. /// </summary> /// <param name="transfer"></param> protected override void OnDownloadStatusUpdate(BackgroundTransferRequest transfer) { base.OnDownloadStatusUpdate(transfer); var path = FileUtilsBase.UnixSeparators(transfer.Tag.Substring(PhoneLocalLibrary.Instance.BaseMusicPath.Length)); var musicItem = MusicProvider.SearchItem(path); var isDownloading = transfer.TransferStatus == TransferStatus.Transferring; var bytesReceived = (ulong)transfer.BytesReceived; // updates track in the library if (musicItem != null) { MusicItem.SetDownloadStatus(musicItem, bytesReceived); musicItem.IsDownloading = isDownloading; } // updates the track in the playlist var playlistTracks = PimpViewModel.Instance.MusicPlayer.Playlist.Songs .Where(item => item.Song.Path == path) .ToList(); // beautiful!!! BasePlaylist.SetDownloadStatus(playlistTracks, bytesReceived); foreach (var item in playlistTracks) { item.Song.IsDownloading = isDownloading; } }
public Task AddToPlaylistRecursively(MusicItem item) { return(AddToPlaylistRecursively(new List <MusicItem>() { item })); }
private Task DeleteLocalItem(MusicItem item) { return(TryFileDeletion(async() => { await LocalLibrary.Delete(item); // Updates the UI. Apparently, MusicFolder.MusicItems.Remove(item); does not update the UI. await RefreshCurrentFolder(); })); }
public async Task DownloadSong(MusicItem song) { if (PhoneLibraryManager.Instance.ActiveEndpoint.EndpointType == EndpointTypes.Subsonic && song.Size > 5242880) { AddMessage("Files over 5MB cannot currently be downloaded from Subsonic due to a technical limitation. The download of " + song.Name + " is likely to fail."); } await SubmitDownload(song); }
public async Task DownloadFolder(MusicItem folder) { var tracks = await MusicProvider.SongsInFolder(folder); foreach (var t in tracks) { await SubmitDownload(t); } }
/// <summary> /// Downloads the song if it is not already available offline. /// </summary> /// <param name="track"></param> /// <returns>the local uri of the downloaded track</returns> public async Task <Uri> DownloadAsync(MusicItem track) { await BeforeDownload(track); var maybeLocalUri = await PhoneLocalLibrary.Instance.LocalUriIfExists(track); if (maybeLocalUri != null) { return(maybeLocalUri); } return(await AddTransferAsync(track.Source, LocalLibrary.AbsolutePathTo(track))); }
public async Task <List <MusicItem> > GetSongsRecursively(MusicItem songOrDir) { if (!songOrDir.IsDir) { var tracks = new List <MusicItem>(); tracks.Add(songOrDir); return(tracks); } else { return(await MusicProvider.SongsInFolder(songOrDir)); } }
protected void Delete(MusicItem song) { try { var shouldRemove = song.IsSourceLocal; LocalLibrary.Delete(song.Path); // refreshes the items in the current view if (shouldRemove) { MusicFolder.MusicItems.Remove(song); } } catch (Exception e) { Send("Unable to delete. Perhaps the file is in use. " + e.Message); } }
public async Task ValidateThenSubmitDownload(MusicItem item) { try { if (item.IsDir) { await DownloadFolder(item); } else { await DownloadSong(item); } } catch (Exception e) { AddMessage("Unable to download " + item.Name + ". " + e.Message); } }
/// <summary> /// Downloads the song in the background if it's not already available offline. /// </summary> /// <param name="track"></param> public async Task SubmitDownload(MusicItem track) { await BeforeDownload(track); try { var maybeLocalUri = await PhoneLocalLibrary.Instance.LocalUriIfExists(track); if (maybeLocalUri == null) { // For Subsonic, the response may be transcoded audio, in which case the // path to the track, which has the original file extension as stored on // the server, may be incorrect (example: response is transcoded to .mp3, // path is .flac). // TODO: Ensure that the file is stored locally with the correct extension, // that is, find out whether the response is transcoded. var destination = LocalLibrary.AbsolutePathTo(track); var downloadUri = MusicProvider.DownloadUriFor(track); // Only downloads tracks that are stored as MP3s, because this app does not support other local file formats. if (destination.EndsWith("mp3")) { var downloadable = new Downloadable(downloadUri, destination); if (LoadTransfersCount() < 3) { AddTransfer(downloadUri, destination); } else { // add the download to persistent storage from which it will be taken // later when there are fewer concurrent downloads DownloadDataContext.Add(downloadable); } } } } catch (PathTooLongException) { // Thrown if track.Path is about over 190 characters long, but I'm not sure what // the limit is and I don't want to be too defensive with this so I catch and // suppress the exception when it occurs. // The exception says "The specified path, file name, or both are too long. // The fully qualified file name must be less than 260 characters, and the // directory name must be less than 248 characters.", however, I don't know // the length of the fully qualified path name, so 190 chars is an estimate. AddMessage("Download of " + track.Name + " failed. The path is too long: " + track.Path); } }
private string GroupKeyOf(MusicItem item) { if (item.IsDir) { var lowerCase = item.Name.ToLowerInvariant(); // crashes if item.Name.Length == 0 if (char.IsDigit(lowerCase, index: 0)) { return(Grouping.DigitGroupHeader); } else { return(lowerCase.ToCharArray()[0].ToString()); } } else { return(Grouping.SongGroupHeader); } }
public async Task OnSingleMusicItemSelected(MusicItem item) { await WithExceptionEvents(async() => { LibraryScrollPositions[MusicFolder.FolderId] = item; //MusicFolder.LatestSelection = item; if (item.IsDir) { string folderPath = MusicFolder.DisplayablePath == String.Empty ? item.Name : MusicFolder.DisplayablePath + "/" + item.Name; var folderIdentifier = MusicProvider.DirectoryIdentifier(item); var folderJson = Json.SerializeToString(new FolderMeta(folderIdentifier, folderPath)); // encode the folder id so we can pass it in a uri query parameter var encodedFolderId = Strings.encode(folderJson); // user clicked a music directory Navigator.NavigateWithinSamePage(encodedFolderId); } else { // user clicked a song, let's play! await MusicPlayer.PlaySong(item); } }); }
public PlaylistMusicItem(MusicItem song, int index) { Song = song; Index = index; }
public static void SetDownloadStatus(MusicItem track, ulong bytesReceived) { track.IsDownloading = bytesReceived < (ulong)track.Size; track.BytesReceived = bytesReceived; }
public async Task OnSingleMusicItemSelected(MusicItem item) { await MusicPlayer.PlaySong(item); }
public Task SubmitDownload(MusicItem track, string username, string password) { return(SubmitDownload(track)); }
// rely on the credentials in the query string because the background downloader // in WP doesn't support custom HTTP headers public Task <Uri> DownloadAsync(MusicItem track, string username, string password) { return(DownloadAsync(track)); }
// TODO: refactor private Task BeforeDownload(MusicItem track) { return(AsyncTasks.Noop()); }
public static string DirOnlySortKey(MusicItem item) { return(item.IsDir ? "a" + item.Name : "b"); }
/// <summary> /// /// </summary> /// <param name="item"></param> /// <returns>a sort key where directories are first, then based on name</returns> public static string SortKey(MusicItem item) { string prefix = item.IsDir ? "a" : "b"; return(prefix + item.Name); }