Compare commits
10 Commits
73cf2ab9b1
...
b1c43da994
Author | SHA1 | Date | |
---|---|---|---|
b1c43da994 | |||
092d48bca2 | |||
43e05f1858 | |||
3a77d7c348 | |||
be4aee2b27 | |||
352a91d4ca | |||
542b736d6e | |||
2b7538de71 | |||
3ed8cf828f | |||
b65ef44f52 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -152,3 +152,4 @@ cython_debug/
|
|||||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||||
#.idea/
|
#.idea/
|
||||||
|
|
||||||
|
Settings.cfg
|
||||||
|
25
MediaManager.py
Normal file
25
MediaManager.py
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import time
|
||||||
|
from Video import videoservice
|
||||||
|
from Universal import loggingservice
|
||||||
|
|
||||||
|
tmp_RunScript = True
|
||||||
|
tmp_Initialized = False
|
||||||
|
tmp_Debug = True
|
||||||
|
tmp_OutputFile = False
|
||||||
|
|
||||||
|
tmp_Library = 1
|
||||||
|
tmp_Resume = False
|
||||||
|
|
||||||
|
logger = loggingservice.Output(tmp_Debug, tmp_OutputFile)
|
||||||
|
|
||||||
|
while tmp_RunScript:
|
||||||
|
if not tmp_Initialized:
|
||||||
|
video_process = videoservice.VideoService()
|
||||||
|
tmp_Initialized = True
|
||||||
|
if tmp_Resume:
|
||||||
|
#TODO Implement Resume to VideoService
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
video_process.startNewProcess(tmp_Library)
|
||||||
|
tmp_Library += 1
|
||||||
|
print("Goodbye")
|
@ -1,3 +1,3 @@
|
|||||||
# Media-Manager
|
# Media-Manager
|
||||||
|
|
||||||
Sorts video content and if allowed uploads to server. For (Plex/Jellyfin)
|
Sorts video content and if allowed uploads to server. Designed Plex/Jellyfin servers in mind. But can be used for personal servers for movies and tv shows.
|
140
Universal/Network/sftpservice.py
Normal file
140
Universal/Network/sftpservice.py
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
import time
|
||||||
|
|
||||||
|
class SFTPService:
|
||||||
|
def __init__(self, logger):
|
||||||
|
self.logger = logger
|
||||||
|
self.report(2, "Initializing")
|
||||||
|
try:
|
||||||
|
import paramiko
|
||||||
|
self.enabled = True
|
||||||
|
self.paramiko = paramiko
|
||||||
|
self.report(2, "API Found")
|
||||||
|
except ImportError:
|
||||||
|
self.enabled = False
|
||||||
|
self.report(3, "Module 'paramiko' was not found")
|
||||||
|
self.report(3, "Disabling SFTP features")
|
||||||
|
self.connected = False
|
||||||
|
|
||||||
|
def isEnabled(self):
|
||||||
|
self.report(2, "isEnabled")
|
||||||
|
return self.enabled
|
||||||
|
|
||||||
|
def isConnected(self):
|
||||||
|
self.report(2, "isConnected")
|
||||||
|
return self.connected
|
||||||
|
|
||||||
|
def connectSFTPServer(self, host, port, username, password, key_file, key_type):
|
||||||
|
self.report(2, "connectSFTPServer")
|
||||||
|
self.host = host
|
||||||
|
self.port = int(port)
|
||||||
|
self.username = username
|
||||||
|
self.password = password
|
||||||
|
self.key_file = key_file
|
||||||
|
self.key_type = key_type
|
||||||
|
tmp_key = None
|
||||||
|
if key_file != None:
|
||||||
|
if key_file.lower().find("dsa") != -1:
|
||||||
|
tmp_key = self.paramiko.DSAKey.from_private_key_file(key_file)
|
||||||
|
elif key_file.lower().find("rsa") != -1:
|
||||||
|
tmp_key = self.paramiko.RSAKey.from_private_key_file(key_file)
|
||||||
|
try:
|
||||||
|
tmp = self.paramiko.Transport((host, int(port)))
|
||||||
|
tmp.connect(None, username, password, tmp_key)
|
||||||
|
except:
|
||||||
|
self.connected = False
|
||||||
|
try:
|
||||||
|
self.sftp_socket = self.paramiko.SFTPClient.from_transport(tmp)
|
||||||
|
self.connected = True
|
||||||
|
return True
|
||||||
|
except:
|
||||||
|
self.connected = False
|
||||||
|
return False
|
||||||
|
|
||||||
|
def attemptReconnect(self, retries=1):
|
||||||
|
self.report(2, "attemptReconnect")
|
||||||
|
self.connected = False
|
||||||
|
if retries == 1:
|
||||||
|
self.report(1, "Lost connect to SFTP Server...")
|
||||||
|
if retries <= 5:
|
||||||
|
self.report(1, "Attempt #" + str(retries) + "... Attemping to reconnect in 15 seconds...")
|
||||||
|
self.closeConnection()
|
||||||
|
time.sleep(15)
|
||||||
|
if self.connectSFTPServer(self.host, self.port, self.username, self.password, self.key_file, self.key_type):
|
||||||
|
self.logger(0, "Successfully reconnected to SFTP Server")
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return self.attemptReconnect(retries + 1)
|
||||||
|
else:
|
||||||
|
self.report(3, "Could not reconnect to SFTP Server...")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def closeConnection(self):
|
||||||
|
self.report(2, "closeConnection")
|
||||||
|
if self.sftp_socket is not None:
|
||||||
|
self.sftp_socket.close()
|
||||||
|
self.connected = False
|
||||||
|
|
||||||
|
def getFileSize(self, path, retried=0):
|
||||||
|
self.report(2, "getFileSize")
|
||||||
|
try:
|
||||||
|
return int(str(self.sftp_socket.lstat(path)).split()[4])
|
||||||
|
except IOError:
|
||||||
|
return None
|
||||||
|
except:
|
||||||
|
if retried == 0:
|
||||||
|
if self.attemptReconnect():
|
||||||
|
return self.getFileSize(path, 1)
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def doesFileExists(self, path):
|
||||||
|
self.report(2, "doesFileExists")
|
||||||
|
if self.getFileSize(path):
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def createFolder(self, path):
|
||||||
|
self.report(2, "createFolder")
|
||||||
|
tmp_full_path = path.split("/")
|
||||||
|
tmp_path = tmp_full_path[0]
|
||||||
|
for current_path in tmp_full_path:
|
||||||
|
if tmp_path != current_path:
|
||||||
|
tmp_path = tmp_path + "/" + current_path
|
||||||
|
try:
|
||||||
|
self.sftp_socket.mkdir(tmp_path)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def deleteFile(self, path):
|
||||||
|
self.report(2, "deleteFile")
|
||||||
|
try:
|
||||||
|
self.sftp_socket.remove(path)
|
||||||
|
return True
|
||||||
|
except:
|
||||||
|
self.report(3, "Failed to delete file")
|
||||||
|
|
||||||
|
def uploadFile(self, input_path, output_path, retried=0):
|
||||||
|
self.report(2, "uploadFile")
|
||||||
|
try:
|
||||||
|
self.createFolder(output_path.replace(output_path.split("/")[-1], ""))
|
||||||
|
if self.doesFileExists(output_path):
|
||||||
|
self.deleteFile(output_path)
|
||||||
|
return self.sftp_socket.put(input_path, output_path)
|
||||||
|
except:
|
||||||
|
if retried == 0:
|
||||||
|
if self.attemptReconnect():
|
||||||
|
return self.uploadFile(input_path, output_path, 1)
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def report(self, level, message):
|
||||||
|
match level:
|
||||||
|
case 0:
|
||||||
|
self.logger.infoReport("[SFTP] " + message)
|
||||||
|
case 1:
|
||||||
|
self.logger.warningReport("[SFTP] " + message)
|
||||||
|
case 2:
|
||||||
|
self.logger.debugReport("[SFTP] " + message)
|
||||||
|
case 3:
|
||||||
|
self.logger.errorReport("[SFTP] " + message)
|
182
Universal/ioservice.py
Normal file
182
Universal/ioservice.py
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
import os
|
||||||
|
import stat
|
||||||
|
import time
|
||||||
|
import shutil
|
||||||
|
import platform
|
||||||
|
import configparser
|
||||||
|
|
||||||
|
M_DEFAULTCONFIG = "./Settings.cfg"
|
||||||
|
M_CONFIGPARSER = configparser.ConfigParser()
|
||||||
|
|
||||||
|
def normalizePath(path):
|
||||||
|
return os.path.normcase(path)
|
||||||
|
|
||||||
|
def doesFileExist(path):
|
||||||
|
return os.path.isfile(path)
|
||||||
|
|
||||||
|
def getPathSeperator():
|
||||||
|
if platform.system() == 'Windows':
|
||||||
|
return "\\"
|
||||||
|
else:
|
||||||
|
return "/"
|
||||||
|
|
||||||
|
def doesFolderExist(path):
|
||||||
|
path = normalizePath(path)
|
||||||
|
if os.path.exists(path):
|
||||||
|
return not doesFileExist(path)
|
||||||
|
return False
|
||||||
|
|
||||||
|
def getFileSize(path):
|
||||||
|
path = normalizePath(path)
|
||||||
|
if doesFileExist(path):
|
||||||
|
return os.path.getsize(path)
|
||||||
|
else:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def checkSizeChange(path):
|
||||||
|
path = normalizePath(path)
|
||||||
|
if doesFileExist(path):
|
||||||
|
tmp_size = getFileSize(path)
|
||||||
|
time.sleep(10)
|
||||||
|
return (tmp_size == getFileSize(path))
|
||||||
|
return False
|
||||||
|
|
||||||
|
def createFolder(path):
|
||||||
|
path = normalizePath(path)
|
||||||
|
if doesFolderExist(path):
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
os.makedirs(path)
|
||||||
|
return True
|
||||||
|
except OSError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def deleteDirectory(path, attempt=True):
|
||||||
|
path = normalizePath(path)
|
||||||
|
if doesFileExist(path):
|
||||||
|
try:
|
||||||
|
os.remove(path)
|
||||||
|
except:
|
||||||
|
if attempt:
|
||||||
|
os.chmod(path, stat.S_IWRITE)
|
||||||
|
os.unlink(path)
|
||||||
|
deleteDirectory(path, False)
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
elif doesFolderExist(path):
|
||||||
|
try:
|
||||||
|
os.rmdir(path)
|
||||||
|
except:
|
||||||
|
if attempt:
|
||||||
|
os.chmod(path, stat.S_IWRITE)
|
||||||
|
os.unlink(path)
|
||||||
|
deleteDirectory(path, False)
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def moveFile(input_path, output_path, copy=False):
|
||||||
|
input_path = normalizePath(input_path)
|
||||||
|
output_path = normalizePath(output_path)
|
||||||
|
folder_created = False
|
||||||
|
if platform.system() == 'Windows':
|
||||||
|
folder_created = createFolder(output_path.rsplit("\\", 1)[0])
|
||||||
|
else:
|
||||||
|
folder_created = createFolder(output_path.rsplit("/", 1)[0])
|
||||||
|
if folder_created:
|
||||||
|
try:
|
||||||
|
if copy:
|
||||||
|
shutil.copyfile(input_path, output_path)
|
||||||
|
else:
|
||||||
|
shutil.move(input_path, output_path)
|
||||||
|
return True
|
||||||
|
except shutil.Error:
|
||||||
|
if copy:
|
||||||
|
print("Copying failed...")
|
||||||
|
else:
|
||||||
|
print("Moving failed...")
|
||||||
|
return False
|
||||||
|
except:
|
||||||
|
if doesFileExist(output_path):
|
||||||
|
if getFileSize(input_path) == getFileSize(output_path):
|
||||||
|
if not copy:
|
||||||
|
try:
|
||||||
|
deleteDirectory(input_path)
|
||||||
|
except:
|
||||||
|
print("Successfully copied file but could not delete input file...")
|
||||||
|
else:
|
||||||
|
if copy:
|
||||||
|
print("Failed to copy file")
|
||||||
|
else:
|
||||||
|
print("Failed to move file")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def listAllFiles(path):
|
||||||
|
path = normalizePath(path)
|
||||||
|
try:
|
||||||
|
tmp_paths = os.listdir(path)
|
||||||
|
tmp_files = []
|
||||||
|
for files in tmp_paths:
|
||||||
|
tmp_current = os.path.join(path, files)
|
||||||
|
if doesFolderExist(tmp_current):
|
||||||
|
tmp_files = tmp_files + listAllFiles(tmp_current)
|
||||||
|
elif doesFileExist(tmp_current):
|
||||||
|
tmp_files.append(tmp_current)
|
||||||
|
return tmp_files
|
||||||
|
except OSError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def deleteEmptyFolders(path, delroot=False):
|
||||||
|
path = normalizePath(path)
|
||||||
|
if not os.path.isdir(path):
|
||||||
|
return
|
||||||
|
tmp_files = os.listdir(path)
|
||||||
|
if tmp_files:
|
||||||
|
for files in tmp_files:
|
||||||
|
tmp_fullpath = os.path.join(path, files)
|
||||||
|
if os.path.isdir(tmp_fullpath):
|
||||||
|
deleteEmptyFolders(tmp_fullpath, True)
|
||||||
|
tmp_files = os.listdir(path)
|
||||||
|
if not tmp_files and delroot:
|
||||||
|
os.rmdir(path)
|
||||||
|
|
||||||
|
def addConfiguration(section, option, key=""):
|
||||||
|
if not doesFileExist(M_DEFAULTCONFIG):
|
||||||
|
M_CONFIGPARSER.add_section(section)
|
||||||
|
M_CONFIGPARSER.set(section, option, key)
|
||||||
|
else:
|
||||||
|
M_CONFIGPARSER.read(M_DEFAULTCONFIG)
|
||||||
|
if not M_CONFIGPARSER.has_section(section):
|
||||||
|
M_CONFIGPARSER.add_section(section)
|
||||||
|
if not M_CONFIGPARSER.has_option(section, option):
|
||||||
|
M_CONFIGPARSER.set(section, option, key)
|
||||||
|
with open(M_DEFAULTCONFIG, 'w') as tmp:
|
||||||
|
M_CONFIGPARSER.write(tmp)
|
||||||
|
|
||||||
|
def setConfiguration(section, option, key=""):
|
||||||
|
if not doesFileExist(M_DEFAULTCONFIG):
|
||||||
|
addConfiguration(section, option, key)
|
||||||
|
else:
|
||||||
|
M_CONFIGPARSER.read(M_CONFIGPARSER)
|
||||||
|
if not M_CONFIGPARSER.has_section(section):
|
||||||
|
M_CONFIGPARSER.add_section(section)
|
||||||
|
M_CONFIGPARSER.set(section, option, key)
|
||||||
|
with open(M_DEFAULTCONFIG, 'w') as tmp:
|
||||||
|
M_CONFIGPARSER.write(tmp)
|
||||||
|
|
||||||
|
def getConfigurationStr(section, option):
|
||||||
|
if doesFileExist(M_DEFAULTCONFIG):
|
||||||
|
M_CONFIGPARSER.read(M_DEFAULTCONFIG)
|
||||||
|
if M_CONFIGPARSER.has_section(section) and M_CONFIGPARSER.has_option(section, option):
|
||||||
|
tmp_key = M_CONFIGPARSER.get(section, option)
|
||||||
|
if tmp_key != "" or tmp_key.lower().find("none") != -1:
|
||||||
|
return tmp_key
|
||||||
|
return None
|
||||||
|
|
||||||
|
def getConfigurationBool(section, option):
|
||||||
|
if doesFileExist(M_DEFAULTCONFIG):
|
||||||
|
M_CONFIGPARSER.read(M_DEFAULTCONFIG)
|
||||||
|
if M_CONFIGPARSER.has_section(section) and M_CONFIGPARSER.has_option(section, option):
|
||||||
|
return M_CONFIGPARSER.getboolean(section, option)
|
||||||
|
return None
|
58
Universal/loggingservice.py
Normal file
58
Universal/loggingservice.py
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
|
||||||
|
import time
|
||||||
|
|
||||||
|
class Debugging:
|
||||||
|
def __init__(self, state):
|
||||||
|
self.state = state
|
||||||
|
|
||||||
|
def getDebugState(self):
|
||||||
|
return self.state
|
||||||
|
|
||||||
|
def setDebugState(self, state):
|
||||||
|
self.state = state
|
||||||
|
|
||||||
|
class Logging:
|
||||||
|
def __init__(self, state, directory):
|
||||||
|
self.state = state
|
||||||
|
self.directory = directory
|
||||||
|
|
||||||
|
def setState(self, state):
|
||||||
|
self.state = state
|
||||||
|
|
||||||
|
def setDirectory(self, directory):
|
||||||
|
self.directory = directory
|
||||||
|
|
||||||
|
def outputFile(self, message):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class Output:
|
||||||
|
def __init__(self, d_state, l_state, l_directory="./Output.log"):
|
||||||
|
self.debug = Debugging(d_state)
|
||||||
|
self.log = Logging(l_state, l_directory)
|
||||||
|
|
||||||
|
def infoReport(self, message, console=True):
|
||||||
|
if console:
|
||||||
|
print("[INFO] " + message)
|
||||||
|
if self.log.state:
|
||||||
|
self.log.outputFile("[INFO] " + message)
|
||||||
|
|
||||||
|
def warningReport(self, message, console=True):
|
||||||
|
if console:
|
||||||
|
print("[WARNING] " + message)
|
||||||
|
if self.log.state:
|
||||||
|
self.log.outputFile("[WARNING] " + message)
|
||||||
|
|
||||||
|
def debugReport(self, message, console=True):
|
||||||
|
if console and self.debug.getDebugState():
|
||||||
|
print("[DEBUG] " + message)
|
||||||
|
if self.log.state:
|
||||||
|
self.log.outputFile("[DEBUG] " + message)
|
||||||
|
|
||||||
|
def errorReport(self, message, console=True, quit=False):
|
||||||
|
if console:
|
||||||
|
print("[ERROR] " + message)
|
||||||
|
if self.log.state:
|
||||||
|
self.log.outputFile("[ERROR] " + message)
|
||||||
|
if quit:
|
||||||
|
time.sleep(10)
|
||||||
|
quit()
|
13
Universal/tools.py
Normal file
13
Universal/tools.py
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
def replace(input_str, search_key, replace_str, checkspaces=False):
|
||||||
|
if replace_str is None:
|
||||||
|
input_str = replace(input_str, search_key, "", checkspaces)
|
||||||
|
else:
|
||||||
|
while input_str.find(search_key) != -1:
|
||||||
|
if checkspaces:
|
||||||
|
if input_str[input_str.index(search_key) - 1] != " ":
|
||||||
|
input_str = input_str.replace(search_key, " " + replace_str)
|
||||||
|
else:
|
||||||
|
input_str = input_str.replace(search_key, replace_str)
|
||||||
|
else:
|
||||||
|
input_str = input_str.replace(search_key, replace_str)
|
||||||
|
return input_str
|
110
Video/Services/tmdbservice.py
Normal file
110
Video/Services/tmdbservice.py
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
from Universal import ioservice
|
||||||
|
|
||||||
|
class TMDBService:
|
||||||
|
def __init__(self, loggingservice):
|
||||||
|
self.logger = loggingservice
|
||||||
|
self.report(2, "Initializing")
|
||||||
|
try:
|
||||||
|
import tmdbsimple
|
||||||
|
self.tmdb = tmdbsimple
|
||||||
|
self.report(2, "API Found")
|
||||||
|
self.enabled = True
|
||||||
|
self.tmdb_key = None
|
||||||
|
self.addDefaultConfiguration()
|
||||||
|
except ImportError:
|
||||||
|
self.report(3, "Module 'tmdbsimple' was not found")
|
||||||
|
self.report(3, "Disabling TMDB API")
|
||||||
|
self.enabled = False
|
||||||
|
self.connected = False
|
||||||
|
|
||||||
|
def addDefaultConfiguration(self):
|
||||||
|
ioservice.addConfiguration("TMDB Service", "TMDB API Key", "NONE")
|
||||||
|
|
||||||
|
def updateConfiguration(self):
|
||||||
|
if self.tmdb_key == None:
|
||||||
|
self.tmdb_key = ioservice.getConfigurationStr("TMDB Service", "TMDB API Key")
|
||||||
|
|
||||||
|
def report(self, level, message):
|
||||||
|
match level:
|
||||||
|
case 0:
|
||||||
|
self.logger.infoReport("[TMDB] " + message)
|
||||||
|
case 1:
|
||||||
|
self.logger.warningReport("[TMDB] " + message)
|
||||||
|
case 2:
|
||||||
|
self.logger.debugReport("[TMDB] " + message)
|
||||||
|
case 3:
|
||||||
|
self.logger.errorReport("[TMDB] " + message)
|
||||||
|
|
||||||
|
def isEnabled(self):
|
||||||
|
return self.enabled
|
||||||
|
|
||||||
|
def isConnected(self):
|
||||||
|
self.report(2, "isConnected")
|
||||||
|
return self.connected
|
||||||
|
|
||||||
|
def connectToService(self):
|
||||||
|
self.report(2, "connectToService")
|
||||||
|
if not self.connected:
|
||||||
|
try:
|
||||||
|
self.report(2, "Attempting to Login")
|
||||||
|
self.updateConfiguration()
|
||||||
|
self.tmdb.API_KEY = self.tmdb_key
|
||||||
|
self.connected = True
|
||||||
|
self.report(2, "Connected")
|
||||||
|
except self.tmdb.APIKeyError:
|
||||||
|
self.report(3, "API Key is incorrect")
|
||||||
|
self.report(3, "Disabling TMDB API")
|
||||||
|
self.enabled = False
|
||||||
|
else:
|
||||||
|
self.report(2, "Already connected")
|
||||||
|
|
||||||
|
def searchMovie(self, name):
|
||||||
|
self.report(2, "searchMovie")
|
||||||
|
tmp_search = self.tmdb.Search()
|
||||||
|
tmp_response = tmp_search.movie(query=name)
|
||||||
|
tmp_list = []
|
||||||
|
for movie in tmp_search.results:
|
||||||
|
tmp_runtime = self.tmdb.Movies(movie['id']).info()['runtime']
|
||||||
|
try:
|
||||||
|
tmp_releasedate = movie['release_date']
|
||||||
|
except:
|
||||||
|
tmp_releasedate = 9999
|
||||||
|
if not tmp_runtime or tmp_runtime is None:
|
||||||
|
tmp_runtime = 0
|
||||||
|
if not tmp_releasedate or tmp_releasedate is None:
|
||||||
|
tmp_releasedate = 9999
|
||||||
|
tmp_list.append([movie['title'], int(tmp_runtime), int(tmp_releasedate[:4])])
|
||||||
|
return tmp_list
|
||||||
|
|
||||||
|
def getBestMovie(self, name, length=None, year=None):
|
||||||
|
self.report(2, "getBestMovie")
|
||||||
|
tmp_movies = self.searchMovie(name)
|
||||||
|
if not year and not length:
|
||||||
|
self.report(0, "Does not know year or runtime... Selecting first response.")
|
||||||
|
return tmp_movies[0]
|
||||||
|
elif not year:
|
||||||
|
self.report(0, "Got runtime but no year. Selecting closest match based on runtime.")
|
||||||
|
tmp_time = []
|
||||||
|
for movies in tmp_movies:
|
||||||
|
if movies[1] == None:
|
||||||
|
movies[1] = 0
|
||||||
|
tmp_time.append(movies[1])
|
||||||
|
return tmp_movies[min(enumerate(tmp_time), key=lambda x:abs(x[1]-length))[0]]
|
||||||
|
else:
|
||||||
|
tmp_year = []
|
||||||
|
for movies in tmp_movies:
|
||||||
|
if movies[2] == None:
|
||||||
|
movies[2] = 9999
|
||||||
|
tmp_year.append(movies[2])
|
||||||
|
return tmp_movies[min(enumerate(tmp_year), key=lambda x:abs(x[1]-year))[0]]
|
||||||
|
|
||||||
|
def getShowName(self, name):
|
||||||
|
self.report(2, "getShowName")
|
||||||
|
tmp_search = self.tmdb.Search()
|
||||||
|
tmp_response = tmp_search.tv(query=name)
|
||||||
|
return tmp_search.results[0]
|
||||||
|
|
||||||
|
def getEpisodeName(self, name, season, episode):
|
||||||
|
self.report(2, "getEpisodeName")
|
||||||
|
self.tmdb.Search().tv(query=name)
|
||||||
|
return self.tmdb.TV_Episodes(self.tmdb.results[0]['id'], season, episode).info()['name']
|
112
Video/Services/tvdbservice.py
Normal file
112
Video/Services/tvdbservice.py
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
from Universal import ioservice
|
||||||
|
|
||||||
|
class TMDBService:
|
||||||
|
def __init__(self, loggingservice):
|
||||||
|
self.logger = loggingservice
|
||||||
|
self.report(2, "Initializing")
|
||||||
|
try:
|
||||||
|
import tvdb_api
|
||||||
|
from tvdb_api import tvdb_seasonnotfound, tvdb_episodenotfound, tvdb_error
|
||||||
|
from tvdb_api import tvdb_notauthorized, tvdb_shownotfound
|
||||||
|
self.tvdb = tvdb_api
|
||||||
|
self.report(2, "API Found")
|
||||||
|
self.enabled = True
|
||||||
|
self.tvdb_key = None
|
||||||
|
self.addDefaultConfiguration()
|
||||||
|
except ImportError:
|
||||||
|
self.report(3, "Module 'tvdb_api' was not found")
|
||||||
|
self.report(3, "Disabling TVDB API")
|
||||||
|
self.enabled = False
|
||||||
|
self.connected = False
|
||||||
|
|
||||||
|
def addDefaultConfiguration(self):
|
||||||
|
ioservice.addConfiguration("TVDB Service", "TVDB API Key", "NONE")
|
||||||
|
|
||||||
|
def updateConfiguration(self):
|
||||||
|
if self.tvdb_key == None:
|
||||||
|
self.tvdb_key = ioservice.getConfigurationStr("TVDB Service", "TVDB API Key")
|
||||||
|
|
||||||
|
def report(self, level, message):
|
||||||
|
match level:
|
||||||
|
case 0:
|
||||||
|
self.logger.infoReport("[TVDB] " + message)
|
||||||
|
case 1:
|
||||||
|
self.logger.warningReport("[TVDB] " + message)
|
||||||
|
case 2:
|
||||||
|
self.logger.debugReport("[TVDB] " + message)
|
||||||
|
case 3:
|
||||||
|
self.logger.errorReport("[TVDB] " + message)
|
||||||
|
|
||||||
|
def isEnabled(self):
|
||||||
|
return self.enabled
|
||||||
|
|
||||||
|
def isConnected(self):
|
||||||
|
self.report(2, "isConnected")
|
||||||
|
return self.connected
|
||||||
|
|
||||||
|
def connectToService(self):
|
||||||
|
self.report(2, "connectToService")
|
||||||
|
if not self.connected:
|
||||||
|
try:
|
||||||
|
self.report(2, "Attempting to Login")
|
||||||
|
self.updateConfiguration()
|
||||||
|
self.tvdb = tvdb_api.Tvdb(apikey=self.tvdb_key)
|
||||||
|
self.connected = True
|
||||||
|
self.report(2, "Connected")
|
||||||
|
except self.tmdb.APIKeyError:
|
||||||
|
self.report(3, "API Key is incorrect")
|
||||||
|
self.report(3, "Disabling TMDB API")
|
||||||
|
self.enabled = False
|
||||||
|
else:
|
||||||
|
self.report(2, "Already connected")
|
||||||
|
|
||||||
|
def searchMovie(self, name):
|
||||||
|
self.report(2, "searchMovie")
|
||||||
|
tmp_search = self.tmdb.Search()
|
||||||
|
tmp_response = tmp_search.movie(query=name)
|
||||||
|
tmp_list = []
|
||||||
|
for movie in tmp_search.results:
|
||||||
|
tmp_runtime = self.tmdb.Movies(movie['id']).info()['runtime']
|
||||||
|
try:
|
||||||
|
tmp_releasedate = movie['release_date']
|
||||||
|
except:
|
||||||
|
tmp_releasedate = 9999
|
||||||
|
if not tmp_runtime or tmp_runtime is None:
|
||||||
|
tmp_runtime = 0
|
||||||
|
if not tmp_releasedate or tmp_releasedate is None:
|
||||||
|
tmp_releasedate = 9999
|
||||||
|
tmp_list.append([movie['title'], int(tmp_runtime), int(tmp_releasedate[:4])])
|
||||||
|
return tmp_list
|
||||||
|
|
||||||
|
def getBestMovie(self, name, length=None, year=None):
|
||||||
|
self.report(2, "getBestMovie")
|
||||||
|
tmp_movies = self.searchMovie(name)
|
||||||
|
if not year and not length:
|
||||||
|
self.report(0, "Does not know year or runtime... Selecting first response.")
|
||||||
|
return tmp_movies[0]
|
||||||
|
elif not year:
|
||||||
|
self.report(0, "Got runtime but no year. Selecting closest match based on runtime.")
|
||||||
|
tmp_time = []
|
||||||
|
for movies in tmp_movies:
|
||||||
|
if movies[1] == None:
|
||||||
|
movies[1] = 0
|
||||||
|
tmp_time.append(movies[1])
|
||||||
|
return tmp_movies[min(enumerate(tmp_time), key=lambda x:abs(x[1]-length))[0]]
|
||||||
|
else:
|
||||||
|
tmp_year = []
|
||||||
|
for movies in tmp_movies:
|
||||||
|
if movies[2] == None:
|
||||||
|
movies[2] = 9999
|
||||||
|
tmp_year.append(movies[2])
|
||||||
|
return tmp_movies[min(enumerate(tmp_year), key=lambda x:abs(x[1]-year))[0]]
|
||||||
|
|
||||||
|
def getShowName(self, name):
|
||||||
|
self.report(2, "getShowName")
|
||||||
|
tmp_search = self.tmdb.Search()
|
||||||
|
tmp_response = tmp_search.tv(query=name)
|
||||||
|
return tmp_search.results[0]
|
||||||
|
|
||||||
|
def getEpisodeName(self, name, season, episode):
|
||||||
|
self.report(2, "getEpisodeName")
|
||||||
|
self.tmdb.Search().tv(query=name)
|
||||||
|
return self.tmdb.TV_Episodes(self.tmdb.results[0]['id'], season, episode).info()['name']
|
87
Video/default.py
Normal file
87
Video/default.py
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
from Universal import ioservice
|
||||||
|
|
||||||
|
def generateConfigs():
|
||||||
|
generateProcessorConfigs()
|
||||||
|
generateServerConfig()
|
||||||
|
generateVideoConfig()
|
||||||
|
|
||||||
|
def generateProcessorConfigs():
|
||||||
|
ioservice.addConfiguration("Video Configuration", "FFProbe Path")
|
||||||
|
ioservice.addConfiguration("Video Configuration", "Encoder Path")
|
||||||
|
ioservice.addConfiguration("Video Configuration", "Sleep Timer (Seconds)", "100")
|
||||||
|
ioservice.addConfiguration("Video Configuration", "Blacklist Words", "1080p, 720p, 480p, 360p, x265, x264")
|
||||||
|
|
||||||
|
def generateServerConfig():
|
||||||
|
ioservice.addConfiguration("Server 1", "Type", "NONE")
|
||||||
|
ioservice.addConfiguration("Server 1", "Host", "127.0.0.1")
|
||||||
|
ioservice.addConfiguration("Server 1", "Port", "22")
|
||||||
|
ioservice.addConfiguration("Server 1", "Username", "NONE")
|
||||||
|
ioservice.addConfiguration("Server 1", "Password", "NONE")
|
||||||
|
ioservice.addConfiguration("Server 1", "Key File", "NONE")
|
||||||
|
ioservice.addConfiguration("Server 1", "Key Type (RSA/DSA)", "RSA")
|
||||||
|
ioservice.addConfiguration("Server 1", "Media Root", "/Server/Drive/Media/")
|
||||||
|
|
||||||
|
def generateVideoConfig():
|
||||||
|
ioservice.addConfiguration("Video Library 1", "Name", "TV Shows")
|
||||||
|
ioservice.addConfiguration("Video Library 1", "Input", "./Input/TV-Shows/")
|
||||||
|
ioservice.addConfiguration("Video Library 1", "Output", "./Output/TV-Shows/")
|
||||||
|
ioservice.addConfiguration("Video Library 1", "Database Service", "TMDB")
|
||||||
|
ioservice.addConfiguration("Video Library 1", "Media Type", "SHOWS")
|
||||||
|
ioservice.addConfiguration("Video Library 1", "Compress Video", "False")
|
||||||
|
ioservice.addConfiguration("Video Library 1", "Output Format", "mkv")
|
||||||
|
ioservice.addConfiguration("Video Library 1", "Directory Format", "/${NAME}/Season ${SEASON}/${NAME} S${SEASON}E${EPISODE}.${FORMAT}")
|
||||||
|
ioservice.addConfiguration("Video Library 1", "Command", "${COMPRESSOR} -i ${INPUT} -o ${OUTPUT}")
|
||||||
|
ioservice.addConfiguration("Video Library 1", "Subtitle Command", "--srt-file ${SRT_FILE} --srt-lang ${SRT_LANGUAGE}")
|
||||||
|
ioservice.addConfiguration("Video Library 1", "Delete Input", "False")
|
||||||
|
ioservice.addConfiguration("Video Library 1", "Server", "None")
|
||||||
|
ioservice.addConfiguration("Video Library 1", "Server Path", "/TV Shows/${NAME}/Season ${SEASON}/${NAME} S${SEASON}E${EPISODE}.${FORMAT}")
|
||||||
|
ioservice.addConfiguration("Video Library 1", "Server Overwrite", "False")
|
||||||
|
|
||||||
|
class VideoSettings:
|
||||||
|
def __init__(self):
|
||||||
|
generateConfigs()
|
||||||
|
self.settings = {}
|
||||||
|
self.library = {}
|
||||||
|
self.server = {}
|
||||||
|
|
||||||
|
def updateSettings(self):
|
||||||
|
tmp_ffprobe = ioservice.getConfigurationStr("Video Configuration", "FFProbe Path")
|
||||||
|
tmp_encoder = ioservice.getConfigurationStr("Video Configuration", "Encoder Path")
|
||||||
|
tmp_timer = ioservice.getConfigurationStr("Video Configuration", "Sleep Timer (Seconds)")
|
||||||
|
tmp_blacklist = ioservice.getConfigurationStr("Video Configuration", "Blacklist Words")
|
||||||
|
self.settings = {"FFProbe": tmp_ffprobe, "Encoder": tmp_encoder, "Timer": tmp_timer,
|
||||||
|
"Blacklist": tmp_blacklist}
|
||||||
|
|
||||||
|
def updateLibrary(self, num):
|
||||||
|
tmp_section = ("Video Library " + str(num))
|
||||||
|
tmp_name = ioservice.getConfigurationStr(tmp_section, "Name")
|
||||||
|
tmp_input = ioservice.getConfigurationStr(tmp_section, "Input")
|
||||||
|
tmp_output = ioservice.getConfigurationStr(tmp_section, "Output")
|
||||||
|
tmp_database = ioservice.getConfigurationStr(tmp_section, "Database Service")
|
||||||
|
tmp_type = ioservice.getConfigurationStr(tmp_section, "Media Type")
|
||||||
|
tmp_compress = ioservice.getConfigurationBool(tmp_section, "Compress Video")
|
||||||
|
tmp_out_format = ioservice.getConfigurationStr(tmp_section, "Output Format")
|
||||||
|
tmp_dir_format = ioservice.getConfigurationStr(tmp_section, "Directory Format")
|
||||||
|
tmp_command = ioservice.getConfigurationStr(tmp_section, "Command")
|
||||||
|
tmp_srt_command = ioservice.getConfigurationStr(tmp_section, "Subtitle Command")
|
||||||
|
tmp_del_input = ioservice.getConfigurationBool(tmp_section, "Delete Input")
|
||||||
|
tmp_server = ioservice.getConfigurationStr(tmp_section, "Server")
|
||||||
|
tmp_server_path = ioservice.getConfigurationStr(tmp_section, "Server Path")
|
||||||
|
tmp_server_overwrite = ioservice.getConfigurationBool(tmp_section, "Server Overwrite")
|
||||||
|
self.library = {"Name": tmp_name, "Input": tmp_input, "Output": tmp_output, "Database": tmp_database,
|
||||||
|
"Type": tmp_type, "Compress": tmp_compress, "Format": tmp_out_format, "Directory": tmp_dir_format,
|
||||||
|
"Command": tmp_command, "SRT Command": tmp_srt_command, "Delete Input": tmp_del_input,
|
||||||
|
"Server": tmp_server, "Server Path": tmp_server_path, "Server Overwrite": tmp_server_overwrite}
|
||||||
|
|
||||||
|
def updateServer(self, num):
|
||||||
|
tmp_section = ("Server " + str(num))
|
||||||
|
tmp_type = ioservice.getConfigurationStr(tmp_section, "Type")
|
||||||
|
tmp_host = ioservice.getConfigurationStr(tmp_section, "Host")
|
||||||
|
tmp_port = ioservice.getConfigurationStr(tmp_section, "Port")
|
||||||
|
tmp_username = ioservice.getConfigurationStr(tmp_section, "Username")
|
||||||
|
tmp_password = ioservice.getConfigurationStr(tmp_section, "Password")
|
||||||
|
tmp_keyfile = ioservice.getConfigurationStr(tmp_section, "Key File")
|
||||||
|
tmp_keytype = ioservice.getConfigurationStr(tmp_section, "Key Type (RSA/DSA)")
|
||||||
|
tmp_root = ioservice.getConfigurationStr(tmp_section, "Media Root")
|
||||||
|
self.server = {"Type": tmp_type, "Host": tmp_host, "Port": tmp_port, "Username": tmp_username,
|
||||||
|
"Password": tmp_password, "Key File": tmp_keyfile, "Key Type": tmp_keytype, "Root": tmp_root}
|
365
Video/videoservice.py
Normal file
365
Video/videoservice.py
Normal file
@ -0,0 +1,365 @@
|
|||||||
|
import os
|
||||||
|
import re
|
||||||
|
import time
|
||||||
|
import subprocess
|
||||||
|
from Video import default
|
||||||
|
from Universal import tools
|
||||||
|
from Universal import ioservice
|
||||||
|
from Universal import loggingservice
|
||||||
|
from Video.Services import tmdbservice
|
||||||
|
from Universal.network import sftpservice
|
||||||
|
|
||||||
|
class VideoService:
|
||||||
|
def __init__(self):
|
||||||
|
self.logger = loggingservice.Output(True, True)
|
||||||
|
self.video_settings = default.VideoSettings()
|
||||||
|
# Movie/TV Show Database
|
||||||
|
self.tmdbservice = tmdbservice.TMDBService(self.logger)
|
||||||
|
# Network Service
|
||||||
|
self.sftpservice = sftpservice.SFTPService(self.logger)
|
||||||
|
self.failedFiles = []
|
||||||
|
self.video_settings.updateSettings()
|
||||||
|
|
||||||
|
def startNewProcess(self, num=1):
|
||||||
|
self.logger.debugReport("[Video] startNewProcess")
|
||||||
|
self.current_library = num
|
||||||
|
self.total_processed = 0
|
||||||
|
self.is_processing = True
|
||||||
|
self.video_settings.updateLibrary(self.current_library)
|
||||||
|
if self.video_settings.library["Server"].lower().find("none") == -1:
|
||||||
|
self.video_settings.updateServer(self.video_settings.library["Server"])
|
||||||
|
while self.is_processing:
|
||||||
|
self.processLibraries()
|
||||||
|
self.startNewProcess(self.current_library)
|
||||||
|
|
||||||
|
def processLibraries(self, refreshLib=True):
|
||||||
|
self.logger.debugReport("[Video] processLibraries")
|
||||||
|
tmp_library_time = time.time()
|
||||||
|
if self.video_settings.library is not None:
|
||||||
|
if self.video_settings.library["Input"] is not None:
|
||||||
|
if refreshLib:
|
||||||
|
self.total_processed = 0
|
||||||
|
time.sleep(2)
|
||||||
|
self.logger.infoReport("[Video] Current Library: " + self.video_settings.library["Name"])
|
||||||
|
self.current_library_list = ioservice.listAllFiles(self.video_settings.library["Input"])
|
||||||
|
if self.current_library_list is not None:
|
||||||
|
self.current_library_list = sorted(self.current_library_list)
|
||||||
|
if self.current_library_list is not None:
|
||||||
|
if refreshLib:
|
||||||
|
self.srt_list = []
|
||||||
|
for item_search in self.current_library_list:
|
||||||
|
if item_search.lower().find(".srt") != -1:
|
||||||
|
self.srt_list.append(item_search)
|
||||||
|
if self.srt_list is not None:
|
||||||
|
for item_search in self.srt_list:
|
||||||
|
self.current_library_list.remove(item_search)
|
||||||
|
if len(self.current_library_list) == 0:
|
||||||
|
self.is_processing = False
|
||||||
|
else:
|
||||||
|
self.logger.infoReport("[Video] Found a total of " + str(len(self.current_library_list)) + " files")
|
||||||
|
for self.current_file in self.current_library_list:
|
||||||
|
if self.total_processed != 0:
|
||||||
|
self.logger.infoReport("[Video] Total file remaining: " + str(len(self.current_library_list) - self.total_processed))
|
||||||
|
self.processVideo()
|
||||||
|
else:
|
||||||
|
self.logger.infoReport("[Video] Folder is empty")
|
||||||
|
ioservice.deleteEmptyFolders(self.video_settings.library["Input"])
|
||||||
|
else:
|
||||||
|
self.is_processing = False
|
||||||
|
else:
|
||||||
|
self.is_processing = False
|
||||||
|
self.logger.infoReport("[Video] Current Library: " + self.video_settings.library["Name"] + " Time Elapsed: " + time.strftime("%H:%M:%S", time.gmtime(time.time() - tmp_library_time)))
|
||||||
|
self.current_library += 1
|
||||||
|
|
||||||
|
def processVideo(self):
|
||||||
|
self.logger.debugReport("[Video] processVideo")
|
||||||
|
self.logger.infoReport("[Video] Current File: " + self.current_file)
|
||||||
|
tmp_isDeleted = False
|
||||||
|
tmp_isSuccess = True
|
||||||
|
tmp_serverPath = None
|
||||||
|
if not ioservice.checkSizeChange(self.current_file):
|
||||||
|
if ioservice.getFileSize(self.current_file) == 0:
|
||||||
|
self.logger.infoReport("[Video] Assuming file has been deleted or moved... Skipping...")
|
||||||
|
tmp_isDeleted = True
|
||||||
|
else:
|
||||||
|
self.logger.infoReport("[Video] File size has changed")
|
||||||
|
self.processVideo()
|
||||||
|
else:
|
||||||
|
if not tmp_isDeleted:
|
||||||
|
tmp_currentFileDetails = self.extractVideoInformation(self.current_file)
|
||||||
|
self.constructOutputPath(tmp_currentFileDetails)
|
||||||
|
if tmp_currentFileDetails["Episode"] is None and tmp_currentFileDetails["Season"] is None:
|
||||||
|
self.logger.infoReport("[Video] " + tmp_currentFileDetails["Name"] + " " + str(tmp_currentFileDetails["Year"]))
|
||||||
|
else:
|
||||||
|
self.logger.infoReport("[Video] " + tmp_currentFileDetails["Name"] + " Season: " + str(tmp_currentFileDetails["Season"]) + " Episode: " + str(tmp_currentFileDetails["Episode"]))
|
||||||
|
self.findSRTs()
|
||||||
|
tmp_compress = False
|
||||||
|
if not self.video_settings.library["Server"].lower().find("none") != -1:
|
||||||
|
self.video_settings.updateServer(self.video_settings.library["Server"])
|
||||||
|
tmp_serverPath = self.constructServerPath()
|
||||||
|
if self.video_settings.server["Type"].lower().find("sftp") != -1:
|
||||||
|
if self.sftpservice.isEnabled():
|
||||||
|
if not self.sftpservice.isConnected():
|
||||||
|
self.sftpservice.connectSFTPServer(self.video_settings.server["Host"], self.video_settings.server["Port"], self.video_settings.server["Username"],
|
||||||
|
self.video_settings.server["Password"], self.video_settings.server["Key File"], self.video_settings.server["Key Type"])
|
||||||
|
if self.sftpservice.isConnected():
|
||||||
|
if not ioservice.doesFileExist(self.currentFileOutputPath):
|
||||||
|
if self.video_settings.library["Server Overwrite"]:
|
||||||
|
tmp_compress = True
|
||||||
|
else:
|
||||||
|
if not self.sftpservice.doesFileExists(tmp_serverPath):
|
||||||
|
tmp_compress = True
|
||||||
|
else:
|
||||||
|
if not ioservice.doesFileExist(self.currentFileOutputPath):
|
||||||
|
tmp_compress = True
|
||||||
|
if tmp_compress and not self.video_settings.library["Compress"]:
|
||||||
|
if self.video_settings.library["Delete Input"]:
|
||||||
|
self.logger.infoReport("[Video] Moving file")
|
||||||
|
self.is_processing = True
|
||||||
|
ioservice.moveFile(self.current_file, self.currentFileOutputPath)
|
||||||
|
i = 0
|
||||||
|
while i < len(self.srt_to_include):
|
||||||
|
ioservice.moveFile(self.srt_to_include[i], self.constructOutputPath(tmp_currentFileDetails, True, i))
|
||||||
|
i += 1
|
||||||
|
self.is_processing = False
|
||||||
|
tmp_isSuccess = True
|
||||||
|
self.logger.infoReport("[Video] Move complete")
|
||||||
|
else:
|
||||||
|
self.logger.infoReport("[Video] Copying file")
|
||||||
|
self.is_processing = True
|
||||||
|
ioservice.moveFile(self.current_file, self.currentFileOutputPath, True)
|
||||||
|
i = 0
|
||||||
|
while i < len(self.srt_to_include):
|
||||||
|
ioservice.moveFile(self.srt_to_include[i], self.constructOutputPath(tmp_currentFileDetails, True, i), True)
|
||||||
|
i += 1
|
||||||
|
self.is_processing = False
|
||||||
|
tmp_isSuccess = True
|
||||||
|
self.logger.infoReport("[Video] Copying complete")
|
||||||
|
elif tmp_compress and self.video_settings.library["Compress"]:
|
||||||
|
self.logger.infoReport("[Video] Compressing video")
|
||||||
|
self.startCompressing(self.current_file, self.currentFileOutputPath, self.srt_to_include)
|
||||||
|
tmp_isSuccess = True
|
||||||
|
self.logger.infoReport("[Video] Compressing completed")
|
||||||
|
if not self.video_settings.library["Server"].lower().find("none") != -1:
|
||||||
|
self.video_settings.updateServer(self.video_settings.library["Server"])
|
||||||
|
if self.video_settings.server["Type"].lower().find("sftp") != -1:
|
||||||
|
if self.sftpservice.isEnabled():
|
||||||
|
if not self.sftpservice.isConnected():
|
||||||
|
self.sftpservice.connectSFTPServer(self.video_settings.server["Host"], self.video_settings.server["Port"], self.video_settings.server["Username"],
|
||||||
|
self.video_settings.server["Password"], self.video_settings.server["Key File"], self.video_settings.server["Key Type"])
|
||||||
|
if self.sftpservice.isConnected():
|
||||||
|
if self.sftpservice.doesFileExists(tmp_serverPath):
|
||||||
|
if self.video_settings.library["Server Overwrite"]:
|
||||||
|
if self.sftpservice.getFileSize(tmp_serverPath) != ioservice.getFileSize(self.currentFileOutputPath):
|
||||||
|
if self.sftpservice.doesFileExist(tmp_serverPath):
|
||||||
|
self.sftpservice.deleteFile(tmp_serverPath)
|
||||||
|
if self.sftpservice.uploadFile(self.currentFileOutputPath, tmp_serverPath):
|
||||||
|
tmp_isSuccess = True
|
||||||
|
self.logger.infoReport("[Video] Upload Complete")
|
||||||
|
else:
|
||||||
|
tmp_isSuccess = False
|
||||||
|
else:
|
||||||
|
if self.sftpservice.uploadFile(self.currentFileOutputPath, tmp_serverPath):
|
||||||
|
tmp_isSuccess = True
|
||||||
|
self.logger.infoReport("[Video] Upload Complete")
|
||||||
|
else:
|
||||||
|
tmp_isSuccess = False
|
||||||
|
else:
|
||||||
|
if self.sftpservice.uploadFile(self.currentFileOutputPath, tmp_serverPath):
|
||||||
|
tmp_isSuccess = True
|
||||||
|
else:
|
||||||
|
tmp_isSuccess = False
|
||||||
|
if not tmp_isSuccess:
|
||||||
|
self.failedFiles = self.failedFiles + [self.current_file]
|
||||||
|
else:
|
||||||
|
self.total_processed += 1
|
||||||
|
|
||||||
|
def extractVideoInformation(self, file):
|
||||||
|
self.logger.debugReport("[Video] extractVideoInformation")
|
||||||
|
file = file.replace(self.video_settings.library["Input"], "").lstrip().replace("_", "")
|
||||||
|
for blacklist_item in self.video_settings.settings["Blacklist"].split(", "):
|
||||||
|
file = file.replace(blacklist_item, "")
|
||||||
|
tmp_input_format = file.rsplit(".", 1)[-1]
|
||||||
|
tmp_name = None
|
||||||
|
tmp_season = None
|
||||||
|
tmp_episode = None
|
||||||
|
tmp_episode_title = None
|
||||||
|
tmp_year = None
|
||||||
|
tmp_episode_year = None
|
||||||
|
tmp_length = self.getVideoLength()
|
||||||
|
tmp_folders, tmp_file = os.path.split(file)
|
||||||
|
for possible_years in re.findall(r'\d+', file):
|
||||||
|
if len(possible_years) == 4:
|
||||||
|
tmp_year = int(possible_years)
|
||||||
|
match len(tmp_folders.split(ioservice.getPathSeperator())):
|
||||||
|
case 1:
|
||||||
|
tmp_name = tmp_folders.split(ioservice.getPathSeperator())[0]
|
||||||
|
if not tmp_name:
|
||||||
|
tmp_name = tmp_file.replace(str(tmp_year), "").replace("." + tmp_input_format, "").strip()
|
||||||
|
case 2:
|
||||||
|
tmp_name = tmp_folders.split(ioservice.getPathSeperator())[0]
|
||||||
|
case 3:
|
||||||
|
tmp_name = tmp_folders.split(ioservice.getPathSeperator())[0]
|
||||||
|
case _:
|
||||||
|
tmp_name = tmp_file.replace("." + tmp_input_format, "")
|
||||||
|
if self.video_settings.library["Type"].lower().find("show") != -1:
|
||||||
|
if tmp_season is None or tmp_episode is None:
|
||||||
|
match len(tmp_folders.split(ioservice.getPathSeperator())):
|
||||||
|
case 2:
|
||||||
|
tmp_season = re.findall(r'\d+', tmp_folders.replace(tmp_name, ""))[0]
|
||||||
|
tmp_episode = re.findall(r'([0-9]*[0-9])', tmp_file.replace("." + tmp_input_format, "").replace(tmp_name, ""))[-1]
|
||||||
|
if tmp_file.lower().find(tmp_episode + "a") != -1:
|
||||||
|
tmp_episode = tmp_episode + " - Part 1"
|
||||||
|
elif tmp_file.lower().find(tmp_episode + "b") != -1:
|
||||||
|
tmp_episode = tmp_episode + " - Part 2"
|
||||||
|
case 3:
|
||||||
|
tmp_season = re.findall(r'\d+', tmp_folders.replace(tmp_name, ""))[0]
|
||||||
|
tmp_episode = re.findall(r'([0-9]*[0-9])', tmp_folders.replace("." + tmp_input_format, "").replace(tmp_name, ""))[-1]
|
||||||
|
if tmp_file.lower().find(tmp_episode + "a") != -1:
|
||||||
|
tmp_episode = tmp_episode + " - Part 1"
|
||||||
|
elif tmp_file.lower().find(tmp_episode + "b") != -1:
|
||||||
|
tmp_episode = tmp_episode + " - Part 2"
|
||||||
|
case _:
|
||||||
|
tmp_episode = re.findall(r'\d+', tmp_file.replace("." + tmp_input_format, "").replace(tmp_name, ""))[-1]
|
||||||
|
try:
|
||||||
|
tmp_season = re.findall(r'\d+', tmp_file.replace("." + tmp_input_format, "").replace(tmp_name, ""))[0]
|
||||||
|
except:
|
||||||
|
tmp_episode = re.findall(r'\d+', tmp_file.replace("." + tmp_input_format, ""))[-1]
|
||||||
|
if tmp_season != "0" and tmp_season:
|
||||||
|
tmp_season = tmp_season.lstrip("0")
|
||||||
|
if tmp_episode != "0" and tmp_episode:
|
||||||
|
tmp_episode = tmp_episode.lstrip("0")
|
||||||
|
match self.video_settings.library["Database"].lower():
|
||||||
|
|
||||||
|
case "tmdb":
|
||||||
|
if self.tmdbservice.isEnabled():
|
||||||
|
self.tmdbservice.connectToTMDB()
|
||||||
|
if self.video_settings.library["Type"].lower().find("movie") != -1:
|
||||||
|
tmp_name = tmp_name.replace(str(tmp_year), "").replace("(", "").replace(")", "")
|
||||||
|
if tmp_length is not None or tmp_year is not None:
|
||||||
|
tmp_data = self.tmdbservice.getBestMovie(tmp_name, tmp_length, tmp_year)
|
||||||
|
if tmp_data:
|
||||||
|
tmp_name = tmp_data[0]
|
||||||
|
tmp_year = tmp_data[2]
|
||||||
|
else:
|
||||||
|
tmp_data = self.tmdbservice.searchMovies(tmp_name)
|
||||||
|
tmp_name = tmp_data[0][0]
|
||||||
|
tmp_year = tmp_data[0][1]
|
||||||
|
elif self.video_settings.library["Type"].lower().find("show") != -1:
|
||||||
|
tmp_data = self.tmdbservice.getShowName(tmp_name)
|
||||||
|
tmp_name = tmp_data["name"]
|
||||||
|
tmp_year = tmp_data["first_air_date"][:4]
|
||||||
|
case "tvdb":
|
||||||
|
#TODO: Implement
|
||||||
|
pass
|
||||||
|
case _:
|
||||||
|
#TODO: Implement
|
||||||
|
pass
|
||||||
|
tmp_name = tmp_name.replace(":", "")
|
||||||
|
if tmp_episode_title:
|
||||||
|
tmp_episode_title = tmp_episode_title.replace(":", "")
|
||||||
|
return {"Name": tmp_name, "Year": tmp_year, "Season": tmp_season, "Episode": tmp_episode, "Episode Title": tmp_episode_title, "Episode Year": tmp_episode_year, "Format": tmp_input_format}
|
||||||
|
|
||||||
|
def constructOutputPath(self, file_info, subtitles=False, num=0):
|
||||||
|
self.logger.debugReport("[Video] constructOutputPath")
|
||||||
|
self.currentFileOutputPath = self.video_settings.library["Output"] + self.video_settings.library["Directory"]
|
||||||
|
self.currentFileOutputPath = tools.replace(self.currentFileOutputPath, "${NAME}", file_info["Name"])
|
||||||
|
self.currentFileOutputPath = tools.replace(self.currentFileOutputPath, "${YEAR}", file_info["Year"])
|
||||||
|
self.currentFileOutputPath = tools.replace(self.currentFileOutputPath, "${SEASON}", file_info["Season"])
|
||||||
|
if isinstance(file_info["Episode"], list):
|
||||||
|
tmp_episodes = None
|
||||||
|
for eps in file_info["Episode"]:
|
||||||
|
if eps != file_info["Episode"][-1]:
|
||||||
|
if not tmp_episodes:
|
||||||
|
tmp_episodes = str(eps) + "-"
|
||||||
|
else:
|
||||||
|
tmp_episodes = tmp_episodes + str(eps) + "-"
|
||||||
|
else:
|
||||||
|
if not tmp_episodes:
|
||||||
|
tmp_episodes = str(eps)
|
||||||
|
else:
|
||||||
|
tmp_episodes = tmp_episodes + str(eps)
|
||||||
|
self.currentFileOutputPath = tools.replace(self.currentFileOutputPath, "${EPISODE}", tmp_episodes)
|
||||||
|
else:
|
||||||
|
self.currentFileOutputPath = tools.replace(self.currentFileOutputPath, "${EPISODE}", file_info["Episode"])
|
||||||
|
self.currentFileOutputPath = tools.replace(self.currentFileOutputPath, "${EPISODE_YEAR}", file_info["Episode Year"])
|
||||||
|
self.currentFileOutputPath = tools.replace(self.currentFileOutputPath, "${EPISODE_NAME}", file_info["Episode Title"])
|
||||||
|
if not subtitles:
|
||||||
|
if self.currentFileOutputPath[self.currentFileOutputPath.index("${FORMAT}") - 1] != ".":
|
||||||
|
if file_info["Format"].startswith("."):
|
||||||
|
self.currentFileOutputPath = tools.replace(self.currentFileOutputPath, "${FORMAT}", file_info["Format"])
|
||||||
|
else:
|
||||||
|
self.currentFileOutputPath = tools.replace(self.currentFileOutputPath, "${FORMAT}", "." + file_info["Format"])
|
||||||
|
else:
|
||||||
|
if file_info["Format"].startswith("."):
|
||||||
|
self.currentFileOutputPath = tools.replace(self.currentFileOutputPath, ".${FORMAT}", file_info["Format"])
|
||||||
|
else:
|
||||||
|
self.currentFileOutputPath = tools.replace(self.currentFileOutputPath, ".${FORMAT}", "." + file_info["Format"])
|
||||||
|
else:
|
||||||
|
if self.currentFileOutputPath[self.currentFileOutputPath.index("${FORMAT}") - 1] != ".":
|
||||||
|
self.currentFileOutputPath = tools.replace(self.currentFileOutputPath, "${FORMAT}", " - " + self.srt_language[num] + ".srt")
|
||||||
|
elif self.currentFileOutputPath[self.currentFileOutputPath.index("${FORMAT}" - 1)] == ".":
|
||||||
|
self.currentFileOutputPath = tools.replace(self.currentFileOutputPath, ".${FORMAT}", " - " + self.srt_language[num] + ".srt")
|
||||||
|
self.logger.debugReport("[Video] Output Path: " + self.currentFileOutputPath)
|
||||||
|
return self.currentFileOutputPath
|
||||||
|
|
||||||
|
def startCompressing(input_path, output_path, srt_list):
|
||||||
|
#TODO: Implement
|
||||||
|
pass
|
||||||
|
|
||||||
|
def constructServerPath(self):
|
||||||
|
#TODO: Implement
|
||||||
|
pass
|
||||||
|
|
||||||
|
def findSRTs(self):
|
||||||
|
self.logger.debugReport("[Video] findSRTs")
|
||||||
|
tmp_srt_lang = ["aar", "abk", "afr", "aka", "alb", "amh", "ara", "arg", "arm", "asm", "ava", "ave", "aym", "aze", "bak", "bam", "baq",
|
||||||
|
"bel", "ben", "bih", "bis", "bos", "bre", "bul", "bur", "cat", "cha", "che", "chi", "chu", "chv", "cor", "cos", "cre",
|
||||||
|
"cze", "dan", "div", "dut", "dzo", "eng", "epo", "est", "ewe", "fao", "fij", "fin", "fre", "fry", "ful", "geo", "ger",
|
||||||
|
"gla", "gle", "glg", "glv", "gre", "grn", "guj", "hat", "hau", "heb", "her", "hin", "hmo", "hrv", "hun", "ibo", "ice",
|
||||||
|
"ido", "iii", "iku", "ile", "ina", "ind", "ipk", "ita", "jav", "jpn", "kal", "kan", "kas", "kau", "kaz", "khm", "kik",
|
||||||
|
"kin", "kir", "kom", "kon", "kor", "kua", "kur", "lao", "lat", "lav", "lim", "lin", "lit", "ltz", "lub", "lug", "mac",
|
||||||
|
"mah", "mal", "mao", "mar", "may", "mlg", "mlt", "mon", "nau", "nav", "nbl", "nde", "ndo", "nep", "nno", "nob", "nor",
|
||||||
|
"nya", "oci", "oji", "ori", "orm", "oss", "pan", "per", "pli", "pol", "por", "pus", "que", "roh", "rum", "run", "rus",
|
||||||
|
"sag", "san", "sin", "slo", "slv", "sme", "smo", "sna", "snd", "som", "sot", "spa", "srd", "srp", "ssw", "sun", "swa",
|
||||||
|
"swe", "tah", "tam", "tat", "tel", "tgk", "tgl", "tha", "tib", "tir", "ton", "tsn", "tso", "tuk", "tur", "twi", "uig",
|
||||||
|
"ukr", "urd", "uzb", "ven", "vie", "vol", "wel", "wln", "wol", "xho", "yid", "yor", "zha", "zul"]
|
||||||
|
self.srt_to_include = []
|
||||||
|
self.srt_language = []
|
||||||
|
tmp_srt = self.current_file.replace(self.current_file.rsplit(".")[-1], "srt").lower()
|
||||||
|
if (ioservice.doesFileExist(tmp_srt)):
|
||||||
|
self.srt_to_include.append(tmp_srt)
|
||||||
|
self.srt_language.append("eng")
|
||||||
|
else:
|
||||||
|
for possible_srt in tmp_srt_lang:
|
||||||
|
tmp_srt = self.current_file.replace(self.current_file.rsplit(".")[-1], "")[:-1].lower()
|
||||||
|
tmp_srt = tmp_srt + "-" + possible_srt + ".srt"
|
||||||
|
if (ioservice.doesFileExist(tmp_srt)):
|
||||||
|
self.srt_to_include.append(tmp_srt)
|
||||||
|
self.srt_language.append(possible_srt)
|
||||||
|
|
||||||
|
def getVideoLength(self):
|
||||||
|
self.logger.debugReport("[Video] getVideoLength")
|
||||||
|
tmp_time = None
|
||||||
|
try:
|
||||||
|
tmp_time = subprocess.check_output([self.video_settings.settings["FFProbe"], '-i', self.current_file, '-show_entries', 'format=duration', '-v', 'quiet', '-of', 'csv=%s' % ("p=0")])
|
||||||
|
tmp_time = str(tmp_time)
|
||||||
|
if not tmp_time.find("N/A") != -1:
|
||||||
|
tmp_hours = time.strftime("%H", time.gmtime(round(float(re.findall('\d+', tmp_time)[0])))).lstrip("0")
|
||||||
|
tmp_minutes = time.strftime("%M", time.gmtime(round(float(re.findall('\d+', tmp_time)[0])))).lstrip("0")
|
||||||
|
try:
|
||||||
|
tmp_hours = int(tmp_hours) * 60
|
||||||
|
except:
|
||||||
|
tmp_hours = 0
|
||||||
|
try:
|
||||||
|
tmp_length = tmp_hours + int(tmp_minutes)
|
||||||
|
except:
|
||||||
|
tmp_length = tmp_hours
|
||||||
|
else:
|
||||||
|
self.logger.errorReport("[Video] FFProbe could not get video content length")
|
||||||
|
tmp_length = None
|
||||||
|
return tmp_length
|
||||||
|
except:
|
||||||
|
self.logger.errorReport("[Video] There was a problem getting the length of video content")
|
||||||
|
return None
|
||||||
|
|
Loading…
Reference in New Issue
Block a user