例外処理とか色々荒削りな部分もありますが、とりあえず動けばいいやレベルで。
以下、スクリプト全体。
# -*- coding: utf-8 -*- ''' Created on 2013/03/11 2013/03/11 - File was created. @author: Juntaro Minezaki ''' import datetime import json import os import platform import shutil import subprocess import sys import threading import time import unicodedata if (platform.system() == 'Linux'): print('Linux OS. Using utf-8.') import pexpect SYSTEM = 'Linux' OS_ENCODING = 'utf-8' ENCODER_PATH = u'mencoder' TASKLIST_PATH = u'~/tasklist.json' WAIT_FOLDER = u'~/Wait' PROCESS_FOLDER = u'~/Wait' TV_FOLDER = u'~/tv' MAX_THREAD = 4 elif (platform.system() == 'Windows'): print('Windows OS. Using cp932') SYSTEM = 'Windows' OS_ENCODING = 'cp932' ENCODER_PATH = u' ' # mencoder.exeのフルパス TASKLIST_PATH = u' ' # 進捗を保存用ファイルのパス WAIT_FOLDER = u' ' # エンコード対象を格納するフォルダ PROCESS_FOLDER = u' ' # 現状未使用 TV_FOLDER = u' ' # エンコード後のファイルを格納するフォルダ MAX_THREAD = 3 # 同時進行するエンコード数 else: print('Unsupported OS. Exit.') sys.exit() MAX_COMPLETED = 100 FIRST_PASS_OPTION = u'-nosound -ovc xvid -xvidencopts pass=1:bitrate=2000:threads=1:me_quality=1: -vf yadif=3,pp=l5,framestep=2,scale=1280:720 -ofps 30000/1001' SECOND_PASS_OPTION = u'-oac mp3lame -lameopts abr:br=192 -ovc xvid -xvidencopts pass=2:bitrate=2000:threads=1 -vf yadif=3,pp=l5,framestep=2,scale=1280:720 -ofps 30000/1001' class TaskList(): def __init__(self): self.tasklist = {'wait':[], 'process':[], 'completed':[]} def __add_file_to_wait(self, path): elemstat = os.stat(path) moddate = datetime.datetime.fromtimestamp(elemstat.st_mtime) datestr = moddate.strftime('%Y-%m-%d %H:%M:%S') for element in self.tasklist['wait']: if element.get('path', u'') == path: return for element in self.tasklist['process']: if element.get('path', u'') == path: return for element in self.tasklist['completed']: if element.get('path', u'') == path: return self.tasklist['wait'].append({ 'date': datestr, 'path': path }) self.tasklist['wait'].sort (key=lambda x:x['date']) def __delete_wait(self, path): for element in self.tasklist['wait']: if element['path'] == path: self.tasklist['wait'].remove(element) return True return False def __delete_old_completed(self): while len(self.tasklist['completed']) > MAX_COMPLETED: element = self.tasklist['completed'][0] self.tasklist['completed'].remove(element) return def __add_process(self, path): current = datetime.datetime.now() datestr = current.strftime('%Y-%m-%d %H:%M:%S') self.tasklist['process'].append({ 'path': path, 'date': datestr, 'pass': 0, 'progress': 0 }) return True def __delete_process(self, path): for element in self.tasklist['process']: if element['path'] == path: self.tasklist['process'].remove(element) return True return False def __add_completed(self, path): current = datetime.datetime.now() datestr = current.strftime('%Y-%m-%d %H:%M:%S') self.tasklist['completed'].append({ 'path': path, 'date': datestr }) self.__delete_old_completed() return True def load(self, src): try: f = open(src, 'r') importtask = json.loads(f.read()) f.close except: importtask = {'wait':[], 'process':[], 'completed':[]} for process in importtask['process']: if (os.path.exists(process.get('path', u'')) == False): continue for element in self.tasklist['process']: if element.get('path', u'') == process.get('path', u''): break else: #暫定 self.__add_file_to_wait(process['path']) for wait in importtask['wait']: if (os.path.exists(wait.get('path', u'')) == False): continue for element in self.tasklist['process']: if element.get('path', u'') == wait.get('path', u''): break else: self.__add_file_to_wait(wait['path']) for completed in importtask['completed']: for element in self.tasklist['completed']: if element.get('path', u'') == completed.get('path', u''): break else: self.tasklist['completed'].append(completed) return def save(self, dest): try: f = open(dest, 'w') f.write(json.dumps(self.tasklist)) f.close except IOError: print ('Failed to open Task List File.') return def add_folder_to_wait(self, folder): for element in os.listdir(folder): elempath = os.path.join(WAIT_FOLDER, element) (root, ext) = os.path.splitext(element) if ext == '.ts': self.__add_file_to_wait(elempath) for element in self.tasklist['wait']: try: if (os.path.exists(element.path) == False): self.__delete_wait(element.path) except: pass return True def move_wait_to_process(self, path): if os.path.exists(path) == False: self.__delete_wait(path) return False if self.__delete_wait(path) == False: return False return self.__add_process(path) def move_process_to_completed(self, srcpath, destpath): if (self.__delete_process(srcpath) == False): return False return self.__add_completed(destpath) def get_highest_in_wait(self): if self.tasklist['wait'] == []: return u'' return self.tasklist['wait'][0].get('path', u'') def update_process(self, process): path = process.get('path', u'') for element in self.tasklist['process']: if element['path'] == path: element['pass'] = process.get('pass', 0) element['progress'] = process.get('progress', 0) break class EncodeThread(threading.Thread): def __init__(self, index): threading.Thread.__init__(self) self.index = index #Thread number self.free = True # self.wait = False #Waiting for execute encoding self.path = u'' #File which will be encoded self.encodepass = 0 self.progress = 0 def setpath(self, path): self.path = path self.wait = True def getpath(self): return self.path def isfree(self): return self.free def iswait(self): return self.wait def iscompleted(self): if (self.isfree () == True): return False if (self.encodepass > 2): return True return False def isnewtask(self): if (self.isfree() == True): if (self.iswait() == True): return True return False def getindex(self): return self.index def getprogress(self): return self.progress def getencodepass(self): return self.encodepass def getprocess(self): return {'path': self.path, 'pass': self.encodepass, 'progress': self.progress} def clear_encode_process(self): self.free = True self.wait = False self.path = u'' self.encodepass = 0 self.progress = 0 return def __update_encoder_progress(self, encoderthread): cpl = encoderthread.compile_pattern_list([pexpect.EOF, "\d+%"]) match = encoderthread.expect_list(cpl, timeout=None) if match == 0: #EOF return False elif match == 1: prog = encoderthread.match.group(0).replace('%', '') self.progress = int(prog, 10) encoderthread.close return True else: return True def __wait_for_move(self, path): if(SYSTEM == 'Windows'): while True: try: f = open(path, 'rb') f.close break except IOError: time.sleep(1) continue else: #todo time.sleep(300) return def execute_encode_process(self): if (os.path.exists(self.path) == False): print('[%d]File not exist.' % self.index) print(' %s' % self.path) self.encodepass = 3 return self.__wait_for_move(self.path) print("Start Encoding: %s" % self.path) (root, ext) = os.path.splitext(self.path) first_cmd = ( u'"%s" %s -passlogfile "%s.log" -o "%s.avi" "%s"' % (ENCODER_PATH, FIRST_PASS_OPTION, root, root, self.path) ) second_cmd = ( u'"%s" %s -passlogfile "%s.log" -o "%s.avi" "%s"' % (ENCODER_PATH, SECOND_PASS_OPTION, root, root, self.path) ) if(SYSTEM == 'Windows'): self.encodepass = 1 p = subprocess.Popen(first_cmd.encode(OS_ENCODING), shell=False) self.progress = -1 p.wait() self.encodepass = 2 p = subprocess.Popen(second_cmd.encode(OS_ENCODING), shell=False) self.progress = -1 p.wait() elif(SYSTEM == 'Linux'): self.encodepass = 1 p = pexpect.spawn(first_cmd.encode(OS_ENCODING)) while (self.__update_encoder_progress(p) == True): pass self.encodepass = 2 p = pexpect.spawn(second_cmd.encode(OS_ENCODING)) while (self.__update_encoder_progress(p) == True): pass else: #todo: unsupported system pass self.encodepass = 3 return def run(self): while True: if (self.isnewtask() == True): self.free = False self.execute_encode_process() time.sleep(30) return class Mover(): def __init__(self): pass def __sortlistbylength (self, intr_list): list_len = len(intr_list) if (list_len < 2): return intr_list for i in range (0, list_len - 1): for j in range (0, list_len - 1 - i): if (len (intr_list[j]) < len (intr_list[j+1])): intr_list[j], intr_list[j+1] = intr_list[j+1], intr_list[j] return intr_list def __getfolderlist(self, dir): folder_list = [] for elem in os.listdir(dir): if (os.path.isdir(os.path.join (dir, elem))): folder_list.append(elem) return self.__sortlistbylength(folder_list) def __getdest(self, src, destdir, folderlist): filename = os.path.basename (src) for elem in folderlist: norm_folder_name = unicodedata.normalize ('NFKC', elem) norm_file_name = unicodedata.normalize ('NFKC', filename) if (norm_file_name.find(norm_folder_name) > -1): return os.path.join (destdir, elem) return destdir def __moveavi(self, src, destdir): if os.path.exists(src) == False: print (u'Source file(%s) was not exist.' % (src)) return src if os.path.exists(destdir) == False: print (u'Destination folder(%s) was not exist.' % (destdir)) return src folderlist = self.__getfolderlist(destdir) dest = self.__getdest(src, destdir, folderlist) if (os.path.dirname (src) != dest): try: shutil.move (src, dest) #todo: ファイルの存在チェック, 重複してたらおしりに日時つければいいかな? except: pass return os.path.join(dest, os.path.basename(src)) def move(self, src, destdir): (root, ext) = os.path.splitext(src) srcavi = root + u'.avi' srclog = root + u'.log' print(srcavi) movdir = self.__moveavi(srcavi, destdir) try: os.remove(srclog) os.remove(src) except: pass return movdir def main(): if (SYSTEM == 'Windows' and os.path.exists(ENCODER_PATH) != True): print('Encoder was not found.') sys.exit() if (os.path.exists(WAIT_FOLDER) != True): print('Wait folder was not found.') sys.exit() if (os.path.exists(PROCESS_FOLDER) != True): print('Process folder was not found.') sys.exit() tasklist = TaskList() tasklist.load(TASKLIST_PATH) tasklist.add_folder_to_wait(WAIT_FOLDER) mover = Mover() threads = [] for i in range(MAX_THREAD): t = EncodeThread(i) threads.append(t) t.start() while True: for t in threads: if (t.isfree() == False): if (t.iscompleted() == True): path = t.getpath() tasklist.move_process_to_completed(path, mover.move(path, TV_FOLDER)) t.clear_encode_process() tasklist.update_process(t.getprocess()) continue tasklist.add_folder_to_wait(WAIT_FOLDER) path = tasklist.get_highest_in_wait() if tasklist.move_wait_to_process(path) == True: t.setpath(path) tasklist.save(TASKLIST_PATH) time.sleep(30) if __name__ == '__main__': main()
以下、ざっくりと処理の内容。
threads = [] for i in range(MAX_THREAD): t = EncodeThread(i) threads.append(t) t.start() while True: for t in threads: if (t.isfree() == False): #print ('[%d] Pass %d, Progress %d' % (t.getindex(), t.getencodepass(), t.getprogress())) if (t.iscompleted() == True): #print ('[%d] Completed' % t.getindex()) path = t.getpath() tasklist.move_process_to_completed(path, mover.move(path, TV_FOLDER)) t.clear_encode_process() #print(json.dumps(tasklist.tasklist, sort_keys=True, indent=2)) tasklist.update_process(t.getprocess()) continue tasklist.add_folder_to_wait(WAIT_FOLDER) path = tasklist.get_highest_in_wait() if tasklist.move_wait_to_process(path) == True: t.setpath(path) tasklist.save(TASKLIST_PATH) time.sleep(30)
エンコード用のクラスEncodeThreadを指定スレッド数分作成。
各スレッドで
プロセス追加→エンコードプロセス開始→プロセス監視→エンコードプロセス終了→ファイル移動→プロセス完了処理
を繰り返すようにしています。
if(SYSTEM == 'Windows'): self.encodepass = 1 p = subprocess.Popen(first_cmd.encode(OS_ENCODING), shell=False) self.progress = -1 p.wait() self.encodepass = 2 p = subprocess.Popen(second_cmd.encode(OS_ENCODING), shell=False) self.progress = -1 p.wait() elif(SYSTEM == 'Linux'): self.encodepass = 1 p = pexpect.spawn(first_cmd.encode(OS_ENCODING)) while (self.__update_encoder_progress(p) == True): pass self.encodepass = 2 p = pexpect.spawn(second_cmd.encode(OS_ENCODING)) while (self.__update_encoder_progress(p) == True): pass
エンコードプロセスは、OS種類で投げ方、監視の仕方を変えました。
Windowsでpexpectが使えればよかったのですが、pexpect相当のwexpectが上手く使えなかったのでsubprocessモジュールで我慢してます…。
def __update_encoder_progress(self, encoderthread): cpl = encoderthread.compile_pattern_list([pexpect.EOF, "\d+%"]) match = encoderthread.expect_list(cpl, timeout=None) if match == 0: #EOF return False elif match == 1: prog = encoderthread.match.group(0).replace('%', '') self.progress = int(prog, 10) encoderthread.close return True else: return True
Linux OSの場合はここを参考にpexpect.spawnで出力からエンコードの進捗を取得しています。
def __moveavi(self, src, destdir): if os.path.exists(src) == False: print (u'Source file(%s) was not exist.' % (src)) return src if os.path.exists(destdir) == False: print (u'Destination folder(%s) was not exist.' % (destdir)) return src folderlist = self.__getfolderlist(destdir) dest = self.__getdest(src, destdir, folderlist) if (os.path.dirname (src) != dest): try: shutil.move (src, dest) #todo: ファイルの存在チェック, 重複してたらおしりに日時つければいいかな? except: pass return os.path.join(dest, os.path.basename(src))
エンコードプロセス完了後は、Moverクラスを使用してエンコード済みファイルを指定のフォルダへ移動させます。
その際、ファイル名の一部がフォルダ内のサブフォルダ名と一致するかを__getdest()内で判定し、該当するサブフォルダがある場合はそちらを優先して移動先とさせました。
エンコード状況を見れるようにするために、TaskListクラスを使用して進捗をjsonファイルに出力するようにしています。
jsonファイルの閲覧用スクリプトはまた後日投稿して行きたいと考えてます。
なんとなくで作っては見たものの、普段はC言語くらいしか触れていないため、クラスが上手く使えていないです。
こればかりは少しずつ学習して、慣れていかないとですね。
0 件のコメント:
コメントを投稿