Changeset 1797
- Timestamp:
- 11/14/08 17:48:21 (16 months ago)
- Location:
- CodeSnippets/VideoTools
- Files:
-
- 5 modified
-
eac3to.py (modified) (10 diffs)
-
setup.py (modified) (2 diffs)
-
shared/config.py (modified) (9 diffs)
-
shared/tools.py (modified) (1 diff)
-
Templatemaker.py (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
CodeSnippets/VideoTools/eac3to.py
r1789 r1797 5 5 """ 6 6 7 import os, sys, time, datetime, subprocess, logging 7 import os, sys, glob, time, datetime, subprocess, logging 8 from pprint import pprint 9 10 import Tkinter as tk 8 11 9 12 import win32api 10 13 11 14 from shared.config import VideoToolsConfig 12 from shared.tools import askopenfilename2, askdirectory2 15 from shared.tk_tools import askopenfilename2, askdirectory2, TkListbox 16 from shared.tools import make_slug, human_filesize 13 17 14 18 DEBUG = False … … 16 20 17 21 18 class StreamInfo(object): 19 def __init__(self, filename, txt_filter, parameter=[]): 20 self.filename = filename 21 self.txt_filter = txt_filter 22 self.parameter = parameter 22 VIDEO_EXT = ".mkv" # ToDo: Should go into the config 23 24 STREAMINFOS = [ 25 { 26 "txt_filter": ("Chapters",), 27 "ext": ".txt", 28 }, 29 { 30 "txt_filter": ("h264", "VC-1", "MPEG2",), 31 "ext": VIDEO_EXT 32 }, 33 { 34 "txt_filter": ("DTS Hi-Res",), 35 "ext": ".dtshr", 36 }, 37 { 38 "txt_filter": ("DTS",), 39 "ext": ".dts", 40 }, 41 { 42 "txt_filter": ("Subtitle",), 43 "ext": ".sup", 44 }, 45 ] 46 47 48 49 class Drive(dict): 50 def __init__(self, driveletter, cfg): 51 self["letter"] = driveletter 52 self.cfg = cfg 53 54 self["vol_info"] = None 55 self["lable"] = None 56 57 self["stream_dir"] = os.path.join(driveletter, cfg["stream_dir"]) 58 59 def set_vol_info(self, vol_info): 60 self["vol_info"] = vol_info 61 self["lable"] = vol_info[0] 62 63 def debug(self): 64 print "_"*79 65 print "Drive debug:" 66 pprint(self) 67 print "-"*79 23 68 24 69 def __str__(self): 25 return "<StreamInfo %s (%s)>" % ( 26 self.filename, self.parameter 70 return "<Drive '%s'>" % self["letter"] 71 # def __repr__(self): 72 # return self.__str__() 73 74 75 76 77 78 79 80 81 class VideoFile(dict): 82 def __init__(self, name, drive, abs_path, stat, cfg): 83 self["name"] = name # Filename 84 85 self["fn"], self.ext = os.path.splitext(self["name"]) 86 87 self["drive"] = drive # class "Drive" 88 self["abs_path"] = abs_path # File path + filename 89 self["stat"] = stat # os.stat 90 91 self.cfg = cfg 92 93 self["out_path"] = os.path.join(cfg["out_dir"], drive["lable"]) 94 95 self["name_prefix"] = self["name"].replace(".", "_") 96 97 self["eac3to_txt_path"] = os.path.join( 98 self["out_path"], 99 "%s_eac3to.txt" % self["name_prefix"] 27 100 ) 28 def __repr__(self): 29 return self.__str__() 30 31 32 VIDEO_EXT = "mkv" # ToDo: Should go into the config 33 34 STREAMINFOS = [ 35 StreamInfo( 36 filename = "chapters.txt", 37 txt_filter = ("Chapters",), 38 ), 39 StreamInfo( 40 filename = "h264."+VIDEO_EXT, 41 txt_filter = ("h264",), 42 ), 43 StreamInfo( 44 filename = "VC1."+VIDEO_EXT, 45 txt_filter = ("VC-1",), 46 ), 47 StreamInfo( 48 filename = "MPEG2."+VIDEO_EXT, 49 txt_filter = ("MPEG2",), 50 ), 51 StreamInfo( 52 filename = "ger 384KBits.ac3", 53 txt_filter = ("German",), 54 parameter = ["-384", "-down6", "-quality=4"], 55 ), 56 ] 57 58 59 60 class Drive(object): 61 def __init__(self, driveletter, cfg): 62 self.letter = driveletter 63 self.cfg = cfg 64 65 self.vol_info = None 66 self.lable = None 67 68 self.stream_dir = os.path.join(driveletter, cfg["stream_dir"]) 69 70 def set_vol_info(self, vol_info): 71 self.vol_info = vol_info 72 self.lable = vol_info[0] 73 74 def __str__(self): 75 return "<Drive '%s'>" % self.letter 76 def __repr__(self): 77 return self.__str__() 78 79 80 class Files(list): 81 def print_info(self): 82 for file in self: 83 print file.abs_path, 84 size = file.stat.st_size/1024.0/1024.0/1024.0 85 print "%.1f GB" % size 86 print "output path....:", file.output_path 87 print "log file path..:", file.log_file_path 88 print 89 90 91 92 93 94 95 96 97 98 99 class VideoFile(object): 100 def __init__(self, name, drive, abs_path, stat, cfg): 101 self.name = name # Filename 102 103 self.fn, self.ext = os.path.splitext(self.name) 104 105 self.drive = drive # class "Drive" 106 self.abs_path = abs_path # File path + filename 107 self.stat = stat # os.stat 108 109 self.cfg = cfg 110 111 self.output_path = os.path.join(cfg["out_dir"], drive.lable) 112 113 log_fn = "%s.log" % self.name.replace(".", "_") 114 self.log_file_path = os.path.join(self.output_path, log_fn) 115 116 self.streams = None # set in self.parse_streaminfo() 101 102 self["log_file_path"] = os.path.join( 103 self["out_path"], 104 "%s.log" % self["name_prefix"] 105 ) 106 self.log_file = file(self["log_file_path"], "a") 107 self.log("Start logging") 108 109 self["streams"] = None # set in self.parse_streaminfo() 117 110 118 111 def create_outdir(self): 119 if not os.path.isdir(self .output_path):120 os.makedirs(self .output_path)112 if not os.path.isdir(self["out_path"]): 113 os.makedirs(self["out_path"]) 121 114 122 115 #------------------------------------------------------------------------- 123 124 def open_log(self):125 """126 Open the log file127 """128 self.log_file = file(self.log_file_path, "a")129 self.log("Start logging")130 116 131 117 def log(self, txt): … … 140 126 #------------------------------------------------------------------------- 141 127 142 def get_command(self): 143 cmds = [self.cfg["eac3to"], self.abs_path] 144 for no, stream in self.streams: 145 #~ print stream 146 out_name = "%s_%s" % (self.fn, stream.filename) 147 out_fn = os.path.join(self.output_path, out_name) 148 cmds.append("%s: %s" % (no, out_fn)) 149 150 if stream.parameter: 151 cmds += stream.parameter 152 153 return " ".join(cmds) 154 155 def parse_line(self, no, info): 156 def in_list(txt, l): 157 for item in l: 158 if item in txt: 159 return True 160 return False 161 162 for stream_info in STREAMINFOS: 163 if in_list(info, stream_info.txt_filter): 164 self.streams.append([no, stream_info]) 128 def get_command(self, stream_selection): 129 cmd = [self.cfg["eac3to"], self["abs_path"]] 130 131 value_dict = {} 132 for k,v in self["streams"].iteritems(): 133 # print k,v 134 if v not in value_dict: 135 value_dict[v] = [] 136 137 value_dict[v].append(k) 138 139 # pprint(value_dict) 140 141 def get_file_ext(stream_txt): 142 def in_list(txt, l): 143 for item in l: 144 if item in txt: 145 return True 146 return False 147 148 for stream_info in STREAMINFOS: 149 if in_list(stream_txt, stream_info["txt_filter"]): 150 return stream_info["ext"] 151 152 raise RuntimeError( 153 "No STREAMINFOS entry matched for '%s'" % stream_txt 154 ) 155 156 selected_streams = {} 157 for stream_txt in stream_selection: 158 try: 159 ids = value_dict[stream_txt] 160 except KeyError: 161 print "Stream '%s' doesn't exist. Skip file." % stream_txt 165 162 return 163 164 file_ext = get_file_ext(stream_txt) 165 166 for id in ids: 167 filename = "%s - %i - %s%s" % ( 168 self["name_prefix"], 169 id, 170 make_slug(stream_txt), 171 file_ext, 172 ) 173 out_filepath = os.path.join(self["out_path"], filename) 174 175 cmd.append("%i:" % id) 176 cmd.append(out_filepath) 177 178 return cmd 166 179 167 180 168 181 def parse_streaminfo(self, txt): 169 self .streams = []182 self["streams"] = {} 170 183 lines = txt.splitlines() 171 184 lines = lines[1:] 172 185 for line in lines: 173 #~ print ">", line 186 if DEBUG: 187 print ">", line 174 188 no, info = line.split(":",1) 175 no = int(no) 189 190 try: 191 no = int(no) 192 except ValueError: 193 continue 194 176 195 info = info.strip() 177 if info.startswith("Subtitle"): 178 # Skip all subtitle streams 179 continue 180 181 self.parse_line(no, info) 182 196 197 self["streams"][no] = info 198 183 199 184 200 def get_stream_info(self): 185 cmd = [self.cfg["eac3to"], self.abs_path] 186 print "run '%s'..." % " ".join(cmd) 187 process, output = subprocess2(cmd) 188 return process, output 201 """ 202 Run eac3to and cache the output data. 203 """ 204 if os.path.isfile(self["eac3to_txt_path"]): 205 print "Use existing eac3to output from cache file '%s'..." % ( 206 self["eac3to_txt_path"] 207 ) 208 f = file(self["eac3to_txt_path"], "r") 209 output = f.read() 210 f.close() 211 else: 212 # run eac3to and save the output data into self["eac3to_txt_path"] 213 cmd = [self.cfg["eac3to"], self["abs_path"]] 214 print "run '%s'..." % " ".join(cmd) 215 process, output = subprocess2(cmd) 216 217 f = file(self["eac3to_txt_path"], "w") 218 f.write(output) 219 f.close() 220 221 return output 189 222 190 223 #------------------------------------------------------------------------- 224 225 def debug(self): 226 print "_"*79 227 print "VideoFile debug:" 228 pprint(self) 229 print "-"*79 191 230 192 231 def __str__(self): 193 return "<File '%s'>" % self.abs_path 194 def __repr__(self): 195 return self.__str__() 196 197 198 232 return "<File '%s'>" % self["abs_path"] 233 234 235 #------------------------------------------------------------------------------ 199 236 200 237 … … 217 254 continue 218 255 219 if os.path.isdir(drive .stream_dir):256 if os.path.isdir(drive["stream_dir"]): 220 257 drive.set_vol_info(vol_info) 221 258 result.append(drive) … … 225 262 226 263 264 def choose_drive(cfg): 265 """ 266 Choose a stream dir manuely. 267 """ 268 path = askopenfilename2( 269 title = "Choose the stream dir:", 270 initialdir = cfg["last sourcedir"], 271 filetypes = [('M2TS File','*.m2ts')], 272 ) 273 274 vol_info = path.replace("\\", "_").replace(":","_") # Fallback 275 for part in reversed(path.split(os.sep)[:-1]): 276 # Use the last part of the path as a vol_info 277 if part.upper() not in ("BDMV", "STREAM"): 278 vol_info = part 279 break 280 281 drive = Drive(os.path.splitdrive(path)[0], cfg) 282 drive["stream_dir"] = os.path.split(path)[0] 283 drive.set_vol_info((vol_info,)) 284 285 drive.debug() 286 287 drives = [drive,] 288 289 cfg["last sourcedir"] = path 290 291 return drives 292 293 294 #------------------------------------------------------------------------------ 295 227 296 228 297 def get_stream_files(drives, skip_size): 229 files = Files()298 files = [] 230 299 for drive in drives: 231 300 print drive, 232 path = drive .stream_dir301 path = drive["stream_dir"] 233 302 print path 234 303 if not os.path.isdir(path): … … 236 305 continue 237 306 238 for fn in sorted(os.listdir(path)): 239 abs_path = os.path.join(path, fn) 307 glob_path = os.path.join(path, cfg["glob"]) 308 print "Looking in", glob_path 309 file_list = glob.glob(glob_path) 310 print "Files found:", file_list 311 for abs_path in sorted(file_list): 312 filename = os.path.basename(abs_path) 313 240 314 stat = os.stat(abs_path) 241 size = stat.st_size 242 if size<cfg["skip_size"]: 243 # Skip small files 244 continue 245 246 f = VideoFile(fn, drive, abs_path, stat, cfg) 315 316 # print (filename, drive, abs_path, stat, cfg) 317 f = VideoFile(filename, drive, abs_path, stat, cfg) 247 318 files.append(f) 248 319 … … 263 334 264 335 336 def select_videofiles(videofiles): 337 """ 338 Select witch founded files should be recordnized. 339 """ 340 activated = [] 341 item_list = [] 342 for index, videofile in enumerate(videofiles): 343 filesize = videofile["stat"].st_size 344 345 line = "%s - %s" % (videofile["abs_path"], human_filesize(filesize)) 346 item_list.append(line) 347 348 if filesize>cfg["skip_size"]: 349 activated.append(index) 350 351 # size = stat.st_size 352 # if size<cfg["skip_size"]: 353 # # Skip small files 354 # continue 355 356 lb = TkListbox( 357 title = "Please select", 358 lable = "Please select files witch should be converted:", 359 items = item_list, 360 activated = activated 361 ) 362 print "selected items:" 363 pprint(lb.selection) # list of selected items. 364 365 curselection = lb.curselection # tuple containing index of selected items 366 print "curselection:", curselection 367 368 new_list = [ 369 item 370 for index, item in enumerate(videofiles) 371 if index in curselection 372 ] 373 374 return new_list 375 376 377 378 265 379 266 380 def subprocess2(cmd): 381 print "_"*80 382 print "subprocess2():" 383 pprint(cmd) 384 print " -"*40 267 385 process = subprocess.Popen( 268 386 cmd, … … 294 412 return process, output 295 413 296 414 415 def select_streams(videofiles): 416 """ 417 select witch streams should be convert. 418 """ 419 print "*"*79 420 streams_txt = [] 421 for videofile in videofiles: 422 # videofile.debug() 423 for stream_txt in videofile["streams"].values(): 424 if not stream_txt in streams_txt: 425 streams_txt.append(stream_txt) 426 # 427 # lb = TkListbox( 428 # title = "Please select", 429 # lable = "Please select streams:", 430 # items = streams_txt 431 # ) 432 # print lb.selection 433 434 selection = TkListbox( 435 title = "Please select", 436 lable = "Please select streams:", 437 items = streams_txt 438 ).selection 439 440 return selection 441 442 def convert_streams(videofile, stream_selection): 443 print "convert_streams():", videofile, stream_selection 444 videofile.debug() 445 446 297 447 298 448 … … 304 454 print "DEBUG!!!" 305 455 drive = Drive("d:", cfg) 306 drive .stream_dir= r"d:\TEST\BDMV\STREAM"456 drive["stream_dir"] = r"d:\TEST\BDMV\STREAM" 307 457 drive.set_vol_info(("TEST_LABLE",)) 308 458 drives = [drive,] … … 313 463 314 464 if not drives: 315 print "No drives with stream files found -> abort, ok." 316 sys.exit() 317 318 files = get_stream_files(drives, cfg) 319 files.print_info() 320 321 if not files: 465 print "No drives with stream files found -> choose manuel" 466 drives = choose_drive(cfg) 467 468 # Get from all path all m2ts files 469 videofiles = get_stream_files(drives, cfg) 470 471 # Select via Tk the files witch realy convert 472 videofiles = select_videofiles(videofiles) 473 474 if not videofiles: 322 475 print "No stream files found -> abort, ok." 323 476 sys.exit() 324 477 325 if not continue_loop():326 print "Abort, ok."327 sys.exit()328 329 for videofile in files:478 # if not continue_loop(): 479 # print "Abort, ok." 480 # sys.exit() 481 482 for videofile in videofiles: 330 483 print videofile 331 484 332 485 videofile.create_outdir() 333 videofile.open_log() 334 335 #~ file.parse_streaminfo(txt) 336 process, output = videofile.get_stream_info() 486 487 output = videofile.get_stream_info() 337 488 print "-"*80 338 489 videofile.log("eac2to analyse output: %s" % output.lstrip("- ")) 339 490 videofile.parse_streaminfo(output) 340 491 341 cmd = videofile.get_command() 492 if videofile["streams"]: 493 print "Streams found:", videofile["streams"] 494 else: 495 print "ERROR: No streams found!" 496 continue 497 498 499 500 stream_selection = select_streams(videofiles) 501 print "selected streams:", stream_selection 502 503 for videofile in videofiles: 504 print videofile 505 cmd = videofile.get_command(stream_selection) 506 342 507 videofile.log("run: %s" % cmd) 343 print cmd344 508 if DEBUG: 345 509 print "DEBUG, skip real run..." 346 510 else: 347 511 process, output = subprocess2(cmd) 348 videofile.log("eac2to output: %s" % output.lstrip("- "))512 videofile.log("eac2to output: %s" % output.lstrip("- ")) 349 513 350 514 videofile.close_log() 351 352 break 515 print "-"*79 353 516 354 517 print " -- END -- " -
CodeSnippets/VideoTools/setup.py
r1790 r1797 5 5 """ 6 6 7 from shared.tk_tools import simple_input 7 8 from shared.config import VideoToolsConfig, DEFAULT_CONFIG 9 10 def set_skip_size(cfg): 11 12 raw_skip_size = simple_input( 13 title="Setup m2ts file skip size:", 14 pre_lable="m2ts file skip size:", 15 init_value=cfg["skip_size"], 16 post_lable="(in Bytes)", 17 ) 18 cfg["skip_size"] = int(raw_skip_size) 19 8 20 9 21 if __name__ == "__main__": … … 16 28 # No new config file created, and out dir requested in the past 17 29 cfg.ask_out_dir() 30 31 set_skip_size(cfg) 18 32 33 cfg.save_config() 19 34 cfg.debug() -
CodeSnippets/VideoTools/shared/config.py
r1790 r1797 6 6 from ConfigParser import RawConfigParser 7 7 8 from t ools import askopenfilename2, askdirectory28 from tk_tools import askopenfilename2, askdirectory2 9 9 10 10 … … 14 14 "out_dir": "", 15 15 16 "skip_size": 100 * 1024 * 1024, 16 "glob": "*.m2ts", 17 "skip_size": 200 * 1024 * 1024, 17 18 "stream_dir": "BDMV\\STREAM", 18 19 "out_video_ext": "mkv", … … 39 40 class PickleConfig(dict): 40 41 41 def __init__(self ):42 self. update(DEFAULT_CONFIG)42 def __init__(self, filename, defaults={}): 43 self.filename = filename 43 44 44 if os.path.isfile(CONFIG_FILENAME): 45 self.update(defaults) 46 47 if not os.path.isfile(self.filename): 48 print "Info: Config file '%s' doesn't exist, yet." % self.filename 49 else: 50 print "Reading '%s'..." % self.filename 45 51 try: 46 f = file( CONFIG_FILENAME, "r")52 f = file(filename, "r") 47 53 except IOError, err: 48 54 print "Error reading config:", err … … 51 57 f.close() 52 58 self.update(pickle_data) 59 53 60 54 61 def save_config(self): 55 print "Save config to '%s'..." % CONFIG_FILENAME,56 f = file( CONFIG_FILENAME, "w")62 print "Save '%s'..." % self.filename, 63 f = file(self.filename, "w") 57 64 pickle.dump(self, f) 58 65 f.close() … … 60 67 61 68 def debug(self): 62 print " Debug config:"69 print "PickleConfig debug:" 63 70 pprint(self) 64 71 print "-"*80 … … 68 75 class VideoToolsConfig(PickleConfig): 69 76 def __init__(self): 70 super(VideoToolsConfig, self).__init__( )77 super(VideoToolsConfig, self).__init__(CONFIG_FILENAME, DEFAULT_CONFIG) 71 78 72 79 # Check/set the path to all EXE files … … 97 104 ) 98 105 self.out_dir_set = True 99 self.save_config()100 106 101 107 … … 103 109 from pprint import pprint 104 110 105 CONFIG_FILENAME= "config_test.ini"106 if os.path.isfile( CONFIG_FILENAME):107 os.remove( CONFIG_FILENAME)111 test_config = "config_test.ini" 112 if os.path.isfile(test_config): 113 os.remove(test_config) 108 114 109 115 # Simple Test 110 c = PickleConfig( )116 c = PickleConfig(test_config, DEFAULT_CONFIG) 111 117 c.debug() 112 118 print "-"*80 … … 118 124 print "-"*80 119 125 # Reopen the written ini file: 120 c = PickleConfig( )126 c = PickleConfig(test_config) 121 127 c.debug() -
CodeSnippets/VideoTools/shared/tools.py
r1789 r1797 1 1 # -*- coding: utf-8 -*- 2 2 3 import os, sys 4 5 import Tkinter as tk 6 from tkFileDialog import askopenfilename, askdirectory 7 8 def _ask(method, *args, **kwargs): 9 root = tk.Tk() 10 kwargs["parent"] = root 11 path = method(*args, **kwargs) 12 root.destroy() 13 14 if path == "": # Nothing selected. 15 sys.exit() 16 17 return os.path.normpath(path) 18 19 def askdirectory2(*args, **kwargs): 20 return _ask(askdirectory, *args, **kwargs) 3 import os, sys, string 21 4 22 5 23 def askopenfilename2(*args, **kwargs): 24 return _ask(askopenfilename, *args, **kwargs) 6 def human_filesize(bytes): 7 """ 8 >>> human_filesize(1*1024) 9 '1.0KB' 10 >>> human_filesize(1*1024*1024) 11 '1.0MB' 12 >>> human_filesize(1.5*1024*1024) 13 '1.5MB' 14 """ 15 bytes = float(bytes) 16 for unit in ['bytes','KB','MB','GB','TB']: 17 if bytes < 1000: 18 return "%s%s" % (round(bytes,1), unit) 19 bytes /= 1024.0 20 21 22 23 24 ALLOW_CHARS = string.ascii_letters + string.digits + "+-,." 25 26 def make_slug(txt, join_string=" ", allow_chars=ALLOW_CHARS): 27 """ 28 delete all non-ALLOW_CHARS characters 29 30 >>> make_slug("a test") 31 'a test' 32 >>> make_slug("") 33 '' 34 >>> make_slug("A other test 1*2/3\\4/*/*/5") 35 'A other test 1 2 3 5' 36 """ 37 parts = [""] 38 for char in txt: 39 if char not in allow_chars: 40 if parts[-1] != "": 41 # No double "-" e.g.: "foo - bar" -> "foo-bar" not "foo---bar" 42 parts.append("") 43 else: 44 parts[-1] += char 45 46 item_name = join_string.join(parts) 47 item_name = item_name.strip(join_string) 48 49 return item_name 50 51 52 53 def makeUnique(item_name, name_list, max_no=1000): 54 """ 55 returns a unique shortcut. 56 - delete all non-ALLOW_CHARS characters. 57 - if the shotcut already exists in name_list -> add a sequential number 58 59 >>> makeUnique("two", ["one", "two", "three"]) 60 'two1' 61 >>> makeUnique("two", ["one", "three"]) 62 'two' 63 >>> makeUnique("two", ["one", "two", "two1", "three"]) 64 'two2' 65 >>> makeUnique("", []) 66 '' 67 """ 68 name_list2 = [i.lower() for i in name_list] 69 70 # make double shortcut unique (add a new free sequential number) 71 if item_name.lower() in name_list2: 72 for i in xrange(1, max_no): 73 testname = "%s%i" % (item_name, i) 74 if testname.lower() not in name_list2: 75 item_name = testname 76 break 77 78 return item_name 79 80 81 if __name__ == "__main__": 82 import doctest 83 doctest.testmod(verbose=False) 84 print "DocTest end." -
CodeSnippets/VideoTools/Templatemaker.py
r1790 r1797 8 8 9 9 from shared.config import VideoToolsConfig 10 from shared.t ools import askopenfilename210 from shared.tk_tools import askopenfilename2 11 11 12 12