mirror of
https://github.com/owntone/owntone-server.git
synced 2025-01-14 16:25:03 -05:00
Enable resuming playback from saved position for certain media kinds (eg audiobooks)
This commit is contained in:
parent
7881df67f6
commit
68912efa1f
@ -187,7 +187,7 @@ expression returns [ pANTLR3_STRING result ]
|
||||
}
|
||||
else if (strcmp((char *)val, "url") == 0)
|
||||
{
|
||||
sprintf(str, "f.data_kind = \%d", DATA_KIND_URL);
|
||||
sprintf(str, "f.data_kind = \%d", DATA_KIND_HTTP);
|
||||
}
|
||||
else if (strcmp((char *)val, "spotify") == 0)
|
||||
{
|
||||
|
23
src/db.c
23
src/db.c
@ -2050,7 +2050,7 @@ db_files_update_songalbumid(void)
|
||||
void
|
||||
db_file_inc_playcount(int id)
|
||||
{
|
||||
#define Q_TMPL "UPDATE files SET play_count = play_count + 1, time_played = %" PRIi64 " WHERE id = %d;"
|
||||
#define Q_TMPL "UPDATE files SET play_count = play_count + 1, time_played = %" PRIi64 ", seek = 0 WHERE id = %d;"
|
||||
char *query;
|
||||
|
||||
query = sqlite3_mprintf(Q_TMPL, (int64_t)time(NULL), id);
|
||||
@ -2754,6 +2754,27 @@ db_file_update_icy(int id, char *artist, char *album)
|
||||
#undef Q_TMPL
|
||||
}
|
||||
|
||||
void
|
||||
db_file_save_seek(int id, uint32_t seek)
|
||||
{
|
||||
#define Q_TMPL "UPDATE files SET seek = %d WHERE id = %d;"
|
||||
char *query;
|
||||
|
||||
if (id == 0)
|
||||
return;
|
||||
|
||||
query = sqlite3_mprintf(Q_TMPL, seek, id);
|
||||
if (!query)
|
||||
{
|
||||
DPRINTF(E_LOG, L_DB, "Out of memory for query string\n");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
db_query_run(query, 1, 0);
|
||||
#undef Q_TMPL
|
||||
}
|
||||
|
||||
void
|
||||
db_file_delete_bypath(char *path)
|
||||
{
|
||||
|
5
src/db.h
5
src/db.h
@ -104,7 +104,7 @@ enum media_kind {
|
||||
|
||||
enum data_kind {
|
||||
DATA_KIND_FILE = 0, /* normal file */
|
||||
DATA_KIND_URL = 1, /* url/stream */
|
||||
DATA_KIND_HTTP = 1, /* network stream (radio) */
|
||||
DATA_KIND_SPOTIFY = 2, /* iTunes has no spotify data kind, but we use 2 */
|
||||
DATA_KIND_PIPE = 3, /* iTunes has no pipe data kind, but we use 3 */
|
||||
};
|
||||
@ -473,6 +473,9 @@ db_file_update(struct media_file_info *mfi);
|
||||
void
|
||||
db_file_update_icy(int id, char *artist, char *album);
|
||||
|
||||
void
|
||||
db_file_save_seek(int id, uint32_t seek);
|
||||
|
||||
void
|
||||
db_file_delete_bypath(char *path);
|
||||
|
||||
|
@ -706,7 +706,7 @@ filescanner_process_media(char *path, time_t mtime, off_t size, int type, struct
|
||||
}
|
||||
else if (type & F_SCAN_TYPE_URL)
|
||||
{
|
||||
mfi->data_kind = DATA_KIND_URL;
|
||||
mfi->data_kind = DATA_KIND_HTTP;
|
||||
ret = scan_metadata_ffmpeg(path, mfi);
|
||||
if (ret < 0)
|
||||
{
|
||||
|
@ -335,14 +335,14 @@ scan_metadata_ffmpeg(char *file, struct media_file_info *mfi)
|
||||
#if LIBAVFORMAT_VERSION_MAJOR >= 54 || (LIBAVFORMAT_VERSION_MAJOR == 53 && LIBAVFORMAT_VERSION_MINOR >= 3)
|
||||
# ifndef HAVE_FFMPEG
|
||||
// Without this, libav is slow to probe some internet streams
|
||||
if (mfi->data_kind == DATA_KIND_URL)
|
||||
if (mfi->data_kind == DATA_KIND_HTTP)
|
||||
{
|
||||
ctx = avformat_alloc_context();
|
||||
ctx->probesize = 64000;
|
||||
}
|
||||
# endif
|
||||
|
||||
if (mfi->data_kind == DATA_KIND_URL)
|
||||
if (mfi->data_kind == DATA_KIND_HTTP)
|
||||
{
|
||||
free(path);
|
||||
ret = http_stream_setup(&path, file);
|
||||
@ -481,8 +481,8 @@ scan_metadata_ffmpeg(char *file, struct media_file_info *mfi)
|
||||
|
||||
DPRINTF(E_DBG, L_SCAN, "Duration %d ms, bitrate %d kbps\n", mfi->song_length, mfi->bitrate);
|
||||
|
||||
/* Try to extract ICY metadata if url/stream */
|
||||
if (mfi->data_kind == DATA_KIND_URL)
|
||||
/* Try to extract ICY metadata if http stream */
|
||||
if (mfi->data_kind == DATA_KIND_HTTP)
|
||||
{
|
||||
icy_metadata = http_icy_metadata_get(ctx, 0);
|
||||
if (icy_metadata && icy_metadata->name)
|
||||
|
@ -170,7 +170,7 @@ dacp_nowplaying(struct evbuffer *evbuf, struct player_status *status, struct med
|
||||
* FIXME: Giving the client invalid ids on purpose is hardly ideal, but the
|
||||
* clients don't seem to use these ids for anything other than rating.
|
||||
*/
|
||||
if (mfi->data_kind == DATA_KIND_URL)
|
||||
if (mfi->data_kind == DATA_KIND_HTTP)
|
||||
{
|
||||
id = djb_hash(mfi->album, strlen(mfi->album));
|
||||
songalbumid = (int64_t)id;
|
||||
|
@ -491,7 +491,7 @@ scrobble(int id)
|
||||
goto noscrobble;
|
||||
|
||||
// Don't scrobble non-music and radio stations
|
||||
if ((mfi->media_kind != MEDIA_KIND_MUSIC) || (mfi->data_kind == DATA_KIND_URL))
|
||||
if ((mfi->media_kind != MEDIA_KIND_MUSIC) || (mfi->data_kind == DATA_KIND_HTTP))
|
||||
goto noscrobble;
|
||||
|
||||
// Don't scrobble songs with unknown artist
|
||||
|
@ -81,7 +81,7 @@ pipe_setup(struct media_file_info *mfi)
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
|
93
src/player.c
93
src/player.c
@ -1173,21 +1173,21 @@ player_queue_make_mpd(char *path, int recursive)
|
||||
static void
|
||||
source_free(struct player_source *ps)
|
||||
{
|
||||
switch (ps->type)
|
||||
switch (ps->data_kind)
|
||||
{
|
||||
case SOURCE_FILE:
|
||||
case SOURCE_HTTP:
|
||||
case DATA_KIND_FILE:
|
||||
case DATA_KIND_HTTP:
|
||||
if (ps->ctx)
|
||||
transcode_cleanup(ps->ctx);
|
||||
break;
|
||||
|
||||
case SOURCE_SPOTIFY:
|
||||
case DATA_KIND_SPOTIFY:
|
||||
#ifdef HAVE_SPOTIFY_H
|
||||
spotify_playback_stop();
|
||||
#endif
|
||||
break;
|
||||
|
||||
case SOURCE_PIPE:
|
||||
case DATA_KIND_PIPE:
|
||||
pipe_cleanup();
|
||||
break;
|
||||
}
|
||||
@ -1202,10 +1202,10 @@ source_stop(struct player_source *ps)
|
||||
|
||||
while (ps)
|
||||
{
|
||||
switch (ps->type)
|
||||
switch (ps->data_kind)
|
||||
{
|
||||
case SOURCE_FILE:
|
||||
case SOURCE_HTTP:
|
||||
case DATA_KIND_FILE:
|
||||
case DATA_KIND_HTTP:
|
||||
if (ps->ctx)
|
||||
{
|
||||
transcode_cleanup(ps->ctx);
|
||||
@ -1213,13 +1213,13 @@ source_stop(struct player_source *ps)
|
||||
}
|
||||
break;
|
||||
|
||||
case SOURCE_SPOTIFY:
|
||||
case DATA_KIND_SPOTIFY:
|
||||
#ifdef HAVE_SPOTIFY_H
|
||||
spotify_playback_stop();
|
||||
#endif
|
||||
break;
|
||||
|
||||
case SOURCE_PIPE:
|
||||
case DATA_KIND_PIPE:
|
||||
pipe_cleanup();
|
||||
break;
|
||||
}
|
||||
@ -1360,7 +1360,7 @@ source_reshuffle(void)
|
||||
|
||||
/* Helper */
|
||||
static int
|
||||
source_open(struct player_source *ps, int no_md)
|
||||
source_open(struct player_source *ps, int no_md, int seek)
|
||||
{
|
||||
struct media_file_info *mfi;
|
||||
char *url;
|
||||
@ -1390,12 +1390,13 @@ source_open(struct player_source *ps, int no_md)
|
||||
|
||||
DPRINTF(E_INFO, L_PLAYER, "Opening '%s' (%s)\n", mfi->title, mfi->path);
|
||||
|
||||
ps->data_kind = mfi->data_kind;
|
||||
ps->media_kind = mfi->media_kind;
|
||||
|
||||
// Setup the source type responsible for getting the audio
|
||||
switch (mfi->data_kind)
|
||||
{
|
||||
case DATA_KIND_URL:
|
||||
ps->type = SOURCE_HTTP;
|
||||
|
||||
case DATA_KIND_HTTP:
|
||||
ret = http_stream_setup(&url, mfi->path);
|
||||
if (ret < 0)
|
||||
break;
|
||||
@ -1407,22 +1408,29 @@ source_open(struct player_source *ps, int no_md)
|
||||
break;
|
||||
|
||||
case DATA_KIND_SPOTIFY:
|
||||
ps->type = SOURCE_SPOTIFY;
|
||||
#ifdef HAVE_SPOTIFY_H
|
||||
ret = spotify_playback_play(mfi);
|
||||
if (seek && mfi->seek)
|
||||
{
|
||||
DPRINTF(E_DBG, L_PLAYER, "Source id %d started with seek %d\n", ps->id, mfi->seek);
|
||||
ret = spotify_playback_seek(mfi->seek);
|
||||
}
|
||||
#else
|
||||
ret = -1;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case DATA_KIND_PIPE:
|
||||
ps->type = SOURCE_PIPE;
|
||||
ret = pipe_setup(mfi);
|
||||
break;
|
||||
|
||||
default:
|
||||
ps->type = SOURCE_FILE;
|
||||
ret = transcode_setup(&ps->ctx, mfi, NULL, 0);
|
||||
if (seek && mfi->seek)
|
||||
{
|
||||
DPRINTF(E_DBG, L_PLAYER, "Source id %d started with seek %d\n", ps->id, mfi->seek);
|
||||
ret = transcode_seek(ps->ctx, mfi->seek);
|
||||
}
|
||||
}
|
||||
|
||||
free_mfi(mfi, 0);
|
||||
@ -1439,7 +1447,7 @@ source_open(struct player_source *ps, int no_md)
|
||||
|
||||
ps->setup_done = 1;
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
@ -1479,7 +1487,7 @@ source_next(int force)
|
||||
if (!cur_streaming)
|
||||
break;
|
||||
|
||||
if ((cur_streaming->type == SOURCE_FILE) && cur_streaming->ctx)
|
||||
if ((cur_streaming->data_kind == DATA_KIND_FILE) && cur_streaming->ctx)
|
||||
{
|
||||
ret = transcode_seek(cur_streaming->ctx, 0);
|
||||
|
||||
@ -1491,7 +1499,7 @@ source_next(int force)
|
||||
metadata_trigger(cur_streaming, 0);
|
||||
}
|
||||
else
|
||||
ret = source_open(cur_streaming, force);
|
||||
ret = source_open(cur_streaming, force, 0);
|
||||
|
||||
if (ret < 0)
|
||||
{
|
||||
@ -1535,7 +1543,7 @@ source_next(int force)
|
||||
|
||||
do
|
||||
{
|
||||
ret = source_open(ps, force);
|
||||
ret = source_open(ps, force, 0);
|
||||
if (ret < 0)
|
||||
{
|
||||
if (shuffle)
|
||||
@ -1593,7 +1601,7 @@ source_prev(void)
|
||||
|
||||
do
|
||||
{
|
||||
ret = source_open(ps, 1);
|
||||
ret = source_open(ps, 1, 0);
|
||||
if (ret < 0)
|
||||
{
|
||||
if (shuffle)
|
||||
@ -1741,7 +1749,7 @@ source_check(void)
|
||||
|
||||
if (ps->setup_done)
|
||||
{
|
||||
if ((ps->type == SOURCE_FILE) && ps->ctx)
|
||||
if ((ps->data_kind == DATA_KIND_FILE) && ps->ctx)
|
||||
{
|
||||
transcode_cleanup(ps->ctx);
|
||||
ps->ctx = NULL;
|
||||
@ -1796,7 +1804,7 @@ source_check(void)
|
||||
|
||||
if (ps->setup_done)
|
||||
{
|
||||
if ((ps->type == SOURCE_FILE) && ps->ctx)
|
||||
if ((ps->data_kind == DATA_KIND_FILE) && ps->ctx)
|
||||
{
|
||||
transcode_cleanup(ps->ctx);
|
||||
ps->ctx = NULL;
|
||||
@ -1882,26 +1890,26 @@ source_read(uint8_t *buf, int len, uint64_t rtptime)
|
||||
|
||||
if (evbuffer_get_length(audio_buf) == 0)
|
||||
{
|
||||
switch (cur_streaming->type)
|
||||
switch (cur_streaming->data_kind)
|
||||
{
|
||||
case SOURCE_HTTP:
|
||||
case DATA_KIND_HTTP:
|
||||
ret = transcode(cur_streaming->ctx, audio_buf, len - nbytes, &icy_timer);
|
||||
|
||||
if (icy_timer)
|
||||
metadata_check_icy();
|
||||
break;
|
||||
|
||||
case SOURCE_FILE:
|
||||
case DATA_KIND_FILE:
|
||||
ret = transcode(cur_streaming->ctx, audio_buf, len - nbytes, &icy_timer);
|
||||
break;
|
||||
|
||||
#ifdef HAVE_SPOTIFY_H
|
||||
case SOURCE_SPOTIFY:
|
||||
case DATA_KIND_SPOTIFY:
|
||||
ret = spotify_audio_get(audio_buf, len - nbytes);
|
||||
break;
|
||||
#endif
|
||||
|
||||
case SOURCE_PIPE:
|
||||
case DATA_KIND_PIPE:
|
||||
ret = pipe_audio_get(audio_buf, len - nbytes);
|
||||
break;
|
||||
|
||||
@ -2647,7 +2655,7 @@ artwork_url_get(struct player_command *cmd)
|
||||
return -1;
|
||||
|
||||
/* Check that we are playing a viable stream, and that it has the requested id */
|
||||
if (!ps->ctx || ps->type != SOURCE_HTTP || ps->id != cmd->arg.icy.id)
|
||||
if (!ps->ctx || ps->data_kind != DATA_KIND_HTTP || ps->id != cmd->arg.icy.id)
|
||||
return -1;
|
||||
|
||||
transcode_metadata_artwork_url(ps->ctx, &cmd->arg.icy.artwork_url);
|
||||
@ -2874,7 +2882,7 @@ playback_start(struct player_command *cmd)
|
||||
else
|
||||
cur_streaming = ps;
|
||||
|
||||
ret = source_open(cur_streaming, 0);
|
||||
ret = source_open(cur_streaming, 0, 1);
|
||||
if (ret < 0)
|
||||
{
|
||||
DPRINTF(E_LOG, L_PLAYER, "Couldn't jump to source %d in queue\n", cur_streaming->id);
|
||||
@ -2886,7 +2894,7 @@ playback_start(struct player_command *cmd)
|
||||
if (idx_id)
|
||||
*idx_id = cur_streaming->id;
|
||||
|
||||
cur_streaming->stream_start = last_rtptime + AIRTUNES_V2_PACKET_SAMPLES;
|
||||
cur_streaming->stream_start = last_rtptime + AIRTUNES_V2_PACKET_SAMPLES - ((uint64_t)ret * 44100) / 1000;
|
||||
cur_streaming->output_start = cur_streaming->stream_start;
|
||||
}
|
||||
else if (!cur_streaming)
|
||||
@ -3023,7 +3031,7 @@ playback_prev_bh(struct player_command *cmd)
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = source_open(cur_streaming, 1);
|
||||
ret = source_open(cur_streaming, 1, 0);
|
||||
if (ret < 0)
|
||||
{
|
||||
playback_abort();
|
||||
@ -3103,18 +3111,18 @@ playback_seek_bh(struct player_command *cmd)
|
||||
ps->end = 0;
|
||||
|
||||
/* Seek to commanded position */
|
||||
switch (ps->type)
|
||||
switch (ps->data_kind)
|
||||
{
|
||||
case SOURCE_FILE:
|
||||
case DATA_KIND_FILE:
|
||||
ret = transcode_seek(ps->ctx, ms);
|
||||
break;
|
||||
#ifdef HAVE_SPOTIFY_H
|
||||
case SOURCE_SPOTIFY:
|
||||
case DATA_KIND_SPOTIFY:
|
||||
ret = spotify_playback_seek(ms);
|
||||
break;
|
||||
#endif
|
||||
case SOURCE_PIPE:
|
||||
case SOURCE_HTTP:
|
||||
case DATA_KIND_PIPE:
|
||||
case DATA_KIND_HTTP:
|
||||
ret = 1;
|
||||
break;
|
||||
|
||||
@ -3162,13 +3170,13 @@ playback_pause_bh(struct player_command *cmd)
|
||||
pos -= ps->stream_start;
|
||||
ms = (int)((pos * 1000) / 44100);
|
||||
|
||||
switch (ps->type)
|
||||
switch (ps->data_kind)
|
||||
{
|
||||
case SOURCE_FILE:
|
||||
case DATA_KIND_FILE:
|
||||
ret = transcode_seek(ps->ctx, ms);
|
||||
break;
|
||||
#ifdef HAVE_SPOTIFY_H
|
||||
case SOURCE_SPOTIFY:
|
||||
case DATA_KIND_SPOTIFY:
|
||||
ret = spotify_playback_seek(ms);
|
||||
break;
|
||||
#endif
|
||||
@ -3192,6 +3200,9 @@ playback_pause_bh(struct player_command *cmd)
|
||||
|
||||
status_update(PLAY_PAUSED);
|
||||
|
||||
if (ps->media_kind & (MEDIA_KIND_MOVIE | MEDIA_KIND_PODCAST | MEDIA_KIND_AUDIOBOOK | MEDIA_KIND_TVSHOW))
|
||||
db_file_save_seek(ps->id, ret);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
10
src/player.h
10
src/player.h
@ -33,13 +33,6 @@ enum repeat_mode {
|
||||
REPEAT_ALL = 2,
|
||||
};
|
||||
|
||||
enum source_type {
|
||||
SOURCE_FILE = 0,
|
||||
SOURCE_SPOTIFY,
|
||||
SOURCE_PIPE,
|
||||
SOURCE_HTTP,
|
||||
};
|
||||
|
||||
struct spk_flags {
|
||||
unsigned selected:1;
|
||||
unsigned has_password:1;
|
||||
@ -84,7 +77,8 @@ struct player_source
|
||||
uint32_t id;
|
||||
uint32_t len_ms;
|
||||
|
||||
enum source_type type;
|
||||
enum data_kind data_kind;
|
||||
enum media_kind media_kind;
|
||||
int setup_done;
|
||||
|
||||
uint64_t stream_start;
|
||||
|
@ -507,13 +507,13 @@ transcode_setup(struct transcode_ctx **nctx, struct media_file_info *mfi, off_t
|
||||
#if LIBAVFORMAT_VERSION_MAJOR >= 54 || (LIBAVFORMAT_VERSION_MAJOR == 53 && LIBAVFORMAT_VERSION_MINOR >= 3)
|
||||
# ifndef HAVE_FFMPEG
|
||||
// Without this, libav is slow to probe some internet streams, which leads to RAOP timeouts
|
||||
if (mfi->data_kind == DATA_KIND_URL)
|
||||
if (mfi->data_kind == DATA_KIND_HTTP)
|
||||
{
|
||||
ctx->fmtctx = avformat_alloc_context();
|
||||
ctx->fmtctx->probesize = 64000;
|
||||
}
|
||||
# endif
|
||||
if (mfi->data_kind == DATA_KIND_URL)
|
||||
if (mfi->data_kind == DATA_KIND_HTTP)
|
||||
av_dict_set(&options, "icy", "1", 0);
|
||||
|
||||
ret = avformat_open_input(&ctx->fmtctx, mfi->path, NULL, &options);
|
||||
|
Loading…
Reference in New Issue
Block a user