D2: Keymap improvements for pitchscreen, quickscreen and context menu. (note: quicksc...
[Rockbox.git] / apps / scrobbler.c
bloba4dd2847ec3fd02c75a8462395e709eda0f449d5
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
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
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 #include "ata_idle_notify.h"
34 #include "misc.h"
36 #if CONFIG_RTC
37 #include "time.h"
38 #include "timefuncs.h"
39 #endif
41 #include "scrobbler.h"
43 #define SCROBBLER_VERSION "1.0"
45 #if CONFIG_RTC
46 #define SCROBBLER_FILE "/.scrobbler.log"
47 #else
48 #define SCROBBLER_FILE "/.scrobbler-timeless.log"
49 #endif
51 /* increment this on any code change that effects output */
52 #define SCROBBLER_REVISION " $Revision$"
54 #define SCROBBLER_MAX_CACHE 32
55 /* longest entry I've had is 323, add a safety margin */
56 #define SCROBBLER_CACHE_LEN 512
58 static char* scrobbler_cache;
60 static int cache_pos;
61 static struct mp3entry scrobbler_entry;
62 static bool pending = false;
63 static bool scrobbler_initialised = false;
64 #if CONFIG_RTC
65 static time_t timestamp;
66 #else
67 static unsigned long timestamp;
68 #endif
70 /* Crude work-around for Archos Sims - return a set amount */
71 #if (CONFIG_CODEC != SWCODEC) && defined(SIMULATOR)
72 unsigned long audio_prev_elapsed(void)
74 return 120000;
76 #endif
78 static void write_cache(void)
80 int i;
81 int fd;
83 /* If the file doesn't exist, create it.
84 Check at each write since file may be deleted at any time */
85 if(!file_exists(SCROBBLER_FILE))
87 fd = open(SCROBBLER_FILE, O_RDWR | O_CREAT);
88 if(fd >= 0)
90 fdprintf(fd, "#AUDIOSCROBBLER/" SCROBBLER_VERSION "\n"
91 "#TZ/UNKNOWN\n"
92 #if CONFIG_RTC
93 "#CLIENT/Rockbox " TARGET_NAME SCROBBLER_REVISION "\n");
94 #else
95 "#CLIENT/Rockbox " TARGET_NAME SCROBBLER_REVISION " Timeless\n");
96 #endif
98 close(fd);
100 else
102 logf("SCROBBLER: cannot create log file");
106 /* write the cache entries */
107 fd = open(SCROBBLER_FILE, O_WRONLY | O_APPEND);
108 if(fd >= 0)
110 logf("SCROBBLER: writing %d entries", cache_pos);
112 for ( i=0; i < cache_pos; i++ )
114 logf("SCROBBLER: write %d", i);
115 fdprintf(fd, "%s", scrobbler_cache+(SCROBBLER_CACHE_LEN*i));
117 close(fd);
119 else
121 logf("SCROBBLER: error writing file");
124 /* clear even if unsuccessful - don't want to overflow the buffer */
125 cache_pos = 0;
128 static bool scrobbler_flush_callback(void)
130 if (scrobbler_initialised && cache_pos)
131 write_cache();
132 return true;
135 static void add_to_cache(unsigned long play_length)
137 if ( cache_pos >= SCROBBLER_MAX_CACHE )
138 write_cache();
140 int ret;
141 char rating = 'S'; /* Skipped */
143 logf("SCROBBLER: add_to_cache[%d]", cache_pos);
145 if ( play_length > (scrobbler_entry.length/2) )
146 rating = 'L'; /* Listened */
148 if (scrobbler_entry.tracknum > 0)
150 ret = snprintf(scrobbler_cache+(SCROBBLER_CACHE_LEN*cache_pos),
151 SCROBBLER_CACHE_LEN,
152 "%s\t%s\t%s\t%d\t%d\t%c\t%ld\n",
153 scrobbler_entry.artist,
154 scrobbler_entry.album?scrobbler_entry.album:"",
155 scrobbler_entry.title,
156 scrobbler_entry.tracknum,
157 (int)scrobbler_entry.length/1000,
158 rating,
159 (long)timestamp);
160 } else {
161 ret = snprintf(scrobbler_cache+(SCROBBLER_CACHE_LEN*cache_pos),
162 SCROBBLER_CACHE_LEN,
163 "%s\t%s\t%s\t\t%d\t%c\t%ld\n",
164 scrobbler_entry.artist,
165 scrobbler_entry.album?scrobbler_entry.album:"",
166 scrobbler_entry.title,
167 (int)scrobbler_entry.length/1000,
168 rating,
169 (long)timestamp);
172 if ( ret >= SCROBBLER_CACHE_LEN )
174 logf("SCROBBLER: entry too long:");
175 logf("SCROBBLER: %s", scrobbler_entry.path);
176 } else {
177 cache_pos++;
178 register_ata_idle_func(scrobbler_flush_callback);
183 void scrobbler_change_event(struct mp3entry *id)
185 /* add entry using the previous scrobbler_entry and timestamp */
186 if (pending)
187 add_to_cache(audio_prev_elapsed());
189 /* check if track was resumed > %50 played
190 check for blank artist or track name */
191 if ((id->elapsed > (id->length/2)) ||
192 (!id->artist ) || (!id->title ) )
194 pending = false;
195 logf("SCROBBLER: skipping file %s", id->path);
197 else
199 logf("SCROBBLER: add pending");
200 copy_mp3entry(&scrobbler_entry, id);
201 #if CONFIG_RTC
202 timestamp = mktime(get_time());
203 #else
204 timestamp = 0;
205 #endif
206 pending = true;
210 int scrobbler_init(void)
212 logf("SCROBBLER: init %d", global_settings.audioscrobbler);
214 if(!global_settings.audioscrobbler)
215 return -1;
217 scrobbler_cache = buffer_alloc(SCROBBLER_MAX_CACHE*SCROBBLER_CACHE_LEN);
219 add_event(PLAYBACK_EVENT_TRACK_CHANGE, false, scrobbler_change_event);
220 cache_pos = 0;
221 pending = false;
222 scrobbler_initialised = true;
224 return 1;
227 void scrobbler_flush_cache(void)
229 if (scrobbler_initialised)
231 /* Add any pending entries to the cache */
232 if(pending)
233 add_to_cache(audio_prev_elapsed());
235 /* Write the cache to disk if needed */
236 if (cache_pos)
237 write_cache();
239 pending = false;
243 void scrobbler_shutdown(void)
245 scrobbler_flush_cache();
247 if (scrobbler_initialised)
249 remove_event(PLAYBACK_EVENT_TRACK_CHANGE, scrobbler_change_event);
250 scrobbler_initialised = false;
254 void scrobbler_poweroff(void)
256 if (scrobbler_initialised && pending)
258 if ( audio_status() )
259 add_to_cache(audio_current_track()->elapsed);
260 else
261 add_to_cache(audio_prev_elapsed());
263 /* scrobbler_shutdown is called later, the cache will be written
264 * make sure the final track isn't added twice when that happens */
265 pending = false;
269 bool scrobbler_is_enabled(void)
271 return scrobbler_initialised;