mirror of
https://github.com/zoriya/Opus.git
synced 2025-12-06 06:26:15 +00:00
402 lines
18 KiB
C#
402 lines
18 KiB
C#
using Android.App;
|
|
using Android.Database;
|
|
using Android.Graphics;
|
|
using Android.Net;
|
|
using Android.OS;
|
|
using Android.Provider;
|
|
using Android.Support.Design.Widget;
|
|
using Android.Support.V4.Content;
|
|
using Android.Widget;
|
|
using Opus.Api.Services;
|
|
using Opus.DataStructure;
|
|
using Opus.Fragments;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using TagLib;
|
|
using ContentResolver = Android.Content.ContentResolver;
|
|
using ContentValues = Android.Content.ContentValues;
|
|
using Intent = Android.Content.Intent;
|
|
using Random = System.Random;
|
|
|
|
namespace Opus.Api
|
|
{
|
|
public class LocalManager
|
|
{
|
|
#region Playback
|
|
/// <summary>
|
|
/// Handle playback of a local file using it's path.
|
|
/// </summary>
|
|
/// <param name="path"></param>
|
|
public static void Play(string path)
|
|
{
|
|
Intent intent = new Intent(Application.Context, typeof(MusicPlayer));
|
|
intent.PutExtra("file", path);
|
|
Application.Context.StartService(intent);
|
|
|
|
MainActivity.instance.ShowPlayer();
|
|
MusicPlayer.UpdateQueueDataBase();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add a local item to the queue.
|
|
/// </summary>
|
|
/// <param name="path"></param>
|
|
public static void PlayNext(string path)
|
|
{
|
|
Intent intent = new Intent(Application.Context, typeof(MusicPlayer));
|
|
intent.PutExtra("file", path);
|
|
intent.SetAction("PlayNext");
|
|
Application.Context.StartService(intent);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add a local item at the end of the queue.
|
|
/// </summary>
|
|
/// <param name="path"></param>
|
|
public static void PlayLast(string path)
|
|
{
|
|
Intent intent = new Intent(Application.Context, typeof(MusicPlayer));
|
|
intent.PutExtra("file", path);
|
|
intent.SetAction("PlayLast");
|
|
Application.Context.StartService(intent);
|
|
}
|
|
|
|
/// <summary>
|
|
/// RandomPlay all file in the folder you selected or all file on the device if the folderPath = null.
|
|
/// </summary>
|
|
/// <param name="folderPath">The path of a folder if you want to shuffle files from this folder only.</param>
|
|
public async static void ShuffleAll(string folderPath = null)
|
|
{
|
|
if (!await MainActivity.instance.GetReadPermission())
|
|
return;
|
|
|
|
List<Song> songs = new List<Song>();
|
|
|
|
await Task.Run(() =>
|
|
{
|
|
if (Looper.MyLooper() == null)
|
|
Looper.Prepare();
|
|
|
|
CursorLoader cursorLoader = new CursorLoader(Application.Context, MediaStore.Audio.Media.ExternalContentUri, null, MediaStore.Audio.Media.InterfaceConsts.Data + " LIKE \"%" + folderPath + "%\"", null, null);
|
|
ICursor musicCursor = (ICursor)cursorLoader.LoadInBackground();
|
|
if (musicCursor != null && musicCursor.MoveToFirst())
|
|
{
|
|
int titleID = musicCursor.GetColumnIndex(MediaStore.Audio.Media.InterfaceConsts.Title);
|
|
int artistID = musicCursor.GetColumnIndex(MediaStore.Audio.Media.InterfaceConsts.Artist);
|
|
int albumID = musicCursor.GetColumnIndex(MediaStore.Audio.Media.InterfaceConsts.Album);
|
|
int thisID = musicCursor.GetColumnIndex(MediaStore.Audio.Media.InterfaceConsts.Id);
|
|
int pathID = musicCursor.GetColumnIndex(MediaStore.Audio.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(MediaStore.Audio.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();
|
|
}
|
|
});
|
|
|
|
if (songs.Count == 0)
|
|
{
|
|
Snackbar snackBar = Snackbar.Make(MainActivity.instance.FindViewById<CoordinatorLayout>(Resource.Id.snackBar), Resource.String.no_song_mix, Snackbar.LengthLong);
|
|
snackBar.View.FindViewById<TextView>(Resource.Id.snackbar_text).SetTextColor(Color.White);
|
|
snackBar.Show();
|
|
return;
|
|
}
|
|
|
|
Random r = new Random();
|
|
songs = songs.OrderBy(x => r.Next()).ToList();
|
|
SongManager.Play(songs[0]);
|
|
songs.RemoveAt(0);
|
|
|
|
await Task.Delay(1000);
|
|
while (MusicPlayer.instance == null)
|
|
await Task.Delay(10);
|
|
|
|
MusicPlayer.instance.AddToQueue(songs);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Play all file from a folder in order.
|
|
/// </summary>
|
|
/// <param name="folderPath">The path of a folder you want to play.</param>
|
|
/// <param name="startingPosition">The position you want to start playing.</param>
|
|
/// <param name="customSelection">If you want to have custom selection for your query (can be because the user searched...).</param>
|
|
public async static void PlayInOrder(string folderPath, int startingPosition = 0, string customSelection = null)
|
|
{
|
|
List<Song> tracks = new List<Song>();
|
|
|
|
await Task.Run(() =>
|
|
{
|
|
if (Looper.MyLooper() == null)
|
|
Looper.Prepare();
|
|
|
|
CursorLoader cursorLoader;
|
|
if(customSelection == null)
|
|
cursorLoader = new CursorLoader(Application.Context, MediaStore.Audio.Media.ExternalContentUri, null, MediaStore.Audio.Media.InterfaceConsts.Data + " LIKE \"%" + folderPath + "%\"", null, null);
|
|
else
|
|
cursorLoader = new CursorLoader(Application.Context, MediaStore.Audio.Media.ExternalContentUri, null, customSelection + " AND " + MediaStore.Audio.Media.InterfaceConsts.Data + " LIKE \"%" + folderPath + "%\"", null, null);
|
|
|
|
ICursor musicCursor = (ICursor)cursorLoader.LoadInBackground();
|
|
if (musicCursor != null && musicCursor.MoveToFirst())
|
|
{
|
|
int titleID = musicCursor.GetColumnIndex(MediaStore.Audio.Media.InterfaceConsts.Title);
|
|
int artistID = musicCursor.GetColumnIndex(MediaStore.Audio.Media.InterfaceConsts.Artist);
|
|
int albumID = musicCursor.GetColumnIndex(MediaStore.Audio.Media.InterfaceConsts.Album);
|
|
int thisID = musicCursor.GetColumnIndex(MediaStore.Audio.Media.InterfaceConsts.Id);
|
|
int pathID = musicCursor.GetColumnIndex(MediaStore.Audio.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(MediaStore.Audio.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";
|
|
|
|
tracks.Add(new Song(Title, Artist, Album, null, AlbumArt, id, path));
|
|
}
|
|
while (musicCursor.MoveToNext());
|
|
musicCursor.Close();
|
|
}
|
|
});
|
|
|
|
if (tracks.Count == 0)
|
|
{
|
|
Snackbar snackBar = Snackbar.Make(MainActivity.instance.FindViewById<CoordinatorLayout>(Resource.Id.snackBar), Resource.String.no_song_mix, Snackbar.LengthLong);
|
|
snackBar.View.FindViewById<TextView>(Resource.Id.snackbar_text).SetTextColor(Color.White);
|
|
snackBar.Show();
|
|
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));
|
|
}
|
|
#endregion
|
|
|
|
#region Metadata
|
|
/// <summary>
|
|
/// Return a song using only it's path (can be normal path or content:// path). This method read metadata in the device database.
|
|
/// </summary>
|
|
/// <param name="filePath"></param>
|
|
/// <returns></returns>
|
|
public async static Task<Song> GetSong(string filePath)
|
|
{
|
|
string Title = "Unknow";
|
|
string Artist = "Unknow";
|
|
long AlbumArt = 0;
|
|
long id = 0;
|
|
string path;
|
|
Uri musicUri = MediaStore.Audio.Media.ExternalContentUri;
|
|
|
|
if (filePath.StartsWith("content://"))
|
|
musicUri = Uri.Parse(filePath);
|
|
|
|
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(MediaStore.Audio.Media.InterfaceConsts.Title);
|
|
int artistID = musicCursor.GetColumnIndex(MediaStore.Audio.Media.InterfaceConsts.Artist);
|
|
int thisID = musicCursor.GetColumnIndex(MediaStore.Audio.Media.InterfaceConsts.Id);
|
|
int pathID = musicCursor.GetColumnIndex(MediaStore.Audio.Media.InterfaceConsts.Data);
|
|
do
|
|
{
|
|
path = musicCursor.GetString(pathID);
|
|
|
|
if (path == filePath || filePath.StartsWith("content://"))
|
|
{
|
|
Artist = musicCursor.GetString(artistID);
|
|
Title = musicCursor.GetString(titleID);
|
|
AlbumArt = musicCursor.GetLong(musicCursor.GetColumnIndex(MediaStore.Audio.Albums.InterfaceConsts.AlbumId));
|
|
id = musicCursor.GetLong(thisID);
|
|
|
|
if (Title == null)
|
|
Title = "Unknown Title";
|
|
if (Artist == null)
|
|
Artist = "Unknow Artist";
|
|
|
|
if (filePath.StartsWith("content://"))
|
|
filePath = path;
|
|
break;
|
|
}
|
|
}
|
|
while (musicCursor.MoveToNext());
|
|
musicCursor.Close();
|
|
}
|
|
});
|
|
return new Song(Title, Artist, null, null, AlbumArt, id, filePath);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Read the youtubeID of a local song in the file's metadata and add it to the song object.
|
|
/// </summary>
|
|
/// <param name="item"></param>
|
|
/// <returns></returns>
|
|
public static Song CompleteItem(Song item)
|
|
{
|
|
item.YoutubeID = GetYtID(item.Path);
|
|
return item;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Read the youtubeID of a local song in the file's metadata using the path of the file.
|
|
/// </summary>
|
|
/// <param name="path"></param>
|
|
/// <returns></returns>
|
|
public static string GetYtID(string path)
|
|
{
|
|
try
|
|
{
|
|
using (Stream stream = new FileStream(path, FileMode.Open, FileAccess.Read))
|
|
{
|
|
var meta = TagLib.File.Create(new StreamFileAbstraction(path, stream, stream));
|
|
return meta.Tag.Comment;
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
System.Console.WriteLine("&GetYtID Error: The format of the file at " + path + " is probably unsupported.");
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Open the EditMetadata page for a song.
|
|
/// </summary>
|
|
/// <param name="item"></param>
|
|
/// <param name="queuePosition">Set this to a position of the song in the queue, it will update the queue slot after the edit.</param>
|
|
public static void EditMetadata(Song item, int queuePosition = -1)
|
|
{
|
|
item = CompleteItem(item);
|
|
Intent intent = new Intent(Application.Context, typeof(EditMetaData));
|
|
intent.PutExtra("Song", item.ToString());
|
|
intent.PutExtra("Position", queuePosition);
|
|
MainActivity.instance.StartActivity(intent);
|
|
}
|
|
#endregion
|
|
|
|
#region PlaylistsImplementation
|
|
/// <summary>
|
|
/// Return true if the local song of id audioID is contained in the local playlist with the id of playlistID.
|
|
/// </summary>
|
|
/// <param name="audioID"></param>
|
|
/// <param name="playlistID"></param>
|
|
/// <returns></returns>
|
|
public async static Task<bool> SongIsContained(long audioID, long playlistID)
|
|
{
|
|
Uri uri = MediaStore.Audio.Playlists.Members.GetContentUri("external", playlistID);
|
|
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 idColumn = cursor.GetColumnIndex(MediaStore.Audio.Playlists.Members.AudioId);
|
|
do
|
|
{
|
|
long id = cursor.GetLong(idColumn);
|
|
if (id == audioID)
|
|
return true;
|
|
}
|
|
while (cursor.MoveToNext());
|
|
cursor.Close();
|
|
}
|
|
|
|
return false;
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add an array of local song in a playlist.
|
|
/// </summary>
|
|
/// <param name="item">The array of songs you want to add to the playlist. Will only add local file, if you input youtube file in this array, they will be ignored<</param>
|
|
/// <param name="playList">The name of the playlist</param>
|
|
/// <param name="LocalID">The id of the local playlist or -1 if you want to add this song to a playlist that will be created after.</param>
|
|
/// <param name="saveAsSynced">Used only if you want to create a playlist with this method. True if the newly created playlist should be synced on youtube.</param>
|
|
/// <param name="position">If you want to add the first song to a specific position (should only be used if you know the exact position that you want). Experimental Feature.</param>
|
|
public async static void AddToPlaylist(Song[] items, string playList, long LocalID, bool saveAsSynced = false, int position = -1)
|
|
{
|
|
if (LocalID == -1)
|
|
{
|
|
LocalID = await PlaylistManager.GetPlaylistID(playList);
|
|
if (LocalID == -1)
|
|
PlaylistManager.CreateLocalPlaylist(playList, items, saveAsSynced, position);
|
|
else
|
|
AddToPlaylist(items, playList, LocalID, saveAsSynced, position);
|
|
}
|
|
else
|
|
{
|
|
if(await MainActivity.instance.GetWritePermission())
|
|
{
|
|
int playlistCount = await PlaylistManager.GetPlaylistCount(LocalID);
|
|
|
|
ContentResolver resolver = MainActivity.instance.ContentResolver;
|
|
List<ContentValues> values = new List<ContentValues>();
|
|
|
|
for (int i = 0; i < items.Length; i++)
|
|
{
|
|
Song item = items[i];
|
|
if (item != null && item.LocalID != 0 && item.LocalID != -1)
|
|
{
|
|
ContentValues value = new ContentValues();
|
|
value.Put(MediaStore.Audio.Playlists.Members.AudioId, item.LocalID);
|
|
if(position != -1)
|
|
{
|
|
value.Put(MediaStore.Audio.Playlists.Members.PlayOrder, position);
|
|
position = -1;
|
|
}
|
|
else
|
|
value.Put(MediaStore.Audio.Playlists.Members.PlayOrder, playlistCount + i + 1);
|
|
values.Add(value);
|
|
}
|
|
}
|
|
|
|
resolver.BulkInsert(MediaStore.Audio.Playlists.Members.GetContentUri("external", LocalID), values.ToArray());
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
}
|
|
} |