#!/usr/bin/env python3 # File: jellystack.py # Location: /mnt/public/Support/Programs/jellyfin/scripts/ # Author: bgstack15 # Startdate: 2024-01-29-2 09:25 # SPDX-License-Identifier: GPL-3.0-only # Title: jellyfin stackrpms cli frontend # Project: jellystack # Purpose: List and rescan individual libraries in jellyfin easier than navigating web ui # History: # Usage: # from rescan-library.sh # Reference: # https://stackoverflow.com/questions/8258145/in-python-check-if-file-modification-time-is-older-than-a-specific-datetime # Improve: # Dependencies: # jellystack_lib.py, python>=3.4 # Documentation: import jellystack_lib as js, os, argparse, shutil, sys from datetime import datetime, timedelta from pathlib import Path # python>=3.4 js_debug = False # Ref: https://stackoverflow.com/questions/8258145/in-python-check-if-file-modification-time-is-older-than-a-specific-datetime def is_file_older_than (file, delta): cutoff = datetime.utcnow() - delta mtime = datetime.utcfromtimestamp(os.path.getmtime(file)) if mtime < cutoff: return True return False def check_cached_library_names(clear_cache = False): """ If cache is absent or older than 23 hours, generate new cache of library names """ cache_dir = os.path.join(os.path.expanduser("~"),".cache","jellystack") _clear_cache = clear_cache _cache_is_empty = True try: os.mkdir(cache_dir) except: pass for thisdir, subdir, files in os.walk(cache_dir): for tf in files: if is_file_older_than(os.path.join(thisdir,tf), timedelta(hours=23)): _clear_cache = True if js_debug: print(f"found too-old file {tf}, will clear the cache.",file=sys.stderr) break if _clear_cache: shutil.rmtree(cache_dir) if js_debug: print(f"Clearing cache...",file=sys.stderr) try: os.mkdir(cache_dir) except: pass # populate cache try: for thisdir, subdir, files in os.walk(cache_dir): if len(files) > 0: _cache_is_empty = False except: pass if _cache_is_empty: if js_debug: print(f"Generating cache...",file=sys.stderr) client = get_client() lib_names = js.get_library_names_only(js.get_media_folders(client)) #print(lib_names) for name in lib_names: Path(os.path.join(cache_dir,name)).touch() # and now, list all the files in that path for thisdir, subdir, files in os.walk(cache_dir): for tf in files: print(tf) def get_client(): try: client = js.get_authenticated_client(server,user,pw) except: raise Exception("Check password or url?") return client if "__main__" == __name__: server = os.environ.get("server","http://vm4:8096") user = os.environ.get("username","admin") pw = os.environ.get("password","none") pwfile = os.path.join(os.path.expanduser("~"),".jellyfin.password") if pw == "none": #raise Exception(f"Set env var \"password\" and try again.") if os.path.exists(pwfile): try: with open(pwfile,"r") as o: pw = o.read().strip() # the strip helps remove the newline that most people stick at the end of passwords in a passwordfile, and I seriously doubt the newline is part of the password. except: pass parser = argparse.ArgumentParser() parser.add_argument("--autocomplete",action="store_true",help="print library names, using the cache.") parser.add_argument("--clear-cache",action="store_true",help="print library names, using the cache.") parser.add_argument("--library",help="Rescan this library by name or uuid.") args = parser.parse_args() if type(args.library) == str: args.library = args.library.rstrip("/") # to help with the bash autocomplete which always wants to add a trailing slash, if PWD has a directory named exactly the same as the library. I cannot find a way around it. #print(args,file=sys.stderr) if args.autocomplete: check_cached_library_names(args.clear_cache) else: client = get_client() folders = js.get_media_folders(client) if args.library is not None: js.refresh_library(args.library, folders, client) else: print("Use --autocomplete, or one of these for --library:") js.libraries_to_csv(folders)