diff --git a/MusicX/MusicX.csproj b/MusicX/MusicX.csproj index ac6ed4fc..b207a5ac 100644 --- a/MusicX/MusicX.csproj +++ b/MusicX/MusicX.csproj @@ -101,7 +101,7 @@ - + diff --git a/MusicX/Services/DownloaderService.cs b/MusicX/Services/DownloaderService.cs index a3a44103..6d52b220 100644 --- a/MusicX/Services/DownloaderService.cs +++ b/MusicX/Services/DownloaderService.cs @@ -9,10 +9,13 @@ using Windows.Media.Transcoding; using Windows.Storage; using FFMediaToolkit; +using FFMediaToolkit.Audio; +using FFMediaToolkit.Decoding; using FFmpegInteropX; using MusicX.Core.Services; using MusicX.Helpers; using MusicX.Services.Player.Playlists; +using MusicX.Services.Player.Sources; using MusicX.Shared.Player; using TagLib; using TagLib.Id3v2; @@ -55,8 +58,8 @@ public async Task DownloadAudioAsync(PlaylistTrack audio, IProgress<(TimeSpan Po if (string.IsNullOrEmpty(audio.Data.Url)) return; - var fileName = $"{audio.GetArtistsString()} - {audio.Title}.mp3"; - fileName = ReplaceSymbols(fileName); + var fileName = $"{audio.GetArtistsString()} - {audio.Title}"; + fileName = ReplaceSymbols(fileName) + ".mp3"; string fileDownloadPath; var musicFolder = GetDownloadDirectoryAsync(); @@ -98,13 +101,17 @@ public async Task DownloadAudioAsync(PlaylistTrack audio, IProgress<(TimeSpan Po } else { - using var ffmpegMediaSource = await FFmpegMediaSource.CreateFromUriAsync(audio.Data.Url, new() - { - FFmpegOptions = - { - ["http_persistent"] = "false" - } - }).AsTask(cancellationToken); + // blocked by FFmpegMediaSource working only with active media player + // using var ffmpegMediaSource = await FFmpegMediaSource.CreateFromUriAsync(audio.Data.Url, new() + // { + // FFmpegOptions = + // { + // ["http_persistent"] = "false" + // } + // }).AsTask(cancellationToken); + // var streamSource = ffmpegMediaSource.GetMediaStreamSource(); + + var streamSource = MediaSourceBase.CreateFFMediaStreamSource(audio.Data.Url); var encodingProfile = MediaEncodingProfile.CreateMp3(AudioEncodingQuality.Auto); @@ -114,7 +121,7 @@ public async Task DownloadAudioAsync(PlaylistTrack audio, IProgress<(TimeSpan Po using var destinationStream = await destination.OpenAsync(FileAccessMode.ReadWrite).AsTask(cancellationToken); var prepareOp = - await _mediaTranscoder.PrepareMediaStreamSourceTranscodeAsync(ffmpegMediaSource.GetMediaStreamSource(), + await _mediaTranscoder.PrepareMediaStreamSourceTranscodeAsync(streamSource, destinationStream, encodingProfile).AsTask(cancellationToken); if (!prepareOp.CanTranscode) @@ -128,9 +135,10 @@ await _mediaTranscoder.PrepareMediaStreamSourceTranscodeAsync(ffmpegMediaSource. var transcodeOp = prepareOp.TranscodeAsync(); - transcodeOp.Progress += (sender, position) => + var duration = streamSource.Duration; + transcodeOp.Progress += (_, position) => { - progress?.Report((TimeSpan.FromSeconds(position), TimeSpan.FromSeconds(1))); + progress?.Report((TimeSpan.FromSeconds(position), duration)); }; await transcodeOp.AsTask(cancellationToken); @@ -141,7 +149,7 @@ await _mediaTranscoder.PrepareMediaStreamSourceTranscodeAsync(ffmpegMediaSource. private string ReplaceSymbols(string fileName) { - return string.Join("_", fileName.Split(Path.GetInvalidFileNameChars())); + return string.Join("_", fileName.Split(Path.GetInvalidFileNameChars())).Replace('.', '_'); } private async Task AddMetadataAsync(PlaylistTrack audio, string filePath, CancellationToken cancellationToken) diff --git a/MusicX/Services/Player/Sources/MediaSourceBase.cs b/MusicX/Services/Player/Sources/MediaSourceBase.cs index 8f3f872d..21436c5a 100644 --- a/MusicX/Services/Player/Sources/MediaSourceBase.cs +++ b/MusicX/Services/Player/Sources/MediaSourceBase.cs @@ -23,7 +23,7 @@ public abstract class MediaSourceBase : ITrackMediaSource { private static readonly Semaphore FFmpegSemaphore = new(1, 1, "MusicX_FFmpegSemaphore"); - protected readonly MediaOptions MediaOptions = new() + protected static readonly MediaOptions MediaOptions = new() { StreamsToLoad = MediaMode.Audio, AudioSampleFormat = SampleFormat.SignedWord, @@ -38,7 +38,9 @@ public abstract class MediaSourceBase : ITrackMediaSource ["reconnect"] = "1", ["reconnect_streamed"] = "1", ["reconnect_delay_max"] = "5", - ["stimeout"] = "10" + ["stimeout"] = "10", + ["timeout"] = "10", + ["rw_timeout"] = "10" } } }; @@ -48,6 +50,18 @@ public abstract class MediaSourceBase : ITrackMediaSource CancellationToken cancellationToken = default); protected static MediaPlaybackItem CreateMediaPlaybackItem(MediaFile file) + { + var streamingSource = CreateFFMediaStreamSource(file); + + return new (MediaSource.CreateFromMediaStreamSource(streamingSource)); + } + + public static MediaStreamSource CreateFFMediaStreamSource(string url) + { + return CreateFFMediaStreamSource(MediaFile.Open(url, MediaOptions)); + } + + public static MediaStreamSource CreateFFMediaStreamSource(MediaFile file) { var properties = AudioEncodingProperties.CreatePcm((uint)file.Audio.Info.SampleRate, (uint)file.Audio.Info.NumChannels, 16); @@ -72,7 +86,7 @@ protected static MediaPlaybackItem CreateMediaPlaybackItem(MediaFile file) try { - FFmpegSemaphore.WaitOne(); + FFmpegSemaphore.WaitOne(TimeSpan.FromSeconds(10)); file.Audio.GetFrame(position); } catch (FFmpegException) @@ -84,9 +98,9 @@ protected static MediaPlaybackItem CreateMediaPlaybackItem(MediaFile file) } }; - streamingSource.Closed += async (_, _) => + streamingSource.Closed += (_, _) => { - await FFmpegSemaphore.WaitOneAsync(); + FFmpegSemaphore.WaitOne(TimeSpan.FromSeconds(10)); try { file.Dispose(); @@ -99,10 +113,7 @@ protected static MediaPlaybackItem CreateMediaPlaybackItem(MediaFile file) streamingSource.SampleRequested += (_, args) => { - //var deferral = args.Request.GetDeferral(); - - //await FFmpegSemaphore.WaitOneAsync(); - FFmpegSemaphore.WaitOne(); + FFmpegSemaphore.WaitOne(TimeSpan.FromSeconds(10)); try { @@ -116,7 +127,6 @@ protected static MediaPlaybackItem CreateMediaPlaybackItem(MediaFile file) finally { FFmpegSemaphore.Release(); - //deferral.Complete(); } }; @@ -149,15 +159,11 @@ protected static MediaPlaybackItem CreateMediaPlaybackItem(MediaFile file) return array; } - return new (MediaSource.CreateFromMediaStreamSource(streamingSource)); + return streamingSource; } - private static FFmpegMediaSource? _source; - public static async Task CreateWinRtMediaPlaybackItem(MediaPlaybackSession playbackSession, TrackData data, IReadOnlyDictionary? customOptions = null) { - _source?.Dispose(); - var options = new PropertySet { ["reconnect"] = "1", @@ -170,7 +176,7 @@ protected static MediaPlaybackItem CreateMediaPlaybackItem(MediaFile file) foreach (var (key, value) in customOptions) options[key] = value; - _source = await FFmpegMediaSource.CreateFromUriAsync(data.Url, new() + var source = await FFmpegMediaSource.CreateFromUriAsync(data.Url, new() { FFmpegOptions = options, General = @@ -179,9 +185,9 @@ protected static MediaPlaybackItem CreateMediaPlaybackItem(MediaFile file) } }); - _source.PlaybackSession = playbackSession; - _source.StartBuffering(); + source.PlaybackSession = playbackSession; + source.StartBuffering(); - return _source.CreateMediaPlaybackItem(); + return source.CreateMediaPlaybackItem(); } } \ No newline at end of file diff --git a/MusicX/Views/DownloadsView.xaml b/MusicX/Views/DownloadsView.xaml index 85db76e3..614e3056 100644 --- a/MusicX/Views/DownloadsView.xaml +++ b/MusicX/Views/DownloadsView.xaml @@ -154,7 +154,7 @@ - + diff --git a/MusicX/packages.lock.json b/MusicX/packages.lock.json index b1198cd4..935ad6a2 100644 --- a/MusicX/packages.lock.json +++ b/MusicX/packages.lock.json @@ -140,9 +140,9 @@ }, "WPF-UI": { "type": "Direct", - "requested": "[3.0.3, )", - "resolved": "3.0.3", - "contentHash": "Nm6Q5StLWxs74eFcqTwNJTGpA6xbTF1BXLniy5HMwnowvjiEa6Wa26/eI3e4/KbD56YF4nZidn69G/lY7t+aNg==" + "requested": "[3.0.4, )", + "resolved": "3.0.4", + "contentHash": "Jbt8nJ4MSC/WBhqx6iXOW06Rt2UUNVxA8+23AOSM63jlOIhU6e6P4BIw8rL/UnRPycOdA2vntYl5i7k53E0AGg==" }, "WpfScreenHelper": { "type": "Direct",