summaryrefslogtreecommitdiff
path: root/apps
diff options
context:
space:
mode:
authorLinus Nielsen Feltzing <linus@haxx.se>2006-10-19 09:42:58 +0000
committerLinus Nielsen Feltzing <linus@haxx.se>2006-10-19 09:42:58 +0000
commitda153da0be485aa4b937d58ee209eda7fb342053 (patch)
tree3506b6bba4935fa252250b892d023203e54f3513 /apps
parent1645d32aa38bd61806125c8d6b1c44fa3ac0f3ca (diff)
downloadrockbox-da153da0be485aa4b937d58ee209eda7fb342053.zip
rockbox-da153da0be485aa4b937d58ee209eda7fb342053.tar.gz
rockbox-da153da0be485aa4b937d58ee209eda7fb342053.tar.bz2
rockbox-da153da0be485aa4b937d58ee209eda7fb342053.tar.xz
Patch #5166 by Robert Keevil - Last.fm logging
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@11269 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps')
-rw-r--r--apps/Makefile3
-rw-r--r--apps/SOURCES1
-rw-r--r--apps/lang/english.lang34
-rw-r--r--apps/main.c6
-rw-r--r--apps/misc.c2
-rw-r--r--apps/playback.c10
-rwxr-xr-xapps/scrobbler.c263
-rw-r--r--apps/scrobbler.h24
-rw-r--r--apps/settings.c2
-rw-r--r--apps/settings.h1
-rw-r--r--apps/settings_menu.c23
-rw-r--r--apps/tree.c3
12 files changed, 365 insertions, 7 deletions
diff --git a/apps/Makefile b/apps/Makefile
index 6214ce1..815e061 100644
--- a/apps/Makefile
+++ b/apps/Makefile
@@ -55,7 +55,8 @@ ifdef APPEXTRA
endif
CFLAGS = $(INCLUDES) $(GCCOPTS) $(TARGET) $(DEFINES) -DTARGET_ID=$(TARGET_ID) \
- -DAPPSVERSION=\"$(VERSION)\" $(EXTRA_DEFINES) -DMEM=${MEMORYSIZE}
+ -DAPPSVERSION=\"$(VERSION)\" $(EXTRA_DEFINES) -DMEM=${MEMORYSIZE} \
+ -DTARGET_NAME=\"$(ARCHOS)\"
OBJS2 := $(OBJDIR)/lang.o $(patsubst %.c, $(OBJDIR)/%.o, $(SRC))
OBJS = $(patsubst %.S, $(OBJDIR)/%.o, $(OBJS2))
diff --git a/apps/SOURCES b/apps/SOURCES
index dccb6f9..d759e49 100644
--- a/apps/SOURCES
+++ b/apps/SOURCES
@@ -29,6 +29,7 @@ talk.c
tree.c
tagtree.c
filetree.c
+scrobbler.c
screen_access.c
gui/buttonbar.c
diff --git a/apps/lang/english.lang b/apps/lang/english.lang
index 8a2341b..6cd5b2d 100644
--- a/apps/lang/english.lang
+++ b/apps/lang/english.lang
@@ -3861,13 +3861,13 @@
</phrase>
<phrase>
id: LANG_DIRCACHE_REBOOT
- desc: when activating directory cache
+ desc: DEPRECATED
user:
<source>
- *: "Please reboot to enable the cache"
+ *: ""
</source>
<dest>
- *: "Please reboot to enable the cache"
+ *: ""
</dest>
<voice>
*: ""
@@ -9940,3 +9940,31 @@
*: "Random"
</voice>
</phrase>
+<phrase>
+ id: LANG_AUDIOSCROBBLER
+ desc: "Last.fm Log" in the playback menu
+ user:
+ <source>
+ *: "Last.fm Log"
+ </source>
+ <dest>
+ *: "Last.fm Log"
+ </dest>
+ <voice>
+ *: "Last.fm Log"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PLEASE_REBOOT
+ desc: when activating an option that requires a reboot
+ user:
+ <source>
+ *: "Please reboot to enable"
+ </source>
+ <dest>
+ *: "Please reboot to enable"
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
diff --git a/apps/main.c b/apps/main.c
index bd1dd7b..838a485 100644
--- a/apps/main.c
+++ b/apps/main.c
@@ -66,6 +66,7 @@
#include "string.h"
#include "splash.h"
#include "eeprom_settings.h"
+#include "scrobbler.h"
#if (CONFIG_CODEC == SWCODEC)
#include "playback.h"
@@ -252,6 +253,8 @@ void init(void)
audio_preinit();
#endif
+ scrobbler_init();
+
/* audio_init must to know the size of voice buffer so init voice first */
#if CONFIG_CODEC == SWCODEC
talk_init();
@@ -455,7 +458,8 @@ void init(void)
status_init();
playlist_init();
tree_init();
-
+ scrobbler_init();
+
/* No buffer allocation (see buffer.c) may take place after the call to
audio_init() since the mpeg thread takes the rest of the buffer space */
mp3_init( global_settings.volume,
diff --git a/apps/misc.c b/apps/misc.c
index 87fd935..4be9e43 100644
--- a/apps/misc.c
+++ b/apps/misc.c
@@ -45,6 +45,7 @@
#include "font.h"
#include "splash.h"
#include "tagcache.h"
+#include "scrobbler.h"
#ifdef HAVE_MMC
#include "ata_mmc.h"
#endif
@@ -625,6 +626,7 @@ long default_event_handler_ex(long event, void (*callback)(void *), void *parame
if (!mmc_touched() || (mmc_remove_request() == SYS_MMC_EXTRACTED))
#endif
{
+ scrobbler_flush_cache();
system_flush();
usb_screen();
system_restore();
diff --git a/apps/playback.c b/apps/playback.c
index 486edf8..495629e 100644
--- a/apps/playback.c
+++ b/apps/playback.c
@@ -272,6 +272,9 @@ struct thread_entry *codec_thread_p;
#ifdef PLAYBACK_VOICE
extern struct codec_api ci_voice;
+/* Play time of the previous track */
+unsigned long prev_track_elapsed;
+
static volatile bool voice_thread_start;
static volatile bool voice_is_playing;
static volatile bool voice_codec_loaded;
@@ -1630,6 +1633,8 @@ static bool codec_load_next_track(void)
{
struct event ev;
+ prev_track_elapsed = CUR_TI->id3.elapsed;
+
if (ci.seek_time)
codec_seek_complete_callback();
@@ -2912,6 +2917,11 @@ void audio_set_track_changed_event(void (*handler)(struct mp3entry *id3))
track_changed_callback = handler;
}
+unsigned long audio_prev_elapsed(void)
+{
+ return prev_track_elapsed;
+}
+
static void audio_stop_codec_flush(void)
{
ci.stop_codec = true;
diff --git a/apps/scrobbler.c b/apps/scrobbler.c
new file mode 100755
index 0000000..68c5e9a
--- /dev/null
+++ b/apps/scrobbler.c
@@ -0,0 +1,263 @@
+/***************************************************************************
+ * __________ __ ___.
+ * 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"
+
+#ifndef SIMULATOR
+#include "ata.h"
+#endif
+
+#ifdef CONFIG_RTC
+#include "time.h"
+#include "timefuncs.h"
+#endif
+
+#include "scrobbler.h"
+
+#define SCROBBLER_VERSION "1.0"
+
+#ifdef CONFIG_RTC
+#define SCROBBLER_FILE "/.scrobbler.log"
+#else
+#define SCROBBLER_FILE "/.scrobbler-timeless.log"
+#endif
+
+/* increment this on any code change that effects output */
+/* replace with CVS Revision keyword? */
+#define SCROBBLER_REVISION "1.0"
+
+#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;
+#ifdef CONFIG_RTC
+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;
+
+ /* 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");
+#ifdef CONFIG_RTC
+ fdprintf(scrobbler_fd, "#CLIENT/Rockbox %s %s\n",
+ TARGET_NAME, SCROBBLER_REVISION);
+#else
+ fdprintf(scrobbler_fd, "#CLIENT/Rockbox %s %s Timeless\n",
+ TARGET_NAME, SCROBBLER_REVISION);
+#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;
+}
+
+static void add_to_cache(void)
+{
+/* using HAVE_MMC to check for Ondios - anything better to use? */
+#ifndef SIMULATOR
+#if defined(IPOD_NANO) || defined(HAVE_MMC)
+ if ( cache_pos >= SCROBBLER_MAX_CACHE )
+#else
+ if ( ( cache_pos >= SCROBBLER_MAX_CACHE ) || ( ata_disk_is_active() ) )
+#endif
+#endif /* !SIMULATOR */
+ write_cache();
+
+ int ret;
+ char rating = 'S'; /* Skipped */
+
+ logf("SCROBBLER: add_to_cache[%d]", cache_pos);
+
+ if ( audio_prev_elapsed() >
+ (scrobbler_entry.length/2) )
+ 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);
+ } else
+ cache_pos++;
+}
+
+void scrobbler_change_event(struct mp3entry *id)
+{
+ /* add entry using the previous scrobbler_entry and timestamp */
+ if (pending)
+ add_to_cache();
+
+ /* 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);
+#ifdef CONFIG_RTC
+ timestamp = mktime(get_time());
+#else
+ timestamp = 0;
+#endif
+ pending = true;
+ }
+}
+
+int scrobbler_init(void)
+{
+ logf("SCROBBLER: init %d", global_settings.audioscrobbler);
+
+ if(!global_settings.audioscrobbler)
+ return -1;
+
+ scrobbler_cache = buffer_alloc(SCROBBLER_MAX_CACHE*SCROBBLER_CACHE_LEN);
+
+ audio_set_track_changed_event(&scrobbler_change_event);
+ 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)
+ add_to_cache();
+
+ /* Write the cache to disk if needed */
+ if (cache_pos)
+ write_cache();
+
+ pending = false;
+ }
+}
+
+void scrobbler_shutdown(void)
+{
+ scrobbler_flush_cache();
+
+ if (scrobbler_initialised)
+ {
+ audio_set_track_changed_event(NULL);
+ scrobbler_initialised = false;
+ }
+}
+
+bool scrobbler_is_enabled(void)
+{
+ return scrobbler_initialised;
+}
diff --git a/apps/scrobbler.h b/apps/scrobbler.h
new file mode 100644
index 0000000..543a30e
--- /dev/null
+++ b/apps/scrobbler.h
@@ -0,0 +1,24 @@
+/***************************************************************************
+ * __________ __ ___.
+ * 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.
+ *
+ ****************************************************************************/
+
+void scrobbler_change_event(struct mp3entry *id);
+int scrobbler_init(void);
+void scrobbler_flush_cache(void);
+void scrobbler_shutdown(void);
+bool scrobbler_is_enabled(void);
diff --git a/apps/settings.c b/apps/settings.c
index 196c432..9794568 100644
--- a/apps/settings.c
+++ b/apps/settings.c
@@ -665,6 +665,8 @@ static const struct bit_entry hd_bits[] =
{2, S_O(fm_region), 0, "fm_region", "eu,us,jp,kr" },
#endif
+ {1, S_O(audioscrobbler), false, "Last.fm Logging", off_on},
+
/* If values are just added to the end, no need to bump the version. */
/* new stuff to be added at the end */
diff --git a/apps/settings.h b/apps/settings.h
index 5313fe3..5fc8c7e 100644
--- a/apps/settings.h
+++ b/apps/settings.h
@@ -498,6 +498,7 @@ struct user_settings
#ifdef CONFIG_TUNER
int fm_region;
#endif
+ bool audioscrobbler; /* Audioscrobbler logging */
};
diff --git a/apps/settings_menu.c b/apps/settings_menu.c
index eabe153..0193aa0 100644
--- a/apps/settings_menu.c
+++ b/apps/settings_menu.c
@@ -56,6 +56,7 @@
#include "yesno.h"
#include "list.h"
#include "color_picker.h"
+#include "scrobbler.h"
#ifdef HAVE_LCD_BITMAP
#include "peakmeter.h"
@@ -1387,6 +1388,23 @@ static bool next_folder(void)
INT, names, 3, NULL );
}
+static bool audioscrobbler(void)
+{
+ bool result = set_bool_options(str(LANG_AUDIOSCROBBLER),
+ &global_settings.audioscrobbler,
+ STR(LANG_ON),
+ STR(LANG_OFF),
+ NULL);
+
+ if (!scrobbler_is_enabled() && global_settings.audioscrobbler)
+ gui_syncsplash(HZ*2, true, str(LANG_PLEASE_REBOOT));
+
+ if(!result)
+ scrobbler_shutdown();
+
+ return result;
+}
+
static bool codepage_setting(void)
{
static const struct opt_items names[] = {
@@ -1605,7 +1623,7 @@ static bool dircache(void)
NULL);
if (!dircache_is_enabled() && global_settings.dircache)
- gui_syncsplash(HZ*2, true, str(LANG_DIRCACHE_REBOOT));
+ gui_syncsplash(HZ*2, true, str(LANG_PLEASE_REBOOT));
if (!result)
dircache_disable();
@@ -1747,8 +1765,9 @@ static bool playback_settings_menu(void)
{ ID2P(LANG_ID3_ORDER), id3_order },
{ ID2P(LANG_NEXT_FOLDER), next_folder },
#ifdef HAVE_HEADPHONE_DETECTION
- { ID2P(LANG_UNPLUG), unplug_menu }
+ { ID2P(LANG_UNPLUG), unplug_menu },
#endif
+ { ID2P(LANG_AUDIOSCROBBLER), audioscrobbler}
};
bool old_shuffle = global_settings.playlist_shuffle;
diff --git a/apps/tree.c b/apps/tree.c
index 70b83f8..bfb6412 100644
--- a/apps/tree.c
+++ b/apps/tree.c
@@ -65,6 +65,7 @@
#include "yesno.h"
#include "gwps-common.h"
#include "eeprom_settings.h"
+#include "scrobbler.h"
/* gui api */
#include "list.h"
@@ -1378,6 +1379,7 @@ void ft_play_filename(char *dir, char *file)
/* These two functions are called by the USB and shutdown handlers */
void tree_flush(void)
{
+ scrobbler_shutdown();
tagcache_shutdown();
playlist_shutdown();
@@ -1439,4 +1441,5 @@ void tree_restore(void)
}
#endif
tagcache_start_scan();
+ scrobbler_init();
}