diff --git a/README.md b/README.md index ad25f035..d53cfa5e 100644 --- a/README.md +++ b/README.md @@ -143,7 +143,7 @@ Incremental surfing. 📈🏄 totally rad!
List all subcommands $ library - xk media library subcommands (v2.2.045) + xk media library subcommands (v2.2.046) local media: lb fsadd Create a local media database; Add folders @@ -285,7 +285,7 @@ Wake up to your own music 30 7 * * * library listen ./audio.db -Wake up to your own music _only when you are *not* home_ (computer on local-only IP) +Wake up to your own music _only when you are *not* home_ (computer on local IP) 30 7 * * * timeout 0.4 nc -z 192.168.1.12 22 || library listen --random diff --git a/readme.py b/readme.py index a7c5d4e2..b4c8f060 100644 --- a/readme.py +++ b/readme.py @@ -269,7 +269,7 @@ 30 7 * * * library listen ./audio.db -Wake up to your own music _only when you are *not* home_ (computer on local-only IP) +Wake up to your own music _only when you are *not* home_ (computer on local IP) 30 7 * * * timeout 0.4 nc -z 192.168.1.12 22 || library listen --random diff --git a/xklb/__init__.py b/xklb/__init__.py index 2a25d921..8a009756 100644 --- a/xklb/__init__.py +++ b/xklb/__init__.py @@ -1 +1 @@ -__version__ = "2.2.045" +__version__ = "2.2.046" diff --git a/xklb/consts.py b/xklb/consts.py index 9553c3da..fe329e26 100644 --- a/xklb/consts.py +++ b/xklb/consts.py @@ -53,6 +53,7 @@ def random_string(length=5): REGEX_V_REDD_IT = re.compile("https?://v.redd.it/(?:[^/?#&]+)") APPLICATION_START = now() TERMINAL_SIZE = shutil.get_terminal_size(fallback=(80, 60)) +MOBILE_TERMINAL = TERMINAL_SIZE.columns < 80 TABULATE_STYLE = "simple" EPOCH_COLUMNS = ( diff --git a/xklb/play_actions.py b/xklb/play_actions.py index 1e4eec76..345b71f0 100644 --- a/xklb/play_actions.py +++ b/xklb/play_actions.py @@ -209,8 +209,8 @@ def parse_args(action, default_chromecast=None) -> argparse.Namespace: parser.add_argument("--gui", action="store_true") parser.add_argument("--shallow-organize", default="/mnt/d/", help=argparse.SUPPRESS) - parser.add_argument("--online-media-only", "--online-only", action="store_true", help=argparse.SUPPRESS) - parser.add_argument("--local-media-only", "--local-only", action="store_true", help=argparse.SUPPRESS) + parser.add_argument("--online-media-only", "--online", action="store_true", help=argparse.SUPPRESS) + parser.add_argument("--local-media-only", "--local", action="store_true", help=argparse.SUPPRESS) parser.add_argument("--safe", "-safe", action="store_true", help="Skip generic URLs") parser.add_argument("--sibling", "--episode", action="store_true") @@ -721,11 +721,13 @@ def process_playqueue(args) -> None: if m is not None and (m["path"].startswith("http") or Path(m["path"]).exists()): play(args, m, len(media) + len(futures)) finally: - if args.interdimensional_cable: - args.sock.send(b"raw quit \n") - Path(args.mpv_socket).unlink(missing_ok=True) - if args.chromecast: - Path(consts.CAST_NOW_PLAYING).unlink(missing_ok=True) + try: + if args.interdimensional_cable: + args.sock.send(b"raw quit \n") + finally: + Path(args.mpv_socket).unlink(missing_ok=True) + if args.chromecast: + Path(consts.CAST_NOW_PLAYING).unlink(missing_ok=True) def watch() -> None: diff --git a/xklb/player.py b/xklb/player.py index 2d797f85..4bba204b 100644 --- a/xklb/player.py +++ b/xklb/player.py @@ -678,14 +678,16 @@ def socket_play(args, m: dict) -> None: if end == 0: return - play_opts = f"start={start},save-position-on-quit=no" + play_opts = f"start={start},save-position-on-quit=no,resume-playback=no" if args.action in (SC.listen): - play_opts += ",video=no,really-quiet=yes" + play_opts += ",video=no" elif args.action in (SC.watch): - play_opts += ",fullscreen=yes,force-window=yes,really-quiet=yes" + play_opts += ",fullscreen=yes,force-window=yes" if m["path"].startswith("http"): play_opts += ",script-opts=ytdl_hook-try_ytdl_first=yes" + else: + play_opts += ",really-quiet=yes" f = m["path"].replace("\\", "\\\\").replace('"', '\\"').replace("\n", "\\n") args.sock.send((f'raw loadfile "{f}" replace "{play_opts}" \n').encode()) @@ -1165,7 +1167,7 @@ def should_align_right(k, v): utils.pipe_print(line.strip()) if args.moved: moved_media(args, [d["path"] for d in media], *args.moved) - elif "j" in args.print or consts.TERMINAL_SIZE.columns < 80: + elif "j" in args.print or consts.MOBILE_TERMINAL: print(json.dumps(media, indent=3)) elif "c" in args.print: utils.write_csv_to_stdout(media) diff --git a/xklb/scripts/playback_control.py b/xklb/scripts/playback_control.py index b8ee3b19..2ac1f652 100644 --- a/xklb/scripts/playback_control.py +++ b/xklb/scripts/playback_control.py @@ -1,6 +1,9 @@ import argparse, platform, textwrap +from copy import deepcopy from pathlib import Path +import ffmpeg + from xklb import consts, utils from xklb.utils import cmd, log @@ -35,20 +38,78 @@ def _now_playing(args) -> dict: return media +def reformat_ffprobe(path): + try: + probe = ffmpeg.probe(path, show_chapters=None) + except Exception: + log.exception(f"[{path}] Failed reading header. Metadata corruption") + return path + + codec_types = [s.get("codec_type") for s in probe["streams"]] + audio_count = sum(1 for s in codec_types if s == "audio") + + excluded_keys = ["encoder", "major_brand", "minor_version", "compatible_brands"] + + seen = set() + metadata = utils.lower_keys(probe["format"]["tags"]) + for key, value in deepcopy(metadata).items(): + if key in excluded_keys or value in seen or path in value: + metadata.pop(key, None) + seen.add(value) + + description = utils.safe_unpack( + metadata.pop("description", None), + metadata.pop("synopsis", None), + ) + artist = utils.safe_unpack( + metadata.pop("artist", None), + ) + title = utils.safe_unpack( + metadata.pop("title", None), + ) + url = utils.safe_unpack( + metadata.pop("purl", None), + metadata.pop("url", None), + metadata.pop("comment", None), + ) + date = utils.safe_unpack( + metadata.pop("date", None), + metadata.pop("time", None), + metadata.pop("creation_time", None), + ) + + formatted_output = "" + for key, value in metadata.items(): + formatted_output += f" {key} : {value}\n" + + if audio_count > 1: + formatted_output += f"Audio tracks: {audio_count}\n" + if len(probe["chapters"]) > 1: + formatted_output += f"Chapters: {len(probe['chapters'])}\n" + + if description and not consts.MOBILE_TERMINAL: + formatted_output += f"Description: \n{textwrap.indent(description, ' ')}\n" + if date: + formatted_output += f"Date: {date}\n" + if url: + formatted_output += f"URL: {url}\n" + if artist: + formatted_output += f"Artist: {artist}\n" + if title: + formatted_output += f"Title: {title}\n" + + formatted_output += f"Duration: {utils.seconds_to_hhmmss(utils.safe_int(probe['format']['duration']))}\n" + formatted_output += f" Start: {utils.seconds_to_hhmmss(utils.safe_int(probe['format']['start_time']))}\n" + + # print(cmd("ffprobe", "-hide_banner", "-loglevel", "info", path).stderr) + return textwrap.indent(formatted_output, " ") + + def now_playing(path) -> str: if path.startswith("http"): text = path else: - text = ( - path - + "\n" - + "\n".join( - line - for line in cmd("ffprobe", "-hide_banner", "-loglevel", "info", path).stderr.splitlines() - if path not in line - ) - + "\n" - ) + text = path + "\n" + reformat_ffprobe(path) try: text.encode() diff --git a/xklb/search.py b/xklb/search.py index 3caec1d1..1dfd0d0f 100644 --- a/xklb/search.py +++ b/xklb/search.py @@ -34,8 +34,8 @@ def parse_args() -> argparse.Namespace: ) parser.add_argument("--ignore-errors", action="store_true", help=argparse.SUPPRESS) - parser.add_argument("--online-media-only", "--online-only", action="store_true", help=argparse.SUPPRESS) - parser.add_argument("--local-media-only", "--local-only", action="store_true", help=argparse.SUPPRESS) + parser.add_argument("--online-media-only", "--online", action="store_true", help=argparse.SUPPRESS) + parser.add_argument("--local-media-only", "--local", action="store_true", help=argparse.SUPPRESS) parser.add_argument("--loop", action="store_true", help=argparse.SUPPRESS) parser.add_argument("--override-player", "--player", "-player", help=argparse.SUPPRESS) diff --git a/xklb/utils.py b/xklb/utils.py index d3048fda..0cd1e073 100644 --- a/xklb/utils.py +++ b/xklb/utils.py @@ -390,6 +390,16 @@ def concat(*args): return (part for part in args if part) +def lower_keys(input_dict): + output_dict = {} + for key, value in input_dict.items(): + lowercase_key = key.lower() + if lowercase_key in output_dict: + log.warning("Overriding key %s: %s -> %s", lowercase_key, output_dict[lowercase_key], value) + output_dict[lowercase_key] = value + return output_dict + + def extract_words(string): if not string: return None