Code

the updated sources
[ncmpc.git] / src / screen_lyrics.c
1 /* 
2  * $Id: screen_lyrics.c 3355 2006-09-1 17:44:04Z tradiaz $
3  *      
4  * (c) 2006 by Kalle Wallin <kaw@linux.se>
5  * Tue Aug  1 23:17:38 2006
6  * lyrics enhancement written by Andreas Obergrusberger <tradiaz@yahoo.de> 
7  * using www.leoslyrics.com XML API
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  *
22  */
24 #include <stdlib.h>
25 #include <string.h>
26 #include <glib.h>
27 #include <ncurses.h>
28 #include <expat.h>
29 #include <unistd.h>
31 #include "config.h"
32 #ifndef DISABLE_LYRICS_SCREEN
33 #include "ncmpc.h"
34 #include "options.h"
35 #include "mpdclient.h"
36 #include "command.h"
37 #include "screen.h"
38 #include "screen_utils.h"
39 #include "easy_download.h"
41 #define LEOSLYRICS_SEARCH_URL "http://api.leoslyrics.com/api_search.php?auth=QuodLibet&artist=%s&songtitle=%s"
42  
43 #define LEOSLYRICS_CONTENT_URL "http://api.leoslyrics.com/api_lyrics.php?auth=QuodLibet&hid=%s"
45 #define CREDITS "Lyrics provided by www.LeosLyrics.com"
46 typedef struct _formed_text
47 {
48         GString *text;
49         GArray *lines;
50         int val;
51 } formed_text;
54 XML_Parser parser, contentp;
55 static int lyrics_text_rows = -1;
56 static list_window_t *lw = NULL;
57 guint8 result;
58 char *hid;
59 GTimer *dltime;
60 short int lock;
61 //GString *lyr_text;
62 //char *lyr_text;       
63 //GArray *textline;
64 formed_text lyr_text;
65 /* result is a bitset in which the succes when searching 4 lyrics is logged
66 countend by position - backwards
67 0: lyrics in database
68 1: proper access  to the lyrics provider
69 2: lyrics found
70 3: exact match
71 4: lyrics downloaded
72 5: lyrics saved
73 wasting 3 bits doesn't mean being a fat memory hog like kde.... does it?
74 */
75 static void lyrics_paint(screen_t *screen, mpdclient_t *c);
77 void get_text_line(formed_text *text, int num, char *dest, int len)
78 {
79         memset(dest, '\0', len*sizeof(char));
80     if(num >= text->lines->len-1) return;       
81         int linelen;
82         if(num == 0)
83         {
84                 linelen = g_array_index(text->lines, int, num);
85                 memcpy(dest, text->text->str, linelen*sizeof(char));    
86         }
87         else if(num == 1)
88         { //dont ask me why, but this is needed....
89                 linelen = g_array_index(text->lines, int, num)
90                         - g_array_index(text->lines, int, num-1);
91                 memcpy(dest, &text->text->str[g_array_index(text->lines, int, num-1)],
92                 linelen*sizeof(char));  
93         }       
94         else
95         {
96                 linelen = g_array_index(text->lines, int, num+1)
97                         - g_array_index(text->lines, int, num);
98                 memcpy(dest, &text->text->str[g_array_index(text->lines, int, num)],
99                 linelen*sizeof(char));  
100         }
101         dest[linelen] = '\n';
102         dest[linelen+1] = '\0';
104         
105 void add_text_line(formed_text *dest, const char *src, int len)
107         // need this because g_array_append_val doesnt work with literals
108         // and expat sends "\n" as an extra line everytime
109         if(len == 0)
110         {
111                 dest->val = strlen(src);
112                 if(dest->lines->len > 0) dest->val += g_array_index(dest->lines, int,
113                                                                         dest->lines->len-1);
114                 g_string_append(dest->text, src);
115                 g_array_append_val(dest->lines, dest->val);
116                 return;
117         }
118         if(len > 1 || dest->val == 0) 
119         {
120                 dest->val = len;        
121                 if(dest->lines->len > 0) dest->val += g_array_index(dest->lines, int,
122                                                                         dest->lines->len-1);
123         }
124         else if (len == 1 && dest->val != 0) dest->val = 0;
125                                 
126         if(dest->val > 0)
127         { 
128                 g_string_append_len(dest->text, src, len);
129                 g_array_append_val(dest->lines, dest->val);
130         }
133 void formed_text_init(formed_text *text)
135         if(text->text != NULL) g_string_free(text->text, TRUE); 
136         text->text = g_string_new("");
137         
138         if(text->lines != NULL) g_array_free(text->lines, TRUE);
139         text->lines = g_array_new(FALSE, TRUE, 4);
140         
141         text->val = 0;
143 /*
144 char *check_lyr_hd(char *artist, char *title, int how)
145 { //checking whether for lyrics file existence and proper access
146         static char path[1024];
147         snprintf(path, 1024, "%s/.lyrics/%s/%s.lyric", 
148                         getenv("HOME"), artist, title);
149     
150     if(g_access(path, how) != 0) return NULL;
151         return path;
152 }               
154 int get_lyr_hd(char *artist, char *title)
156         char *path = check_lyr_hd(artist, title, R_OK);
157         if(path == NULL) return -1;
158         
159         FILE *lyr_file; 
160         lyr_file = fopen(path, "r");
161         if(lyr_file == NULL) return -1;
162         
163         char *buf = NULL;
164         char **line = &buf;
165         size_t n = 0;
166         
167         while(1)
168         {
169          n = getline(line, &n, lyr_file); 
170          if( n < 1 || *line == NULL || feof(lyr_file) != 0 ) return 0;
171          add_text_line(&lyr_text, *line, n+1);
172          free(*line);
173          *line = NULL; n = 0;
174         }
175         
176         return 0;
177 }       
178     
179 int store_lyr_hd()
181         char artist[512];
182         char title[512];
183         static char path[1024];
184         FILE *lyr_file;
185         
186         get_text_line(&lyr_text, 0, artist, 512);
187         get_text_line(&lyr_text, 1, title, 512);
188         artist[strlen(artist)-2] = '\0';
189         title[strlen(title)-2] = '\0';
190         
191         snprintf(path, 1024, "%s/.lyrics/%s/%s.lyric", 
192                         getenv("HOME"), artist, title);
193         
194         if (check_lyr_hd(artist, title, F_OK) == 0) lyr_file = fopen(path, "w");
195         else lyr_file = fopen(path, "xw");
196         if(lyr_file == NULL) return -1;
197         
198         int i;
199         char line_buf[1024];
200         
201         for(i = 2; i <= lyr_text.text->len; i++)
202         {
203                 get_text_line(&lyr_text, i, line_buf, 0);
204                 fputs(line_buf, lyr_file);
205         }
206         fclose(lyr_file);
207         return 0;
209                                 
210 */      
211 void check_repaint()
213         if(screen_get_id("lyrics") == get_cur_mode_id())lyrics_paint(NULL, NULL);
216 int check_dl_progress(void *clientp, double dltotal, double dlnow,
217                         double ultotal, double ulnow)
219         if(g_timer_elapsed(dltime, NULL) >= options.lyrics_timeout)
220         {       
221                 formed_text_init(&lyr_text);
222                 return -1;
223         }
224         return 0;
225 }       
229 static void check_content(void *data, const char *name, const char **atts)
231         if(strstr(name, "text") != NULL)
232         {
234                 result |= 16;
235         }
237         
239 static void check_search_response(void *data, const char *name,
240                  const char **atts)
242         if(strstr(name, "response") != NULL)
243         {
244         result |=2;
245         return;
246         }  
247         
248         if(result & 4)
249         {
250                 if(strstr(name, "result") != NULL)
251                 {
252                         if(strstr(atts[2], "hid") != NULL)
253                         {
254                                 hid = atts[3];
255                         }
256         
257                         if(strstr(atts[2], "exactMatch") != NULL)
258                         {
259                                 result |= 8;
260                         }                       
261                 }
262         }
263                         
266 static void end_tag(void *data, const char *name)
268   //hmmmmmm             
271   static void check_search_success(void *userData, const XML_Char *s, int len)
272     {
273         if(result & 2)  //lets first check whether we're right
274         {               //we don't really want to search in the wrong string
275                 if(strstr((char*) s, "SUCCESS"))
276                 {
277                 result |=4;
278                 }
279         }       
280     }
282 static void fetch_text(void *userData, const XML_Char *s, int len) 
284         if(result & 16)
285         {
286                 add_text_line(&lyr_text, s, len); 
287         }
290 gpointer get_lyr(void *c)
292         mpd_Status *status = ((mpdclient_t*)c)->status;
293         mpd_Song *cur = ((mpdclient_t*)c)->song; 
294         mpdclient_update((mpdclient_t*)c);
295         
296         if(!(IS_PAUSED(status->state)||IS_PLAYING(status->state)))
297         {
298                 formed_text_init(&lyr_text);                    
299                 return NULL;
300         }
301         
302         char url_avail[256];
303         char url_hid[256];
304         char artist[MAX_SONGNAME_LENGTH];
305         char title[MAX_SONGNAME_LENGTH];
306         lock = 2;
307         result = 0;
308         
309         if(dltime == NULL) dltime = g_timer_new();
310         
311         strfsong(artist, MAX_SONGNAME_LENGTH, "%artist%", cur);
312         strfsong(title, MAX_SONGNAME_LENGTH, "%title%", cur);
313         
314         formed_text_init(&lyr_text);
315         add_text_line(&lyr_text, artist, 0);
316         add_text_line(&lyr_text, title, 0);
317         
318         //if(get_lyr_hd(artist, title) == 0) return &lyr_text;
319         
320         //this replacess the whitespaces with '+'
321         g_strdelimit(artist, " ", '+');
322         g_strdelimit(title, " ", '+');
323         
324         //we insert the artist and the title into the url               
325         snprintf(url_avail, 512, LEOSLYRICS_SEARCH_URL, artist, title);
327         //download that xml!
328         easy_download_struct lyr_avail = {NULL, 0,-1};  
329         
330         g_timer_start(dltime);
331         if(!easy_download(url_avail, &lyr_avail, check_dl_progress)) return NULL;
332         g_timer_stop(dltime);
334         //we gotta parse that stuff with expat
335         parser = XML_ParserCreate(NULL);
336         XML_SetUserData(parser, NULL);
337         
338         int state;
340         XML_SetElementHandler(parser, check_search_response, end_tag);
341         XML_SetCharacterDataHandler(parser, check_search_success);
342         XML_Parse(parser, lyr_avail.data, strlen(lyr_avail.data), state);       
343         XML_ParserFree(parser); 
345         if(!(result & 4)) return NULL; //check whether lyrics found
346         easy_download_struct lyr_content = {NULL, 0,-1};  
347         snprintf(url_hid, 512, LEOSLYRICS_CONTENT_URL, hid);
348         
349         g_timer_continue(dltime);               
350         if(!(easy_download(url_hid, &lyr_content, check_dl_progress))) return NULL;
351         g_timer_stop(dltime);
352         
353         contentp = XML_ParserCreate(NULL);
354         XML_SetUserData(contentp, NULL);
355         XML_SetElementHandler(contentp, check_content, end_tag);        
356         XML_SetCharacterDataHandler(contentp, fetch_text);
357         XML_Parse(contentp, lyr_content.data, strlen(lyr_content.data), state);
358         XML_ParserFree(contentp);
359         
360         lw->start = 0;
361         check_repaint();
362         
363     lock = 1;
364         return &lyr_text;
365 }       
367 static char *
368 list_callback(int index, int *highlight, void *data)
370         static char buf[512];
371         int linelen;
373     //i think i'ts fine to write it into the 1st line...
374   if((index == lyr_text.lines->len && lyr_text.lines->len != 2)||
375           ((lyr_text.lines->len == 0 
376           ||lyr_text.lines->len == 2) && index == 0))
377   {
378     *highlight=3; 
379         return CREDITS;
380   }
381     
382   if(index < 2 && lyr_text.lines->len > 2) *highlight=3;
383   else if(index >=  lyr_text.lines->len || index == 2)
384   {
385           return "";
386   }
387   if(index >1) index--;
388   get_text_line(&lyr_text, index, buf, 512);
389   return buf;
390
393 static void
394 lyrics_init(WINDOW *w, int cols, int rows)
396   lw = list_window_init(w, cols, rows);
397   lw->flags = LW_HIDE_CURSOR;
398   //lyr_text.lines = g_array_new(FALSE, TRUE, 4);
399   formed_text_init(&lyr_text);
400   if (!g_thread_supported()) g_thread_init(NULL);
401   
404 static void
405 lyrics_resize(int cols, int rows)
407   lw->cols = cols;
408   lw->rows = rows;
411 static void
412 lyrics_exit(void)
414   list_window_free(lw);
418 static char *
419 lyrics_title(char *str, size_t size)
421         if(lyr_text.lines->len == 2){ 
422         if(lock == 1 && !(result & 2)) return _("Lyrics  [No connection]");
423     if(lock == 1 && !(result & 4)) return _("Lyrics  [Not found]");             
424         if(lock == 2) return _("Lyrics  [retrieving]");
425         }
426         /*if(lyr_text.lines->len > 2) 
427         {
428                 static char buf[512];
429                 char artist[512];
430                 char title[512];
431                 get_text_line(&lyr_text, 0, artist, 512);
432                 get_text_line(&lyr_text, 1, artist, 512);
433                 snprintf(buf, 512, "Lyrics  %s - %s", artist, title);
434                 return buf;
435         }*/
436         return "Lyrics";
439 static void 
440 lyrics_paint(screen_t *screen, mpdclient_t *c)
442   lw->clear = 1;
443   list_window_paint(lw, list_callback, NULL);
444   wrefresh(lw->w);
447 static void 
448 lyrics_update(screen_t *screen, mpdclient_t *c)
449 {  
450   if( lw->repaint )
451     {
452       list_window_paint(lw, list_callback, NULL);
453       wrefresh(lw->w);
454       lw->repaint = 0;
455     }
459 static int 
460 lyrics_cmd(screen_t *screen, mpdclient_t *c, command_t cmd)
462   lw->repaint=1;
463   switch(cmd)
464     {
465     case CMD_LIST_NEXT:
466       if( lw->start+lw->rows < lyr_text.lines->len+1 )
467         lw->start++;
468       return 1;
469     case CMD_LIST_PREVIOUS:
470       if( lw->start >0 )
471         lw->start--;
472       return 1;
473     case CMD_LIST_FIRST:
474       lw->start = 0;
475       return 1;
476     case CMD_LIST_LAST:
477       lw->start = lyrics_text_rows-lw->rows;
478       if( lw->start<0 )
479         lw->start = 0;
480       return 1;
481     case CMD_LIST_NEXT_PAGE:
482       lw->start = lw->start + lw->rows-1;
483       if( lw->start+lw->rows >= lyr_text.lines->len+1 )
484         lw->start = lyr_text.lines->len-lw->rows+1;
485       if( lw->start<0 )
486         lw->start = 0;
487        return 1;
488     case CMD_LIST_PREVIOUS_PAGE:
489       lw->start = lw->start - lw->rows;
490       if( lw->start<0 )
491         lw->start = 0;
492       return 1;
493         case CMD_SELECT:
494           g_thread_create(get_lyr, c, FALSE, NULL);     
495           return 1;     
496         default:
497       break;
498     }
500   lw->selected = lw->start+lw->rows;
501   if( screen_find(screen, c, 
502                   lw,  lyrics_text_rows,
503                   cmd, list_callback, NULL) )
504     {
505       /* center the row */
506       lw->start = lw->selected-(lw->rows/2);
507       if( lw->start+lw->rows > lyrics_text_rows )
508         lw->start = lyrics_text_rows-lw->rows;
509       if( lw->start<0 )
510         lw->start=0;
511       return 1;
512     }
514   return 0;
517 static list_window_t *
518 lyrics_lw(void)
520   return lw;
523 screen_functions_t *
524 get_screen_lyrics(void)
526   static screen_functions_t functions;
528   memset(&functions, 0, sizeof(screen_functions_t));
529   functions.init   = lyrics_init;
530   functions.exit   = lyrics_exit;
531   functions.open   = NULL;
532   functions.close  = NULL;
533   functions.resize = lyrics_resize;
534   functions.paint  = lyrics_paint;
535   functions.update = lyrics_update;
536   functions.cmd    = lyrics_cmd;
537   functions.get_lw = lyrics_lw;
538   functions.get_title = lyrics_title;
540   return &functions;
542 #endif /* ENABLE_LYRICS_SCREEN */