import os import json import argparse import shutil def build_tree_from_json(json_path, target_dir, dry_run=False, use_symlink=False, rename_source=False, rename_dir=False): with open(json_path, "r", encoding="utf-8") as f: data = json.load(f) actions = [] dir_rename_actions = [] for section in ["matched", "unmatched"]: for entry in data.get(section, []): # Directory rename logic if rename_dir: original_dir = entry.get("path") # Use the first suggested file to get the new directory name jellyfin_files = entry.get("jellyfin_media_files", []) if jellyfin_files: suggested_path = jellyfin_files[0]["suggested"] suggested_dir = os.path.dirname(suggested_path) if original_dir and suggested_dir and original_dir != suggested_dir: dir_rename_actions.append((original_dir, suggested_dir)) # File actions for file_info in entry.get("jellyfin_media_files", []): suggested_path = file_info["suggested"] original_path = file_info.get("original", suggested_path) filename = os.path.basename(suggested_path) subdir_name = os.path.splitext(filename)[0] subdir_path = os.path.join(target_dir, subdir_name) file_path = os.path.join(subdir_path, filename) actions.append((subdir_path, file_path, original_path, suggested_path)) if dry_run: print(f"Dry run: The following directory tree would be created under {target_dir}:") tree = {} for subdir, file_path, original_path, suggested_path in actions: rel_subdir = os.path.relpath(subdir, target_dir) tree.setdefault(rel_subdir, []).append(os.path.basename(file_path)) for subdir, files in tree.items(): print(f"{os.path.join(target_dir, subdir)}/") for f in files: print(f" {f}") if rename_source: print("\nDry run: The following source files would be renamed:") for _, _, original_path, suggested_path in actions: if original_path != suggested_path: bak_path = original_path + ".bak" print(f" Would create 0-byte: {bak_path}") print(f" Would rename: {original_path} -> {suggested_path}") if rename_dir: print("\nDry run: The following directories would be renamed:") for original_dir, suggested_dir in dir_rename_actions: print(f" Would rename directory: {original_dir} -> {suggested_dir}") else: # Directory renaming if rename_dir: for original_dir, suggested_dir in dir_rename_actions: if os.path.exists(original_dir) and not os.path.exists(suggested_dir): try: os.makedirs(os.path.dirname(suggested_dir), exist_ok=True) os.rename(original_dir, suggested_dir) print(f"Renamed directory: {original_dir} -> {suggested_dir}") except Exception as e: print(f"Failed to rename directory {original_dir}: {e}") # File actions for subdir, file_path, original_path, suggested_path in actions: os.makedirs(subdir, exist_ok=True) if rename_source: bak_path = original_path + ".bak" if not os.path.exists(bak_path): with open(bak_path, "wb"): pass if original_path != suggested_path and os.path.exists(original_path): try: os.rename(original_path, suggested_path) print(f"Renamed: {original_path} -> {suggested_path}") except Exception as e: print(f"Failed to rename {original_path}: {e}") elif use_symlink: if not os.path.exists(file_path): try: os.symlink(original_path, file_path) print(f"Symlinked: {file_path} -> {original_path}") except Exception as e: print(f"Failed to symlink {file_path}: {e}") else: if not os.path.exists(file_path): with open(file_path, "wb"): pass print(f"Created {len(actions)} files in {target_dir} (symlink={use_symlink}, rename_source={rename_source}, rename_dir={rename_dir})") if __name__ == "__main__": parser = argparse.ArgumentParser(description="Build a Jellyfin-ready directory tree from a JSON file.") parser.add_argument("json_path", help="Path to the ghibli_jellyfin_ready.json file") parser.add_argument("target_dir", help="Target directory to create the tree in") parser.add_argument("--dry-run", action="store_true", help="Only print what would be created") parser.add_argument("--symlink", action="store_true", help="Create symlinks to source files instead of 0-byte files") parser.add_argument("--rename-source", action="store_true", help="Rename source files to the suggested name and create a .bak 0-byte file with the original name") parser.add_argument("--rename-dir", action="store_true", help="Rename source directories to the suggested directory name") args = parser.parse_args() build_tree_from_json( args.json_path, args.target_dir, dry_run=args.dry_run, use_symlink=args.symlink, rename_source=args.rename_source, rename_dir=args.rename_dir ) # def build_tree_from_json(json_path, target_dir, dry_run=False, use_symlink=False, rename_source=False): # with open(json_path, "r", encoding="utf-8") as f: # data = json.load(f) # actions = [] # for section in ["matched", "unmatched"]: # for entry in data.get(section, []): # for file_info in entry.get("jellyfin_media_files", []): # suggested_path = file_info["suggested"] # original_path = file_info.get("original", suggested_path) # filename = os.path.basename(suggested_path) # subdir_name = os.path.splitext(filename)[0] # subdir_path = os.path.join(target_dir, subdir_name) # file_path = os.path.join(subdir_path, filename) # actions.append((subdir_path, file_path, original_path, suggested_path)) # if dry_run: # print(f"Dry run: The following directory tree would be created under {target_dir}:") # tree = {} # for subdir, file_path, original_path, suggested_path in actions: # rel_subdir = os.path.relpath(subdir, target_dir) # tree.setdefault(rel_subdir, []).append(os.path.basename(file_path)) # for subdir, files in tree.items(): # print(f"{os.path.join(target_dir, subdir)}/") # for f in files: # print(f" {f}") # if rename_source: # print("\nDry run: The following source files would be renamed:") # for _, _, original_path, suggested_path in actions: # if original_path != suggested_path: # bak_path = original_path + ".bak" # print(f" Would create 0-byte: {bak_path}") # print(f" Would rename: {original_path} -> {suggested_path}") # else: # for subdir, file_path, original_path, suggested_path in actions: # os.makedirs(subdir, exist_ok=True) # if rename_source: # # Create a 0-byte .bak file with the original filename # bak_path = original_path + ".bak" # if not os.path.exists(bak_path): # with open(bak_path, "wb"): # pass # # Rename the source file to the suggested name if different # if original_path != suggested_path and os.path.exists(original_path): # try: # os.rename(original_path, suggested_path) # print(f"Renamed: {original_path} -> {suggested_path}") # except Exception as e: # print(f"Failed to rename {original_path}: {e}") # elif use_symlink: # # Create a symlink to the source file if it doesn't exist # if not os.path.exists(file_path): # try: # os.symlink(original_path, file_path) # print(f"Symlinked: {file_path} -> {original_path}") # except Exception as e: # print(f"Failed to symlink {file_path}: {e}") # else: # # Create a 0-byte file if it doesn't exist # if not os.path.exists(file_path): # with open(file_path, "wb"): # pass # print(f"Created {len(actions)} files in {target_dir} (symlink={use_symlink}, rename_source={rename_source})") # if __name__ == "__main__": # parser = argparse.ArgumentParser(description="Build a Jellyfin-ready directory tree from a JSON file.") # parser.add_argument("json_path", help="Path to the ghibli_jellyfin_ready.json file") # parser.add_argument("target_dir", help="Target directory to create the tree in") # parser.add_argument("--dry-run", action="store_true", help="Only print what would be created") # parser.add_argument("--symlink", action="store_true", help="Create symlinks to source files instead of 0-byte files") # parser.add_argument("--rename-source", action="store_true", help="Rename source files to the suggested name and create a .bak 0-byte file with the original name") # args = parser.parse_args() # build_tree_from_json( # args.json_path, # args.target_dir, # dry_run=args.dry_run, # use_symlink=args.symlink, # rename_source=args.rename_source # ) # def build_tree_from_json(json_path, target_dir, dry_run=False, use_symlink=False): # with open(json_path, "r", encoding="utf-8") as f: # data = json.load(f) # actions = [] # for section in ["matched", "unmatched"]: # for entry in data.get(section, []): # for file_info in entry.get("jellyfin_media_files", []): # suggested_path = file_info["suggested"] # original_path = file_info.get("original", suggested_path) # # Get the filename and its parent directory # filename = os.path.basename(suggested_path) # subdir_name = os.path.splitext(filename)[0] # subdir_path = os.path.join(target_dir, subdir_name) # file_path = os.path.join(subdir_path, filename) # actions.append((subdir_path, file_path, original_path)) # if dry_run: # print(f"Dry run: The following directory tree would be created under {target_dir}:") # tree = {} # for subdir, file_path, original_path in actions: # rel_subdir = os.path.relpath(subdir, target_dir) # tree.setdefault(rel_subdir, []).append(os.path.basename(file_path)) # for subdir, files in tree.items(): # print(f"{os.path.join(target_dir, subdir)}/") # for f in files: # print(f" {f}") # else: # for subdir, file_path, original_path in actions: # os.makedirs(subdir, exist_ok=True) # if use_symlink: # # Create a symlink to the source file if it doesn't exist # if not os.path.exists(file_path): # try: # os.symlink(original_path, file_path) # print(f"Symlinked: {file_path} -> {original_path}") # except Exception as e: # print(f"Failed to symlink {file_path}: {e}") # else: # # Create a 0-byte file if it doesn't exist # if not os.path.exists(file_path): # with open(file_path, "wb"): # pass # print(f"Created {len(actions)} files in {target_dir} (symlink={use_symlink})") # if __name__ == "__main__": # # python3 build_jellyfin_dir.py --dry-run --symlink ghibli_jellyfin_ready.json ./anime # parser = argparse.ArgumentParser(description="Build a Jellyfin-ready directory tree from a JSON file.") # parser.add_argument("json_path", help="Path to the ghibli_jellyfin_ready.json file") # parser.add_argument("target_dir", help="Target directory to create the tree in") # parser.add_argument("--dry-run", action="store_true", help="Only print what would be created") # parser.add_argument("--symlink", action="store_true", help="Create symlinks to source files instead of 0-byte files") # args = parser.parse_args() # build_tree_from_json(args.json_path, args.target_dir, dry_run=args.dry_run, use_symlink=args.symlink) # import os # import json # import argparse # def build_tree_from_json(json_path, target_dir, dry_run=False): # with open(json_path, "r", encoding="utf-8") as f: # data = json.load(f) # actions = [] # for section in ["matched", "unmatched"]: # for entry in data.get(section, []): # for file_info in entry.get("jellyfin_media_files", []): # suggested_path = file_info["suggested"] # # Get the filename and its parent directory # filename = os.path.basename(suggested_path) # subdir_name = os.path.splitext(filename)[0] # subdir_path = os.path.join(target_dir, subdir_name) # file_path = os.path.join(subdir_path, filename) # actions.append((subdir_path, file_path)) # print(actions) # if dry_run: # print(f"Dry run: The following directory tree would be created under {target_dir}:") # tree = {} # for subdir, file_path in actions: # rel_subdir = os.path.relpath(subdir, target_dir) # rel_file = os.path.relpath(file_path, target_dir) # tree.setdefault(rel_subdir, []).append(os.path.basename(file_path)) # for subdir, files in tree.items(): # print(f"{os.path.join(target_dir, subdir)}/") # for f in files: # print(f" {f}") # else: # for subdir, file_path in actions: # os.makedirs(subdir, exist_ok=True) # # Create a 0-byte file if it doesn't exist # if not os.path.exists(file_path): # with open(file_path, "wb"): # pass # print(f"Created {len(actions)} files in {target_dir}") # if __name__ == "__main__": # # python3 build_jellyfin_dir.py --dry-run ghibli_jellyfin_ready.json ./anime # # # parser = argparse.ArgumentParser(description="Build a Jellyfin-ready directory tree from a JSON file.") # parser.add_argument("json_path", help="Path to the ghibli_jellyfin_ready.json file") # parser.add_argument("target_dir", help="Target directory to create the tree in") # parser.add_argument("--dry-run", action="store_true", help="Only print what would be created") # args = parser.parse_args() # build_tree_from_json(args.json_path, args.target_dir, dry_run=args.dry_run)