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, False) 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