Skip to content

Commit 504bb37

Browse files
cocoaui: fetch lyrics from lyrics.ovh
1 parent f06bf12 commit 504bb37

File tree

1 file changed

+173
-38
lines changed

1 file changed

+173
-38
lines changed

plugins/cocoaui/DesignMode/Widgets/Lyrics/LyricsViewController.m

Lines changed: 173 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,16 @@
77
//
88

99
#import "LyricsViewController.h"
10+
#import "Weakify.h"
1011

1112
extern 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

Comments
 (0)