diff options
-rw-r--r-- | apps/SOURCES | 1 | ||||
-rw-r--r-- | apps/lang/english.lang | 8 | ||||
-rw-r--r-- | apps/main.c | 7 | ||||
-rw-r--r-- | apps/menus/playback_menu.c | 23 | ||||
-rw-r--r-- | apps/misc.c | 2 | ||||
-rw-r--r-- | apps/plugins/CATEGORIES | 1 | ||||
-rw-r--r-- | apps/plugins/SOURCES | 1 | ||||
-rw-r--r-- | apps/plugins/lastfm_scrobbler.c | 584 | ||||
-rw-r--r-- | apps/scrobbler.c | 287 | ||||
-rw-r--r-- | apps/scrobbler.h | 29 | ||||
-rw-r--r-- | apps/settings.h | 1 | ||||
-rw-r--r-- | apps/settings_list.c | 2 |
12 files changed, 591 insertions, 355 deletions
diff --git a/apps/SOURCES b/apps/SOURCES index f440104a19..444951bbcb 100644 --- a/apps/SOURCES +++ b/apps/SOURCES | |||
@@ -58,7 +58,6 @@ tree.c | |||
58 | tagtree.c | 58 | tagtree.c |
59 | #endif | 59 | #endif |
60 | filetree.c | 60 | filetree.c |
61 | scrobbler.c | ||
62 | #ifdef IPOD_ACCESSORY_PROTOCOL | 61 | #ifdef IPOD_ACCESSORY_PROTOCOL |
63 | iap/iap-core.c | 62 | iap/iap-core.c |
64 | iap/iap-lingo0.c | 63 | iap/iap-lingo0.c |
diff --git a/apps/lang/english.lang b/apps/lang/english.lang index c951028494..04bbf70a6f 100644 --- a/apps/lang/english.lang +++ b/apps/lang/english.lang | |||
@@ -1772,16 +1772,16 @@ | |||
1772 | </phrase> | 1772 | </phrase> |
1773 | <phrase> | 1773 | <phrase> |
1774 | id: LANG_AUDIOSCROBBLER | 1774 | id: LANG_AUDIOSCROBBLER |
1775 | desc: "Last.fm Log" in the playback menu | 1775 | desc: "Last.fm Logger" in Plugin/apps/scrobbler |
1776 | user: core | 1776 | user: core |
1777 | <source> | 1777 | <source> |
1778 | *: "Last.fm Log" | 1778 | *: "Last.fm Logger" |
1779 | </source> | 1779 | </source> |
1780 | <dest> | 1780 | <dest> |
1781 | *: "Last.fm Log" | 1781 | *: "Last.fm Logger" |
1782 | </dest> | 1782 | </dest> |
1783 | <voice> | 1783 | <voice> |
1784 | *: "Last.fm Log" | 1784 | *: "Last.fm Logger" |
1785 | </voice> | 1785 | </voice> |
1786 | </phrase> | 1786 | </phrase> |
1787 | <phrase> | 1787 | <phrase> |
diff --git a/apps/main.c b/apps/main.c index 2f3b246210..dff9dc5778 100644 --- a/apps/main.c +++ b/apps/main.c | |||
@@ -70,7 +70,6 @@ | |||
70 | #include "string.h" | 70 | #include "string.h" |
71 | #include "splash.h" | 71 | #include "splash.h" |
72 | #include "eeprom_settings.h" | 72 | #include "eeprom_settings.h" |
73 | #include "scrobbler.h" | ||
74 | #include "icon.h" | 73 | #include "icon.h" |
75 | #include "viewport.h" | 74 | #include "viewport.h" |
76 | #include "skin_engine/skin_engine.h" | 75 | #include "skin_engine/skin_engine.h" |
@@ -373,9 +372,6 @@ static void init(void) | |||
373 | playlist_init(); | 372 | playlist_init(); |
374 | shortcuts_init(); | 373 | shortcuts_init(); |
375 | 374 | ||
376 | if (global_settings.audioscrobbler) | ||
377 | scrobbler_init(); | ||
378 | |||
379 | audio_init(); | 375 | audio_init(); |
380 | talk_announce_voice_invalid(); /* notify user w/ voice prompt if voice file invalid */ | 376 | talk_announce_voice_invalid(); /* notify user w/ voice prompt if voice file invalid */ |
381 | settings_apply_skins(); | 377 | settings_apply_skins(); |
@@ -630,9 +626,6 @@ static void init(void) | |||
630 | tree_mem_init(); | 626 | tree_mem_init(); |
631 | filetype_init(); | 627 | filetype_init(); |
632 | 628 | ||
633 | if (global_settings.audioscrobbler) | ||
634 | scrobbler_init(); | ||
635 | |||
636 | shortcuts_init(); | 629 | shortcuts_init(); |
637 | 630 | ||
638 | CHART(">audio_init"); | 631 | CHART(">audio_init"); |
diff --git a/apps/menus/playback_menu.c b/apps/menus/playback_menu.c index fe319d6027..881a4b5a99 100644 --- a/apps/menus/playback_menu.c +++ b/apps/menus/playback_menu.c | |||
@@ -31,7 +31,6 @@ | |||
31 | #include "sound_menu.h" | 31 | #include "sound_menu.h" |
32 | #include "kernel.h" | 32 | #include "kernel.h" |
33 | #include "playlist.h" | 33 | #include "playlist.h" |
34 | #include "scrobbler.h" | ||
35 | #include "audio.h" | 34 | #include "audio.h" |
36 | #include "cuesheet.h" | 35 | #include "cuesheet.h" |
37 | #include "misc.h" | 36 | #include "misc.h" |
@@ -150,26 +149,6 @@ MENUITEM_SETTING(spdif_enable, &global_settings.spdif_enable, NULL); | |||
150 | MENUITEM_SETTING(next_folder, &global_settings.next_folder, NULL); | 149 | MENUITEM_SETTING(next_folder, &global_settings.next_folder, NULL); |
151 | MENUITEM_SETTING(constrain_next_folder, | 150 | MENUITEM_SETTING(constrain_next_folder, |
152 | &global_settings.constrain_next_folder, NULL); | 151 | &global_settings.constrain_next_folder, NULL); |
153 | static int audioscrobbler_callback(int action, | ||
154 | const struct menu_item_ex *this_item, | ||
155 | struct gui_synclist *this_list) | ||
156 | { | ||
157 | (void)this_item; | ||
158 | (void)this_list; | ||
159 | switch (action) | ||
160 | { | ||
161 | case ACTION_EXIT_MENUITEM: /* on exit */ | ||
162 | if (!scrobbler_is_enabled() && global_settings.audioscrobbler) | ||
163 | scrobbler_init(); | ||
164 | |||
165 | if(scrobbler_is_enabled() && !global_settings.audioscrobbler) | ||
166 | scrobbler_shutdown(false); | ||
167 | break; | ||
168 | } | ||
169 | return action; | ||
170 | } | ||
171 | MENUITEM_SETTING(audioscrobbler, &global_settings.audioscrobbler, audioscrobbler_callback); | ||
172 | |||
173 | 152 | ||
174 | static int cuesheet_callback(int action, | 153 | static int cuesheet_callback(int action, |
175 | const struct menu_item_ex *this_item, | 154 | const struct menu_item_ex *this_item, |
@@ -242,7 +221,7 @@ MAKE_MENU(playback_settings,ID2P(LANG_PLAYBACK),0, | |||
242 | #ifdef HAVE_SPDIF_POWER | 221 | #ifdef HAVE_SPDIF_POWER |
243 | &spdif_enable, | 222 | &spdif_enable, |
244 | #endif | 223 | #endif |
245 | &next_folder, &constrain_next_folder, &audioscrobbler, &cuesheet | 224 | &next_folder, &constrain_next_folder, &cuesheet |
246 | #ifdef HAVE_HEADPHONE_DETECTION | 225 | #ifdef HAVE_HEADPHONE_DETECTION |
247 | ,&unplug_menu | 226 | ,&unplug_menu |
248 | #endif | 227 | #endif |
diff --git a/apps/misc.c b/apps/misc.c index a4958a59ea..4d8c2e975a 100644 --- a/apps/misc.c +++ b/apps/misc.c | |||
@@ -58,7 +58,6 @@ | |||
58 | #include "font.h" | 58 | #include "font.h" |
59 | #include "splash.h" | 59 | #include "splash.h" |
60 | #include "tagcache.h" | 60 | #include "tagcache.h" |
61 | #include "scrobbler.h" | ||
62 | #include "sound.h" | 61 | #include "sound.h" |
63 | #include "playlist.h" | 62 | #include "playlist.h" |
64 | #include "yesno.h" | 63 | #include "yesno.h" |
@@ -365,7 +364,6 @@ static bool clean_shutdown(void (*callback)(void *), void *parameter) | |||
365 | #if defined(HAVE_RECORDING) | 364 | #if defined(HAVE_RECORDING) |
366 | audio_close_recording(); | 365 | audio_close_recording(); |
367 | #endif | 366 | #endif |
368 | scrobbler_shutdown(true); | ||
369 | 367 | ||
370 | system_flush(); | 368 | system_flush(); |
371 | #ifdef HAVE_EEPROM_SETTINGS | 369 | #ifdef HAVE_EEPROM_SETTINGS |
diff --git a/apps/plugins/CATEGORIES b/apps/plugins/CATEGORIES index 89aba0e32f..0ef3fe81a4 100644 --- a/apps/plugins/CATEGORIES +++ b/apps/plugins/CATEGORIES | |||
@@ -49,6 +49,7 @@ jpeg,viewers | |||
49 | keybox,apps | 49 | keybox,apps |
50 | keyremap,apps | 50 | keyremap,apps |
51 | lamp,apps | 51 | lamp,apps |
52 | lastfm_scrobbler,apps | ||
52 | logo,demos | 53 | logo,demos |
53 | lrcplayer,apps | 54 | lrcplayer,apps |
54 | lua,viewers | 55 | lua,viewers |
diff --git a/apps/plugins/SOURCES b/apps/plugins/SOURCES index d2f3c39d54..e3a10c9d15 100644 --- a/apps/plugins/SOURCES +++ b/apps/plugins/SOURCES | |||
@@ -12,6 +12,7 @@ dict.c | |||
12 | jackpot.c | 12 | jackpot.c |
13 | keybox.c | 13 | keybox.c |
14 | keyremap.c | 14 | keyremap.c |
15 | lastfm_scrobbler.c | ||
15 | logo.c | 16 | logo.c |
16 | lrcplayer.c | 17 | lrcplayer.c |
17 | mosaique.c | 18 | mosaique.c |
diff --git a/apps/plugins/lastfm_scrobbler.c b/apps/plugins/lastfm_scrobbler.c new file mode 100644 index 0000000000..db75427895 --- /dev/null +++ b/apps/plugins/lastfm_scrobbler.c | |||
@@ -0,0 +1,584 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2006-2008 Robert Keevil | ||
11 | * Converted to Plugin | ||
12 | * Copyright (C) 2022 William Wilgus | ||
13 | * | ||
14 | * This program is free software; you can redistribute it and/or | ||
15 | * modify it under the terms of the GNU General Public License | ||
16 | * as published by the Free Software Foundation; either version 2 | ||
17 | * of the License, or (at your option) any later version. | ||
18 | * | ||
19 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
20 | * KIND, either express or implied. | ||
21 | * | ||
22 | ****************************************************************************/ | ||
23 | /* Scrobbler Plugin | ||
24 | Audioscrobbler spec at: | ||
25 | http://www.audioscrobbler.net/wiki/Portable_Player_Logging | ||
26 | */ | ||
27 | |||
28 | #include "plugin.h" | ||
29 | |||
30 | #ifdef ROCKBOX_HAS_LOGF | ||
31 | #define logf rb->logf | ||
32 | #else | ||
33 | #define logf(...) do { } while(0) | ||
34 | #endif | ||
35 | |||
36 | |||
37 | /****************** constants ******************/ | ||
38 | #define EV_EXIT MAKE_SYS_EVENT(SYS_EVENT_CLS_PRIVATE, 0xFF) | ||
39 | #define EV_OTHINSTANCE MAKE_SYS_EVENT(SYS_EVENT_CLS_PRIVATE, 0xFE) | ||
40 | #define EV_STARTUP MAKE_SYS_EVENT(SYS_EVENT_CLS_PRIVATE, 0x01) | ||
41 | #define EV_TRACKCHANGE MAKE_SYS_EVENT(SYS_EVENT_CLS_PRIVATE, 0x02) | ||
42 | #define EV_TRACKFINISH MAKE_SYS_EVENT(SYS_EVENT_CLS_PRIVATE, 0x03) | ||
43 | |||
44 | #define SCROBBLER_VERSION "1.1" | ||
45 | |||
46 | /* increment this on any code change that effects output */ | ||
47 | #define SCROBBLER_REVISION " $Revision$" | ||
48 | |||
49 | #define SCROBBLER_MAX_CACHE 32 | ||
50 | /* longest entry I've had is 323, add a safety margin */ | ||
51 | #define SCROBBLER_CACHE_LEN 512 | ||
52 | |||
53 | #define ITEM_HDR "#ARTIST #ALBUM #TITLE #TRACKNUM #LENGTH #RATING #TIMESTAMMP #MUSICBRAINZ_TRACKID\n" | ||
54 | |||
55 | #if CONFIG_RTC | ||
56 | static time_t timestamp; | ||
57 | #define BASE_FILENAME HOME_DIR "/.scrobbler.log" | ||
58 | #define HDR_STR_TIMELESS | ||
59 | #define get_timestamp() ((long)timestamp) | ||
60 | #define record_timestamp() ((void)(timestamp = rb->mktime(rb->get_time()))) | ||
61 | #else /* !CONFIG_RTC */ | ||
62 | #define HDR_STR_TIMELESS " Timeless" | ||
63 | #define BASE_FILENAME ".scrobbler-timeless.log" | ||
64 | #define get_timestamp() (0l) | ||
65 | #define record_timestamp() ({}) | ||
66 | #endif /* CONFIG_RTC */ | ||
67 | |||
68 | #define THREAD_STACK_SIZE 4*DEFAULT_STACK_SIZE | ||
69 | |||
70 | #if (CONFIG_KEYPAD == IRIVER_H100_PAD) || \ | ||
71 | (CONFIG_KEYPAD == IRIVER_H300_PAD) | ||
72 | #define SCROBBLE_OFF BUTTON_OFF | ||
73 | #define SCROBBLE_OFF_TXT "STOP" | ||
74 | #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \ | ||
75 | (CONFIG_KEYPAD == IPOD_3G_PAD) || \ | ||
76 | (CONFIG_KEYPAD == IPOD_1G2G_PAD) | ||
77 | #define SCROBBLE_OFF BUTTON_MENU | ||
78 | #define SCROBBLE_OFF_TXT "MENU" | ||
79 | #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD || \ | ||
80 | CONFIG_KEYPAD == AGPTEK_ROCKER_PAD | ||
81 | #define SCROBBLE_OFF BUTTON_POWER | ||
82 | #define SCROBBLE_OFF_TXT "POWER" | ||
83 | #elif (CONFIG_KEYPAD == SANSA_E200_PAD) || \ | ||
84 | (CONFIG_KEYPAD == SANSA_C200_PAD) || \ | ||
85 | (CONFIG_KEYPAD == SANSA_CLIP_PAD) || \ | ||
86 | (CONFIG_KEYPAD == SANSA_M200_PAD) | ||
87 | #define SCROBBLE_OFF BUTTON_POWER | ||
88 | #define SCROBBLE_OFF_TXT "POWER" | ||
89 | #elif (CONFIG_KEYPAD == SANSA_FUZE_PAD) | ||
90 | #define SCROBBLE_OFF BUTTON_HOME | ||
91 | #define SCROBBLE_OFF_TXT "HOME" | ||
92 | #elif (CONFIG_KEYPAD == IRIVER_H10_PAD || \ | ||
93 | CONFIG_KEYPAD == CREATIVE_ZENXFI3_PAD || \ | ||
94 | CONFIG_KEYPAD == SONY_NWZ_PAD || \ | ||
95 | CONFIG_KEYPAD == XDUOO_X3_PAD || \ | ||
96 | CONFIG_KEYPAD == IHIFI_770_PAD || \ | ||
97 | CONFIG_KEYPAD == IHIFI_800_PAD || \ | ||
98 | CONFIG_KEYPAD == XDUOO_X3II_PAD || \ | ||
99 | CONFIG_KEYPAD == XDUOO_X20_PAD || \ | ||
100 | CONFIG_KEYPAD == FIIO_M3K_LINUX_PAD || \ | ||
101 | CONFIG_KEYPAD == EROSQ_PAD) | ||
102 | #define SCROBBLE_OFF BUTTON_POWER | ||
103 | #define SCROBBLE_OFF_TXT "POWER" | ||
104 | #elif CONFIG_KEYPAD == GIGABEAT_PAD | ||
105 | #define SCROBBLE_OFF BUTTON_POWER | ||
106 | #define SCROBBLE_OFF_TXT "POWER" | ||
107 | #elif CONFIG_KEYPAD == GIGABEAT_S_PAD \ | ||
108 | || CONFIG_KEYPAD == SAMSUNG_YPR0_PAD \ | ||
109 | || CONFIG_KEYPAD == CREATIVE_ZEN_PAD | ||
110 | #define SCROBBLE_OFF BUTTON_BACK | ||
111 | #define SCROBBLE_OFF_TXT "BACK" | ||
112 | #elif CONFIG_KEYPAD == MROBE500_PAD | ||
113 | #define SCROBBLE_OFF BUTTON_POWER | ||
114 | #define SCROBBLE_OFF_TXT "POWER" | ||
115 | #elif CONFIG_KEYPAD == MROBE100_PAD | ||
116 | #define SCROBBLE_OFF BUTTON_POWER | ||
117 | #define SCROBBLE_OFF_TXT "POWER" | ||
118 | #elif CONFIG_KEYPAD == IAUDIO_M3_PAD | ||
119 | #define SCROBBLE_OFF BUTTON_REC | ||
120 | #define BATTERY_RC_OFF BUTTON_RC_REC | ||
121 | #define SCROBBLE_OFF_TXT "REC" | ||
122 | #elif CONFIG_KEYPAD == COWON_D2_PAD | ||
123 | #define SCROBBLE_OFF BUTTON_POWER | ||
124 | #define SCROBBLE_OFF_TXT "POWER" | ||
125 | #elif CONFIG_KEYPAD == CREATIVEZVM_PAD | ||
126 | #define SCROBBLE_OFF BUTTON_BACK | ||
127 | #define SCROBBLE_OFF_TXT "BACK" | ||
128 | #elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD | ||
129 | #define SCROBBLE_OFF BUTTON_POWER | ||
130 | #define SCROBBLE_OFF_TXT "POWER" | ||
131 | #elif CONFIG_KEYPAD == PHILIPS_HDD6330_PAD | ||
132 | #define SCROBBLE_OFF BUTTON_POWER | ||
133 | #define SCROBBLE_OFF_TXT "POWER" | ||
134 | #elif CONFIG_KEYPAD == PHILIPS_SA9200_PAD | ||
135 | #define SCROBBLE_OFF BUTTON_POWER | ||
136 | #define SCROBBLE_OFF_TXT "POWER" | ||
137 | #elif CONFIG_KEYPAD == ONDAVX747_PAD | ||
138 | #define SCROBBLE_OFF BUTTON_POWER | ||
139 | #define SCROBBLE_OFF_TXT "POWER" | ||
140 | #elif CONFIG_KEYPAD == ONDAVX777_PAD | ||
141 | #define SCROBBLE_OFF BUTTON_POWER | ||
142 | #define SCROBBLE_OFF_TXT "POWER" | ||
143 | #elif (CONFIG_KEYPAD == SAMSUNG_YH820_PAD) || \ | ||
144 | (CONFIG_KEYPAD == SAMSUNG_YH92X_PAD) | ||
145 | #define SCROBBLE_OFF BUTTON_RIGHT | ||
146 | #define SCROBBLE_OFF_TXT "RIGHT" | ||
147 | #elif CONFIG_KEYPAD == PBELL_VIBE500_PAD | ||
148 | #define SCROBBLE_OFF BUTTON_REC | ||
149 | #define SCROBBLE_OFF_TXT "REC" | ||
150 | #elif CONFIG_KEYPAD == MPIO_HD200_PAD | ||
151 | #define SCROBBLE_OFF BUTTON_REC | ||
152 | #define SCROBBLE_OFF_TXT "REC" | ||
153 | #elif CONFIG_KEYPAD == MPIO_HD300_PAD | ||
154 | #define SCROBBLE_OFF BUTTON_REC | ||
155 | #define SCROBBLE_OFF_TXT "REC" | ||
156 | #elif CONFIG_KEYPAD == SANSA_FUZEPLUS_PAD | ||
157 | #define SCROBBLE_OFF BUTTON_POWER | ||
158 | #define SCROBBLE_OFF_TXT "POWER" | ||
159 | #elif CONFIG_KEYPAD == SANSA_CONNECT_PAD | ||
160 | #define SCROBBLE_OFF BUTTON_POWER | ||
161 | #define SCROBBLE_OFF_TXT "POWER" | ||
162 | #elif (CONFIG_KEYPAD == HM60X_PAD) || (CONFIG_KEYPAD == HM801_PAD) | ||
163 | #define SCROBBLE_OFF BUTTON_POWER | ||
164 | #define SCROBBLE_OFF_TXT "POWER" | ||
165 | #elif CONFIG_KEYPAD == DX50_PAD | ||
166 | #define SCROBBLE_OFF BUTTON_POWER_LONG | ||
167 | #define SCROBBLE_OFF_TXT "Power Long" | ||
168 | #elif CONFIG_KEYPAD == CREATIVE_ZENXFI2_PAD | ||
169 | #define SCROBBLE_OFF BUTTON_POWER | ||
170 | #define SCROBBLE_OFF_TXT "Power" | ||
171 | #elif CONFIG_KEYPAD == FIIO_M3K_PAD | ||
172 | #define SCROBBLE_OFF BUTTON_POWER | ||
173 | #define SCROBBLE_OFF_TXT "Power" | ||
174 | #elif CONFIG_KEYPAD == SHANLING_Q1_PAD | ||
175 | /* use touchscreen */ | ||
176 | #else | ||
177 | #error "No keymap defined!" | ||
178 | #endif | ||
179 | #if defined(HAVE_TOUCHSCREEN) | ||
180 | #ifndef SCROBBLE_OFF | ||
181 | #define SCROBBLE_OFF BUTTON_TOPLEFT | ||
182 | #endif | ||
183 | #ifndef SCROBBLE_OFF_TXT | ||
184 | #define SCROBBLE_OFF_TXT "TOPLEFT" | ||
185 | #endif | ||
186 | #endif | ||
187 | /****************** prototypes ******************/ | ||
188 | int plugin_main(const void* parameter); /* main loop */ | ||
189 | enum plugin_status plugin_start(const void* parameter); /* entry */ | ||
190 | |||
191 | /****************** globals ******************/ | ||
192 | unsigned char **language_strings; /* for use with str() macro; must be init */ | ||
193 | /* communication to the worker thread */ | ||
194 | static struct | ||
195 | { | ||
196 | bool exiting; /* signal to the thread that we want to exit */ | ||
197 | unsigned int id; /* worker thread id */ | ||
198 | struct event_queue queue; /* thread event queue */ | ||
199 | long stack[THREAD_STACK_SIZE / sizeof(long)]; | ||
200 | } gThread; | ||
201 | |||
202 | static struct | ||
203 | { | ||
204 | char *buf; | ||
205 | int pos; | ||
206 | size_t size; | ||
207 | bool pending; | ||
208 | bool force_flush; | ||
209 | } gCache; | ||
210 | |||
211 | /****************** helper fuctions ******************/ | ||
212 | |||
213 | int scrobbler_init(void) | ||
214 | { | ||
215 | gCache.buf = rb->plugin_get_buffer(&gCache.size); | ||
216 | |||
217 | size_t reqsz = SCROBBLER_MAX_CACHE*SCROBBLER_CACHE_LEN; | ||
218 | gCache.size = PLUGIN_BUFFER_SIZE - rb->plugin_reserve_buffer(reqsz); | ||
219 | |||
220 | if (gCache.size < reqsz) | ||
221 | { | ||
222 | logf("SCROBBLER: OOM , %ld < req:%ld", gCache.size, reqsz); | ||
223 | return -1; | ||
224 | } | ||
225 | |||
226 | gCache.pos = 0; | ||
227 | gCache.pending = false; | ||
228 | gCache.force_flush = true; | ||
229 | logf("Scrobbler Initialized"); | ||
230 | return 1; | ||
231 | } | ||
232 | |||
233 | static void get_scrobbler_filename(char *path, size_t size) | ||
234 | { | ||
235 | int used; | ||
236 | |||
237 | used = rb->snprintf(path, size, "/%s", BASE_FILENAME); | ||
238 | |||
239 | if (used >= (int)size) | ||
240 | { | ||
241 | logf("%s: not enough buffer space for log file", __func__); | ||
242 | rb->memset(path, 0, size); | ||
243 | } | ||
244 | } | ||
245 | |||
246 | static void scrobbler_write_cache(void) | ||
247 | { | ||
248 | int i; | ||
249 | int fd; | ||
250 | logf("%s", __func__); | ||
251 | char scrobbler_file[MAX_PATH]; | ||
252 | get_scrobbler_filename(scrobbler_file, sizeof(scrobbler_file)); | ||
253 | |||
254 | /* If the file doesn't exist, create it. | ||
255 | Check at each write since file may be deleted at any time */ | ||
256 | if(!rb->file_exists(scrobbler_file)) | ||
257 | { | ||
258 | fd = rb->open(scrobbler_file, O_RDWR | O_CREAT, 0666); | ||
259 | if(fd >= 0) | ||
260 | { | ||
261 | rb->fdprintf(fd, "#AUDIOSCROBBLER/" SCROBBLER_VERSION "\n" | ||
262 | "#TZ/UNKNOWN\n" "#CLIENT/Rockbox " | ||
263 | TARGET_NAME SCROBBLER_REVISION | ||
264 | HDR_STR_TIMELESS "\n"); | ||
265 | rb->fdprintf(fd, ITEM_HDR); | ||
266 | |||
267 | rb->close(fd); | ||
268 | } | ||
269 | else | ||
270 | { | ||
271 | logf("SCROBBLER: cannot create log file (%s)", scrobbler_file); | ||
272 | } | ||
273 | } | ||
274 | |||
275 | /* write the cache entries */ | ||
276 | fd = rb->open(scrobbler_file, O_WRONLY | O_APPEND); | ||
277 | if(fd >= 0) | ||
278 | { | ||
279 | logf("SCROBBLER: writing %d entries", gCache.pos); | ||
280 | /* copy data to temporary storage in case data moves during I/O */ | ||
281 | char temp_buf[SCROBBLER_CACHE_LEN]; | ||
282 | for ( i=0; i < gCache.pos; i++ ) | ||
283 | { | ||
284 | logf("SCROBBLER: write %d", i); | ||
285 | char* scrobbler_buf = gCache.buf; | ||
286 | ssize_t len = rb->strlcpy(temp_buf, scrobbler_buf+(SCROBBLER_CACHE_LEN*i), | ||
287 | sizeof(temp_buf)); | ||
288 | if (rb->write(fd, temp_buf, len) != len) | ||
289 | break; | ||
290 | } | ||
291 | rb->close(fd); | ||
292 | } | ||
293 | else | ||
294 | { | ||
295 | logf("SCROBBLER: error writing file"); | ||
296 | } | ||
297 | |||
298 | /* clear even if unsuccessful - don't want to overflow the buffer */ | ||
299 | gCache.pos = 0; | ||
300 | } | ||
301 | |||
302 | static void scrobbler_flush_callback(void) | ||
303 | { | ||
304 | (void) gCache.force_flush; | ||
305 | if(gCache.pos <= 0) | ||
306 | return; | ||
307 | #if (CONFIG_STORAGE & STORAGE_ATA) | ||
308 | else | ||
309 | #else | ||
310 | if ((gCache.pos >= SCROBBLER_MAX_CACHE / 2) || gCache.force_flush == true) | ||
311 | #endif | ||
312 | { | ||
313 | gCache.force_flush = false; | ||
314 | logf("%s", __func__); | ||
315 | scrobbler_write_cache(); | ||
316 | } | ||
317 | } | ||
318 | |||
319 | static void scrobbler_add_to_cache(const struct mp3entry *id) | ||
320 | { | ||
321 | if ( gCache.pos >= SCROBBLER_MAX_CACHE ) | ||
322 | scrobbler_write_cache(); | ||
323 | |||
324 | char rating = 'S'; /* Skipped */ | ||
325 | char* scrobbler_buf = gCache.buf; | ||
326 | |||
327 | logf("SCROBBLER: add_to_cache[%d]", gCache.pos); | ||
328 | |||
329 | if (id->elapsed > id->length / 2) | ||
330 | rating = 'L'; /* Listened */ | ||
331 | |||
332 | char tracknum[11] = { "" }; | ||
333 | |||
334 | if (id->tracknum > 0) | ||
335 | rb->snprintf(tracknum, sizeof (tracknum), "%d", id->tracknum); | ||
336 | |||
337 | int ret = rb->snprintf(&scrobbler_buf[(SCROBBLER_CACHE_LEN*gCache.pos)], | ||
338 | SCROBBLER_CACHE_LEN, | ||
339 | "%s\t%s\t%s\t%s\t%d\t%c\t%ld\t%s\n", | ||
340 | id->artist, | ||
341 | id->album ?: "", | ||
342 | id->title, | ||
343 | tracknum, | ||
344 | (int)(id->length / 1000), | ||
345 | rating, | ||
346 | get_timestamp(), | ||
347 | id->mb_track_id ?id->mb_track_id: ""); | ||
348 | |||
349 | if ( ret >= SCROBBLER_CACHE_LEN ) | ||
350 | { | ||
351 | logf("SCROBBLER: entry too long:"); | ||
352 | logf("SCROBBLER: %s", id->path); | ||
353 | } | ||
354 | else | ||
355 | { | ||
356 | logf("Added %s", scrobbler_buf); | ||
357 | gCache.pos++; | ||
358 | rb->register_storage_idle_func(scrobbler_flush_callback); | ||
359 | } | ||
360 | |||
361 | } | ||
362 | |||
363 | static void scrobbler_flush_cache(void) | ||
364 | { | ||
365 | logf("%s", __func__); | ||
366 | /* Add any pending entries to the cache */ | ||
367 | if (gCache.pending) | ||
368 | { | ||
369 | gCache.pending = false; | ||
370 | if (rb->audio_status()) | ||
371 | scrobbler_add_to_cache(rb->audio_current_track()); | ||
372 | } | ||
373 | |||
374 | /* Write the cache to disk if needed */ | ||
375 | if (gCache.pos > 0) | ||
376 | { | ||
377 | scrobbler_write_cache(); | ||
378 | } | ||
379 | } | ||
380 | |||
381 | static void scrobbler_change_event(unsigned short id, void *ev_data) | ||
382 | { | ||
383 | (void)id; | ||
384 | logf("%s", __func__); | ||
385 | struct mp3entry *id3 = ((struct track_event *)ev_data)->id3; | ||
386 | |||
387 | /* check if track was resumed > %50 played ( likely got saved ) | ||
388 | check for blank artist or track name */ | ||
389 | if ((id3->elapsed > id3->length / 2) || !id3->artist || !id3->title) | ||
390 | { | ||
391 | gCache.pending = false; | ||
392 | logf("SCROBBLER: skipping file %s", id3->path); | ||
393 | } | ||
394 | else | ||
395 | { | ||
396 | logf("SCROBBLER: add pending %s",id3->path); | ||
397 | record_timestamp(); | ||
398 | gCache.pending = true; | ||
399 | } | ||
400 | } | ||
401 | #ifdef ROCKBOX_HAS_LOGF | ||
402 | static const char* track_event_info(struct track_event* te) | ||
403 | { | ||
404 | |||
405 | static const char *strflags[] = {"TEF_NONE", "TEF_CURRENT", | ||
406 | "TEF_AUTOSKIP", "TEF_CUR|ASKIP", | ||
407 | "TEF_REWIND", "TEF_CUR|REW", | ||
408 | "TEF_ASKIP|REW", "TEF_CUR|ASKIP|REW"}; | ||
409 | /*TEF_NONE = 0x0, no flags are set | ||
410 | * TEF_CURRENT = 0x1, event is for the current track | ||
411 | * TEF_AUTO_SKIP = 0x2, event is sent in context of auto skip | ||
412 | * TEF_REWIND = 0x4, interpret as rewind, id3->elapsed is the | ||
413 | position before the seek back to 0 | ||
414 | */ | ||
415 | logf("flag %d", te->flags); | ||
416 | return strflags[te->flags&0x7]; | ||
417 | } | ||
418 | |||
419 | #endif | ||
420 | static void scrobbler_finish_event(unsigned short id, void *ev_data) | ||
421 | { | ||
422 | (void)id; | ||
423 | struct track_event *te = ((struct track_event *)ev_data); | ||
424 | struct mp3entry *id3 = te->id3; | ||
425 | logf("%s %s %s", __func__, gCache.pending?"True":"False", track_event_info(te)); | ||
426 | /* add entry using the currently ending track */ | ||
427 | if (gCache.pending && (te->flags & TEF_CURRENT) && !(te->flags & TEF_REWIND)) | ||
428 | { | ||
429 | gCache.pending = false; | ||
430 | |||
431 | if (id3->elapsed*2 >= id3->length) | ||
432 | scrobbler_add_to_cache(te->id3); | ||
433 | else | ||
434 | { | ||
435 | logf("%s Discarding < 50%% played", __func__); | ||
436 | } | ||
437 | } | ||
438 | |||
439 | |||
440 | } | ||
441 | |||
442 | /****************** main thread + helper ******************/ | ||
443 | void thread(void) | ||
444 | { | ||
445 | bool in_usb = false; | ||
446 | |||
447 | struct queue_event ev; | ||
448 | while (!gThread.exiting) | ||
449 | { | ||
450 | rb->queue_wait(&gThread.queue, &ev); | ||
451 | |||
452 | switch (ev.id) | ||
453 | { | ||
454 | case SYS_USB_CONNECTED: | ||
455 | scrobbler_flush_cache(); | ||
456 | rb->usb_acknowledge(SYS_USB_CONNECTED_ACK); | ||
457 | in_usb = true; | ||
458 | break; | ||
459 | case SYS_USB_DISCONNECTED: | ||
460 | in_usb = false; | ||
461 | /*fall through*/ | ||
462 | case EV_STARTUP: | ||
463 | rb->beep_play(1500, 100, 1000); | ||
464 | break; | ||
465 | case SYS_POWEROFF: | ||
466 | gCache.force_flush = true; | ||
467 | /*fall through*/ | ||
468 | case EV_EXIT: | ||
469 | rb->unregister_storage_idle_func(scrobbler_flush_callback, !in_usb); | ||
470 | return; | ||
471 | case EV_OTHINSTANCE: | ||
472 | scrobbler_flush_cache(); | ||
473 | rb->splashf(HZ * 2, "%s Cache Flushed", str(LANG_AUDIOSCROBBLER)); | ||
474 | break; | ||
475 | default: | ||
476 | logf("default %ld", ev.id); | ||
477 | break; | ||
478 | } | ||
479 | } | ||
480 | } | ||
481 | |||
482 | void thread_create(void) | ||
483 | { | ||
484 | /* put the thread's queue in the bcast list */ | ||
485 | rb->queue_init(&gThread.queue, true); | ||
486 | gThread.id = rb->create_thread(thread, gThread.stack, sizeof(gThread.stack), | ||
487 | 0, "Last.Fm_TSR" | ||
488 | IF_PRIO(, PRIORITY_BACKGROUND) | ||
489 | IF_COP(, CPU)); | ||
490 | rb->queue_post(&gThread.queue, EV_STARTUP, 0); | ||
491 | rb->yield(); | ||
492 | } | ||
493 | |||
494 | void thread_quit(void) | ||
495 | { | ||
496 | if (!gThread.exiting) { | ||
497 | rb->queue_post(&gThread.queue, EV_EXIT, 0); | ||
498 | rb->thread_wait(gThread.id); | ||
499 | /* we don't want any more events */ | ||
500 | rb->remove_event(PLAYBACK_EVENT_TRACK_CHANGE, scrobbler_change_event); | ||
501 | rb->remove_event(PLAYBACK_EVENT_TRACK_FINISH, scrobbler_finish_event); | ||
502 | /* remove the thread's queue from the broadcast list */ | ||
503 | rb->queue_delete(&gThread.queue); | ||
504 | gThread.exiting = true; | ||
505 | } | ||
506 | } | ||
507 | |||
508 | /* callback to end the TSR plugin, called before a new one gets loaded */ | ||
509 | static bool exit_tsr(bool reenter) | ||
510 | { | ||
511 | logf("%s", __func__); | ||
512 | bool is_exit = false; | ||
513 | int button; | ||
514 | if (reenter) | ||
515 | { | ||
516 | logf(" reenter other instance "); | ||
517 | rb->queue_post(&gThread.queue, EV_OTHINSTANCE, 0); | ||
518 | return false; /* dont let it start again */ | ||
519 | } | ||
520 | rb->lcd_clear_display(); | ||
521 | rb->lcd_puts_scroll(0, 0, "Scrobbler is currently running."); | ||
522 | rb->lcd_puts_scroll(0, 1, "Press " SCROBBLE_OFF_TXT " to exit"); | ||
523 | rb->lcd_puts_scroll(0, 2, "Anything else will resume"); | ||
524 | |||
525 | rb->lcd_update(); | ||
526 | rb->button_clear_queue(); | ||
527 | while (1) | ||
528 | { | ||
529 | button = rb->button_get(true); | ||
530 | if (IS_SYSEVENT(button)) | ||
531 | continue; | ||
532 | if (button == SCROBBLE_OFF) | ||
533 | { | ||
534 | rb->queue_post(&gThread.queue, EV_EXIT, 0); | ||
535 | rb->thread_wait(gThread.id); | ||
536 | /* remove the thread's queue from the broadcast list */ | ||
537 | rb->queue_delete(&gThread.queue); | ||
538 | is_exit = true; | ||
539 | } | ||
540 | else is_exit = false; | ||
541 | |||
542 | break; | ||
543 | } | ||
544 | FOR_NB_SCREENS(idx) | ||
545 | rb->screens[idx]->scroll_stop(); | ||
546 | |||
547 | if (is_exit) | ||
548 | thread_quit(); | ||
549 | |||
550 | return is_exit; | ||
551 | } | ||
552 | |||
553 | /****************** main ******************/ | ||
554 | |||
555 | int plugin_main(const void* parameter) | ||
556 | { | ||
557 | (void)parameter; | ||
558 | |||
559 | rb->memset(&gThread, 0, sizeof(gThread)); | ||
560 | rb->splashf(HZ / 2, "%s Started",str(LANG_AUDIOSCROBBLER)); | ||
561 | logf("%s: %s Started", __func__, str(LANG_AUDIOSCROBBLER)); | ||
562 | |||
563 | rb->plugin_tsr(exit_tsr); /* stay resident */ | ||
564 | |||
565 | rb->add_event(PLAYBACK_EVENT_TRACK_CHANGE, scrobbler_change_event); | ||
566 | rb->add_event(PLAYBACK_EVENT_TRACK_FINISH, scrobbler_finish_event); | ||
567 | thread_create(); | ||
568 | |||
569 | return 0; | ||
570 | } | ||
571 | |||
572 | /***************** Plugin Entry Point *****************/ | ||
573 | |||
574 | enum plugin_status plugin_start(const void* parameter) | ||
575 | { | ||
576 | /* now go ahead and have fun! */ | ||
577 | if (rb->usb_inserted() == true) | ||
578 | return PLUGIN_USB_CONNECTED; | ||
579 | language_strings = rb->language_strings; | ||
580 | if (scrobbler_init() < 0) | ||
581 | return PLUGIN_ERROR; | ||
582 | int ret = plugin_main(parameter); | ||
583 | return (ret==0) ? PLUGIN_OK : PLUGIN_ERROR; | ||
584 | } | ||
diff --git a/apps/scrobbler.c b/apps/scrobbler.c deleted file mode 100644 index f5ccf4a61c..0000000000 --- a/apps/scrobbler.c +++ /dev/null | |||
@@ -1,287 +0,0 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2006-2008 Robert Keevil | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | /* | ||
22 | Audioscrobbler spec at: | ||
23 | http://www.audioscrobbler.net/wiki/Portable_Player_Logging | ||
24 | */ | ||
25 | |||
26 | #include <stdio.h> | ||
27 | #include <config.h> | ||
28 | #include "file.h" | ||
29 | #include "logf.h" | ||
30 | #include "metadata.h" | ||
31 | #include "kernel.h" | ||
32 | #include "audio.h" | ||
33 | #include "core_alloc.h" | ||
34 | #include "rbpaths.h" | ||
35 | #include "ata_idle_notify.h" | ||
36 | #include "pathfuncs.h" | ||
37 | #include "appevents.h" | ||
38 | #include "string-extra.h" | ||
39 | #if CONFIG_RTC | ||
40 | #include "time.h" | ||
41 | #include "timefuncs.h" | ||
42 | #endif | ||
43 | |||
44 | #include "scrobbler.h" | ||
45 | |||
46 | #define SCROBBLER_VERSION "1.1" | ||
47 | |||
48 | /* increment this on any code change that effects output */ | ||
49 | #define SCROBBLER_REVISION " $Revision$" | ||
50 | |||
51 | #define SCROBBLER_MAX_CACHE 32 | ||
52 | /* longest entry I've had is 323, add a safety margin */ | ||
53 | #define SCROBBLER_CACHE_LEN 512 | ||
54 | |||
55 | static bool scrobbler_initialised = false; | ||
56 | static int scrobbler_cache = 0; | ||
57 | static int cache_pos = 0; | ||
58 | static bool pending = false; | ||
59 | #if CONFIG_RTC | ||
60 | static time_t timestamp; | ||
61 | #define BASE_FILENAME HOME_DIR "/.scrobbler.log" | ||
62 | #define HDR_STR_TIMELESS | ||
63 | #define get_timestamp() ((long)timestamp) | ||
64 | #define record_timestamp() ((void)(timestamp = mktime(get_time()))) | ||
65 | #else /* !CONFIG_RTC */ | ||
66 | #define HDR_STR_TIMELESS " Timeless" | ||
67 | #define BASE_FILENAME ".scrobbler-timeless.log" | ||
68 | #define get_timestamp() (0l) | ||
69 | #define record_timestamp() ({}) | ||
70 | #endif /* CONFIG_RTC */ | ||
71 | |||
72 | static void get_scrobbler_filename(char *path, size_t size) | ||
73 | { | ||
74 | int used; | ||
75 | |||
76 | used = snprintf(path, size, "/%s", BASE_FILENAME); | ||
77 | |||
78 | if (used >= (int)size) | ||
79 | { | ||
80 | logf("SCROBBLER: not enough buffer space for log file"); | ||
81 | memset(path, 0, size); | ||
82 | } | ||
83 | } | ||
84 | |||
85 | static void write_cache(void) | ||
86 | { | ||
87 | int i; | ||
88 | int fd; | ||
89 | |||
90 | char scrobbler_file[MAX_PATH]; | ||
91 | get_scrobbler_filename(scrobbler_file, MAX_PATH); | ||
92 | |||
93 | /* If the file doesn't exist, create it. | ||
94 | Check at each write since file may be deleted at any time */ | ||
95 | if(!file_exists(scrobbler_file)) | ||
96 | { | ||
97 | fd = open(scrobbler_file, O_RDWR | O_CREAT, 0666); | ||
98 | if(fd >= 0) | ||
99 | { | ||
100 | fdprintf(fd, "#AUDIOSCROBBLER/" SCROBBLER_VERSION "\n" | ||
101 | "#TZ/UNKNOWN\n" "#CLIENT/Rockbox " | ||
102 | TARGET_NAME SCROBBLER_REVISION | ||
103 | HDR_STR_TIMELESS "\n"); | ||
104 | |||
105 | close(fd); | ||
106 | } | ||
107 | else | ||
108 | { | ||
109 | logf("SCROBBLER: cannot create log file (%s)", scrobbler_file); | ||
110 | } | ||
111 | } | ||
112 | |||
113 | /* write the cache entries */ | ||
114 | fd = open(scrobbler_file, O_WRONLY | O_APPEND); | ||
115 | if(fd >= 0) | ||
116 | { | ||
117 | logf("SCROBBLER: writing %d entries", cache_pos); | ||
118 | /* copy data to temporary storage in case data moves during I/O */ | ||
119 | char temp_buf[SCROBBLER_CACHE_LEN]; | ||
120 | for ( i=0; i < cache_pos; i++ ) | ||
121 | { | ||
122 | logf("SCROBBLER: write %d", i); | ||
123 | char* scrobbler_buf = core_get_data(scrobbler_cache); | ||
124 | ssize_t len = strlcpy(temp_buf, scrobbler_buf+(SCROBBLER_CACHE_LEN*i), | ||
125 | sizeof(temp_buf)); | ||
126 | if (write(fd, temp_buf, len) != len) | ||
127 | break; | ||
128 | } | ||
129 | close(fd); | ||
130 | } | ||
131 | else | ||
132 | { | ||
133 | logf("SCROBBLER: error writing file"); | ||
134 | } | ||
135 | |||
136 | /* clear even if unsuccessful - don't want to overflow the buffer */ | ||
137 | cache_pos = 0; | ||
138 | } | ||
139 | |||
140 | static void scrobbler_flush_callback(void) | ||
141 | { | ||
142 | if (scrobbler_initialised && cache_pos) | ||
143 | write_cache(); | ||
144 | } | ||
145 | |||
146 | static void add_to_cache(const struct mp3entry *id) | ||
147 | { | ||
148 | if ( cache_pos >= SCROBBLER_MAX_CACHE ) | ||
149 | write_cache(); | ||
150 | |||
151 | char rating = 'S'; /* Skipped */ | ||
152 | char* scrobbler_buf = core_get_data(scrobbler_cache); | ||
153 | |||
154 | logf("SCROBBLER: add_to_cache[%d]", cache_pos); | ||
155 | |||
156 | if (id->elapsed > id->length / 2) | ||
157 | rating = 'L'; /* Listened */ | ||
158 | |||
159 | char tracknum[11] = { "" }; | ||
160 | |||
161 | if (id->tracknum > 0) | ||
162 | snprintf(tracknum, sizeof (tracknum), "%d", id->tracknum); | ||
163 | |||
164 | int ret = snprintf(scrobbler_buf+(SCROBBLER_CACHE_LEN*cache_pos), | ||
165 | SCROBBLER_CACHE_LEN, | ||
166 | "%s\t%s\t%s\t%s\t%d\t%c\t%ld\t%s\n", | ||
167 | id->artist, | ||
168 | id->album ?: "", | ||
169 | id->title, | ||
170 | tracknum, | ||
171 | (int)(id->length / 1000), | ||
172 | rating, | ||
173 | get_timestamp(), | ||
174 | id->mb_track_id ?: ""); | ||
175 | |||
176 | if ( ret >= SCROBBLER_CACHE_LEN ) | ||
177 | { | ||
178 | logf("SCROBBLER: entry too long:"); | ||
179 | logf("SCROBBLER: %s", id->path); | ||
180 | } | ||
181 | else | ||
182 | { | ||
183 | cache_pos++; | ||
184 | register_storage_idle_func(scrobbler_flush_callback); | ||
185 | } | ||
186 | |||
187 | } | ||
188 | |||
189 | static void scrobbler_change_event(unsigned short id, void *ev_data) | ||
190 | { | ||
191 | (void)id; | ||
192 | struct mp3entry *id3 = ((struct track_event *)ev_data)->id3; | ||
193 | |||
194 | /* check if track was resumed > %50 played | ||
195 | check for blank artist or track name */ | ||
196 | if (id3->elapsed > id3->length / 2 || !id3->artist || !id3->title) | ||
197 | { | ||
198 | pending = false; | ||
199 | logf("SCROBBLER: skipping file %s", id3->path); | ||
200 | } | ||
201 | else | ||
202 | { | ||
203 | logf("SCROBBLER: add pending"); | ||
204 | record_timestamp(); | ||
205 | pending = true; | ||
206 | } | ||
207 | } | ||
208 | |||
209 | static void scrobbler_finish_event(unsigned short id, void *data) | ||
210 | { | ||
211 | (void)id; | ||
212 | struct track_event *te = (struct track_event *)data; | ||
213 | |||
214 | /* add entry using the currently ending track */ | ||
215 | if (pending && (te->flags & TEF_CURRENT) | ||
216 | && !(te->flags & TEF_REWIND) | ||
217 | ) | ||
218 | { | ||
219 | pending = false; | ||
220 | add_to_cache(te->id3); | ||
221 | } | ||
222 | } | ||
223 | |||
224 | int scrobbler_init(void) | ||
225 | { | ||
226 | if (scrobbler_initialised) | ||
227 | return 1; | ||
228 | |||
229 | scrobbler_cache = core_alloc("scrobbler", | ||
230 | SCROBBLER_MAX_CACHE*SCROBBLER_CACHE_LEN); | ||
231 | |||
232 | if (scrobbler_cache <= 0) | ||
233 | { | ||
234 | logf("SCROOBLER: OOM"); | ||
235 | return -1; | ||
236 | } | ||
237 | |||
238 | cache_pos = 0; | ||
239 | pending = false; | ||
240 | |||
241 | scrobbler_initialised = true; | ||
242 | |||
243 | add_event(PLAYBACK_EVENT_TRACK_CHANGE, scrobbler_change_event); | ||
244 | add_event(PLAYBACK_EVENT_TRACK_FINISH, scrobbler_finish_event); | ||
245 | |||
246 | return 1; | ||
247 | } | ||
248 | |||
249 | static void scrobbler_flush_cache(void) | ||
250 | { | ||
251 | /* Add any pending entries to the cache */ | ||
252 | if (pending) | ||
253 | { | ||
254 | pending = false; | ||
255 | if (audio_status()) | ||
256 | add_to_cache(audio_current_track()); | ||
257 | } | ||
258 | |||
259 | /* Write the cache to disk if needed */ | ||
260 | if (cache_pos) | ||
261 | write_cache(); | ||
262 | } | ||
263 | |||
264 | void scrobbler_shutdown(bool poweroff) | ||
265 | { | ||
266 | if (!scrobbler_initialised) | ||
267 | return; | ||
268 | |||
269 | remove_event(PLAYBACK_EVENT_TRACK_CHANGE, scrobbler_change_event); | ||
270 | remove_event(PLAYBACK_EVENT_TRACK_FINISH, scrobbler_finish_event); | ||
271 | |||
272 | scrobbler_flush_cache(); | ||
273 | |||
274 | if (!poweroff) | ||
275 | { | ||
276 | /* get rid of the buffer */ | ||
277 | core_free(scrobbler_cache); | ||
278 | scrobbler_cache = 0; | ||
279 | } | ||
280 | |||
281 | scrobbler_initialised = false; | ||
282 | } | ||
283 | |||
284 | bool scrobbler_is_enabled(void) | ||
285 | { | ||
286 | return scrobbler_initialised; | ||
287 | } | ||
diff --git a/apps/scrobbler.h b/apps/scrobbler.h deleted file mode 100644 index a3d1b361df..0000000000 --- a/apps/scrobbler.h +++ /dev/null | |||
@@ -1,29 +0,0 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2006 Robert Keevil | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | #ifndef __SCROBBLER_H__ | ||
23 | #define __SCROBBLER_H__ | ||
24 | |||
25 | int scrobbler_init(void); | ||
26 | void scrobbler_shutdown(bool poweroff); | ||
27 | bool scrobbler_is_enabled(void); | ||
28 | |||
29 | #endif /* __SCROBBLER_H__ */ | ||
diff --git a/apps/settings.h b/apps/settings.h index 8ff006d682..53d7d35cae 100644 --- a/apps/settings.h +++ b/apps/settings.h | |||
@@ -503,7 +503,6 @@ struct user_settings | |||
503 | int single_mode; /* single mode - stop after every track, album, album artist, | 503 | int single_mode; /* single mode - stop after every track, album, album artist, |
504 | artist, composer, work, or genre */ | 504 | artist, composer, work, or genre */ |
505 | bool party_mode; /* party mode - unstoppable music */ | 505 | bool party_mode; /* party mode - unstoppable music */ |
506 | bool audioscrobbler; /* Audioscrobbler logging */ | ||
507 | bool cuesheet; | 506 | bool cuesheet; |
508 | bool car_adapter_mode; /* 0=off 1=on */ | 507 | bool car_adapter_mode; /* 0=off 1=on */ |
509 | int car_adapter_mode_delay; /* delay before resume, in seconds*/ | 508 | int car_adapter_mode_delay; /* delay before resume, in seconds*/ |
diff --git a/apps/settings_list.c b/apps/settings_list.c index aa2ebbf883..3a731bac2c 100644 --- a/apps/settings_list.c +++ b/apps/settings_list.c | |||
@@ -1832,8 +1832,6 @@ const struct settings_list settings[] = { | |||
1832 | ID2P(LANG_FM_ITALY), ID2P(LANG_FM_OTHER)), | 1832 | ID2P(LANG_FM_ITALY), ID2P(LANG_FM_OTHER)), |
1833 | #endif | 1833 | #endif |
1834 | 1834 | ||
1835 | OFFON_SETTING(F_BANFROMQS, audioscrobbler, LANG_AUDIOSCROBBLER, false, | ||
1836 | "Last.fm Logging", NULL), | ||
1837 | #if CONFIG_TUNER | 1835 | #if CONFIG_TUNER |
1838 | TEXT_SETTING(0, fmr_file, "fmr", "-", | 1836 | TEXT_SETTING(0, fmr_file, "fmr", "-", |
1839 | FMPRESET_PATH "/", ".fmr"), | 1837 | FMPRESET_PATH "/", ".fmr"), |