From e3bb0420c16fcbed46a1decf7f0a8bb1bee2a527 Mon Sep 17 00:00:00 2001 From: "Carsten Haitzler (Rasterman)" Date: Tue, 5 Aug 2014 18:57:26 +0900 Subject: [PATCH] rage - add album art fetching for audio only stuff. if the track is audio only, it'll look in a cache of albumart, if not, request the big google in the sky for an image with some search terms guessed from id3/metadata/filename and then use that. tak'es the first thing found. not very smart, but pretty damned good most of the time. --- data/themes/default.edc | 81 +++++++++++- src/bin/Makefile.am | 3 +- src/bin/albumart.c | 282 ++++++++++++++++++++++++++++++++++++++++ src/bin/albumart.h | 6 + src/bin/video.c | 24 +++- src/bin/video.h | 3 + src/bin/videothumb.c | 20 ++- src/bin/win.c | 46 +++++++ src/bin/win.h | 3 +- 9 files changed, 455 insertions(+), 13 deletions(-) create mode 100644 src/bin/albumart.c create mode 100644 src/bin/albumart.h diff --git a/data/themes/default.edc b/data/themes/default.edc index cce94ef..070af3a 100644 --- a/data/themes/default.edc +++ b/data/themes/default.edc @@ -316,11 +316,86 @@ collections { target: "novid_clip2"; } + part { name: "art_clip"; type: RECT; mouse_events: 0; + description { state: "default" 0.0; + max: 0 0; + visible: 0; + color: 255 255 255 0; + rel1.to: "base"; + rel2.to: "base"; + fixed: 1 1; + aspect: 1.0 1.0; aspect_preference: BOTH; + } + description { state: "visible" 0.0; + inherit: "default" 0.0; + max: 9999 9999; + visible: 1; + color: 255 255 255 255; + } + } + part { name: "art_clip2"; type: RECT; mouse_events: 0; + description { state: "default" 0.0; + visible: 0; + color: 255 255 255 0; + rel1.to: "base"; + rel2.to: "base"; + fixed: 1 1; + aspect: 1.0 1.0; aspect_preference: BOTH; + } + description { state: "visible" 0.0; + inherit: "default" 0.0; + visible: 1; + color: 255 255 255 255; + } + } + part { name: "artshadow"; mouse_events: 0; + clip_to: "art_clip2"; + description { state: "default" 0.0; + fixed: 1 1; + rel1.to: "art_clip"; + rel2.to: "art_clip"; + image.normal: "win_shadow.png"; + image.border: 14 14 14 14; + image.middle: 0; + rel1.offset: -7 -3; + rel2.offset: 6 11; + fill.smooth: 0; + } + } + part { name: "rage.art"; type: SWALLOW; + clip_to: "art_clip"; + description { state: "default" 0.0; + rel1.to: "art_clip"; + rel2.to: "art_clip"; + } + } + program { + signal: "action,newvid"; source: "rage"; + action: STATE_SET "default" 0.0; + transition: ACCELERATE 0.2; + target: "art_clip"; + target: "art_clip2"; + } + program { + signal: "state,noart"; source: "rage"; + action: STATE_SET "default" 0.0; + transition: ACCELERATE 1.0; + target: "art_clip"; + target: "art_clip2"; + } + program { + signal: "state,art"; source: "rage"; + action: STATE_SET "visible" 0.0; + transition: DECELERATE 1.0; + target: "art_clip"; + target: "art_clip2"; + } + part { name: "rage.content"; type: SWALLOW; description { state: "default" 0.0; } } - + part { name: "rage.list"; type: SWALLOW; description { state: "default" 0.0; align: 0.0 0.5; @@ -1557,13 +1632,13 @@ collections { } program { name: "posshow"; action: STATE_SET "visible" 0.0; - transition: SINUSOIDAL 0.3 CURRENT; + transition: SINUSOIDAL 0.3; target: "poscover"; target: "posclip"; } program { name: "poshide"; action: STATE_SET "default" 0.0; - transition: SINUSOIDAL 1.0 CURRENT; + transition: SINUSOIDAL 1.0; target: "poscover"; target: "posclip"; } diff --git a/src/bin/Makefile.am b/src/bin/Makefile.am index bc92fe4..358121a 100644 --- a/src/bin/Makefile.am +++ b/src/bin/Makefile.am @@ -21,7 +21,8 @@ win.c win.h \ winlist.c winlist.h \ config.c config.h \ sha1.c sha1.h \ -videothumb.c videothumb.h +videothumb.c videothumb.h \ +albumart.c albumart.h internal_bindir = $(libdir)/rage/utils internal_bin_PROGRAMS = rage_thumb diff --git a/src/bin/albumart.c b/src/bin/albumart.c new file mode 100644 index 0000000..346bfa6 --- /dev/null +++ b/src/bin/albumart.c @@ -0,0 +1,282 @@ +#include +#include "video.h" +#include "win.h" +#include "rage_config.h" +#include "config.h" +#include "albumart.h" +#include "sha1.h" + +#define Q_START "http://www.google.com/search?as_st=y&tbm=isch&hl=en&as_q=" +#define Q_END "&as_epq=&as_oq=&as_eq=&cr=&as_sitesearch=&safe=images&tbs=iar:s,ift:jpg" + +static Ecore_Con_Url *fetch = NULL; +static Ecore_Event_Handler *handle_data = NULL; +static Ecore_Event_Handler *handle_complete = NULL; +static Eina_Strbuf *sb_result = NULL; +static Eina_Bool fetch_image = EINA_FALSE; +static char *fetchfile = NULL; +static FILE *fout = NULL; +static Evas_Object *fetchwin = NULL; + +static char * +_thumbpath(const char *file) +{ + char buf_base[PATH_MAX]; + char buf_file[PATH_MAX]; + unsigned char sum[20]; + + if (!sha1((unsigned char *)file, strlen(file), sum)) return NULL; + snprintf(buf_base, sizeof(buf_base), "%s/rage/albumart/%02x", + efreet_cache_home_get(), sum[0]); + if (!ecore_file_mkpath(buf_base)) return NULL; + snprintf(buf_file, sizeof(buf_base), + "%s/%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x" + "%02x%02x%02x%02x%02x%02x%02x%02x.jpg", + buf_base, + sum[1], sum[2], sum[3], + sum[4], sum[5], sum[6], sum[7], + sum[8], sum[9], sum[10], sum[11], + sum[12], sum[13], sum[14], sum[15], + sum[16], sum[17], sum[18], sum[19]); + return strdup(buf_file); +} + +static Ecore_Con_Url * +_fetch(Eina_Strbuf *sb) +{ + Ecore_Con_Url *f; + const char *qs; + + qs = eina_strbuf_string_get(sb); + if (!qs) return NULL; + f = ecore_con_url_new(qs); + if (!f) return NULL; + ecore_con_url_additional_header_add + (f, "user-agent", + "Mozilla/5.0 (Windows NT 5.1; rv:31.0) Gecko/20100101 Firefox/31.0"); + ecore_con_url_get(f); + return f; +} + +static Eina_Bool +_cb_http_data(void *data EINA_UNUSED, int type EINA_UNUSED, void *event) +{ + Ecore_Con_Event_Url_Data *ev = event; + + if (ev->url_con != fetch) return EINA_TRUE; + if (fetch_image) fwrite(ev->data, ev->size, 1, fout); + else if (sb_result) + eina_strbuf_append_length(sb_result, (char *)ev->data, (size_t)ev->size); + return EINA_FALSE; +} + +static Eina_Bool +_cb_http_complete(void *data EINA_UNUSED, int type EINA_UNUSED, void *event) +{ + Ecore_Con_Event_Url_Complete *ev = event; + Eina_Bool ok = EINA_FALSE; + + if (ev->url_con != fetch) return EINA_TRUE; + if (fetch_image) + { + char *path; + + fetch_image = EINA_FALSE; + fclose(fout); + fout = NULL; + path = _thumbpath(fetchfile); + if (path) + { + win_art(fetchwin, path); + free(path); + } + } + else + { + if (sb_result) + { + const char *res = eina_strbuf_string_get(sb_result); + if (res) + { + const char *p, *pe; + Eina_Strbuf *sb; + + sb = eina_strbuf_new(); + p = strstr(res, "= 'a') && (*s <= 'z')) || + ((*s >= 'A') && (*s <= 'Z')) || + ((*s >= '0') && (*s <= '9'))) + { + if (!word) + { + if (hadword) + { + eina_strbuf_append_char(sb, '+'); + word = EINA_FALSE; + } + } + eina_strbuf_append_char(sb, *s); + word = EINA_TRUE; + hadword = EINA_TRUE; + } + else word = EINA_FALSE; + if (*s == '.') break; + } + return hadword; +} + +void +albumart_find(Evas_Object *win, Evas_Object *vid) +{ + const char *file, *album, *artist, *title; + Eina_Strbuf *sb; + char *realfile, *path; + + if (fetchfile) + { + free(fetchfile); + fetchfile = NULL; + } + if (fetch) + { + ecore_con_url_free(fetch); + fetch = NULL; + } + if (fout) + { + fclose(fout); + fout = NULL; + } + if (sb_result) + { + eina_strbuf_free(sb_result); + sb_result = NULL; + } + fetch_image = EINA_FALSE; + if (!vid) return; + + if (!handle_data) + handle_data = ecore_event_handler_add(ECORE_CON_EVENT_URL_DATA, + _cb_http_data, NULL); + if (!handle_complete) + handle_complete = ecore_event_handler_add(ECORE_CON_EVENT_URL_COMPLETE, + _cb_http_complete, NULL); + + file = video_file_get(vid); + if (!file) return; + realfile = ecore_file_realpath(file); + if (!realfile) fetchfile = strdup(file); + else fetchfile = realfile; + + path = _thumbpath(fetchfile); + if (path) + { + if (ecore_file_exists(path)) + { + win_art(win, path); + free(path); + free(fetchfile); + fetchfile = NULL; + return; + } + else free(path); + } + fetchwin = win; + + sb = eina_strbuf_new(); + eina_strbuf_append(sb, Q_START); + + title = video_meta_title_get(vid); + artist = video_meta_artist_get(vid); + album = video_meta_album_get(vid); + if ((title) || (album) || (artist)) + { + Eina_Bool had = EINA_FALSE; + + if (artist) had = _search_append(sb, artist, had); + if (album) had = _search_append(sb, album, had); + if (title) had = _search_append(sb, title, had); + } + else + _search_append(sb, ecore_file_file_get(fetchfile), EINA_FALSE); + + eina_strbuf_append(sb, Q_END); + + if (sb_result) eina_strbuf_free(sb_result); + sb_result = eina_strbuf_new(); + + fetch = _fetch(sb); + + eina_strbuf_free(sb); +} diff --git a/src/bin/albumart.h b/src/bin/albumart.h new file mode 100644 index 0000000..0130c69 --- /dev/null +++ b/src/bin/albumart.h @@ -0,0 +1,6 @@ +#ifndef _ALBUMART_H__ +#define _ALBUMART_H__ 1 + +void albumart_find(Evas_Object *win, Evas_Object *vid); + +#endif diff --git a/src/bin/video.c b/src/bin/video.c index 3ac4f0f..6282b92 100644 --- a/src/bin/video.c +++ b/src/bin/video.c @@ -1,5 +1,4 @@ #include -#include #include "video.h" #include "rage_config.h" #include "config.h" @@ -825,6 +824,29 @@ video_lowquality_get(Evas_Object *obj) return sd->lowqual; } +const char * +video_meta_title_get(Evas_Object *obj) +{ + Video *sd = evas_object_smart_data_get(obj); + if (!sd) return NULL; + return emotion_object_meta_info_get(sd->o_vid, EMOTION_META_INFO_TRACK_TITLE); +} + +const char * +video_meta_artist_get(Evas_Object *obj) +{ + Video *sd = evas_object_smart_data_get(obj); + if (!sd) return NULL; + return emotion_object_meta_info_get(sd->o_vid, EMOTION_META_INFO_TRACK_ARTIST); +} + +const char * +video_meta_album_get(Evas_Object *obj) +{ + Video *sd = evas_object_smart_data_get(obj); + if (!sd) return NULL; + return emotion_object_meta_info_get(sd->o_vid, EMOTION_META_INFO_TRACK_ALBUM); +} // emotion_object_seekable_get // emotion_object_play_speed_set diff --git a/src/bin/video.h b/src/bin/video.h index 8257520..bd5b294 100644 --- a/src/bin/video.h +++ b/src/bin/video.h @@ -46,5 +46,8 @@ int video_spu_button_get(Evas_Object *obj); void video_event_send(Evas_Object *obj, Emotion_Event ev); void video_lowquality_set(Evas_Object *obj, Eina_Bool lowq); Eina_Bool video_lowquality_get(Evas_Object *obj); +const char *video_meta_title_get(Evas_Object *obj); +const char *video_meta_artist_get(Evas_Object *obj); +const char *video_meta_album_get(Evas_Object *obj); #endif diff --git a/src/bin/videothumb.c b/src/bin/videothumb.c index 8d41f5b..2d33a75 100644 --- a/src/bin/videothumb.c +++ b/src/bin/videothumb.c @@ -12,6 +12,7 @@ struct _Videothumb Ecore_Event_Handler *exe_handler; const char *file; const char *realfile; + char *realpath; double pos; unsigned int realpos; int iw, ih; @@ -87,7 +88,7 @@ _thumb_match_update(Evas_Object *obj, const char *file) Videothumb *sd = evas_object_smart_data_get(obj); if (!sd) return; - if (!strcmp(sd->file, file)) _thumb_update(obj); + if (!strcmp(sd->realpath, file)) _thumb_update(obj); } static Eina_Bool @@ -103,8 +104,11 @@ _cb_thumb_exe(void *data, int type EINA_UNUSED, void *event) Eina_List *l; Evas_Object *o; - _busy_del(sd->file); - EINA_LIST_FOREACH(vidthumbs, l, o) _thumb_match_update(o, sd->file); + _busy_del(sd->realpath); + EINA_LIST_FOREACH(vidthumbs, l, o) + { + _thumb_match_update(o, sd->realpath); + } if ((sd->iw <= 0) || (sd->ih <= 0)) { if (sd->exe_handler) @@ -144,7 +148,7 @@ _videothumb_image_load(Evas_Object *obj) if (!sd->file) return; sd->o_img = evas_object_image_filled_add(evas_object_evas_get(obj)); evas_object_smart_member_add(sd->o_img, obj); - if (!sha1((unsigned char *)sd->file, strlen(sd->file), sum)) return; + if (!sha1((unsigned char *)sd->realpath, strlen(sd->realpath), sum)) return; snprintf(buf_base, sizeof(buf_base), "%s/rage/thumb/%02x", efreet_cache_home_get(), sum[0]); snprintf(buf_file, sizeof(buf_base), @@ -170,7 +174,7 @@ _videothumb_image_load(Evas_Object *obj) Eina_Bool ok = EINA_FALSE; struct stat st1, st2; - if (stat(sd->file, &st1) == 0) + if (stat(sd->realpath, &st1) == 0) { if (stat(sd->realfile, &st2) == 0) { @@ -183,14 +187,14 @@ _videothumb_image_load(Evas_Object *obj) return; } } - if (!_busy_add(sd->file)) return; + if (!_busy_add(sd->realpath)) return; ecore_exe_run_priority_set(10); if (sd->thumb_exe) { ecore_exe_free(sd->thumb_exe); sd->thumb_exe = NULL; } - s = ecore_file_escape_name(sd->file); + s = ecore_file_escape_name(sd->realpath); if (s) { libdir = elm_app_lib_dir_get(); @@ -283,6 +287,7 @@ _smart_del(Evas_Object *obj) vidthumbs = eina_list_remove(vidthumbs, obj); if (sd->file) eina_stringshare_del(sd->file); if (sd->realfile) eina_stringshare_del(sd->realfile); + if (sd->realpath) free(sd->realpath); if (sd->o_img) evas_object_del(sd->o_img); if (sd->thumb_exe) ecore_exe_free(sd->thumb_exe); if (sd->exe_handler) ecore_event_handler_del(sd->exe_handler); @@ -366,6 +371,7 @@ videothumb_file_set(Evas_Object *obj, const char *file, double pos) if ((sd->file == file) && (sd->pos == pos)) return; if (sd->file) eina_stringshare_del(sd->file); sd->file = eina_stringshare_add(file); + sd->realpath = ecore_file_realpath(sd->file); sd->pos = pos; _videothumb_eval(obj, EINA_TRUE); } diff --git a/src/bin/win.c b/src/bin/win.c index d356099..69eab61 100644 --- a/src/bin/win.c +++ b/src/bin/win.c @@ -8,6 +8,7 @@ #include "key.h" #include "controls.h" #include "gesture.h" +#include "albumart.h" static void _cb_fullscreen(void *data EINA_UNUSED, Evas_Object *obj, void *event EINA_UNUSED) @@ -363,6 +364,48 @@ win_title_update(Evas_Object *win) } } +void +win_art(Evas_Object *win, const char *path) +{ + Inf *inf = evas_object_data_get(win, "inf"); + + if (!path) + { + elm_layout_signal_emit(inf->lay, "state,noart", "rage"); + if (inf->artimg) + { + evas_object_del(inf->artimg); + inf->artimg = NULL; + } + } + else + { + int iw, ih; + + if (inf->artimg) + { + evas_object_del(inf->artimg); + inf->artimg = NULL; + } + inf->artimg = evas_object_image_filled_add(evas_object_evas_get(win)); + evas_object_image_file_set(inf->artimg, path, NULL); + evas_object_image_size_get(inf->artimg, &iw, &ih); + if ((iw > 0) && (ih > 0)) + { + evas_object_size_hint_aspect_set(inf->artimg, + EVAS_ASPECT_CONTROL_NEITHER, + iw, ih); + elm_object_part_content_set(inf->lay, "rage.art", inf->artimg); + elm_layout_signal_emit(inf->lay, "state,art", "rage"); + } + else + { + evas_object_del(inf->artimg); + inf->artimg = NULL; + } + } +} + void win_show(Evas_Object *win, int w, int h) { @@ -378,6 +421,9 @@ win_show(Evas_Object *win, int w, int h) } evas_object_show(win); } + if (!video_has_video_get(inf->vid)) albumart_find(win, inf->vid); + else albumart_find(win, NULL); + if (!video_has_video_get(inf->vid)) elm_layout_signal_emit(inf->lay, "state,novideo", "rage"); else diff --git a/src/bin/win.h b/src/bin/win.h index 6967f78..8c96245 100644 --- a/src/bin/win.h +++ b/src/bin/win.h @@ -5,7 +5,7 @@ typedef struct _Inf Inf; struct _Inf { - Evas_Object *vid, *lay, *event, *event2, *glayer, *vidthumb; + Evas_Object *vid, *lay, *event, *event2, *glayer, *vidthumb, *artimg; Eina_List *file_list, *file_cur; Ecore_Job *next_job; Ecore_Timer *show_timeout; @@ -42,6 +42,7 @@ Eina_Bool win_video_have_prev(Evas_Object *win); Evas_Object *win_add(void); void win_title_update(Evas_Object *win); void win_show(Evas_Object *win, int w, int h); +void win_art(Evas_Object *win, const char *path); void win_aspect_adjust(Evas_Object *win); void win_frame_decode(Evas_Object *win);