summaryrefslogtreecommitdiff
path: root/apps/scrobbler.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/scrobbler.c')
-rwxr-xr-xapps/scrobbler.c263
1 files changed, 263 insertions, 0 deletions
diff --git a/apps/scrobbler.c b/apps/scrobbler.c
new file mode 100755
index 0000000000..68c5e9addf
--- /dev/null
+++ b/apps/scrobbler.c
@@ -0,0 +1,263 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2006 Robert Keevil
11 *
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.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 ****************************************************************************/
19/*
20Audioscrobbler spec at:
21http://www.audioscrobbler.net/wiki/Portable_Player_Logging
22*/
23
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
34#ifndef SIMULATOR
35#include "ata.h"
36#endif
37
38#ifdef CONFIG_RTC
39#include "time.h"
40#include "timefuncs.h"
41#endif
42
43#include "scrobbler.h"
44
45#define SCROBBLER_VERSION "1.0"
46
47#ifdef CONFIG_RTC
48#define SCROBBLER_FILE "/.scrobbler.log"
49#else
50#define SCROBBLER_FILE "/.scrobbler-timeless.log"
51#endif
52
53/* increment this on any code change that effects output */
54/* replace with CVS Revision keyword? */
55#define SCROBBLER_REVISION "1.0"
56
57#define SCROBBLER_MAX_CACHE 32
58/* longest entry I've had is 323, add a safety margin */
59#define SCROBBLER_CACHE_LEN 512
60
61static char* scrobbler_cache;
62
63static int scrobbler_fd = -1;
64static int cache_pos;
65static struct mp3entry scrobbler_entry;
66static bool pending = false;
67static bool scrobbler_initialised = false;
68#ifdef CONFIG_RTC
69static time_t timestamp;
70#else
71static unsigned long timestamp;
72#endif
73
74/* Crude work-around for Archos Sims - return a set amount */
75#if (CONFIG_CODEC != SWCODEC) && defined(SIMULATOR)
76unsigned long audio_prev_elapsed(void)
77{
78 return 120000;
79}
80#endif
81
82static void write_cache(void)
83{
84 int i;
85
86 /* If the file doesn't exist, create it.
87 Check at each write since file may be deleted at any time */
88 scrobbler_fd = open(SCROBBLER_FILE, O_RDONLY);
89 if(scrobbler_fd < 0)
90 {
91 scrobbler_fd = open(SCROBBLER_FILE, O_RDWR | O_CREAT);
92 if(scrobbler_fd >= 0)
93 {
94 fdprintf(scrobbler_fd, "#AUDIOSCROBBLER/%s\n", SCROBBLER_VERSION);
95 fdprintf(scrobbler_fd, "#TZ/UNKNOWN\n");
96#ifdef CONFIG_RTC
97 fdprintf(scrobbler_fd, "#CLIENT/Rockbox %s %s\n",
98 TARGET_NAME, SCROBBLER_REVISION);
99#else
100 fdprintf(scrobbler_fd, "#CLIENT/Rockbox %s %s Timeless\n",
101 TARGET_NAME, SCROBBLER_REVISION);
102#endif
103 close(scrobbler_fd);
104 }
105 else
106 {
107 logf("SCROBBLER: cannot create log file");
108 }
109 }
110 close(scrobbler_fd);
111 scrobbler_fd = -1;
112
113 /* write the cache entries */
114 scrobbler_fd = open(SCROBBLER_FILE, O_WRONLY | O_APPEND);
115 if(scrobbler_fd >= 0)
116 {
117 logf("SCROBBLER: writing %d entries", cache_pos);
118
119 for ( i=0; i < cache_pos; i++ )
120 {
121 logf("SCROBBLER: write %d", i);
122 fdprintf(scrobbler_fd, "%s", scrobbler_cache+(SCROBBLER_CACHE_LEN*i));
123 }
124 close(scrobbler_fd);
125 }
126 else
127 {
128 logf("SCROBBLER: error writing file");
129 }
130
131 /* clear even if unsuccessful - don't want to overflow the buffer */
132 cache_pos = 0;
133 scrobbler_fd = -1;
134}
135
136static void add_to_cache(void)
137{
138/* using HAVE_MMC to check for Ondios - anything better to use? */
139#ifndef SIMULATOR
140#if defined(IPOD_NANO) || defined(HAVE_MMC)
141 if ( cache_pos >= SCROBBLER_MAX_CACHE )
142#else
143 if ( ( cache_pos >= SCROBBLER_MAX_CACHE ) || ( ata_disk_is_active() ) )
144#endif
145#endif /* !SIMULATOR */
146 write_cache();
147
148 int ret;
149 char rating = 'S'; /* Skipped */
150
151 logf("SCROBBLER: add_to_cache[%d]", cache_pos);
152
153 if ( audio_prev_elapsed() >
154 (scrobbler_entry.length/2) )
155 rating = 'L'; /* Listened */
156
157 if (scrobbler_entry.tracknum > 0)
158 {
159 ret = snprintf(scrobbler_cache+(SCROBBLER_CACHE_LEN*cache_pos),
160 SCROBBLER_CACHE_LEN,
161 "%s\t%s\t%s\t%d\t%d\t%c\t%ld\n",
162 scrobbler_entry.artist,
163 scrobbler_entry.album?scrobbler_entry.album:"",
164 scrobbler_entry.title,
165 scrobbler_entry.tracknum,
166 (int)scrobbler_entry.length/1000,
167 rating,
168 (long)timestamp);
169 } else {
170 ret = snprintf(scrobbler_cache+(SCROBBLER_CACHE_LEN*cache_pos),
171 SCROBBLER_CACHE_LEN,
172 "%s\t%s\t%s\t\t%d\t%c\t%ld\n",
173 scrobbler_entry.artist,
174 scrobbler_entry.album?scrobbler_entry.album:"",
175 scrobbler_entry.title,
176 (int)scrobbler_entry.length/1000,
177 rating,
178 (long)timestamp);
179 }
180
181 if ( ret >= SCROBBLER_CACHE_LEN )
182 {
183 logf("SCROBBLER: entry too long:");
184 logf("SCROBBLER: %s", scrobbler_entry.path);
185 } else
186 cache_pos++;
187}
188
189void scrobbler_change_event(struct mp3entry *id)
190{
191 /* add entry using the previous scrobbler_entry and timestamp */
192 if (pending)
193 add_to_cache();
194
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 ) )
199 {
200 pending = false;
201 logf("SCROBBLER: skipping file %s", id->path);
202 }
203 else
204 {
205 logf("SCROBBLER: add pending");
206 copy_mp3entry(&scrobbler_entry, id);
207#ifdef CONFIG_RTC
208 timestamp = mktime(get_time());
209#else
210 timestamp = 0;
211#endif
212 pending = true;
213 }
214}
215
216int scrobbler_init(void)
217{
218 logf("SCROBBLER: init %d", global_settings.audioscrobbler);
219
220 if(!global_settings.audioscrobbler)
221 return -1;
222
223 scrobbler_cache = buffer_alloc(SCROBBLER_MAX_CACHE*SCROBBLER_CACHE_LEN);
224
225 audio_set_track_changed_event(&scrobbler_change_event);
226 cache_pos = 0;
227 pending = false;
228 scrobbler_initialised = true;
229
230 return 1;
231}
232
233void scrobbler_flush_cache(void)
234{
235 if (scrobbler_initialised)
236 {
237 /* Add any pending entries to the cache */
238 if(pending)
239 add_to_cache();
240
241 /* Write the cache to disk if needed */
242 if (cache_pos)
243 write_cache();
244
245 pending = false;
246 }
247}
248
249void scrobbler_shutdown(void)
250{
251 scrobbler_flush_cache();
252
253 if (scrobbler_initialised)
254 {
255 audio_set_track_changed_event(NULL);
256 scrobbler_initialised = false;
257 }
258}
259
260bool scrobbler_is_enabled(void)
261{
262 return scrobbler_initialised;
263}