#! /usr/bin/python3 import os import threading import subprocess import time import re import fcntl import traceback import json CDROM_DRIVE_STATUS = 0x5326 CDS_NO_INFO = 0 CDS_NO_DISC = 1 CDS_TRAY_OPEN = 2 CDS_DRIVE_NOT_READ =3 CDS_DISC_OK = 4 CDS_STR = ["no info", "no disc", "tray open", "drive not read", "disc ok"] CDROM_DISC_STATUS = 0x5327 CDS_AUDIO = 100 CDS_DATA_1 = 101 CDS_DATA_2 = 102 CDROM_LOCKDOOR = 0x5329 CDROM_EJECT = 0x5309 class Reader(threading.Thread): def __init__(self, device, directory=None, **kwargs): self.device = device self.directory = directory self.status = { "type": "reader", "state": "idle", "device": self.device, } self.complete = 0 self.drive = os.open(device, os.O_RDONLY | os.O_NONBLOCK) return super().__init__(**kwargs) def run(self): while True: self.status["state"] = "idle" rv = fcntl.ioctl(self.drive, CDROM_DRIVE_STATUS) if rv == CDS_DISC_OK: rv = fcntl.ioctl(self.drive, CDROM_DISC_STATUS) try: if rv == CDS_AUDIO: self.handle_audio() elif rv in [CDS_DATA_1, CDS_DATA_2]: self.handle_data() else: print("Can't handle disc type %d" % rv) except Exception as e: print("Error in disc handler:", e) print(traceback.format_exc()) self.eject() else: time.sleep(3) def eject(self): self.status["state"] = "ejecting" for i in range(20): try: fcntl.ioctl(self.drive, CDROM_LOCKDOOR, 0) fcntl.ioctl(self.drive, CDROM_EJECT) return except Exception as e: time.sleep(i * 5) def finished(self, **kwargs): self.status["state"] = "finished read" fn = os.path.join(self.directory, "video", self.status["title"], "sucker.json") newfn = fn + ".new" with open(newfn, "w") as fout: json.dump(obj=self.status, fp=fout) os.rename(src=newfn, dst=fn) def handle_audio(self): pass # XXX def handle_data(self): self.video_scan() self.video_copy() self.finished() def video_scan(self): self.status["state"] = "Scanning for DVD title" p = subprocess.run( [ "dvdbackup", "--input=" + self.device, "--info", ], encoding="utf-8", capture_output=True, ) mediaSize = 0 title = "Unknown DVD" for l in p.stdout.split("\n"): if l.startswith("DVD-Video information"): title = l.split('"')[1] elif l.endswith("MiB"): parts = l.split() mediaSize += float(parts[-2]) * 1024 * 1024 elif l.endswith("KiB"): parts = l.split() mediaSize += float(parts[-2]) * 1024 self.status["title"] = title if mediaSize == 0: print("Media size = 0; aborting") return self.status["size"] = mediaSize def video_copy(self): self.status["state"] = "copying" mediaSize = self.status["size"] title = self.status["title"] basedir = os.path.join(self.directory, "video") p = subprocess.Popen( [ "dvdbackup", "--input=" + self.device, "--name=" + title, "--mirror", "--progress", ], encoding="utf-8", stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=basedir, ) totalBytes = titleSize = lastTitleSize = 0 progressRe = re.compile(r"^Copying.*([0-9.]+)/[0-9.]+ (MiB|KiB)") for line in p.stdout: line = line.strip() m = progressRe.search(line) if m and m[2] == "MiB": titleSize = float(m[1]) * 1024 * 1024 elif m and m[2] == "KiB": titleSize = float(m[1]) * 1024 if titleSize < lastTitleSize: totalBytes += lastTitleSize lastTitleSize = titleSize self.status["complete"] = (totalBytes + titleSize) / mediaSize # vi: sw=4 ts=4 et ai