Compare commits

..

4 Commits

Author SHA1 Message Date
140d4ea49a Updated packages. 2026-07-26 17:08:07 -04:00
aa9861162d Optimized filtering playlist songs. 2026-07-26 17:07:49 -04:00
ffe3802b25 Moved fetching image to background thread. 2026-07-26 17:07:14 -04:00
3fd9e8a00c Fixed removing locked object. 2026-07-26 17:06:33 -04:00
5 changed files with 53 additions and 52 deletions

View File

@@ -60,7 +60,7 @@ public abstract class Cache<TKey, TValue> : ICache<TKey, TValue> where TKey : no
if (lockAcquired) if (lockAcquired)
lockObject.Release(); lockObject.Release();
_locks.TryRemove(lockObject, out _); _locks.TryRemove(actualKey, out _);
if (throttlerAcquired) if (throttlerAcquired)
_throttler.Release(); _throttler.Release();

View File

@@ -1,5 +1,4 @@
using Harmonia.Core.Extensions; using Harmonia.Core.Extensions;
using Harmonia.Core.Playlists;
using Harmonia.WinUI.Caching; using Harmonia.WinUI.Caching;
using Harmonia.WinUI.Storage; using Harmonia.WinUI.Storage;
using Harmonia.WinUI.ViewModels; using Harmonia.WinUI.ViewModels;

View File

@@ -40,7 +40,9 @@ public class AudioBitmapImageCache(IAudioImageExtractor audioImageExtractor) : M
protected override async ValueTask<BitmapImage?> FetchAsync(Song key, CancellationToken cancellationToken) protected override async ValueTask<BitmapImage?> FetchAsync(Song key, CancellationToken cancellationToken)
{ {
SongPictureInfo? songPictureInfo = await audioImageExtractor.ExtractImageAsync(key.FileName, cancellationToken); SongPictureInfo? songPictureInfo = await Task.Run(
() => audioImageExtractor.ExtractImageAsync(key.FileName, cancellationToken),
cancellationToken);
if (songPictureInfo == null) if (songPictureInfo == null)
return GetDefaultBitmapImage(); return GetDefaultBitmapImage();
@@ -51,8 +53,17 @@ public class AudioBitmapImageCache(IAudioImageExtractor audioImageExtractor) : M
await bitmapImage.SetSourceAsync(stream.AsRandomAccessStream()); await bitmapImage.SetSourceAsync(stream.AsRandomAccessStream());
bitmapImage.DecodePixelWidth = GetDecodePixelWidth(bitmapImage); int decodePixelWidth = GetDecodePixelWidth(bitmapImage);
bitmapImage.DecodePixelHeight = GetDecodePixelHeight(bitmapImage); int decodePixelHeight = GetDecodePixelHeight(bitmapImage);
if (decodePixelWidth > 0 || decodePixelHeight > 0)
{
bitmapImage.DecodePixelWidth = decodePixelWidth;
bitmapImage.DecodePixelHeight = decodePixelHeight;
stream.Seek(0, SeekOrigin.Begin);
await bitmapImage.SetSourceAsync(stream.AsRandomAccessStream());
}
return bitmapImage; return bitmapImage;
} }
@@ -62,8 +73,6 @@ public class AudioBitmapImageCache(IAudioImageExtractor audioImageExtractor) : M
Uri uri = new("ms-appx:///Assets/Default.png", UriKind.Absolute); Uri uri = new("ms-appx:///Assets/Default.png", UriKind.Absolute);
BitmapImage bitmapImage = new(); BitmapImage bitmapImage = new();
bitmapImage.DecodePixelWidth = GetDecodePixelWidth(bitmapImage);
bitmapImage.DecodePixelHeight = GetDecodePixelHeight(bitmapImage);
bitmapImage.UriSource = uri; bitmapImage.UriSource = uri;
return bitmapImage; return bitmapImage;
@@ -99,7 +108,10 @@ public class AudioBitmapImageCache(IAudioImageExtractor audioImageExtractor) : M
protected override long GetEntrySize(BitmapImage entry) protected override long GetEntrySize(BitmapImage entry)
{ {
return entry.DecodePixelWidth * entry.DecodePixelHeight; int width = entry.DecodePixelWidth > 0 ? entry.DecodePixelWidth : entry.PixelWidth;
int height = entry.DecodePixelHeight > 0 ? entry.DecodePixelHeight : entry.PixelHeight;
return (long)width * height * 4;
} }
} }

View File

@@ -44,7 +44,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.2" /> <PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.2" />
<PackageReference Include="CommunityToolkit.WinUI.Media" Version="8.2.251219" /> <PackageReference Include="CommunityToolkit.WinUI.Media" Version="8.2.251219" />
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.28000.2270" /> <PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.28000.2526" />
<PackageReference Include="Microsoft.WindowsAppSDK" Version="2.3.1" /> <PackageReference Include="Microsoft.WindowsAppSDK" Version="2.3.1" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@@ -20,11 +20,10 @@ using System.Linq;
using System.Text.Json; using System.Text.Json;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Timers;
using System.Windows.Input; using System.Windows.Input;
using Windows.ApplicationModel.DataTransfer; using Windows.ApplicationModel.DataTransfer;
using DispatcherQueue = Microsoft.UI.Dispatching.DispatcherQueue; using DispatcherQueue = Microsoft.UI.Dispatching.DispatcherQueue;
using Timer = System.Timers.Timer; using DispatcherQueueTimer = Microsoft.UI.Dispatching.DispatcherQueueTimer;
namespace Harmonia.WinUI.ViewModels; namespace Harmonia.WinUI.ViewModels;
@@ -40,7 +39,7 @@ public partial class PlaylistViewModel : ViewModelBase
private readonly DispatcherQueue _dispatcherQueue; private readonly DispatcherQueue _dispatcherQueue;
private readonly ConcurrentDictionary<int, CancellationTokenSource> _imageCancellationTokens = []; private readonly ConcurrentDictionary<int, CancellationTokenSource> _imageCancellationTokens = [];
private Timer? _filterTimer; private DispatcherQueueTimer? _filterTimer;
public Playlist? Playlist { get; private set; } public Playlist? Playlist { get; private set; }
@@ -300,26 +299,19 @@ public partial class PlaylistViewModel : ViewModelBase
{ {
if (_filterTimer == null) if (_filterTimer == null)
{ {
_filterTimer = new Timer(300); _filterTimer = _dispatcherQueue.CreateTimer();
_filterTimer.Elapsed += OnFilterTimerElapsed; _filterTimer.Interval = TimeSpan.FromMilliseconds(300);
_filterTimer.Start(); _filterTimer.IsRepeating = false;
_filterTimer.Tick += OnFilterTimerTick;
} }
else
{
_filterTimer.Interval = 300;
}
}
private void OnFilterTimerElapsed(object? sender, ElapsedEventArgs e)
{
if (_filterTimer == null)
return;
_filterTimer.Stop(); _filterTimer.Stop();
_filterTimer.Dispose(); _filterTimer.Start();
_filterTimer = null; }
_dispatcherQueue.TryEnqueue(UpdateFilteredSongs); private void OnFilterTimerTick(DispatcherQueueTimer sender, object args)
{
UpdateFilteredSongs();
} }
private void UpdateFilteredSongs() private void UpdateFilteredSongs()
@@ -327,32 +319,31 @@ public partial class PlaylistViewModel : ViewModelBase
if (Playlist == null) if (Playlist == null)
return; return;
List<PlaylistSong> filteredPlaylistSongs = [.. Playlist.Songs.Where(playlistSong => IsFiltered(playlistSong.Song))]; string? filter = Filter;
//FilteredPlaylistSongs = [.. filteredPlaylistSongs]; HashSet<PlaylistSong> updatedFilterSet = [.. Playlist.Songs.Where(playlistSong => IsFiltered(playlistSong.Song, filter))];
for (int i = FilteredPlaylistSongs.Count - 1; i >= 0; i--) for (int i = FilteredPlaylistSongs.Count - 1; i >= 0; i--)
{ {
PlaylistSong playlistSong = FilteredPlaylistSongs[i]; PlaylistSong playlistSong = FilteredPlaylistSongs[i];
bool inPlaylist = Playlist.Songs.Contains(playlistSong); bool inFilterSet = updatedFilterSet.Contains(playlistSong);
bool inFilter = filteredPlaylistSongs.Contains(playlistSong);
if (!inPlaylist || !inFilter) if (!inFilterSet)
{ FilteredPlaylistSongs.RemoveAt(i);
FilteredPlaylistSongs.Remove(playlistSong);
}
} }
HashSet<PlaylistSong> currentSet = [.. FilteredPlaylistSongs];
int insertionIndex = 0; int insertionIndex = 0;
foreach (PlaylistSong playlistSong in Playlist.Songs) foreach (PlaylistSong playlistSong in Playlist.Songs)
{ {
bool inFilter = filteredPlaylistSongs.Contains(playlistSong); bool inFilterSet = updatedFilterSet.Contains(playlistSong);
bool inCurrentFilteredList = FilteredPlaylistSongs.Contains(playlistSong); bool inCurrentSet = currentSet.Contains(playlistSong);
if (inFilter) if (!inFilterSet)
{ continue;
if (!inCurrentFilteredList)
if (!inCurrentSet)
{ {
FilteredPlaylistSongs.Insert(insertionIndex, playlistSong); FilteredPlaylistSongs.Insert(insertionIndex, playlistSong);
} }
@@ -360,28 +351,27 @@ public partial class PlaylistViewModel : ViewModelBase
insertionIndex++; insertionIndex++;
} }
} }
}
private bool IsFiltered(Song song) private static bool IsFiltered(Song song, string? filter)
{ {
if (string.IsNullOrWhiteSpace(Filter)) if (string.IsNullOrWhiteSpace(filter))
return true; return true;
var shortFileName = Path.GetFileName(song.FileName); var shortFileName = Path.GetFileName(song.FileName);
if (shortFileName.Contains(Filter, StringComparison.OrdinalIgnoreCase)) if (shortFileName.Contains(filter, StringComparison.OrdinalIgnoreCase))
return true; return true;
if (string.IsNullOrWhiteSpace(song.Title) == false && song.Title.Contains(Filter, StringComparison.OrdinalIgnoreCase)) if (string.IsNullOrWhiteSpace(song.Title) == false && song.Title.Contains(filter, StringComparison.OrdinalIgnoreCase))
return true; return true;
if (string.IsNullOrWhiteSpace(song.Album) == false && song.Album.Contains(Filter, StringComparison.OrdinalIgnoreCase)) if (string.IsNullOrWhiteSpace(song.Album) == false && song.Album.Contains(filter, StringComparison.OrdinalIgnoreCase))
return true; return true;
if (song.AlbumArtists.Any(x => x.Contains(Filter, StringComparison.OrdinalIgnoreCase))) if (song.AlbumArtists.Any(x => x.Contains(filter, StringComparison.OrdinalIgnoreCase)))
return true; return true;
if (song.Artists.Any(x => x.Contains(Filter, StringComparison.OrdinalIgnoreCase))) if (song.Artists.Any(x => x.Contains(filter, StringComparison.OrdinalIgnoreCase)))
return true; return true;
return false; return false;