summaryrefslogtreecommitdiff
path: root/apps/scrobbler.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/scrobbler.c')
-rw-r--r--apps/scrobbler.c287
1 files changed, 0 insertions, 287 deletions
diff --git a/apps/scrobbler.c b/apps/scrobbler.c
deleted file mode 100644
index f5ccf4a61c..0000000000
--- a/apps/scrobbler.c
+++ /dev/null
@@ -1,287 +0,0 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2006-2008 Robert Keevil
11 *
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.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21/*
22Audioscrobbler spec at:
23http://www.audioscrobbler.net/wiki/Portable_Player_Logging
24*/
25
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 "core_alloc.h"
34#include "rbpaths.h"
35#include "ata_idle_notify.h"
36#include "pathfuncs.h"
37#include "appevents.h"
38#include "string-extra.h"
39#if CONFIG_RTC
40#include "time.h"
41#include "timefuncs.h"
42#endif
43
44#include "scrobbler.h"
45
46#define SCROBBLER_VERSION "1.1"
47
48/* increment this on any code change that effects output */
49#define SCROBBLER_REVISION " $Revision$"
50
51#define SCROBBLER_MAX_CACHE 32
52/* longest entry I've had is 323, add a safety margin */
53#define SCROBBLER_CACHE_LEN 512
54
55static bool scrobbler_initialised = false;
56static int scrobbler_cache = 0;
57static int cache_pos = 0;
58static bool pending = false;
59#if CONFIG_RTC
60static time_t timestamp;
61#define BASE_FILENAME HOME_DIR "/.scrobbler.log"
62#define HDR_STR_TIMELESS
63#define get_timestamp() ((long)timestamp)
64#define record_timestamp() ((void)(timestamp = mktime(get_time())))
65#else /* !CONFIG_RTC */
66#define HDR_STR_TIMELESS " Timeless"
67#define BASE_FILENAME ".scrobbler-timeless.log"
68#define get_timestamp() (0l)
69#define record_timestamp() ({})
70#endif /* CONFIG_RTC */
71
72static void get_scrobbler_filename(char *path, size_t size)
73{
74 int used;
75
76 used = snprintf(path, size, "/%s", BASE_FILENAME);
77
78 if (used >= (int)size)
79 {
80 logf("SCROBBLER: not enough buffer space for log file");
81 memset(path, 0, size);
82 }
83}
84
85static void write_cache(void)
86{
87 int i;
88 int fd;
89
90 char scrobbler_file[MAX_PATH];
91 get_scrobbler_filename(scrobbler_file, MAX_PATH);
92
93 /* If the file doesn't exist, create it.
94 Check at each write since file may be deleted at any time */
95 if(!file_exists(scrobbler_file))
96 {
97 fd = open(scrobbler_file, O_RDWR | O_CREAT, 0666);
98 if(fd >= 0)
99 {
100 fdprintf(fd, "#AUDIOSCROBBLER/" SCROBBLER_VERSION "\n"
101 "#TZ/UNKNOWN\n" "#CLIENT/Rockbox "
102 TARGET_NAME SCROBBLER_REVISION
103 HDR_STR_TIMELESS "\n");
104
105 close(fd);
106 }
107 else
108 {
109 logf("SCROBBLER: cannot create log file (%s)", scrobbler_file);
110 }
111 }
112
113 /* write the cache entries */
114 fd = open(scrobbler_file, O_WRONLY | O_APPEND);
115 if(fd >= 0)
116 {
117 logf("SCROBBLER: writing %d entries", cache_pos);
118 /* copy data to temporary storage in case data moves during I/O */
119 char temp_buf[SCROBBLER_CACHE_LEN];
120 for ( i=0; i < cache_pos; i++ )
121 {
122 logf("SCROBBLER: write %d", i);
123 char* scrobbler_buf = core_get_data(scrobbler_cache);
124 ssize_t len = strlcpy(temp_buf, scrobbler_buf+(SCROBBLER_CACHE_LEN*i),
125 sizeof(temp_buf));
126 if (write(fd, temp_buf, len) != len)
127 break;
128 }
129 close(fd);
130 }
131 else
132 {
133 logf("SCROBBLER: error writing file");
134 }
135
136 /* clear even if unsuccessful - don't want to overflow the buffer */
137 cache_pos = 0;
138}
139
140static void scrobbler_flush_callback(void)
141{
142 if (scrobbler_initialised && cache_pos)
143 write_cache();
144}
145
146static void add_to_cache(const struct mp3entry *id)
147{
148 if ( cache_pos >= SCROBBLER_MAX_CACHE )
149 write_cache();
150
151 char rating = 'S'; /* Skipped */
152 char* scrobbler_buf = core_get_data(scrobbler_cache);
153
154 logf("SCROBBLER: add_to_cache[%d]", cache_pos);
155
156 if (id->elapsed > id->length / 2)
157 rating = 'L'; /* Listened */
158
159 char tracknum[11] = { "" };
160
161 if (id->tracknum > 0)
162 snprintf(tracknum, sizeof (tracknum), "%d", id->tracknum);
163
164 int ret = snprintf(scrobbler_buf+(SCROBBLER_CACHE_LEN*cache_pos),
165 SCROBBLER_CACHE_LEN,
166 "%s\t%s\t%s\t%s\t%d\t%c\t%ld\t%s\n",
167 id->artist,
168 id->album ?: "",
169 id->title,
170 tracknum,
171 (int)(id->length / 1000),
172 rating,
173 get_timestamp(),
174 id->mb_track_id ?: "");
175
176 if ( ret >= SCROBBLER_CACHE_LEN )
177 {
178 logf("SCROBBLER: entry too long:");
179 logf("SCROBBLER: %s", id->path);
180 }
181 else
182 {
183 cache_pos++;
184 register_storage_idle_func(scrobbler_flush_callback);
185 }
186
187}
188
189static void scrobbler_change_event(unsigned short id, void *ev_data)
190{
191 (void)id;
192 struct mp3entry *id3 = ((struct track_event *)ev_data)->id3;
193
194 /* check if track was resumed > %50 played
195 check for blank artist or track name */
196 if (id3->elapsed > id3->length / 2 || !id3->artist || !id3->title)
197 {
198 pending = false;
199 logf("SCROBBLER: skipping file %s", id3->path);
200 }
201 else
202 {
203 logf("SCROBBLER: add pending");
204 record_timestamp();
205 pending = true;
206 }
207}
208
209static void scrobbler_finish_event(unsigned short id, void *data)
210{
211 (void)id;
212 struct track_event *te = (struct track_event *)data;
213
214 /* add entry using the currently ending track */
215 if (pending && (te->flags & TEF_CURRENT)
216 && !(te->flags & TEF_REWIND)
217 )
218 {
219 pending = false;
220 add_to_cache(te->id3);
221 }
222}
223
224int scrobbler_init(void)
225{
226 if (scrobbler_initialised)
227 return 1;
228
229 scrobbler_cache = core_alloc("scrobbler",
230 SCROBBLER_MAX_CACHE*SCROBBLER_CACHE_LEN);
231
232 if (scrobbler_cache <= 0)
233 {
234 logf("SCROOBLER: OOM");
235 return -1;
236 }
237
238 cache_pos = 0;
239 pending = false;
240
241 scrobbler_initialised = true;
242
243 add_event(PLAYBACK_EVENT_TRACK_CHANGE, scrobbler_change_event);
244 add_event(PLAYBACK_EVENT_TRACK_FINISH, scrobbler_finish_event);
245
246 return 1;
247}
248
249static void scrobbler_flush_cache(void)
250{
251 /* Add any pending entries to the cache */
252 if (pending)
253 {
254 pending = false;
255 if (audio_status())
256 add_to_cache(audio_current_track());
257 }
258
259 /* Write the cache to disk if needed */
260 if (cache_pos)
261 write_cache();
262}
263
264void scrobbler_shutdown(bool poweroff)
265{
266 if (!scrobbler_initialised)
267 return;
268
269 remove_event(PLAYBACK_EVENT_TRACK_CHANGE, scrobbler_change_event);
270 remove_event(PLAYBACK_EVENT_TRACK_FINISH, scrobbler_finish_event);
271
272 scrobbler_flush_cache();
273
274 if (!poweroff)
275 {
276 /* get rid of the buffer */
277 core_free(scrobbler_cache);
278 scrobbler_cache = 0;
279 }
280
281 scrobbler_initialised = false;
282}
283
284bool scrobbler_is_enabled(void)
285{
286 return scrobbler_initialised;
287}