diff options
Diffstat (limited to 'apps/scrobbler.c')
-rwxr-xr-x | apps/scrobbler.c | 263 |
1 files changed, 263 insertions, 0 deletions
diff --git a/apps/scrobbler.c b/apps/scrobbler.c new file mode 100755 index 0000000000..68c5e9addf --- /dev/null +++ b/apps/scrobbler.c | |||
@@ -0,0 +1,263 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2006 Robert Keevil | ||
11 | * | ||
12 | * All files in this archive are subject to the GNU General Public License. | ||
13 | * See the file COPYING in the source tree root for full license agreement. | ||
14 | * | ||
15 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
16 | * KIND, either express or implied. | ||
17 | * | ||
18 | ****************************************************************************/ | ||
19 | /* | ||
20 | Audioscrobbler spec at: | ||
21 | http://www.audioscrobbler.net/wiki/Portable_Player_Logging | ||
22 | */ | ||
23 | |||
24 | #include "file.h" | ||
25 | #include "sprintf.h" | ||
26 | #include "playback.h" | ||
27 | #include "logf.h" | ||
28 | #include "id3.h" | ||
29 | #include "kernel.h" | ||
30 | #include "audio.h" | ||
31 | #include "buffer.h" | ||
32 | #include "settings.h" | ||
33 | |||
34 | #ifndef SIMULATOR | ||
35 | #include "ata.h" | ||
36 | #endif | ||
37 | |||
38 | #ifdef CONFIG_RTC | ||
39 | #include "time.h" | ||
40 | #include "timefuncs.h" | ||
41 | #endif | ||
42 | |||
43 | #include "scrobbler.h" | ||
44 | |||
45 | #define SCROBBLER_VERSION "1.0" | ||
46 | |||
47 | #ifdef CONFIG_RTC | ||
48 | #define SCROBBLER_FILE "/.scrobbler.log" | ||
49 | #else | ||
50 | #define SCROBBLER_FILE "/.scrobbler-timeless.log" | ||
51 | #endif | ||
52 | |||
53 | /* increment this on any code change that effects output */ | ||
54 | /* replace with CVS Revision keyword? */ | ||
55 | #define SCROBBLER_REVISION "1.0" | ||
56 | |||
57 | #define SCROBBLER_MAX_CACHE 32 | ||
58 | /* longest entry I've had is 323, add a safety margin */ | ||
59 | #define SCROBBLER_CACHE_LEN 512 | ||
60 | |||
61 | static char* scrobbler_cache; | ||
62 | |||
63 | static int scrobbler_fd = -1; | ||
64 | static int cache_pos; | ||
65 | static struct mp3entry scrobbler_entry; | ||
66 | static bool pending = false; | ||
67 | static bool scrobbler_initialised = false; | ||
68 | #ifdef CONFIG_RTC | ||
69 | static time_t timestamp; | ||
70 | #else | ||
71 | static unsigned long timestamp; | ||
72 | #endif | ||
73 | |||
74 | /* Crude work-around for Archos Sims - return a set amount */ | ||
75 | #if (CONFIG_CODEC != SWCODEC) && defined(SIMULATOR) | ||
76 | unsigned long audio_prev_elapsed(void) | ||
77 | { | ||
78 | return 120000; | ||
79 | } | ||
80 | #endif | ||
81 | |||
82 | static void write_cache(void) | ||
83 | { | ||
84 | int i; | ||
85 | |||
86 | /* If the file doesn't exist, create it. | ||
87 | Check at each write since file may be deleted at any time */ | ||
88 | scrobbler_fd = open(SCROBBLER_FILE, O_RDONLY); | ||
89 | if(scrobbler_fd < 0) | ||
90 | { | ||
91 | scrobbler_fd = open(SCROBBLER_FILE, O_RDWR | O_CREAT); | ||
92 | if(scrobbler_fd >= 0) | ||
93 | { | ||
94 | fdprintf(scrobbler_fd, "#AUDIOSCROBBLER/%s\n", SCROBBLER_VERSION); | ||
95 | fdprintf(scrobbler_fd, "#TZ/UNKNOWN\n"); | ||
96 | #ifdef CONFIG_RTC | ||
97 | fdprintf(scrobbler_fd, "#CLIENT/Rockbox %s %s\n", | ||
98 | TARGET_NAME, SCROBBLER_REVISION); | ||
99 | #else | ||
100 | fdprintf(scrobbler_fd, "#CLIENT/Rockbox %s %s Timeless\n", | ||
101 | TARGET_NAME, SCROBBLER_REVISION); | ||
102 | #endif | ||
103 | close(scrobbler_fd); | ||
104 | } | ||
105 | else | ||
106 | { | ||
107 | logf("SCROBBLER: cannot create log file"); | ||
108 | } | ||
109 | } | ||
110 | close(scrobbler_fd); | ||
111 | scrobbler_fd = -1; | ||
112 | |||
113 | /* write the cache entries */ | ||
114 | scrobbler_fd = open(SCROBBLER_FILE, O_WRONLY | O_APPEND); | ||
115 | if(scrobbler_fd >= 0) | ||
116 | { | ||
117 | logf("SCROBBLER: writing %d entries", cache_pos); | ||
118 | |||
119 | for ( i=0; i < cache_pos; i++ ) | ||
120 | { | ||
121 | logf("SCROBBLER: write %d", i); | ||
122 | fdprintf(scrobbler_fd, "%s", scrobbler_cache+(SCROBBLER_CACHE_LEN*i)); | ||
123 | } | ||
124 | close(scrobbler_fd); | ||
125 | } | ||
126 | else | ||
127 | { | ||
128 | logf("SCROBBLER: error writing file"); | ||
129 | } | ||
130 | |||
131 | /* clear even if unsuccessful - don't want to overflow the buffer */ | ||
132 | cache_pos = 0; | ||
133 | scrobbler_fd = -1; | ||
134 | } | ||
135 | |||
136 | static void add_to_cache(void) | ||
137 | { | ||
138 | /* using HAVE_MMC to check for Ondios - anything better to use? */ | ||
139 | #ifndef SIMULATOR | ||
140 | #if defined(IPOD_NANO) || defined(HAVE_MMC) | ||
141 | if ( cache_pos >= SCROBBLER_MAX_CACHE ) | ||
142 | #else | ||
143 | if ( ( cache_pos >= SCROBBLER_MAX_CACHE ) || ( ata_disk_is_active() ) ) | ||
144 | #endif | ||
145 | #endif /* !SIMULATOR */ | ||
146 | write_cache(); | ||
147 | |||
148 | int ret; | ||
149 | char rating = 'S'; /* Skipped */ | ||
150 | |||
151 | logf("SCROBBLER: add_to_cache[%d]", cache_pos); | ||
152 | |||
153 | if ( audio_prev_elapsed() > | ||
154 | (scrobbler_entry.length/2) ) | ||
155 | rating = 'L'; /* Listened */ | ||
156 | |||
157 | if (scrobbler_entry.tracknum > 0) | ||
158 | { | ||
159 | ret = snprintf(scrobbler_cache+(SCROBBLER_CACHE_LEN*cache_pos), | ||
160 | SCROBBLER_CACHE_LEN, | ||
161 | "%s\t%s\t%s\t%d\t%d\t%c\t%ld\n", | ||
162 | scrobbler_entry.artist, | ||
163 | scrobbler_entry.album?scrobbler_entry.album:"", | ||
164 | scrobbler_entry.title, | ||
165 | scrobbler_entry.tracknum, | ||
166 | (int)scrobbler_entry.length/1000, | ||
167 | rating, | ||
168 | (long)timestamp); | ||
169 | } else { | ||
170 | ret = snprintf(scrobbler_cache+(SCROBBLER_CACHE_LEN*cache_pos), | ||
171 | SCROBBLER_CACHE_LEN, | ||
172 | "%s\t%s\t%s\t\t%d\t%c\t%ld\n", | ||
173 | scrobbler_entry.artist, | ||
174 | scrobbler_entry.album?scrobbler_entry.album:"", | ||
175 | scrobbler_entry.title, | ||
176 | (int)scrobbler_entry.length/1000, | ||
177 | rating, | ||
178 | (long)timestamp); | ||
179 | } | ||
180 | |||
181 | if ( ret >= SCROBBLER_CACHE_LEN ) | ||
182 | { | ||
183 | logf("SCROBBLER: entry too long:"); | ||
184 | logf("SCROBBLER: %s", scrobbler_entry.path); | ||
185 | } else | ||
186 | cache_pos++; | ||
187 | } | ||
188 | |||
189 | void scrobbler_change_event(struct mp3entry *id) | ||
190 | { | ||
191 | /* add entry using the previous scrobbler_entry and timestamp */ | ||
192 | if (pending) | ||
193 | add_to_cache(); | ||
194 | |||
195 | /* check if track was resumed > %50 played | ||
196 | check for blank artist or track name */ | ||
197 | if ((id->elapsed > (id->length/2)) || | ||
198 | (!id->artist ) || (!id->title ) ) | ||
199 | { | ||
200 | pending = false; | ||
201 | logf("SCROBBLER: skipping file %s", id->path); | ||
202 | } | ||
203 | else | ||
204 | { | ||
205 | logf("SCROBBLER: add pending"); | ||
206 | copy_mp3entry(&scrobbler_entry, id); | ||
207 | #ifdef CONFIG_RTC | ||
208 | timestamp = mktime(get_time()); | ||
209 | #else | ||
210 | timestamp = 0; | ||
211 | #endif | ||
212 | pending = true; | ||
213 | } | ||
214 | } | ||
215 | |||
216 | int scrobbler_init(void) | ||
217 | { | ||
218 | logf("SCROBBLER: init %d", global_settings.audioscrobbler); | ||
219 | |||
220 | if(!global_settings.audioscrobbler) | ||
221 | return -1; | ||
222 | |||
223 | scrobbler_cache = buffer_alloc(SCROBBLER_MAX_CACHE*SCROBBLER_CACHE_LEN); | ||
224 | |||
225 | audio_set_track_changed_event(&scrobbler_change_event); | ||
226 | cache_pos = 0; | ||
227 | pending = false; | ||
228 | scrobbler_initialised = true; | ||
229 | |||
230 | return 1; | ||
231 | } | ||
232 | |||
233 | void scrobbler_flush_cache(void) | ||
234 | { | ||
235 | if (scrobbler_initialised) | ||
236 | { | ||
237 | /* Add any pending entries to the cache */ | ||
238 | if(pending) | ||
239 | add_to_cache(); | ||
240 | |||
241 | /* Write the cache to disk if needed */ | ||
242 | if (cache_pos) | ||
243 | write_cache(); | ||
244 | |||
245 | pending = false; | ||
246 | } | ||
247 | } | ||
248 | |||
249 | void scrobbler_shutdown(void) | ||
250 | { | ||
251 | scrobbler_flush_cache(); | ||
252 | |||
253 | if (scrobbler_initialised) | ||
254 | { | ||
255 | audio_set_track_changed_event(NULL); | ||
256 | scrobbler_initialised = false; | ||
257 | } | ||
258 | } | ||
259 | |||
260 | bool scrobbler_is_enabled(void) | ||
261 | { | ||
262 | return scrobbler_initialised; | ||
263 | } | ||