mirror of https://github.com/mpv-player/mpv
player: make watch later/resume work when "playing" directories
If you do "mpv /bla/", and then branch out into sub-directories using playlist navigation, and then used quit and watch later, then playing the same directory did not resume from the previous point. This was because resuming is based on the path hash, so a path prefix can't be detected when resuming the parent directory. Solve this by writing each path prefix when playing directories is involved. (This includes all parent paths, so interestingly, "mpv /" would also resume in the above example.) Something like this was requested multiple times, and I want it too.
This commit is contained in:
parent
b7e179f6d3
commit
35f43dfacb
|
@ -206,6 +206,18 @@ void playlist_add_base_path(struct playlist *pl, bstr base_path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add redirected_from as new redirect entry to each item in pl.
|
||||||
|
void playlist_add_redirect(struct playlist *pl, const char *redirected_from)
|
||||||
|
{
|
||||||
|
for (struct playlist_entry *e = pl->first; e; e = e->next) {
|
||||||
|
if (e->num_redirects >= 10) // arbitrary limit for sanity
|
||||||
|
break;
|
||||||
|
char *s = talloc_strdup(e, redirected_from);
|
||||||
|
if (s)
|
||||||
|
MP_TARRAY_APPEND(e, e->redirects, e->num_redirects, s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Move all entries from source_pl to pl, appending them after the current entry
|
// Move all entries from source_pl to pl, appending them after the current entry
|
||||||
// of pl. source_pl will be empty, and all entries have changed ownership to pl.
|
// of pl. source_pl will be empty, and all entries have changed ownership to pl.
|
||||||
void playlist_transfer_entries(struct playlist *pl, struct playlist *source_pl)
|
void playlist_transfer_entries(struct playlist *pl, struct playlist *source_pl)
|
||||||
|
|
|
@ -36,6 +36,11 @@ struct playlist_entry {
|
||||||
|
|
||||||
char *title;
|
char *title;
|
||||||
|
|
||||||
|
// If the user plays a playlist, then the playlist's URL will be appended
|
||||||
|
// as redirect to each entry. (Same for directories etc.)
|
||||||
|
char **redirects;
|
||||||
|
int num_redirects;
|
||||||
|
|
||||||
// Set to true if playback didn't seem to work, or if the file could be
|
// Set to true if playback didn't seem to work, or if the file could be
|
||||||
// played only for a very short time. This is used to make playlist
|
// played only for a very short time. This is used to make playlist
|
||||||
// navigation just work in case the user has unplayable files in the
|
// navigation just work in case the user has unplayable files in the
|
||||||
|
@ -88,6 +93,7 @@ void playlist_add_file(struct playlist *pl, const char *filename);
|
||||||
void playlist_shuffle(struct playlist *pl);
|
void playlist_shuffle(struct playlist *pl);
|
||||||
struct playlist_entry *playlist_get_next(struct playlist *pl, int direction);
|
struct playlist_entry *playlist_get_next(struct playlist *pl, int direction);
|
||||||
void playlist_add_base_path(struct playlist *pl, bstr base_path);
|
void playlist_add_base_path(struct playlist *pl, bstr base_path);
|
||||||
|
void playlist_add_redirect(struct playlist *pl, const char *redirected_from);
|
||||||
void playlist_transfer_entries(struct playlist *pl, struct playlist *source_pl);
|
void playlist_transfer_entries(struct playlist *pl, struct playlist *source_pl);
|
||||||
void playlist_append_entries(struct playlist *pl, struct playlist *source_pl);
|
void playlist_append_entries(struct playlist *pl, struct playlist *source_pl);
|
||||||
|
|
||||||
|
|
|
@ -213,6 +213,21 @@ struct bstr mp_dirname(const char *path)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#if HAVE_DOS_PATHS
|
||||||
|
static const char mp_path_separators[] = "\\/";
|
||||||
|
#else
|
||||||
|
static const char mp_path_separators[] = "/";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Mutates path and removes a trailing '/' (or '\' on Windows)
|
||||||
|
void mp_path_strip_trailing_separator(char *path)
|
||||||
|
{
|
||||||
|
size_t len = strlen(path);
|
||||||
|
if (len > 0 && strchr(mp_path_separators, path[len - 1]))
|
||||||
|
path[len - 1] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
char *mp_splitext(const char *path, bstr *root)
|
char *mp_splitext(const char *path, bstr *root)
|
||||||
{
|
{
|
||||||
assert(path);
|
assert(path);
|
||||||
|
|
|
@ -65,6 +65,8 @@ char *mp_splitext(const char *path, bstr *root);
|
||||||
*/
|
*/
|
||||||
struct bstr mp_dirname(const char *path);
|
struct bstr mp_dirname(const char *path);
|
||||||
|
|
||||||
|
void mp_path_strip_trailing_separator(char *path);
|
||||||
|
|
||||||
/* Join two path components and return a newly allocated string
|
/* Join two path components and return a newly allocated string
|
||||||
* for the result. '/' is inserted between the components if needed.
|
* for the result. '/' is inserted between the components if needed.
|
||||||
* If p2 is an absolute path then the value of p1 is ignored.
|
* If p2 is an absolute path then the value of p1 is ignored.
|
||||||
|
|
|
@ -273,11 +273,35 @@ static bool needs_config_quoting(const char *s)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void write_filename(struct MPContext *mpctx, FILE *file, char *filename)
|
||||||
|
{
|
||||||
|
if (mpctx->opts->write_filename_in_watch_later_config) {
|
||||||
|
char write_name[1024] = {0};
|
||||||
|
for (int n = 0; filename[n] && n < sizeof(write_name) - 1; n++)
|
||||||
|
write_name[n] = (unsigned char)filename[n] < 32 ? '_' : filename[n];
|
||||||
|
fprintf(file, "# %s\n", write_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void write_redirect(struct MPContext *mpctx, char *path)
|
||||||
|
{
|
||||||
|
char *conffile = mp_get_playback_resume_config_filename(mpctx, path);
|
||||||
|
if (conffile) {
|
||||||
|
FILE *file = fopen(conffile, "wb");
|
||||||
|
if (file) {
|
||||||
|
fprintf(file, "# redirect entry\n");
|
||||||
|
write_filename(mpctx, file, path);
|
||||||
|
fclose(file);
|
||||||
|
}
|
||||||
|
talloc_free(conffile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void mp_write_watch_later_conf(struct MPContext *mpctx)
|
void mp_write_watch_later_conf(struct MPContext *mpctx)
|
||||||
{
|
{
|
||||||
char *filename = mpctx->filename;
|
struct playlist_entry *cur = mpctx->playing;
|
||||||
char *conffile = NULL;
|
char *conffile = NULL;
|
||||||
if (!filename)
|
if (!cur)
|
||||||
goto exit;
|
goto exit;
|
||||||
|
|
||||||
struct demuxer *demux = mpctx->demuxer;
|
struct demuxer *demux = mpctx->demuxer;
|
||||||
|
@ -288,7 +312,7 @@ void mp_write_watch_later_conf(struct MPContext *mpctx)
|
||||||
|
|
||||||
mp_mk_config_dir(mpctx->global, MP_WATCH_LATER_CONF);
|
mp_mk_config_dir(mpctx->global, MP_WATCH_LATER_CONF);
|
||||||
|
|
||||||
conffile = mp_get_playback_resume_config_filename(mpctx, filename);
|
conffile = mp_get_playback_resume_config_filename(mpctx, cur->filename);
|
||||||
if (!conffile)
|
if (!conffile)
|
||||||
goto exit;
|
goto exit;
|
||||||
|
|
||||||
|
@ -297,12 +321,9 @@ void mp_write_watch_later_conf(struct MPContext *mpctx)
|
||||||
FILE *file = fopen(conffile, "wb");
|
FILE *file = fopen(conffile, "wb");
|
||||||
if (!file)
|
if (!file)
|
||||||
goto exit;
|
goto exit;
|
||||||
if (mpctx->opts->write_filename_in_watch_later_config) {
|
|
||||||
char write_name[1024] = {0};
|
write_filename(mpctx, file, cur->filename);
|
||||||
for (int n = 0; filename[n] && n < sizeof(write_name) - 1; n++)
|
|
||||||
write_name[n] = (unsigned char)filename[n] < 32 ? '_' : filename[n];
|
|
||||||
fprintf(file, "# %s\n", write_name);
|
|
||||||
}
|
|
||||||
double pos = get_current_time(mpctx);
|
double pos = get_current_time(mpctx);
|
||||||
if (pos != MP_NOPTS_VALUE)
|
if (pos != MP_NOPTS_VALUE)
|
||||||
fprintf(file, "start=%f\n", pos);
|
fprintf(file, "start=%f\n", pos);
|
||||||
|
@ -328,6 +349,37 @@ void mp_write_watch_later_conf(struct MPContext *mpctx)
|
||||||
}
|
}
|
||||||
fclose(file);
|
fclose(file);
|
||||||
|
|
||||||
|
// This allows us to recursively resume directories etc., whose entries are
|
||||||
|
// expanded the first time it's "played". For example, if "/a/b/c.mkv" is
|
||||||
|
// the current entry, then we want to resume this file if the user does
|
||||||
|
// "mpv /a". This would expand to the directory entries in "/a", and if
|
||||||
|
// "/a/a.mkv" is not the first entry, this would be played.
|
||||||
|
// Here, we write resume entries for "/a" and "/a/b".
|
||||||
|
// (Unfortunately, this will leave stray resume files on resume, because
|
||||||
|
// obviously it resumes only from one of those paths.)
|
||||||
|
for (int n = 0; n < cur->num_redirects; n++)
|
||||||
|
write_redirect(mpctx, cur->redirects[n]);
|
||||||
|
// And at last, for local directories, we write an entry for each path
|
||||||
|
// prefix, so the user can resume from an arbitrary directory. This starts
|
||||||
|
// with the first redirect (all other redirects are further prefixes).
|
||||||
|
if (cur->num_redirects) {
|
||||||
|
char *path = cur->redirects[0];
|
||||||
|
char tmp[4096];
|
||||||
|
if (!mp_is_url(bstr0(path)) && strlen(path) < sizeof(tmp)) {
|
||||||
|
snprintf(tmp, sizeof(tmp), "%s", path);
|
||||||
|
for (;;) {
|
||||||
|
bstr dir = mp_dirname(tmp);
|
||||||
|
if (dir.len == strlen(tmp) || !dir.len || bstr_equals0(dir, "."))
|
||||||
|
break;
|
||||||
|
|
||||||
|
tmp[dir.len] = '\0';
|
||||||
|
if (strlen(tmp) >= 2) // keep "/"
|
||||||
|
mp_path_strip_trailing_separator(tmp);
|
||||||
|
write_redirect(mpctx, tmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
exit:
|
exit:
|
||||||
talloc_free(conffile);
|
talloc_free(conffile);
|
||||||
}
|
}
|
||||||
|
|
|
@ -836,6 +836,8 @@ static void transfer_playlist(struct MPContext *mpctx, struct playlist *pl)
|
||||||
if (pl->first) {
|
if (pl->first) {
|
||||||
prepare_playlist(mpctx, pl);
|
prepare_playlist(mpctx, pl);
|
||||||
struct playlist_entry *new = pl->current;
|
struct playlist_entry *new = pl->current;
|
||||||
|
if (mpctx->playlist->current)
|
||||||
|
playlist_add_redirect(pl, mpctx->playlist->current->filename);
|
||||||
playlist_transfer_entries(mpctx->playlist, pl);
|
playlist_transfer_entries(mpctx->playlist, pl);
|
||||||
// current entry is replaced
|
// current entry is replaced
|
||||||
if (mpctx->playlist->current)
|
if (mpctx->playlist->current)
|
||||||
|
|
Loading…
Reference in New Issue