1+ using MiniMediaScanner . Helpers ;
2+ using MiniMediaScanner . Models ;
3+ using MiniMediaScanner . Repositories ;
4+ using MiniMediaScanner . Services ;
5+ using RestSharp ;
6+
7+ namespace MiniMediaScanner . Commands ;
8+
9+ public class CoverArtDiscogsCommandHandler
10+ {
11+ private readonly MetadataRepository _metadataRepository ;
12+ private readonly ArtistRepository _artistRepository ;
13+ private readonly MatchRepository _matchRepository ;
14+ private readonly DiscogsRepository _discogsRepository ;
15+ private readonly DiscogsAPIService _discogsApiService ;
16+
17+ public CoverArtDiscogsCommandHandler ( string connectionString , string discogsToken )
18+ {
19+ _metadataRepository = new MetadataRepository ( connectionString ) ;
20+ _artistRepository = new ArtistRepository ( connectionString ) ;
21+ _matchRepository = new MatchRepository ( connectionString ) ;
22+ _discogsRepository = new DiscogsRepository ( connectionString ) ;
23+ _discogsApiService = new DiscogsAPIService ( discogsToken ) ;
24+ }
25+
26+ public async Task CheckAllMissingCoversAsync ( string album , string coverAlbumFileName , string coverArtistFileName )
27+ {
28+ await ParallelHelper . ForEachAsync ( await _artistRepository . GetAllArtistNamesAsync ( ) , 1 , async artist =>
29+ {
30+ try
31+ {
32+ await CheckAllMissingCoversAsync ( artist , album , coverAlbumFileName , coverArtistFileName ) ;
33+ }
34+ catch ( Exception e )
35+ {
36+ Console . WriteLine ( e . Message ) ;
37+ }
38+ } ) ;
39+ }
40+
41+ public async Task CheckAllMissingCoversAsync ( string artist , string album , string coverAlbumFileName , string coverArtistFileName )
42+ {
43+ Console . WriteLine ( $ "Checking artist '{ artist } '") ;
44+ var coverModels = ( await _metadataRepository . GetFolderPathsByArtistForCoversAsync ( artist , album ) )
45+ . ToList ( ) ;
46+
47+ Dictionary < Guid , int > discogsArtistIds = ( await Task . WhenAll (
48+ coverModels
49+ . DistinctBy ( cover => cover . ArtistId )
50+ . Select ( async cover => new
51+ {
52+ DiscogsArtistId = await _matchRepository . GetBestDiscogsMatchAsync ( cover . ArtistId , cover . ArtistName ) ,
53+ CoverArtistId = cover . ArtistId
54+ } )
55+ ) )
56+ . Where ( cover => cover . DiscogsArtistId > 0 )
57+ . ToDictionary ( cover => cover . CoverArtistId , cover => cover . DiscogsArtistId ?? 0 ) ;
58+
59+ if ( discogsArtistIds . Count == 0 )
60+ {
61+ Console . WriteLine ( $ "No Discogs artist found in the database for '{ artist } '") ;
62+ return ;
63+ }
64+
65+ foreach ( MetadataPathCoverModel coverModel in coverModels )
66+ {
67+ await CheckAlbumCoverAsync ( coverModel , coverAlbumFileName , discogsArtistIds ) ;
68+ }
69+
70+ await CheckArtistCoverAsync ( coverModels , coverArtistFileName , discogsArtistIds ) ;
71+ }
72+
73+ private async Task CheckArtistCoverAsync ( List < MetadataPathCoverModel > coverModels ,
74+ string coverArtistFileName ,
75+ Dictionary < Guid , int > discogsArtistIds )
76+ {
77+ var coverModel = coverModels . FirstOrDefault ( ) ;
78+ string ? subDirPath = coverModel ? . FolderPath ;
79+ if ( string . IsNullOrWhiteSpace ( subDirPath ) ||
80+ string . IsNullOrWhiteSpace ( coverModel ? . ArtistName ) )
81+ {
82+ return ;
83+ }
84+
85+ DirectoryInfo subDir = new DirectoryInfo ( subDirPath ) ;
86+ DirectoryInfo ? artistMusicFolder = GetArtistMusicFolder ( subDir , coverModel . ArtistName ) ;
87+ if ( artistMusicFolder == null )
88+ {
89+ return ;
90+ }
91+
92+ string coverArtistFileNameWithoutExtension = Path . GetFileNameWithoutExtension ( coverArtistFileName ) ;
93+ bool exists = artistMusicFolder . GetFiles ( "*.*" , SearchOption . TopDirectoryOnly )
94+ . FirstOrDefault ( fileName => fileName . Name . ToLower ( ) . StartsWith ( coverArtistFileNameWithoutExtension . ToLower ( ) ) ) != null ;
95+
96+ if ( exists )
97+ {
98+ return ;
99+ }
100+
101+ int discogsArtistId = 0 ;
102+ discogsArtistIds . TryGetValue ( coverModel . ArtistId , out discogsArtistId ) ;
103+
104+ if ( discogsArtistId == 0 )
105+ {
106+ Console . WriteLine ( $ "No artist found by '{ coverModel . ArtistName } '") ;
107+ return ;
108+ }
109+
110+ var discogsArtist = await _discogsApiService . GetArtistByIdAsync ( discogsArtistId ) ;
111+ string ? coverUrl = discogsArtist ? . Images ?
112+ . FirstOrDefault ( img => img . Type == "primary" && ! string . IsNullOrWhiteSpace ( img . Uri ) ) ? . Uri ;
113+
114+ if ( string . IsNullOrEmpty ( coverUrl ) )
115+ {
116+ Console . WriteLine ( $ "No cover art found for '{ coverModel . ArtistName } '") ;
117+ return ;
118+ }
119+
120+ string coverArtPath = Path . Join ( artistMusicFolder . FullName , coverArtistFileName ) ;
121+
122+ Console . WriteLine ( $ "Downloading cover art for { coverModel . ArtistName } ") ;
123+ await DownloadImageAsync ( coverUrl , coverArtPath ) ;
124+ }
125+
126+ private async Task CheckAlbumCoverAsync (
127+ MetadataPathCoverModel coverModel ,
128+ string coverAlbumFileName ,
129+ Dictionary < Guid , int > discogsArtistIds )
130+ {
131+ string coverAlbumFileNameWithoutExtension = Path . GetFileNameWithoutExtension ( coverAlbumFileName ) ;
132+ DirectoryInfo di = new DirectoryInfo ( coverModel . FolderPath ) ;
133+ if ( ! di . Exists )
134+ {
135+ return ;
136+ }
137+
138+ bool exists = di . GetFiles ( )
139+ . FirstOrDefault ( fileName => fileName . Name . ToLower ( ) . StartsWith ( coverAlbumFileNameWithoutExtension . ToLower ( ) ) ) != null ;
140+
141+ if ( exists )
142+ {
143+ return ;
144+ }
145+
146+ int discogsArtistId = 0 ;
147+ discogsArtistIds . TryGetValue ( coverModel . ArtistId , out discogsArtistId ) ;
148+
149+ if ( discogsArtistId == 0 )
150+ {
151+ Console . WriteLine ( $ "No artist found by '{ coverModel . ArtistName } '") ;
152+ return ;
153+ }
154+
155+ var releaseId = await _discogsRepository . GetAlbumIdByNameAsync ( discogsArtistId , coverModel . AlbumName ) ;
156+ if ( releaseId == 0 )
157+ {
158+ Console . WriteLine ( $ "No album found by '{ coverModel . ArtistName } ', '{ coverModel . AlbumName } '") ;
159+ return ;
160+ }
161+
162+ var discogsRelease = await _discogsApiService . GetReleaseByIdAsync ( releaseId . Value ) ;
163+ string ? coverUrl = discogsRelease ? . Images ?
164+ . OrderBy ( img => img . Type )
165+ . FirstOrDefault ( img => ! string . IsNullOrWhiteSpace ( img . Uri ) ) ? . Uri ;
166+
167+ if ( string . IsNullOrEmpty ( coverUrl ) )
168+ {
169+ Console . WriteLine ( $ "No cover art found for '{ coverModel . ArtistName } ', '{ coverModel . AlbumName } '") ;
170+ return ;
171+ }
172+
173+ string coverArtPath = Path . Join ( coverModel . FolderPath , coverAlbumFileName ) ;
174+
175+ Console . WriteLine ( $ "Downloading cover art for { coverModel . ArtistName } , { coverModel . AlbumName } ") ;
176+ await DownloadImageAsync ( coverUrl , coverArtPath ) ;
177+ }
178+
179+ private async Task DownloadImageAsync ( string imageUrl , string fileName )
180+ {
181+ try
182+ {
183+ using HttpClient client = new HttpClient ( ) ;
184+ HttpResponseMessage response = await client . GetAsync ( imageUrl ) ;
185+
186+ if ( response . IsSuccessStatusCode )
187+ {
188+ byte [ ] imageBytes = await response . Content . ReadAsByteArrayAsync ( ) ;
189+ File . WriteAllBytes ( fileName , imageBytes ) ;
190+ Console . WriteLine ( $ "Cover art downloaded and saved as: { fileName } ") ;
191+ }
192+ else
193+ {
194+ Console . WriteLine ( $ "Failed to download image. HTTP Status: { response . StatusCode } ") ;
195+ }
196+ }
197+ catch ( Exception ex )
198+ {
199+ Console . WriteLine ( $ "An error occurred while downloading the image: { ex . Message } ") ;
200+ }
201+ }
202+
203+ private DirectoryInfo ? GetArtistMusicFolder ( DirectoryInfo subDirectory , string artistName )
204+ {
205+ subDirectory = subDirectory . Parent ;
206+
207+ while ( subDirectory != null && ! string . Equals ( subDirectory . Name , artistName , StringComparison . OrdinalIgnoreCase ) )
208+ {
209+ subDirectory = subDirectory . Parent ;
210+ }
211+ return subDirectory ;
212+ }
213+ }
0 commit comments