cloning over all the old files
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
import os
|
||||
import csv
|
||||
import shutil
|
||||
from collections import defaultdict
|
||||
|
||||
def find_duplicate_basenames(root_directory, valid_extensions):
|
||||
# Dictionary to store basenames and their corresponding file paths
|
||||
basenames_to_paths = defaultdict(list)
|
||||
|
||||
# Traverse the directory tree
|
||||
for dirpath, dirnames, filenames in os.walk(root_directory):
|
||||
for filename in filenames:
|
||||
full_path = os.path.join(dirpath, filename)
|
||||
basename, extension = os.path.splitext(filename)
|
||||
if extension.lower() in valid_extensions:
|
||||
basenames_to_paths[basename].append(full_path)
|
||||
|
||||
# Filter basenames with more than one file
|
||||
duplicate_basenames = {basename: paths for basename, paths in basenames_to_paths.items() if len(paths) > 1}
|
||||
|
||||
return duplicate_basenames
|
||||
|
||||
def get_file_size(path):
|
||||
try:
|
||||
return os.path.getsize(path)
|
||||
except OSError:
|
||||
return 0
|
||||
|
||||
def display_parent_levels(path, levels=2):
|
||||
# Display 'levels' parent directories above the file path
|
||||
components = path.split(os.sep)
|
||||
return os.sep.join(components[-levels:])
|
||||
|
||||
def prompt_user_for_selection(paths):
|
||||
print("Choose which file to keep:")
|
||||
for i, path in enumerate(paths, start=1):
|
||||
size_mb = get_file_size(path) / (1024 * 1024)
|
||||
print(f"{i}. {display_parent_levels(path)} ({size_mb:.2f} MB)")
|
||||
|
||||
while True:
|
||||
try:
|
||||
choice = int(input("Enter the number of the file to keep (or 0 to skip): "))
|
||||
if 0 <= choice <= len(paths):
|
||||
return choice
|
||||
else:
|
||||
print("Invalid choice. Please enter a valid number.")
|
||||
except ValueError:
|
||||
print("Invalid input. Please enter a number.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
server_directory = r"\\truenas\isolation\videos" # Replace with your server directory
|
||||
video_extensions = {".mp4", ".mkv", ".avi", ".mov"} # Add more video extensions if needed
|
||||
duplicate_files = find_duplicate_basenames(server_directory, video_extensions)
|
||||
|
||||
if duplicate_files:
|
||||
for basename, paths in duplicate_files.items():
|
||||
print(f"\nBasename: {basename}")
|
||||
choice = prompt_user_for_selection(paths)
|
||||
if choice == 0:
|
||||
print("Skipping this duplicate set.")
|
||||
else:
|
||||
# Keep the selected file and delete the others
|
||||
keep_path = paths[choice - 1]
|
||||
for path in paths:
|
||||
if path != keep_path:
|
||||
print(f"Deleting: {display_parent_levels(path)}")
|
||||
os.remove(path)
|
||||
print("\nDuplicate files processed.")
|
||||
else:
|
||||
print("No duplicate video files found.")
|
||||
@@ -0,0 +1,41 @@
|
||||
import os
|
||||
import csv
|
||||
from collections import defaultdict
|
||||
|
||||
def find_duplicate_basenames(root_directory, valid_extensions):
|
||||
# Dictionary to store basenames and their corresponding file paths
|
||||
basenames_to_paths = defaultdict(list)
|
||||
|
||||
# Traverse the directory tree
|
||||
for dirpath, dirnames, filenames in os.walk(root_directory):
|
||||
for filename in filenames:
|
||||
full_path = os.path.join(dirpath, filename)
|
||||
basename, extension = os.path.splitext(filename)
|
||||
if extension.lower() in valid_extensions:
|
||||
basenames_to_paths[basename].append(full_path)
|
||||
|
||||
# Filter basenames with more than one file
|
||||
duplicate_basenames = {basename: paths for basename, paths in basenames_to_paths.items() if len(paths) > 1}
|
||||
|
||||
return duplicate_basenames
|
||||
|
||||
def export_to_csv(duplicate_files, output_csv):
|
||||
with open(output_csv, "w", newline="") as csvfile:
|
||||
fieldnames = ["Basename", "File Paths"]
|
||||
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
|
||||
writer.writeheader()
|
||||
|
||||
for basename, paths in duplicate_files.items():
|
||||
writer.writerow({"Basename": basename, "File Paths": ", ".join(paths)})
|
||||
|
||||
if __name__ == "__main__":
|
||||
server_directory = r"\\truenas\isolation\videos" # Replace with your server directory
|
||||
video_extensions = {".mp4", ".mkv", ".avi", ".mov"} # Add more video extensions if needed
|
||||
duplicate_files = find_duplicate_basenames(server_directory, video_extensions)
|
||||
|
||||
if duplicate_files:
|
||||
output_csv_file = "duplicate_video_files.csv"
|
||||
export_to_csv(duplicate_files, output_csv_file)
|
||||
print(f"Duplicate video files exported to {output_csv_file}")
|
||||
else:
|
||||
print("No duplicate video files found.")
|
||||
@@ -0,0 +1,70 @@
|
||||
import os
|
||||
import csv
|
||||
import shutil
|
||||
from collections import defaultdict
|
||||
|
||||
def find_duplicate_basenames(root_directory, valid_extensions):
|
||||
# Dictionary to store basenames and their corresponding file paths
|
||||
basenames_to_paths = defaultdict(list)
|
||||
|
||||
# Traverse the directory tree
|
||||
for dirpath, dirnames, filenames in os.walk(root_directory):
|
||||
for filename in filenames:
|
||||
full_path = os.path.join(dirpath, filename)
|
||||
basename, extension = os.path.splitext(filename)
|
||||
if extension.lower() in valid_extensions:
|
||||
basenames_to_paths[basename].append(full_path)
|
||||
|
||||
# Filter basenames with more than one file
|
||||
duplicate_basenames = {basename: paths for basename, paths in basenames_to_paths.items() if len(paths) > 1}
|
||||
|
||||
return duplicate_basenames
|
||||
|
||||
def get_file_size(path):
|
||||
try:
|
||||
return os.path.getsize(path)
|
||||
except OSError:
|
||||
return 0
|
||||
|
||||
def display_parent_levels(path, levels=2):
|
||||
# Display 'levels' parent directories above the file path
|
||||
components = path.split(os.sep)
|
||||
return os.sep.join(components[-levels:])
|
||||
|
||||
def prompt_user_for_selection(paths):
|
||||
print("Choose which file to keep:")
|
||||
for i, path in enumerate(paths, start=1):
|
||||
size_mb = get_file_size(path) / (1024 * 1024)
|
||||
print(f"{i}. {display_parent_levels(path)} ({size_mb:.2f} MB)")
|
||||
|
||||
while True:
|
||||
try:
|
||||
choice = int(input("Enter the number of the file to keep (or 0 to skip): "))
|
||||
if 0 <= choice <= len(paths):
|
||||
return choice
|
||||
else:
|
||||
print("Invalid choice. Please enter a valid number.")
|
||||
except ValueError:
|
||||
print("Invalid input. Please enter a number.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
server_directory = r"\\truenas\isolation\videos" # Replace with your server directory
|
||||
video_extensions = {".mp4", ".mkv", ".avi", ".mov"} # Add more video extensions if needed
|
||||
duplicate_files = find_duplicate_basenames(server_directory, video_extensions)
|
||||
|
||||
if duplicate_files:
|
||||
for basename, paths in duplicate_files.items():
|
||||
print(f"\nBasename: {basename}")
|
||||
choice = prompt_user_for_selection(paths)
|
||||
if choice == 0:
|
||||
print("Skipping this duplicate set.")
|
||||
else:
|
||||
# Keep the selected file and delete the others
|
||||
keep_path = paths[choice - 1]
|
||||
for path in paths:
|
||||
if path != keep_path:
|
||||
print(f"Deleting: {display_parent_levels(path)}")
|
||||
os.remove(path)
|
||||
print("\nDuplicate files processed.")
|
||||
else:
|
||||
print("No duplicate video files found.")
|
||||
@@ -0,0 +1,41 @@
|
||||
import os
|
||||
import csv
|
||||
from collections import defaultdict
|
||||
|
||||
def find_duplicate_basenames(root_directory, valid_extensions):
|
||||
# Dictionary to store basenames and their corresponding file paths
|
||||
basenames_to_paths = defaultdict(list)
|
||||
|
||||
# Traverse the directory tree
|
||||
for dirpath, dirnames, filenames in os.walk(root_directory):
|
||||
for filename in filenames:
|
||||
full_path = os.path.join(dirpath, filename)
|
||||
basename, extension = os.path.splitext(filename)
|
||||
if extension.lower() in valid_extensions:
|
||||
basenames_to_paths[basename].append(full_path)
|
||||
|
||||
# Filter basenames with more than one file
|
||||
duplicate_basenames = {basename: paths for basename, paths in basenames_to_paths.items() if len(paths) > 1}
|
||||
|
||||
return duplicate_basenames
|
||||
|
||||
def export_to_csv(duplicate_files, output_csv):
|
||||
with open(output_csv, "w", newline="") as csvfile:
|
||||
fieldnames = ["Basename", "File Paths"]
|
||||
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
|
||||
writer.writeheader()
|
||||
|
||||
for basename, paths in duplicate_files.items():
|
||||
writer.writerow({"Basename": basename, "File Paths": ", ".join(paths)})
|
||||
|
||||
if __name__ == "__main__":
|
||||
server_directory = r"\\truenas\isolation\videos" # Replace with your server directory
|
||||
video_extensions = {".mp4", ".mkv", ".avi", ".mov"} # Add more video extensions if needed
|
||||
duplicate_files = find_duplicate_basenames(server_directory, video_extensions)
|
||||
|
||||
if duplicate_files:
|
||||
output_csv_file = "duplicate_video_files.csv"
|
||||
export_to_csv(duplicate_files, output_csv_file)
|
||||
print(f"Duplicate video files exported to {output_csv_file}")
|
||||
else:
|
||||
print("No duplicate video files found.")
|
||||
@@ -0,0 +1,182 @@
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
from pymediainfo import MediaInfo # You might need to install this: pip install pymediainfo
|
||||
|
||||
# Define the network locations - KEEP THESE UNCHANGED as per user's original request
|
||||
LOCATION1 = r"\\truenas\isolation\videos\redmilf_refresh"
|
||||
LOCATION2 = r"\\truenas\isolation\videos\redmilf"
|
||||
FINAL_LOCATION = r"\\truenas\isolation\videos\redmilf_final" # This is the final destination for ALL copied files
|
||||
|
||||
# Ensure the final destination directory exists
|
||||
os.makedirs(FINAL_LOCATION, exist_ok=True)
|
||||
|
||||
def bytes_to_mb(bytes_value):
|
||||
"""Converts bytes to megabytes, rounded to two decimal places."""
|
||||
return round(bytes_value / (1024 * 1024), 2)
|
||||
|
||||
def extract_did_number(filename):
|
||||
"""
|
||||
Extracts the DIDXXX or DIDXXXX number from a filename.
|
||||
Returns the number as a string if found, otherwise None.
|
||||
"""
|
||||
# Regex to match DID followed by 3 or 4 digits
|
||||
match = re.search(r"DID(\d{3,4})", filename, re.IGNORECASE)
|
||||
if match:
|
||||
return match.group(1) # Return the matched number string (e.g., "001" or "1234")
|
||||
return None
|
||||
|
||||
def get_file_info(filepath):
|
||||
"""
|
||||
Gets file size and attempts to get codec/bitrate using pymediainfo.
|
||||
Returns a dictionary with 'size_bytes', 'size_mb', 'codec', 'bitrate'.
|
||||
If mediainfo fails, 'codec' and 'bitrate' will be None.
|
||||
"""
|
||||
try:
|
||||
size_bytes = os.path.getsize(filepath)
|
||||
size_mb = bytes_to_mb(size_bytes)
|
||||
media_info = MediaInfo.parse(filepath)
|
||||
codec = None
|
||||
bitrate = 0 # Use 0 as a default for comparison
|
||||
|
||||
for track in media_info.tracks:
|
||||
if track.track_type == 'Video':
|
||||
if track.format:
|
||||
codec = track.format
|
||||
if track.bit_rate:
|
||||
bitrate = track.bit_rate
|
||||
break # Assuming one video track is sufficient
|
||||
|
||||
return {"size_bytes": size_bytes, "size_mb": size_mb, "codec": codec, "bitrate": bitrate}
|
||||
except Exception as e:
|
||||
print(f"Warning: Could not get media info for {filepath}: {e}")
|
||||
# Fallback if mediainfo fails
|
||||
size_bytes = os.path.getsize(filepath)
|
||||
size_mb = bytes_to_mb(size_bytes)
|
||||
return {"size_bytes": size_bytes, "size_mb": size_mb, "codec": None, "bitrate": 0}
|
||||
|
||||
def main():
|
||||
files_in_loc1 = {}
|
||||
files_in_loc2 = {}
|
||||
|
||||
print("Welcome to the DID Video Comparison and Management Script!")
|
||||
print("\nDo you want to enable 'Auto-Run' mode?")
|
||||
print("In 'Auto-Run' mode, the script will automatically:")
|
||||
print(" 1. Keep the file with the larger size.")
|
||||
print(f" 2. Use the filename from '{os.path.basename(LOCATION2)}' for the final copy.")
|
||||
print("If you decline, you will be prompted for each file manually.")
|
||||
|
||||
auto_run_choice = input("Enter 'yes' to enable Auto-Run, or 'no' to run manually (yes/no): ").strip().lower()
|
||||
auto_run = (auto_run_choice == 'yes')
|
||||
|
||||
if auto_run:
|
||||
print("\nAuto-Run mode enabled. Processing files automatically.")
|
||||
else:
|
||||
print("\nManual mode selected. You will be prompted for each file.")
|
||||
|
||||
# Objective 1: Identify files with the same naming convention
|
||||
# Scan Location 1
|
||||
for root, _, files in os.walk(LOCATION1):
|
||||
for file in files:
|
||||
did_num = extract_did_number(file) # Changed function call
|
||||
if did_num:
|
||||
if did_num not in files_in_loc1:
|
||||
files_in_loc1[did_num] = []
|
||||
files_in_loc1[did_num].append(os.path.join(root, file))
|
||||
|
||||
# Scan Location 2
|
||||
for root, _, files in os.walk(LOCATION2):
|
||||
for file in files:
|
||||
did_num = extract_did_number(file) # Changed function call
|
||||
if did_num:
|
||||
if did_num not in files_in_loc2:
|
||||
files_in_loc2[did_num] = []
|
||||
files_in_loc2[did_num].append(os.path.join(root, file))
|
||||
|
||||
# Process matched files
|
||||
matched_did_numbers = set(files_in_loc1.keys()).intersection(set(files_in_loc2.keys()))
|
||||
|
||||
if not matched_did_numbers:
|
||||
print(f"No matching DID files found in both '{LOCATION1}' and '{LOCATION2}'.")
|
||||
return
|
||||
|
||||
for did_num in sorted(list(matched_did_numbers)):
|
||||
# Assuming only one file per DIDXXX/XXXX number per location for simplicity.
|
||||
path1 = files_in_loc1[did_num][0]
|
||||
path2 = files_in_loc2[did_num][0]
|
||||
|
||||
file1_info = get_file_info(path1)
|
||||
file2_info = get_file_info(path2)
|
||||
|
||||
print(f"\n--- Processing DID{did_num} ---")
|
||||
print(f" File 1 (from '{os.path.basename(os.path.dirname(path1))}'): {os.path.basename(path1)} (Size: {file1_info['size_mb']} MB, Codec: {file1_info['codec']}, Bitrate: {file1_info['bitrate']} bps)")
|
||||
print(f" File 2 (from '{os.path.basename(os.path.dirname(path2))}'): {os.path.basename(path2)} (Size: {file2_info['size_mb']} MB, Codec: {file2_info['codec']}, Bitrate: {file2_info['bitrate']} bps)")
|
||||
|
||||
|
||||
file_to_keep_path = None
|
||||
final_filename = None
|
||||
|
||||
if auto_run:
|
||||
# Auto-run logic: keep larger size, use filename from Location 2
|
||||
if file1_info['size_bytes'] >= file2_info['size_bytes']:
|
||||
file_to_keep_path = path1
|
||||
else:
|
||||
file_to_keep_path = path2
|
||||
final_filename = os.path.basename(path2) # Always use filename from Location 2
|
||||
print(f" [Auto] Keeping: {os.path.basename(file_to_keep_path)}")
|
||||
print(f" [Auto] Using filename: {final_filename}")
|
||||
|
||||
else: # Manual mode
|
||||
# Original objective 2: Compare size, quality, and codec and list the better file
|
||||
recommended_better_path = None
|
||||
# Prioritize bitrate for recommendation
|
||||
if file1_info['bitrate'] > file2_info['bitrate']:
|
||||
recommended_better_path = path1
|
||||
elif file2_info['bitrate'] > file1_info['bitrate']:
|
||||
recommended_better_path = path2
|
||||
else:
|
||||
# If bitrates are similar or unavailable, use size as a proxy for recommendation
|
||||
if file1_info['size_bytes'] >= file2_info['size_bytes']:
|
||||
recommended_better_path = path1
|
||||
else:
|
||||
recommended_better_path = path2
|
||||
print(f" Recommended to keep: {os.path.basename(recommended_better_path)}")
|
||||
|
||||
|
||||
# Original objective 3: Prompt the user which one they would like to keep
|
||||
while True:
|
||||
choice = input("Which file would you like to keep? (1/2): ").strip()
|
||||
if choice == '1':
|
||||
file_to_keep_path = path1
|
||||
break
|
||||
elif choice == '2':
|
||||
file_to_keep_path = path2
|
||||
break
|
||||
else:
|
||||
print("Invalid choice. Please enter '1' or '2'.")
|
||||
|
||||
# Original objective 4: Prompt the user for each file, which one's file name will be used for the final copy
|
||||
while True:
|
||||
name_choice = input(f"Which file's name would you like to use for the final copy of DID{did_num}? (1/2): ").strip() # Changed prompt
|
||||
if name_choice == '1':
|
||||
final_filename = os.path.basename(path1)
|
||||
break
|
||||
elif name_choice == '2':
|
||||
final_filename = os.path.basename(path2)
|
||||
break
|
||||
else:
|
||||
print("Invalid choice. Please enter '1' or '2'.")
|
||||
|
||||
# Objective 5: Copy the selected file to the new location with the chosen name
|
||||
destination_path = os.path.join(FINAL_LOCATION, final_filename)
|
||||
try:
|
||||
print(f"Copying '{os.path.basename(file_to_keep_path)}' to '{destination_path}'...")
|
||||
shutil.copy2(file_to_keep_path, destination_path) # copy2 preserves metadata
|
||||
print("Copy successful!")
|
||||
except Exception as e:
|
||||
print(f"Error copying file {file_to_keep_path} to {destination_path}: {e}")
|
||||
|
||||
print("\nComparison and copying process complete.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,156 @@
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
from pymediainfo import MediaInfo # You might need to install this: pip install python-mediainfo
|
||||
|
||||
# Define the network locations
|
||||
LOCATION1 = r"\\truenas\isolation\videos\redmilf_refresh"
|
||||
LOCATION2 = r"\\truenas\isolation\videos\redmilf"
|
||||
FINAL_LOCATION = r"\\truenas\isolation\videos\redmilf_final"
|
||||
|
||||
# Ensure the final destination directory exists
|
||||
os.makedirs(FINAL_LOCATION, exist_ok=True)
|
||||
|
||||
def extract_milf_number(filename):
|
||||
"""
|
||||
Extracts the MILFXXX number from a filename.
|
||||
Returns the number as a string if found, otherwise None.
|
||||
"""
|
||||
match = re.search(r"MILF(\d{3})", filename, re.IGNORECASE)
|
||||
if match:
|
||||
return match.group(1)
|
||||
return None
|
||||
|
||||
def get_file_info(filepath):
|
||||
"""
|
||||
Gets file size and attempts to get codec/bitrate using pymediainfo.
|
||||
Returns a dictionary with 'size', 'codec', 'bitrate'.
|
||||
If mediainfo fails, 'codec' and 'bitrate' will be None.
|
||||
"""
|
||||
try:
|
||||
size = os.path.getsize(filepath)
|
||||
media_info = MediaInfo.parse(filepath)
|
||||
codec = None
|
||||
bitrate = 0 # Use 0 as a default for comparison
|
||||
|
||||
for track in media_info.tracks:
|
||||
if track.track_type == 'Video':
|
||||
if track.format:
|
||||
codec = track.format
|
||||
if track.bit_rate:
|
||||
bitrate = track.bit_rate
|
||||
break # Assuming one video track is sufficient
|
||||
|
||||
return {"size": size, "codec": codec, "bitrate": bitrate}
|
||||
except Exception as e:
|
||||
print(f"Warning: Could not get media info for {filepath}: {e}")
|
||||
return {"size": os.path.getsize(filepath), "codec": None, "bitrate": 0}
|
||||
|
||||
def determine_better_file(file1_info, file2_info, path1, path2):
|
||||
"""
|
||||
Compares two file infos to determine which one is 'better'.
|
||||
Prioritizes bitrate (quality), then size if bitrate is similar or unavailable.
|
||||
"""
|
||||
print(f"\nComparing:")
|
||||
print(f" File 1: {os.path.basename(path1)} (Size: {file1_info['size']} bytes, Codec: {file1_info['codec']}, Bitrate: {file1_info['bitrate']} bps)")
|
||||
print(f" File 2: {os.path.basename(path2)} (Size: {file2_info['size']} bytes, Codec: {file2_info['codec']}, Bitrate: {file2_info['bitrate']} bps)")
|
||||
|
||||
# Prioritize bitrate for quality
|
||||
if file1_info['bitrate'] > file2_info['bitrate']:
|
||||
return path1
|
||||
elif file2_info['bitrate'] > file1_info['bitrate']:
|
||||
return path2
|
||||
else:
|
||||
# If bitrates are similar or unavailable, use size as a proxy
|
||||
if file1_info['size'] >= file2_info['size']: # Greater or equal size usually means better quality
|
||||
return path1
|
||||
else:
|
||||
return path2
|
||||
|
||||
def main():
|
||||
files_in_loc1 = {}
|
||||
files_in_loc2 = {}
|
||||
|
||||
# Objective 1: Identify files with the same naming convention
|
||||
# Scan Location 1
|
||||
for root, _, files in os.walk(LOCATION1):
|
||||
for file in files:
|
||||
milf_num = extract_milf_number(file)
|
||||
if milf_num:
|
||||
if milf_num not in files_in_loc1:
|
||||
files_in_loc1[milf_num] = []
|
||||
files_in_loc1[milf_num].append(os.path.join(root, file))
|
||||
|
||||
# Scan Location 2
|
||||
for root, _, files in os.walk(LOCATION2):
|
||||
for file in files:
|
||||
milf_num = extract_milf_number(file)
|
||||
if milf_num:
|
||||
if milf_num not in files_in_loc2:
|
||||
files_in_loc2[milf_num] = []
|
||||
files_in_loc2[milf_num].append(os.path.join(root, file))
|
||||
|
||||
# Process matched files
|
||||
matched_milf_numbers = set(files_in_loc1.keys()).intersection(set(files_in_loc2.keys()))
|
||||
|
||||
if not matched_milf_numbers:
|
||||
print("No matching MILF files found in both locations.")
|
||||
return
|
||||
|
||||
for milf_num in sorted(list(matched_milf_numbers)):
|
||||
# Assuming only one file per MILFXXX number per location for simplicity.
|
||||
# If multiple files with the same MILFXXX number exist in one location,
|
||||
# this script will compare the first one it finds.
|
||||
# For a more robust solution, you might need to handle this by comparing
|
||||
# all permutations or selecting the "best" within each location first.
|
||||
path1 = files_in_loc1[milf_num][0]
|
||||
path2 = files_in_loc2[milf_num][0]
|
||||
|
||||
# Objective 2: Compare size, quality, and codec and list the better file
|
||||
file1_info = get_file_info(path1)
|
||||
file2_info = get_file_info(path2)
|
||||
|
||||
recommended_better_path = determine_better_file(file1_info, file2_info, path1, path2)
|
||||
|
||||
# Objective 3: Prompt the user which one they would like to keep
|
||||
print(f"\nFor MILF{milf_num}:")
|
||||
print(f" Option 1 (from '{os.path.basename(os.path.dirname(path1))}'): {os.path.basename(path1)}")
|
||||
print(f" Option 2 (from '{os.path.basename(os.path.dirname(path2))}'): {os.path.basename(path2)}")
|
||||
print(f" Recommended: {os.path.basename(recommended_better_path)}")
|
||||
|
||||
while True:
|
||||
choice = input("Which file would you like to keep? (1/2): ").strip()
|
||||
if choice == '1':
|
||||
file_to_keep_path = path1
|
||||
break
|
||||
elif choice == '2':
|
||||
file_to_keep_path = path2
|
||||
break
|
||||
else:
|
||||
print("Invalid choice. Please enter '1' or '2'.")
|
||||
|
||||
# Objective 4: Prompt the user for each file, which one's file name will be used for the final copy
|
||||
while True:
|
||||
name_choice = input(f"Which file's name would you like to use for the final copy of MILF{milf_num}? (1/2): ").strip()
|
||||
if name_choice == '1':
|
||||
final_filename = os.path.basename(path1)
|
||||
break
|
||||
elif name_choice == '2':
|
||||
final_filename = os.path.basename(path2)
|
||||
break
|
||||
else:
|
||||
print("Invalid choice. Please enter '1' or '2'.")
|
||||
|
||||
# Objective 5: Copy the selected file to the new location with the chosen name
|
||||
destination_path = os.path.join(FINAL_LOCATION, final_filename)
|
||||
try:
|
||||
print(f"Copying '{os.path.basename(file_to_keep_path)}' to '{destination_path}'...")
|
||||
shutil.copy2(file_to_keep_path, destination_path) # copy2 preserves metadata
|
||||
print("Copy successful!")
|
||||
except Exception as e:
|
||||
print(f"Error copying file {file_to_keep_path} to {destination_path}: {e}")
|
||||
|
||||
print("\nComparison and copying process complete.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,182 @@
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
from pymediainfo import MediaInfo # You might need to install this: pip install pymediainfo
|
||||
|
||||
# Define the network locations
|
||||
LOCATION1 = r"\\truenas\isolation\videos\redmilf_refresh"
|
||||
LOCATION2 = r"\\truenas\isolation\videos\redmilf"
|
||||
FINAL_LOCATION = r"\\truenas\isolation\videos\redmilf_final"
|
||||
|
||||
# Ensure the final destination directory exists
|
||||
os.makedirs(FINAL_LOCATION, exist_ok=True)
|
||||
|
||||
def bytes_to_mb(bytes_value):
|
||||
"""Converts bytes to megabytes, rounded to two decimal places."""
|
||||
return round(bytes_value / (1024 * 1024), 2)
|
||||
|
||||
def extract_milf_number(filename):
|
||||
"""
|
||||
Extracts the MILFXXX or MILFXXXX number from a filename.
|
||||
Returns the number as a string if found, otherwise None.
|
||||
"""
|
||||
# Updated regex to match MILF followed by 3 or 4 digits
|
||||
match = re.search(r"MILF(\d{3,4})", filename, re.IGNORECASE)
|
||||
if match:
|
||||
return match.group(1) # Return the matched number string (e.g., "001" or "1234")
|
||||
return None
|
||||
|
||||
def get_file_info(filepath):
|
||||
"""
|
||||
Gets file size and attempts to get codec/bitrate using pymediainfo.
|
||||
Returns a dictionary with 'size_bytes', 'size_mb', 'codec', 'bitrate'.
|
||||
If mediainfo fails, 'codec' and 'bitrate' will be None.
|
||||
"""
|
||||
try:
|
||||
size_bytes = os.path.getsize(filepath)
|
||||
size_mb = bytes_to_mb(size_bytes)
|
||||
media_info = MediaInfo.parse(filepath)
|
||||
codec = None
|
||||
bitrate = 0 # Use 0 as a default for comparison
|
||||
|
||||
for track in media_info.tracks:
|
||||
if track.track_type == 'Video':
|
||||
if track.format:
|
||||
codec = track.format
|
||||
if track.bit_rate:
|
||||
bitrate = track.bit_rate
|
||||
break # Assuming one video track is sufficient
|
||||
|
||||
return {"size_bytes": size_bytes, "size_mb": size_mb, "codec": codec, "bitrate": bitrate}
|
||||
except Exception as e:
|
||||
print(f"Warning: Could not get media info for {filepath}: {e}")
|
||||
# Fallback if mediainfo fails
|
||||
size_bytes = os.path.getsize(filepath)
|
||||
size_mb = bytes_to_mb(size_bytes)
|
||||
return {"size_bytes": size_bytes, "size_mb": size_mb, "codec": None, "bitrate": 0}
|
||||
|
||||
def main():
|
||||
files_in_loc1 = {}
|
||||
files_in_loc2 = {}
|
||||
|
||||
print("Welcome to the MILF Video Comparison and Management Script!")
|
||||
print("\nDo you want to enable 'Auto-Run' mode?")
|
||||
print("In 'Auto-Run' mode, the script will automatically:")
|
||||
print(" 1. Keep the file with the larger size.")
|
||||
print(f" 2. Use the filename from '{os.path.basename(LOCATION2)}' for the final copy.")
|
||||
print("If you decline, you will be prompted for each file manually.")
|
||||
|
||||
auto_run_choice = input("Enter 'yes' to enable Auto-Run, or 'no' to run manually (yes/no): ").strip().lower()
|
||||
auto_run = (auto_run_choice == 'yes')
|
||||
|
||||
if auto_run:
|
||||
print("\nAuto-Run mode enabled. Processing files automatically.")
|
||||
else:
|
||||
print("\nManual mode selected. You will be prompted for each file.")
|
||||
|
||||
# Objective 1: Identify files with the same naming convention
|
||||
# Scan Location 1
|
||||
for root, _, files in os.walk(LOCATION1):
|
||||
for file in files:
|
||||
milf_num = extract_milf_number(file)
|
||||
if milf_num:
|
||||
if milf_num not in files_in_loc1:
|
||||
files_in_loc1[milf_num] = []
|
||||
files_in_loc1[milf_num].append(os.path.join(root, file))
|
||||
|
||||
# Scan Location 2
|
||||
for root, _, files in os.walk(LOCATION2):
|
||||
for file in files:
|
||||
milf_num = extract_milf_number(file)
|
||||
if milf_num:
|
||||
if milf_num not in files_in_loc2:
|
||||
files_in_loc2[milf_num] = []
|
||||
files_in_loc2[milf_num].append(os.path.join(root, file))
|
||||
|
||||
# Process matched files
|
||||
matched_milf_numbers = set(files_in_loc1.keys()).intersection(set(files_in_loc2.keys()))
|
||||
|
||||
if not matched_milf_numbers:
|
||||
print("No matching MILF files found in both locations.")
|
||||
return
|
||||
|
||||
for milf_num in sorted(list(matched_milf_numbers)):
|
||||
# Assuming only one file per MILFXXX/XXXX number per location for simplicity.
|
||||
path1 = files_in_loc1[milf_num][0]
|
||||
path2 = files_in_loc2[milf_num][0]
|
||||
|
||||
file1_info = get_file_info(path1)
|
||||
file2_info = get_file_info(path2)
|
||||
|
||||
print(f"\n--- Processing MILF{milf_num} ---")
|
||||
print(f" File 1 (from '{os.path.basename(os.path.dirname(path1))}'): {os.path.basename(path1)} (Size: {file1_info['size_mb']} MB, Codec: {file1_info['codec']}, Bitrate: {file1_info['bitrate']} bps)")
|
||||
print(f" File 2 (from '{os.path.basename(os.path.dirname(path2))}'): {os.path.basename(path2)} (Size: {file2_info['size_mb']} MB, Codec: {file2_info['codec']}, Bitrate: {file2_info['bitrate']} bps)")
|
||||
|
||||
|
||||
file_to_keep_path = None
|
||||
final_filename = None
|
||||
|
||||
if auto_run:
|
||||
# Auto-run logic: keep larger size, use filename from Location 2
|
||||
if file1_info['size_bytes'] >= file2_info['size_bytes']:
|
||||
file_to_keep_path = path1
|
||||
else:
|
||||
file_to_keep_path = path2
|
||||
final_filename = os.path.basename(path2) # Always use filename from Location 2
|
||||
print(f" [Auto] Keeping: {os.path.basename(file_to_keep_path)}")
|
||||
print(f" [Auto] Using filename: {final_filename}")
|
||||
|
||||
else: # Manual mode
|
||||
# Original objective 2: Compare size, quality, and codec and list the better file
|
||||
recommended_better_path = None
|
||||
# Prioritize bitrate for recommendation
|
||||
if file1_info['bitrate'] > file2_info['bitrate']:
|
||||
recommended_better_path = path1
|
||||
elif file2_info['bitrate'] > file1_info['bitrate']:
|
||||
recommended_better_path = path2
|
||||
else:
|
||||
# If bitrates are similar or unavailable, use size as a proxy for recommendation
|
||||
if file1_info['size_bytes'] >= file2_info['size_bytes']:
|
||||
recommended_better_path = path1
|
||||
else:
|
||||
recommended_better_path = path2
|
||||
print(f" Recommended to keep: {os.path.basename(recommended_better_path)}")
|
||||
|
||||
|
||||
# Original objective 3: Prompt the user which one they would like to keep
|
||||
while True:
|
||||
choice = input("Which file would you like to keep? (1/2): ").strip()
|
||||
if choice == '1':
|
||||
file_to_keep_path = path1
|
||||
break
|
||||
elif choice == '2':
|
||||
file_to_keep_path = path2
|
||||
break
|
||||
else:
|
||||
print("Invalid choice. Please enter '1' or '2'.")
|
||||
|
||||
# Original objective 4: Prompt the user for each file, which one's file name will be used for the final copy
|
||||
while True:
|
||||
name_choice = input(f"Which file's name would you like to use for the final copy of MILF{milf_num}? (1/2): ").strip()
|
||||
if name_choice == '1':
|
||||
final_filename = os.path.basename(path1)
|
||||
break
|
||||
elif name_choice == '2':
|
||||
final_filename = os.path.basename(path2)
|
||||
break
|
||||
else:
|
||||
print("Invalid choice. Please enter '1' or '2'.")
|
||||
|
||||
# Objective 5: Copy the selected file to the new location with the chosen name
|
||||
destination_path = os.path.join(FINAL_LOCATION, final_filename)
|
||||
try:
|
||||
print(f"Copying '{os.path.basename(file_to_keep_path)}' to '{destination_path}'...")
|
||||
shutil.copy2(file_to_keep_path, destination_path) # copy2 preserves metadata
|
||||
print("Copy successful!")
|
||||
except Exception as e:
|
||||
print(f"Error copying file {file_to_keep_path} to {destination_path}: {e}")
|
||||
|
||||
print("\nComparison and copying process complete.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,114 @@
|
||||
import sqlite3
|
||||
import csv
|
||||
import os
|
||||
from datetime import datetime
|
||||
|
||||
# Function to convert Linux path to Windows path and replace specific substring
|
||||
def convert_path(path):
|
||||
return path.replace("/", "\\").replace("\\data\\", "\\\\truenas\\isolation\\videos\\")
|
||||
|
||||
# Fileshare path
|
||||
fileshare_path = r"\\truenas\isolation\stashapp\generated\transcodes"
|
||||
|
||||
# SQLite database path
|
||||
db_path = r"\\servervm\main\appdata\stashapp\config\stash-go.sqlite"
|
||||
|
||||
# Create a folder for the attempt with current date and time
|
||||
folder_name = "Attempt_" + datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
os.makedirs(folder_name)
|
||||
|
||||
# Connect to SQLite database
|
||||
conn = sqlite3.connect(db_path)
|
||||
cursor = conn.cursor()
|
||||
|
||||
# Error handling for gathering basenames
|
||||
try:
|
||||
# List all file basenames (without extensions) from fileshare path
|
||||
basenames = [os.path.splitext(name)[0] for name in os.listdir(fileshare_path)]
|
||||
except FileNotFoundError:
|
||||
print(f"Error: Fileshare path '{fileshare_path}' not found.")
|
||||
exit()
|
||||
except PermissionError:
|
||||
print(f"Error: Permission denied accessing fileshare path '{fileshare_path}'.")
|
||||
exit()
|
||||
|
||||
# Check if there are no basenames
|
||||
if not basenames:
|
||||
print("Error: No basenames found in the fileshare path.")
|
||||
exit()
|
||||
|
||||
# Construct the IN clause for the query
|
||||
placeholders = ",".join(['"{}"'.format(b) for b in basenames])
|
||||
|
||||
# Construct the query
|
||||
query_template = """
|
||||
SELECT f.*, ff.*, fo.path
|
||||
FROM files AS f
|
||||
INNER JOIN files_fingerprints AS ff ON f.id = ff.file_id
|
||||
INNER JOIN folders AS fo ON f.parent_folder_id = fo.id
|
||||
WHERE ff.fingerprint IN ({});
|
||||
"""
|
||||
|
||||
# Execute query
|
||||
cursor.execute(query_template.format(placeholders))
|
||||
rows = cursor.fetchall()
|
||||
|
||||
# Check if there are any results
|
||||
if len(rows) == 0:
|
||||
print("No results found.")
|
||||
exit()
|
||||
|
||||
# Export result to CSV
|
||||
csv_path = os.path.join(folder_name, "result.csv")
|
||||
with open(csv_path, "w", newline="", encoding="utf-8") as csv_file:
|
||||
csv_writer = csv.writer(csv_file)
|
||||
csv_writer.writerow([i[0] for i in cursor.description]) # Write header
|
||||
csv_writer.writerows(rows)
|
||||
|
||||
# Create new CSV with additional columns
|
||||
new_rows = []
|
||||
for row in rows:
|
||||
converted_path = convert_path(row[-1])
|
||||
full_file_path_video = os.path.join(converted_path, row[1]) # Construct full file path for videos
|
||||
|
||||
# Original filename from transcodes directory
|
||||
original_filename = os.path.join(fileshare_path, str(row[10])) # Assuming the fingerprint ID is in the 11th column (index 10)
|
||||
|
||||
new_rows.append((full_file_path_video, original_filename + ".mp4")) # Append ".mp4" to the second column
|
||||
|
||||
# Write new CSV
|
||||
new_csv_path = os.path.join(folder_name, "new_result.csv")
|
||||
with open(new_csv_path, "w", newline="", encoding="utf-8") as csv_file:
|
||||
csv_writer = csv.writer(csv_file)
|
||||
csv_writer.writerow(["Full File Path (Videos)", "Original File Path (Transcodes)"])
|
||||
csv_writer.writerows(new_rows)
|
||||
|
||||
# Confirm with the user
|
||||
print("CSV files created successfully in folder:", folder_name)
|
||||
print("Do you want to proceed to replace the files in column 1 with the files in column 2?")
|
||||
user_input = input("Enter 'yes' to proceed, or any other key to cancel: ")
|
||||
|
||||
if user_input.lower() == 'yes':
|
||||
print("Proceeding to replace the files...")
|
||||
|
||||
# Prepare to replace files
|
||||
for original_file, new_file in new_rows:
|
||||
original_basename = os.path.basename(original_file)
|
||||
original_dirname = os.path.dirname(original_file)
|
||||
new_basename = os.path.basename(new_file)
|
||||
new_dirname = os.path.dirname(new_file)
|
||||
|
||||
# Remove existing file if exists
|
||||
if os.path.exists(original_file):
|
||||
os.remove(original_file)
|
||||
|
||||
# Move the new file to replace the original file
|
||||
try:
|
||||
os.rename(new_file, os.path.join(original_dirname, original_basename))
|
||||
print(f"Replaced '{original_basename}' with '{new_basename}'.")
|
||||
except FileNotFoundError:
|
||||
print(f"Error: '{new_basename}' not found. Skipped replacement.")
|
||||
|
||||
print("Replacement completed.")
|
||||
else:
|
||||
print("Replacement canceled.")
|
||||
Reference in New Issue
Block a user