Skip to content

Commit

Permalink
Merge pull request automatic-ripping-machine#977 from shitwolfymakes/…
Browse files Browse the repository at this point in the history
…models-refactor

Models Refactor
  • Loading branch information
microtechno9000 authored Nov 18, 2023
2 parents 9492c45 + 53b3ef9 commit 1fc7b8b
Show file tree
Hide file tree
Showing 28 changed files with 791 additions and 751 deletions.
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2.6.60
2.6.67
18 changes: 18 additions & 0 deletions arm/models/alembic_version.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from arm.ui import db


class AlembicVersion(db.Model):
"""
Class to hold the A.R.M db version
"""
version_num = db.Column(db.String(36), autoincrement=False, primary_key=True)

def __init__(self, version=None):
self.version_num = version

def __repr__(self):
return f'<AlembicVersion: {self.version_num}>'

def __str__(self):
"""Returns a string of the object"""
return self.__class__.__name__ + ": " + self.version_num
118 changes: 118 additions & 0 deletions arm/models/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
from prettytable import PrettyTable

from arm.ui import db


hidden_attribs = ("OMDB_API_KEY", "EMBY_USERID", "EMBY_PASSWORD",
"EMBY_API_KEY", "PB_KEY", "IFTTT_KEY", "PO_KEY",
"PO_USER_KEY", "PO_APP_KEY", "ARM_API_KEY",
"TMDB_API_KEY", "_sa_instance_state")
HIDDEN_VALUE = "<hidden>"


class Config(db.Model):
""" Holds all the config settings for each job
as these may change between each job """
CONFIG_ID = db.Column(db.Integer, primary_key=True)
job_id = db.Column(db.Integer, db.ForeignKey('job.job_id'))
ARM_CHECK_UDF = db.Column(db.Boolean)
GET_VIDEO_TITLE = db.Column(db.Boolean)
SKIP_TRANSCODE = db.Column(db.Boolean)
VIDEOTYPE = db.Column(db.String(25))
MINLENGTH = db.Column(db.String(6))
MAXLENGTH = db.Column(db.String(6))
MANUAL_WAIT = db.Column(db.Boolean)
MANUAL_WAIT_TIME = db.Column(db.Integer)
RAW_PATH = db.Column(db.String(255))
TRANSCODE_PATH = db.Column(db.String(255))
COMPLETED_PATH = db.Column(db.String(255))
EXTRAS_SUB = db.Column(db.String(255))
INSTALLPATH = db.Column(db.String(255))
LOGPATH = db.Column(db.String(255))
LOGLEVEL = db.Column(db.String(255))
LOGLIFE = db.Column(db.Integer)
DBFILE = db.Column(db.String(255))
WEBSERVER_IP = db.Column(db.String(25))
WEBSERVER_PORT = db.Column(db.Integer)
SET_MEDIA_PERMISSIONS = db.Column(db.Boolean)
CHMOD_VALUE = db.Column(db.Integer)
SET_MEDIA_OWNER = db.Column(db.Boolean)
CHOWN_USER = db.Column(db.String(50))
CHOWN_GROUP = db.Column(db.String(50))
RIPMETHOD = db.Column(db.String(25))
MKV_ARGS = db.Column(db.String(25))
DELRAWFILES = db.Column(db.Boolean)
HASHEDKEYS = db.Column(db.Boolean)
HB_PRESET_DVD = db.Column(db.String(256))
HB_PRESET_BD = db.Column(db.String(256))
DEST_EXT = db.Column(db.String(10))
HANDBRAKE_CLI = db.Column(db.String(25))
MAINFEATURE = db.Column(db.Boolean)
HB_ARGS_DVD = db.Column(db.String(256))
HB_ARGS_BD = db.Column(db.String(256))
EMBY_REFRESH = db.Column(db.Boolean)
EMBY_SERVER = db.Column(db.String(25))
EMBY_PORT = db.Column(db.String(6))
EMBY_CLIENT = db.Column(db.String(25))
EMBY_DEVICE = db.Column(db.String(50))
EMBY_DEVICEID = db.Column(db.String(128))
EMBY_USERNAME = db.Column(db.String(50))
EMBY_USERID = db.Column(db.String(128))
EMBY_PASSWORD = db.Column(db.String(128))
EMBY_API_KEY = db.Column(db.String(64))
NOTIFY_RIP = db.Column(db.Boolean)
NOTIFY_TRANSCODE = db.Column(db.Boolean)
PB_KEY = db.Column(db.String(64))
IFTTT_KEY = db.Column(db.String(64))
IFTTT_EVENT = db.Column(db.String(25))
PO_USER_KEY = db.Column(db.String(64))
PO_APP_KEY = db.Column(db.String(64))
OMDB_API_KEY = db.Column(db.String(64))

def __init__(self, c, job_id):
self.__dict__.update(c)
self.job_id = job_id

def __str__(self):
"""Returns a string of the object"""
return_string = self.__class__.__name__ + ": "
for attr, value in self.__dict__.items():
if str(attr) in hidden_attribs and value:
value = HIDDEN_VALUE
return_string = return_string + "(" + str(attr) + "=" + str(value) + ") "

return return_string

def list_params(self):
"""Returns a string of the object"""
return_string = self.__class__.__name__ + ": "
for attr, value in self.__dict__.items():
if return_string:
return_string = return_string + "\n"
if str(attr) in hidden_attribs and value:
value = HIDDEN_VALUE
return_string = return_string + str(attr) + ":" + str(value)

return return_string

def pretty_table(self):
"""Returns a string of the PrettyTable"""
pretty_table = PrettyTable()
pretty_table.field_names = ["Config", "Value"]
pretty_table._max_width = {"Config": 20, "Value": 30}
for attr, value in self.__dict__.items():
if str(attr) in hidden_attribs and value:
value = HIDDEN_VALUE
pretty_table.add_row([str(attr), str(value)])
return str(pretty_table.get_string())

def get_d(self):
"""
Return a dict of class - exclude any sensitive info
:return: dict containing all attribs from class
"""
return_dict = {}
for key, value in self.__dict__.items():
if str(key) not in hidden_attribs:
return_dict[str(key)] = str(value)
return return_dict
217 changes: 217 additions & 0 deletions arm/models/job.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
import logging
import os
import psutil
import pyudev
import subprocess
import time

from prettytable import PrettyTable
from arm.ripper import music_brainz
from arm.ui import db
import arm.config.config as cfg

# THESE IMPORTS ARE REQUIRED FOR THE db.Relationships to work
from arm.models.track import Track # noqa: F401
from arm.models.config import Config # noqa: F401


class Job(db.Model):
"""
Job Class hold most of the details for each job
connects to track, config
"""
job_id = db.Column(db.Integer, primary_key=True)
arm_version = db.Column(db.String(20))
crc_id = db.Column(db.String(63))
logfile = db.Column(db.String(256))
start_time = db.Column(db.DateTime)
stop_time = db.Column(db.DateTime)
job_length = db.Column(db.String(12))
status = db.Column(db.String(32))
stage = db.Column(db.String(63))
no_of_titles = db.Column(db.Integer)
title = db.Column(db.String(256))
title_auto = db.Column(db.String(256))
title_manual = db.Column(db.String(256))
year = db.Column(db.String(4))
year_auto = db.Column(db.String(4))
year_manual = db.Column(db.String(4))
video_type = db.Column(db.String(20))
video_type_auto = db.Column(db.String(20))
video_type_manual = db.Column(db.String(20))
imdb_id = db.Column(db.String(15))
imdb_id_auto = db.Column(db.String(15))
imdb_id_manual = db.Column(db.String(15))
poster_url = db.Column(db.String(256))
poster_url_auto = db.Column(db.String(256))
poster_url_manual = db.Column(db.String(256))
devpath = db.Column(db.String(15))
mountpoint = db.Column(db.String(20))
hasnicetitle = db.Column(db.Boolean)
errors = db.Column(db.Text)
disctype = db.Column(db.String(20)) # dvd/bluray/data/music/unknown
label = db.Column(db.String(256))
path = db.Column(db.String(256))
ejected = db.Column(db.Boolean)
updated = db.Column(db.Boolean)
pid = db.Column(db.Integer)
pid_hash = db.Column(db.Integer)
tracks = db.relationship('Track', backref='job', lazy='dynamic')
config = db.relationship('Config', uselist=False, backref="job")

def __init__(self, devpath):
"""Return a disc object"""
self.devpath = devpath
self.mountpoint = "/mnt" + devpath
self.hasnicetitle = False
self.video_type = "unknown"
self.ejected = False
self.updated = False
if cfg.arm_config['VIDEOTYPE'] != "auto":
self.video_type = cfg.arm_config['VIDEOTYPE']
self.parse_udev()
self.get_pid()
self.stage = str(round(time.time() * 100))

if self.disctype == "dvd" and not self.label:
logging.info("No disk label Available. Trying lsdvd")
command = f"lsdvd {devpath} | grep 'Disc Title' | cut -d ' ' -f 3-"
lsdvdlbl = str(subprocess.check_output(command, shell=True).strip(), 'utf-8')
self.label = lsdvdlbl

def __str__(self):
"""Returns a string of the object"""

return_string = self.__class__.__name__ + ": "
for attr, value in self.__dict__.items():
return_string = return_string + "(" + str(attr) + "=" + str(value) + ") "

return return_string

def __repr__(self):
return f'<Job {self.label}>'

def parse_udev(self):
"""Parse udev for properties of current disc"""
context = pyudev.Context()
device = pyudev.Devices.from_device_file(context, self.devpath)
self.disctype = "unknown"

for key, value in device.items():
logging.debug(f"pyudev: {key}: {value}")
if key == "ID_FS_LABEL":
self.label = value
if value == "iso9660":
self.disctype = "data"
elif key == "ID_CDROM_MEDIA_BD":
self.disctype = "bluray"
elif key == "ID_CDROM_MEDIA_DVD":
self.disctype = "dvd"
elif key == "ID_CDROM_MEDIA_TRACK_COUNT_AUDIO":
self.disctype = "music"
else:
continue

def get_pid(self):
"""
Get the jobs process id
:return: None
"""
pid = os.getpid()
process_id = psutil.Process(pid)
self.pid = pid
self.pid_hash = hash(process_id)

def get_disc_type(self, found_hvdvd_ts):
"""
Checks/corrects the current disc-type
:param found_hvdvd_ts: gets pushed in from utils - saves importing utils
:return: None
"""
if self.disctype == "music":
logging.debug("Disc is music.")
self.label = music_brainz.main(self)
elif os.path.isdir(self.mountpoint + "/VIDEO_TS"):
logging.debug(f"Found: {self.mountpoint}/VIDEO_TS")
self.disctype = "dvd"
elif os.path.isdir(self.mountpoint + "/video_ts"):
logging.debug(f"Found: {self.mountpoint}/video_ts")
self.disctype = "dvd"
elif os.path.isdir(self.mountpoint + "/BDMV"):
logging.debug(f"Found: {self.mountpoint}/BDMV")
self.disctype = "bluray"
elif os.path.isdir(self.mountpoint + "/HVDVD_TS"):
logging.debug(f"Found: {self.mountpoint}/HVDVD_TS")
# do something here
elif found_hvdvd_ts:
logging.debug("Found file: HVDVD_TS")
# do something here too
else:
logging.debug("Did not find valid dvd/bd files. Changing disc-type to 'data'")
self.disctype = "data"

def identify_audio_cd(self):
"""
Get the title for audio cds to use for the logfile name.
Needs the job class passed into it so it can be forwarded to mb
return - only the logfile - setup_logging() adds the full path
"""
# Use the music label if we can find it - defaults to music_cd.log
disc_id = music_brainz.get_disc_id(self)
logging.debug(f"music_id: {disc_id}")
mb_title = music_brainz.get_title(disc_id, self)
logging.debug(f"mm_title: {mb_title}")

if mb_title == "not identified":
self.label = self.title = "not identified"
logfile = "music_cd.log"
new_log_file = f"music_cd_{round(time.time() * 100)}.log"
else:
logfile = f"{mb_title}.log"
new_log_file = f"{mb_title}_{round(time.time() * 100)}.log"

temp_log_full = os.path.join(cfg.arm_config['LOGPATH'], logfile)
logfile = new_log_file if os.path.isfile(temp_log_full) else logfile
return logfile

def pretty_table(self):
"""Returns a string of the prettytable"""
pretty_table = PrettyTable()
pretty_table.field_names = ["Config", "Value"]
pretty_table._max_width = {"Config": 50, "Value": 60}
for attr, value in self.__dict__.items():
if attr == "config":
pretty_table.add_row([str(attr), str(value.pretty_table())])
else:
pretty_table.add_row([str(attr), str(value)])
return str(pretty_table.get_string())

def get_d(self):
"""
Return a dict of class - exclude the _sa_instance_state
:return: dict containing all attribs from class
"""
return_dict = {}
for key, value in self.__dict__.items():
if '_sa_instance_state' not in key:
return_dict[str(key)] = str(value)
return return_dict

def eject(self):
"""Eject disc if it hasn't previously been ejected"""
if not self.ejected:
self.ejected = True
try:
# This might always return true
if bool(os.system("umount " + self.devpath)):
logging.debug(f"Unmounted disc {self.devpath}")
else:
logging.debug(f"Failed to unmount {self.devpath}")
if bool(os.system("eject -sv " + self.devpath)):
logging.debug(f"Ejected disc {self.devpath}")
else:
logging.debug(f"Failed to eject {self.devpath}")
except Exception as error:
logging.debug(f"{self.devpath} couldn't be ejected {error}")
Loading

0 comments on commit 1fc7b8b

Please sign in to comment.