77//
88
99#import " LyricsViewController.h"
10+ #import " Weakify.h"
1011
1112extern DB_functions_t *deadbeef;
1213
14+ static NSString * const lyricsNotAvailableString = @" Lyrics Not Available" ;
15+
1316@interface LyricsViewController ()
1417
18+ @property (nonatomic ) NSCache <NSString *, NSString *> *lyricsCache;
19+
1520@property (unsafe_unretained) IBOutlet NSTextView *textView;
1621
1722@end
@@ -27,52 +32,132 @@ - (void)dealloc {
2732
2833- (void )viewDidLoad {
2934 [super viewDidLoad ];
35+ self.lyricsCache = [NSCache new ];
3036 [self update ];
3137}
3238
33- - (void )update {
34- NSString *lyrics = @" Lyrics Not Available" ;
39+ - (NSString *)parseLyricsFromData : (NSData *)data response : (NSURLResponse *)response {
40+ NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
41+
42+ if (data == nil || httpResponse.statusCode != 200 || ![response.MIMEType isEqualToString: @" application/json" ]) {
43+ return nil ;
44+ }
45+
46+ id object = [NSJSONSerialization JSONObjectWithData: data options: 0 error: nil ];
47+ if (![object isKindOfClass: NSDictionary .class]) {
48+ return nil ;
49+ }
50+
51+ NSDictionary *dict = object;
52+ NSString *lyrics = dict[@" lyrics" ];
53+ if (![lyrics isKindOfClass: NSString .class]) {
54+ return nil ;
55+ }
56+
57+ return lyrics;
58+ }
59+
60+ - (void )fetchFromLyricsOvh {
3561 NSString *artist;
3662 NSString *title;
37- if (self.track != NULL ) {
38- // get lyrics from tags
39- deadbeef->pl_lock ();
40-
41- // A bit of a hack since deadbeef stores multiline values as 0-separated lines.
42- DB_metaInfo_t *meta = deadbeef->pl_meta_for_key (self.track , " lyrics" );
43- if (meta != NULL ) {
44- size_t buffer_size = 100000 ;
45- char *lyrics_buffer = malloc (100000 );
46- *lyrics_buffer = 0 ;
47-
48- size_t value_size = meta->valuesize ;
49- if (value_size > buffer_size - 1 ) {
50- value_size = buffer_size - 1 ;
51- }
52- memcpy (lyrics_buffer, meta->value , value_size);
53- char *p = lyrics_buffer;
54- for (size_t i = 0 ; i <= value_size; i++, p++) {
55- if (*p == 0 ) {
56- *p = ' \n ' ;
57- }
58- }
59- lyrics_buffer[value_size] = 0 ;
60- lyrics = @(lyrics_buffer);
61- free (lyrics_buffer);
62- }
6363
64- const char *str = deadbeef->pl_find_meta (self.track , " artist" );
65- if (str != NULL ) {
66- artist = @(str);
67- }
68- str = deadbeef->pl_find_meta (self.track , " title" );
69- if (str != NULL ) {
70- title = @(str);
71- }
64+ ddb_playItem_t *track = self.track ;
65+ deadbeef->pl_item_ref (track);
7266
73- deadbeef->pl_unlock ();
67+ deadbeef->pl_lock ();
68+
69+ const char *str = deadbeef->pl_find_meta (self.track , " artist" );
70+ if (str != NULL ) {
71+ artist = @(str);
72+ }
73+ str = deadbeef->pl_find_meta (self.track , " title" );
74+ if (str != NULL ) {
75+ title = @(str);
76+ }
77+
78+ deadbeef->pl_unlock ();
79+
80+ if (artist != nil && title != nil ) {
81+ NSURL *url = [[[NSURL URLWithString: @" https://api.lyrics.ovh/v1" ] URLByAppendingPathComponent: artist] URLByAppendingPathComponent: title];
82+ NSURLRequest *request = [NSURLRequest requestWithURL: url];
83+
84+ weakify (self);
85+ NSURLSessionDataTask *task = [NSURLSession .sharedSession dataTaskWithRequest: request completionHandler: ^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
86+ strongify (self);
87+
88+ NSString *lyrics = [self parseLyricsFromData: data response: response];
89+
90+ dispatch_async (dispatch_get_main_queue (), ^{
91+ [self updateWithLyrics: lyrics track: track];
92+ });
93+ }];
94+ [task resume ];
95+ }
96+ }
97+
98+ - (NSString *)currentArtist {
99+ NSString *result;
100+
101+ if (self.track == nil ) {
102+ return nil ;
103+ }
104+
105+ deadbeef->pl_lock ();
106+
107+ const char *str = deadbeef->pl_find_meta (self.track , " artist" );
108+ if (str != NULL ) {
109+ result = @(str);
110+ }
111+
112+ deadbeef->pl_unlock ();
113+ return result;
114+ }
115+
116+ - (NSString *)currentTitle {
117+ if (self.track == nil ) {
118+ return nil ;
119+ }
120+
121+ NSString *result;
122+ deadbeef->pl_lock ();
123+
124+ const char *str = deadbeef->pl_find_meta (self.track , " title" );
125+ if (str != NULL ) {
126+ result = @(str);
127+ }
128+
129+ deadbeef->pl_unlock ();
130+ return result;
131+ }
132+
133+ - (NSString *)currentKey {
134+ NSString *artist = [self currentArtist ];
135+ NSString *title = [self currentTitle ];
136+
137+ return [NSString stringWithFormat: @" artist:%@ ;title:%@ " , artist ?: @" null" , title ?: @" null" ];
138+ }
139+
140+ - (void )updateWithLyrics : (NSString * _Nullable)lyrics track : (ddb_playItem_t * _Nullable)track {
141+ if (track != self.track ) {
142+ deadbeef->pl_item_unref (track);
143+ return ;
74144 }
75145
146+ if (track != NULL ) {
147+ deadbeef->pl_item_unref (track);
148+ track = NULL ;
149+ }
150+
151+ if (lyrics == nil ) {
152+ lyrics = lyricsNotAvailableString;
153+ }
154+
155+ NSString *key = [self currentKey ];
156+ [self .lyricsCache setObject: lyrics forKey: key];
157+
158+ NSString *artist = [self currentArtist ];
159+ NSString *title = [self currentTitle ];
160+
76161 NSMutableParagraphStyle *paragraphStyle = [NSParagraphStyle .defaultParagraphStyle mutableCopy ];
77162 paragraphStyle.alignment = NSTextAlignmentCenter;
78163
@@ -86,7 +171,6 @@ - (void)update {
86171 NSFontAttributeName : headingFont,
87172 NSParagraphStyleAttributeName : paragraphStyle
88173 }];
89-
90174 }
91175
92176 NSAttributedString *contentAttributedString = [[NSAttributedString alloc ] initWithString: lyrics attributes: @{
@@ -106,6 +190,57 @@ - (void)update {
106190 self.textView .selectedRange = NSMakeRange (0 , 0 );
107191}
108192
193+ - (void )update {
194+ if (self.track == NULL ) {
195+ return [self updateWithLyrics: nil track: nil ];
196+ }
197+
198+ NSString *key = [self currentKey ];
199+ NSString *cachedLyrics = [self .lyricsCache objectForKey: key];
200+ if (cachedLyrics != nil ) {
201+ if (self.track != nil ) {
202+ deadbeef->pl_item_ref (self.track );
203+ }
204+ return [self updateWithLyrics: cachedLyrics track: self .track];
205+ }
206+
207+ deadbeef->pl_lock ();
208+
209+ // A bit of a hack since deadbeef stores multiline values as 0-separated lines.
210+ DB_metaInfo_t *meta = deadbeef->pl_meta_for_key (self.track , " lyrics" );
211+ if (meta == NULL ) {
212+ deadbeef->pl_unlock ();
213+ [self fetchFromLyricsOvh ];
214+ return ;
215+ }
216+
217+ // get lyrics from tags
218+ size_t buffer_size = 100000 ;
219+ char *lyrics_buffer = malloc (100000 );
220+ *lyrics_buffer = 0 ;
221+
222+ size_t value_size = meta->valuesize ;
223+ if (value_size > buffer_size - 1 ) {
224+ value_size = buffer_size - 1 ;
225+ }
226+ memcpy (lyrics_buffer, meta->value , value_size);
227+ char *p = lyrics_buffer;
228+ for (size_t i = 0 ; i <= value_size; i++, p++) {
229+ if (*p == 0 ) {
230+ *p = ' \n ' ;
231+ }
232+ }
233+ lyrics_buffer[value_size] = 0 ;
234+ NSString *lyrics = @(lyrics_buffer);
235+ free (lyrics_buffer);
236+ lyrics_buffer = NULL ;
237+
238+ deadbeef->pl_unlock ();
239+
240+ deadbeef->pl_item_ref (self.track );
241+ [self updateWithLyrics: lyrics track: self .track];
242+ }
243+
109244- (void )setTrack : (ddb_playItem_t *)track {
110245 if (_track != NULL ) {
111246 deadbeef->pl_item_unref (_track);
0 commit comments