Move declaration of button_int and clickwheel_int to the proper header file instead...
[Rockbox.git] / apps / scrobbler.c
blob7af45a0c8fe8dc1e937d583caaf406a94363f8db
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2006 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 "file.h"
27 #include "sprintf.h"
28 #include "playback.h"
29 #include "logf.h"
30 #include "id3.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"
38 #if CONFIG_RTC
39 #include "time.h"
40 #include "timefuncs.h"
41 #endif
43 #include "scrobbler.h"
45 #define SCROBBLER_VERSION "1.0"
47 #if CONFIG_RTC
48 #define SCROBBLER_FILE "/.scrobbler.log"
49 #else
50 #define SCROBBLER_FILE "/.scrobbler-timeless.log"
51 #endif
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;
62 static int cache_pos;
63 static struct mp3entry scrobbler_entry;
64 static bool pending = false;
65 static bool scrobbler_initialised = false;
66 #if CONFIG_RTC
67 static time_t timestamp;
68 #else
69 static unsigned long timestamp;
70 #endif
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)
76 return 120000;
78 #endif
80 static void write_cache(void)
82 int i;
83 int fd;
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);
90 if(fd >= 0)
92 fdprintf(fd, "#AUDIOSCROBBLER/" SCROBBLER_VERSION "\n"
93 "#TZ/UNKNOWN\n"
94 #if CONFIG_RTC
95 "#CLIENT/Rockbox " TARGET_NAME SCROBBLER_REVISION "\n");
96 #else
97 "#CLIENT/Rockbox " TARGET_NAME SCROBBLER_REVISION " Timeless\n");
98 #endif
100 close(fd);
102 else
104 logf("SCROBBLER: cannot create log file");
108 /* write the cache entries */
109 fd = open(SCROBBLER_FILE, O_WRONLY | O_APPEND);
110 if(fd >= 0)
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));
119 close(fd);
121 else
123 logf("SCROBBLER: error writing file");
126 /* clear even if unsuccessful - don't want to overflow the buffer */
127 cache_pos = 0;
130 static bool scrobbler_flush_callback(void)
132 if (scrobbler_initialised && cache_pos)
133 write_cache();
134 return true;
137 static void add_to_cache(unsigned long play_length)
139 if ( cache_pos >= SCROBBLER_MAX_CACHE )
140 write_cache();
142 int ret;
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),
153 SCROBBLER_CACHE_LEN,
154 "%s\t%s\t%s\t%d\t%d\t%c\t%ld\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,
160 rating,
161 (long)timestamp);
162 } else {
163 ret = snprintf(scrobbler_cache+(SCROBBLER_CACHE_LEN*cache_pos),
164 SCROBBLER_CACHE_LEN,
165 "%s\t%s\t%s\t\t%d\t%c\t%ld\n",
166 scrobbler_entry.artist,
167 scrobbler_entry.album?scrobbler_entry.album:"",
168 scrobbler_entry.title,
169 (int)scrobbler_entry.length/1000,
170 rating,
171 (long)timestamp);
174 if ( ret >= SCROBBLER_CACHE_LEN )
176 logf("SCROBBLER: entry too long:");
177 logf("SCROBBLER: %s", scrobbler_entry.path);
178 } else {
179 cache_pos++;
180 register_ata_idle_func(scrobbler_flush_callback);
185 void scrobbler_change_event(struct mp3entry *id)
187 /* add entry using the previous scrobbler_entry and timestamp */
188 if (pending)
189 add_to_cache(audio_prev_elapsed());
191 /* check if track was resumed > %50 played
192 check for blank artist or track name */
193 if ((id->elapsed > (id->length/2)) ||
194 (!id->artist ) || (!id->title ) )
196 pending = false;
197 logf("SCROBBLER: skipping file %s", id->path);
199 else
201 logf("SCROBBLER: add pending");
202 copy_mp3entry(&scrobbler_entry, id);
203 #if CONFIG_RTC
204 timestamp = mktime(get_time());
205 #else
206 timestamp = 0;
207 #endif
208 pending = true;
212 int scrobbler_init(void)
214 logf("SCROBBLER: init %d", global_settings.audioscrobbler);
216 if(!global_settings.audioscrobbler)
217 return -1;
219 scrobbler_cache = buffer_alloc(SCROBBLER_MAX_CACHE*SCROBBLER_CACHE_LEN);
221 add_event(PLAYBACK_EVENT_TRACK_CHANGE, false, scrobbler_change_event);
222 cache_pos = 0;
223 pending = false;
224 scrobbler_initialised = true;
226 return 1;
229 void scrobbler_flush_cache(void)
231 if (scrobbler_initialised)
233 /* Add any pending entries to the cache */
234 if(pending)
235 add_to_cache(audio_prev_elapsed());
237 /* Write the cache to disk if needed */
238 if (cache_pos)
239 write_cache();
241 pending = false;
245 void scrobbler_shutdown(void)
247 scrobbler_flush_cache();
249 if (scrobbler_initialised)
251 remove_event(PLAYBACK_EVENT_TRACK_CHANGE, scrobbler_change_event);
252 scrobbler_initialised = false;
256 void scrobbler_poweroff(void)
258 if (scrobbler_initialised && pending)
260 if ( audio_status() )
261 add_to_cache(audio_current_track()->elapsed);
262 else
263 add_to_cache(audio_prev_elapsed());
265 /* scrobbler_shutdown is called later, the cache will be written
266 * make sure the final track isn't added twice when that happens */
267 pending = false;
271 bool scrobbler_is_enabled(void)
273 return scrobbler_initialised;