2022-02-26 08:05:20 -07:00
|
|
|
#! /usr/bin/python3
|
|
|
|
|
|
|
|
import os
|
|
|
|
import threading
|
|
|
|
import subprocess
|
|
|
|
import time
|
|
|
|
import re
|
|
|
|
import fcntl
|
2022-05-24 17:23:12 -06:00
|
|
|
import traceback
|
2022-08-21 15:58:11 -06:00
|
|
|
import json
|
2022-08-20 16:42:53 -06:00
|
|
|
import logging
|
2022-08-21 15:58:11 -06:00
|
|
|
import dvd
|
2022-08-23 22:06:44 -06:00
|
|
|
import cd
|
2022-02-26 08:05:20 -07:00
|
|
|
|
|
|
|
CDROM_DRIVE_STATUS = 0x5326
|
|
|
|
CDS_NO_INFO = 0
|
|
|
|
CDS_NO_DISC = 1
|
|
|
|
CDS_TRAY_OPEN = 2
|
2022-08-20 16:42:53 -06:00
|
|
|
CDS_DRIVE_NOT_READ = 3
|
2022-02-26 08:05:20 -07:00
|
|
|
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
|
2022-08-20 16:42:53 -06:00
|
|
|
self.staleness = 0
|
|
|
|
self.drive = None
|
|
|
|
logging.info("Starting reader on %s" % self.device)
|
2022-02-26 08:05:20 -07:00
|
|
|
return super().__init__(**kwargs)
|
|
|
|
|
2022-08-20 16:42:53 -06:00
|
|
|
def reopen(self):
|
|
|
|
if (self.staleness > 15) or not self.drive:
|
2022-08-23 22:06:44 -06:00
|
|
|
if self.drive:
|
|
|
|
self.drive.close()
|
|
|
|
self.drive = None
|
2022-08-20 16:42:53 -06:00
|
|
|
try:
|
|
|
|
self.drive = os.open(self.device, os.O_RDONLY | os.O_NONBLOCK)
|
2022-08-21 21:40:00 -06:00
|
|
|
logging.debug("Reopened %s" % self.device)
|
2022-08-20 16:42:53 -06:00
|
|
|
except FileNotFoundError:
|
|
|
|
pass
|
|
|
|
self.staleness = 0
|
|
|
|
else:
|
|
|
|
self.staleness += 1
|
|
|
|
|
2022-02-26 08:05:20 -07:00
|
|
|
def run(self):
|
|
|
|
while True:
|
|
|
|
self.status["state"] = "idle"
|
2022-08-20 16:42:53 -06:00
|
|
|
self.reopen()
|
|
|
|
if not self.drive:
|
|
|
|
time.sleep(30)
|
|
|
|
continue
|
2022-02-26 08:05:20 -07:00
|
|
|
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:
|
2022-08-20 16:42:53 -06:00
|
|
|
logging.info("Can't handle disc type %d" % rv)
|
2022-02-26 08:05:20 -07:00
|
|
|
except Exception as e:
|
2022-08-20 16:42:53 -06:00
|
|
|
logging.error("Error in disc handler: %s" % e)
|
|
|
|
logging.error(traceback.format_exc())
|
2022-02-26 08:05:20 -07:00
|
|
|
self.eject()
|
2022-08-22 19:45:02 -06:00
|
|
|
elif rv in (CDS_TRAY_OPEN, CDS_NO_DISC):
|
2022-08-21 17:22:06 -06:00
|
|
|
time.sleep(3)
|
2022-02-26 08:05:20 -07:00
|
|
|
else:
|
2022-08-20 16:42:53 -06:00
|
|
|
logging.info("CDROM_DRIVE_STATUS: %d (%s)" % (rv, CDS_STR[rv]))
|
2022-02-26 08:05:20 -07:00
|
|
|
time.sleep(3)
|
|
|
|
|
|
|
|
def eject(self):
|
|
|
|
self.status["state"] = "ejecting"
|
2022-08-21 15:58:11 -06:00
|
|
|
|
2022-02-26 08:05:20 -07:00
|
|
|
for i in range(20):
|
|
|
|
try:
|
|
|
|
fcntl.ioctl(self.drive, CDROM_LOCKDOOR, 0)
|
|
|
|
fcntl.ioctl(self.drive, CDROM_EJECT)
|
|
|
|
return
|
|
|
|
except Exception as e:
|
2022-08-20 16:42:53 -06:00
|
|
|
logging.error("Ejecting: %v" % e)
|
2022-02-26 08:05:20 -07:00
|
|
|
time.sleep(i * 5)
|
|
|
|
|
2022-08-23 22:06:44 -06:00
|
|
|
# XXX: rename this to something like "write_status"
|
2022-02-26 08:05:20 -07:00
|
|
|
def finished(self, **kwargs):
|
|
|
|
self.status["state"] = "finished read"
|
2022-08-21 15:58:11 -06:00
|
|
|
fn = os.path.join(self.directory, self.status["title"], "sucker.json")
|
2022-02-26 08:05:20 -07:00
|
|
|
newfn = fn + ".new"
|
2022-05-24 17:23:12 -06:00
|
|
|
with open(newfn, "w") as fout:
|
2022-02-26 08:05:20 -07:00
|
|
|
json.dump(obj=self.status, fp=fout)
|
|
|
|
os.rename(src=newfn, dst=fn)
|
|
|
|
|
|
|
|
def handle_audio(self):
|
2022-08-23 22:06:44 -06:00
|
|
|
self.status["video"] = False
|
|
|
|
|
|
|
|
self.status["state"] = "reading"
|
|
|
|
cd.read(self.device, self.status)
|
|
|
|
|
|
|
|
directory = os.path.join(self.directory, status["title"])
|
|
|
|
os.makedirs(directory, exist_ok=True)
|
|
|
|
self.status["state"] = "copying"
|
|
|
|
cd.copy(self.device, self.status, self.directory)
|
|
|
|
self.finished() # XXX: rename this to something like "write_status"
|
|
|
|
|
2022-02-26 08:05:20 -07:00
|
|
|
|
|
|
|
def handle_data(self):
|
2022-08-21 17:22:06 -06:00
|
|
|
self.status["video"] = True
|
|
|
|
src = dvd.Copier(self.device, self.status)
|
2022-08-21 15:58:11 -06:00
|
|
|
src.copy(self.directory)
|
2022-02-26 08:05:20 -07:00
|
|
|
self.finished()
|
|
|
|
|
|
|
|
# vi: sw=4 ts=4 et ai
|