diff --git a/MusicApp/Resources/Portable Class/Browse.cs b/MusicApp/Resources/Portable Class/Browse.cs index 986f892..da348e8 100644 --- a/MusicApp/Resources/Portable Class/Browse.cs +++ b/MusicApp/Resources/Portable Class/Browse.cs @@ -373,6 +373,57 @@ namespace MusicApp.Resources.Portable_Class builder.Show(); } + public static long GetPlaylistID(string playlistName) + { + Android.Net.Uri uri = MediaStore.Audio.Playlists.ExternalContentUri; + Looper.Prepare(); + CursorLoader loader = new CursorLoader(Android.App.Application.Context, uri, null, null, null, null); + ICursor cursor = (ICursor)loader.LoadInBackground(); + + if (cursor != null && cursor.MoveToFirst()) + { + int nameID = cursor.GetColumnIndex(MediaStore.Audio.Playlists.InterfaceConsts.Name); + int plID = cursor.GetColumnIndex(MediaStore.Audio.Playlists.InterfaceConsts.Id); + do + { + string name = cursor.GetString(nameID); + + if (name != playlistName) + continue; + + return cursor.GetLong(plID); + } + while (cursor.MoveToNext()); + cursor.Close(); + } + + //Playlist do not exist, create it + ContentResolver resolver = act.ContentResolver; + ContentValues value = new ContentValues(); + value.Put(MediaStore.Audio.Playlists.InterfaceConsts.Name, playlistName); + resolver.Insert(uri, value); + + CursorLoader loaderBis = new CursorLoader(Android.App.Application.Context, uri, null, null, null, null); + ICursor cursorBis = (ICursor)loader.LoadInBackground(); + + if (cursorBis != null && cursorBis.MoveToFirst()) + { + int nameID = cursorBis.GetColumnIndex(MediaStore.Audio.Playlists.InterfaceConsts.Name); + int getplaylistID = cursorBis.GetColumnIndex(MediaStore.Audio.Playlists.InterfaceConsts.Id); + do + { + string name = cursorBis.GetString(nameID); + long id = cursorBis.GetLong(getplaylistID); + + if (playlistName == name) + return id; + } + while (cursorBis.MoveToNext()); + cursorBis.Close(); + } + return -1; + } + public async static Task CheckWritePermission() { const string permission = Manifest.Permission.WriteExternalStorage; diff --git a/MusicApp/Resources/Portable Class/Downloader.cs b/MusicApp/Resources/Portable Class/Downloader.cs index 1e1f041..261308b 100644 --- a/MusicApp/Resources/Portable Class/Downloader.cs +++ b/MusicApp/Resources/Portable Class/Downloader.cs @@ -2,7 +2,12 @@ using Android.App; using Android.Content; using Android.Content.PM; +using Android.Database; +using Android.Media; +using Android.Net; using Android.OS; +using Android.Provider; +using Android.Support.Design.Widget; using Android.Support.V4.App; using MusicApp.Resources.values; using System.Collections.Generic; @@ -19,7 +24,7 @@ using File = System.IO.File; namespace MusicApp.Resources.Portable_Class { [Service] - public class Downloader : Service + public class Downloader : Service, MediaScannerConnection.IOnScanCompletedListener { public static Downloader instance; public string downloadPath; @@ -28,8 +33,8 @@ namespace MusicApp.Resources.Portable_Class private int currentStrike = 0; private static bool isDownloading = false; private NotificationCompat.Builder notification; - private int notificationID = 1001; - private int RequestCode = 5465; + private const int notificationID = 1001; + private const int RequestCode = 5465; public override IBinder OnBind(Intent intent) @@ -83,20 +88,31 @@ namespace MusicApp.Resources.Portable_Class currentStrike++; CreateNotification(file.name); + if (YoutubeEngine.FileIsAlreadyDownloaded(file.videoID) && !file.skipCheck) + { + Snackbar.Make(MainActivity.instance.FindViewById(Resource.Id.snackBar), file.name + " is already on your device.", Snackbar.LengthShort).SetAction("Download Anyway", (v) => + { + file.skipCheck = true; + Download(file); + }).Show(); + } + YoutubeClient client = new YoutubeClient(); Video videoInfo = await client.GetVideoAsync(file.videoID); MediaStreamInfoSet mediaStreamInfo = await client.GetVideoMediaStreamInfosAsync(file.videoID); AudioStreamInfo streamInfo = mediaStreamInfo.Audio.Where(x => x.Container == Container.M4A).OrderBy(s => s.Bitrate).Last(); - System.Console.WriteLine("&" + streamInfo.Url); - //With Where container == Container.M4A, output file should be a m4a file, so ffmpeg is usless - string fileExtension = streamInfo.Container.GetFileExtension(); string fileName = $"{videoInfo.Title}.{fileExtension}"; - string filePath = Path.Combine(path, fileName); + string outpath = path; + if(file.playlist != null) + { + outpath = Path.Combine(path, file.playlist); + Directory.CreateDirectory(outpath); + } - System.Console.WriteLine("&Client and path created"); + string filePath = Path.Combine(outpath, fileName); MediaStream input = await client.GetMediaStreamAsync(streamInfo); @@ -104,18 +120,16 @@ namespace MusicApp.Resources.Portable_Class await input.CopyToAsync(output); output.Dispose(); - System.Console.WriteLine("&Webm Output created"); - - SetMetaData(filePath, videoInfo.Title, videoInfo.Author, videoInfo.Thumbnails.HighResUrl, file.videoID); + SetMetaData(filePath, videoInfo.Title, videoInfo.Author, videoInfo.Thumbnails.HighResUrl, file.videoID, file.playlist); isDownloading = false; if (queue.Count != 0) DownloadAudio(queue[0], path); } - private void SetMetaData(string filePath, string title, string artist, string album, string youtubeID) + private void SetMetaData(string filePath, string title, string artist, string album, string youtubeID, string playlist) { - Stream stream = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite); + System.IO.Stream stream = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite); var meta = TagLib.File.Create(new StreamFileAbstraction(filePath, stream, stream)); meta.Tag.Title = title; @@ -130,9 +144,24 @@ namespace MusicApp.Resources.Portable_Class meta.Tag.Pictures = pictures; meta.Save(); stream.Dispose(); - Android.Media.MediaScannerConnection.ScanFile(this, new string[] { filePath }, null, null); + MediaScannerConnection.ScanFile(this, new string[] { filePath }, null, this); - StopForeground(true); + if (queue.Count == 0) + StopForeground(true); + } + + public void OnScanCompleted(string path, Uri uri) + { + long id = long.Parse(uri.ToString().Substring(uri.ToString().IndexOf("audio/media/") + 12, uri.ToString().Length - uri.ToString().IndexOf("audio/media/") - 12)); + string playlist = path.Substring(downloadPath.Length + 1, path.IndexOf("/", downloadPath.Length) - downloadPath.Length + 1); + + Browse.act = MainActivity.instance; + long playlistID = Browse.GetPlaylistID(playlist); + + ContentValues value = new ContentValues(); + value.Put(MediaStore.Audio.Playlists.Members.AudioId, id); + value.Put(MediaStore.Audio.Playlists.Members.PlayOrder, 0); + ContentResolver.Insert(MediaStore.Audio.Playlists.Members.GetContentUri("external", playlistID), value); } void CreateNotification(string title) diff --git a/MusicApp/Resources/Portable Class/FolderBrowse.cs b/MusicApp/Resources/Portable Class/FolderBrowse.cs index bb08fc4..9f40617 100644 --- a/MusicApp/Resources/Portable Class/FolderBrowse.cs +++ b/MusicApp/Resources/Portable Class/FolderBrowse.cs @@ -348,8 +348,6 @@ namespace MusicApp.Resources.Portable_Class CursorLoader cursorLoader = new CursorLoader(Android.App.Application.Context, musicUri, null, null, null, null); ICursor musicCursor = (ICursor)cursorLoader.LoadInBackground(); - System.Console.WriteLine("&Path: " + path + " URI: " + musicUri.ToString()); - if (musicCursor != null && musicCursor.MoveToFirst()) { int thisID = musicCursor.GetColumnIndex(MediaStore.Audio.Media.InterfaceConsts.Id); diff --git a/MusicApp/Resources/Portable Class/Playlist.cs b/MusicApp/Resources/Portable Class/Playlist.cs index ba30cea..0d01c48 100644 --- a/MusicApp/Resources/Portable Class/Playlist.cs +++ b/MusicApp/Resources/Portable Class/Playlist.cs @@ -21,6 +21,7 @@ namespace MusicApp.Resources.Portable_Class { public static Playlist instance; public RecyclerView ListView; + private bool populated = false; //Local playlists private List playList = new List(); @@ -32,7 +33,6 @@ namespace MusicApp.Resources.Portable_Class private List YtPlaylists = new List(); private PlaylistAdapter adapter; - private bool isEmpty = false; private View emptyView; public override void OnActivityCreated(Bundle savedInstanceState) @@ -43,20 +43,6 @@ namespace MusicApp.Resources.Portable_Class MainActivity.instance.OnPaddingChanged += OnPaddingChanged; } - public void AddEmptyView() - { - if (emptyView.Parent != null) - ((ViewGroup)emptyView.Parent).RemoveView(emptyView); - - Activity.AddContentView(emptyView, View.LayoutParameters); - } - - public void RemoveEmptyView() - { - ViewGroup rootView = Activity.FindViewById(Android.Resource.Id.Content); - rootView.RemoveView(emptyView); - } - private void OnPaddingChanged(object sender, PaddingChange e) { if (MainActivity.paddingBot > e.oldPadding) @@ -69,8 +55,6 @@ namespace MusicApp.Resources.Portable_Class { MainActivity.instance.contentRefresh.Refresh -= OnRefresh; MainActivity.instance.OnPaddingChanged -= OnPaddingChanged; - if (isEmpty) - RemoveEmptyView(); base.OnDestroy(); instance = null; @@ -90,6 +74,8 @@ namespace MusicApp.Resources.Portable_Class public async Task PopulateView() { + populated = false; + //Local playlists playList.Clear(); playlistId.Clear(); @@ -145,6 +131,9 @@ namespace MusicApp.Resources.Portable_Class YouTubeService youtube = YoutubeEngine.youtubeService; + if (instance == null) + return; + PlaylistsResource.ListRequest request = youtube.Playlists.List("snippet,contentDetails"); request.Mine = true; request.MaxResults = 25; @@ -185,6 +174,9 @@ namespace MusicApp.Resources.Portable_Class PlaylistsResource.ListRequest plRequest = youtube.Playlists.List("snippet, contentDetails"); plRequest.Id = section.ContentDetails.Playlists[i]; + if (instance == null) + return; + PlaylistListResponse plResponse = await plRequest.ExecuteAsync(); if (instance == null) @@ -209,6 +201,8 @@ namespace MusicApp.Resources.Portable_Class adapter.SetYtPlaylists(ytPlaylists, true); else adapter.SetYtPlaylists(ytPlaylists, true); + + populated = true; } public static Fragment NewInstance() @@ -225,7 +219,8 @@ namespace MusicApp.Resources.Portable_Class public async Task Refresh() { - await PopulateView(); + if(populated) + await PopulateView(); } private void ListView_ItemClick(object sender, int Position) @@ -290,8 +285,6 @@ namespace MusicApp.Resources.Portable_Class act.SupportActionBar.Title = playlist.GetName(); instance = null; MainActivity.instance.contentRefresh.Refresh -= OnRefresh; - if (isEmpty) - RemoveEmptyView(); if (local) MainActivity.instance.Transition(Resource.Id.contentView, PlaylistTracks.NewInstance(playlist.GetID(), playlist.GetName()), true); @@ -352,7 +345,7 @@ namespace MusicApp.Resources.Portable_Class RemoveYoutubePlaylist(Position, playlist.GetPath()); break; case 4: - YoutubeEngine.DownloadPlaylist(playlist.GetPath()); + YoutubeEngine.DownloadPlaylist(playlist.GetName(), playlist.GetPath()); break; default: break; @@ -373,7 +366,7 @@ namespace MusicApp.Resources.Portable_Class Unfork(Position, playlist.GetPath()); break; case 3: - YoutubeEngine.DownloadPlaylist(playlist.GetPath()); + YoutubeEngine.DownloadPlaylist(playlist.GetName(), playlist.GetPath()); break; default: break; diff --git a/MusicApp/Resources/Portable Class/YoutubeEngine.cs b/MusicApp/Resources/Portable Class/YoutubeEngine.cs index 5ef951c..e5c3b92 100644 --- a/MusicApp/Resources/Portable Class/YoutubeEngine.cs +++ b/MusicApp/Resources/Portable Class/YoutubeEngine.cs @@ -417,19 +417,11 @@ namespace MusicApp.Resources.Portable_Class parseProgress.Visibility = ViewStates.Gone; } - public async static void Download(string name, string videoID, bool skipExistVerification = false) + public async static void Download(string name, string videoID) { ISharedPreferences prefManager = PreferenceManager.GetDefaultSharedPreferences(Android.App.Application.Context); if (prefManager.GetString("downloadPath", null) != null) { - if (FileIsAlreadyDownloaded(videoID) && !skipExistVerification) - { - Snackbar.Make(MainActivity.instance.FindViewById(Resource.Id.snackBar), name + " is already on your device.", Snackbar.LengthShort).SetAction("Download Anyway", (v) => - { - Download(name, videoID, true); - }).Show(); - } - Toast.MakeText(Android.App.Application.Context, "Downloading...", ToastLength.Short).Show(); Context context = Android.App.Application.Context; Intent intent = new Intent(context, typeof(Downloader)); @@ -439,7 +431,7 @@ namespace MusicApp.Resources.Portable_Class await Task.Delay(10); Downloader.instance.downloadPath = prefManager.GetString("downloadPath", null); - Downloader.instance.Download(new DownloadFile(name, videoID)); + Downloader.instance.Download(new DownloadFile(name, videoID, null)); } else { @@ -451,56 +443,56 @@ namespace MusicApp.Resources.Portable_Class } } - public static async void DownloadFiles(string[] names, string[] videoIDs, bool skipExistVerification = false) + public static async void DownloadFiles(string[] names, string[] videoIDs, string playlist) { ISharedPreferences prefManager = PreferenceManager.GetDefaultSharedPreferences(Android.App.Application.Context); if (prefManager.GetString("downloadPath", null) != null) { - if (!skipExistVerification) - { - List downloadedName = new List(); - List downloadedID = new List(); - for (int i = 0; i < names.Length; i++) - { - if (FileIsAlreadyDownloaded(videoIDs[i])) - { - downloadedName.Add(names[i]); - downloadedID.Add(videoIDs[i]); - } - } + //if (!skipExistVerification) + //{ + // List downloadedName = new List(); + // List downloadedID = new List(); + // for (int i = 0; i < names.Length; i++) + // { + // if (FileIsAlreadyDownloaded(videoIDs[i])) + // { + // downloadedName.Add(names[i]); + // downloadedID.Add(videoIDs[i]); + // } + // } - if (downloadedName.Count > 0) - { - List namesList = names.ToList(); - List idList = videoIDs.ToList(); + // if (downloadedName.Count > 0) + // { + // List namesList = names.ToList(); + // List idList = videoIDs.ToList(); - for(int i = 0; i < downloadedName.Count; i++) - { - namesList.Remove(downloadedName[i]); - idList.Remove(downloadedID[i]); - } + // for(int i = 0; i < downloadedName.Count; i++) + // { + // namesList.Remove(downloadedName[i]); + // idList.Remove(downloadedID[i]); + // } - names = namesList.ToArray(); - videoIDs = idList.ToArray(); + // names = namesList.ToArray(); + // videoIDs = idList.ToArray(); - if (downloadedName.Count == 1) - { - Snackbar.Make(MainActivity.instance.FindViewById(Resource.Id.snackBar), downloadedName[0] + " is already on your device.", Snackbar.LengthShort).SetAction("Download this file anyway", (v) => - { - Downloader.instance.Download(new DownloadFile(downloadedName[0], downloadedID[0])); - }).Show(); - } - else - { - Snackbar.Make(MainActivity.instance.FindViewById(Resource.Id.snackBar), downloadedName.Count + " files are already on your device", Snackbar.LengthShort).SetAction("Download all this files anyway", (v) => - { - for(int i = 0; i < downloadedName.Count; i++) - Downloader.instance.Download(new DownloadFile(downloadedName[i], downloadedID[i])); + // if (downloadedName.Count == 1) + // { + // Snackbar.Make(MainActivity.instance.FindViewById(Resource.Id.snackBar), downloadedName[0] + " is already on your device.", Snackbar.LengthShort).SetAction("Download this file anyway", (v) => + // { + // Downloader.instance.Download(new DownloadFile(downloadedName[0], downloadedID[0], playlist)); + // }).Show(); + // } + // else + // { + // Snackbar.Make(MainActivity.instance.FindViewById(Resource.Id.snackBar), downloadedName.Count + " files are already on your device", Snackbar.LengthShort).SetAction("Download all this files anyway", (v) => + // { + // for(int i = 0; i < downloadedName.Count; i++) + // Downloader.instance.Download(new DownloadFile(downloadedName[i], downloadedID[i], playlist)); - }).Show(); - } - } - } + // }).Show(); + // } + // } + //} Toast.MakeText(Android.App.Application.Context, "Downloading...", ToastLength.Short).Show(); Context context = Android.App.Application.Context; @@ -513,7 +505,7 @@ namespace MusicApp.Resources.Portable_Class Downloader.instance.downloadPath = prefManager.GetString("downloadPath", null); for(int i = 0; i < names.Length; i++) - Downloader.instance.Download(new DownloadFile(names[i], videoIDs[i])); + Downloader.instance.Download(new DownloadFile(names[i], videoIDs[i], playlist)); } else { @@ -772,7 +764,7 @@ namespace MusicApp.Resources.Portable_Class PlayFiles(tracks.ToArray()); } - public static async void DownloadPlaylist(string playlistID) + public static async void DownloadPlaylist(string playlist, string playlistID) { List names = new List(); List videoIDs = new List(); @@ -794,7 +786,7 @@ namespace MusicApp.Resources.Portable_Class nextPageToken = ytPlaylist.NextPageToken; } - DownloadFiles(names.ToArray(), videoIDs.ToArray()); + DownloadFiles(names.ToArray(), videoIDs.ToArray(), playlist); } } } diff --git a/MusicApp/Resources/Portable Class/YtAdapter.cs b/MusicApp/Resources/Portable Class/YtAdapter.cs index 00b6da7..5b27405 100644 --- a/MusicApp/Resources/Portable Class/YtAdapter.cs +++ b/MusicApp/Resources/Portable Class/YtAdapter.cs @@ -106,7 +106,7 @@ namespace MusicApp.Resources.Portable_Class YoutubeEngine.ForkPlaylist(playlist.GetPath()); break; case 2: - YoutubeEngine.DownloadPlaylist(playlist.GetPath()); + YoutubeEngine.DownloadPlaylist(playlist.GetName(), playlist.GetPath()); break; default: break; diff --git a/MusicApp/Resources/values/DownloadFile.cs b/MusicApp/Resources/values/DownloadFile.cs index e298de2..e8d6718 100644 --- a/MusicApp/Resources/values/DownloadFile.cs +++ b/MusicApp/Resources/values/DownloadFile.cs @@ -5,11 +5,14 @@ { public string name; public string videoID; + public string playlist; + public bool skipCheck = false; - public DownloadFile(string name, string videoID) + public DownloadFile(string name, string videoID, string playlist) { this.name = name; this.videoID = videoID; + this.playlist = playlist; } } } \ No newline at end of file