Song elasping functionality implemented, can seek to a certain point of a song via the scroll bar, Song metadata are displayed, and continuing working on the Player view. #8, #24, #25, #27, #33

This commit is contained in:
amazing-username
2019-06-01 12:53:39 -04:00
parent c1fe9d45a4
commit 7959458c5e
9 changed files with 461 additions and 75 deletions
+6 -3
View File
@@ -30,7 +30,7 @@ namespace Mear.Playback
#region Methods #region Methods
public static async Task StreamSongDemoAsync(Song song) public static async Task<Song> StreamSongDemoAsync(Song song)
{ {
string tmpFile = Path.GetTempPath() + "track.mp3"; string tmpFile = Path.GetTempPath() + "track.mp3";
@@ -55,14 +55,17 @@ namespace Mear.Playback
var response = client.DownloadData(request); var response = client.DownloadData(request);
await CrossMediaManager.Current.Play(tmpFile); await CrossMediaManager.Current.Play(tmpFile);
var title = CrossMediaManager.Current.MediaQueue.Current.Title; song.SongPath = tmpFile;
var ttl = CrossMediaManager.Current.MediaQueue.Title;
return song;
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
var msg = ex.Message; var msg = ex.Message;
} }
return null;
} }
#endregion #endregion
} }
@@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Text; using System.Text;
using Newtonsoft.Json; using Newtonsoft.Json;
@@ -8,6 +9,7 @@ using RestSharp;
using Mear.Constants.API; using Mear.Constants.API;
using Mear.Models; using Mear.Models;
using Mear.Repositories.Database; using Mear.Repositories.Database;
using Mear.Utilities;
namespace Mear.Repositories.Remote namespace Mear.Repositories.Remote
{ {
@@ -51,6 +53,33 @@ namespace Mear.Repositories.Remote
return null; return null;
} }
public void DownloadSong(Song song)
{
try
{
var dirMgr = new DirectoryManager(song);
var path = dirMgr.CreateSongPath(song);
using (var writer = File.OpenWrite(path))
{
var client = new RestClient(API.ApiUrl);
var apiEndpoint = $@"api/{API.APIVersion}/song/data/{song.Id}"; ;
var request = new RestRequest(apiEndpoint, Method.GET);
DBTokenRepository tkRepo = new DBTokenRepository();
var token = tkRepo.RetrieveToken();
request.AddHeader("Authorization", $"Bearer {token.AccessToken}");
request.ResponseWriter = (responseStream) =>
responseStream.CopyTo(writer);
var response = client.DownloadData(request);
}
}
catch (Exception ex)
{
var msg = ex.Message;
}
}
#endregion #endregion
} }
} }
+119
View File
@@ -0,0 +1,119 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using Mear.Models;
namespace Mear.Utilities
{
public class DirectoryManager
{
#region Fields
private Song _song;
private string _path;
#endregion
#region Properties
#endregion
#region Constructors
public DirectoryManager(Song song)
{
_song = song;
}
#endregion
#region Methods
public string CreateSongPath(Song song)
{
try
{
var artist = song.Artist;
var album = song.Album;
var title = song.Title;
if (!ArtistDirectoryExist(song))
{
Directory.CreateDirectory(ArtistPath(song));
}
if (!AlbumDirectoryExist(song))
{
Directory.CreateDirectory(AlbumPath(song));
}
if (SongPathExist(song))
{
// TODO: Song exists
return SongPath(song);
}
else
{
return SongPath(song);
}
}
catch (Exception ex)
{
var msg = ex.Message;
}
return string.Empty;
}
private string RootPath(Song song)
{
var path = $@"{Environment.GetFolderPath(Environment.SpecialFolder.Personal)}/";
return path;
}
private string ArtistPath(Song song)
{
var path = RootPath(song);
path += $@"{song.Artist}/";
return path;
}
private string AlbumPath(Song song)
{
var path = ArtistPath(song);
path += $@"{song.Album}/";
return path;
}
private string SongPath(Song song)
{
var path = AlbumPath(song);
path += $@"{song.Filename}";
return path;
}
private bool ArtistDirectoryExist(Song song)
{
var path = RootPath(song);
path += song.Artist;
return Directory.Exists(path);
}
private bool AlbumDirectoryExist(Song song)
{
var path = AlbumPath(song);
return Directory.Exists(path);
}
private bool SongPathExist(Song song)
{
var path = SongPath(song);
return File.Exists(path);
}
private void Initialize()
{
_path = Environment.GetFolderPath(Environment.SpecialFolder.Personal) + @"/";
}
#endregion
}
}
@@ -1,11 +1,14 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Text; using System.Text;
using TagLib; using TagLib;
using Mear.Models; using Mear.Models;
using IOFile = System.IO.File;
namespace Mear.Utilities namespace Mear.Utilities
{ {
public class SongMetadataRetriever public class SongMetadataRetriever
@@ -35,6 +38,37 @@ namespace Mear.Utilities
return null; return null;
} }
public Stream ExtractCovertArtStream(Song song)
{
var strm = new FileStream(song.SongPath, FileMode.Open);
try
{
}
catch (Exception ex)
{
var msg = ex.Message;
}
return strm;
}
public string ExtractCoverArtData(Song song)
{
var imgData = string.Empty;
try
{
var d = IOFile.ReadAllBytes(song.SongPath);
/**
*/
}
catch (Exception ex)
{
var msg = ex.Message;
}
return imgData;
}
#endregion #endregion
} }
} }
+42
View File
@@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Mear.Utilities
{
public class TimeFormat
{
#region Fields
#endregion
#region Properties
#endregion
#region Constructors
#endregion
#region Methods
public string ConvertToSongTime(int seconds)
{
var curTime = string.Empty;
var dur = seconds;
var min = TimeSpan.FromSeconds((double) dur).Minutes;
var remainingSec = dur % 60;
if (remainingSec < 10)
{
curTime = $"{min}:0{remainingSec}";
}
else
{
curTime = $"{min}:{remainingSec}";
}
return curTime;
}
#endregion
}
}
@@ -0,0 +1,45 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
using Mear.Models;
namespace Mear.ViewModels
{
public class MearPlayerViewModel : BaseViewModel
{
#region Fields
private ObservableCollection<Song> _song;
#endregion
#region Properties
public ObservableCollection<Song> SongItem
{
get => _song;
}
#endregion
#region Constructors
public MearPlayerViewModel()
{
}
public MearPlayerViewModel(Song song)
{
_song = new ObservableCollection<Song>();
_song.Clear();
_song.Add(song);
}
#endregion
#region Methods
#endregion
}
}
+45 -20
View File
@@ -4,32 +4,50 @@
xmlns:d="http://xamarin.com/schemas/2014/forms/design" xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" mc:Ignorable="d"
x:Class="Mear.Views.MearPlayerView" > x:Class="Mear.Views.MearPlayerView"
xmlns:vm="clr-namespace:Mear.ViewModels"
Title="{Binding Title}">
<!--
<ContentPage.Content> <ContentPage.Content>
-->
<StackLayout>
<StackLayout Padding="10" VerticalOptions="EndAndExpand">
<StackLayout VerticalOptions="End">
<Image x:Name="SongCover"
Source="" />
</StackLayout>
<StackLayout BindableLayout.ItemsSource="{Binding SongItem}"
VerticalOptions="End">
<BindableLayout.ItemTemplate>
<DataTemplate>
<StackLayout HorizontalOptions="Center">
<Label x:Name="SongTitle"
Text="{Binding Title}"
d:Text="{Binding .}" />
<Label x:Name="ArtistName"
Text="{Binding Artist}" />
<Label x:Name="AlbumName"
Text="{Binding Album}"/>
</StackLayout>
<!--
<StackLayout> <StackLayout>
<Grid> <Grid>
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="*" /> <RowDefinition Height="*" />
<RowDefinition Height="*" /> <RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="*" /> <ColumnDefinition Width="*" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Image x:Name="SongCover"
Grid.Row="1"
Source="" />
<StackLayout Grid.Row="2"> </Grid>
<Label x:Name="SongTitle" </StackLayout>
Text="Title" /> -->
<Label x:Name="ArtistName" </DataTemplate>
Text="Artist" /> </BindableLayout.ItemTemplate>
<Label x:Name="AlbumName"
Text="Album"/>
</StackLayout> </StackLayout>
<StackLayout Orientation="Horizontal" <StackLayout Orientation="Horizontal"
Grid.Row="3"> Grid.Row="3">
@@ -40,19 +58,21 @@
Text="Shf" Text="Shf"
Clicked="Shuffle_Clicked" /> Clicked="Shuffle_Clicked" />
</StackLayout> </StackLayout>
<StackLayout Orientation="Horizontal" <StackLayout Orientation="Horizontal">
Grid.Row="4">
<Label x:Name="StartTime" <Label x:Name="StartTime"
Text="0:00" /> Text="0:00" />
<ProgressBar x:Name="SongProgress" <Slider x:Name="SongProgress"
HorizontalOptions="CenterAndExpand" HorizontalOptions="CenterAndExpand"
Progress="0.7" /> Minimum="0"
Maximum="100"
WidthRequest="275"
DragCompleted="SongProgress_DragCompleted"
Scale="1" />
<Label x:Name="EndTime" <Label x:Name="EndTime"
Text="0:00" /> Text="0:00" />
</StackLayout> </StackLayout>
<StackLayout Orientation="Horizontal" <StackLayout Orientation="Horizontal"
HorizontalOptions="CenterAndExpand" HorizontalOptions="CenterAndExpand"
Grid.Row="5"
Spacing="5"> Spacing="5">
<Button x:Name="Previous" <Button x:Name="Previous"
Text="Prev" Text="Prev"
@@ -64,7 +84,12 @@
Text="Nxt" Text="Nxt"
Clicked="Next_Clicked" /> Clicked="Next_Clicked" />
</StackLayout> </StackLayout>
</Grid>
</StackLayout> </StackLayout>
</StackLayout>
<!--
</ContentPage.Content> </ContentPage.Content>
-->
<ContentPage.ToolbarItems>
<ToolbarItem Name="Download" Order="Secondary" Priority="1" Clicked="Download_Clicked" />
</ContentPage.ToolbarItems>
</ContentPage> </ContentPage>
+101 -12
View File
@@ -1,13 +1,19 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaManager;
using Xamarin.Forms; using Xamarin.Forms;
using Xamarin.Forms.Xaml; using Xamarin.Forms.Xaml;
using Mear.Models; using Mear.Models;
using Mear.Repositories.Remote;
using Mear.Utilities;
using Mear.ViewModels;
namespace Mear.Views namespace Mear.Views
{ {
@@ -15,6 +21,7 @@ namespace Mear.Views
public partial class MearPlayerView : ContentPage public partial class MearPlayerView : ContentPage
{ {
#region Fields #region Fields
private MearPlayerViewModel _viewModel;
private Song _song; private Song _song;
#endregion #endregion
@@ -27,14 +34,19 @@ namespace Mear.Views
public MearPlayerView() public MearPlayerView()
{ {
InitializeComponent(); InitializeComponent();
Initialize();
BindingContext = _viewModel = new MearPlayerViewModel();
} }
public MearPlayerView(Song song) public MearPlayerView(Song song)
{ {
InitializeComponent(); InitializeComponent();
_song = song; _song = song;
Initialize();
InitializeControls(); InitializeControls();
BackgroundSongElasping();
BackgroundSongCoverUpdate();
BindingContext = _viewModel = new MearPlayerViewModel(song);
} }
#endregion #endregion
@@ -42,27 +54,88 @@ namespace Mear.Views
#region Methods #region Methods
private void Initialize() private void Initialize()
{ {
SongTitle.Text = "";
ArtistName.Text = "";
AlbumName.Text = "";
EndTime.Text = "";
} }
private void InitializeControls() private void InitializeControls()
{ {
SongTitle.Text = _song.Title; var songCnvrt = new TimeFormat();
ArtistName.Text = _song.Artist; var dur = _song.Duration;
AlbumName.Text = _song.Album; var endTime = songCnvrt.ConvertToSongTime(dur.Value);
EndTime.Text = $"{_song.Duration}";
EndTime.Text = endTime;
} }
#region Background
private async Task BackgroundSongElasping()
{
try
{
new Thread(async () =>
{
while (true)
{
Device.BeginInvokeOnMainThread(() =>
{
var ttlSec = (int)CrossMediaManager.Current.Position.TotalSeconds;
var cnvrt = new TimeFormat();
var curPos = cnvrt.ConvertToSongTime(ttlSec);
StartTime.Text = $"{curPos}";
double progVal = ((double)ttlSec) / ((double)_song.Duration);
progVal *= 100;
SongProgress.Value = progVal;
});
await Task.Delay(500);
}
}).Start();
}
catch (Exception ex)
{
var msg = ex.Message;
}
}
private async Task BackgroundSongCoverUpdate()
{
try
{
new Thread(async () =>
{
while (true)
{
Device.BeginInvokeOnMainThread(() =>
{
if (SongCover.Source.IsEmpty)
{
var meta = new SongMetadataRetriever();
var data = meta.ExtractCoverArtData(_song);
}
});
await Task.Delay(1000);
}
}).Start();
}
catch (Exception ex)
{
var msg = ex.Message;
}
}
#endregion
#region Events #region Events
private void Previous_Clicked(object sender, EventArgs e) private void Previous_Clicked(object sender, EventArgs e)
{ {
} }
private void Play_Clicked(object sender, EventArgs e) private async void Play_Clicked(object sender, EventArgs e)
{ {
if (CrossMediaManager.Current.IsPlaying())
{
await CrossMediaManager.Current.Pause();
}
else
{
await CrossMediaManager.Current.Play();
}
} }
private void Next_Clicked(object sender, EventArgs e) private void Next_Clicked(object sender, EventArgs e)
{ {
@@ -76,7 +149,23 @@ namespace Mear.Views
{ {
} }
private async void SongProgress_DragCompleted(object sender, EventArgs e)
{
var progVal = SongProgress.Value;
double newPos = (progVal / 100) * ((double)_song.Duration);
await CrossMediaManager.Current.SeekTo(TimeSpan.FromSeconds(newPos));
}
private void Download_Clicked(object sender, EventArgs e)
{
var songRepo = new RemoteSongRepository();
songRepo.DownloadSong(_song);
}
#endregion #endregion
#endregion #endregion
} }
} }
+2 -2
View File
@@ -49,8 +49,8 @@ namespace Mear.Views
try try
{ {
var song = (Song)SongListView.SelectedItem; var song = (Song)SongListView.SelectedItem;
await MearPlayer.StreamSongDemoAsync(song); song = await MearPlayer.StreamSongDemoAsync(song);
await Navigation.PushModalAsync(new MearPlayerView(song)); await Navigation.PushModalAsync(new NavigationPage(new MearPlayerView(song)));
} }
catch (Exception ex) catch (Exception ex)
{ {