Push latest changes
This commit is contained in:
		
							parent
							
								
									5543177e5d
								
							
						
					
					
						commit
						3366ce4ec1
					
				
					 5 changed files with 578 additions and 320 deletions
				
			
		|  | @ -2,37 +2,64 @@ import cffi | |||
| import os | ||||
| import sys | ||||
| import time | ||||
| from dvdnav import DVDNav | ||||
| from dvdnav import DVDNav,DVDError | ||||
| from dvdread import DVDRead | ||||
| import subprocess as SP | ||||
| import json | ||||
| from glob import glob | ||||
| import itertools as ITT | ||||
| from vob_demux import demux | ||||
| from ff_d2v import make_d2v | ||||
| def loadlib(dll_path,*includes,**kwargs): | ||||
| 
 | ||||
| def loadlib(dll_path, *includes, **kwargs): | ||||
|     ffi = cffi.FFI() | ||||
|     for include in includes: | ||||
|         ffi.cdef(open(include).read(),kwargs) | ||||
|     return ffi,ffi.dlopen(dll_path) | ||||
|         ffi.cdef(open(include).read(), kwargs) | ||||
|     return ffi, ffi.dlopen(dll_path) | ||||
| 
 | ||||
| os.environ["DVDCSS_VERBOSE"]="2" | ||||
| os.environ["DVDCSS_METHOD"]="disc" | ||||
| for dvd_path in ITT.chain.from_iterable(map(glob,sys.argv[1:])): | ||||
|     r = DVDRead(dvd_path) | ||||
|     # r.grab_ifos() | ||||
|     # r.grab_vobs() | ||||
|     # exit() | ||||
| 
 | ||||
| r=DVDRead(sys.argv[1]) | ||||
| out_folder=os.path.join("out","_".join([r.disc_id,r.udf_disc_name or r.iso_disc_name]).replace(" ","_")) | ||||
| del r | ||||
| os.makedirs(out_folder,exist_ok=True) | ||||
| d=DVDNav(sys.argv[1]) | ||||
| for k,v in d.titles.items(): | ||||
|     v['duration']=v['duration'].total_seconds() | ||||
|     v['chapters']=[c.total_seconds() for c in v['chapters']] | ||||
|     d.titles[k]=v | ||||
|     with open(os.path.join(out_folder,f"{k}.json"),"w") as fh: | ||||
|         json.dump(v,fh) | ||||
|     for a in range(v['angles']): | ||||
|         a+=1 | ||||
|         outfile=os.path.join(out_folder,f"{k}_{a}.vob") | ||||
|         with open(outfile,"wb") as fh: | ||||
|             for block in d.get_blocks(k,a): | ||||
|                 fh.write(block) | ||||
|         demux(outfile) | ||||
|         os.unlink(outfile) | ||||
|     out_folder = os.path.join( | ||||
|         "out", "_".join([r.disc_id, r.udf_disc_name or r.iso_disc_name]).replace(" ", "_") | ||||
|     ) | ||||
|     os.makedirs(out_folder, exist_ok=True) | ||||
|     d = DVDNav(dvd_path) | ||||
|     to_demux = [] | ||||
|     for k, v in d.titles.items(): | ||||
|         v["duration"] = v["duration"].total_seconds() | ||||
|         v["chapters"] = [c.total_seconds() for c in v["chapters"]] | ||||
|         d.titles[k] = v | ||||
|         with open(os.path.join(out_folder, f"{k:03}.json"), "w") as fh: | ||||
|             json.dump(v, fh) | ||||
|         for a in range(0,99): | ||||
|             block=0 | ||||
|             outfile = os.path.join(out_folder, f"t{k:03}_a{a:03}_b{block:03}.vob") | ||||
|             to_demux.append(outfile) | ||||
|             fh = open(outfile, "wb") | ||||
|             try: | ||||
|                 for block in d.get_blocks(k, a): | ||||
|                     if isinstance(block, int): | ||||
|                         outfile = os.path.join(out_folder, f"t{k:03}_a{a:03}_b{block:03}.vob") | ||||
|                         to_demux.append(outfile) | ||||
|                         if fh: | ||||
|                             fh.close() | ||||
|                         fh = open(outfile, "wb") | ||||
|                     else: | ||||
|                         fh.write(block) | ||||
|             except DVDError as e: | ||||
|                 if str(e)!="Invalid angle specified!": | ||||
|                     raise | ||||
|             if fh.tell()==0: | ||||
|                 fh.close() | ||||
|                 os.unlink(fh.name) | ||||
|                 while fh.name in to_demux: | ||||
|                     to_demux.remove(fh.name) | ||||
|     for file in to_demux: | ||||
|         demux(file) | ||||
|         os.unlink(file) | ||||
|     for file in glob(os.path.join(out_folder,"*.m2v")): | ||||
|         make_d2v(file) | ||||
|  |  | |||
							
								
								
									
										315
									
								
								dvdnav.py
									
										
									
									
									
								
							
							
						
						
									
										315
									
								
								dvdnav.py
									
										
									
									
									
								
							|  | @ -3,58 +3,77 @@ import os | |||
| import functools | ||||
| from datetime import timedelta | ||||
| from tqdm import tqdm | ||||
| from dvdread import DVDRead | ||||
| 
 | ||||
| def loadlib(dll_path,*includes,**kwargs): | ||||
| 
 | ||||
| def loadlib(dll_path, *includes, **kwargs): | ||||
|     ffi = cffi.FFI() | ||||
|     for include in includes: | ||||
|         ffi.cdef(open(include).read(),kwargs) | ||||
|     return ffi,ffi.dlopen(dll_path) | ||||
|         ffi.cdef(open(include).read(), kwargs) | ||||
|     return ffi, ffi.dlopen(dll_path) | ||||
| 
 | ||||
| 
 | ||||
| class DVDError(Exception): | ||||
|     pass | ||||
| 
 | ||||
| 
 | ||||
| class DVDNav(object): | ||||
|     def __init__(self,path,verbose=2,method="disc"): | ||||
|         os.environ["DVDCSS_VERBOSE"]=str(verbose) | ||||
|         os.environ["DVDCSS_METHOD"]=method | ||||
|         self.dvd=None | ||||
|         self.ffi,self.lib = loadlib("libdvdnav-4.dll", | ||||
|     def __init__(self, path, verbose=None, method="disc"): | ||||
|         if verbose is None: | ||||
|             os.environ.pop("DVDCSS_VERBOSE", None) | ||||
|         else: | ||||
|             os.environ["DVDCSS_VERBOSE"] = str(verbose) | ||||
|         os.environ["DVDCSS_METHOD"] = method | ||||
|         self.dvd = None | ||||
|         self.ffi, self.lib = loadlib( | ||||
|             "libdvdnav-4.dll", | ||||
|             "dvd_types.h", | ||||
|             "dvd_reader.h", | ||||
|             "ifo_types.h", | ||||
|             "nav_types.h", | ||||
|             "dvdnav_events.h", | ||||
|             "dvdnav.h") | ||||
|         self.path=path | ||||
|         self.titles={} | ||||
|             "dvdnav.h", | ||||
|             pack=True, | ||||
|         ) | ||||
|         self.path = path | ||||
|         self.titles = {} | ||||
|         self.open(path) | ||||
| 
 | ||||
|     def __del__(self): | ||||
|         self.__check_error(self.lib.dvdnav_close(self.dvd)) | ||||
|         self.dvd=None | ||||
|         self.dvd = None | ||||
| 
 | ||||
|     def __repr__(self): | ||||
|         return "<DVD Path={0.path} Title={0.title} Serial={0.serial}".format(self) | ||||
| 
 | ||||
|     def get_blocks(self,title,angle=1,slang=None): | ||||
|         self.__check_error(self.lib.dvdnav_set_PGC_positioning_flag(self.dvd,1)) | ||||
|         self.__check_error(self.lib.dvdnav_title_play(self.dvd,title)) | ||||
|         self.__check_error(self.lib.dvdnav_angle_change(self.dvd,angle)) | ||||
|     def get_blocks(self, title, angle=1, slang=None): | ||||
|         self.__check_error(self.lib.dvdnav_set_PGC_positioning_flag(self.dvd, 1)) | ||||
|         self.__check_error(self.lib.dvdnav_title_play(self.dvd, title)) | ||||
|         curr_angle = self.ffi.new("int32_t*", 0) | ||||
|         num_angles = self.ffi.new("int32_t*", 0) | ||||
|         self.__check_error( | ||||
|             self.lib.dvdnav_get_angle_info(self.dvd, curr_angle, num_angles) | ||||
|         ) | ||||
|         if angle != 0: | ||||
|             if angle < 1 or angle > num_angles[0]: | ||||
|                 raise DVDError("Invalid angle specified!") | ||||
|             if angle != curr_angle[0]: | ||||
|                 self.__check_error(self.lib.dvdnav_angle_change(self.dvd, angle)) | ||||
|         if slang is not None: | ||||
|             self.__check_error(self.lib.dvdnav_spu_language_select(self.dvd,slang)) | ||||
|         event=self.lib.DVDNAV_NOP | ||||
|         buf=self.ffi.new("char[]",4096) | ||||
|         ev=self.ffi.new("int32_t*",self.lib.DVDNAV_NOP) | ||||
|         size=self.ffi.new("int32_t*",0) | ||||
|         pos=self.ffi.new("uint32_t*",0) | ||||
|         total_size=self.ffi.new("uint32_t*",0) | ||||
|             self.__check_error(self.lib.dvdnav_spu_language_select(self.dvd, slang)) | ||||
|         event = self.lib.DVDNAV_NOP | ||||
|         buf = self.ffi.new("char[]", 4096) | ||||
|         ev = self.ffi.new("int32_t*", self.lib.DVDNAV_NOP) | ||||
|         size = self.ffi.new("int32_t*", 0) | ||||
|         pos = self.ffi.new("uint32_t*", 0) | ||||
|         total_size = self.ffi.new("uint32_t*", 0) | ||||
|         domains = { | ||||
|             1: "FirstPlay", | ||||
|             2: "VTSTitle", | ||||
|             4: "VMGM", | ||||
|             8: "VTSMenu" | ||||
|             8: "VTSMenu", | ||||
|         } | ||||
|         events={ | ||||
|         events = { | ||||
|             0: "DVDNAV_BLOCK_OK", | ||||
|             1: "DVDNAV_NOP", | ||||
|             2: "DVDNAV_STILL_FRAME", | ||||
|  | @ -67,146 +86,180 @@ class DVDNav(object): | |||
|             9: "DVDNAV_HIGHLIGHT", | ||||
|             10: "DVDNAV_SPU_CLUT_CHANGE", | ||||
|             12: "DVDNAV_HOP_CHANNEL", | ||||
|             13: "DVDNAV_WAIT" | ||||
|             13: "DVDNAV_WAIT", | ||||
|         } | ||||
|         progbar=tqdm(unit_divisor=1024,unit_scale=True,unit="iB",desc="Ripping DVD") | ||||
|         progbar = tqdm( | ||||
|             unit_divisor=1024, | ||||
|             unit_scale=True, | ||||
|             unit="iB", | ||||
|             desc="Ripping DVD", | ||||
|             disable=False, | ||||
|         ) | ||||
|         ripped = set() | ||||
|         current_vts = None | ||||
|         current_cell = None | ||||
|         current_pg = None | ||||
|         while True: | ||||
|             self.__check_error(self.lib.dvdnav_get_next_block(self.dvd,buf,ev,size)) | ||||
|             if self.lib.dvdnav_get_position(self.dvd,pos,total_size)==self.lib.DVDNAV_STATUS_OK: | ||||
|                 progbar.total=total_size[0]*2048 | ||||
|                 progbar.n=max(progbar.n,min(progbar.total,pos[0]*2048)) | ||||
|             self.__check_error(self.lib.dvdnav_get_next_block(self.dvd, buf, ev, size)) | ||||
|             if ( | ||||
|                 self.lib.dvdnav_get_position(self.dvd, pos, total_size) | ||||
|                 == self.lib.DVDNAV_STATUS_OK | ||||
|             ): | ||||
|                 progbar.total = total_size[0] * 2048 | ||||
|                 progbar.n = max(progbar.n, min(progbar.total, pos[0] * 2048)) | ||||
|                 progbar.update(0) | ||||
|                 progbar.set_postfix( | ||||
|                     vts=current_vts, | ||||
|                     cell=current_cell, | ||||
|                     pg=current_pg, | ||||
|                     angle=angle, | ||||
|                     title=title, | ||||
|                 ) | ||||
|             # print("Got event:",events.get(ev[0],ev[0]),size[0]) | ||||
|             if ev[0] in [self.lib.DVDNAV_SPU_CLUT_CHANGE,self.lib.DVDNAV_HOP_CHANNEL,self.lib.DVDNAV_NOP]: | ||||
|             if ev[0] in [ | ||||
|                 self.lib.DVDNAV_SPU_CLUT_CHANGE, | ||||
|                 self.lib.DVDNAV_HOP_CHANNEL, | ||||
|                 self.lib.DVDNAV_NOP, | ||||
|                 self.lib.DVDNAV_HIGHLIGHT, | ||||
|             ]: | ||||
|                 continue | ||||
|             elif ev[0]==self.lib.DVDNAV_BLOCK_OK: | ||||
|                 # print("Read",size[0]) | ||||
|                 yield self.ffi.buffer(buf,size[0])[:] | ||||
|             elif ev[0]==self.lib.DVDNAV_STOP: | ||||
|             elif ev[0] == self.lib.DVDNAV_BLOCK_OK: | ||||
|                 yield self.ffi.buffer(buf, size[0])[:] | ||||
|             elif ev[0] == self.lib.DVDNAV_STOP: | ||||
|                 progbar.write(f"[{title}|{angle}] Stop") | ||||
|                 break | ||||
|             elif ev[0]==self.lib.DVDNAV_NAV_PACKET: | ||||
|                 nav=self.lib.dvdnav_get_current_nav_pci(self.dvd) | ||||
|                 # print("PTS:",timedelta(seconds=nav.pci_gi.vobu_s_ptm/90000)) | ||||
|             elif ev[0]==self.lib.DVDNAV_STILL_FRAME: | ||||
|                 # print("Still") | ||||
|             elif ev[0] == self.lib.DVDNAV_NAV_PACKET: | ||||
|                 pass | ||||
|             elif ev[0] == self.lib.DVDNAV_STILL_FRAME: | ||||
|                 self.__check_error(self.lib.dvdnav_still_skip(self.dvd)) | ||||
|             elif ev[0]==self.lib.DVDNAV_WAIT: | ||||
|                 # print("Wait",size[0]) | ||||
|             elif ev[0] == self.lib.DVDNAV_WAIT: | ||||
|                 self.__check_error(self.lib.dvdnav_wait_skip(self.dvd)) | ||||
|             elif ev[0]==self.lib.DVDNAV_SPU_STREAM_CHANGE: | ||||
|                 spu=self.ffi.cast("dvdnav_spu_stream_change_event_t*",buf) | ||||
|                 progbar.write(f"[{title}|{angle}] SPU: Wide: {spu.physical_wide} Letterbox: {spu.physical_letterbox} Pan&Scan: {spu.physical_pan_scan} Logical: {spu.logical}") | ||||
|             elif ev[0]==self.lib.DVDNAV_AUDIO_STREAM_CHANGE: | ||||
|                 audio=self.ffi.cast("dvdnav_audio_stream_change_event_t*",buf) | ||||
|                 progbar.write(f"[{title}|{angle}] Audio: Physical: {audio.physical} Logical: {audio.logical}") | ||||
|             elif ev[0]==self.lib.DVDNAV_CELL_CHANGE: | ||||
|                 cell=self.ffi.cast("dvdnav_cell_change_event_t*",buf) | ||||
|                 progbar.write(f"[{title}|{angle}] Cell: {cell.cellN} ({cell.cell_start}-{cell.cell_start+cell.cell_length}), PG: {cell.pgN} ({cell.pg_start}-{cell.pg_start+cell.pg_length})") | ||||
|             elif ev[0]==self.lib.DVDNAV_VTS_CHANGE: | ||||
|                 vts=self.ffi.cast("dvdnav_vts_change_event_t*",buf) | ||||
|                 old_domain=sorted(domains[k] for k in domains if vts.old_domain&k) | ||||
|                 new_domain=sorted(domains[k] for k in domains if vts.new_domain&k) | ||||
|                 progbar.write(f"[{title}|{angle}] VTS: {vts.old_vtsN} ({vts.old_domain} {old_domain}) -> {vts.new_vtsN} ({vts.new_domain} {new_domain})") | ||||
|                 if vts.new_domain==8: # back to menu | ||||
|             elif ev[0] == self.lib.DVDNAV_SPU_STREAM_CHANGE: | ||||
|                 pass | ||||
|             elif ev[0] == self.lib.DVDNAV_AUDIO_STREAM_CHANGE: | ||||
|                 audio = self.ffi.cast("dvdnav_audio_stream_change_event_t*", buf) | ||||
|             elif ev[0] == self.lib.DVDNAV_CELL_CHANGE: | ||||
|                 cell = self.ffi.cast("dvdnav_cell_change_event_t*", buf) | ||||
|                 current_cell = cell.cellN | ||||
|                 current_pg = cell.pgN | ||||
|                 progbar.write( | ||||
|                     f"[{title}|{angle}] Cell: {cell.cellN} ({cell.cell_start}-{cell.cell_start+cell.cell_length}), PG: {cell.pgN} ({cell.pg_start}-{cell.pg_start+cell.pg_length})" | ||||
|                 ) | ||||
|             elif ev[0] == self.lib.DVDNAV_VTS_CHANGE: | ||||
|                 vts = self.ffi.cast("dvdnav_vts_change_event_t*", buf) | ||||
|                 new_vts = (vts.new_vtsN, vts.new_domain) | ||||
|                 ripped.add((vts.old_vtsN, vts.old_domain)) | ||||
|                 # progbar.write(f"[{title}|{angle}] VTS: {vts.old_vtsN} ({vts.old_domain} {old_domain}) -> {vts.new_vtsN} ({vts.new_domain} {new_domain})") | ||||
|                 if new_vts in ripped:  # looped | ||||
|                     progbar.write(f"[{title}|{angle}] Looped!") | ||||
|                     break | ||||
|                 current_vts = (vts.new_vtsN, vts.new_domain) | ||||
|                 if vts.new_domain == 8:  # back to menu | ||||
|                     progbar.write(f"[{title}|{angle}] Back to menu!") | ||||
|                     break | ||||
|                 yield vts.new_vtsN | ||||
|             else: | ||||
|                 progbar.write(f"[{title}|{angle}] Unhandled: {events.get(ev[0],ev[0])} {size[0]}") | ||||
|                 progbar.write( | ||||
|                     f"[{title}|{angle}] Unhandled: {events.get(ev[0],ev[0])} {size[0]}" | ||||
|                 ) | ||||
|         self.__check_error(self.lib.dvdnav_stop(self.dvd)) | ||||
| 
 | ||||
| 
 | ||||
|     def __check_error(self,ret): | ||||
|         if ret==self.lib.DVDNAV_STATUS_ERR: | ||||
|     def __check_error(self, ret): | ||||
|         if ret == self.lib.DVDNAV_STATUS_ERR: | ||||
|             if self.dvd: | ||||
|                 err=self.ffi.string(self.lib.dvdnav_err_to_string(self.dvd)) | ||||
|                 err = self.ffi.string(self.lib.dvdnav_err_to_string(self.dvd)) | ||||
|                 raise DVDError(err) | ||||
|             raise DVDError("Unknown error") | ||||
| 
 | ||||
|     def __get_titles(self): | ||||
|         titles=self.ffi.new("int32_t*",0) | ||||
|         p_times=self.ffi.new("uint64_t[]",512) | ||||
|         times=self.ffi.new("uint64_t**",p_times) | ||||
|         duration=self.ffi.new("uint64_t*",0) | ||||
|         titles=self.ffi.new("int32_t*",0) | ||||
|         self.lib.dvdnav_get_number_of_titles(self.dvd,titles) | ||||
|         num_titles=titles[0] | ||||
|         for title in range(0,num_titles+1): | ||||
|             if self.lib.dvdnav_get_number_of_parts(self.dvd,title,titles)==0: | ||||
|         titles = self.ffi.new("int32_t*", 0) | ||||
|         p_times = self.ffi.new("uint64_t[]", 512) | ||||
|         times = self.ffi.new("uint64_t**", p_times) | ||||
|         duration = self.ffi.new("uint64_t*", 0) | ||||
|         titles = self.ffi.new("int32_t*", 0) | ||||
|         self.lib.dvdnav_get_number_of_titles(self.dvd, titles) | ||||
|         num_titles = titles[0] | ||||
|         for title in range(0, num_titles + 1): | ||||
|             if self.lib.dvdnav_get_number_of_parts(self.dvd, title, titles) == 0: | ||||
|                 continue | ||||
|             num_parts=titles[0] | ||||
|             self.lib.dvdnav_get_number_of_angles(self.dvd,title,titles) | ||||
|             num_angles=titles[0] | ||||
|             num_chapters=self.lib.dvdnav_describe_title_chapters(self.dvd,title,times,duration) | ||||
|             if duration[0]==0: | ||||
|             num_parts = titles[0] | ||||
|             self.lib.dvdnav_get_number_of_angles(self.dvd, title, titles) | ||||
|             num_angles = titles[0] | ||||
|             num_chapters = self.lib.dvdnav_describe_title_chapters( | ||||
|                 self.dvd, title, times, duration | ||||
|             ) | ||||
|             if duration[0] == 0: | ||||
|                 continue | ||||
|             chapters=[] | ||||
|             chapters = [] | ||||
|             for t in range(num_chapters): | ||||
|                 chapters.append(timedelta(seconds=times[0][t]/90000)) | ||||
|             self.titles[title]={ | ||||
|                 'parts':num_parts, | ||||
|                 'angles': num_angles, | ||||
|                 'duration': timedelta(seconds=duration[0]/90000), | ||||
|                 'chapters': chapters | ||||
|                 chapters.append(timedelta(seconds=times[0][t] / 90000)) | ||||
|             self.titles[title] = { | ||||
|                 "parts": num_parts, | ||||
|                 "angles": num_angles, | ||||
|                 "duration": timedelta(seconds=duration[0] / 90000), | ||||
|                 "chapters": chapters, | ||||
|             } | ||||
| 
 | ||||
|     def __get_info(self): | ||||
|         s=self.ffi.new("char**",self.ffi.NULL) | ||||
|         self.lib.dvdnav_get_title_string(self.dvd,s) | ||||
|         self.title=str(self.ffi.string(s[0]),"utf8").strip() or None | ||||
|         self.lib.dvdnav_get_serial_string(self.dvd,s) | ||||
|         self.serial=str(self.ffi.string(s[0]),"utf8").strip() or None | ||||
|         s = self.ffi.new("char**", self.ffi.NULL) | ||||
|         self.lib.dvdnav_get_title_string(self.dvd, s) | ||||
|         self.title = str(self.ffi.string(s[0]), "utf8").strip() or None | ||||
|         self.lib.dvdnav_get_serial_string(self.dvd, s) | ||||
|         self.serial = str(self.ffi.string(s[0]), "utf8").strip() or None | ||||
|         self.__get_titles() | ||||
| 
 | ||||
|     def open(self,path): | ||||
|         audio_attrs=self.ffi.new("audio_attr_t*") | ||||
|         spu_attr=self.ffi.new("subp_attr_t*") | ||||
|         dvdnav=self.ffi.new("dvdnav_t**",self.ffi.cast("dvdnav_t*",0)) | ||||
|         self.__check_error(self.lib.dvdnav_open(dvdnav,bytes(path,"utf8"))) | ||||
|         self.dvd=dvdnav[0] | ||||
|         self.__check_error(self.lib.dvdnav_set_readahead_flag(self.dvd,1)) | ||||
|     def open(self, path): | ||||
|         audio_attrs = self.ffi.new("audio_attr_t*") | ||||
|         spu_attr = self.ffi.new("subp_attr_t*") | ||||
|         dvdnav = self.ffi.new("dvdnav_t**", self.ffi.cast("dvdnav_t*", 0)) | ||||
|         self.__check_error(self.lib.dvdnav_open(dvdnav, bytes(path, "utf8"))) | ||||
|         self.dvd = dvdnav[0] | ||||
|         self.__check_error(self.lib.dvdnav_set_readahead_flag(self.dvd, 1)) | ||||
|         self.__get_info() | ||||
|         for title in self.titles: | ||||
|             self.__check_error(self.lib.dvdnav_title_play(self.dvd,title)) | ||||
|             self.titles[title]['audio']={} | ||||
|             self.titles[title]['subtitles']={} | ||||
|             self.__check_error(self.lib.dvdnav_title_play(self.dvd, title)) | ||||
|             self.titles[title]["audio"] = {} | ||||
|             self.titles[title]["subtitles"] = {} | ||||
|             for n in range(255): | ||||
|                 stream_id=self.lib.dvdnav_get_audio_logical_stream(self.dvd,n) | ||||
|                 if stream_id==-1: | ||||
|                 stream_id = self.lib.dvdnav_get_audio_logical_stream(self.dvd, n) | ||||
|                 if stream_id == -1: | ||||
|                     continue | ||||
|                 self.__check_error(self.lib.dvdnav_get_audio_attr(self.dvd,stream_id,audio_attrs)) | ||||
|                 alang=None | ||||
|                 self.__check_error( | ||||
|                     self.lib.dvdnav_get_audio_attr(self.dvd, stream_id, audio_attrs) | ||||
|                 ) | ||||
|                 alang = None | ||||
|                 if audio_attrs.lang_type: | ||||
|                     alang = str(audio_attrs.lang_code.to_bytes(2,'big'),"utf8") | ||||
|                 channels = audio_attrs.channels+1 | ||||
|                 codec = { | ||||
|                     0: 'ac3', | ||||
|                     2: 'mpeg1', | ||||
|                     3: 'mpeg-2ext', | ||||
|                     4: 'lpcm', | ||||
|                     6: 'dts' | ||||
|                 }[audio_attrs.audio_format] | ||||
|                 audio_type={ | ||||
|                     alang = str(audio_attrs.lang_code.to_bytes(2, "big"), "utf8") | ||||
|                 channels = audio_attrs.channels + 1 | ||||
|                 codec = {0: "ac3", 2: "mpeg1", 3: "mpeg-2ext", 4: "lpcm", 6: "dts"}[ | ||||
|                     audio_attrs.audio_format | ||||
|                 ] | ||||
|                 audio_type = { | ||||
|                     0: None, | ||||
|                     1: 'normal', | ||||
|                     2: 'descriptive', | ||||
|                     1: "normal", | ||||
|                     2: "descriptive", | ||||
|                     3: "director's commentary", | ||||
|                     4: "alternate director's commentary" | ||||
|                     4: "alternate director's commentary", | ||||
|                 }[audio_attrs.code_extension] | ||||
|                 self.titles[title]['audio'][n]={ | ||||
|                     'stream_id': stream_id, | ||||
|                     'lang':alang, | ||||
|                     'channels': channels, | ||||
|                     'codec': codec, | ||||
|                     'type': audio_type | ||||
|                 self.titles[title]["audio"][n] = { | ||||
|                     "stream_id": stream_id, | ||||
|                     "lang": alang, | ||||
|                     "channels": channels, | ||||
|                     "codec": codec, | ||||
|                     "type": audio_type, | ||||
|                 } | ||||
|             for n in range(255): | ||||
|                 stream_id=self.lib.dvdnav_get_spu_logical_stream(self.dvd,n) | ||||
|                 if stream_id==-1: | ||||
|                 stream_id = self.lib.dvdnav_get_spu_logical_stream(self.dvd, n) | ||||
|                 if stream_id == -1: | ||||
|                     continue | ||||
|                 self.__check_error(self.lib.dvdnav_get_spu_attr(self.dvd,stream_id,spu_attr)) | ||||
|                 self.__check_error( | ||||
|                     self.lib.dvdnav_get_spu_attr(self.dvd, stream_id, spu_attr) | ||||
|                 ) | ||||
|                 slang = None | ||||
|                 if spu_attr.type==1: | ||||
|                     slang = str(spu_attr.lang_code.to_bytes(2,'big'),"utf8") | ||||
|                 self.titles[title]['subtitles'][n]={ | ||||
|                     'stream_id': stream_id, | ||||
|                     'lang':slang | ||||
|                 if spu_attr.type == 1: | ||||
|                     slang = str(spu_attr.lang_code.to_bytes(2, "big"), "utf8") | ||||
|                 self.titles[title]["subtitles"][n] = { | ||||
|                     "stream_id": stream_id, | ||||
|                     "lang": slang, | ||||
|                 } | ||||
|         self.__check_error(self.lib.dvdnav_stop(self.dvd)) | ||||
|         self.__check_error(self.lib.dvdnav_stop(self.dvd)) | ||||
|  |  | |||
							
								
								
									
										160
									
								
								dvdread.py
									
										
									
									
									
								
							
							
						
						
									
										160
									
								
								dvdread.py
									
										
									
									
									
								
							|  | @ -4,16 +4,24 @@ import functools | |||
| import binascii | ||||
| from datetime import timedelta | ||||
| 
 | ||||
| def loadlib(dll_path,*includes,**kwargs): | ||||
| 
 | ||||
| def loadlib(dll_path, *includes, **kwargs): | ||||
|     ffi = cffi.FFI() | ||||
|     for include in includes: | ||||
|         ffi.cdef(open(include).read(),**kwargs) | ||||
|     return ffi,ffi.dlopen(dll_path) | ||||
|         ffi.cdef(open(include).read(), **kwargs) | ||||
|     return ffi, ffi.dlopen(dll_path) | ||||
| 
 | ||||
| 
 | ||||
| class DVDRead(object): | ||||
|     def __init__(self,path): | ||||
|         self.dvd=None | ||||
|         self.ffi,self.lib = loadlib("libdvdread-8.dll", | ||||
|     def __init__(self, path, verbose="0", method="disc"): | ||||
|         if verbose is None: | ||||
|             os.environ.pop("DVDCSS_VERBOSE", None) | ||||
|         else: | ||||
|             os.environ["DVDCSS_VERBOSE"] = str(verbose) | ||||
|         os.environ["DVDCSS_METHOD"] = method | ||||
|         self.dvd = None | ||||
|         self.ffi, self.lib = loadlib( | ||||
|             "libdvdread-8.dll", | ||||
|             "dvd_types.h", | ||||
|             "dvd_reader.h", | ||||
|             "ifo_types.h", | ||||
|  | @ -21,23 +29,137 @@ class DVDRead(object): | |||
|             "ifo_print.h", | ||||
|             "nav_types.h", | ||||
|             "nav_read.h", | ||||
|             "nav_print.h") | ||||
|         self.path=path | ||||
|         self.titles={} | ||||
|             "nav_print.h", | ||||
|             packed=True, | ||||
|         ) | ||||
|         self.path = path | ||||
|         self.titles = {} | ||||
|         self.open(path) | ||||
|         self.lb_len=self.lib.DVD_VIDEO_LB_LEN | ||||
| 
 | ||||
|     def grab_ifo(self,title,bup=False): | ||||
|         from tqdm import tqdm | ||||
|         buf = self.ffi.new("unsigned char[]", 512) | ||||
|         if bup: | ||||
|             fh = self.lib.DVDOpenFile(self.dvd, title, self.lib.DVD_READ_INFO_BACKUP_FILE) | ||||
|         else: | ||||
|             fh = self.lib.DVDOpenFile(self.dvd, title, self.lib.DVD_READ_INFO_FILE) | ||||
|         total_size = self.lib.DVDFileSize(fh)*self.lb_len | ||||
|         remaining = total_size | ||||
|         num_read = True | ||||
|         pbar = tqdm(total=total_size, unit="iB", unit_scale=True, unit_divisor=1024,leave=False) | ||||
|         while num_read: | ||||
|             num_read=self.lib.DVDReadBytes( fh, buf, 512) | ||||
|             num_read=min(num_read,remaining) | ||||
|             remaining-=num_read | ||||
|             pbar.update(num_read) | ||||
|             yield self.ffi.buffer(buf,num_read)[:] | ||||
|         self.lib.DVDCloseFile(fh) | ||||
| 
 | ||||
|     def grab_ifos(self): | ||||
|         vmg_ifo = self.lib.ifoOpen(self.dvd, 0) | ||||
|         if vmg_ifo == self.ffi.NULL: | ||||
|             return | ||||
|         title_sets = vmg_ifo.vts_atrt.nr_of_vtss | ||||
|         for t in range(1,title_sets + 1): | ||||
|             vts = self.lib.ifoOpen(self.dvd, t) | ||||
|             if vts == self.ffi.NULL: | ||||
|                 continue | ||||
|             self.lib.ifoClose(vts) | ||||
|             outfile=os.path.join("RIP",f"VTS_{t:02}_0.ifo") | ||||
|             with open(outfile, "wb") as out_ifo: | ||||
|                 for block in self.grab_ifo(t,bup=False): | ||||
|                     out_ifo.write(block) | ||||
|             outfile=os.path.join("RIP",f"VTS_{t:02}_0.bup") | ||||
|             with open(outfile, "wb") as out_ifo: | ||||
|                 for block in self.grab_ifo(t,bup=True): | ||||
|                     out_ifo.write(block) | ||||
|         self.lib.ifoClose(vmg_ifo) | ||||
| 
 | ||||
|     def grab_vob(self,title): | ||||
|         from tqdm import tqdm | ||||
|         buf = self.ffi.new("unsigned char[]", 512 * self.lb_len) | ||||
|         fh = self.lib.DVDOpenFile(self.dvd, title, self.lib.DVD_READ_TITLE_VOBS) | ||||
|         total_size = self.lib.DVDFileSize(fh)*self.lb_len | ||||
|         remaining = total_size | ||||
|         num_read = True | ||||
|         pos=0 | ||||
|         pbar = tqdm(total=total_size, unit="iB", unit_scale=True, unit_divisor=1024,leave=False) | ||||
|         while remaining: | ||||
|             num_read=self.lib.DVDReadBlocks( fh,pos, 512, buf) | ||||
|             if num_read<0: | ||||
|                 raise RuntimeError("Error reading!") | ||||
|             num_read_bytes=num_read*self.lb_len | ||||
|             num_read_bytes=min(num_read_bytes,remaining) | ||||
|             remaining-=num_read_bytes | ||||
|             pbar.update(num_read_bytes) | ||||
|             yield self.ffi.buffer(buf,num_read_bytes)[:] | ||||
|             pos+=num_read | ||||
|         pbar.close() | ||||
|         self.lib.DVDCloseFile(fh) | ||||
| 
 | ||||
|     def grab_vobs(self): | ||||
|         vmg_ifo = self.lib.ifoOpen(self.dvd, 0) | ||||
|         if vmg_ifo == self.ffi.NULL: | ||||
|             return | ||||
|         title_sets = vmg_ifo.vts_atrt.nr_of_vtss | ||||
|         for t in range(1,title_sets + 1): | ||||
|             vts = self.lib.ifoOpen(self.dvd, t) | ||||
|             if vts == self.ffi.NULL: | ||||
|                 continue | ||||
|             self.lib.ifoClose(vts) | ||||
|             outfile=os.path.join("RIP",f"VTS_{t:02}_0.vob") | ||||
|             with open(outfile, "wb") as out_ifo: | ||||
|                 for block in self.grab_vob(t): | ||||
|                     out_ifo.write(block) | ||||
|         self.lib.ifoClose(vmg_ifo) | ||||
| 
 | ||||
| 
 | ||||
|     def test(self): | ||||
|         from tqdm import tqdm | ||||
|         fn = 0 | ||||
|         chunk_size = 2048 | ||||
|         buf = self.ffi.new("unsigned char[]", chunk_size * 2048) | ||||
|         for fn in range(title_sets + 1): | ||||
|             pos = 0 | ||||
|             fh = self.lib.DVDOpenFile(self.dvd, fn, self.lib.DVD_READ_TITLE_VOBS) | ||||
|             if fh: | ||||
|                 total_size = self.lib.DVDFileSize(fh) | ||||
|                 if total_size == -1: | ||||
|                     self.lib.DVDCloseFile(fh) | ||||
|                     break | ||||
|                 pbar = tqdm(total=total_size * 2048, unit="iB", unit_scale=True, unit_divisor=1024,leave=False) | ||||
|                 last=False | ||||
|                 with open(f"out_{fn}.vob", "wb") as out_vob: | ||||
|                     while True: | ||||
|                         if (pos+chunk_size)>total_size: | ||||
|                             chunk_size=total_size-pos | ||||
|                         count = self.lib.DVDReadBlocks(fh, pos, chunk_size, buf) | ||||
|                         if count == -1: | ||||
|                             break | ||||
|                         pbar.update( | ||||
|                             out_vob.write(self.ffi.buffer(buf, count * 2048)[:]) | ||||
|                         ) | ||||
|                         pos += count | ||||
|                         if pos>=total_size: | ||||
|                             break | ||||
|                 self.lib.DVDCloseFile(fh) | ||||
|             fn += 1 | ||||
|             if fn>200: | ||||
|                 break | ||||
| 
 | ||||
|     def __del__(self): | ||||
|         if self.dvd: | ||||
|             self.lib.DVDClose(self.dvd) | ||||
| 
 | ||||
|     def open(self,path): | ||||
|     def open(self, path): | ||||
|         # self.dvd_css=self.css_lib.dvdcss_open() | ||||
|         self.dvd=self.lib.DVDOpen(bytes(path,"utf8")) | ||||
|         vol_id=self.ffi.new("unsigned char[]",32) | ||||
|         self.lib.DVDDiscID(self.dvd,vol_id) | ||||
|         self.disc_id=str(binascii.hexlify(self.ffi.buffer(vol_id,16)[:]),"utf8") | ||||
|         self.lib.DVDUDFVolumeInfo(self.dvd,vol_id,32,self.ffi.NULL,0) | ||||
|         self.udf_disc_name=str(self.ffi.string(vol_id),"utf8") | ||||
|         self.lib.DVDISOVolumeInfo(self.dvd,vol_id,32,self.ffi.NULL,0) | ||||
|         self.iso_disc_name=str(self.ffi.string(vol_id),"utf8") | ||||
|         self.ffi.release(vol_id) | ||||
|         self.dvd = self.lib.DVDOpen(bytes(path, "utf8")) | ||||
|         vol_id = self.ffi.new("unsigned char[]", 32) | ||||
|         self.lib.DVDDiscID(self.dvd, vol_id) | ||||
|         self.disc_id = str(binascii.hexlify(self.ffi.buffer(vol_id, 16)[:]), "utf8") | ||||
|         self.lib.DVDUDFVolumeInfo(self.dvd, vol_id, 32, self.ffi.NULL, 0) | ||||
|         self.udf_disc_name = str(self.ffi.string(vol_id), "utf8") | ||||
|         self.lib.DVDISOVolumeInfo(self.dvd, vol_id, 32, self.ffi.NULL, 0) | ||||
|         self.iso_disc_name = str(self.ffi.string(vol_id), "utf8") | ||||
|         self.ffi.release(vol_id) | ||||
|  |  | |||
							
								
								
									
										236
									
								
								ff_d2v.py
									
										
									
									
									
								
							
							
						
						
									
										236
									
								
								ff_d2v.py
									
										
									
									
									
								
							|  | @ -6,7 +6,7 @@ import itertools as ITT | |||
| from tqdm import tqdm | ||||
| 
 | ||||
| 
 | ||||
| colorspace={ | ||||
| colorspace = { | ||||
|     "gbr": 0, | ||||
|     "bt709": 1, | ||||
|     "unknown": 2, | ||||
|  | @ -23,151 +23,183 @@ colorspace={ | |||
|     "ictcp": 14, | ||||
| } | ||||
| 
 | ||||
| pict_types={ | ||||
|     'I':0b01, | ||||
|     'P':0b10, | ||||
|     'B':0b11 | ||||
| } | ||||
| pict_types = {"I": 0b01, "P": 0b10, "B": 0b11} | ||||
| 
 | ||||
| 
 | ||||
| def make_info(frames): | ||||
|     has_interlaced = any(frame['interlaced_frame'] for frame in frames) | ||||
|     new_gop='timecode' in frames[0].get('tags',{}) | ||||
|     info=0x000 | ||||
|     info|=1<<11 # always 1 | ||||
|     info|=0<<10 # 0=Closed GOP, 1=Open GOP | ||||
|     info|=(not has_interlaced)<<9 # Progressive | ||||
|     info|=new_gop<<8 | ||||
|     has_interlaced = any(frame["interlaced_frame"] for frame in frames) | ||||
|     new_gop = "timecode" in frames[0].get("tags", {}) | ||||
|     info = 0x000 | ||||
|     info |= 1 << 11  # always 1 | ||||
|     info |= 0 << 10  # 0=Closed GOP, 1=Open GOP | ||||
|     info |= (not has_interlaced) << 9  # Progressive | ||||
|     info |= new_gop << 8 | ||||
|     return info | ||||
| 
 | ||||
| 
 | ||||
| def make_flags(frames): | ||||
|     flags=[] | ||||
|     flags = [] | ||||
|     for frame in frames: | ||||
|         needs_prev=False | ||||
|         progressive=not int(frame['interlaced_frame']) | ||||
|         pt=pict_types[frame['pict_type']] | ||||
|         reserved=0b00 | ||||
|         tff=int(frame['top_field_first']) | ||||
|         rff=int(frame['repeat_pict']) | ||||
|         flag=0b0 | ||||
|         flag|=(not needs_prev)<<7 | ||||
|         flag|=progressive<<6 | ||||
|         flag|=pt<<4 | ||||
|         flag|=reserved<<2 | ||||
|         flag|=tff<<1 | ||||
|         flag|=rff | ||||
|         needs_prev = False | ||||
|         progressive = not int(frame["interlaced_frame"]) | ||||
|         pt = pict_types[frame["pict_type"]] | ||||
|         reserved = 0b00 | ||||
|         tff = int(frame["top_field_first"]) | ||||
|         rff = int(frame["repeat_pict"]) | ||||
|         flag = 0b0 | ||||
|         flag |= (not needs_prev) << 7 | ||||
|         flag |= progressive << 6 | ||||
|         flag |= pt << 4 | ||||
|         flag |= reserved << 2 | ||||
|         flag |= tff << 1 | ||||
|         flag |= rff | ||||
|         flags.append(f"{flag:02x}") | ||||
|     return flags | ||||
| 
 | ||||
| 
 | ||||
| def make_line(frames,stream): | ||||
|     info=f"{make_info(frames):03x}" | ||||
|     matrix=colorspace[stream['color_space']] | ||||
|     file=0 | ||||
|     position=frames[0]['pkt_pos'] | ||||
|     skip=0 | ||||
|     vob=0 | ||||
|     cell=0 | ||||
|     flags=make_flags(frames) | ||||
|     return " ".join(map(str,[info,matrix,file,position,skip,vob,cell,*flags])) | ||||
| def make_line(frames, stream): | ||||
|     info = f"{make_info(frames):03x}" | ||||
|     matrix = colorspace[stream["color_space"]] | ||||
|     file = 0 | ||||
|     position = frames[0]["pkt_pos"] | ||||
|     skip = 0 | ||||
|     vob = 0 | ||||
|     cell = 0 | ||||
|     flags = make_flags(frames) | ||||
|     return " ".join(map(str, [info, matrix, file, position, skip, vob, cell, *flags])) | ||||
| 
 | ||||
| 
 | ||||
| def get_frames(path): | ||||
|     proc=SP.Popen([ | ||||
|         "ffprobe", | ||||
|         "-probesize", str(0x7fffffff), | ||||
|         "-analyzeduration", str(0x7fffffff), | ||||
|         "-v","fatal", | ||||
|         "-i",path, | ||||
|         "-select_streams","v:0", | ||||
|         "-show_frames", | ||||
|         "-print_format","compact" | ||||
|     ],stdout=SP.PIPE,stdin=SP.DEVNULL,bufsize=0) | ||||
|     data=None | ||||
|     proc = SP.Popen( | ||||
|         [ | ||||
|             "ffprobe", | ||||
|             "-probesize", | ||||
|             str(0x7FFFFFFF), | ||||
|             "-analyzeduration", | ||||
|             str(0x7FFFFFFF), | ||||
|             "-v", | ||||
|             "fatal", | ||||
|             "-i", | ||||
|             path, | ||||
|             "-select_streams", | ||||
|             "v:0", | ||||
|             "-show_frames", | ||||
|             "-print_format", | ||||
|             "compact", | ||||
|         ], | ||||
|         stdout=SP.PIPE, | ||||
|         stdin=SP.DEVNULL, | ||||
|         bufsize=0, | ||||
|     ) | ||||
|     data = None | ||||
|     for line in proc.stdout: | ||||
|         line=str(line,"utf8").strip().split("|") | ||||
|         line={line[0]: dict(v.split("=") for v in line[1:])} | ||||
|         line = str(line, "utf8").strip().split("|") | ||||
|         line = {line[0]: dict(v.split("=") for v in line[1:])} | ||||
|         yield line | ||||
|     ret=proc.wait() | ||||
|     if ret!=0: | ||||
|     ret = proc.wait() | ||||
|     if ret != 0: | ||||
|         exit(ret) | ||||
|     return data | ||||
| 
 | ||||
| 
 | ||||
| def get_streams(path): | ||||
|     proc=SP.Popen([ | ||||
|         "ffprobe", | ||||
|         "-probesize", str(0x7fffffff), | ||||
|         "-analyzeduration", str(0x7fffffff), | ||||
|         "-v","fatal", | ||||
|         "-i",path, | ||||
|         "-select_streams","v:0", | ||||
|         "-show_streams", | ||||
|         "-show_format", | ||||
|         "-print_format","json" | ||||
|     ],stdout=SP.PIPE,stdin=SP.DEVNULL,bufsize=0) | ||||
|     data=json.load(proc.stdout) | ||||
|     ret=proc.wait() | ||||
|     if ret!=0: | ||||
|     proc = SP.Popen( | ||||
|         [ | ||||
|             "ffprobe", | ||||
|             "-probesize", | ||||
|             str(0x7FFFFFFF), | ||||
|             "-analyzeduration", | ||||
|             str(0x7FFFFFFF), | ||||
|             "-v", | ||||
|             "fatal", | ||||
|             "-i", | ||||
|             path, | ||||
|             "-select_streams", | ||||
|             "v:0", | ||||
|             "-show_streams", | ||||
|             "-show_format", | ||||
|             "-print_format", | ||||
|             "json", | ||||
|         ], | ||||
|         stdout=SP.PIPE, | ||||
|         stdin=SP.DEVNULL, | ||||
|         bufsize=0, | ||||
|     ) | ||||
|     data = json.load(proc.stdout) | ||||
|     ret = proc.wait() | ||||
|     if ret != 0: | ||||
|         exit(ret) | ||||
|     return data['streams'],data['format'] | ||||
|     return data["streams"], data["format"] | ||||
| 
 | ||||
| 
 | ||||
| def make_header(file): | ||||
|     return ["DGIndexProjectFile16","1",os.path.abspath(file)] | ||||
|     return ["DGIndexProjectFile16", "1", os.path.abspath(file)] | ||||
| 
 | ||||
| 
 | ||||
| def make_settings(stream): | ||||
|     pict_size="x".join(map(str,[stream["width"],stream["height"]])) | ||||
|     frame_rate = list(map(int,stream['r_frame_rate'].split("/"))) | ||||
|     frame_rate=(frame_rate[0]*1000)//frame_rate[1] | ||||
|     frame_rate=f"{frame_rate} ({stream['r_frame_rate']})" | ||||
|     header=[ | ||||
|         ("Stream_Type",0), # Elementary Stream | ||||
|         ("MPEG_Type",2), # MPEG-2 | ||||
|         ("iDCT_Algorithm",5), # 64-bit IEEE-1180 Reference | ||||
|         ("YUVRGB_Scale",int(stream["color_range"]!="tv")), | ||||
|         ("Luminance_Filter","0,0"), | ||||
|         ("Clipping","0,0,0,0"), | ||||
|         ("Aspect_Ratio",stream["display_aspect_ratio"]), | ||||
|         ("Picture_Size",pict_size), | ||||
|         ("Field_Operation",0), # Honor Pulldown Flags | ||||
|     pict_size = "x".join(map(str, [stream["width"], stream["height"]])) | ||||
|     frame_rate = list(map(int, stream["r_frame_rate"].split("/"))) | ||||
|     frame_rate = (frame_rate[0] * 1000) // frame_rate[1] | ||||
|     frame_rate = f"{frame_rate} ({stream['r_frame_rate']})" | ||||
|     header = [ | ||||
|         ("Stream_Type", 0),  # Elementary Stream | ||||
|         ("MPEG_Type", 2),  # MPEG-2 | ||||
|         ("iDCT_Algorithm", 5),  # 64-bit IEEE-1180 Reference | ||||
|         ("YUVRGB_Scale", int(stream["color_range"] != "tv")), | ||||
|         ("Luminance_Filter", "0,0"), | ||||
|         ("Clipping", "0,0,0,0"), | ||||
|         ("Aspect_Ratio", stream["display_aspect_ratio"]), | ||||
|         ("Picture_Size", pict_size), | ||||
|         ("Field_Operation", 0),  # Honor Pulldown Flags | ||||
|         ("Frame_Rate", frame_rate), | ||||
|         ("Location",f"0,0,0,0"), | ||||
|         ("Location", "0,0,0,0"), | ||||
|     ] | ||||
|     for k,v in header: | ||||
|     for k, v in header: | ||||
|         yield f"{k}={v}" | ||||
| 
 | ||||
| 
 | ||||
| def gen_d2v(path): | ||||
|     yield from make_header(path) | ||||
|     yield "" | ||||
|     streams,fmt=get_streams(path) | ||||
|     stream=[s for s in streams if s['codec_type']=='video'][0] | ||||
|     stream['index']=str(stream['index']) | ||||
|     streams, fmt = get_streams(path) | ||||
|     stream = [s for s in streams if s["codec_type"] == "video"][0] | ||||
|     stream["index"] = str(stream["index"]) | ||||
|     yield from make_settings(stream) | ||||
|     yield "" | ||||
|     line_buffer=[] | ||||
|     frames=get_frames(path) | ||||
|     prog_bar=tqdm(frames,total=int(fmt['size']),unit_divisor=1024,unit_scale=True,unit="iB",dec="Writing d2v") | ||||
|     line_buffer = [] | ||||
|     frames = get_frames(path) | ||||
|     prog_bar = tqdm( | ||||
|         frames, | ||||
|         total=int(fmt["size"]), | ||||
|         unit_divisor=1024, | ||||
|         unit_scale=True, | ||||
|         unit="iB", | ||||
|         desc="Writing d2v", | ||||
|     ) | ||||
|     for line in prog_bar: | ||||
|         if 'frame' not in line: | ||||
|         if "frame" not in line: | ||||
|             continue | ||||
|         frame=line['frame'] | ||||
|         prog_bar.n=min(max(prog_bar.n,int(frame['pkt_pos'])),int(fmt['size'])) | ||||
|         frame = line["frame"] | ||||
|         prog_bar.n = min(max(prog_bar.n, int(frame["pkt_pos"])), int(fmt["size"])) | ||||
|         prog_bar.update(0) | ||||
|         if frame['stream_index']!=stream['index']: | ||||
|         if frame["stream_index"] != stream["index"]: | ||||
|             continue | ||||
|         if frame['pict_type']=="I" and line_buffer: | ||||
|             yield make_line(line_buffer,stream) | ||||
|         if frame["pict_type"] == "I" and line_buffer: | ||||
|             yield make_line(line_buffer, stream) | ||||
|             line_buffer.clear() | ||||
|         line_buffer.append(frame) | ||||
|     prog_bar.close() | ||||
|     yield None | ||||
| 
 | ||||
| 
 | ||||
| def make_d2v(path): | ||||
|     outfile=os.path.splitext(os.path.basename(path))[0] | ||||
|     outfile=os.path.extsep.join([outfile,"d2v"]) | ||||
|     a,b=ITT.tee(gen_d2v(path)) | ||||
|     outfile = os.path.splitext(os.path.basename(path))[0] | ||||
|     outfile = os.path.extsep.join([outfile, "d2v"]) | ||||
|     a, b = ITT.tee(gen_d2v(path)) | ||||
|     next(b) | ||||
|     with open(outfile,"w") as fh: | ||||
|         for line,next_line in zip(a,b): | ||||
|     with open(outfile, "w") as fh: | ||||
|         for line, next_line in zip(a, b): | ||||
|             fh.write(line) | ||||
|             if next_line is None: # last line, append end marker | ||||
|             if next_line is None:  # last line, append end marker | ||||
|                 fh.write(" ff") | ||||
|             fh.write("\n") | ||||
|  |  | |||
							
								
								
									
										110
									
								
								vob_demux.py
									
										
									
									
									
								
							
							
						
						
									
										110
									
								
								vob_demux.py
									
										
									
									
									
								
							|  | @ -5,59 +5,83 @@ import subprocess as SP | |||
| 
 | ||||
| 
 | ||||
| def get_streams(path): | ||||
|     proc=SP.Popen([ | ||||
|         "ffprobe", | ||||
|         "-probesize", str(0x7fffffff), | ||||
|         "-analyzeduration", str(0x7fffffff), | ||||
|         "-v","fatal", | ||||
|         "-i",path, | ||||
|         "-show_streams", | ||||
|         "-show_format", | ||||
|         "-print_format","json" | ||||
|     ],stdout=SP.PIPE,stdin=SP.DEVNULL,bufsize=0) | ||||
|     data=json.load(proc.stdout) | ||||
|     ret=proc.wait() | ||||
|     if ret!=0: | ||||
|         return [],{} | ||||
|     return data['streams'],data['format'] | ||||
|     proc = SP.Popen( | ||||
|         [ | ||||
|             "ffprobe", | ||||
|             "-probesize", | ||||
|             str(0x7FFFFFFF), | ||||
|             "-analyzeduration", | ||||
|             str(0x7FFFFFFF), | ||||
|             "-v", | ||||
|             "fatal", | ||||
|             "-i", | ||||
|             path, | ||||
|             "-show_streams", | ||||
|             "-show_format", | ||||
|             "-print_format", | ||||
|             "json", | ||||
|         ], | ||||
|         stdout=SP.PIPE, | ||||
|         stdin=SP.DEVNULL, | ||||
|         bufsize=0, | ||||
|     ) | ||||
|     data = json.load(proc.stdout) | ||||
|     ret = proc.wait() | ||||
|     if ret != 0: | ||||
|         return [], {} | ||||
|     return data["streams"], data["format"] | ||||
| 
 | ||||
| types={ | ||||
|     'mpeg2video': 'm2v', | ||||
|     'ac3': 'ac3', | ||||
|     'dvd_subtitle': 'sup', | ||||
| 
 | ||||
| types = { | ||||
|     "mpeg2video": "m2v", | ||||
|     "ac3": "ac3", | ||||
|     "dvd_subtitle": "sup", | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| def demux(path): | ||||
|     folder=os.path.dirname(path) | ||||
|     basename=os.path.splitext(os.path.basename(path))[0] | ||||
|     streams,fmt=get_streams(path) | ||||
|     cmd=[ | ||||
|     folder = os.path.dirname(path) | ||||
|     basename = os.path.splitext(os.path.basename(path))[0] | ||||
|     streams, fmt = get_streams(path) | ||||
|     cmd = [ | ||||
|         "ffmpeg", | ||||
|         "-y", | ||||
|         "-strict","-2", | ||||
|         "-fflags","+genpts", | ||||
|         "-probesize", str(0x7fffffff), | ||||
|         "-analyzeduration", str(0x7fffffff), | ||||
|         "-i",path, | ||||
|         "-scodec","copy", | ||||
|         "-vcodec","copy", | ||||
|         "-acodec","copy", | ||||
|         # "-fflags","+genpts+igndts", | ||||
|         "-probesize", | ||||
|         str(0x7FFFFFFF), | ||||
|         "-analyzeduration", | ||||
|         str(0x7FFFFFFF), | ||||
|         "-i", | ||||
|         path, | ||||
|         "-strict", | ||||
|         "-2", | ||||
|         "-vcodec", | ||||
|         "copy", | ||||
|         "-acodec", | ||||
|         "copy", | ||||
|         "-scodec", | ||||
|         "copy", | ||||
|     ] | ||||
|     need_ffmpeg=False | ||||
|     need_ffmpeg = False | ||||
|     for stream in streams: | ||||
|         codec=stream['codec_name'] | ||||
|         ext=types.get(codec,codec) | ||||
|         idx=stream['index'] | ||||
|         hex_id=stream['id'] | ||||
|         codec_name=stream['codec_long_name'] | ||||
|         outfile=os.path.join(folder,f"{basename}_{idx}_{hex_id}") | ||||
|         if codec=="dvd_subtitle": | ||||
|         codec = stream["codec_name"] | ||||
|         ext = types.get(codec, codec) | ||||
|         idx = stream["index"] | ||||
|         hex_id = stream["id"] | ||||
|         codec_name = stream["codec_long_name"] | ||||
|         outfile = os.path.join(folder, f"{basename}_{idx}_{hex_id}") | ||||
|         if codec=="dvd_nav_packet": | ||||
|             continue | ||||
|         print(idx, hex_id, codec_name, codec) | ||||
|         if codec == "dvd_subtitle": | ||||
|             SP.check_call([ | ||||
|                 "mencoder",path,"-vobsuboutindex",str(idx),"-vobsubout", outfile,"-nosound","-ovc", "copy", "-o",os.devnull | ||||
|             ]) | ||||
|             continue | ||||
|         print(idx,hex_id,codec_name,codec) | ||||
|         cmd+=["-map",f"0:#{hex_id}",outfile+f".{ext}"] | ||||
|         need_ffmpeg=True | ||||
|         cmd += ["-map", f"0:#{hex_id}", "-strict", "-2", outfile + f".{ext}"] | ||||
|         need_ffmpeg = True | ||||
|     if need_ffmpeg: | ||||
|         SP.check_call(cmd) | ||||
|         SP.check_call(cmd) | ||||
| 
 | ||||
| if __name__=="__main__": | ||||
|     demux(sys.argv[1]) | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue