mirror of
https://github.com/Rockbox/rockbox.git
synced 2025-10-13 10:07:38 -04:00
[Feature] playback logging from core
people don't like having to remember to run the TSR plugin so lets meet them halfway all tracks are added with timestamp, elapsed, length, trackname added buffering for ATA devices still needed: -Done -- a plugin that parses for duplicates and reads the track info to create the actual scrobbler log (log can be truncated once dumped) this should allow you to run the plugin at leisure later I'd like to expand this logging to allow track culling based on skipped songs.. remove the TSR plugin as hard disk no longer need to use it Change-Id: Ib0b74b4c868fecb3e4941a8f4b9de7bd8728fe3e
This commit is contained in:
parent
e5c9846cb4
commit
c3fd32bdf2
14 changed files with 705 additions and 643 deletions
|
@ -16573,3 +16573,31 @@
|
||||||
*: "tracks saved"
|
*: "tracks saved"
|
||||||
</voice>
|
</voice>
|
||||||
</phrase>
|
</phrase>
|
||||||
|
<phrase>
|
||||||
|
id: LANG_LOGGING
|
||||||
|
desc: playback logging
|
||||||
|
user: core
|
||||||
|
<source>
|
||||||
|
*: "Logging"
|
||||||
|
</source>
|
||||||
|
<dest>
|
||||||
|
*: "Logging"
|
||||||
|
</dest>
|
||||||
|
<voice>
|
||||||
|
*: "logging"
|
||||||
|
</voice>
|
||||||
|
</phrase>
|
||||||
|
<phrase>
|
||||||
|
id: LANG_VIEWLOG
|
||||||
|
desc: view Log
|
||||||
|
user: core
|
||||||
|
<source>
|
||||||
|
*: "View log"
|
||||||
|
</source>
|
||||||
|
<dest>
|
||||||
|
*: "View log"
|
||||||
|
</dest>
|
||||||
|
<voice>
|
||||||
|
*: "view log"
|
||||||
|
</voice>
|
||||||
|
</phrase>
|
||||||
|
|
|
@ -200,6 +200,7 @@ int main(void)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if !defined(BOOTLOADER)
|
#if !defined(BOOTLOADER)
|
||||||
|
allocate_playback_log();
|
||||||
if (!file_exists(ROCKBOX_DIR"/playername.txt"))
|
if (!file_exists(ROCKBOX_DIR"/playername.txt"))
|
||||||
{
|
{
|
||||||
int fd = open(ROCKBOX_DIR"/playername.txt", O_CREAT|O_WRONLY|O_TRUNC, 0666);
|
int fd = open(ROCKBOX_DIR"/playername.txt", O_CREAT|O_WRONLY|O_TRUNC, 0666);
|
||||||
|
|
|
@ -199,6 +199,8 @@ MENUITEM_SETTING(album_art, &global_settings.album_art,
|
||||||
albumart_callback);
|
albumart_callback);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
MENUITEM_SETTING(playback_log, &global_settings.playback_log, NULL);
|
||||||
|
|
||||||
MAKE_MENU(playback_settings,ID2P(LANG_PLAYBACK),0,
|
MAKE_MENU(playback_settings,ID2P(LANG_PLAYBACK),0,
|
||||||
Icon_Playback_menu,
|
Icon_Playback_menu,
|
||||||
&shuffle_item, &repeat_mode, &play_selected,
|
&shuffle_item, &repeat_mode, &play_selected,
|
||||||
|
@ -232,6 +234,7 @@ MAKE_MENU(playback_settings,ID2P(LANG_PLAYBACK),0,
|
||||||
#ifdef HAVE_ALBUMART
|
#ifdef HAVE_ALBUMART
|
||||||
,&album_art
|
,&album_art
|
||||||
#endif
|
#endif
|
||||||
|
,&playback_log
|
||||||
);
|
);
|
||||||
|
|
||||||
/* PLAYBACK MENU */
|
/* PLAYBACK MENU */
|
||||||
|
|
108
apps/playback.c
108
apps/playback.c
|
@ -46,6 +46,8 @@
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
#include "audiohw.h"
|
#include "audiohw.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
#ifdef HAVE_TAGCACHE
|
#ifdef HAVE_TAGCACHE
|
||||||
#include "tagcache.h"
|
#include "tagcache.h"
|
||||||
#endif
|
#endif
|
||||||
|
@ -346,6 +348,11 @@ static int codec_skip_status;
|
||||||
static bool codec_seeking = false; /* Codec seeking ack expected? */
|
static bool codec_seeking = false; /* Codec seeking ack expected? */
|
||||||
static unsigned int position_key = 0;
|
static unsigned int position_key = 0;
|
||||||
|
|
||||||
|
#if (CONFIG_STORAGE & STORAGE_ATA)
|
||||||
|
#define PLAYBACK_LOG_BUFSZ (MAX_PATH * 10)
|
||||||
|
static int playback_log_handle = 0; /* core_alloc handle for playback log buffer */
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Forward declarations */
|
/* Forward declarations */
|
||||||
enum audio_start_playback_flags
|
enum audio_start_playback_flags
|
||||||
{
|
{
|
||||||
|
@ -1229,6 +1236,104 @@ static void audio_handle_track_load_status(int trackstat)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void allocate_playback_log(void) INIT_ATTR;
|
||||||
|
void allocate_playback_log(void)
|
||||||
|
{
|
||||||
|
#if (CONFIG_STORAGE & STORAGE_ATA)
|
||||||
|
if (global_settings.playback_log && playback_log_handle == 0)
|
||||||
|
{
|
||||||
|
playback_log_handle = core_alloc(PLAYBACK_LOG_BUFSZ);
|
||||||
|
if (playback_log_handle > 0)
|
||||||
|
{
|
||||||
|
DEBUGF("%s Allocated %d bytes\n", __func__, PLAYBACK_LOG_BUFSZ);
|
||||||
|
char *buf = core_get_data(playback_log_handle);
|
||||||
|
buf[0] = '\0';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_playbacklog(struct mp3entry *id3)
|
||||||
|
{
|
||||||
|
if (!global_settings.playback_log)
|
||||||
|
return;
|
||||||
|
ssize_t used = 0;
|
||||||
|
unsigned long timestamp = current_tick;
|
||||||
|
#if (CONFIG_STORAGE & STORAGE_ATA)
|
||||||
|
char *buf = NULL;
|
||||||
|
ssize_t bufsz;
|
||||||
|
|
||||||
|
/* if the user just enabled playback logging rather than stopping playback
|
||||||
|
* to allocate a buffer or if buffer too large just flush direct to disk
|
||||||
|
* buffer will attempt to be allocated next start-up */
|
||||||
|
if (playback_log_handle > 0)
|
||||||
|
{
|
||||||
|
buf = core_get_data(playback_log_handle);
|
||||||
|
used = strlen(buf);
|
||||||
|
bufsz = PLAYBACK_LOG_BUFSZ - used;
|
||||||
|
buf += used;
|
||||||
|
DEBUGF("%s Used %lu Remain: %lu\n", __func__, used, bufsz);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (id3 && id3->elapsed > 500) /* 500 ms*/
|
||||||
|
{
|
||||||
|
#if CONFIG_RTC
|
||||||
|
timestamp = mktime(get_time());
|
||||||
|
#endif
|
||||||
|
#if (CONFIG_STORAGE & STORAGE_ATA)
|
||||||
|
if (buf) /* we have a buffer allocd from core */
|
||||||
|
{
|
||||||
|
/*10:10:10:MAX_PATH\n*/
|
||||||
|
ssize_t entrylen = snprintf(buf, bufsz,"%lu:%ld:%ld:%s\n",
|
||||||
|
timestamp, (long)id3->elapsed, (long)id3->length, id3->path);
|
||||||
|
|
||||||
|
if (entrylen < bufsz)
|
||||||
|
{
|
||||||
|
DEBUGF("BUFFERED: time: %lu elapsed %ld/%ld saving file: %s\n",
|
||||||
|
timestamp, id3->elapsed, id3->length, id3->path);
|
||||||
|
return; /* succeed or snprintf fail return */
|
||||||
|
}
|
||||||
|
buf[0] = '\0';
|
||||||
|
}
|
||||||
|
/* that didn't fit, flush buffer & write this entry to disk */
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else
|
||||||
|
id3 = NULL;
|
||||||
|
|
||||||
|
if (id3 || used > 0) /* flush */
|
||||||
|
{
|
||||||
|
DEBUGF("Opening %s \n", ROCKBOX_DIR "/playback.log");
|
||||||
|
int fd = open(ROCKBOX_DIR "/playback.log", O_WRONLY|O_CREAT|O_APPEND, 0666);
|
||||||
|
if (fd < 0)
|
||||||
|
{
|
||||||
|
return; /* failure */
|
||||||
|
}
|
||||||
|
#if (CONFIG_STORAGE & STORAGE_ATA)
|
||||||
|
if (buf) /* we have a buffer allocd from core */
|
||||||
|
{
|
||||||
|
buf = core_get_data_pinned(playback_log_handle); /* we might yield - pin it*/
|
||||||
|
write(fd, buf, used);
|
||||||
|
DEBUGF("%s Writing %lu bytes of buf:\n%s\n", __func__, used, buf);
|
||||||
|
buf[0] = '\0';
|
||||||
|
core_put_data_pinned(buf);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (id3)
|
||||||
|
{
|
||||||
|
/* we have the timestamp from when we tried to add to buffer */
|
||||||
|
DEBUGF("LOGGED: time: %lu elapsed %ld/%ld saving file: %s\n",
|
||||||
|
timestamp, id3->elapsed, id3->length, id3->path);
|
||||||
|
fdprintf(fd, "%lu:%ld:%ld:%s\n",
|
||||||
|
timestamp, (long)id3->elapsed, (long)id3->length, id3->path);
|
||||||
|
}
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Send track events that use a struct track_event for data */
|
/* Send track events that use a struct track_event for data */
|
||||||
static void send_track_event(unsigned int id, unsigned int flags,
|
static void send_track_event(unsigned int id, unsigned int flags,
|
||||||
struct mp3entry *id3)
|
struct mp3entry *id3)
|
||||||
|
@ -1246,9 +1351,9 @@ static void audio_playlist_track_finish(void)
|
||||||
struct mp3entry *id3 = valid_mp3entry(ply_id3);
|
struct mp3entry *id3 = valid_mp3entry(ply_id3);
|
||||||
|
|
||||||
playlist_update_resume_info(filling == STATE_ENDED ? NULL : id3);
|
playlist_update_resume_info(filling == STATE_ENDED ? NULL : id3);
|
||||||
|
|
||||||
if (id3)
|
if (id3)
|
||||||
{
|
{
|
||||||
|
add_playbacklog(id3);
|
||||||
send_track_event(PLAYBACK_EVENT_TRACK_FINISH,
|
send_track_event(PLAYBACK_EVENT_TRACK_FINISH,
|
||||||
track_event_flags, id3);
|
track_event_flags, id3);
|
||||||
}
|
}
|
||||||
|
@ -3010,6 +3115,7 @@ static void audio_stop_playback(void)
|
||||||
/* Go idle */
|
/* Go idle */
|
||||||
filling = STATE_IDLE;
|
filling = STATE_IDLE;
|
||||||
cancel_cpu_boost();
|
cancel_cpu_boost();
|
||||||
|
add_playbacklog(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Pause the playback of the current track
|
/* Pause the playback of the current track
|
||||||
|
|
|
@ -95,4 +95,7 @@ unsigned int playback_status(void);
|
||||||
|
|
||||||
struct mp3entry* get_temp_mp3entry(struct mp3entry *free);
|
struct mp3entry* get_temp_mp3entry(struct mp3entry *free);
|
||||||
|
|
||||||
|
void allocate_playback_log(void);
|
||||||
|
void add_playbacklog(struct mp3entry *id3);
|
||||||
|
|
||||||
#endif /* _PLAYBACK_H */
|
#endif /* _PLAYBACK_H */
|
||||||
|
|
|
@ -841,6 +841,7 @@ static const struct plugin_api rockbox_api = {
|
||||||
|
|
||||||
/* new stuff at the end, sort into place next time
|
/* new stuff at the end, sort into place next time
|
||||||
the API gets incompatible */
|
the API gets incompatible */
|
||||||
|
add_playbacklog,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int plugin_buffer_handle;
|
static int plugin_buffer_handle;
|
||||||
|
|
|
@ -990,6 +990,7 @@ struct plugin_api {
|
||||||
#endif
|
#endif
|
||||||
/* new stuff at the end, sort into place next time
|
/* new stuff at the end, sort into place next time
|
||||||
the API gets incompatible */
|
the API gets incompatible */
|
||||||
|
void (*add_playbacklog)(struct mp3entry *id3);
|
||||||
};
|
};
|
||||||
|
|
||||||
/* plugin header */
|
/* plugin header */
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -77,6 +77,7 @@ enum e_find_type {
|
||||||
};
|
};
|
||||||
|
|
||||||
static void synclist_set(int selected_item, int items, int sel_size, struct printcell_data_t *pc_data);
|
static void synclist_set(int selected_item, int items, int sel_size, struct printcell_data_t *pc_data);
|
||||||
|
static int scrobbler_read_line(int fd, char* buf, size_t buf_size);
|
||||||
static void pc_data_set_header(struct printcell_data_t *pc_data);
|
static void pc_data_set_header(struct printcell_data_t *pc_data);
|
||||||
|
|
||||||
static void browse_file(char *buf, size_t bufsz)
|
static void browse_file(char *buf, size_t bufsz)
|
||||||
|
@ -286,7 +287,7 @@ static const char* list_get_name_cb(int selected_item, void* data,
|
||||||
rb->lseek(fd, file_last_seek, SEEK_SET);
|
rb->lseek(fd, file_last_seek, SEEK_SET);
|
||||||
line_num = file_line_num;
|
line_num = file_line_num;
|
||||||
|
|
||||||
while ((rb->read_line(fd, buf, buf_len)) > 0)
|
while ((scrobbler_read_line(fd, buf, buf_len)) > 0)
|
||||||
{
|
{
|
||||||
if(buf[0] == '#')
|
if(buf[0] == '#')
|
||||||
continue;
|
continue;
|
||||||
|
@ -395,6 +396,45 @@ static enum themable_icons list_icon_cb(int selected_item, void *data)
|
||||||
return Icon_NOICON;
|
return Icon_NOICON;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int scrobbler_read_line(int fd, char* buf, size_t buf_size)
|
||||||
|
{
|
||||||
|
int count = 0;
|
||||||
|
unsigned int pos = 0;
|
||||||
|
char sep = '\t';
|
||||||
|
char ch, last_ch = sep;
|
||||||
|
bool comment = false;
|
||||||
|
|
||||||
|
while (rb->read(fd, &ch, 1) > 0)
|
||||||
|
{
|
||||||
|
if (ch == sep && last_ch == sep && buf_size > pos)
|
||||||
|
buf[pos++] = ' ';
|
||||||
|
|
||||||
|
if (count++ == 0 && ch == '#') /* skip comments */
|
||||||
|
comment = true;
|
||||||
|
else if (!comment && ch != '\r' && buf_size > pos)
|
||||||
|
buf[pos++] = ch;
|
||||||
|
|
||||||
|
last_ch = ch;
|
||||||
|
|
||||||
|
if (pos > PRINTCELL_MAXLINELEN * 2)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (ch == '\n')
|
||||||
|
{
|
||||||
|
if (!comment)
|
||||||
|
{
|
||||||
|
buf[pos] = '\0';
|
||||||
|
if (buf_size > pos)
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
last_ch = sep;
|
||||||
|
comment = false;
|
||||||
|
count = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
/* load file entries into pc_data buffer, file should already be opened
|
/* load file entries into pc_data buffer, file should already be opened
|
||||||
* and will be closed if the whole file was buffered */
|
* and will be closed if the whole file was buffered */
|
||||||
static int file_load_entries(struct printcell_data_t *pc_data)
|
static int file_load_entries(struct printcell_data_t *pc_data)
|
||||||
|
@ -404,18 +444,23 @@ static int file_load_entries(struct printcell_data_t *pc_data)
|
||||||
int buffered = 0;
|
int buffered = 0;
|
||||||
unsigned int pos = 0;
|
unsigned int pos = 0;
|
||||||
bool comment = false;
|
bool comment = false;
|
||||||
char ch;
|
char sep = '\t';
|
||||||
|
char ch, last_ch = sep;
|
||||||
int fd = pc_data->fd_cur;
|
int fd = pc_data->fd_cur;
|
||||||
if (fd < 0)
|
if (fd < 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
size_t buf_size = pc_data->buf_size - 1;
|
||||||
|
|
||||||
rb->lseek(fd, 0, SEEK_SET);
|
rb->lseek(fd, 0, SEEK_SET);
|
||||||
|
|
||||||
while (rb->read(fd, &ch, 1) > 0)
|
while (rb->read(fd, &ch, 1) > 0)
|
||||||
{
|
{
|
||||||
|
if (ch == sep && last_ch == sep && buf_size > pos)
|
||||||
|
pc_data->buf[pos++] = ' ';
|
||||||
|
|
||||||
if (count++ == 0 && ch == '#') /* skip comments */
|
if (count++ == 0 && ch == '#') /* skip comments */
|
||||||
comment = true;
|
comment = true;
|
||||||
else if (!comment && ch != '\r' && pc_data->buf_size > pos)
|
else if (!comment && ch != '\r' && buf_size > pos)
|
||||||
pc_data->buf[pos++] = ch;
|
pc_data->buf[pos++] = ch;
|
||||||
|
|
||||||
if (items == 0 && pos > PRINTCELL_MAXLINELEN * 2)
|
if (items == 0 && pos > PRINTCELL_MAXLINELEN * 2)
|
||||||
|
@ -426,13 +471,14 @@ static int file_load_entries(struct printcell_data_t *pc_data)
|
||||||
if (!comment)
|
if (!comment)
|
||||||
{
|
{
|
||||||
pc_data->buf[pos] = '\0';
|
pc_data->buf[pos] = '\0';
|
||||||
if (pc_data->buf_size > pos)
|
if (buf_size > pos)
|
||||||
{
|
{
|
||||||
pos++;
|
pos++;
|
||||||
buffered++;
|
buffered++;
|
||||||
}
|
}
|
||||||
items++;
|
items++;
|
||||||
}
|
}
|
||||||
|
last_ch = sep;
|
||||||
comment = false;
|
comment = false;
|
||||||
count = 0;
|
count = 0;
|
||||||
rb->yield();
|
rb->yield();
|
||||||
|
@ -903,9 +949,32 @@ static void synclist_set(int selected_item, int items, int sel_size, struct prin
|
||||||
SCROBBLER_MIN_COLUMNS, pc_data);
|
SCROBBLER_MIN_COLUMNS, pc_data);
|
||||||
if (max_cols < SCROBBLER_MIN_COLUMNS) /* not a scrobbler file? */
|
if (max_cols < SCROBBLER_MIN_COLUMNS) /* not a scrobbler file? */
|
||||||
{
|
{
|
||||||
rb->gui_synclist_set_voice_callback(&lists, NULL);
|
/*check for a playlist_control file or a playback log*/
|
||||||
pc_data->view_columns = printcell_set_columns(&lists, NULL,
|
|
||||||
"$*512$", Icon_Questionmark);
|
max_cols = count_max_columns(items, ':', 3, pc_data);
|
||||||
|
|
||||||
|
if (max_cols >= 3)
|
||||||
|
{
|
||||||
|
char headerbuf[32];
|
||||||
|
int w = gConfig.col_width;
|
||||||
|
rb->snprintf(headerbuf, sizeof(headerbuf),
|
||||||
|
"$*%d$$*%d$$*%d$$*%d$", w, w, w, w);
|
||||||
|
|
||||||
|
|
||||||
|
struct printcell_settings pcs = {.cell_separator = gConfig.separator,
|
||||||
|
.title_delimeter = '$',
|
||||||
|
.text_delimeter = ':',
|
||||||
|
.hidecol_flags = gConfig.hidecol_flags};
|
||||||
|
rb->gui_synclist_set_voice_callback(&lists, NULL);
|
||||||
|
pc_data->view_columns = printcell_set_columns(&lists, &pcs,
|
||||||
|
headerbuf, Icon_Rockbox);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rb->gui_synclist_set_voice_callback(&lists, NULL);
|
||||||
|
pc_data->view_columns = printcell_set_columns(&lists, NULL,
|
||||||
|
"$*512$", Icon_Questionmark);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int curcol = printcell_get_column_selected();
|
int curcol = printcell_get_column_selected();
|
||||||
|
@ -951,19 +1020,32 @@ enum plugin_status plugin_start(const void* parameter)
|
||||||
|
|
||||||
if (parameter)
|
if (parameter)
|
||||||
{
|
{
|
||||||
rb->strlcpy(filename, (const char*)parameter, MAX_PATH);
|
rb->strlcpy(filename, (const char*)parameter, sizeof(filename));
|
||||||
filename[MAX_PATH - 1] = '\0';
|
filename[MAX_PATH - 1] = '\0';
|
||||||
|
parameter = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!rb->file_exists(filename))
|
if (!rb->file_exists(filename))
|
||||||
{
|
{
|
||||||
browse_file(filename, sizeof(filename));
|
if (rb->strcmp(filename, "-scrobbler_view_pbl") == 0)
|
||||||
if (!rb->file_exists(filename))
|
|
||||||
{
|
{
|
||||||
rb->splash(HZ, "No Scrobbler file Goodbye.");
|
parameter = PLUGIN_APPS_DIR "/lastfm_scrobbler.rock";
|
||||||
return PLUGIN_ERROR;
|
rb->strlcpy(filename, ROCKBOX_DIR "/playback.log", MAX_PATH);
|
||||||
|
if (!rb->file_exists(filename))
|
||||||
|
{
|
||||||
|
rb->splashf(HZ * 2, "Viewer: NO log! %s", filename);
|
||||||
|
return rb->plugin_open(parameter, "-resume");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
browse_file(filename, sizeof(filename));
|
||||||
|
if (!rb->file_exists(filename))
|
||||||
|
{
|
||||||
|
rb->splash(HZ, "No file Goodbye.");
|
||||||
|
return PLUGIN_ERROR;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
config_set_defaults();
|
config_set_defaults();
|
||||||
|
@ -1029,5 +1111,9 @@ enum plugin_status plugin_start(const void* parameter)
|
||||||
}
|
}
|
||||||
config_save();
|
config_save();
|
||||||
cleanup(&printcell_data);
|
cleanup(&printcell_data);
|
||||||
|
if (parameter)
|
||||||
|
{
|
||||||
|
return rb->plugin_open((const char*)parameter, "-resume");
|
||||||
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
@ -910,6 +910,7 @@ struct user_settings
|
||||||
#if defined(HAVE_EROS_QN_CODEC)
|
#if defined(HAVE_EROS_QN_CODEC)
|
||||||
int hp_lo_select; /* indicates automatic, headphone-only, or lineout-only operation */
|
int hp_lo_select; /* indicates automatic, headphone-only, or lineout-only operation */
|
||||||
#endif
|
#endif
|
||||||
|
bool playback_log; /* ROCKBOX_DIR/playback.log for tracks played */
|
||||||
};
|
};
|
||||||
|
|
||||||
/** global variables **/
|
/** global variables **/
|
||||||
|
|
|
@ -2314,6 +2314,7 @@ const struct settings_list settings[] = {
|
||||||
"auto,headphone,lineout", hp_lo_select_apply, 3,
|
"auto,headphone,lineout", hp_lo_select_apply, 3,
|
||||||
ID2P(LANG_AUTO), ID2P(LANG_HEADPHONE), ID2P(LANG_LINEOUT)),
|
ID2P(LANG_AUTO), ID2P(LANG_HEADPHONE), ID2P(LANG_LINEOUT)),
|
||||||
#endif
|
#endif
|
||||||
|
OFFON_SETTING(0, playback_log, LANG_LOGGING, false, "play log", NULL),
|
||||||
};
|
};
|
||||||
|
|
||||||
const int nb_settings = sizeof(settings)/sizeof(*settings);
|
const int nb_settings = sizeof(settings)/sizeof(*settings);
|
||||||
|
|
|
@ -325,3 +325,15 @@ you to configure settings related to audio playback.
|
||||||
\setting{Prefer Image File}. The default behavior is to
|
\setting{Prefer Image File}. The default behavior is to
|
||||||
\setting{Prefer Embedded} album art.
|
\setting{Prefer Embedded} album art.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
\section{Logging}\index{Logging}
|
||||||
|
This option will record information about tracks played on the device
|
||||||
|
in the following format 'timestamp:elapsed(ms):length(ms):path'
|
||||||
|
Devices without a Real Time Clock will use current system tick.
|
||||||
|
Tracks played for a very short duration < 1 second will not be recorded
|
||||||
|
to minimize disk access while skipping quickly through songs.
|
||||||
|
You should periodically delete this log as excessive file sizes may cause
|
||||||
|
decreased device performace see \setting{LastFm Scrobbler} plugin.
|
||||||
|
\begin{verbatim}
|
||||||
|
the log can be found under '/.rockbox/playback.log'
|
||||||
|
\end{verbatim}
|
||||||
|
|
53
manual/plugins/lastfm_scrobbler.tex
Normal file
53
manual/plugins/lastfm_scrobbler.tex
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
\subsection{LastFm Scrobbler}
|
||||||
|
The \setting{LastFm Scrobbler} plugin enables you to parse the rockbox
|
||||||
|
playback log for tracks you have played for your own logging or upload
|
||||||
|
to scrobbling services, such as Last.fm, Libre.fm or ListenBrainz.
|
||||||
|
|
||||||
|
\setting{Playback Logging} must be enabled to record the tracks played
|
||||||
|
the plugin will ask you to enable logging if run with logging disabled.
|
||||||
|
|
||||||
|
\subsubsection{Menu}
|
||||||
|
\begin{itemize}
|
||||||
|
\item Remove duplicates - Only keeps the same track with the most time elapsed.
|
||||||
|
\item Delete playback log - Remove the current playback log once it has been read.
|
||||||
|
\item Save threshold - Percentage of track played to be considered 'L'istened.
|
||||||
|
\item Minimum elapsed (ms) - Tracks played less than this will not be recorded in log.
|
||||||
|
\item View log - View the current playback log.
|
||||||
|
\item Revert to Default - Default settings restored.
|
||||||
|
\item Cancel - Exit, you will be asked to save any changes
|
||||||
|
\item Export - Append scrobbler log and save any changes, not visible if no playback log exists.
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
After the plugin has exported the scrobbler log you can find it in the root
|
||||||
|
of the drive '.scrobbler.log' open it in the file browser to view the log.
|
||||||
|
|
||||||
|
Subsequent exports will be appended to .scrobbler.log thus Delete playback log is advised.
|
||||||
|
|
||||||
|
\begin{verbatim}
|
||||||
|
A copy of the log can be found in
|
||||||
|
'/rockbox/playback.old'
|
||||||
|
it will be overwritten with each export
|
||||||
|
\end{verbatim}
|
||||||
|
|
||||||
|
\subsubsection{Format}
|
||||||
|
Data will be saved in Audioscrobbler spec at: (use wayback machine).
|
||||||
|
\url{http://www.audioscrobbler.net/wiki/Portable_Player_Logging}.
|
||||||
|
|
||||||
|
\begin{verbatim}
|
||||||
|
The scrobbler format consists of the following data items
|
||||||
|
tab '\t' separated followed by newline '\n'
|
||||||
|
\end{verbatim}
|
||||||
|
|
||||||
|
\begin{itemize}
|
||||||
|
\item ARTIST
|
||||||
|
\item ALBUM
|
||||||
|
\item TITLE
|
||||||
|
\item TRACKNUM
|
||||||
|
\item LENGTH
|
||||||
|
\item RATING
|
||||||
|
\item TIMESTAMP
|
||||||
|
\item MUSICBRAINZ-TRACKID
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
If track info is not available (due to missing file or format limitations)
|
||||||
|
the track path will be used instead.
|
|
@ -263,6 +263,8 @@ option from the \setting{Context Menu} (see \reference{ref:Contextmenu}).}
|
||||||
|
|
||||||
\opt{HAVE_BACKLIGHT}{\input{plugins/lamp.tex}}
|
\opt{HAVE_BACKLIGHT}{\input{plugins/lamp.tex}}
|
||||||
|
|
||||||
|
\input{plugins/lastfm_scrobbler.tex}
|
||||||
|
|
||||||
\input{plugins/lrcplayer.tex}
|
\input{plugins/lrcplayer.tex}
|
||||||
|
|
||||||
\input{plugins/main_menu_config.tex}
|
\input{plugins/main_menu_config.tex}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue