Skip to content
This repository was archived by the owner on Jan 19, 2026. It is now read-only.

Commit d9d239f

Browse files
committed
feat(artist sorting)
1 parent 110fa90 commit d9d239f

File tree

16 files changed

+279
-7
lines changed

16 files changed

+279
-7
lines changed

App.axaml.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
using Avalonix.Services.DiskWriter;
1212
using Avalonix.Services.PlayableManager;
1313
using Avalonix.Services.PlayableManager.AlbumManager;
14+
using Avalonix.Services.PlayableManager.ArtistManager;
1415
using Avalonix.Services.PlayableManager.PlayboxManager;
1516
using Avalonix.Services.PlayableManager.PlaylistManager;
1617
using Avalonix.Services.SettingsManager;
@@ -58,6 +59,7 @@ public override void OnFrameworkInitializationCompleted()
5859
services.AddSingleton<IPlaylistManager, PlaylistManager>();
5960
services.AddSingleton<IPlayboxManager, PlayboxManager>();
6061
services.AddSingleton<IAlbumManager, AlbumManager>();
62+
services.AddSingleton<IArtistManager, ArtistManager>();
6163
services.AddSingleton<IThemeManager, ThemeManager>();
6264
}).ConfigureLogging(log =>
6365
{

Avalonix.csproj

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
<Folder Include="github\"/>
1414
<AvaloniaResource Include="Assets\**"/>
1515
<Folder Include="Model\"/>
16-
<Folder Include="Model\Media\Artist\"/>
1716
</ItemGroup>
1817

1918

Model/Media/Album/Album.cs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,12 @@ namespace Avalonix.Model.Media.Album;
88

99
public record Album : IPlayable
1010
{
11-
public AlbumMetadata? Metadata;
12-
1311
public Album(List<Track.Track> tracks, IMediaPlayer player, ILogger logger, PlaySettings settings)
1412
{
1513
PlayQueue = new PlayQueue(player, logger, settings);
1614

17-
Metadata = new AlbumMetadata(tracks);
18-
Name = Metadata.AlbumName;
15+
var metadata = new AlbumMetadata(tracks);
16+
Name = metadata.AlbumName;
1917

2018
PlayQueue.FillQueue(tracks);
2119
}

Model/Media/Album/AlbumMetadata.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ private async Task FillMetadata(List<Track.Track> tracks)
1919
foreach (var track in tracks)
2020
await track.FillPrimaryMetaData();
2121

22-
AlbumName = tracks[0].Metadata.Album ?? "none";
22+
AlbumName = tracks[0].Metadata.Album ?? string.Empty;
2323
var tracksMetadata = tracks.Select(x => x.Metadata).ToList();
2424
foreach (var trackMetadata in
2525
tracksMetadata.Where(trackMetadata => !string.IsNullOrEmpty(trackMetadata.Artist)))

Model/Media/Artist/Artist.cs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
using System.Collections.Generic;
2+
using System.Threading.Tasks;
3+
using Avalonix.Model.Media.MediaPlayer;
4+
using Avalonix.Model.UserSettings.AvalonixSettingsFiles;
5+
using Microsoft.Extensions.Logging;
6+
7+
namespace Avalonix.Model.Media.Artist;
8+
9+
public record Artist : IPlayable
10+
{
11+
public string Name { get; }
12+
public PlayQueue PlayQueue { get; }
13+
14+
public Artist(List<Track.Track> tracks, IMediaPlayer player, ILogger logger, PlaySettings settings)
15+
{
16+
PlayQueue = new PlayQueue(player, logger, settings);
17+
Name = tracks[0].Metadata.Artist ?? string.Empty;
18+
PlayQueue.FillQueue(tracks);
19+
}
20+
21+
public async Task Play()
22+
{
23+
await PlayQueue.Play();
24+
}
25+
26+
public void Pause()
27+
{
28+
PlayQueue.Pause();
29+
}
30+
31+
public void Stop()
32+
{
33+
PlayQueue.Stop();
34+
}
35+
36+
public void Resume()
37+
{
38+
PlayQueue.Resume();
39+
}
40+
41+
public void NextTrack()
42+
{
43+
PlayQueue.NextTrack();
44+
}
45+
46+
public void BackTrack()
47+
{
48+
PlayQueue.BackTrack();
49+
}
50+
51+
public void ForceStartTrack(Track.Track track)
52+
{
53+
PlayQueue.ForceStartTrack(track);
54+
}
55+
56+
public async Task LoadTracksMetadata()
57+
{
58+
foreach (var track in PlayQueue.Tracks) await track.FillPrimaryMetaData();
59+
}
60+
61+
public bool QueueIsEmpty()
62+
{
63+
return PlayQueue.QueueIsEmpty();
64+
}
65+
}

Services/PlayableManager/AlbumManager/AlbumManager.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,6 @@ private async Task<List<Album>> GetAlbums()
124124

125125
private async Task LoadTracks()
126126
{
127-
var settings = settingsManager.Settings.Avalonix;
128127
var paths = diskManager.GetMusicFiles();
129128

130129
_tracks.Clear();
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Threading;
5+
using System.Threading.Tasks;
6+
using Avalonix.Model.Media;
7+
using Avalonix.Model.Media.Artist;
8+
using Avalonix.Model.Media.MediaPlayer;
9+
using Avalonix.Model.Media.Track;
10+
using Avalonix.Model.UserSettings;
11+
using Avalonix.Services.CacheManager;
12+
using Avalonix.Services.DiskManager;
13+
using Avalonix.Services.SettingsManager;
14+
using Microsoft.Extensions.Logging;
15+
16+
namespace Avalonix.Services.PlayableManager.ArtistManager;
17+
18+
public class ArtistManager(
19+
ILogger logger,
20+
IMediaPlayer player,
21+
ISettingsManager settingsManager,
22+
IDiskManager diskManager,
23+
IMediaPlayer mediaPlayer,
24+
ICacheManager cacheManager) : IArtistManager
25+
{
26+
private readonly Settings _settings = settingsManager.Settings;
27+
private readonly List<Track> _tracks = [];
28+
private bool _tracksLoaded;
29+
30+
public CancellationTokenSource? GlobalCancellationTokenSource { get; private set; }
31+
public IMediaPlayer MediaPlayer { get; } = mediaPlayer;
32+
public IPlayable? PlayingPlayable { get; set; }
33+
public async Task<List<IPlayable>> GetPlayables()
34+
{
35+
var result = await Task.Run(GetArtists);
36+
return result.Cast<IPlayable>().ToList();
37+
}
38+
39+
public void StartPlayable(IPlayable artist)
40+
{
41+
try
42+
{
43+
GlobalCancellationTokenSource?.Cancel();
44+
}
45+
catch (ObjectDisposedException)
46+
{
47+
}
48+
finally
49+
{
50+
GlobalCancellationTokenSource?.Dispose();
51+
}
52+
53+
GlobalCancellationTokenSource = new CancellationTokenSource();
54+
55+
PlayingPlayable = artist;
56+
57+
PlayableChanged?.Invoke();
58+
59+
_ = Task.Run(async () =>
60+
{
61+
try
62+
{
63+
await PlayingPlayable.Play();
64+
}
65+
catch (OperationCanceledException)
66+
{
67+
}
68+
catch (Exception ex)
69+
{
70+
logger.LogError(ex, "Artist play failed");
71+
}
72+
});
73+
}
74+
75+
private async Task<List<Artist>> GetArtists()
76+
{
77+
await Task.Run(LoadTracks);
78+
79+
if (!_tracksLoaded)
80+
{
81+
logger.LogWarning("Tracks metadata not loaded yet. Call LoadTracks() first.");
82+
return [];
83+
}
84+
85+
var allValidTracks = _tracks.Where(track =>
86+
!string.IsNullOrEmpty(track.Metadata.Artist));
87+
88+
var albumGroups = allValidTracks.GroupBy(track => new { track.Metadata.Artist});
89+
90+
return albumGroups.Select(group =>
91+
new Artist(group.ToList(), player, logger, settingsManager.Settings.Avalonix.PlaySettings)).ToList();
92+
}
93+
94+
private async Task LoadTracks()
95+
{
96+
var paths = diskManager.GetMusicFiles();
97+
98+
_tracks.Clear();
99+
_tracksLoaded = false;
100+
101+
var tracks = new Track[paths.Count];
102+
103+
for (var i = 0; i < tracks.Length; i++)
104+
{
105+
var path = paths[i];
106+
var track = new Track(path, cacheManager);
107+
await track.FillPrimaryMetaData();
108+
tracks[i] = track;
109+
}
110+
111+
_tracks.AddRange(tracks);
112+
_tracksLoaded = true;
113+
}
114+
115+
public event Action<bool> PlaybackStateChanged
116+
{
117+
add => player.PlaybackStateChanged += value;
118+
remove => player.PlaybackStateChanged -= value;
119+
}
120+
121+
public event Action TrackChanged
122+
{
123+
add => player.TrackChanged += value;
124+
remove => player.TrackChanged -= value;
125+
}
126+
127+
public event Action<bool> ShuffleChanged
128+
{
129+
add => _settings.Avalonix.ShuffleChanged += value;
130+
remove => _settings.Avalonix.ShuffleChanged -= value;
131+
}
132+
133+
public event Action<bool> LoopChanged
134+
{
135+
add => _settings.Avalonix.LoopChanged += value;
136+
remove => _settings.Avalonix.LoopChanged -= value;
137+
}
138+
139+
public event Action? PlayableChanged;
140+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
namespace Avalonix.Services.PlayableManager.ArtistManager;
2+
3+
public interface IArtistManager : IPlayableManager
4+
{
5+
6+
}

Services/PlayableManager/PlayablesManager.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@
22
using System.Threading.Tasks;
33
using Avalonix.Model.Media;
44
using Avalonix.Model.Media.Album;
5+
using Avalonix.Model.Media.Artist;
56
using Avalonix.Model.Media.MediaPlayer;
67
using Avalonix.Model.Media.PlayBox;
78
using Avalonix.Model.Media.Playlist;
89
using Avalonix.Model.UserSettings;
910
using Avalonix.Services.PlayableManager.AlbumManager;
11+
using Avalonix.Services.PlayableManager.ArtistManager;
1012
using Avalonix.Services.PlayableManager.PlayboxManager;
1113
using Avalonix.Services.PlayableManager.PlaylistManager;
1214
using Avalonix.Services.SettingsManager;
@@ -19,6 +21,7 @@ public class PlayablesManager(
1921
ILogger logger,
2022
IPlaylistManager playlistManager,
2123
IAlbumManager albumManager,
24+
IArtistManager artistManager,
2225
IPlayboxManager playboxManager,
2326
IMediaPlayer mediaPlayer,
2427
ISettingsManager settingsManager) : IPlayablesManager
@@ -32,6 +35,7 @@ public Task StartPlayable(IPlayable playable)
3235
{
3336
playlistManager.PlayingPlayable?.Stop();
3437
albumManager.PlayingPlayable?.Stop();
38+
artistManager.PlayingPlayable?.Stop();
3539
playboxManager.PlayingPlayable?.Stop();
3640
PlayingPlayable = playable;
3741

@@ -43,6 +47,9 @@ public Task StartPlayable(IPlayable playable)
4347
case Album:
4448
albumManager.StartPlayable(playable);
4549
break;
50+
case Artist:
51+
artistManager.StartPlayable(playable);
52+
break;
4653
case Playbox:
4754
playboxManager.StartPlayable(playable);
4855
break;

Services/WindowManager/IWindowManager.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,6 @@ public interface IWindowManager
1717
EditMetadataWindow EditMetadataWindow_Open(Track track);
1818
PlayableSelectWindow PlaylistDeleteWindow_Open();
1919
PlayableSelectWindow AlbumSelectAndPlayWindow_Open();
20+
PlayableSelectWindow ArtistSelectAndPlayWindow_Open();
2021
SettingsWindow SettingsWindow_Open();
2122
}

0 commit comments

Comments
 (0)