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+ }
0 commit comments