Advertisement
Guest User

Untitled

a guest
Apr 12th, 2025
59
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 5.96 KB | None | 0 0
  1.  
  2. import os
  3. import re
  4. import subprocess
  5. import json
  6. import requests
  7. from pathlib import Path
  8. from urllib.parse import quote
  9. from rich import print
  10.  
  11. # === CONFIG ===
  12. MOVIES_DIR = Path(r"PATH:/TO/VIDEOS") << Replace this with your folder path
  13. VIDEO_EXTS = {".mp4", ".mkv", ".avi", ".mov", ".webm"}
  14. OMDB_API_KEY = "API_KEY" # << Replace this with your OMDb API key
  15. YEAR_REGEX = re.compile(r"\((\d{4})\)")
  16.  
  17. # === FUNCTIONS ===
  18.  
  19. def get_video_duration(file_path):
  20. cmd = [
  21. "ffprobe", "-v", "error", "-show_entries",
  22. "format=duration", "-of", "json", str(file_path)
  23. ]
  24. result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
  25. try:
  26. duration_sec = float(json.loads(result.stdout)["format"]["duration"])
  27. return round(duration_sec / 60, 1)
  28. except:
  29. return None
  30.  
  31. def clean_title(file_stem):
  32. title = re.sub(r"[.\-_]", " ", file_stem)
  33. title = re.sub(r"\{.*?\}", "", title)
  34. title = re.sub(r"\[.*?\]", "", title)
  35. title = re.sub(r"\b(1080p|720p|4k|bluray|webrip|hdrip|x264|x265|h264|h265|aac|dvdrip|hevc)\b", "", title, flags=re.I)
  36. title = re.sub(r"\s{2,}", " ", title).strip()
  37. return title
  38.  
  39. def search_omdb(title, year=None):
  40. clean = re.sub(r"\(\d{4}\)", "", title)
  41. clean = re.sub(r"\bHDTV|WEBRip|BluRay|1080p|720p|x264|x265|HEVC|AAC\b", "", clean, flags=re.I)
  42. clean = re.sub(r"[\[\]\{\}\.\-_]", " ", clean)
  43. clean = re.sub(r"\s{2,}", " ", clean).strip()
  44.  
  45. params = {
  46. "apikey": OMDB_API_KEY,
  47. "t": clean,
  48. "type": "movie",
  49. }
  50. if year:
  51. params["y"] = year
  52.  
  53. resp = requests.get("http://www.omdbapi.com/", params=params)
  54. data = resp.json()
  55.  
  56. if data.get("Response") == "True":
  57. runtime = data.get("Runtime", "")
  58. if "min" in runtime:
  59. try:
  60. return int(runtime.replace("min", "").strip())
  61. except:
  62. return None
  63. else:
  64. print(f"[dim]OMDb Error: {data.get('Error')}[/dim]")
  65.  
  66. return None
  67.  
  68. def scan_and_compare():
  69. flagged = []
  70.  
  71. for root, dirs, files in os.walk(MOVIES_DIR):
  72. for file in files:
  73. if Path(file).suffix.lower() in VIDEO_EXTS:
  74. if "{edition-" in file:
  75. continue # Skip already-tagged files
  76.  
  77. full_path = Path(root) / file
  78. local_duration = get_video_duration(full_path)
  79. if not local_duration:
  80. continue
  81.  
  82. file_stem = Path(file).stem
  83. folder_name = Path(root).name
  84.  
  85. title_raw = folder_name or file_stem
  86. title = clean_title(title_raw)
  87.  
  88. match = YEAR_REGEX.search(folder_name) or YEAR_REGEX.search(file_stem)
  89. year = match.group(1) if match else None
  90.  
  91. print(f"\n[cyan]Checking:[/cyan] {file_stem}")
  92. print(f" → Title Guess: [green]{title}[/green], Year: {year}")
  93. print(f" → Local runtime: {local_duration} min")
  94.  
  95. imdb_runtime = search_omdb(title, year)
  96.  
  97. if imdb_runtime:
  98. diff = round(local_duration - imdb_runtime, 1)
  99. if diff >= 5:
  100. print(f"[bold yellow] → Potential edition tag:[/bold yellow] {{edition-Extended Cut}} (IMDb: {imdb_runtime} min, diff: +{diff} min)")
  101. flagged.append((full_path, title))
  102. elif diff <= -5:
  103. print(f"[bold] → Note:[/bold] Local runtime is [red]{abs(diff)} min shorter[/red] than IMDb ({imdb_runtime} min)")
  104. flagged.append((full_path, title))
  105. else:
  106. print(f" → Matches IMDb runtime ({imdb_runtime} min)")
  107. else:
  108. print("[red] × IMDb runtime not found[/red]")
  109. flagged.append((full_path, title))
  110.  
  111. return flagged
  112.  
  113. def interactive_mode(flagged_movies):
  114. if not flagged_movies:
  115. print("\n[green]All movies match expected runtimes. No action needed.[/green]")
  116. return
  117.  
  118. print("\n[bold underline]Flagged Movies:[/bold underline]")
  119. for idx, (path, title) in enumerate(flagged_movies, 1):
  120. print(f"[{idx}] {title}")
  121.  
  122. while True:
  123. try:
  124. selection = int(input("\nEnter number of movie to investigate or 0 to cancel: "))
  125. if selection == 0:
  126. break
  127.  
  128. selected_path, selected_title = flagged_movies[selection - 1]
  129. query = f"{selected_title} extended cut runtime"
  130. search_url = f"https://www.google.com/search?q={quote(query)}"
  131. print(f"{search_url}")
  132.  
  133. tag = input("Enter edition tag (e.g., Director's Cut, Extended, Final): ").strip()
  134. if not tag:
  135. print("No tag entered. Skipping.")
  136. continue
  137.  
  138. tag_str = f" {{edition-{tag}}}"
  139. folder = selected_path.parent
  140. stem = selected_path.stem
  141. base_name = re.sub(r"\{edition-.*?\}", "", stem).strip()
  142. ext = selected_path.suffix
  143.  
  144. new_name = f"{base_name}{tag_str}{ext}"
  145. new_path = folder / new_name
  146.  
  147. print(f"\n[cyan]Renaming all files in folder starting with:[/cyan] {base_name}")
  148. for f in folder.iterdir():
  149. if f.is_file() and f.stem.startswith(base_name):
  150. new_f_name = f"{re.sub(r'\\{edition-.*?\\}', '', f.stem).strip()}{tag_str}{f.suffix}"
  151. new_f_path = folder / new_f_name
  152. print(f"→ {f.name} → {new_f_path.name}")
  153. f.rename(new_f_path)
  154.  
  155. print("[green]Rename complete.[/green]")
  156.  
  157. except (ValueError, IndexError):
  158. print("[red]Invalid selection.[/red]")
  159.  
  160. # === MODIFIED RUN ===
  161. if __name__ == "__main__":
  162. flagged = scan_and_compare()
  163. interactive_mode(flagged)
  164.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement