#! /usr/bin/python3 import copy, os, re, shutil, subprocess, sys def checkDir(directory, mode = 0o755): if not os.path.exists (directory): os.makedirs(directory, mode) def runCommand(args): cmdString = ' '.join(args) print("Running %s" % cmdString) p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) out, err = p.communicate() rc = p.wait() if rc != 0: print("%s failed: rc=%d" % (cmdString, rc)) print(out) print(err) return rc def getBasename(videoFile): if videoFile.endswith(".mkv"): return videoFile[:-4] elif videoFile.endswith(".ts"): return videoFile[:-3] elif videoFile.endswith(".mpg"): return videoFile[:-4] raise Exception("%s: Unknown input file extension" % videoFile) def comskipVideo(videoFile): logs = "/home/hts/comskip/logs" args = [] args.append("/usr/bin/comskip") args.append(videoFile) rc = runCommand(args) baseName = getBasename(videoFile) d, baseFile = os.path.split(baseName) if os.path.exists(logs): for ext in ( ".log", ".txt", ".logo.txt" ): if os.path.exists(baseName + ext): shutil.move(baseName + ext, os.path.join(logs, baseFile + ext)) class VideoConverter: def __init__(self): os.nice(19) try: import psutil p = psutil.Process(os.getpid()) p.set_ionice(psutil.IOPRIO_CLASS_IDLE) print("Success setting ionice class idle") except: pass self.reInterlaced = re.compile('.*Interlaced') self.reAspect43 = re.compile('Display aspect.*4:3') self.reAc3 = re.compile('Format.*AC-3') self.reStereo = re.compile('Channel.*2 channels') self.reWidth720 = re.compile('Width.*720') self.reWidth704 = re.compile('Width.*704') self.reHeight480 = re.compile('Height.*480') self.reHeight720 = re.compile('Height.*720') self.reHeight1080 = re.compile('Height.*1 080') self.reBitRate192 = re.compile('Bit rate.*192') self.reBitRate224 = re.compile('Bit rate.*224') self.reBitRate384 = re.compile('Bit rate.*384') self.reFps23976 = re.compile(".*23.976.*fps") self.reFps239 = re.compile(".*23.9.*fps") self.reFps24 = re.compile(".*24.*fps") self.reFps2997 = re.compile(".*29.97.*fps") self.reFps299 = re.compile(".*29.9.*fps") self.reFps50 = re.compile(".*50.000 fps") self.reFps5994 = re.compile(".*59.940 fps") self.passLogFile = "/var/tmp/VideoToCuBox%d.passlog" % os.getpid() self.tmpVideoFile = None def getOpts(self, inputFile): args = [] args.append("mediainfo") args.append(inputFile) p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) out, err = p.communicate() rc = p.wait() if rc != 0: print("%s failed: rc=%d" % (' '.join(args), rc)) sys.exit(1) abitrate='256k' useFps23976 = False useFps24 = False useFps2997 = False useFps30 = False isAspect43 = False isInterlaced = False isAc3 = False is5994i = False height = 0 width = 0 for l in out.split('\n'): if self.reInterlaced.match(l): isInterlaced = True elif self.reAspect43.match(l): isAspect43 = True elif self.reAc3.match(l): isAc3 = True elif self.reStereo.match(l): isStereo = True for l in out.split('\n'): if self.reWidth704.match(l): width = 704 elif self.reWidth720.match(l): width = 720 elif self.reHeight480.match(l): height = 480 elif self.reHeight1080.match(l): height = 1080 elif self.reHeight720.match(l): height = 720 elif self.reFps23976.match(l): useFps23976 = True elif self.reFps24.match(l): useFps24 = True elif self.reFps2997.match(l): useFps2997 = True elif self.reFps5994.match(l): useFps2997 = True if isInterlaced: print("Converting 59.94i fps to 29.97p fps") else: print("Converting 59.94p fps to 29.97p fps") elif self.reFps299.match(l): useFps30 = True elif self.reFps50.match(l): print("Converting 50 fps to 24.0 fps") useFps24 = True elif self.reBitRate192.match(l): abitrate = '192k' elif self.reBitRate224.match(l): abitrate = '224k' elif self.reBitRate384.match(l): abitrate = '384k' if height == 480 and width == 720: if isInterlaced: print("480i video") else: print("480p video") else: print("1080p video") # conversion to vorbis @96k should be good enough # for ac3 stereo at 192k if isAc3 and isStereo and abitrate == '192k': abitrate = '128k' elif isAc3 and isStereo and abitrate == '384k': abitrate = '192k' opts = [] if isInterlaced: opts.append('-deinterlace') if isAspect43 and height == 480: opts.append("-s") opts.append("544x480") elif height == 1080 or height == 720: # 1280x720 is same aspect as 1920x1080 opts.append("-s") opts.append("1280x720") if useFps2997: opts.append("-r"); opts.append("29.97"); if useFps23976 or useFps24: opts.append("-x264opts") opts.append("keyint=24") elif useFps2997 or useFps30: opts.append("-x264opts") opts.append("keyint=30") # 28.0 (avg bitrate just under 1Mbit), 26.0 (avg bitrage 1040 Kbit) # 24.0 (avg bitrate 1.4 Mbit) opts.append("-crf") opts.append("24.0") if isAc3: opts.append("-codec:a") opts.append("copy") else: opts.append("-ac") opts.append("2") opts.append("-b:a") opts.append(abitrate) return opts def getPassOpts(self, passNo, inputFile, fmt): opts = [] opts.append("ffmpeg") opts.append("-y") # overwrite existing files opts.append("-i") opts.append(inputFile) if format == "mp4": opts.append("-f") opts.append(fmt) opts.append("-codec:v") opts.append("libx264") opts.append("-profile:v") opts.append("high") opts.append("-level:v") #opts.append("40") opts.append("41") opts += self.getOpts(inputFile) if passNo == 2: opts.append("-preset") opts.append("slow") if passNo == 0: opts.append(self.tmpVideoFile) elif passNo == 1 or passNo == 2: opts.append("-passlogfile") opts.append(self.passLogFile) if passNo == 1: opts.append("-pass") opts.append("1") opts.append("/dev/null") elif passNo == 2: opts.append("-pass") opts.append("2") opts.append(self.tmpVideoFile) return opts def getOutputFilename(self, inputFile): return getBasename(inputFile) + ".mkv" def convert(self, inputFile): fmt = "mkv" bn = getBasename(inputFile) outputFile = self.getOutputFilename(inputFile) self.tmpVideoFile = "/var/tmp/VideoToCuBox%d.%s" % ( os.getpid(), fmt ) rc = 0 if False: rc = runCommand(self.getPassOpts(1, inputFile, None, fmt)) if rc == 0: rc = runCommand(self.getPassOpts(2, inputFile, fmt)) else: rc = runCommand(self.getPassOpts(0, inputFile, fmt)) if rc == 0: if inputFile == outputFile: shutil.move(inputFile, bn + "-orig.mkv") shutil.move(self.tmpVideoFile, outputFile) if self.passLogFile and os.path.exists(self.passLogFile): os.remove(self.passLogFile) if self.tmpVideoFile and os.path.exists(self.tmpVideoFile): os.remove(self.tmpVideoFile) return rc def main(argv): vc = VideoConverter() for f in argv[1:]: vc.convert(f) if __name__ == "__main__": main(sys.argv)