diff --git a/DOCS/man/mpv.rst b/DOCS/man/mpv.rst index 7c67cf0d96d18..ecb54f42abec9 100644 --- a/DOCS/man/mpv.rst +++ b/DOCS/man/mpv.rst @@ -328,6 +328,9 @@ g-l g-d Select an audio device. +g-h + Select a file from the watch history. Requires ``--save-watch-history``. + g-w Select a file from watch later config files (see `RESUMING PLAYBACK`_) to resume playing. Requires ``--write-filename-in-watch-later-config``. This diff --git a/DOCS/man/options.rst b/DOCS/man/options.rst index 4e932f1eff69c..b00d2a2f02903 100644 --- a/DOCS/man/options.rst +++ b/DOCS/man/options.rst @@ -1148,6 +1148,17 @@ Watch Later Ignore path (i.e. use filename only) when using watch later feature. (Default: disabled) +Watch History +------------- + +``--save-watch-history`` + Whether to save which files are played (default: no). These can be then + selected with the default ``g-h`` key binding. + +``--watch-history-path=`` + The path in which to store the watch history. Default: + ``~~state/watch_history.jsonl`` (see `PATHS`_). + Video ----- diff --git a/etc/input.conf b/etc/input.conf index 2faa88c233b28..adbb824a0d838 100644 --- a/etc/input.conf +++ b/etc/input.conf @@ -187,6 +187,7 @@ #g-e script-binding select/select-edition #g-l script-binding select/select-subtitle-line #g-d script-binding select/select-audio-device +#g-h script-binding select/select-watch-history #g-w script-binding select/select-watch-later #g-b script-binding select/select-binding #g-r script-binding select/show-properties diff --git a/options/options.c b/options/options.c index 3bb958c49a042..2d7cfce922a10 100644 --- a/options/options.c +++ b/options/options.c @@ -811,6 +811,9 @@ static const m_option_t mp_opts[] = { {"watch-later-directory", OPT_ALIAS("watch-later-dir")}, {"watch-later-options", OPT_STRINGLIST(watch_later_options)}, + {"save-watch-history", OPT_BOOL(save_watch_history)}, + {"watch-history-path", OPT_STRING(watch_history_path), .flags = M_OPT_FILE}, + {"ordered-chapters", OPT_BOOL(ordered_chapters)}, {"ordered-chapters-files", OPT_STRING(ordered_chapters_files), .flags = M_OPT_FILE}, @@ -986,6 +989,7 @@ static const struct MPOpts mp_default_opts = { .sync_max_factor = 5, .load_config = true, .position_resume = true, + .watch_history_path = "~~state/watch_history.jsonl", .autoload_files = true, .demuxer_thread = true, .demux_termination_timeout = 0.1, diff --git a/options/options.h b/options/options.h index 3f184c49cf5a9..ef8c4306845db 100644 --- a/options/options.h +++ b/options/options.h @@ -276,6 +276,8 @@ typedef struct MPOpts { bool ignore_path_in_watch_later_config; char *watch_later_dir; char **watch_later_options; + bool save_watch_history; + char *watch_history_path; bool pause; int keep_open; bool keep_open_pause; diff --git a/player/lua/select.lua b/player/lua/select.lua index 7f20602aff098..57030fc149623 100644 --- a/player/lua/select.lua +++ b/player/lua/select.lua @@ -353,6 +353,93 @@ mp.add_key_binding(nil, "select-audio-device", function () }) end) +local history_file_path = + mp.command_native({"expand-path", mp.get_property("watch-history-path")}) + +mp.register_event("file-loaded", function () + if not mp.get_property_native("save-watch-history") then + return + end + + local history_file, error_message = io.open(history_file_path, "a") + if not history_file then + show_error("Failed to write the watch history: " .. error_message) + return + end + + local path = mp.command_native({"normalize-path", mp.get_property("path")}) + local title = mp.get_property("playlist/" .. mp.get_property("playlist-pos") .. "/title") + + history_file:write(utils.format_json({os.time(), path, title}) .. "\n") + history_file:close() +end) + +local function add_history_entry(line, items, paths, osd_playlist_entry) + local entry = utils.parse_json(line) + + if not entry then + mp.msg.warn(line .. " in " .. history_file_path .. " is not valid JSON.") + return + end + + local time, path, title = unpack(entry) + + local status, date = pcall(os.date, "(%Y-%m-%d %H:%M) ", time) + + if not status or not path then + mp.msg.warn(line .. " in " .. history_file_path .. " has invalid data.") + return + end + + local item = path + if title and osd_playlist_entry == "title" then + item = title + elseif title and osd_playlist_entry == "both" then + item = title .. " (" .. path .. ")" + end + + table.insert(items, 1, date .. item) + table.insert(paths, 1, path) +end + +mp.add_key_binding(nil, "select-watch-history", function () + local history_file, error_message = io.open(history_file_path) + if not history_file then + show_warning(mp.get_property_native("save-watch-history") + and error_message + or "Enable --save-watch-history") + return + end + + local items = {} + local paths = {} + local osd_playlist_entry = mp.get_property("osd-playlist-entry") + + for line in history_file:lines() do + add_history_entry(line, items, paths, osd_playlist_entry) + end + + items[#items+1] = "Clear the history" + + input.select({ + prompt = "Select a file:", + items = items, + submit = function (i) + if paths[i] then + mp.commandv("loadfile", paths[i]) + return + end + + error_message = select(2, os.remove(history_file_path)) + if error_message then + show_error(error_message) + else + mp.osd_message("History cleared.") + end + end, + }) +end) + mp.add_key_binding(nil, "select-watch-later", function () local watch_later_dir = mp.get_property("current-watch-later-dir")