player: use XDG_STATE_HOME for watch_later

A pain point for some users is the fact that watch_later is stored in
the ~/.config directory when it's really not configuration data. Roughly
2 years ago, XDG_STATE_DIR was added to the XDG Base Directory
Specification[0] and its description, user-specific state data, actually
perfectly matches what watch_later data is for. Let's go ahead and use
this directory as the default for watch_later. This change only affects
non-darwin unix-like systems (i.e. Linux, BSDs, etc.). The directory
doesn't move for anyone else.

Internally, quite a few things change with regards to the path
selection. If the platform in question does not have a statedir concept,
then the path selection will simply return "home" instead (old
behavior). Fixes #9147.

[0]: 4f2884e16d
This commit is contained in:
Dudemanguy 2023-05-02 19:29:27 -05:00
parent 5158b85b21
commit 7c4c9bc86f
9 changed files with 75 additions and 39 deletions

View File

@ -61,6 +61,9 @@ Interface changes
`--gamma` to float. `--gamma` to float.
- add `platform` property - add `platform` property
- add `--auto-window-resize` - add `--auto-window-resize`
- `--save-position-on-quit` and its associated commands now store state files in
the XDG_STATE_HOME directory by default. This only has an effect on linux/bsd
systems.
--- mpv 0.35.0 --- --- mpv 0.35.0 ---
- add the `--vo=gpu-next` video output driver, as well as the options - add the `--vo=gpu-next` video output driver, as well as the options
`--allow-delayed-peak-detect`, `--builtin-scalers`, `--allow-delayed-peak-detect`, `--builtin-scalers`,

View File

@ -461,6 +461,8 @@ Name Meaning
``~~desktop/`` the path to the desktop (win32, macOS) ``~~desktop/`` the path to the desktop (win32, macOS)
``~~exe_dir/`` win32 only: the path to the directory containing the exe (for ``~~exe_dir/`` win32 only: the path to the directory containing the exe (for
config file purposes; ``$MPV_HOME`` overrides it) config file purposes; ``$MPV_HOME`` overrides it)
``~~state/`` the path to application state data (``~/.local/state/mpv/``)
On some platforms, this will be the same as ``~~home/``.
``~~old_home/`` do not use ``~~old_home/`` do not use
================ =============================================================== ================ ===============================================================
@ -1584,7 +1586,7 @@ For Windows-specifics, see `FILES ON WINDOWS`_ section.
See `Script location`_ for details. See `Script location`_ for details.
``~/.config/mpv/watch_later/`` ``~/.local/state/mpv/watch_later/``
Contains temporary config files needed for resuming playback of files with Contains temporary config files needed for resuming playback of files with
the watch later feature. See for example the ``Q`` key binding, or the the watch later feature. See for example the ``Q`` key binding, or the
``quit-watch-later`` input command. ``quit-watch-later`` input command.

View File

@ -1015,8 +1015,9 @@ Watch Later
``--watch-later-directory=<path>`` ``--watch-later-directory=<path>``
The directory in which to store the "watch later" temporary files. The directory in which to store the "watch later" temporary files.
The default is a subdirectory named "watch_later" underneath the If this option is unset, the files will be stored in a subdirectory
config directory (usually ``~/.config/mpv/``). named "watch_later" underneath the local state directory
(usually ``~/.local/state/mpv/``).
``--no-resume-playback`` ``--no-resume-playback``
Do not restore playback position from the ``watch_later`` configuration Do not restore playback position from the ``watch_later`` configuration

View File

@ -10,6 +10,7 @@ struct mpv_global {
struct mp_client_api *client_api; struct mp_client_api *client_api;
char *configdir; char *configdir;
struct stats_base *stats; struct stats_base *stats;
bool no_statedir;
}; };
#endif #endif

View File

@ -65,19 +65,6 @@ static const char *const config_dirs[] = {
"global", "global",
}; };
void mp_init_paths(struct mpv_global *global, struct MPOpts *opts)
{
TA_FREEP(&global->configdir);
const char *force_configdir = getenv("MPV_HOME");
if (opts->force_configdir && opts->force_configdir[0])
force_configdir = opts->force_configdir;
if (!opts->load_config)
force_configdir = "";
global->configdir = talloc_strdup(global, force_configdir);
}
// Return a platform specific path using a path type as defined in osdep/path.h. // Return a platform specific path using a path type as defined in osdep/path.h.
// Keep in mind that the only way to free the return value is freeing talloc_ctx // Keep in mind that the only way to free the return value is freeing talloc_ctx
// (or its children), as this function can return a statically allocated string. // (or its children), as this function can return a statically allocated string.
@ -87,13 +74,19 @@ static const char *mp_get_platform_path(void *talloc_ctx,
{ {
assert(talloc_ctx); assert(talloc_ctx);
if (global->configdir) { bool config_dir = strcmp(type, "state") != 0;
if (global->configdir && config_dir) {
for (int n = 0; n < MP_ARRAY_SIZE(config_dirs); n++) { for (int n = 0; n < MP_ARRAY_SIZE(config_dirs); n++) {
if (strcmp(config_dirs[n], type) == 0) if (strcmp(config_dirs[n], type) == 0)
return (n == 0 && global->configdir[0]) ? global->configdir : NULL; return (n == 0 && global->configdir[0]) ? global->configdir : NULL;
} }
} }
// Return the native config path if the platform doesn't support the
// type we are trying to fetch.
if (strcmp(type, "state") == 0 && global->no_statedir)
type = "home";
for (int n = 0; n < MP_ARRAY_SIZE(path_resolvers); n++) { for (int n = 0; n < MP_ARRAY_SIZE(path_resolvers); n++) {
const char *path = path_resolvers[n](talloc_ctx, type); const char *path = path_resolvers[n](talloc_ctx, type);
if (path && path[0]) if (path && path[0])
@ -102,6 +95,27 @@ static const char *mp_get_platform_path(void *talloc_ctx,
return NULL; return NULL;
} }
void mp_init_paths(struct mpv_global *global, struct MPOpts *opts)
{
TA_FREEP(&global->configdir);
// Check if the platform has unique directories that differ from
// the standard config directory.
void *tmp = talloc_new(NULL);
const char *state = mp_get_platform_path(tmp, global, "state");
if (!state)
global->no_statedir = true;
talloc_free(tmp);
const char *force_configdir = getenv("MPV_HOME");
if (opts->force_configdir && opts->force_configdir[0])
force_configdir = opts->force_configdir;
if (!opts->load_config)
force_configdir = "";
global->configdir = talloc_strdup(global, force_configdir);
}
char *mp_find_user_file(void *talloc_ctx, struct mpv_global *global, char *mp_find_user_file(void *talloc_ctx, struct mpv_global *global,
const char *type, const char *filename) const char *type, const char *filename)
{ {

View File

@ -27,14 +27,16 @@ static pthread_once_t path_init_once = PTHREAD_ONCE_INIT;
static char mpv_home[512]; static char mpv_home[512];
static char old_home[512]; static char old_home[512];
static char mpv_state[512];
static void path_init(void) static void path_init(void)
{ {
char *home = getenv("HOME"); char *home = getenv("HOME");
char *xdg_dir = getenv("XDG_CONFIG_HOME"); char *xdg_config = getenv("XDG_CONFIG_HOME");
char *xdg_state = getenv("XDG_STATE_HOME");
if (xdg_dir && xdg_dir[0]) { if (xdg_config && xdg_config[0]) {
snprintf(mpv_home, sizeof(mpv_home), "%s/mpv", xdg_dir); snprintf(mpv_home, sizeof(mpv_home), "%s/mpv", xdg_config);
} else if (home && home[0]) { } else if (home && home[0]) {
snprintf(mpv_home, sizeof(mpv_home), "%s/.config/mpv", home); snprintf(mpv_home, sizeof(mpv_home), "%s/.config/mpv", home);
} }
@ -43,10 +45,17 @@ static void path_init(void)
if (home && home[0]) if (home && home[0])
snprintf(old_home, sizeof(old_home), "%s/.mpv", home); snprintf(old_home, sizeof(old_home), "%s/.mpv", home);
if (xdg_state && xdg_state[0]) {
snprintf(mpv_state, sizeof(mpv_state), "%s/mpv", xdg_state);
} else if (home && home[0]) {
snprintf(mpv_state, sizeof(mpv_state), "%s/.local/state/mpv", home);
}
// If the old ~/.mpv exists, and the XDG config dir doesn't, use the old // If the old ~/.mpv exists, and the XDG config dir doesn't, use the old
// config dir only. // config dir only. Also do not use any other XDG directories.
if (mp_path_exists(old_home) && !mp_path_exists(mpv_home)) { if (mp_path_exists(old_home) && !mp_path_exists(mpv_home)) {
snprintf(mpv_home, sizeof(mpv_home), "%s", old_home); snprintf(mpv_home, sizeof(mpv_home), "%s", old_home);
snprintf(mpv_state, sizeof(mpv_state), "%s", old_home);
old_home[0] = '\0'; old_home[0] = '\0';
} }
} }
@ -58,6 +67,8 @@ const char *mp_get_platform_path_unix(void *talloc_ctx, const char *type)
return mpv_home; return mpv_home;
if (strcmp(type, "old_home") == 0) if (strcmp(type, "old_home") == 0)
return old_home; return old_home;
if (strcmp(type, "state") == 0)
return mpv_state;
if (strcmp(type, "global") == 0) if (strcmp(type, "global") == 0)
return MPV_CONFDIR; return MPV_CONFDIR;
if (strcmp(type, "desktop") == 0) if (strcmp(type, "desktop") == 0)

View File

@ -11,6 +11,12 @@
// "global" the least priority, global config file location // "global" the least priority, global config file location
// "desktop" path to desktop contents // "desktop" path to desktop contents
// //
// These additional types are also defined. However, they are not necessarily
// implemented on every platform. Unlike some other type values that are
// platform specific (like "osxbundle"), the value of "home" is returned
// instead if these types are not explicitly defined.
// "state" the native mpv-specific user state dir
//
// It is allowed to return a static string, so the caller must set talloc_ctx // It is allowed to return a static string, so the caller must set talloc_ctx
// to something other than NULL to avoid memory leaks. // to something other than NULL to avoid memory leaks.
typedef const char *(*mp_get_platform_path_cb)(void *talloc_ctx, const char *type); typedef const char *(*mp_get_platform_path_cb)(void *talloc_ctx, const char *type);

View File

@ -192,6 +192,17 @@ static bool copy_mtime(const char *f1, const char *f2)
return true; return true;
} }
static char *mp_get_playback_resume_dir(struct MPContext *mpctx)
{
char *wl_dir = mpctx->opts->watch_later_directory;
if (wl_dir && wl_dir[0]) {
wl_dir = mp_get_user_path(mpctx, mpctx->global, wl_dir);
} else {
wl_dir = mp_find_user_file(mpctx, mpctx->global, "state", MP_WATCH_LATER_CONF);
}
return wl_dir;
}
static char *mp_get_playback_resume_config_filename(struct MPContext *mpctx, static char *mp_get_playback_resume_config_filename(struct MPContext *mpctx,
const char *fname) const char *fname)
{ {
@ -216,21 +227,9 @@ static char *mp_get_playback_resume_config_filename(struct MPContext *mpctx,
for (int i = 0; i < 16; i++) for (int i = 0; i < 16; i++)
conf = talloc_asprintf_append(conf, "%02X", md5[i]); conf = talloc_asprintf_append(conf, "%02X", md5[i]);
if (!mpctx->cached_watch_later_configdir) { char *wl_dir = mp_get_playback_resume_dir(mpctx);
char *wl_dir = mpctx->opts->watch_later_directory; if (wl_dir && wl_dir[0])
if (wl_dir && wl_dir[0]) { res = mp_path_join(NULL, wl_dir, conf);
mpctx->cached_watch_later_configdir =
mp_get_user_path(mpctx, mpctx->global, wl_dir);
}
}
if (!mpctx->cached_watch_later_configdir) {
mpctx->cached_watch_later_configdir =
mp_find_user_file(mpctx, mpctx->global, "home", MP_WATCH_LATER_CONF);
}
if (mpctx->cached_watch_later_configdir)
res = mp_path_join(NULL, mpctx->cached_watch_later_configdir, conf);
exit: exit:
talloc_free(tmp); talloc_free(tmp);
@ -292,7 +291,8 @@ void mp_write_watch_later_conf(struct MPContext *mpctx)
if (!conffile) if (!conffile)
goto exit; goto exit;
mp_mk_user_dir(mpctx->global, "home", mpctx->cached_watch_later_configdir); char *wl_dir = mp_get_playback_resume_dir(mpctx);
mp_mk_user_dir(mpctx->global, "state", wl_dir);
MP_INFO(mpctx, "Saving state.\n"); MP_INFO(mpctx, "Saving state.\n");

View File

@ -424,8 +424,6 @@ typedef struct MPContext {
struct mp_recorder *recorder; struct mp_recorder *recorder;
char *cached_watch_later_configdir;
struct screenshot_ctx *screenshot_ctx; struct screenshot_ctx *screenshot_ctx;
struct command_ctx *command_ctx; struct command_ctx *command_ctx;
struct encode_lavc_context *encode_lavc_ctx; struct encode_lavc_context *encode_lavc_ctx;