1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2006-2008 Robert Keevil
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.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
22 Audioscrobbler spec at:
23 http://www.audioscrobbler.net/wiki/Portable_Player_Logging
34 #include "ata_idle_notify.h"
36 #include "appevents.h"
40 #include "timefuncs.h"
43 #include "scrobbler.h"
45 #define SCROBBLER_VERSION "1.1"
48 #define SCROBBLER_FILE "/.scrobbler.log"
50 #define SCROBBLER_FILE "/.scrobbler-timeless.log"
53 /* increment this on any code change that effects output */
54 #define SCROBBLER_REVISION " $Revision$"
56 #define SCROBBLER_MAX_CACHE 32
57 /* longest entry I've had is 323, add a safety margin */
58 #define SCROBBLER_CACHE_LEN 512
60 static char* scrobbler_cache
;
63 static struct mp3entry scrobbler_entry
;
64 static bool pending
= false;
65 static bool scrobbler_initialised
= false;
67 static time_t timestamp
;
69 static unsigned long timestamp
;
72 /* Crude work-around for Archos Sims - return a set amount */
73 #if (CONFIG_CODEC != SWCODEC) && defined(SIMULATOR)
74 unsigned long audio_prev_elapsed(void)
80 static void write_cache(void)
85 /* If the file doesn't exist, create it.
86 Check at each write since file may be deleted at any time */
87 if(!file_exists(SCROBBLER_FILE
))
89 fd
= open(SCROBBLER_FILE
, O_RDWR
| O_CREAT
);
92 fdprintf(fd
, "#AUDIOSCROBBLER/" SCROBBLER_VERSION
"\n"
95 "#CLIENT/Rockbox " TARGET_NAME SCROBBLER_REVISION
"\n");
97 "#CLIENT/Rockbox " TARGET_NAME SCROBBLER_REVISION
" Timeless\n");
104 logf("SCROBBLER: cannot create log file");
108 /* write the cache entries */
109 fd
= open(SCROBBLER_FILE
, O_WRONLY
| O_APPEND
);
112 logf("SCROBBLER: writing %d entries", cache_pos
);
114 for ( i
=0; i
< cache_pos
; i
++ )
116 logf("SCROBBLER: write %d", i
);
117 fdprintf(fd
, "%s", scrobbler_cache
+(SCROBBLER_CACHE_LEN
*i
));
123 logf("SCROBBLER: error writing file");
126 /* clear even if unsuccessful - don't want to overflow the buffer */
130 static void scrobbler_flush_callback(void *data
)
133 if (scrobbler_initialised
&& cache_pos
)
137 static void add_to_cache(unsigned long play_length
)
139 if ( cache_pos
>= SCROBBLER_MAX_CACHE
)
143 char rating
= 'S'; /* Skipped */
145 logf("SCROBBLER: add_to_cache[%d]", cache_pos
);
147 if ( play_length
> (scrobbler_entry
.length
/2) )
148 rating
= 'L'; /* Listened */
150 if (scrobbler_entry
.tracknum
> 0)
152 ret
= snprintf(scrobbler_cache
+(SCROBBLER_CACHE_LEN
*cache_pos
),
154 "%s\t%s\t%s\t%d\t%d\t%c\t%ld\t%s\n",
155 scrobbler_entry
.artist
,
156 scrobbler_entry
.album
?scrobbler_entry
.album
:"",
157 scrobbler_entry
.title
,
158 scrobbler_entry
.tracknum
,
159 (int)scrobbler_entry
.length
/1000,
162 scrobbler_entry
.mb_track_id
?scrobbler_entry
.mb_track_id
:"");
164 ret
= snprintf(scrobbler_cache
+(SCROBBLER_CACHE_LEN
*cache_pos
),
166 "%s\t%s\t%s\t\t%d\t%c\t%ld\t%s\n",
167 scrobbler_entry
.artist
,
168 scrobbler_entry
.album
?scrobbler_entry
.album
:"",
169 scrobbler_entry
.title
,
170 (int)scrobbler_entry
.length
/1000,
173 scrobbler_entry
.mb_track_id
?scrobbler_entry
.mb_track_id
:"");
176 if ( ret
>= SCROBBLER_CACHE_LEN
)
178 logf("SCROBBLER: entry too long:");
179 logf("SCROBBLER: %s", scrobbler_entry
.path
);
182 register_storage_idle_func(scrobbler_flush_callback
);
187 static void scrobbler_change_event(void *data
)
189 struct mp3entry
*id
= (struct mp3entry
*)data
;
190 /* add entry using the previous scrobbler_entry and timestamp */
192 add_to_cache(audio_prev_elapsed());
194 /* check if track was resumed > %50 played
195 check for blank artist or track name */
196 if ((id
->elapsed
> (id
->length
/2)) ||
197 (!id
->artist
) || (!id
->title
) )
200 logf("SCROBBLER: skipping file %s", id
->path
);
204 logf("SCROBBLER: add pending");
205 copy_mp3entry(&scrobbler_entry
, id
);
207 timestamp
= mktime(get_time());
215 int scrobbler_init(void)
217 logf("SCROBBLER: init %d", global_settings
.audioscrobbler
);
219 if(!global_settings
.audioscrobbler
)
222 scrobbler_cache
= buffer_alloc(SCROBBLER_MAX_CACHE
*SCROBBLER_CACHE_LEN
);
224 add_event(PLAYBACK_EVENT_TRACK_CHANGE
, false, scrobbler_change_event
);
227 scrobbler_initialised
= true;
232 void scrobbler_flush_cache(void)
234 if (scrobbler_initialised
)
236 /* Add any pending entries to the cache */
238 add_to_cache(audio_prev_elapsed());
240 /* Write the cache to disk if needed */
248 void scrobbler_shutdown(void)
250 scrobbler_flush_cache();
252 if (scrobbler_initialised
)
254 remove_event(PLAYBACK_EVENT_TRACK_CHANGE
, scrobbler_change_event
);
255 scrobbler_initialised
= false;
259 void scrobbler_poweroff(void)
261 if (scrobbler_initialised
&& pending
)
263 if ( audio_status() )
264 add_to_cache(audio_current_track()->elapsed
);
266 add_to_cache(audio_prev_elapsed());
268 /* scrobbler_shutdown is called later, the cache will be written
269 * make sure the final track isn't added twice when that happens */
274 bool scrobbler_is_enabled(void)
276 return scrobbler_initialised
;