build_jellyfin_dir.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. import os
  2. import json
  3. import argparse
  4. import shutil
  5. def build_tree_from_json(json_path, target_dir, dry_run=False, use_symlink=False, rename_source=False, rename_dir=False):
  6. with open(json_path, "r", encoding="utf-8") as f:
  7. data = json.load(f)
  8. actions = []
  9. dir_rename_actions = []
  10. for section in ["matched", "unmatched"]:
  11. for entry in data.get(section, []):
  12. # Directory rename logic
  13. if rename_dir:
  14. original_dir = entry.get("path")
  15. # Use the first suggested file to get the new directory name
  16. jellyfin_files = entry.get("jellyfin_media_files", [])
  17. if jellyfin_files:
  18. suggested_path = jellyfin_files[0]["suggested"]
  19. suggested_dir = os.path.dirname(suggested_path)
  20. if original_dir and suggested_dir and original_dir != suggested_dir:
  21. dir_rename_actions.append((original_dir, suggested_dir))
  22. # File actions
  23. for file_info in entry.get("jellyfin_media_files", []):
  24. suggested_path = file_info["suggested"]
  25. original_path = file_info.get("original", suggested_path)
  26. filename = os.path.basename(suggested_path)
  27. subdir_name = os.path.splitext(filename)[0]
  28. subdir_path = os.path.join(target_dir, subdir_name)
  29. file_path = os.path.join(subdir_path, filename)
  30. actions.append((subdir_path, file_path, original_path, suggested_path))
  31. if dry_run:
  32. print(f"Dry run: The following directory tree would be created under {target_dir}:")
  33. tree = {}
  34. for subdir, file_path, original_path, suggested_path in actions:
  35. rel_subdir = os.path.relpath(subdir, target_dir)
  36. tree.setdefault(rel_subdir, []).append(os.path.basename(file_path))
  37. for subdir, files in tree.items():
  38. print(f"{os.path.join(target_dir, subdir)}/")
  39. for f in files:
  40. print(f" {f}")
  41. if rename_source:
  42. print("\nDry run: The following source files would be renamed:")
  43. for _, _, original_path, suggested_path in actions:
  44. if original_path != suggested_path:
  45. bak_path = original_path + ".bak"
  46. print(f" Would create 0-byte: {bak_path}")
  47. print(f" Would rename: {original_path} -> {suggested_path}")
  48. if rename_dir:
  49. print("\nDry run: The following directories would be renamed:")
  50. for original_dir, suggested_dir in dir_rename_actions:
  51. print(f" Would rename directory: {original_dir} -> {suggested_dir}")
  52. else:
  53. # Directory renaming
  54. if rename_dir:
  55. for original_dir, suggested_dir in dir_rename_actions:
  56. if os.path.exists(original_dir) and not os.path.exists(suggested_dir):
  57. try:
  58. os.makedirs(os.path.dirname(suggested_dir), exist_ok=True)
  59. os.rename(original_dir, suggested_dir)
  60. print(f"Renamed directory: {original_dir} -> {suggested_dir}")
  61. except Exception as e:
  62. print(f"Failed to rename directory {original_dir}: {e}")
  63. # File actions
  64. for subdir, file_path, original_path, suggested_path in actions:
  65. os.makedirs(subdir, exist_ok=True)
  66. if rename_source:
  67. bak_path = original_path + ".bak"
  68. if not os.path.exists(bak_path):
  69. with open(bak_path, "wb"):
  70. pass
  71. if original_path != suggested_path and os.path.exists(original_path):
  72. try:
  73. os.rename(original_path, suggested_path)
  74. print(f"Renamed: {original_path} -> {suggested_path}")
  75. except Exception as e:
  76. print(f"Failed to rename {original_path}: {e}")
  77. elif use_symlink:
  78. if not os.path.exists(file_path):
  79. try:
  80. os.symlink(original_path, file_path)
  81. print(f"Symlinked: {file_path} -> {original_path}")
  82. except Exception as e:
  83. print(f"Failed to symlink {file_path}: {e}")
  84. else:
  85. if not os.path.exists(file_path):
  86. with open(file_path, "wb"):
  87. pass
  88. print(f"Created {len(actions)} files in {target_dir} (symlink={use_symlink}, rename_source={rename_source}, rename_dir={rename_dir})")
  89. if __name__ == "__main__":
  90. parser = argparse.ArgumentParser(description="Build a Jellyfin-ready directory tree from a JSON file.")
  91. parser.add_argument("json_path", help="Path to the ghibli_jellyfin_ready.json file")
  92. parser.add_argument("target_dir", help="Target directory to create the tree in")
  93. parser.add_argument("--dry-run", action="store_true", help="Only print what would be created")
  94. parser.add_argument("--symlink", action="store_true", help="Create symlinks to source files instead of 0-byte files")
  95. 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")
  96. parser.add_argument("--rename-dir", action="store_true", help="Rename source directories to the suggested directory name")
  97. args = parser.parse_args()
  98. build_tree_from_json(
  99. args.json_path,
  100. args.target_dir,
  101. dry_run=args.dry_run,
  102. use_symlink=args.symlink,
  103. rename_source=args.rename_source,
  104. rename_dir=args.rename_dir
  105. )
  106. # def build_tree_from_json(json_path, target_dir, dry_run=False, use_symlink=False, rename_source=False):
  107. # with open(json_path, "r", encoding="utf-8") as f:
  108. # data = json.load(f)
  109. # actions = []
  110. # for section in ["matched", "unmatched"]:
  111. # for entry in data.get(section, []):
  112. # for file_info in entry.get("jellyfin_media_files", []):
  113. # suggested_path = file_info["suggested"]
  114. # original_path = file_info.get("original", suggested_path)
  115. # filename = os.path.basename(suggested_path)
  116. # subdir_name = os.path.splitext(filename)[0]
  117. # subdir_path = os.path.join(target_dir, subdir_name)
  118. # file_path = os.path.join(subdir_path, filename)
  119. # actions.append((subdir_path, file_path, original_path, suggested_path))
  120. # if dry_run:
  121. # print(f"Dry run: The following directory tree would be created under {target_dir}:")
  122. # tree = {}
  123. # for subdir, file_path, original_path, suggested_path in actions:
  124. # rel_subdir = os.path.relpath(subdir, target_dir)
  125. # tree.setdefault(rel_subdir, []).append(os.path.basename(file_path))
  126. # for subdir, files in tree.items():
  127. # print(f"{os.path.join(target_dir, subdir)}/")
  128. # for f in files:
  129. # print(f" {f}")
  130. # if rename_source:
  131. # print("\nDry run: The following source files would be renamed:")
  132. # for _, _, original_path, suggested_path in actions:
  133. # if original_path != suggested_path:
  134. # bak_path = original_path + ".bak"
  135. # print(f" Would create 0-byte: {bak_path}")
  136. # print(f" Would rename: {original_path} -> {suggested_path}")
  137. # else:
  138. # for subdir, file_path, original_path, suggested_path in actions:
  139. # os.makedirs(subdir, exist_ok=True)
  140. # if rename_source:
  141. # # Create a 0-byte .bak file with the original filename
  142. # bak_path = original_path + ".bak"
  143. # if not os.path.exists(bak_path):
  144. # with open(bak_path, "wb"):
  145. # pass
  146. # # Rename the source file to the suggested name if different
  147. # if original_path != suggested_path and os.path.exists(original_path):
  148. # try:
  149. # os.rename(original_path, suggested_path)
  150. # print(f"Renamed: {original_path} -> {suggested_path}")
  151. # except Exception as e:
  152. # print(f"Failed to rename {original_path}: {e}")
  153. # elif use_symlink:
  154. # # Create a symlink to the source file if it doesn't exist
  155. # if not os.path.exists(file_path):
  156. # try:
  157. # os.symlink(original_path, file_path)
  158. # print(f"Symlinked: {file_path} -> {original_path}")
  159. # except Exception as e:
  160. # print(f"Failed to symlink {file_path}: {e}")
  161. # else:
  162. # # Create a 0-byte file if it doesn't exist
  163. # if not os.path.exists(file_path):
  164. # with open(file_path, "wb"):
  165. # pass
  166. # print(f"Created {len(actions)} files in {target_dir} (symlink={use_symlink}, rename_source={rename_source})")
  167. # if __name__ == "__main__":
  168. # parser = argparse.ArgumentParser(description="Build a Jellyfin-ready directory tree from a JSON file.")
  169. # parser.add_argument("json_path", help="Path to the ghibli_jellyfin_ready.json file")
  170. # parser.add_argument("target_dir", help="Target directory to create the tree in")
  171. # parser.add_argument("--dry-run", action="store_true", help="Only print what would be created")
  172. # parser.add_argument("--symlink", action="store_true", help="Create symlinks to source files instead of 0-byte files")
  173. # 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")
  174. # args = parser.parse_args()
  175. # build_tree_from_json(
  176. # args.json_path,
  177. # args.target_dir,
  178. # dry_run=args.dry_run,
  179. # use_symlink=args.symlink,
  180. # rename_source=args.rename_source
  181. # )
  182. # def build_tree_from_json(json_path, target_dir, dry_run=False, use_symlink=False):
  183. # with open(json_path, "r", encoding="utf-8") as f:
  184. # data = json.load(f)
  185. # actions = []
  186. # for section in ["matched", "unmatched"]:
  187. # for entry in data.get(section, []):
  188. # for file_info in entry.get("jellyfin_media_files", []):
  189. # suggested_path = file_info["suggested"]
  190. # original_path = file_info.get("original", suggested_path)
  191. # # Get the filename and its parent directory
  192. # filename = os.path.basename(suggested_path)
  193. # subdir_name = os.path.splitext(filename)[0]
  194. # subdir_path = os.path.join(target_dir, subdir_name)
  195. # file_path = os.path.join(subdir_path, filename)
  196. # actions.append((subdir_path, file_path, original_path))
  197. # if dry_run:
  198. # print(f"Dry run: The following directory tree would be created under {target_dir}:")
  199. # tree = {}
  200. # for subdir, file_path, original_path in actions:
  201. # rel_subdir = os.path.relpath(subdir, target_dir)
  202. # tree.setdefault(rel_subdir, []).append(os.path.basename(file_path))
  203. # for subdir, files in tree.items():
  204. # print(f"{os.path.join(target_dir, subdir)}/")
  205. # for f in files:
  206. # print(f" {f}")
  207. # else:
  208. # for subdir, file_path, original_path in actions:
  209. # os.makedirs(subdir, exist_ok=True)
  210. # if use_symlink:
  211. # # Create a symlink to the source file if it doesn't exist
  212. # if not os.path.exists(file_path):
  213. # try:
  214. # os.symlink(original_path, file_path)
  215. # print(f"Symlinked: {file_path} -> {original_path}")
  216. # except Exception as e:
  217. # print(f"Failed to symlink {file_path}: {e}")
  218. # else:
  219. # # Create a 0-byte file if it doesn't exist
  220. # if not os.path.exists(file_path):
  221. # with open(file_path, "wb"):
  222. # pass
  223. # print(f"Created {len(actions)} files in {target_dir} (symlink={use_symlink})")
  224. # if __name__ == "__main__":
  225. # # python3 build_jellyfin_dir.py --dry-run --symlink ghibli_jellyfin_ready.json ./anime
  226. # parser = argparse.ArgumentParser(description="Build a Jellyfin-ready directory tree from a JSON file.")
  227. # parser.add_argument("json_path", help="Path to the ghibli_jellyfin_ready.json file")
  228. # parser.add_argument("target_dir", help="Target directory to create the tree in")
  229. # parser.add_argument("--dry-run", action="store_true", help="Only print what would be created")
  230. # parser.add_argument("--symlink", action="store_true", help="Create symlinks to source files instead of 0-byte files")
  231. # args = parser.parse_args()
  232. # build_tree_from_json(args.json_path, args.target_dir, dry_run=args.dry_run, use_symlink=args.symlink)
  233. # import os
  234. # import json
  235. # import argparse
  236. # def build_tree_from_json(json_path, target_dir, dry_run=False):
  237. # with open(json_path, "r", encoding="utf-8") as f:
  238. # data = json.load(f)
  239. # actions = []
  240. # for section in ["matched", "unmatched"]:
  241. # for entry in data.get(section, []):
  242. # for file_info in entry.get("jellyfin_media_files", []):
  243. # suggested_path = file_info["suggested"]
  244. # # Get the filename and its parent directory
  245. # filename = os.path.basename(suggested_path)
  246. # subdir_name = os.path.splitext(filename)[0]
  247. # subdir_path = os.path.join(target_dir, subdir_name)
  248. # file_path = os.path.join(subdir_path, filename)
  249. # actions.append((subdir_path, file_path))
  250. # print(actions)
  251. # if dry_run:
  252. # print(f"Dry run: The following directory tree would be created under {target_dir}:")
  253. # tree = {}
  254. # for subdir, file_path in actions:
  255. # rel_subdir = os.path.relpath(subdir, target_dir)
  256. # rel_file = os.path.relpath(file_path, target_dir)
  257. # tree.setdefault(rel_subdir, []).append(os.path.basename(file_path))
  258. # for subdir, files in tree.items():
  259. # print(f"{os.path.join(target_dir, subdir)}/")
  260. # for f in files:
  261. # print(f" {f}")
  262. # else:
  263. # for subdir, file_path in actions:
  264. # os.makedirs(subdir, exist_ok=True)
  265. # # Create a 0-byte file if it doesn't exist
  266. # if not os.path.exists(file_path):
  267. # with open(file_path, "wb"):
  268. # pass
  269. # print(f"Created {len(actions)} files in {target_dir}")
  270. # if __name__ == "__main__":
  271. # # python3 build_jellyfin_dir.py --dry-run ghibli_jellyfin_ready.json ./anime
  272. # #
  273. # parser = argparse.ArgumentParser(description="Build a Jellyfin-ready directory tree from a JSON file.")
  274. # parser.add_argument("json_path", help="Path to the ghibli_jellyfin_ready.json file")
  275. # parser.add_argument("target_dir", help="Target directory to create the tree in")
  276. # parser.add_argument("--dry-run", action="store_true", help="Only print what would be created")
  277. # args = parser.parse_args()
  278. # build_tree_from_json(args.json_path, args.target_dir, dry_run=args.dry_run)