1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2006 Robert Keevil
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.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
20 Audioscrobbler spec at:
21 http://www.audioscrobbler.net/wiki/Portable_Player_Logging
33 #include "ata_idle_notify.h"
37 #include "timefuncs.h"
40 #include "scrobbler.h"
42 #define SCROBBLER_VERSION "1.0"
45 #define SCROBBLER_FILE "/.scrobbler.log"
47 #define SCROBBLER_FILE "/.scrobbler-timeless.log"
50 /* increment this on any code change that effects output */
51 #define SCROBBLER_REVISION " $Revision$"
53 #define SCROBBLER_MAX_CACHE 32
54 /* longest entry I've had is 323, add a safety margin */
55 #define SCROBBLER_CACHE_LEN 512
57 static char* scrobbler_cache
;
59 static int scrobbler_fd
= -1;
61 static struct mp3entry scrobbler_entry
;
62 static bool pending
= false;
63 static bool scrobbler_initialised
= false;
64 static bool scrobbler_ata_callback
= false;
66 static time_t timestamp
;
68 static unsigned long timestamp
;
71 /* Crude work-around for Archos Sims - return a set amount */
72 #if (CONFIG_CODEC != SWCODEC) && defined(SIMULATOR)
73 unsigned long audio_prev_elapsed(void)
79 static void write_cache(void)
83 scrobbler_ata_callback
= false;
85 /* If the file doesn't exist, create it.
86 Check at each write since file may be deleted at any time */
87 scrobbler_fd
= open(SCROBBLER_FILE
, O_RDONLY
);
90 scrobbler_fd
= open(SCROBBLER_FILE
, O_RDWR
| O_CREAT
);
93 fdprintf(scrobbler_fd
, "#AUDIOSCROBBLER/%s\n", SCROBBLER_VERSION
);
94 fdprintf(scrobbler_fd
, "#TZ/UNKNOWN\n");
96 fdprintf(scrobbler_fd
,
97 "#CLIENT/Rockbox " TARGET_NAME SCROBBLER_REVISION
"\n");
99 fdprintf(scrobbler_fd
,
100 "#CLIENT/Rockbox " TARGET_NAME SCROBBLER_REVISION
" Timeless\n");
106 logf("SCROBBLER: cannot create log file");
112 /* write the cache entries */
113 scrobbler_fd
= open(SCROBBLER_FILE
, O_WRONLY
| O_APPEND
);
114 if(scrobbler_fd
>= 0)
116 logf("SCROBBLER: writing %d entries", cache_pos
);
118 for ( i
=0; i
< cache_pos
; i
++ )
120 logf("SCROBBLER: write %d", i
);
121 fdprintf(scrobbler_fd
, "%s", scrobbler_cache
+(SCROBBLER_CACHE_LEN
*i
));
127 logf("SCROBBLER: error writing file");
130 /* clear even if unsuccessful - don't want to overflow the buffer */
135 static bool scrobbler_flush_callback(void)
137 if (scrobbler_initialised
&& cache_pos
)
142 static void add_to_cache(unsigned long play_length
)
144 if ( cache_pos
>= SCROBBLER_MAX_CACHE
)
148 char rating
= 'S'; /* Skipped */
150 logf("SCROBBLER: add_to_cache[%d]", cache_pos
);
152 if ( play_length
> (scrobbler_entry
.length
/2) )
153 rating
= 'L'; /* Listened */
155 if (scrobbler_entry
.tracknum
> 0)
157 ret
= snprintf(scrobbler_cache
+(SCROBBLER_CACHE_LEN
*cache_pos
),
159 "%s\t%s\t%s\t%d\t%d\t%c\t%ld\n",
160 scrobbler_entry
.artist
,
161 scrobbler_entry
.album
?scrobbler_entry
.album
:"",
162 scrobbler_entry
.title
,
163 scrobbler_entry
.tracknum
,
164 (int)scrobbler_entry
.length
/1000,
168 ret
= snprintf(scrobbler_cache
+(SCROBBLER_CACHE_LEN
*cache_pos
),
170 "%s\t%s\t%s\t\t%d\t%c\t%ld\n",
171 scrobbler_entry
.artist
,
172 scrobbler_entry
.album
?scrobbler_entry
.album
:"",
173 scrobbler_entry
.title
,
174 (int)scrobbler_entry
.length
/1000,
179 if ( ret
>= SCROBBLER_CACHE_LEN
)
181 logf("SCROBBLER: entry too long:");
182 logf("SCROBBLER: %s", scrobbler_entry
.path
);
185 if (!scrobbler_ata_callback
)
186 scrobbler_ata_callback
= register_ata_idle_func(scrobbler_flush_callback
);
191 void scrobbler_change_event(struct mp3entry
*id
)
193 /* add entry using the previous scrobbler_entry and timestamp */
195 add_to_cache(audio_prev_elapsed());
197 /* check if track was resumed > %50 played
198 check for blank artist or track name */
199 if ((id
->elapsed
> (id
->length
/2)) ||
200 (!id
->artist
) || (!id
->title
) )
203 logf("SCROBBLER: skipping file %s", id
->path
);
207 logf("SCROBBLER: add pending");
208 copy_mp3entry(&scrobbler_entry
, id
);
210 timestamp
= mktime(get_time());
218 int scrobbler_init(void)
220 logf("SCROBBLER: init %d", global_settings
.audioscrobbler
);
222 if(!global_settings
.audioscrobbler
)
225 scrobbler_cache
= buffer_alloc(SCROBBLER_MAX_CACHE
*SCROBBLER_CACHE_LEN
);
227 #if CONFIG_CODEC == SWCODEC
228 playback_add_event(PLAYBACK_EVENT_TRACK_CHANGE
, scrobbler_change_event
);
230 audio_set_track_changed_event(&scrobbler_change_event
);
234 scrobbler_initialised
= true;
239 void scrobbler_flush_cache(void)
241 if (scrobbler_initialised
)
243 /* Add any pending entries to the cache */
245 add_to_cache(audio_prev_elapsed());
247 /* Write the cache to disk if needed */
255 void scrobbler_shutdown(void)
258 if (scrobbler_ata_callback
)
259 unregister_ata_idle_func(scrobbler_flush_callback
, false);
262 scrobbler_flush_cache();
264 if (scrobbler_initialised
)
266 #if CONFIG_CODEC == SWCODEC
267 playback_remove_event(PLAYBACK_EVENT_TRACK_CHANGE
, scrobbler_change_event
);
269 audio_set_track_changed_event(NULL
);
271 scrobbler_initialised
= false;
275 void scrobbler_poweroff(void)
277 if (scrobbler_initialised
&& pending
)
279 if ( audio_status() )
280 add_to_cache(audio_current_track()->elapsed
);
282 add_to_cache(audio_prev_elapsed());
284 /* scrobbler_shutdown is called later, the cache will be written
285 * make sure the final track isn't added twice when that happens */
290 bool scrobbler_is_enabled(void)
292 return scrobbler_initialised
;