From bc42662924d5140535957356070b900ce382094b Mon Sep 17 00:00:00 2001 From: Julian Mutter Date: Tue, 16 Aug 2022 21:31:08 +0200 Subject: [PATCH] Redid script with python --- .gitignore | 1 + bell.oga | Bin 0 -> 8495 bytes rip_dvd.py | 180 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 181 insertions(+) create mode 100644 .gitignore create mode 100644 bell.oga create mode 100644 rip_dvd.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c274ebe --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/rip/ diff --git a/bell.oga b/bell.oga new file mode 100644 index 0000000000000000000000000000000000000000..144d2b3670e8e294ee7cfe343355bf90123cb687 GIT binary patch literal 8495 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 literal 0 HcmV?d00001 diff --git a/rip_dvd.py b/rip_dvd.py new file mode 100644 index 0000000..ee52307 --- /dev/null +++ b/rip_dvd.py @@ -0,0 +1,180 @@ +#!/usr/bin/env python3 + +import argparse +import subprocess +import os +from datetime import datetime +from playsound import playsound +import time +import re + + +def parse_args(): + parser = argparse.ArgumentParser( + description="Rip content of dvds" # , formatter_class=argparse.RawTextHelpFormatter + ) + parser.add_argument( + "type", + choices=("movie", "series"), + help="If the dvd contains one movie, or multiple episodes of a series", + ) + parser.add_argument( + "title", + help='The title of the movie. Series must end with "Sx Ex-x". E.g.: "Lost S01 E1-04"', + ) + parser.add_argument( + "--dev", + help=f"Dvd device to rip from. Defaults to {DEFAULT_DVD_DEVICE}", + default=DEFAULT_DVD_DEVICE, + ) + + return parser.parse_args() + + +def validate_series_title(title): + if not is_series_title_valid(title): + print("Invalid series title!") + exit(1) + + +def is_series_title_valid(title): + try: + episode_matches = re.search(SERIES_TITLE_REGEX, title) + if episode_matches is None: + return False + episode_from = int(episode_matches.group(1)) + episode_to = int(episode_matches.group(2)) + if episode_from < episode_to: + return True + except: + return False + + return False + + +def chdir_to_script_dir(): + os.chdir(os.path.dirname(__file__)) + + +def mkdirs(): + os.makedirs(LOGS_DIR, exist_ok=True) + os.makedirs(RIPPED_DIR, exist_ok=True) + + +def create_log_line(args): + date = datetime.now().strftime("%d/%m/%Y %H:%M:%S") + return f'{date} - {args.type} - "{args.title}"' + + +def write_line_to_logfile(line, filename): + with open(filename, "a") as file: + file.write(line + "\n") + + +def do_rip(args) -> bool: + """Returns: success of command""" + command = create_rip_command(args) + proc = subprocess.run(command, shell=True, capture_output=False) + + return proc.returncode == 0 + + +def create_rip_command(args): + dest = RIPPED_DIR + dev = args.dev + title = args.title + + if args.type == "movie": + return f"dvdbackup -i '{dev}' -o '{dest}' -F -n '{title}'" + else: + return f"dvdbackup -i '{dev}' -o '{dest}' -M -n '{title}'" + + +def notify_ripping_success(args, program_execution_time_str): + print("Success!") + print(f"Ripping took {program_execution_time_str}") + send_notification( + f'{args.type.capitalize()} "{args.title}" ripped successfully in {program_execution_time_str}!' + ) + playsound(NOTIFICATION_SOUND) + + +def notify_ripping_error(args, program_execution_time_str): + print(f"Ripping failure after {program_execution_time_str}") + send_notification( + f'Error ripping {args.type.capitalize()} "{args.title}" after {program_execution_time_str}!' + ) + playsound(NOTIFICATION_SOUND) + + +def send_notification(text): + subprocess.run( + ["notify-send", text], + shell=False, + ) + + +def transfer_ripped_to_transcoder(args) -> bool: + """Returns: success of command""" + src = os.path.join(RIPPED_DIR, args.title) + destination = None + if args.type == "movie": + destination = f"{TRANSCODER_INPUT_FOLDER}/movies/" + else: + destination = f"{TRANSCODER_INPUT_FOLDER}/series/" + + proc = subprocess.run(["rsync", "-azv", src, destination]) + success = proc.returncode == 0 + if success: + print("Transfer successful") + else: + print("Transfer failed!") + return success + + +def delete_ripped(args): + try: + os.rmdir(os.path.join(RIPPED_DIR, args.title)) + except FileNotFoundError: + pass + + +TRANSCODER_FOLDER = "transcode" # pi@192.168.xxx:/home/pi/transcode +TRANSCODER_INPUT_FOLDER = f"{TRANSCODER_FOLDER}/raw" +DEFAULT_DVD_DEVICE = "/dev/cdrom" +SERIES_TITLE_REGEX = r"S\d+[ _]?E(\d+)-(\d+)$" +RIPPED_DIR = "rip/ripped" +LOGS_DIR = "rip/logs" +RIP_LOGFILE = f"{LOGS_DIR}/rip.log" +RIP_ERR_LOGFILE = f"{LOGS_DIR}/rip-err.log" +RIP_SUCCESS_LOGFILE = f"{LOGS_DIR}/rip-success.log" +NOTIFICATION_SOUND = "bell.oga" + +if __name__ == "__main__": + program_start_time = time.time() + args = parse_args() + + if args.type == "series": + validate_series_title(args.title) + chdir_to_script_dir() + mkdirs() + + log_line = create_log_line(args) + write_line_to_logfile(log_line, RIP_LOGFILE) + + success = do_rip(args) + program_execution_time_minutes = (program_start_time - time.time()) / 60.0 + program_execution_time_minutes = max(0.0, program_execution_time_minutes) + program_execution_time_str = f"{program_execution_time_minutes:.1f} minutes" + if success: + write_line_to_logfile(log_line, RIP_SUCCESS_LOGFILE) + notify_ripping_success(args, program_execution_time_str) + transfer_success = transfer_ripped_to_transcoder(args) + if transfer_success: + print("Deleting output") + delete_ripped(args) + else: + write_line_to_logfile(log_line, RIP_ERR_LOGFILE) + notify_ripping_error(args, program_execution_time_str) + print("Deleting partial output") + delete_ripped(args)