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.
- add `platform` property
- 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 ---
- add the `--vo=gpu-next` video output driver, as well as the options
`--allow-delayed-peak-detect`, `--builtin-scalers`,

View File

@ -461,6 +461,8 @@ Name Meaning
``~~desktop/`` the path to the desktop (win32, macOS)
``~~exe_dir/`` win32 only: the path to the directory containing the exe (for
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
================ ===============================================================
@ -1584,7 +1586,7 @@ For Windows-specifics, see `FILES ON WINDOWS`_ section.
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
the watch later feature. See for example the ``Q`` key binding, or the
``quit-watch-later`` input command.

View File

@ -1015,8 +1015,9 @@ Watch Later
``--watch-later-directory=<path>``
The directory in which to store the "watch later" temporary files.
The default is a subdirectory named "watch_later" underneath the
config directory (usually ``~/.config/mpv/``).
If this option is unset, the files will be stored in a subdirectory
named "watch_later" underneath the local state directory
(usually ``~/.local/state/mpv/``).
``--no-resume-playback``
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;
char *configdir;
struct stats_base *stats;
bool no_statedir;
};
#endif

View File

@ -65,19 +65,6 @@ static const char *const config_dirs[] = {
"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.
// 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.
@ -87,13 +74,19 @@ static const char *mp_get_platform_path(void *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++) {
if (strcmp(config_dirs[n], type) == 0)
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++) {
const char *path = path_resolvers[n](talloc_ctx, type);
if (path && path[0])
@ -102,6 +95,27 @@ static const char *mp_get_platform_path(void *talloc_ctx,
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,
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 old_home[512];
static char mpv_state[512];
static void path_init(void)
{
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]) {
snprintf(mpv_home, sizeof(mpv_home), "%s/mpv", xdg_dir);
if (xdg_config && xdg_config[0]) {
snprintf(mpv_home, sizeof(mpv_home), "%s/mpv", xdg_config);
} else if (home && home[0]) {
snprintf(mpv_home, sizeof(mpv_home), "%s/.config/mpv", home);
}
@ -43,10 +45,17 @@ static void path_init(void)
if (home && home[0])
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
// 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)) {
snprintf(mpv_home, sizeof(mpv_home), "%s", old_home);
snprintf(mpv_state, sizeof(mpv_state), "%s", old_home);
old_home[0] = '\0';
}
}
@ -58,6 +67,8 @@ const char *mp_get_platform_path_unix(void *talloc_ctx, const char *type)
return mpv_home;
if (strcmp(type, "old_home") == 0)
return old_home;
if (strcmp(type, "state") == 0)
return mpv_state;
if (strcmp(type, "global") == 0)
return MPV_CONFDIR;
if (strcmp(type, "desktop") == 0)

View File

@ -11,6 +11,12 @@
// "global" the least priority, global config file location
// "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
// to something other than NULL to avoid memory leaks.
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;
}
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,
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++)
conf = talloc_asprintf_append(conf, "%02X", md5[i]);
if (!mpctx->cached_watch_later_configdir) {
char *wl_dir = mpctx->opts->watch_later_directory;
if (wl_dir && wl_dir[0]) {
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);
char *wl_dir = mp_get_playback_resume_dir(mpctx);
if (wl_dir && wl_dir[0])
res = mp_path_join(NULL, wl_dir, conf);
exit:
talloc_free(tmp);
@ -292,7 +291,8 @@ void mp_write_watch_later_conf(struct MPContext *mpctx)
if (!conffile)
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");

View File

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