2006-10-19 09:42:58 +00:00
|
|
|
/***************************************************************************
|
|
|
|
* __________ __ ___.
|
|
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
|
|
* \/ \/ \/ \/ \/
|
|
|
|
* $Id$
|
|
|
|
*
|
|
|
|
* Copyright (C) 2006 Robert Keevil
|
|
|
|
*
|
|
|
|
* All files in this archive are subject to the GNU General Public License.
|
|
|
|
* See the file COPYING in the source tree root for full license agreement.
|
|
|
|
*
|
|
|
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
|
|
|
* KIND, either express or implied.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
/*
|
|
|
|
Audioscrobbler spec at:
|
|
|
|
http://www.audioscrobbler.net/wiki/Portable_Player_Logging
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "file.h"
|
|
|
|
#include "sprintf.h"
|
|
|
|
#include "playback.h"
|
|
|
|
#include "logf.h"
|
|
|
|
#include "id3.h"
|
|
|
|
#include "kernel.h"
|
|
|
|
#include "audio.h"
|
|
|
|
#include "buffer.h"
|
|
|
|
#include "settings.h"
|
2007-02-11 05:34:14 +00:00
|
|
|
#include "ata_idle_notify.h"
|
2006-10-19 09:42:58 +00:00
|
|
|
|
2007-03-16 23:47:03 +00:00
|
|
|
#if CONFIG_RTC
|
2006-10-19 09:42:58 +00:00
|
|
|
#include "time.h"
|
|
|
|
#include "timefuncs.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "scrobbler.h"
|
|
|
|
|
|
|
|
#define SCROBBLER_VERSION "1.0"
|
|
|
|
|
2007-03-16 23:47:03 +00:00
|
|
|
#if CONFIG_RTC
|
2006-10-19 09:42:58 +00:00
|
|
|
#define SCROBBLER_FILE "/.scrobbler.log"
|
|
|
|
#else
|
|
|
|
#define SCROBBLER_FILE "/.scrobbler-timeless.log"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* increment this on any code change that effects output */
|
2006-10-19 10:25:10 +00:00
|
|
|
#define SCROBBLER_REVISION " $Revision$"
|
2006-10-19 09:42:58 +00:00
|
|
|
|
|
|
|
#define SCROBBLER_MAX_CACHE 32
|
|
|
|
/* longest entry I've had is 323, add a safety margin */
|
|
|
|
#define SCROBBLER_CACHE_LEN 512
|
|
|
|
|
|
|
|
static char* scrobbler_cache;
|
|
|
|
|
|
|
|
static int scrobbler_fd = -1;
|
|
|
|
static int cache_pos;
|
|
|
|
static struct mp3entry scrobbler_entry;
|
|
|
|
static bool pending = false;
|
|
|
|
static bool scrobbler_initialised = false;
|
2007-02-11 05:34:14 +00:00
|
|
|
static bool scrobbler_ata_callback = false;
|
2007-03-16 23:47:03 +00:00
|
|
|
#if CONFIG_RTC
|
2006-10-19 09:42:58 +00:00
|
|
|
static time_t timestamp;
|
|
|
|
#else
|
|
|
|
static unsigned long timestamp;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Crude work-around for Archos Sims - return a set amount */
|
|
|
|
#if (CONFIG_CODEC != SWCODEC) && defined(SIMULATOR)
|
|
|
|
unsigned long audio_prev_elapsed(void)
|
|
|
|
{
|
|
|
|
return 120000;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static void write_cache(void)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2007-02-11 05:34:14 +00:00
|
|
|
scrobbler_ata_callback = false;
|
|
|
|
|
2006-10-19 09:42:58 +00:00
|
|
|
/* If the file doesn't exist, create it.
|
|
|
|
Check at each write since file may be deleted at any time */
|
|
|
|
scrobbler_fd = open(SCROBBLER_FILE, O_RDONLY);
|
|
|
|
if(scrobbler_fd < 0)
|
|
|
|
{
|
|
|
|
scrobbler_fd = open(SCROBBLER_FILE, O_RDWR | O_CREAT);
|
|
|
|
if(scrobbler_fd >= 0)
|
|
|
|
{
|
|
|
|
fdprintf(scrobbler_fd, "#AUDIOSCROBBLER/%s\n", SCROBBLER_VERSION);
|
|
|
|
fdprintf(scrobbler_fd, "#TZ/UNKNOWN\n");
|
2007-03-16 23:47:03 +00:00
|
|
|
#if CONFIG_RTC
|
2006-10-19 10:25:10 +00:00
|
|
|
fdprintf(scrobbler_fd,
|
|
|
|
"#CLIENT/Rockbox " TARGET_NAME SCROBBLER_REVISION "\n");
|
2006-10-19 09:42:58 +00:00
|
|
|
#else
|
2006-10-19 10:25:10 +00:00
|
|
|
fdprintf(scrobbler_fd,
|
|
|
|
"#CLIENT/Rockbox " TARGET_NAME SCROBBLER_REVISION " Timeless\n");
|
2006-10-19 09:42:58 +00:00
|
|
|
#endif
|
|
|
|
close(scrobbler_fd);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
logf("SCROBBLER: cannot create log file");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
close(scrobbler_fd);
|
|
|
|
scrobbler_fd = -1;
|
|
|
|
|
|
|
|
/* write the cache entries */
|
|
|
|
scrobbler_fd = open(SCROBBLER_FILE, O_WRONLY | O_APPEND);
|
|
|
|
if(scrobbler_fd >= 0)
|
|
|
|
{
|
|
|
|
logf("SCROBBLER: writing %d entries", cache_pos);
|
|
|
|
|
|
|
|
for ( i=0; i < cache_pos; i++ )
|
|
|
|
{
|
|
|
|
logf("SCROBBLER: write %d", i);
|
|
|
|
fdprintf(scrobbler_fd, "%s", scrobbler_cache+(SCROBBLER_CACHE_LEN*i));
|
|
|
|
}
|
|
|
|
close(scrobbler_fd);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
logf("SCROBBLER: error writing file");
|
|
|
|
}
|
|
|
|
|
|
|
|
/* clear even if unsuccessful - don't want to overflow the buffer */
|
|
|
|
cache_pos = 0;
|
|
|
|
scrobbler_fd = -1;
|
|
|
|
}
|
|
|
|
|
2007-02-11 05:34:14 +00:00
|
|
|
static bool scrobbler_flush_callback(void)
|
|
|
|
{
|
|
|
|
if (scrobbler_initialised && cache_pos)
|
|
|
|
write_cache();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2007-06-24 18:46:04 +00:00
|
|
|
static void add_to_cache(unsigned long play_length)
|
2006-10-19 09:42:58 +00:00
|
|
|
{
|
|
|
|
if ( cache_pos >= SCROBBLER_MAX_CACHE )
|
|
|
|
write_cache();
|
|
|
|
|
|
|
|
int ret;
|
|
|
|
char rating = 'S'; /* Skipped */
|
|
|
|
|
|
|
|
logf("SCROBBLER: add_to_cache[%d]", cache_pos);
|
|
|
|
|
2007-06-24 18:46:04 +00:00
|
|
|
if ( play_length > (scrobbler_entry.length/2) )
|
2006-10-19 09:42:58 +00:00
|
|
|
rating = 'L'; /* Listened */
|
|
|
|
|
|
|
|
if (scrobbler_entry.tracknum > 0)
|
|
|
|
{
|
|
|
|
ret = snprintf(scrobbler_cache+(SCROBBLER_CACHE_LEN*cache_pos),
|
|
|
|
SCROBBLER_CACHE_LEN,
|
|
|
|
"%s\t%s\t%s\t%d\t%d\t%c\t%ld\n",
|
|
|
|
scrobbler_entry.artist,
|
|
|
|
scrobbler_entry.album?scrobbler_entry.album:"",
|
|
|
|
scrobbler_entry.title,
|
|
|
|
scrobbler_entry.tracknum,
|
|
|
|
(int)scrobbler_entry.length/1000,
|
|
|
|
rating,
|
|
|
|
(long)timestamp);
|
|
|
|
} else {
|
|
|
|
ret = snprintf(scrobbler_cache+(SCROBBLER_CACHE_LEN*cache_pos),
|
|
|
|
SCROBBLER_CACHE_LEN,
|
|
|
|
"%s\t%s\t%s\t\t%d\t%c\t%ld\n",
|
|
|
|
scrobbler_entry.artist,
|
|
|
|
scrobbler_entry.album?scrobbler_entry.album:"",
|
|
|
|
scrobbler_entry.title,
|
|
|
|
(int)scrobbler_entry.length/1000,
|
|
|
|
rating,
|
|
|
|
(long)timestamp);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( ret >= SCROBBLER_CACHE_LEN )
|
|
|
|
{
|
|
|
|
logf("SCROBBLER: entry too long:");
|
|
|
|
logf("SCROBBLER: %s", scrobbler_entry.path);
|
2007-02-11 05:34:14 +00:00
|
|
|
} else {
|
2006-10-19 09:42:58 +00:00
|
|
|
cache_pos++;
|
2007-02-11 05:34:14 +00:00
|
|
|
if (!scrobbler_ata_callback)
|
|
|
|
scrobbler_ata_callback = register_ata_idle_func(scrobbler_flush_callback);
|
|
|
|
}
|
|
|
|
|
2006-10-19 09:42:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void scrobbler_change_event(struct mp3entry *id)
|
|
|
|
{
|
|
|
|
/* add entry using the previous scrobbler_entry and timestamp */
|
|
|
|
if (pending)
|
2007-06-24 18:46:04 +00:00
|
|
|
add_to_cache(audio_prev_elapsed());
|
2006-10-19 09:42:58 +00:00
|
|
|
|
|
|
|
/* check if track was resumed > %50 played
|
|
|
|
check for blank artist or track name */
|
|
|
|
if ((id->elapsed > (id->length/2)) ||
|
|
|
|
(!id->artist ) || (!id->title ) )
|
|
|
|
{
|
|
|
|
pending = false;
|
|
|
|
logf("SCROBBLER: skipping file %s", id->path);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
logf("SCROBBLER: add pending");
|
|
|
|
copy_mp3entry(&scrobbler_entry, id);
|
2007-03-16 23:47:03 +00:00
|
|
|
#if CONFIG_RTC
|
2006-10-19 09:42:58 +00:00
|
|
|
timestamp = mktime(get_time());
|
|
|
|
#else
|
|
|
|
timestamp = 0;
|
|
|
|
#endif
|
|
|
|
pending = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int scrobbler_init(void)
|
|
|
|
{
|
|
|
|
logf("SCROBBLER: init %d", global_settings.audioscrobbler);
|
2007-06-24 18:46:04 +00:00
|
|
|
|
2006-10-19 09:42:58 +00:00
|
|
|
if(!global_settings.audioscrobbler)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
scrobbler_cache = buffer_alloc(SCROBBLER_MAX_CACHE*SCROBBLER_CACHE_LEN);
|
|
|
|
|
2008-03-08 00:02:51 +00:00
|
|
|
#if CONFIG_CODEC == SWCODEC
|
2008-03-07 22:56:51 +00:00
|
|
|
playback_add_event(PLAYBACK_EVENT_TRACK_CHANGE, scrobbler_change_event);
|
2008-03-08 00:02:51 +00:00
|
|
|
#else
|
|
|
|
audio_set_track_changed_event(&scrobbler_change_event);
|
|
|
|
#endif
|
2006-10-19 09:42:58 +00:00
|
|
|
cache_pos = 0;
|
|
|
|
pending = false;
|
|
|
|
scrobbler_initialised = true;
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void scrobbler_flush_cache(void)
|
|
|
|
{
|
|
|
|
if (scrobbler_initialised)
|
|
|
|
{
|
|
|
|
/* Add any pending entries to the cache */
|
|
|
|
if(pending)
|
2007-06-24 18:46:04 +00:00
|
|
|
add_to_cache(audio_prev_elapsed());
|
|
|
|
|
2006-10-19 09:42:58 +00:00
|
|
|
/* Write the cache to disk if needed */
|
|
|
|
if (cache_pos)
|
|
|
|
write_cache();
|
|
|
|
|
|
|
|
pending = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void scrobbler_shutdown(void)
|
|
|
|
{
|
2007-02-11 05:34:14 +00:00
|
|
|
#ifndef SIMULATOR
|
|
|
|
if (scrobbler_ata_callback)
|
|
|
|
unregister_ata_idle_func(scrobbler_flush_callback, false);
|
|
|
|
#endif
|
|
|
|
|
2006-10-19 09:42:58 +00:00
|
|
|
scrobbler_flush_cache();
|
2007-06-24 18:46:04 +00:00
|
|
|
|
2006-10-19 09:42:58 +00:00
|
|
|
if (scrobbler_initialised)
|
|
|
|
{
|
2008-03-08 00:02:51 +00:00
|
|
|
#if CONFIG_CODEC == SWCODEC
|
2008-03-07 22:56:51 +00:00
|
|
|
playback_remove_event(PLAYBACK_EVENT_TRACK_CHANGE, scrobbler_change_event);
|
2008-03-08 00:02:51 +00:00
|
|
|
#else
|
|
|
|
audio_set_track_changed_event(NULL);
|
|
|
|
#endif
|
2006-10-19 09:42:58 +00:00
|
|
|
scrobbler_initialised = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-06-24 18:46:04 +00:00
|
|
|
void scrobbler_poweroff(void)
|
|
|
|
{
|
|
|
|
if (scrobbler_initialised && pending)
|
|
|
|
{
|
|
|
|
if ( audio_status() )
|
|
|
|
add_to_cache(audio_current_track()->elapsed);
|
|
|
|
else
|
|
|
|
add_to_cache(audio_prev_elapsed());
|
|
|
|
|
|
|
|
/* scrobbler_shutdown is called later, the cache will be written
|
|
|
|
* make sure the final track isn't added twice when that happens */
|
|
|
|
pending = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-10-19 09:42:58 +00:00
|
|
|
bool scrobbler_is_enabled(void)
|
|
|
|
{
|
|
|
|
return scrobbler_initialised;
|
|
|
|
}
|