Build doom on clipv2 and clip+
[kugel-rb.git] / apps / scrobbler.c
blob8d9f694ec2a19e636b9893fdd7f9a1bc090b5ac2
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
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
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 "buffer.h"
34 #include "settings.h"
35 #include "ata_idle_notify.h"
36 #include "misc.h"
37 #include "appevents.h"
39 #if CONFIG_RTC
40 #include "time.h"
41 #include "timefuncs.h"
42 #endif
44 #include "scrobbler.h"
46 #define SCROBBLER_VERSION "1.1"
48 #if CONFIG_RTC
49 #define SCROBBLER_FILE "/.scrobbler.log"
50 #else
51 #define SCROBBLER_FILE "/.scrobbler-timeless.log"
52 #endif
54 /* increment this on any code change that effects output */
55 #define SCROBBLER_REVISION " $Revision$"
57 #define SCROBBLER_MAX_CACHE 32
58 /* longest entry I've had is 323, add a safety margin */
59 #define SCROBBLER_CACHE_LEN 512
61 static char* scrobbler_cache;
63 static int cache_pos;
64 static struct mp3entry scrobbler_entry;
65 static bool pending = false;
66 static bool scrobbler_initialised = false;
67 #if CONFIG_RTC
68 static time_t timestamp;
69 #else
70 static unsigned long timestamp;
71 #endif
73 /* Crude work-around for Archos Sims - return a set amount */
74 #if (CONFIG_CODEC != SWCODEC) && (CONFIG_PLATFORM & PLATFORM_HOSTED)
75 unsigned long audio_prev_elapsed(void)
77 return 120000;
79 #endif
81 static void write_cache(void)
83 int i;
84 int fd;
86 /* If the file doesn't exist, create it.
87 Check at each write since file may be deleted at any time */
88 if(!file_exists(SCROBBLER_FILE))
90 fd = open(SCROBBLER_FILE, O_RDWR | O_CREAT, 0666);
91 if(fd >= 0)
93 fdprintf(fd, "#AUDIOSCROBBLER/" SCROBBLER_VERSION "\n"
94 "#TZ/UNKNOWN\n"
95 #if CONFIG_RTC
96 "#CLIENT/Rockbox " TARGET_NAME SCROBBLER_REVISION "\n");
97 #else
98 "#CLIENT/Rockbox " TARGET_NAME SCROBBLER_REVISION " Timeless\n");
99 #endif
101 close(fd);
103 else
105 logf("SCROBBLER: cannot create log file");
109 /* write the cache entries */
110 fd = open(SCROBBLER_FILE, O_WRONLY | O_APPEND);
111 if(fd >= 0)
113 logf("SCROBBLER: writing %d entries", cache_pos);
115 for ( i=0; i < cache_pos; i++ )
117 logf("SCROBBLER: write %d", i);
118 fdprintf(fd, "%s", scrobbler_cache+(SCROBBLER_CACHE_LEN*i));
120 close(fd);
122 else
124 logf("SCROBBLER: error writing file");
127 /* clear even if unsuccessful - don't want to overflow the buffer */
128 cache_pos = 0;
131 static void scrobbler_flush_callback(void *data)
133 (void)data;
134 if (scrobbler_initialised && cache_pos)
135 write_cache();
138 static void add_to_cache(unsigned long play_length)
140 if ( cache_pos >= SCROBBLER_MAX_CACHE )
141 write_cache();
143 int ret;
144 char rating = 'S'; /* Skipped */
146 logf("SCROBBLER: add_to_cache[%d]", cache_pos);
148 if ( play_length > (scrobbler_entry.length/2) )
149 rating = 'L'; /* Listened */
151 if (scrobbler_entry.tracknum > 0)
153 ret = snprintf(scrobbler_cache+(SCROBBLER_CACHE_LEN*cache_pos),
154 SCROBBLER_CACHE_LEN,
155 "%s\t%s\t%s\t%d\t%d\t%c\t%ld\t%s\n",
156 scrobbler_entry.artist,
157 scrobbler_entry.album?scrobbler_entry.album:"",
158 scrobbler_entry.title,
159 scrobbler_entry.tracknum,
160 (int)scrobbler_entry.length/1000,
161 rating,
162 (long)timestamp,
163 scrobbler_entry.mb_track_id?scrobbler_entry.mb_track_id:"");
164 } else {
165 ret = snprintf(scrobbler_cache+(SCROBBLER_CACHE_LEN*cache_pos),
166 SCROBBLER_CACHE_LEN,
167 "%s\t%s\t%s\t\t%d\t%c\t%ld\t%s\n",
168 scrobbler_entry.artist,
169 scrobbler_entry.album?scrobbler_entry.album:"",
170 scrobbler_entry.title,
171 (int)scrobbler_entry.length/1000,
172 rating,
173 (long)timestamp,
174 scrobbler_entry.mb_track_id?scrobbler_entry.mb_track_id:"");
177 if ( ret >= SCROBBLER_CACHE_LEN )
179 logf("SCROBBLER: entry too long:");
180 logf("SCROBBLER: %s", scrobbler_entry.path);
181 } else {
182 cache_pos++;
183 register_storage_idle_func(scrobbler_flush_callback);
188 static void scrobbler_change_event(void *data)
190 struct mp3entry *id = (struct mp3entry*)data;
191 /* add entry using the previous scrobbler_entry and timestamp */
192 if (pending)
193 add_to_cache(audio_prev_elapsed());
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 ) )
200 pending = false;
201 logf("SCROBBLER: skipping file %s", id->path);
203 else
205 logf("SCROBBLER: add pending");
206 copy_mp3entry(&scrobbler_entry, id);
207 #if CONFIG_RTC
208 timestamp = mktime(get_time());
209 #else
210 timestamp = 0;
211 #endif
212 pending = true;
216 int scrobbler_init(void)
218 logf("SCROBBLER: init %d", global_settings.audioscrobbler);
220 if(!global_settings.audioscrobbler)
221 return -1;
223 scrobbler_cache = buffer_alloc(SCROBBLER_MAX_CACHE*SCROBBLER_CACHE_LEN);
225 add_event(PLAYBACK_EVENT_TRACK_CHANGE, false, scrobbler_change_event);
226 cache_pos = 0;
227 pending = false;
228 scrobbler_initialised = true;
230 return 1;
233 void scrobbler_flush_cache(void)
235 if (scrobbler_initialised)
237 /* Add any pending entries to the cache */
238 if(pending)
239 add_to_cache(audio_prev_elapsed());
241 /* Write the cache to disk if needed */
242 if (cache_pos)
243 write_cache();
245 pending = false;
249 void scrobbler_shutdown(void)
251 scrobbler_flush_cache();
253 if (scrobbler_initialised)
255 remove_event(PLAYBACK_EVENT_TRACK_CHANGE, scrobbler_change_event);
256 scrobbler_initialised = false;
260 void scrobbler_poweroff(void)
262 if (scrobbler_initialised && pending)
264 if ( audio_status() )
265 add_to_cache(audio_current_track()->elapsed);
266 else
267 add_to_cache(audio_prev_elapsed());
269 /* scrobbler_shutdown is called later, the cache will be written
270 * make sure the final track isn't added twice when that happens */
271 pending = false;
275 bool scrobbler_is_enabled(void)
277 return scrobbler_initialised;