Skip to content

Conversation

yatinlala
Copy link

The History menu (CTRL-P -> History) displays the titles of previously played files, but the Watch later menu displays a list of filenames.

This PR adds --write-title-in-watch-later-config. With the option set, save-position-on-quit inserts the playing file's title in a comment below the filename in the file's watch_later entry. The Watch later picker then displays these titles instead of filenames when they are available.

Examples

  • mpv --write-filename-in-watch-later-config:
  • # https://example.com/?v=foobar
    start=35
    gamma=1
  • mpv --write-filename-in-watch-later-config --write-title-in-watch-later-config:
  • # https://example.com/?v=foobar
    # FooBar (HD Video)
    start=35
    gamma=1
  • mpv --write-title-in-watch-later-config:
  • start=35
    gamma=1

Notes:

  • --write-title-in-watch-later-config silently no-ops if --write-filename-in-watch-later config is not set. The Watch later menu is disabled without --write-filename-in-watch-later-config anyway, so this seems like a reasonable behaviour to me. If it is preferred that --write-title... automatically enables --write-filename... or emits an error, let me know.

  • These changes are backwards compatible with older versions of MPV, since, forcing --write-filename... to be set ensures that a title comment will never be inserted without a filename comment above it. Title comments will never be misinterpreted as paths to a file by older versions.

  • Titles are displayed instead of filenames in the Watch later menu if --write-title-in-watch-later-config is set, otherwise existing behaviour is preserved. Perhaps this should be separated out into its own option, applying to both the History and Watch later menus?

  • I acknowledge this is a hacky solution. If breaking backwards compatibility is an option, it might be nice to replace the watch_later system with a structured data format in line with watch_history.jsonl.

@guidocella
Copy link
Contributor

guidocella commented Jul 12, 2025

Thanks for the patch, I also thought this would be useful for web URLs and this was requested on IRC.

Some thoughts:

  • Do we need a new option instead of just always saving the title with --write-filename-in-watch-later-config, like saving history always saves the title? Is there a reason to save the filename but not the title?
  • Can we call format_history_entry() so the format is consistent with history? Which also allows customizing the date format
  • I acknowledge this is a hacky solution. If breaking backwards compatibility is an option, it might be nice to replace the watch_later system with a structured data format in line with watch_history.jsonl.

I chose jsonl for history because it is only appended to, it is slow if you rewrite the whole file. That is why the single-file shader cache was replaced with multiple files. In fact doing the opposite was requested in
#16429 (comment)

@guidocella
Copy link
Contributor

Also maybe we should prefix title= so we can add other data in the future. I know @Earnestly saves the playlist title with a custom script for selecting youtube playlists, maybe he has suggestions?

@Earnestly
Copy link

Earnestly commented Jul 12, 2025

Unhelpfully I don't have an opinion on how this all might integrate with mpv's new menuing system, which is the crux of this PR. I've not really had much chance to make use of it yet, although I do intend to eventually as the playlist menu has been extremely handy.

As for my own history on this topic, here is what I can share: my old PR #13476 did something similar but preserved the redirect path instead of adding a title.

I abandoned this approach because it would end up overburdening mpv's watch_later files as I expanded the data I would want to include.

Instead I wrote my own "watch later" command as lua script. Here I can include all the data I want. It is bound as Shift+q script-message super-quit-watch-later as the script calls quit-watch-later; it acts as a superset of its functionality (hence the name). This means I still use watch_later for its purpose which is resuming playback.

(It does use JSONL as its database format, but it's managed with an external menuing script instead: superwatchlater).

As an aside, title is a little ambiguous because it can refer to multiple things; this PR uses (afaict) media-title but in my case I need both the playlist title and video title (which is what #15098 was for), this requirement was the push for me dropping captured metadata inside watch_later files; the parsing (and dependency resolution between files) became more involved than it needed to be.

@guidocella
Copy link
Contributor

Ok I guess the title= prefix is not needed since the playlist title would be stored separately in the redirect entry. I think this PR can just store the video title, and a future PR can resurrect #13476 and also store the playlist title after the playlist path. Then redirect entries with a title can be added to the menu.

Also unlike with json this all breaks with paths and titles containing newlines, but they are not common anyway.

@Earnestly
Copy link

Also unlike with json this all breaks with paths and titles containing newlines, but they are not common anyway.

Newlines aren't the problem with json, however json does require valid UTF-8 which is not required by unix filenames.

@yatinlala yatinlala force-pushed the title-in-watch-later branch from cf6ea86 to cef46ac Compare July 14, 2025 23:16
@yatinlala
Copy link
Author

Thank you for all the feedback!

  • Do we need a new option instead of just always saving the title with --write-filename-in-watch-later-config, like saving history always saves the title? Is there a reason to save the filename but not the title?

You're right! Storing filename without title certainly seems pointless. I could imagine someone wanting to be able to choose between displaying titles and filenames in the menu, but it is a separate thing. I've rolled the change into --write-filename-... if that is alright with you.

  • Can we call format_history_entry() so the format is consistent with history? Which also allows customizing the date format

Done.

  • Ok I guess the title= prefix is not needed since the playlist title would be stored separately in the redirect entry.

I haven't added a prefix as per your discussion. It seems to me that a prefix would make it a bit nicer to add more metadata down the line, but I'm uneducated about how playlists work and what additional metadata you might want to store. Let me know if you have a contrary preference.

Copy link

github-actions bot commented Jul 15, 2025

const char *title = mp_find_non_filename_media_title(mpctx);
if (title) {
char write_title[1024] = {0};
for (int n = 0; title[n] && n < sizeof(write_title) - 1; n++)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know this is copied from the function above but is - 1 necessary?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need write_title[1023] to be reserved for the null byte, so sizeof(write_title) - 1 populates the array up to write_title[1022].

@guidocella
Copy link
Contributor

I haven't added a prefix as per your discussion. It seems to me that a prefix would make it a bit nicer to add more metadata down the line, but I'm uneducated about how playlists work and what additional metadata you might want to store. Let me know if you have a contrary preference.

Dunno I can't think of other metadata we may need. If you or someone else thinks a prefix is better go ahead and add it.

@yatinlala yatinlala force-pushed the title-in-watch-later branch 2 times, most recently from 3245d7b to d0fcc85 Compare July 16, 2025 19:28
@yatinlala
Copy link
Author

One last change — apologies @guidocella6a0454c makes select.lua check all lines after url line for # title:. This way we can have unordered metadata in the future that relies on the prefix to identify what it is, instead of hardcoding line numbers.

@guidocella
Copy link
Contributor

Ok I don't think that needs to be done preemptively but whatever you prefer. Do you have other metadata to store in mind?

@yatinlala
Copy link
Author

yatinlala commented Jul 16, 2025

Nothing specific in mind. Thumbnail paths or arbitrary user metadata via the Lua API could be cool, but is perhaps unneeded. I'm not familiar enough with the project to know what is possible already and what makes sense to add. 🙂

I do think that it beats the point of a prefix to assume specific pieces of metadata are coming on specific lines (beyond perhaps better readability). If hardcoding the line number is the way to go, perhaps I should get rid of the prefix all together.

In my opinion, this way gives more forward flexibility for little cost though.

@yatinlala yatinlala force-pushed the title-in-watch-later branch from 6a0454c to cdb6743 Compare July 16, 2025 21:00
@yatinlala yatinlala changed the title options: add --write-title-in-watch-later-config options: make --write-filename-in-watch-later-config write title as well Jul 18, 2025
@yatinlala yatinlala force-pushed the title-in-watch-later branch from cdb6743 to fe132d1 Compare July 24, 2025 02:49
@yatinlala yatinlala force-pushed the title-in-watch-later branch from fe132d1 to 1f6dcda Compare August 2, 2025 16:13
@yatinlala yatinlala force-pushed the title-in-watch-later branch from 1f6dcda to 067d46b Compare August 10, 2025 16:07
@guidocella
Copy link
Contributor

This implements playlist selection with the playlist title on top of this PR. It doesn't have to be included in this PR but it may be worth considering beforehand.

From 629cb4aab0365f1cb7c7263751cf22cca153607a Mon Sep 17 00:00:00 2001
From: Guido Cella <***>
Date: Wed, 13 Aug 2025 13:16:09 +0200
Subject: [PATCH 1/2] player/configfiles: write playlist titles in watch later
 files

If ytdl_hook extracted a playlist title, write it in the watch later
redirect entry. This will be shown in the watch later menu.
---
 player/configfiles.c | 19 +++++++++----------
 1 file changed, 9 insertions(+), 10 deletions(-)

diff --git a/player/configfiles.c b/player/configfiles.c
index f2bce93001..3ead8d124c 100644
--- a/player/configfiles.c
+++ b/player/configfiles.c
@@ -266,16 +266,13 @@ static void write_filename(struct MPContext *mpctx, FILE *file, char *filename)
     }
 }

-static void write_title(struct MPContext *mpctx, FILE *file)
+static void write_title(struct MPContext *mpctx, FILE *file, const char *title)
 {
-    if (mpctx->opts->write_filename_in_watch_later_config) {
-        const char *title = mp_find_non_filename_media_title(mpctx);
-        if (title) {
-            char write_title[1024] = {0};
-            for (int n = 0; title[n] && n < sizeof(write_title) - 1; n++)
-                write_title[n] = (unsigned char)title[n] < 32 ? '_' : title[n];
-            fprintf(file, "# title: %s\n", write_title);
-        }
+    if (mpctx->opts->write_filename_in_watch_later_config && title) {
+        char write_title[1024] = {0};
+        for (int n = 0; title[n] && n < sizeof(write_title) - 1; n++)
+            write_title[n] = (unsigned char)title[n] < 32 ? '_' : title[n];
+        fprintf(file, "# title: %s\n", write_title);
     }
 }

@@ -287,6 +284,8 @@ static void write_redirect(struct MPContext *mpctx, char *path)
         if (file) {
             fprintf(file, "# redirect entry\n");
             write_filename(mpctx, file, path);
+            write_title(mpctx, file,
+                        mp_tags_get_str(mpctx->demuxer->metadata, "ytdl_playlist_title"));
             fclose(file);
         }

@@ -352,7 +351,7 @@ void mp_write_watch_later_conf(struct MPContext *mpctx)

     write_filename(mpctx, file, path);

-    write_title(mpctx, file);
+    write_title(mpctx, file, mp_find_non_filename_media_title(mpctx));

     bool write_start = true;
     double pos = get_playback_time(mpctx);
--
2.50.1


From c85229f6353f5040c94ae5741ef96c28122b15bc Mon Sep 17 00:00:00 2001
From: Guido Cella <***>
Date: Wed, 13 Aug 2025 13:21:38 +0200
Subject: [PATCH 2/2] select.lua: show playlists with a title in watch later
 menu

Add redirect entries with a title to the watch later menu, e.g. youtube
playlists.

Checking for the title avoids showing redirect entries of local
directories, which would drastically increase the number of entries,
when you can already play adjacent local files with
--autocreate-playlist.
---
 player/lua/select.lua | 24 ++++++++++++++++++------
 1 file changed, 18 insertions(+), 6 deletions(-)

diff --git a/player/lua/select.lua b/player/lua/select.lua
index 01817e35ff..07996233e9 100644
--- a/player/lua/select.lua
+++ b/player/lua/select.lua
@@ -571,19 +571,31 @@ mp.add_key_binding(nil, "select-watch-later", function ()
         local file_handle = io.open(watch_later_file)
         if file_handle then
             local line1 = file_handle:read()
-            if line1 and line1 ~= "# redirect entry" and line1:find("^#") then
-                local entry = {
-                    path = line1:sub(3),
-                    time = utils.file_info(watch_later_file).mtime
-                }
+            local is_redirect = false
+
+            if line1 == "# redirect entry" then
+                is_redirect = true
+                line1 = file_handle:read()
+            end
+
+            if line1 and line1:find("^#") then
+                local entry = {}
+
                 for line in file_handle:lines() do
                     if line:find("^# title: ") then
                         entry.title = line:sub(10)
                         break
                     end
                 end
-                files[#files + 1] = entry
+
+                if not is_redirect or entry.title then
+                    entry.path = line1:sub(3)
+                    entry.time = utils.file_info(watch_later_file).mtime
+
+                    files[#files + 1] = entry
+                end
             end
+
             file_handle:close()
         end
     end
--
2.50.1

yatinlala and others added 5 commits August 24, 2025 21:32
write_title() writes the media title to the media's watch_later file
as a comment if 'write-filename-in-watch-later-config' is set. Prior to
this change, setting the option would only write a commented filename at
the top of the file. Now the media title is commented below the
filename, with the prefix "title: "

Before:
 # https://example.com/watch?v=foobar
 start=35
 gamma=1

After:
 # https://example.com/watch?v=foobar
 # title: Foo Bar (HD Video)
 start=35
 gamma=1
If --write-filename-in-watch-later-config is set and a title is written
in the watch later file, display it in the Watch later menu.
If ytdl_hook extracted a playlist title, write it in the watch later
redirect entry. This will be shown in the watch later menu.
Add redirect entries with a title to the watch later menu, e.g. youtube
playlists.

Checking for the title avoids showing redirect entries of local
directories, which would drastically increase the number of entries,
when you can already play adjacent local files with
--autocreate-playlist.
@yatinlala yatinlala force-pushed the title-in-watch-later branch from 067d46b to 7d78a66 Compare August 25, 2025 04:33
@yatinlala yatinlala changed the title options: make --write-filename-in-watch-later-config write title as well options: improve watch later menu Aug 25, 2025
@guidocella
Copy link
Contributor

Commit 4 needs to check if demuxer is available in case you quit-watch-later while a network file is still loading:

From e34394901fdaa977f0f8fc77b7e97dc72f3c67af Mon Sep 17 00:00:00 2001
From: Guido Cella <***>
Date: Wed, 13 Aug 2025 13:16:09 +0200
Subject: [PATCH] player/configfiles: write playlist titles in watch later
 files

If ytdl_hook extracted a playlist title, write it in the watch later
redirect entry. This will be shown in the watch later menu.
---
 player/configfiles.c | 21 +++++++++++----------
 1 file changed, 11 insertions(+), 10 deletions(-)

diff --git a/player/configfiles.c b/player/configfiles.c
index f2bce93001..0c07bd5aa3 100644
--- a/player/configfiles.c
+++ b/player/configfiles.c
@@ -266,16 +266,13 @@ static void write_filename(struct MPContext *mpctx, FILE *file, char *filename)
     }
 }

-static void write_title(struct MPContext *mpctx, FILE *file)
+static void write_title(struct MPContext *mpctx, FILE *file, const char *title)
 {
-    if (mpctx->opts->write_filename_in_watch_later_config) {
-        const char *title = mp_find_non_filename_media_title(mpctx);
-        if (title) {
-            char write_title[1024] = {0};
-            for (int n = 0; title[n] && n < sizeof(write_title) - 1; n++)
-                write_title[n] = (unsigned char)title[n] < 32 ? '_' : title[n];
-            fprintf(file, "# title: %s\n", write_title);
-        }
+    if (mpctx->opts->write_filename_in_watch_later_config && title) {
+        char write_title[1024] = {0};
+        for (int n = 0; title[n] && n < sizeof(write_title) - 1; n++)
+            write_title[n] = (unsigned char)title[n] < 32 ? '_' : title[n];
+        fprintf(file, "# title: %s\n", write_title);
     }
 }

@@ -287,6 +284,10 @@ static void write_redirect(struct MPContext *mpctx, char *path)
         if (file) {
             fprintf(file, "# redirect entry\n");
             write_filename(mpctx, file, path);
+            if (mpctx->demuxer) {
+                write_title(mpctx, file,
+                            mp_tags_get_str(mpctx->demuxer->metadata, "ytdl_playlist_title"));
+            }
             fclose(file);
         }

@@ -352,7 +353,7 @@ void mp_write_watch_later_conf(struct MPContext *mpctx)

     write_filename(mpctx, file, path);

-    write_title(mpctx, file);
+    write_title(mpctx, file, mp_find_non_filename_media_title(mpctx));

     bool write_start = true;
     double pos = get_playback_time(mpctx);
--
2.51.0

(also you could fix the email in my commits)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants