diff options
Diffstat (limited to '')
-rw-r--r-- | LICENSE | 2 | ||||
-rwxr-xr-x | later | 81 | ||||
-rw-r--r-- | later.1 | 27 |
3 files changed, 104 insertions, 6 deletions
@@ -1,6 +1,6 @@ ISC License -Copyright (c) 2024 Wolfgang Müller +Copyright (c) 2024 Wolfgang Müller, Juhani Krekelä Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above @@ -1,8 +1,11 @@ #!/usr/bin/env python3 +import argparse import datetime +import json import os -import argparse +import re +import sys from datetime import datetime as dt @@ -12,6 +15,24 @@ if "XDG_STATE_HOME" in os.environ: watch_later_dir = os.path.join(xdg_state_home, "mpv/watch_later") +xdg_cache_home = os.path.expanduser("~/.cache") +if "XDG_CACHE_HOME" in os.environ: + xdg_cache_home = os.environ["XDG_CACHE_HOME"] + +later_cache_dir = os.path.join(xdg_cache_home, "later") +title_map_file = os.path.join(later_cache_dir, "titles.json") + + +class YTDLPLogger: + def debug(self, msg): + pass + + def warning(self, msg): + pass + + def error(self, msg): + print("later: yt-dlp:", msg, file=sys.stderr) + def entries(): def get_mtime(entry): @@ -27,8 +48,35 @@ def entries(): parser = argparse.ArgumentParser( prog="later", description="List mpv's watch_later entries" ) +parser.add_argument( + "-u", + "--update-titles", + action="store_true", + help="update titles of videos using yt-dlp", +) args = parser.parse_args() +if args.update_titles: + import yt_dlp + + yt_dlp_opts = { + "logger": YTDLPLogger(), + } + ytdl = yt_dlp.YoutubeDL(yt_dlp_opts) + +write_title_map = args.update_titles + +try: + with open(title_map_file, "r") as handle: + title_map = json.load(handle) +except FileNotFoundError: + title_map = {} +except json.decoder.JSONDecodeError: + title_map = {} + write_title_map = True +except Exception as err: + sys.exit(f"later: {err}") + for path, basename, mtime in entries(): with open(path, "r") as handle: first = handle.readline().rstrip() @@ -40,10 +88,39 @@ for path, basename, mtime in entries(): if name == "redirect entry": continue + if args.update_titles and name not in title_map: + if re.fullmatch(r"https?://.*", name): + try: + info = ytdl.extract_info(name, download=False) + + # The generic extractor uses the filename part of the url as the + # title. Since we already display the URL, this adds no extra + # information. + if info["extractor"] == "generic": + title_map[name] = "" + else: + title_map[name] = info["title"] + + except yt_dlp.utils.DownloadError: + pass + def format_time(time): now = dt.now() if time < now - datetime.timedelta(days=7): return time.strftime("%b %d %Y") return time.strftime("%b %d %H:%M") - print(f"{format_time(mtime)}\t{name}") + output = [format_time(mtime), name] + + if title := title_map.get(name): + output.append("# " + title) + + print(*output, sep="\t") + +if write_title_map: + try: + os.mkdir(later_cache_dir) + except FileExistsError: + pass + with open(title_map_file, "w") as handle: + json.dump(title_map, handle) @@ -1,4 +1,4 @@ -.Dd September 10, 2024 +.Dd September 12, 2024 .Dt LATER 1 .Os .Sh NAME @@ -6,12 +6,31 @@ .Nd list mpv's watch_later entries .Sh SYNOPSIS .Nm -.Op Fl h -.Op Fl \-help +.Op Fl u | \-update-titles +.Nm +.Fl h | \-help .Sh DESCRIPTION .Nm lists all watch_later entries as saved by .Xr mpv 1 . +.Pp +The options are as follows: +.Bl -tag -width "-h, --update-titles" +.It Fl u , Fl \-update-titles +update titles of videos using yt-dlp +.It Fl h , Fl \-help +show a conscise help message +.El +.Sh FILES +.Bl -tag -width "1" +.It Pa $XDG_CACHE_HOME/later/titles.json +JSON-encoded mapping of URLs to video titles. +Populated by running +.Nm +with the +.Fl \-update-titles +option. +.El .Sh SEE ALSO .Xr mpv 1 .Sh AUTHORS @@ -19,3 +38,5 @@ lists all watch_later entries as saved by .Nm was written by .An Wolfgang Müller Aq Mt wolf@oriole.systems +with yt-dlp integration by +.An Juhani Krekelä Aq Mt juhani@krekelä.fi |