Media-Manager/Video/videoservice.py

365 lines
22 KiB
Python

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