mirror of
https://github.com/zoriya/Opus.git
synced 2025-12-06 06:26:15 +00:00
1161 lines
53 KiB
C#
1161 lines
53 KiB
C#
using Android.App;
|
|
using Android.Content;
|
|
using Android.Database;
|
|
using Android.Graphics;
|
|
using Android.OS;
|
|
using Android.Provider;
|
|
using Android.Support.Design.Widget;
|
|
using Android.Support.V7.Widget;
|
|
using Android.Views;
|
|
using Android.Widget;
|
|
using Google.Apis.YouTube.v3;
|
|
using Google.Apis.YouTube.v3.Data;
|
|
using Opus.Adapter;
|
|
using Opus.Api.Services;
|
|
using Opus.DataStructure;
|
|
using Opus.Others;
|
|
using SQLite;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using static Android.Provider.MediaStore.Audio;
|
|
using CursorLoader = Android.Support.V4.Content.CursorLoader;
|
|
using Environment = System.Environment;
|
|
using Path = System.IO.Path;
|
|
using Playlist = Google.Apis.YouTube.v3.Data.Playlist;
|
|
using PlaylistItem = Opus.DataStructure.PlaylistItem;
|
|
using AlertDialog = Android.Support.V7.App.AlertDialog;
|
|
using Uri = Android.Net.Uri;
|
|
|
|
namespace Opus.Api
|
|
{
|
|
public class PlaylistManager
|
|
{
|
|
/// <summary>
|
|
/// Return a complete PlaylistItem object from the youtube id of the playlist
|
|
/// </summary>
|
|
/// <param name="playlistID"></param>
|
|
/// <returns></returns>
|
|
public static async Task<PlaylistItem> GetPlaylist(string playlistID)
|
|
{
|
|
PlaylistsResource.ListRequest request = YoutubeManager.YoutubeService.Playlists.List("snippet");
|
|
request.Id = playlistID;
|
|
|
|
PlaylistListResponse response = await request.ExecuteAsync();
|
|
|
|
if (response.Items.Count > 0)
|
|
{
|
|
Playlist result = response.Items[0];
|
|
return new PlaylistItem(result.Snippet.Title, -1, playlistID)
|
|
{
|
|
HasWritePermission = false,
|
|
ImageURL = result.Snippet.Thumbnails.High.Url,
|
|
Owner = result.Snippet.ChannelTitle
|
|
};
|
|
}
|
|
else
|
|
return null;
|
|
}
|
|
|
|
#region PlayInOrder
|
|
/// <summary>
|
|
/// Play all tracks of a playlist in the default order. Handle both youtube and local playlists.
|
|
/// </summary>
|
|
/// <param name="item"></param>
|
|
/// <param name="startingPosition">The position where you want to start playing</param>
|
|
public static void PlayInOrder(PlaylistItem item, int startingPosition = 0)
|
|
{
|
|
if (item.LocalID != -1 && item.LocalID != 0 || item.SyncState == SyncState.True)
|
|
PlayInOrder(item.LocalID, startingPosition);
|
|
else
|
|
PlayInOrder(item.YoutubeID, startingPosition);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Play a local playlist in the default order.
|
|
/// </summary>
|
|
/// <param name="LocalID"></param>
|
|
/// <param name="startingPosition">The position where you want to start playing</param>
|
|
public static async void PlayInOrder(long LocalID, int startingPosition = 0)
|
|
{
|
|
List<Song> tracks = await GetTracksFromLocalPlaylist(LocalID);
|
|
|
|
if (tracks.Count == 0)
|
|
return;
|
|
|
|
SongManager.Play(tracks[startingPosition]);
|
|
tracks.RemoveAt(startingPosition);
|
|
|
|
await Task.Delay(1000);
|
|
|
|
while (MusicPlayer.instance == null)
|
|
await Task.Delay(10);
|
|
|
|
MusicPlayer.instance.InsertToQueue(0, tracks.GetRange(0, startingPosition).ToArray());
|
|
MusicPlayer.currentID = startingPosition;
|
|
Queue.instance?.RefreshCurrent();
|
|
Player.instance?.RefreshPlayer();
|
|
MusicPlayer.instance.AddToQueue(tracks.GetRange(startingPosition, tracks.Count - startingPosition));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Play a youtube playlist in the default order.
|
|
/// </summary>
|
|
/// <param name="YoutubeID"></param>
|
|
/// <param name="startingPosition">The position where you want to start playing</param>
|
|
public static async void PlayInOrder(string YoutubeID, int startingPosition = 0)
|
|
{
|
|
List<Song> tracks = await GetTracksFromYoutubePlaylist(YoutubeID, (song) =>
|
|
{
|
|
SongManager.Play(song);
|
|
}, startingPosition);
|
|
|
|
if (tracks.Count == 0)
|
|
return;
|
|
|
|
tracks.RemoveAt(startingPosition);
|
|
|
|
await Task.Delay(1000);
|
|
while (MusicPlayer.instance == null)
|
|
await Task.Delay(10);
|
|
|
|
MusicPlayer.instance.InsertToQueue(0, tracks.GetRange(0, startingPosition).ToArray());
|
|
MusicPlayer.currentID = startingPosition;
|
|
Queue.instance?.RefreshCurrent();
|
|
Player.instance?.RefreshPlayer();
|
|
MusicPlayer.instance.AddToQueue(tracks.GetRange(startingPosition, tracks.Count - startingPosition));
|
|
}
|
|
#endregion
|
|
|
|
#region Shuffle
|
|
/// <summary>
|
|
/// Play all tracks of a playlist in a random order. Handle both youtube and local playlists.
|
|
/// </summary>
|
|
/// <param name="item"></param>
|
|
public static void Shuffle(PlaylistItem item)
|
|
{
|
|
if (item.LocalID != -1 && item.LocalID != 0 || item.SyncState == SyncState.True)
|
|
Shuffle(item.LocalID);
|
|
else
|
|
Shuffle(item.YoutubeID);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Play all tracks of a local playlist in a random order.
|
|
/// </summary>
|
|
/// <param name="LocalID"></param>
|
|
public async static void Shuffle(long LocalID)
|
|
{
|
|
List<Song> tracks = await GetTracksFromLocalPlaylist(LocalID);
|
|
if (tracks.Count == 0)
|
|
return;
|
|
|
|
Random r = new Random();
|
|
tracks = tracks.OrderBy(x => r.Next()).ToList();
|
|
|
|
SongManager.Play(tracks[0]);
|
|
tracks.RemoveAt(0);
|
|
|
|
await Task.Delay(1000);
|
|
while (MusicPlayer.instance == null)
|
|
await Task.Delay(10);
|
|
|
|
MusicPlayer.instance.AddToQueue(tracks);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Play all tracks of a youtube playlist in a random order.
|
|
/// </summary>
|
|
/// <param name="YoutubeID"></param>
|
|
public static async void Shuffle(string YoutubeID)
|
|
{
|
|
Random r = new Random();
|
|
int playPos = r.Next(50);
|
|
List<Song> tracks = await GetTracksFromYoutubePlaylist(YoutubeID, (song) =>
|
|
{
|
|
MusicPlayer.currentID = -1;
|
|
SongManager.Play(song);
|
|
}, playPos);
|
|
|
|
if (tracks.Count == 0)
|
|
return;
|
|
|
|
tracks.RemoveAt(playPos);
|
|
tracks = tracks.OrderBy(x => r.Next()).ToList();
|
|
MusicPlayer.instance.AddToQueue(tracks);
|
|
}
|
|
#endregion
|
|
|
|
#region AddToQueue
|
|
/// <summary>
|
|
/// Add every song of a playlist in the queue. (Using default order). Handle both local and youtube playlists.
|
|
/// </summary>
|
|
/// <param name="item"></param>
|
|
public static void AddToQueue(PlaylistItem item)
|
|
{
|
|
if (item.LocalID != -1 && item.LocalID != 0 || item.SyncState == SyncState.True)
|
|
AddToQueue(item.LocalID);
|
|
else
|
|
AddToQueue(item.YoutubeID);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add every song of a local playlist in the queue. (Using default order).
|
|
/// </summary>
|
|
/// <param name="LocalID"></param>
|
|
public async static void AddToQueue(long LocalID)
|
|
{
|
|
if (MusicPlayer.instance == null || MusicPlayer.queue == null || MusicPlayer.queue?.Count == 0)
|
|
{
|
|
PlayInOrder(LocalID);
|
|
return;
|
|
}
|
|
|
|
List<Song> tracks = await GetTracksFromLocalPlaylist(LocalID);
|
|
MusicPlayer.instance.AddToQueue(tracks);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add every song of a youtube playlist in the queue. (Using default order).
|
|
/// </summary>
|
|
/// <param name="YoutubeID"></param>
|
|
public static async void AddToQueue(string YoutubeID)
|
|
{
|
|
if (MusicPlayer.instance == null || MusicPlayer.queue == null || MusicPlayer.queue?.Count == 0)
|
|
{
|
|
PlayInOrder(YoutubeID);
|
|
return;
|
|
}
|
|
|
|
MusicPlayer.instance.AddToQueue((await GetTracksFromYoutubePlaylist(YoutubeID)));
|
|
}
|
|
#endregion
|
|
|
|
#region UI
|
|
/// <summary>
|
|
/// Display the create playlist dialog (where the user can choose a location and a name). After completing the creation, the songs array will be added to this playlist.
|
|
/// </summary>
|
|
/// <param name="songs"></param>
|
|
public static void CreatePlalistDialog(Song[] songs)
|
|
{
|
|
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.instance, MainActivity.dialogTheme);
|
|
builder.SetTitle(Resource.String.new_playlist);
|
|
View view = MainActivity.instance.LayoutInflater.Inflate(Resource.Layout.CreatePlaylistDialog, null);
|
|
builder.SetView(view);
|
|
PlaylistLocationAdapter adapter = new PlaylistLocationAdapter(MainActivity.instance, Android.Resource.Layout.SimpleSpinnerItem, new string[] { MainActivity.instance.GetString(Resource.String.create_local), MainActivity.instance.GetString(Resource.String.create_youtube), MainActivity.instance.GetString(Resource.String.create_synced) })
|
|
{
|
|
YoutubeWorkflow = YoutubeManager.IsUsingAPI || (songs.Length == 1 && songs[0].YoutubeID == null) ? false : true
|
|
};
|
|
adapter.SetDropDownViewResource(Android.Resource.Layout.SimpleSpinnerDropDownItem);
|
|
view.FindViewById<Spinner>(Resource.Id.playlistLocation).Adapter = adapter;
|
|
builder.SetNegativeButton(Resource.String.cancel, (senderAlert, args) => { });
|
|
builder.SetPositiveButton(Resource.String.ok, (senderAlert, args) =>
|
|
{
|
|
switch (view.FindViewById<Spinner>(Resource.Id.playlistLocation).SelectedItemPosition)
|
|
{
|
|
case 0:
|
|
CreateLocalPlaylist(view.FindViewById<EditText>(Resource.Id.playlistName).Text, songs);
|
|
break;
|
|
case 1:
|
|
CreateYoutubePlaylist(view.FindViewById<EditText>(Resource.Id.playlistName).Text, songs);
|
|
break;
|
|
case 2:
|
|
CreateLocalPlaylist(view.FindViewById<EditText>(Resource.Id.playlistName).Text, songs, true);
|
|
CreateYoutubePlaylist(view.FindViewById<EditText>(Resource.Id.playlistName).Text, songs);
|
|
break;
|
|
}
|
|
});
|
|
builder.Show();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Display the dialog listing all the playlist where you can add a song. If the song is already contained in one of the playlist, it will be displayed.
|
|
/// </summary>
|
|
/// <param name="item"></param>
|
|
public static async void AddSongToPlaylistDialog(Song item)
|
|
{
|
|
if (item.YoutubeID == null)
|
|
item = LocalManager.CompleteItem(item);
|
|
|
|
List<PlaylistItem> playlists = new List<PlaylistItem>();
|
|
List<PlaylistItem> synced = new List<PlaylistItem>();
|
|
|
|
(List<PlaylistItem> localPlaylists, string error) = await GetLocalPlaylists();
|
|
if (localPlaylists != null)
|
|
{
|
|
foreach (PlaylistItem playlist in localPlaylists)
|
|
playlist.SongContained = await LocalManager.SongIsContained(item.LocalID, playlist.LocalID);
|
|
|
|
(playlists, synced) = await ProcessSyncedPlaylists(localPlaylists);
|
|
playlists.AddRange(synced);
|
|
}
|
|
|
|
PlaylistItem Loading = new PlaylistItem("Loading", null);
|
|
playlists.Add(Loading);
|
|
|
|
View Layout = MainActivity.instance.LayoutInflater.Inflate(Resource.Layout.AddToPlaylistLayout, null);
|
|
if (MainActivity.Theme == 1)
|
|
{
|
|
Layout.FindViewById<ImageView>(Resource.Id.leftIcon).SetColorFilter(Color.White);
|
|
Layout.FindViewById<View>(Resource.Id.divider).SetBackgroundColor(Color.White);
|
|
}
|
|
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.instance, MainActivity.dialogTheme);
|
|
builder.SetTitle(Resource.String.save_playlist);
|
|
builder.SetView(Layout);
|
|
RecyclerView ListView = Layout.FindViewById<RecyclerView>(Resource.Id.recycler);
|
|
ListView.SetLayoutManager(new LinearLayoutManager(MainActivity.instance));
|
|
AddToPlaylistAdapter adapter = new AddToPlaylistAdapter(playlists);
|
|
ListView.SetAdapter(adapter);
|
|
adapter.ItemClick += async (sender, position) =>
|
|
{
|
|
AddToPlaylistHolder holder = (AddToPlaylistHolder)ListView.GetChildViewHolder(ListView.GetChildAt(position));
|
|
bool add = !holder.Added.Checked;
|
|
holder.Added.Checked = add;
|
|
|
|
PlaylistItem playlist = playlists[position];
|
|
if (add)
|
|
{
|
|
if (playlist.LocalID != 0)
|
|
{
|
|
if (item.LocalID == 0 || item.LocalID == -1)
|
|
YoutubeManager.Download(new[] { item }, playlist.Name);
|
|
else
|
|
LocalManager.AddToPlaylist(new[] { item }, playlist.Name, playlist.LocalID);
|
|
}
|
|
if (playlist.YoutubeID != null)
|
|
YoutubeManager.AddToPlaylist(new[] { item }, playlist.YoutubeID);
|
|
}
|
|
else
|
|
{
|
|
if (playlist.YoutubeID != null)
|
|
item = await CompleteItem(item, playlist.YoutubeID);
|
|
|
|
|
|
if (item.TrackID != null)
|
|
RemoveFromYoutubePlaylist(item.TrackID);
|
|
|
|
if (playlist.LocalID != 0)
|
|
{
|
|
ContentResolver resolver = MainActivity.instance.ContentResolver;
|
|
Uri plUri = Playlists.Members.GetContentUri("external", playlist.LocalID);
|
|
resolver.Delete(plUri, Playlists.Members.AudioId + "=?", new string[] { item.LocalID.ToString() });
|
|
}
|
|
}
|
|
};
|
|
builder.SetPositiveButton(Resource.String.ok, (sender, e) => { });
|
|
AlertDialog dialog = builder.Create();
|
|
Layout.FindViewById<LinearLayout>(Resource.Id.CreatePlaylist).Click += (sender, e) => { dialog.Dismiss(); CreatePlalistDialog(new[] { item }); };
|
|
dialog.Show();
|
|
|
|
if (item.YoutubeID == null)
|
|
{
|
|
if (item.YoutubeID == null)
|
|
{
|
|
Toast.MakeText(MainActivity.instance, Resource.String.playlist_add_song_not_found, ToastLength.Long).Show();
|
|
playlists.Remove(Loading);
|
|
adapter.NotifyItemRemoved(playlists.Count);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (YoutubeManager.IsUsingAPI || !await MainActivity.instance.WaitForYoutube())
|
|
{
|
|
Toast.MakeText(MainActivity.instance, Resource.String.youtube_loading_error, ToastLength.Long).Show();
|
|
playlists.Remove(Loading);
|
|
adapter.NotifyItemRemoved(playlists.Count);
|
|
return;
|
|
}
|
|
|
|
(List<PlaylistItem> youtube, string er) = await GetOwnedYoutubePlaylists(synced, (playlist, position) =>
|
|
{
|
|
AddToPlaylistHolder holder = (AddToPlaylistHolder)ListView.GetChildViewHolder(ListView.GetChildAt(position));
|
|
holder.SyncLoading.Visibility = ViewStates.Gone;
|
|
holder.Status.Visibility = ViewStates.Visible;
|
|
holder.Status.SetImageResource(Resource.Drawable.Sync);
|
|
});
|
|
|
|
if(youtube != null)
|
|
{
|
|
foreach (PlaylistItem playlist in youtube)
|
|
playlist.SongContained = await YoutubeManager.SongIsContained(item.YoutubeID, playlist.YoutubeID);
|
|
|
|
int positionStart = playlists.IndexOf(Loading);
|
|
playlists.Remove(Loading);
|
|
playlists.AddRange(youtube);
|
|
adapter.NotifyItemRangeInserted(positionStart, youtube.Count - 1);
|
|
adapter.NotifyItemChanged(playlists.Count);
|
|
}
|
|
else
|
|
{
|
|
playlists.Remove(Loading);
|
|
adapter.NotifyItemRemoved(playlists.Count);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Display a dialog that will allow the user to rename the playlist.
|
|
/// </summary>
|
|
/// <param name="item">The playlist you want to rename</param>
|
|
/// <param name="UiCallback">A callback called after the rename</param>
|
|
public static void Rename(PlaylistItem item, Action UiCallback = null)
|
|
{
|
|
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.instance, MainActivity.dialogTheme);
|
|
builder.SetTitle(Resource.String.rename_playlist);
|
|
View view = MainActivity.instance.LayoutInflater.Inflate(Resource.Layout.CreatePlaylistDialog, null);
|
|
view.FindViewById(Resource.Id.playlistLocation).Visibility = ViewStates.Gone;
|
|
builder.SetView(view);
|
|
builder.SetNegativeButton(Resource.String.cancel, (senderAlert, args) => { });
|
|
builder.SetPositiveButton(Resource.String.rename, async (senderAlert, args) =>
|
|
{
|
|
if (item.LocalID != 0 && !await MainActivity.instance.GetWritePermission())
|
|
return;
|
|
|
|
string newName = view.FindViewById<EditText>(Resource.Id.playlistName).Text;
|
|
|
|
if (item.YoutubeID != null)
|
|
{
|
|
try
|
|
{
|
|
Playlist playlist = new Playlist
|
|
{
|
|
Snippet = new PlaylistSnippet()
|
|
};
|
|
playlist.Snippet.Title = newName;
|
|
playlist.Id = item.YoutubeID;
|
|
|
|
await YoutubeManager.YoutubeService.Playlists.Update(playlist, "snippet").ExecuteAsync();
|
|
}
|
|
catch (System.Net.Http.HttpRequestException)
|
|
{
|
|
MainActivity.instance.Timout();
|
|
}
|
|
}
|
|
if (item.LocalID != 0)
|
|
{
|
|
ContentValues value = new ContentValues();
|
|
value.Put(Playlists.InterfaceConsts.Name, newName);
|
|
MainActivity.instance.ContentResolver.Update(Playlists.ExternalContentUri, value, Playlists.InterfaceConsts.Id + "=?", new string[] { item.LocalID.ToString() });
|
|
}
|
|
|
|
item.Name = newName;
|
|
UiCallback?.Invoke();
|
|
});
|
|
builder.Show();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Display a dialog that allow the user to delete the playlist (from youtube, from the local storage or both).
|
|
/// </summary>
|
|
/// <param name="item"></param>
|
|
/// <param name="UiCallback">Called when the user deleted the playlist</param>
|
|
public static void Delete(PlaylistItem item, Action UiCallback)
|
|
{
|
|
AlertDialog dialog = new AlertDialog.Builder(MainActivity.instance, MainActivity.dialogTheme)
|
|
.SetTitle(MainActivity.instance.GetString(Resource.String.delete_playlist, item.Name))
|
|
.SetPositiveButton(Resource.String.yes, async (sender, e) =>
|
|
{
|
|
if (item.LocalID != 0 && !await MainActivity.instance.GetWritePermission())
|
|
return;
|
|
|
|
if (item.SyncState != SyncState.False)
|
|
StopSyncing(item);
|
|
|
|
if (item.YoutubeID != null)
|
|
{
|
|
if (item.HasWritePermission)
|
|
{
|
|
try
|
|
{
|
|
PlaylistsResource.DeleteRequest deleteRequest = YoutubeManager.YoutubeService.Playlists.Delete(item.YoutubeID);
|
|
await deleteRequest.ExecuteAsync();
|
|
}
|
|
catch (System.Net.Http.HttpRequestException)
|
|
{
|
|
MainActivity.instance.Timout();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//try
|
|
//{
|
|
// ChannelSectionsResource.ListRequest forkedRequest = YoutubeEngine.youtubeService.ChannelSections.List("snippet,contentDetails");
|
|
// forkedRequest.Mine = true;
|
|
// ChannelSectionListResponse forkedResponse = await forkedRequest.ExecuteAsync();
|
|
|
|
// foreach (ChannelSection section in forkedResponse.Items)
|
|
// {
|
|
// if (section.Snippet.Title == "Saved Playlists")
|
|
// {
|
|
// section.ContentDetails.Playlists.Remove(YoutubeID);
|
|
// ChannelSectionsResource.UpdateRequest request = YoutubeEngine.youtubeService.ChannelSections.Update(section, "snippet,contentDetails");
|
|
// ChannelSection response = await request.ExecuteAsync();
|
|
// }
|
|
// }
|
|
//}
|
|
//catch (System.Net.Http.HttpRequestException)
|
|
//{
|
|
// MainActivity.instance.Timout();
|
|
//}
|
|
}
|
|
}
|
|
if (item.LocalID != 0)
|
|
{
|
|
ContentResolver resolver = MainActivity.instance.ContentResolver;
|
|
Uri uri = Playlists.ExternalContentUri;
|
|
resolver.Delete(Playlists.ExternalContentUri, Playlists.InterfaceConsts.Id + "=?", new string[] { item.LocalID.ToString() });
|
|
}
|
|
|
|
UiCallback?.Invoke();
|
|
})
|
|
.SetNegativeButton(Resource.String.no, (sender, e) => { })
|
|
.Create();
|
|
dialog.Show();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Display the stop syncing dialog.
|
|
/// </summary>
|
|
/// <param name="item"></param>
|
|
/// <param name="UiCallback">Callback called when the playlist is not synced anymore.</param>
|
|
public static void StopSyncingDialog(PlaylistItem item, Action UiCallback)
|
|
{
|
|
AlertDialog dialog = new AlertDialog.Builder(MainActivity.instance, MainActivity.dialogTheme)
|
|
.SetTitle(Resource.String.stop_syncing)
|
|
.SetPositiveButton(Resource.String.yes, (sender, e) => { StopSyncing(item); UiCallback?.Invoke(); })
|
|
.SetNegativeButton(Resource.String.no, (sender, e) => { })
|
|
.Create();
|
|
dialog.Show();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Remove a playlist from the synced database.
|
|
/// </summary>
|
|
/// <param name="item"></param>
|
|
public async static void StopSyncing(PlaylistItem item)
|
|
{
|
|
await Task.Run(() =>
|
|
{
|
|
SQLiteConnection db = new SQLiteConnection(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), "SyncedPlaylists.sqlite"));
|
|
db.CreateTable<PlaylistItem>();
|
|
|
|
db.Delete(db.Table<PlaylistItem>().ToList().Find(x => x.LocalID == item.LocalID));
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// Display the dialog to remove a track from the playlist. This can be undo by clicking on the "undo" button of the snackbar that will apear.
|
|
/// </summary>
|
|
/// <param name="item">The playlist item</param>
|
|
/// <param name="song">The song item</param>
|
|
/// <param name="RemovedCallback">A callback called when the user click ok in the dialog. You just need to remove the track from the UI, everything else is already handled.</param>
|
|
/// <param name="CancelledCallback">A callback called when the user click cancel in the dialog</param>
|
|
/// <param name="UndoCallback">A callback called when the user click undo in the snackbar</param>
|
|
public async static void RemoveTrackFromPlaylistDialog(PlaylistItem item, Song song, Action RemovedCallback, Action CancelledCallback, Action UndoCallback)
|
|
{
|
|
if (!await MainActivity.instance.GetWritePermission())
|
|
return;
|
|
|
|
AlertDialog dialog = new AlertDialog.Builder(MainActivity.instance, MainActivity.dialogTheme)
|
|
.SetTitle(MainActivity.instance.GetString(Resource.String.remove_from_playlist, song.Title))
|
|
.SetPositiveButton(Resource.String.yes, async (sender, e) =>
|
|
{
|
|
if(item.LocalID != -1)
|
|
RemoveFromLocalPlaylist(song, item.LocalID);
|
|
|
|
if (item.YoutubeID != null)
|
|
{
|
|
if (song.TrackID == null)
|
|
song = await CompleteItem(song, item.YoutubeID);
|
|
}
|
|
|
|
RemovedCallback?.Invoke();
|
|
|
|
RemoveTrackFromPlaylistCallback callback = new RemoveTrackFromPlaylistCallback(song, item.LocalID);
|
|
Snackbar snackBar = Snackbar.Make(MainActivity.instance.FindViewById(Resource.Id.snackBar), (song.Title.Length > 20 ? song.Title.Substring(0, 17) + "..." : song.Title) + MainActivity.instance.GetString(Resource.String.removed_from_playlist), Snackbar.LengthLong)
|
|
.SetAction(MainActivity.instance.GetString(Resource.String.undo), (v) =>
|
|
{
|
|
callback.canceled = true;
|
|
UndoCallback?.Invoke();
|
|
});
|
|
snackBar.AddCallback(callback);
|
|
snackBar.View.FindViewById<TextView>(Resource.Id.snackbar_text).SetTextColor(Color.White);
|
|
snackBar.Show();
|
|
})
|
|
.SetNegativeButton(Resource.String.no, (sender, e) => { CancelledCallback?.Invoke(); })
|
|
.Create();
|
|
dialog.Show();
|
|
}
|
|
#endregion
|
|
|
|
#region GetPlaylists
|
|
/// <summary>
|
|
/// This method will return all playlists available on the local storage in the array "playlists".
|
|
/// If there is an error, the Task will return an error message to display to the user.
|
|
/// </summary>
|
|
/// <param name="askForPermission">Should we ask for the read perission or simple return nothing if the user has not given the read permission.</param>
|
|
/// <returns>The List<PlaylistItem> contains all the local playlists or is null if there is an erorr. The string is the error message. If there is no error, this string is null.</returns>
|
|
public static async Task<(List<PlaylistItem>, string)> GetLocalPlaylists(bool askForPermission = true)
|
|
{
|
|
if (askForPermission)
|
|
{
|
|
if (!await MainActivity.instance.GetReadPermission())
|
|
return (null, Application.Context.GetString(Resource.String.localpl_noperm));
|
|
}
|
|
else
|
|
{
|
|
if (!MainActivity.instance.HasReadPermission())
|
|
return (null, Application.Context.GetString(Resource.String.localpl_noperm));
|
|
}
|
|
|
|
List<PlaylistItem> playlists = new List<PlaylistItem>();
|
|
|
|
Uri uri = Playlists.ExternalContentUri;
|
|
await Task.Run(() =>
|
|
{
|
|
if (Looper.MyLooper() == null)
|
|
Looper.Prepare();
|
|
|
|
CursorLoader loader = new CursorLoader(Application.Context, uri, null, null, null, null);
|
|
ICursor cursor = (ICursor)loader.LoadInBackground();
|
|
|
|
if (cursor != null && cursor.MoveToFirst())
|
|
{
|
|
int nameID = cursor.GetColumnIndex(Playlists.InterfaceConsts.Name);
|
|
int listID = cursor.GetColumnIndex(Playlists.InterfaceConsts.Id);
|
|
do
|
|
{
|
|
string name = cursor.GetString(nameID);
|
|
long id = cursor.GetLong(listID);
|
|
|
|
Uri musicUri = Playlists.Members.GetContentUri("external", id);
|
|
CursorLoader cursorLoader = new CursorLoader(Application.Context, musicUri, null, null, null, null);
|
|
ICursor musicCursor = (ICursor)cursorLoader.LoadInBackground();
|
|
|
|
playlists.Add(new PlaylistItem(name, id, musicCursor.Count) { HasWritePermission = true });
|
|
}
|
|
while (cursor.MoveToNext());
|
|
cursor.Close();
|
|
}
|
|
});
|
|
|
|
if (playlists.Count == 0)
|
|
return (null, "EMPTY");
|
|
else
|
|
return (playlists, null);
|
|
}
|
|
|
|
/// <summary>
|
|
/// This method will proceed the local playlists and split synced one from the local one.
|
|
/// The outputed youtube playlists will already have the right sync state set (loading, synced...)
|
|
/// </summary>
|
|
/// <param name="localPlaylists">The array of local playlists (can be obtaines with the GetLocalPlaylists method).</param>
|
|
/// <returns>The first list contains all the local only playlists and the second one contains all the synced one. Every playlists has the right sync state set.</returns>
|
|
public static async Task<(List<PlaylistItem>, List<PlaylistItem>)> ProcessSyncedPlaylists(List<PlaylistItem> localPlaylists)
|
|
{
|
|
List<PlaylistItem> syncedPlaylists = new List<PlaylistItem>();
|
|
await Task.Run(() =>
|
|
{
|
|
SQLiteConnection db = new SQLiteConnection(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), "SyncedPlaylists.sqlite"));
|
|
db.CreateTable<PlaylistItem>();
|
|
|
|
syncedPlaylists = db.Table<PlaylistItem>().ToList();
|
|
});
|
|
|
|
foreach (PlaylistItem synced in syncedPlaylists)
|
|
{
|
|
PlaylistItem local = localPlaylists?.Find(x => x.LocalID == synced.LocalID);
|
|
Console.WriteLine("&SyncedName: " + synced.Name + " YoutubeID: " + synced.YoutubeID);
|
|
if (local != null)
|
|
{
|
|
synced.Count = local.Count;
|
|
localPlaylists.Remove(local); //This playlist is a synced one, we don't want to display it in the "local" collumn but in the youtube one.
|
|
|
|
//Set sync state of the playlist (SyncState can't be false since we take the playlist in the synced database)
|
|
if (synced.YoutubeID == null)
|
|
synced.SyncState = SyncState.Loading;
|
|
else
|
|
synced.SyncState = SyncState.True;
|
|
}
|
|
else
|
|
{
|
|
//If local is null, we had an error loading local playlists or another thing (can be read permission denied for example)
|
|
synced.SyncState = SyncState.Error;
|
|
}
|
|
}
|
|
|
|
return (localPlaylists, syncedPlaylists);
|
|
}
|
|
|
|
/// <summary>
|
|
/// This method return all youtube playlists owned by the user and process synced playlist if you give an array of know youtube synced playlists
|
|
/// The YoutubePlaylists array should contains the synced playlist availables. Warning, this will return your initial array(proceded if there is synced playlist) + owned
|
|
/// The second outputed var(the string) is the error message that should be displayed to the user(the list will be null if there is an error)
|
|
/// </summary>
|
|
/// <param name="SyncedPlaylists"></param>
|
|
/// <param name="UiCallback">A callback that will tell the ui to update a synced playlist with the new data got from the item.</param>
|
|
/// <returns></returns>
|
|
public static async Task<(List<PlaylistItem>, string)> GetOwnedYoutubePlaylists(List<PlaylistItem> SyncedPlaylists, Action<PlaylistItem, int> UiCallback)
|
|
{
|
|
if (!await MainActivity.instance.WaitForYoutube() || YoutubeManager.IsUsingAPI)
|
|
return (null, "Error"); //Should have a better error handling
|
|
|
|
List<PlaylistItem> YoutubePlaylists = new List<PlaylistItem>();
|
|
|
|
try
|
|
{
|
|
YouTubeService youtube = YoutubeManager.YoutubeService;
|
|
|
|
PlaylistsResource.ListRequest request = youtube.Playlists.List("snippet,contentDetails");
|
|
request.Mine = true;
|
|
request.MaxResults = 25;
|
|
PlaylistListResponse response = await request.ExecuteAsync();
|
|
|
|
for (int i = 0; i < response.Items.Count; i++)
|
|
{
|
|
Playlist playlist = response.Items[i];
|
|
PlaylistItem item = new PlaylistItem(playlist.Snippet.Title, playlist.Id, playlist, (int)playlist.ContentDetails.ItemCount)
|
|
{
|
|
Owner = playlist.Snippet.ChannelTitle,
|
|
ImageURL = playlist.Snippet.Thumbnails.High.Url,
|
|
HasWritePermission = true
|
|
};
|
|
|
|
ProcessPlaylistSyncState(item, SyncedPlaylists, YoutubePlaylists, UiCallback);
|
|
}
|
|
|
|
return (YoutubePlaylists, null);
|
|
}
|
|
catch (System.Net.Http.HttpRequestException)
|
|
{
|
|
return (null, "Error"); //Should handle precise error here
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// This method return all youtube playlists saved by the user and process synced playlist if you give an array of know youtube synced playlists
|
|
/// The YoutubePlaylists array should contains the synced playlist availables.Warning, this will return your initial array(proceded if there is synced playlist) + saved playlists
|
|
/// The second outputed var(the string) is the error message that should be displayed to the user(the list will be null if there is an error)
|
|
/// </summary>
|
|
/// <param name="SyncedPlaylists"></param>
|
|
/// <param name="UiCallback">A callback that will tell the ui to update a synced playlist with the new data got from the item.</param>
|
|
/// <returns></returns>
|
|
public static async Task<List<PlaylistItem>> GetSavedYoutubePlaylists(List<PlaylistItem> SyncedPlaylists, Action<PlaylistItem, int> UiCallback)
|
|
{
|
|
List<PlaylistItem> SavedPlaylists = new List<PlaylistItem>();
|
|
await Task.Run(() =>
|
|
{
|
|
SQLiteConnection db = new SQLiteConnection(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), "SavedPlaylists.sqlite"));
|
|
db.CreateTable<SavedPlaylist>();
|
|
|
|
SavedPlaylists = db.Table<SavedPlaylist>().ToList().ConvertAll(x => { PlaylistItem y = x; y.YoutubeID = x.YoutubeID; y.LocalID = -1; return y; });
|
|
});
|
|
|
|
List<PlaylistItem> YoutubePlaylists = new List<PlaylistItem>();
|
|
|
|
foreach (PlaylistItem item in SavedPlaylists)
|
|
ProcessPlaylistSyncState(item, SyncedPlaylists, YoutubePlaylists, UiCallback);
|
|
|
|
return YoutubePlaylists;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Complete the synced playlist array and the youtube playlists array with a new youtube playlist. The method will add the item to the right array or if the playlist is contained in one of the array, it will complete this item.
|
|
/// </summary>
|
|
/// <param name="item">The playlist you want to process</param>
|
|
/// <param name="SyncedPlaylists">An array of known synced playlists (Will be updated)</param>
|
|
/// <param name="YoutubePlaylists">An array of youtube playlists. (Will be updated)</param>
|
|
/// <param name="UiCallback">A callback that will tell the ui to update a synced playlist with the new data got from the item.</param>
|
|
public /*async*/ static void ProcessPlaylistSyncState(PlaylistItem item, List<PlaylistItem> SyncedPlaylists, List<PlaylistItem> YoutubePlaylists, Action<PlaylistItem, int> UiCallback)
|
|
{
|
|
PlaylistItem syncedItem = SyncedPlaylists?.Find(x => x.YoutubeID == item.YoutubeID);
|
|
if (syncedItem != null)
|
|
{
|
|
syncedItem.Snippet = item.Snippet;
|
|
syncedItem.Count = item.Count;
|
|
syncedItem.SyncState = SyncState.True;
|
|
|
|
if(syncedItem.ImageURL == null)
|
|
{
|
|
syncedItem.ImageURL = item.ImageURL;
|
|
/*await*/ Task.Run(() =>
|
|
{
|
|
SQLiteConnection db = new SQLiteConnection(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), "SyncedPlaylists.sqlite"));
|
|
db.CreateTable<PlaylistItem>();
|
|
db.InsertOrReplace(syncedItem);
|
|
});
|
|
}
|
|
}
|
|
else if (SyncedPlaylists?.Find(x => x.Name == item.Name) != null)
|
|
{
|
|
Console.WriteLine("&Match found");
|
|
/*We couldn't find a match of a synced playlist with the exact youtube id but we found a synced playlist with the exact same name as this one (item).
|
|
* We bind them and complete the database for future calls. */
|
|
syncedItem = SyncedPlaylists.Find(x => x.Name == item.Name);
|
|
int syncIndex = SyncedPlaylists.IndexOf(syncedItem);
|
|
item.LocalID = syncedItem.LocalID;
|
|
item.SyncState = SyncState.True;
|
|
|
|
Console.WriteLine("&YoutubeID: " + item.YoutubeID);
|
|
|
|
//If the URL is the youtube "no thumb", we don't want to save this in the database. We'll wait for the next real thumb.
|
|
if (item.Count == 0)
|
|
item.ImageURL = null;
|
|
|
|
SyncedPlaylists[syncIndex] = item;
|
|
|
|
if (UiCallback != null)
|
|
MainActivity.instance.RunOnUiThread(() => { UiCallback.Invoke(item, syncIndex); });
|
|
|
|
/*await*/ Task.Run(() =>
|
|
{
|
|
SQLiteConnection db = new SQLiteConnection(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), "SyncedPlaylists.sqlite"));
|
|
db.CreateTable<PlaylistItem>();
|
|
db.InsertOrReplace(item);
|
|
});
|
|
}
|
|
else
|
|
YoutubePlaylists.Add(item);
|
|
}
|
|
#endregion
|
|
|
|
#region LocalPlaylists
|
|
/// <summary>
|
|
/// Return a list containing all the songs in the local playlist.
|
|
/// </summary>
|
|
/// <param name="LocalID"></param>
|
|
/// <returns></returns>
|
|
public async static Task<List<Song>> GetTracksFromLocalPlaylist(long LocalID)
|
|
{
|
|
if (Looper.MyLooper() == null)
|
|
Looper.Prepare();
|
|
|
|
List<Song> songs = new List<Song>();
|
|
Uri musicUri = Playlists.Members.GetContentUri("external", LocalID);
|
|
await Task.Run(() =>
|
|
{
|
|
if (Looper.MyLooper() == null)
|
|
Looper.Prepare();
|
|
|
|
CursorLoader cursorLoader = new CursorLoader(Application.Context, musicUri, null, null, null, null);
|
|
ICursor musicCursor = (ICursor)cursorLoader.LoadInBackground();
|
|
|
|
if (musicCursor != null && musicCursor.MoveToFirst())
|
|
{
|
|
int titleID = musicCursor.GetColumnIndex(Media.InterfaceConsts.Title);
|
|
int artistID = musicCursor.GetColumnIndex(Media.InterfaceConsts.Artist);
|
|
int albumID = musicCursor.GetColumnIndex(Media.InterfaceConsts.Album);
|
|
int thisID = musicCursor.GetColumnIndex(Media.InterfaceConsts.Id);
|
|
int pathID = musicCursor.GetColumnIndex(Media.InterfaceConsts.Data);
|
|
do
|
|
{
|
|
string Artist = musicCursor.GetString(artistID);
|
|
string Title = musicCursor.GetString(titleID);
|
|
string Album = musicCursor.GetString(albumID);
|
|
long AlbumArt = musicCursor.GetLong(musicCursor.GetColumnIndex(Albums.InterfaceConsts.AlbumId));
|
|
long id = musicCursor.GetLong(thisID);
|
|
string path = musicCursor.GetString(pathID);
|
|
|
|
if (Title == null)
|
|
Title = "Unknown Title";
|
|
if (Artist == null)
|
|
Artist = "Unknow Artist";
|
|
if (Album == null)
|
|
Album = "Unknow Album";
|
|
|
|
songs.Add(new Song(Title, Artist, Album, null, AlbumArt, id, path));
|
|
}
|
|
while (musicCursor.MoveToNext());
|
|
musicCursor.Close();
|
|
}
|
|
});
|
|
|
|
return songs;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Will create a local playlist and add an array of songs in this playlist
|
|
/// </summary>
|
|
/// <param name="name">The name of the playlist</param>
|
|
/// <param name="items">The array of songs you want to add. Can be local one or youtube one, it will download them and add them after.</param>
|
|
/// <param name="syncedPlaylist">True if you want the playlist to be created and synced on youtube too</param>
|
|
public async static void CreateLocalPlaylist(string name, Song[] items, bool syncedPlaylist = false)
|
|
{
|
|
if (!await MainActivity.instance.GetWritePermission())
|
|
return;
|
|
|
|
//Create the playlist in the local storage db.
|
|
ContentValues value = new ContentValues();
|
|
value.Put(Playlists.InterfaceConsts.Name, name);
|
|
MainActivity.instance.ContentResolver.Insert(Playlists.ExternalContentUri, value);
|
|
|
|
long playlistID = await GetPlaylistID(name);
|
|
|
|
if (items != null && items.Length > 0)
|
|
{
|
|
LocalManager.AddToPlaylist(items, name, playlistID); //Will only add files already downloaded
|
|
YoutubeManager.Download(items, name); //Will download missing files and add them (if there was youtube songs in the items array.
|
|
}
|
|
|
|
if (syncedPlaylist)
|
|
{
|
|
await Task.Run(() =>
|
|
{
|
|
SQLiteConnection db = new SQLiteConnection(Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal), "SyncedPlaylists.sqlite"));
|
|
db.CreateTable<PlaylistItem>();
|
|
db.InsertOrReplace(new PlaylistItem(name, playlistID, null));
|
|
});
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Return the id of a local playlist by using it's name. Return -1 if the name is not found.
|
|
/// </summary>
|
|
/// <param name="playlistName"></param>
|
|
/// <returns></returns>
|
|
public async static Task<long> GetPlaylistID(string playlistName)
|
|
{
|
|
Uri uri = Playlists.ExternalContentUri;
|
|
return await Task.Run(() =>
|
|
{
|
|
if (Looper.MyLooper() == null)
|
|
Looper.Prepare();
|
|
|
|
CursorLoader loader = new CursorLoader(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();
|
|
}
|
|
return -1;
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// Remove a track from a local playlist
|
|
/// </summary>
|
|
/// <param name="song"></param>
|
|
/// <param name="LocalPlaylistID"></param>
|
|
public /*async*/ static void RemoveFromLocalPlaylist(Song song, long LocalPlaylistID)
|
|
{
|
|
Console.WriteLine("&Remove Track From Local Playlist Called");
|
|
Console.WriteLine("&Song localID: " + song.LocalID + " LocalPlaylistID: " + LocalPlaylistID);
|
|
|
|
//await Task.Run(() =>
|
|
//{
|
|
// if (Looper.MyLooper() == null)
|
|
// Looper.Prepare();
|
|
|
|
ContentResolver resolver = MainActivity.instance.ContentResolver;
|
|
Uri uri = Playlists.Members.GetContentUri("external", LocalPlaylistID);
|
|
resolver.Delete(uri, Playlists.Members.Id + "=?", new string[] { song.LocalID.ToString() });
|
|
//});
|
|
}
|
|
#endregion
|
|
|
|
#region YoutubePlaylists
|
|
/// <summary>
|
|
/// This will list all the tracks contained in the playlist.
|
|
/// </summary>
|
|
/// <param name="YoutubeID">The id of the youtube playlist</param>
|
|
/// <param name="action">An action to exectute before the end of the listing, allowing the app to start playback before the end of the execution of this method.</param
|
|
/// <param name="positionListening">The position of the item that will be used for the action.</param>
|
|
/// <returns></returns>
|
|
public async static Task<List<Song>> GetTracksFromYoutubePlaylist(string YoutubeID, Action<Song> action = null, int positionListening = -1)
|
|
{
|
|
List<Song> tracks = new List<Song>();
|
|
|
|
if (!await MainActivity.instance.WaitForYoutube())
|
|
{
|
|
Toast.MakeText(Application.Context, Resource.String.youtube_loading_error, ToastLength.Long).Show();
|
|
return null;
|
|
}
|
|
|
|
try
|
|
{
|
|
string nextPageToken = "";
|
|
while (nextPageToken != null)
|
|
{
|
|
var ytPlaylistRequest = YoutubeManager.YoutubeService.PlaylistItems.List("snippet, contentDetails");
|
|
ytPlaylistRequest.PlaylistId = YoutubeID;
|
|
ytPlaylistRequest.MaxResults = 50;
|
|
ytPlaylistRequest.PageToken = nextPageToken;
|
|
|
|
var ytPlaylist = await ytPlaylistRequest.ExecuteAsync();
|
|
|
|
foreach (var item in ytPlaylist.Items)
|
|
{
|
|
if (item.Snippet.Title != "[Deleted video]" && item.Snippet.Title != "Private video" && item.Snippet.Title != "Deleted video")
|
|
{
|
|
Song song = new Song(item.Snippet.Title, item.Snippet.ChannelTitle, item.Snippet.Thumbnails.Default__.Url, item.ContentDetails.VideoId, -1, -1, item.ContentDetails.VideoId, true, false);
|
|
tracks.Add(song);
|
|
|
|
if (tracks.Count == positionListening + 1)
|
|
action?.Invoke(tracks.Last());
|
|
}
|
|
}
|
|
|
|
nextPageToken = ytPlaylist.NextPageToken;
|
|
}
|
|
|
|
return tracks;
|
|
}
|
|
catch (System.Net.Http.HttpRequestException)
|
|
{
|
|
MainActivity.instance.Timout();
|
|
return tracks;
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Will create a youtube playlist and add an array of songs in this playlist
|
|
/// </summary>
|
|
/// <param name="name">The name of the playlist</param>
|
|
/// <param name="items">The array of songs you want to add. Can be local one or youtube one, it will download them and add them after.</param>
|
|
public async static void CreateYoutubePlaylist(string playlistName, Song[] items)
|
|
{
|
|
try
|
|
{
|
|
Playlist playlist = new Playlist();
|
|
PlaylistSnippet snippet = new PlaylistSnippet();
|
|
PlaylistStatus status = new PlaylistStatus();
|
|
snippet.Title = playlistName;
|
|
playlist.Snippet = snippet;
|
|
playlist.Status = status;
|
|
|
|
var createRequest = YoutubeManager.YoutubeService.Playlists.Insert(playlist, "snippet, status");
|
|
Playlist response = await createRequest.ExecuteAsync();
|
|
|
|
YoutubeManager.AddToPlaylist(items, response.Id);
|
|
}
|
|
catch (System.Net.Http.HttpRequestException)
|
|
{
|
|
MainActivity.instance.Timout();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Will set the TrackID of the song using the playlist given with the YoutubeID var. (The trackID represent a track in a youtube playlist).
|
|
/// </summary>
|
|
/// <param name="song"></param>
|
|
/// <param name="PlaylistYoutubeID"></param>
|
|
/// <returns></returns>
|
|
public static async Task<Song> CompleteItem(Song song, string PlaylistYoutubeID)
|
|
{
|
|
song.TrackID = null;
|
|
if (await MainActivity.instance.WaitForYoutube())
|
|
{
|
|
try
|
|
{
|
|
var request = YoutubeManager.YoutubeService.PlaylistItems.List("snippet");
|
|
request.PlaylistId = PlaylistYoutubeID;
|
|
request.VideoId = song.YoutubeID;
|
|
request.MaxResults = 1;
|
|
|
|
var result = await request.ExecuteAsync();
|
|
if (result.Items.Count > 0)
|
|
song.TrackID = result.Items[0].Id;
|
|
else
|
|
song.Title = null;
|
|
}
|
|
catch (System.Net.Http.HttpRequestException)
|
|
{
|
|
MainActivity.instance.Timout();
|
|
}
|
|
}
|
|
else
|
|
MainActivity.instance.Timout();
|
|
|
|
return song;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Remove a song from a youtube playlist. Identify the song and the playlist directly with the TrackID.
|
|
/// </summary>
|
|
/// <param name="TrackID"></param>
|
|
public static async void RemoveFromYoutubePlaylist(string TrackID)
|
|
{
|
|
if (TrackID == null)
|
|
return;
|
|
|
|
try
|
|
{
|
|
await YoutubeManager.YoutubeService.PlaylistItems.Delete(TrackID).ExecuteAsync();
|
|
}
|
|
catch (System.Net.Http.HttpRequestException)
|
|
{
|
|
MainActivity.instance.Timout();
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region YoutubeFork
|
|
/// <summary>
|
|
/// Return true if the playlist is already forked.
|
|
/// </summary>
|
|
/// <param name="item"></param>
|
|
/// <returns></returns>
|
|
public async static Task<bool> IsForked(PlaylistItem item)
|
|
{
|
|
List<PlaylistItem> SavedPlaylistt = await GetSavedYoutubePlaylists(null, null);
|
|
if (SavedPlaylistt.Count(x => x.YoutubeID == item.YoutubeID) > 0)
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Save a playlist in the user library.
|
|
/// </summary>
|
|
/// <param name="item"></param>
|
|
/// <returns></returns>
|
|
public static async void ForkPlaylist(PlaylistItem item)
|
|
{
|
|
SavedPlaylist pl = new SavedPlaylist(item);
|
|
await Task.Run(() =>
|
|
{
|
|
SQLiteConnection db = new SQLiteConnection(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), "SavedPlaylists.sqlite"));
|
|
db.CreateTable<SavedPlaylist>();
|
|
db.InsertOrReplace(pl);
|
|
});
|
|
|
|
Snackbar snackBar = Snackbar.Make(MainActivity.instance.FindViewById(Resource.Id.snackBar), MainActivity.instance.GetString(Resource.String.playlist_saved), Snackbar.LengthLong);
|
|
snackBar.View.FindViewById<TextView>(Resource.Id.snackbar_text).SetTextColor(Color.White);
|
|
snackBar.Show();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Remove a playlist from the user library.
|
|
/// </summary>
|
|
/// <param name="item"></param>
|
|
public static async void Unfork(PlaylistItem item)
|
|
{
|
|
SavedPlaylist pl = new SavedPlaylist(item);
|
|
await Task.Run(() =>
|
|
{
|
|
SQLiteConnection db = new SQLiteConnection(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), "SavedPlaylists.sqlite"));
|
|
db.CreateTable<SavedPlaylist>();
|
|
db.Delete(pl);
|
|
});
|
|
|
|
Snackbar snackBar = Snackbar.Make(MainActivity.instance.FindViewById(Resource.Id.snackBar), MainActivity.instance.GetString(Resource.String.playlist_unsaved), Snackbar.LengthLong);
|
|
snackBar.View.FindViewById<TextView>(Resource.Id.snackbar_text).SetTextColor(Color.White);
|
|
snackBar.Show();
|
|
}
|
|
#endregion
|
|
}
|
|
} |