Possibly working?

This commit is contained in:
Neale Pickett 2022-02-26 08:05:20 -07:00
parent 22304132f3
commit c8b4d57027
5 changed files with 281 additions and 156 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
__pycache__

89
encoder.py Normal file
View File

@ -0,0 +1,89 @@
#! /usr/bin/python3
import os
import threading
import subprocess
import glob
import os
import json
import io
import shutil
import time
import re
class Encoder(threading.Thread):
def __init__(self, directory=None, **kwargs):
self.status = {}
self.directory = directory
for d in ("audio", "video"):
os.makedirs(os.path.join(directory, d), exist_ok=True)
return super().__init__(**kwargs)
def run(self):
while True:
wait = True
self.status = {"type": "encoder", "state": "idle"}
for mtype in ("audio", "video"):
for fn in glob.glob(os.path.join(self.directory, mtype, "*", "sucker.json")):
fdir = os.path.dirname(fn)
with open(fn) as f:
obj = json.load(f)
self.encode(mtype, fdir, obj)
wait = False
if wait:
time.sleep(12)
def encode(self, mtype, fdir, obj):
if mtype == "audio":
self.encode_audio(fdir, obj)
else:
self.encode_video(fdir, obj)
shutil.rmtree(fdir)
def encode_audio(self, fdir, obj):
print("Not implemented")
def encode_video(self, fdir, obj):
self.status["state"] = "encoding"
title = os.path.basename(fdir)
self.status["title"] = title
print("encoding", title, fdir)
outfn = "%s.mkv" % title
tmppath = os.path.join(fdir, outfn)
outpath = os.path.join(self.directory, outfn)
p = subprocess.Popen(
[
"HandBrakeCLI",
"--json",
"-i", "%s/VIDEO_TS" % fdir,
"--main-feature",
"--native-language", "eng",
"-Z", "Chromecast 1080p30 Surround",
"-o", tmppath,
],
encoding="utf-8",
stdout=subprocess.PIPE,
stderr=subprocess.DEVNULL,
)
# HandBrakeCLI spits out sort of JSON.
# But Python has no built-in way to stream JSON objects.
# Hence this kludge.
progressRe = re.compile(r'^"Progress": ([0-9.]+),')
for line in p.stdout:
line = line.strip()
m = progressRe.search(line)
if m:
progress = float(m[1])
self.status["complete"] = progress
os.rename(
src=tmppath,
dst=outpath,
)
pass
# vi: sw=4 ts=4 et ai

146
reader.py Normal file
View File

@ -0,0 +1,146 @@
#! /usr/bin/python3
import os
import threading
import subprocess
import time
import re
import fcntl
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)
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, "wb") 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.path,
"--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.path,
"--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

28
statuser.py Normal file
View File

@ -0,0 +1,28 @@
#! /usr/bin/python3
import threading
import json
import glob
import time
import os
class Statuser(threading.Thread):
def __init__(self, workers, directory=None, **kwargs):
self.workers = workers
self.directory = directory
self.status = {}
super().__init__(**kwargs)
def run(self):
while True:
self.status["finished"] = {
"video": glob.glob(os.path.join(self.directory, "*.mkv")),
"audio": glob.glob(os.path.join(self.directory, "*/*/*.mp3")),
}
self.status["workers"] = [w.status for w in self.workers]
time.sleep(12)
def json(self):
return json.dumps(self.status)
# vi: sw=4 ts=4 et ai

173
sucker.py
View File

@ -1,39 +1,16 @@
#! /usr/bin/python3
import os
import argparse
import threading
import subprocess
import http.server
import pathlib
import json
import glob
import io
import time
import re
import fcntl
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_EJECT = 0x5309
status = {
"queue": [],
}
import reader, encoder, statuser
class HTTPRequestHandler(http.server.SimpleHTTPRequestHandler):
def __init__(self, r, a, s, directory, statuser):
self.statuser = statuser
super().__init__(r, a, s, directory=directory)
def do_GET(self):
if self.path == "/status.json":
return self.get_status()
@ -42,129 +19,9 @@ class HTTPRequestHandler(http.server.SimpleHTTPRequestHandler):
def get_status(self):
self.send_response(200)
self.end_headers()
buf = json.dumps(status).encode("utf-8")
buf = self.statuser.json().encode("utf-8")
self.wfile.write(buf)
class Reader(threading.Thread):
def __init__(self, path, directory=None, **kwargs):
self.path = path
self.directory = directory
self.status = {"state": "idle"}
self.complete = 0
self.drive = os.open(path, os.O_RDONLY | os.O_NONBLOCK)
return super().__init__(**kwargs)
def run(self):
while True:
self.status = {
"type": "reader",
"state": "idle",
"device": self.path,
}
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)
fcntl.ioctl(self.drive, CDROM_EJECT)
else:
time.sleep(3)
def handle_audio(self):
pass # XXX
def handle_data(self):
self.status["state"] = "Scanning for DVD title"
p = subprocess.run(
[
"dvdbackup",
"--input=" + self.path,
"--info",
],
encoding="utf-8",
capture_output=True,
)
mediaSize = 0
title = None
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
print("Copying %r (%d bytes)" % (title, mediaSize))
self.copy(mediaSize)
open(os.path.join(title, "finished"), "w")
def copy(self, mediaSize):
self.status["state"] = "copying"
p = subprocess.Popen(
[
"dvdbackup",
"--input=" + self.path,
"--mirror",
"--progress",
],
encoding="utf-8",
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
cwd=os.path.join(self.directory, "video"),
)
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
class Encoder(threading.Thread):
def __init__(self, directory=None, **kwargs):
self.status = {"state": "idle"}
self.directory = directory
for d in ("audio", "video"):
os.makedirs(os.path.join(directory, d), exist_ok=True)
return super().__init__(**kwargs)
def run(self):
self.status = {"type": "encoder", "state": "idle"}
class Statuser(threading.Thread):
def __init__(self, workers, directory=None, **kwargs):
self.workers = workers
self.directory = directory
super().__init__(**kwargs)
def run(self):
while True:
status["finished"] = {
"video": glob.glob(os.path.join(self.directory, "*.mkv")),
"audio": glob.glob(os.path.join(self.directory, "*/*/*.mp3")),
}
status["workers"] = [w.status for w in self.workers]
time.sleep(12)
def main():
parser = argparse.ArgumentParser(description="Rip/encode optical media")
parser.add_argument("-incoming", type=pathlib.Path, default="/incoming")
@ -173,14 +30,18 @@ def main():
parser.add_argument("-drive", nargs="+", default=["/dev/sr0"])
args = parser.parse_args()
readers = [Reader(d, directory=args.incoming, daemon=True) for d in args.drive]
encoder = Encoder(directory=args.incoming, daemon=True)
statuser = Statuser(readers + [encoder], directory=args.incoming, daemon=True)
[r.start() for r in readers]
encoder.start()
statuser.start()
readers = []
for d in args.drive:
readers.append(reader.Reader(d, directory=args.incoming, daemon=True))
encoders = []
for i in range(1):
encoders.append(encoder.Encoder(directory=args.incoming, daemon=True))
st = statuser.Statuser(readers + encoders, directory=args.incoming, daemon=True)
handler = lambda r, a, s: HTTPRequestHandler(r, a, s, directory=args.www)
[w.start() for w in readers + encoders]
st.start()
handler = lambda r, a, s: HTTPRequestHandler(r, a, s, directory=args.www, statuser=st)
httpd = http.server.ThreadingHTTPServer(('', args.port), handler)
httpd.serve_forever()