Even more linting: no more variable with camel case.

This commit is contained in:
Frédéric Tronel
2025-10-29 10:04:47 +01:00
parent 75f227786f
commit c565699875

View File

@@ -53,7 +53,14 @@ from iso639.exceptions import InvalidLanguageValue
# Then finally, change the Private Codec Data in the final MKV. # Then finally, change the Private Codec Data in the final MKV.
def checkRequiredTools(): def checkRequiredTools() -> tuple[bool,list[str]]:
"""Check if required external tools are installed.
Args:
Returns:
tuple[bool, list[str]] : does all optional tools are installed and the paths of all tools.
"""
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
all_optional_tools = True all_optional_tools = True
paths = {} paths = {}
@@ -76,7 +83,15 @@ def checkRequiredTools():
return all_optional_tools, paths return all_optional_tools, paths
def getTesseractSupportedLang(tesseract): def getTesseractSupportedLang(tesseract:str) -> dict[str,str]|None:
"""Returns the set of natural languages supported by Tesseract OCR tool.
Args:
tesseract: str: path to tesseract binary.
Returns:
dict[str, str] : a mapping ....
"""
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
res = {} res = {}
@@ -102,7 +117,7 @@ def getTesseractSupportedLang(tesseract):
return res return res
def getFrameRate(ffprobe, inputFile): def getFrameRate(ffprobe:str, inputFile) -> float|None:
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
infd = inputFile.fileno() infd = inputFile.fileno()
@@ -167,7 +182,7 @@ def getFrameRate(ffprobe, inputFile):
return None return None
def getSubTitlesTracks(ffprobe, mkvPath): def getSubTitlesTracks(ffprobe:str, mkvPath: str) -> dict[str,str]|None:
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
tracks={} tracks={}
@@ -199,7 +214,7 @@ def getSubTitlesTracks(ffprobe, mkvPath):
return tracks return tracks
def extractSRT(mkvextract, fileName, subtitles, langs): def extractSRT(mkvextract:str, fileName:str, subtitles:str, langs:list[str]) -> list|None:
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
params = [mkvextract, fileName, 'tracks'] params = [mkvextract, fileName, 'tracks']
@@ -279,7 +294,7 @@ def doOCR(vobsubocr, idxs, duration, temporaries, dumpMemFD=False):
write(srtfd, line.encode(encoding='UTF-8')) write(srtfd, line.encode(encoding='UTF-8'))
m = re.match(timestamps, line) m = re.match(timestamps, line)
if m!=None: if m is not None:
hours = int(m.group('hours')) hours = int(m.group('hours'))
minutes = int(m.group('hours')) minutes = int(m.group('hours'))
seconds = int(m.group('seconds')) seconds = int(m.group('seconds'))
@@ -499,11 +514,11 @@ def parseRBSPTrailingBits(buf, bit_position):
bit_position, one = readBit(buf, bit_position) bit_position, one = readBit(buf, bit_position)
if one==0: if one==0:
raise Exception(f'Stop bit should be equal to one. Read: {one:d}') raise ValueError(f'Stop bit should be equal to one. Read: {one:d}')
while bit_position%8 != 0: while bit_position%8 != 0:
bit_position, zero = readBit(buf, bit_position) bit_position, zero = readBit(buf, bit_position)
if zero==1: if zero==1:
raise Exception('Trailing bit should be equal to zero') raise ValueError('Trailing bit should be equal to zero')
return bit_position return bit_position
@@ -534,7 +549,7 @@ def moreRBSPData(buf, bit_position):
break break
if not found: if not found:
raise Exception('Impossible to find trailing stop bit !') raise ValueError('Impossible to find trailing stop bit !')
# No more data # No more data
if bit_position == pos: if bit_position == pos:
@@ -888,20 +903,10 @@ class SPS:
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
x264opts = [] x264opts = []
if self.profile_idc in [ 0x42, 0x4D, 0x64, 0x6E, 0x7A, 0xF4, 0x2C]: try:
if self.profile_idc == 0x42: profile = {0x42:'baseline', 0x4D:'main', 0x64:'high', 0x6E:'high10', 0x7A:'high422',
profile = 'baseline' 0xF4:'high444'}[self.profile_idc]
elif self.profile_idc == 0x4D: except KeyError:
profile = 'main'
elif self.profile_idc == 0x64 :
profile = 'high'
elif self.profile_idc == 0x6E:
profile = 'high10'
elif self.profile_idc == 0x7A:
profile = 'high422'
elif self.profile_idc == 0xF4:
profile = 'high444'
else:
logger.error('Unknow profile: %x', self.profile_idc) logger.error('Unknow profile: %x', self.profile_idc)
return [] return []
@@ -942,13 +947,13 @@ class SPS:
# NAL Unit SPS # NAL Unit SPS
bit_position, zero = readBit(buf, bit_position) bit_position, zero = readBit(buf, bit_position)
if zero != 0: if zero != 0:
raise Exception(f'Reserved bit is not equal to 0: {zero:d}') raise ValueError(f'Reserved bit is not equal to 0: {zero:d}')
bit_position, nal_ref_idc = readBits(buf, bit_position,2) bit_position, nal_ref_idc = readBits(buf, bit_position,2)
if nal_ref_idc != 3: if nal_ref_idc != 3:
raise Exception(f'NAL ref idc is not equal to 3: {nal_ref_idc:d}') raise ValueError(f'NAL ref idc is not equal to 3: {nal_ref_idc:d}')
bit_position, nal_unit_type = readBits(buf, bit_position,5) bit_position, nal_unit_type = readBits(buf, bit_position,5)
if nal_unit_type != 7: if nal_unit_type != 7:
raise Exception(f'NAL unit type is not a SPS: {nal_unit_type:d}') raise ValueError(f'NAL unit type is not a SPS: {nal_unit_type:d}')
bit_position, self.profile_idc = readByte(buf, bit_position) bit_position, self.profile_idc = readByte(buf, bit_position)
bit_position, self.constraint_set0_flag = readBit(buf,bit_position) bit_position, self.constraint_set0_flag = readBit(buf,bit_position)
@@ -959,7 +964,7 @@ class SPS:
bit_position, self.constraint_set5_flag = readBit(buf,bit_position) bit_position, self.constraint_set5_flag = readBit(buf,bit_position)
bit_position, v = readBits(buf, bit_position, 2) bit_position, v = readBits(buf, bit_position, 2)
if v!=0: if v!=0:
raise Exception(f'Reserved bits different from 0b00: {v:x}') raise ValueError(f'Reserved bits different from 0b00: {v:x}')
bit_position, self.level_idc = readByte(buf, bit_position) bit_position, self.level_idc = readByte(buf, bit_position)
bit_position, self.seq_parameter_set_id = readUnsignedExpGolomb(buf, bit_position) bit_position, self.seq_parameter_set_id = readUnsignedExpGolomb(buf, bit_position)
if self.profile_idc in [44, 83, 86, 100, 110, 118, 122, 128, 134, 135, 138, 139, 244]: if self.profile_idc in [44, 83, 86, 100, 110, 118, 122, 128, 134, 135, 138, 139, 244]:
@@ -1153,13 +1158,13 @@ class PPS:
# NAL Unit PPS # NAL Unit PPS
bit_position, zero = readBit(buf, bit_position) bit_position, zero = readBit(buf, bit_position)
if zero != 0: if zero != 0:
raise Exception(f'Reserved bit is not equal to 0: {zero:d}') raise ValueError(f'Reserved bit is not equal to 0: {zero:d}')
bit_position, nal_ref_idc = readBits(buf, bit_position,2) bit_position, nal_ref_idc = readBits(buf, bit_position,2)
if nal_ref_idc != 3: if nal_ref_idc != 3:
raise Exception(f'NAL ref idc is not equal to 3: {nal_ref_idc:d}') raise ValueError(f'NAL ref idc is not equal to 3: {nal_ref_idc:d}')
bit_position, nal_unit_type = readBits(buf, bit_position,5) bit_position, nal_unit_type = readBits(buf, bit_position,5)
if nal_unit_type != 8: if nal_unit_type != 8:
raise Exception(f'NAL unit type is not a PPS: {nal_unit_type:d}') raise ValueError(f'NAL unit type is not a PPS: {nal_unit_type:d}')
bit_position, self.pic_parameter_set_id = readUnsignedExpGolomb(buf, bit_position) bit_position, self.pic_parameter_set_id = readUnsignedExpGolomb(buf, bit_position)
bit_position, self.seq_parameter_set_id = readUnsignedExpGolomb(buf, bit_position) bit_position, self.seq_parameter_set_id = readUnsignedExpGolomb(buf, bit_position)
@@ -1348,17 +1353,17 @@ class AVCDecoderConfiguration:
bit_position, self.AVCLevelIndication = readByte(buf, bit_position) bit_position, self.AVCLevelIndication = readByte(buf, bit_position)
bit_position, v = readBits(buf, bit_position, 6) bit_position, v = readBits(buf, bit_position, 6)
if v != 0b111111: if v != 0b111111:
raise Exception(f'Reserved bits are not equal to 0b111111: {v:x}') raise ValueError(f'Reserved bits are not equal to 0b111111: {v:x}')
bit_position, self.lengthSizeMinusOne = readBits(buf, bit_position, 2) bit_position, self.lengthSizeMinusOne = readBits(buf, bit_position, 2)
bit_position, v = readBits(buf, bit_position, 3) bit_position, v = readBits(buf, bit_position, 3)
if v != 0b111: if v != 0b111:
raise Exception(f'Reserved bits are not equal to 0b111: {v:x}') raise ValueError(f'Reserved bits are not equal to 0b111: {v:x}')
bit_position, self.numOfSequenceParameterSets= readBits(buf, bit_position, 5) bit_position, self.numOfSequenceParameterSets= readBits(buf, bit_position, 5)
logger.debug('Number of SPS: %d', self.numOfSequenceParameterSets) logger.debug('Number of SPS: %d', self.numOfSequenceParameterSets)
for _ in range(0,self.numOfSequenceParameterSets): for _ in range(0,self.numOfSequenceParameterSets):
bit_position, length = readWord(buf, bit_position) bit_position, length = readWord(buf, bit_position)
if bit_position % 8 != 0: if bit_position % 8 != 0:
raise Exception(f'SPS is not located at a byte boundary: {bit_position:d}') raise ValueError(f'SPS is not located at a byte boundary: {bit_position:d}')
sps = SPS() sps = SPS()
sodb = RBSP2SODB(buf[floor(bit_position/8):]) sodb = RBSP2SODB(buf[floor(bit_position/8):])
@@ -1381,7 +1386,7 @@ class AVCDecoderConfiguration:
for _ in range(0,self.numOfPictureParameterSets): for _ in range(0,self.numOfPictureParameterSets):
bit_position, length = readWord(buf, bit_position) bit_position, length = readWord(buf, bit_position)
if bit_position % 8 != 0: if bit_position % 8 != 0:
raise Exception('PPS is not located at a byte boundary: {bit_position:d}') raise ValueError('PPS is not located at a byte boundary: {bit_position:d}')
pps = PPS() pps = PPS()
sodb = RBSP2SODB(buf[floor(bit_position/8):]) sodb = RBSP2SODB(buf[floor(bit_position/8):])
@@ -1402,15 +1407,15 @@ class AVCDecoderConfiguration:
if self.AVCProfileIndication in [100, 110, 122, 144]: if self.AVCProfileIndication in [100, 110, 122, 144]:
bit_position, reserved = readBits(buf, bit_position, 6) bit_position, reserved = readBits(buf, bit_position, 6)
if reserved != 0b111111: if reserved != 0b111111:
raise Exception(f'Reserved bits are different from 111111: {reserved:x}') raise ValueError(f'Reserved bits are different from 111111: {reserved:x}')
bit_position, self.chroma_format = readBits(buf, bit_position, 2) bit_position, self.chroma_format = readBits(buf, bit_position, 2)
bit_position, reserved = readBits(buf, bit_position, 5) bit_position, reserved = readBits(buf, bit_position, 5)
if reserved != 0b11111: if reserved != 0b11111:
raise Exception(f'Reserved bits are different from 11111: {reserved:x}') raise ValueError(f'Reserved bits are different from 11111: {reserved:x}')
bit_position, self.bit_depth_luma_minus8 = readBits(buf, bit_position, 3) bit_position, self.bit_depth_luma_minus8 = readBits(buf, bit_position, 3)
bit_position, reserved = readBits(buf, bit_position, 5) bit_position, reserved = readBits(buf, bit_position, 5)
if reserved != 0b11111: if reserved != 0b11111:
raise Exception(f'Reserved bits are different from 11111: {reserved:x}') raise ValueError(f'Reserved bits are different from 11111: {reserved:x}')
bit_position, self.bit_depth_chroma_minus8 = readBits(buf, bit_position, 3) bit_position, self.bit_depth_chroma_minus8 = readBits(buf, bit_position, 3)
bit_position, self.numOfSequenceParameterSetExt = readByte(buf, bit_position) bit_position, self.numOfSequenceParameterSetExt = readByte(buf, bit_position)
for _ in range(0, self.numOfSequenceParameterSetExt): for _ in range(0, self.numOfSequenceParameterSetExt):
@@ -1482,28 +1487,28 @@ class AVCDecoderConfiguration:
def merge(self, config): def merge(self, config):
# Check config compatibility # Check config compatibility
if self.configurationVersion != config.configurationVersion: if self.configurationVersion != config.configurationVersion:
raise Exception('Configuration versions are different: %d vs %s' %\ raise ValueError('Configuration versions are different: %d vs %s' %\
(self.configurationVersion, config.configurationVersion)) (self.configurationVersion, config.configurationVersion))
if self.AVCProfileIndication != config.AVCProfileIndication: if self.AVCProfileIndication != config.AVCProfileIndication:
raise Exception('AVC profiles are different: %d vs %s' %\ raise ValueError('AVC profiles are different: %d vs %s' %\
(self.AVCProfileIndication, config.AVCProfileIndication)) (self.AVCProfileIndication, config.AVCProfileIndication))
if self.profile_compatibility != config.profile_compatibility: if self.profile_compatibility != config.profile_compatibility:
raise Exception('Profile compatilities are different: %d vs %s' %\ raise ValueError('Profile compatilities are different: %d vs %s' %\
(self.profile_compatibility, config.profile_compatibility)) (self.profile_compatibility, config.profile_compatibility))
if self.AVCLevelIndication != config.AVCLevelIndication: if self.AVCLevelIndication != config.AVCLevelIndication:
raise Exception('Level indications are different: %d vs %s' %\ raise ValueError('Level indications are different: %d vs %s' %\
(self.AVCLevelIndication, config.AVCLevelIndication)) (self.AVCLevelIndication, config.AVCLevelIndication))
if self.lengthSizeMinusOne != config.lengthSizeMinusOne: if self.lengthSizeMinusOne != config.lengthSizeMinusOne:
raise Exception('Length units are different: %d vs %s' %\ raise ValueError('Length units are different: %d vs %s' %\
(self.lengthSizeMinusOne, config.lengthSizeMinusOne)) (self.lengthSizeMinusOne, config.lengthSizeMinusOne))
if self.chroma_format != config.chroma_format: if self.chroma_format != config.chroma_format:
raise Exception('Colour format are different: %d vs %s' %\ raise ValueError('Colour format are different: %d vs %s' %\
(self.chroma_format, config.chroma_format)) (self.chroma_format, config.chroma_format))
if self.bit_depth_luma_minus8 != config.bit_depth_luma_minus8: if self.bit_depth_luma_minus8 != config.bit_depth_luma_minus8:
raise Exception('Depth of luminance are different: %d vs %s' %\ raise ValueError('Depth of luminance are different: %d vs %s' %\
(self.bit_depth_luma_minus8, config.bit_depth_luma_minus8)) (self.bit_depth_luma_minus8, config.bit_depth_luma_minus8))
if self.bit_depth_chroma_minus8 != config.bit_depth_chroma_minus8: if self.bit_depth_chroma_minus8 != config.bit_depth_chroma_minus8:
raise Exception('Depth of chromaticity are different: %d vs %s' %\ raise ValueError('Depth of chromaticity are different: %d vs %s' %\
(self.bit_depth_chroma_minus8, config.bit_depth_luma_minus8)) (self.bit_depth_chroma_minus8, config.bit_depth_luma_minus8))
for spsid in config.sps: for spsid in config.sps:
@@ -1511,7 +1516,7 @@ class AVCDecoderConfiguration:
if spsid in self.sps: if spsid in self.sps:
localsps = self.sps[spsid] localsps = self.sps[spsid]
if sps!=localsps: if sps!=localsps:
raise Exception(f'Profile are not compatible. They contain two different SPS\ raise ValueError(f'Profile are not compatible. They contain two different SPS\
with the same identifier ({spsid:d}): {localsps}\n{sps}\n') with the same identifier ({spsid:d}): {localsps}\n{sps}\n')
self.sps[spsid] = sps self.sps[spsid] = sps
@@ -1522,7 +1527,7 @@ class AVCDecoderConfiguration:
if ppsid in self.pps: if ppsid in self.pps:
localpps = self.pps[ppsid] localpps = self.pps[ppsid]
if pps!=localpps: if pps!=localpps:
raise Exception(f'Profile are not compatible. They contain two different PPS\ raise ValueError(f'Profile are not compatible. They contain two different PPS\
with the same identifier ({ppsid:d}): {localpps}\n{pps}\n') with the same identifier ({ppsid:d}): {localpps}\n{pps}\n')
self.pps[ppsid] = pps self.pps[ppsid] = pps
@@ -1532,12 +1537,12 @@ class AVCDecoderConfiguration:
def parseCodecPrivate(codecPrivateData): def parseCodecPrivate(codecPrivateData):
if codecPrivateData[0] != 0x63: if codecPrivateData[0] != 0x63:
raise Exception(f'Matroska header is wrong: {codecPrivateData[0]:x}') raise ValueError(f'Matroska header is wrong: {codecPrivateData[0]:x}')
if codecPrivateData[1] != 0xA2: if codecPrivateData[1] != 0xA2:
raise Exception(f'Matroska header is wrong: {codecPrivateData[1]:x}') raise ValueError(f'Matroska header is wrong: {codecPrivateData[1]:x}')
length = codecPrivateData[2] length = codecPrivateData[2]
if length == 0: if length == 0:
raise Exception('Matroska length cannot start with zero byte.') raise ValueError('Matroska length cannot start with zero byte.')
for nb_zeroes in range(0,8): for nb_zeroes in range(0,8):
b = readBit(codecPrivateData[2:], nb_zeroes) b = readBit(codecPrivateData[2:], nb_zeroes)
if b != 0: if b != 0:
@@ -1563,14 +1568,14 @@ def getAvcConfigFromH264(inputFile):
bit_position = 0 bit_position = 0
bit_position, start_code = readLong(sodb, bit_position) bit_position, start_code = readLong(sodb, bit_position)
if start_code != 1: if start_code != 1:
raise Exception(f'Starting code not detected: {start_code:x}') raise ValueError(f'Starting code not detected: {start_code:x}')
sps = SPS() sps = SPS()
bit_length = sps.fromBytes(sodb[4:]) bit_length = sps.fromBytes(sodb[4:])
bit_position+=bit_length bit_position+=bit_length
bit_position, start_code = readLong(sodb, bit_position) bit_position, start_code = readLong(sodb, bit_position)
if start_code != 1: if start_code != 1:
raise Exception('Starting code not detected: {start_code:x}') raise ValueError(f'Starting code not detected: {start_code:x}')
pps = PPS() pps = PPS()
bit_length = pps.fromBytes(sodb[floor(bit_position/8):], sps.chroma_format_idc) bit_length = pps.fromBytes(sodb[floor(bit_position/8):], sps.chroma_format_idc)
logger.debug(pps) logger.debug(pps)
@@ -1625,7 +1630,7 @@ def parseMKVTree(mkvinfo, inputFile):
else: else:
position = int(m.group('position')) position = int(m.group('position'))
size = int(m.group('size')) size = int(m.group('size'))
root = m.group('root')!=None root = m.group('root') is not None
if root: if root:
depth = 0 depth = 0
else: else:
@@ -2490,18 +2495,17 @@ def dumpPPM(pictures, prefix, temporaries):
header_len=2+1+ceil(log(width, 10))+1+ceil(log(height, 10))+1+3+1 header_len=2+1+ceil(log(width, 10))+1+ceil(log(height, 10))+1+3+1
try: try:
out = open(filename, 'w', encoding='utf8') with open(filename, 'w', encoding='utf8') as out:
outfd = out.fileno()
except IOError:
logger.error('Impossible to create file: %s', filename)
temporaries.append(out) temporaries.append(out)
outfd = out.fileno()
length=header_len+3*width*height length=header_len+3*width*height
nb_bytes = 0 nb_bytes = 0
while nb_bytes < length: while nb_bytes < length:
nb_bytes+=write(outfd, pictures[pos+nb_bytes:pos+length]) nb_bytes+=write(outfd, pictures[pos+nb_bytes:pos+length])
pos+=length pos+=length
picture+=1 picture+=1
except IOError:
logger.error('Impossible to create file: %s', filename)
def extractAllStreams(ffmpeg, ffprobe, inputFile, begin, end, streams, filesPrefix, nbFrames, def extractAllStreams(ffmpeg, ffprobe, inputFile, begin, end, streams, filesPrefix, nbFrames,
@@ -2629,16 +2633,15 @@ def extractAllStreams(ffmpeg, ffprobe, inputFile, begin, end, streams, filesPref
if dumpMemFD: if dumpMemFD:
try: try:
output = open(tmpname,'w', encoding='utf8') with open(tmpname,'w', encoding='utf8') as output:
except IOError: temporaries.append(output)
logger.error('Impossible to create file: %s', tmpname)
return None
outfd = output.fileno() outfd = output.fileno()
pos = 0 pos = 0
while pos < len(sound_bytes): while pos < len(sound_bytes):
pos+=write(outfd, sound_bytes[pos:]) pos+=write(outfd, sound_bytes[pos:])
temporaries.append(output) except IOError:
logger.error('Impossible to create file: %s', tmpname)
return None
# We rewind to zero the memory file descriptor # We rewind to zero the memory file descriptor
lseek(memfd, 0, SEEK_SET) lseek(memfd, 0, SEEK_SET)
@@ -3351,65 +3354,65 @@ def main():
# Creating MKV file that corresponds to current part between I-frames # Creating MKV file that corresponds to current part between I-frames
# Internal video with all streams (video, audio and subtitles) # Internal video with all streams (video, audio and subtitles)
internalMKVName = f'part-{partnum:d}-internal.mkv' internal_mkv_name = f'part-{partnum:d}-internal.mkv'
# Internal video stream as a raw H264 stream # Internal video stream as a raw H264 stream
internalH264Name = f'part-{partnum:d}-internal.h264' internal_h264_name = f'part-{partnum:d}-internal.h264'
# Internal video timestamps # Internal video timestamps
internalH264TSName = f'part-{partnum:d}-internal-ts.txt' internal_h264_ts_name = f'part-{partnum:d}-internal-ts.txt'
# Internal video with only audio and subtitles streams # Internal video with only audio and subtitles streams
internalNoVideoMKVName = f'part-{partnum:d}-internal-novideo.mkv' internal_novideo_mkv_name = f'part-{partnum:d}-internal-novideo.mkv'
try: try:
internalMKV = open(internalMKVName, 'w+', encoding='utf8') internal_mkv = open(internal_mkv_name, 'w+', encoding='utf8')
except IOError: except IOError:
logger.error('Impossible to create file: %s', internalMKVName) logger.error('Impossible to create file: %s', internal_mkv_name)
exit(-1) exit(-1)
try: try:
internalNoVideoMKV = open(internalNoVideoMKVName, 'w+', encoding='utf8') internal_novideo_mkv = open(internal_novideo_mkv_name, 'w+', encoding='utf8')
except IOError: except IOError:
logger.error('Impossible to create file: %s', internalNoVideoMKVName) logger.error('Impossible to create file: %s', internal_novideo_mkv_name)
exit(-1) exit(-1)
try: try:
internalH264 = open(internalH264Name, 'w+', encoding='utf8') internal_h264 = open(internal_h264_name, 'w+', encoding='utf8')
except IOError: except IOError:
logger.error('Impossible to create file: %s', internalH264Name) logger.error('Impossible to create file: %s', internal_h264_name)
exit(-1) exit(-1)
try: try:
internalH264TS = open(internalH264TSName, 'w+', encoding='utf8') internal_h264_ts = open(internal_h264_ts_name, 'w+', encoding='utf8')
except IOError: except IOError:
logger.error('Impossible to create file: %s', internalH264TSName) logger.error('Impossible to create file: %s', internal_h264_ts_name)
exit(-1) exit(-1)
# logger.info('Merge header, middle and trailer subpart into: %s' % internalMKVName) # logger.info('Merge header, middle and trailer subpart into: %s' % internal_mkv_name)
# Extract internal part of MKV # Extract internal part of MKV
extractMKVPart(mkvmerge=paths['mkvmerge'], inputFile=mkv, outputFile=internalMKV, extractMKVPart(mkvmerge=paths['mkvmerge'], inputFile=mkv, outputFile=internal_mkv,
begin=head_iframe_ts, end=tail_iframe_ts) begin=head_iframe_ts, end=tail_iframe_ts)
# Extract video stream of internal part as a raw H264 and its timestamps. # Extract video stream of internal part as a raw H264 and its timestamps.
logger.info('Extract video track as raw H264 file.') logger.info('Extract video track as raw H264 file.')
extractTrackFromMKV(mkvextract=paths['mkvextract'], inputFile=internalMKV, index=0, extractTrackFromMKV(mkvextract=paths['mkvextract'], inputFile=internal_mkv, index=0,
outputFile=internalH264, timestamps=internalH264TS) outputFile=internal_h264, timestamps=internal_h264_ts)
# Remove video track from internal part of MKV # Remove video track from internal part of MKV
logger.info('Remove video track from %s', internalMKVName) logger.info('Remove video track from %s', internal_mkv_name)
removeVideoTracksFromMKV(mkvmerge=paths['mkvmerge'], inputFile=internalMKV, removeVideoTracksFromMKV(mkvmerge=paths['mkvmerge'], inputFile=internal_mkv,
outputFile=internalNoVideoMKV) outputFile=internal_novideo_mkv)
temporaries.append(internalMKV) temporaries.append(internal_mkv)
temporaries.append(internalH264) temporaries.append(internal_h264)
temporaries.append(internalH264TS) temporaries.append(internal_h264_ts)
temporaries.append(internalNoVideoMKV) temporaries.append(internal_novideo_mkv)
h264parts.append(internalH264) h264parts.append(internal_h264)
h264_ts.append(internalH264TS) h264_ts.append(internal_h264_ts)
subparts.append(internalNoVideoMKV) subparts.append(internal_novideo_mkv)
if (not args.coarse) and (nb_tail_frames > args.threshold): if (not args.coarse) and (nb_tail_frames > args.threshold):
# We extract all frames between the I-frame (including it) upto the end. # We extract all frames between the I-frame (including it) upto the end.
h264Tail, h264TailTS, mkvTail = extractAllStreams(ffmpeg=paths['ffmpeg'], h264_tail, h264_tail_ts, mkv_tail = extractAllStreams(ffmpeg=paths['ffmpeg'],
ffprobe=paths['ffprobe'], ffprobe=paths['ffprobe'],
inputFile=mkv, begin=tail_iframe_ts, inputFile=mkv, begin=tail_iframe_ts,
end=ts2, nbFrames=nb_tail_frames, end=ts2, nbFrames=nb_tail_frames,
@@ -3420,14 +3423,14 @@ def main():
temporaries=temporaries, temporaries=temporaries,
dumpMemFD=args.dump) dumpMemFD=args.dump)
if mkvTail is not None: if mkv_tail is not None:
subparts.append(mkvTail) subparts.append(mkv_tail)
if h264Tail is not None: if h264_tail is not None:
avcconfig = getAvcConfigFromH264(h264Tail) avcconfig = getAvcConfigFromH264(h264_tail)
other_avc_configs.append(avcconfig) other_avc_configs.append(avcconfig)
h264parts.append(h264Tail) h264parts.append(h264_tail)
if h264TailTS is not None: if h264_tail_ts is not None:
h264_ts.append(h264TailTS) h264_ts.append(h264_tail_ts)
logger.info('Merging MKV: %s', subparts) logger.info('Merging MKV: %s', subparts)
part = mergeMKVs(mkvmerge=paths['mkvmerge'], inputs=subparts, part = mergeMKVs(mkvmerge=paths['mkvmerge'], inputs=subparts,
@@ -3441,77 +3444,77 @@ def main():
checks.append(pos) checks.append(pos)
# When using coarse option there is a single AVC configuration. # When using coarse option there is a single AVC configuration.
for avcConfig in other_avc_configs: for avc_config in other_avc_configs:
main_avc_config.merge(avcConfig) main_avc_config.merge(avc_config)
logger.debug('Merged AVC configuration: %s', main_avc_config) logger.debug('Merged AVC configuration: %s', main_avc_config)
nbMKVParts = len(mkvparts) nb_mkv_parts = len(mkvparts)
if nbMKVParts > 0: if nb_mkv_parts > 0:
try: try:
fullH264 = open(f'{basename}-full.h264', 'w+', encoding='utf8') full_h264 = open(f'{basename}-full.h264', 'w+', encoding='utf8')
except IOError: except IOError:
logger.error('Impossible to create file full H264 stream.') logger.error('Impossible to create file full H264 stream.')
exit(-1) exit(-1)
logger.info('Merging all H264 tracks') logger.info('Merging all H264 tracks')
concatenateH264Parts(h264parts=h264parts, output=fullH264) concatenateH264Parts(h264parts=h264parts, output=full_h264)
temporaries.append(fullH264) temporaries.append(full_h264)
try: try:
fullH264TS = open(f'{basename}-ts.txt', 'w+', encoding='utf8') full_h264_ts = open(f'{basename}-ts.txt', 'w+', encoding='utf8')
except IOError: except IOError:
logger.error('Impossible to create file containing all video timestamps.') logger.error('Impossible to create file containing all video timestamps.')
exit(-1) exit(-1)
logger.info('Merging H264 timestamps') logger.info('Merging H264 timestamps')
concatenateH264TSParts(h264TSParts=h264_ts, output=fullH264TS) concatenateH264TSParts(h264TSParts=h264_ts, output=full_h264_ts)
temporaries.append(fullH264TS) temporaries.append(full_h264_ts)
finalNoVideoName = f'{basename}-novideo.mkv' final_novideo_name = f'{basename}-novideo.mkv'
finalWithVideoName = f'{basename}-video.mkv' final_with_video_name = f'{basename}-video.mkv'
if nbMKVParts > 1: if nb_mkv_parts > 1:
logger.info('Merging all audio and subtitles parts: %s', mkvparts) logger.info('Merging all audio and subtitles parts: %s', mkvparts)
mergeMKVs(mkvmerge=paths['mkvmerge'], inputs=mkvparts, outputName=finalNoVideoName, mergeMKVs(mkvmerge=paths['mkvmerge'], inputs=mkvparts, outputName=final_novideo_name,
concatenate=True) concatenate=True)
elif nbMKVParts == 1: elif nb_mkv_parts == 1:
copyfile('part-1.mkv', finalNoVideoName) copyfile('part-1.mkv', final_novideo_name)
else: else:
logger.info("Nothing else to do.") logger.info("Nothing else to do.")
copyfile(mkvfilename, finalWithVideoName) copyfile(mkvfilename, final_with_video_name)
if nbMKVParts >=1 : if nb_mkv_parts >=1 :
try: try:
finalNoVideo = open(finalNoVideoName, 'r', encoding='utf8') final_novideo = open(final_novideo_name, 'r', encoding='utf8')
except IOError: except IOError:
logger.error('Impossible to open file: %s.', finalNoVideoName) logger.error('Impossible to open file: %s.', final_novideo_name)
exit(-1) exit(-1)
temporaries.append(finalNoVideo) temporaries.append(final_novideo)
fullH264TS.seek(0) full_h264_ts.seek(0)
logger.info('Merging final video track and all other tracks together') logger.info('Merging final video track and all other tracks together')
finalWithVideo = mergeMKVs(mkvmerge=paths['mkvmerge'], inputs=[fullH264, finalNoVideo], final_with_video = mergeMKVs(mkvmerge=paths['mkvmerge'], inputs=[full_h264, final_novideo],
outputName=finalWithVideoName, concatenate=False, outputName=final_with_video_name, concatenate=False,
timestamps={0: fullH264TS}) timestamps={0: full_h264_ts})
finalCodecPrivateData = dumpCodecPrivateData(main_avc_config) final_codec_private_data = dumpCodecPrivateData(main_avc_config)
logger.debug('Final codec private data: %s', hexdump.dump(finalCodecPrivateData, sep=':')) logger.debug('Final codec private data: %s', hexdump.dump(final_codec_private_data, sep=':'))
logger.info('Changing codec private data with the new one.') logger.info('Changing codec private data with the new one.')
changeCodecPrivateData(paths['mkvinfo'], finalWithVideo, finalCodecPrivateData) changeCodecPrivateData(paths['mkvinfo'], final_with_video, final_codec_private_data)
if args.srt: if args.srt:
if not all_optional_tools: if not all_optional_tools:
logger.warning("Missing tools for extracting subtitles.") logger.warning("Missing tools for extracting subtitles.")
move(finalWithVideoName, args.outputFile) move(final_with_video_name, args.outputFile)
else: else:
# Final cut is not any more the final step. # Final cut is not any more the final step.
temporaries.append(finalWithVideo) temporaries.append(final_with_video)
duration = getMovieDuration(paths['ffprobe'], finalWithVideo) duration = getMovieDuration(paths['ffprobe'], final_with_video)
supportedLangs = getTesseractSupportedLang(paths['tesseract']) supported_langs = getTesseractSupportedLang(paths['tesseract'])
logger.info('Supported lang: %s', supportedLangs) logger.info('Supported lang: %s', supported_langs)
logger.info('Find subtitles tracks and language.') logger.info('Find subtitles tracks and language.')
subtitles = findSubtitlesTracks(paths['ffprobe'], finalWithVideo) subtitles = findSubtitlesTracks(paths['ffprobe'], final_with_video)
logger.info(subtitles) logger.info(subtitles)
sts = {} sts = {}
for subtitle in subtitles: for subtitle in subtitles:
@@ -3532,10 +3535,10 @@ def main():
logger.info(sts) logger.info(sts)
if len(sts) > 0: if len(sts) > 0:
listOfSubtitles = extractSRT(paths['mkvextract'], finalWithVideoName, sts, list_of_subtitles = extractSRT(paths['mkvextract'], final_with_video_name, sts,
supportedLangs) supported_langs)
logger.info(listOfSubtitles) logger.info(list_of_subtitles)
for idx_name, sub_name, _, _ in listOfSubtitles: for idx_name, sub_name, _, _ in list_of_subtitles:
try: try:
idx = open(idx_name,'r', encoding='utf8') idx = open(idx_name,'r', encoding='utf8')
except IOError: except IOError:
@@ -3550,15 +3553,15 @@ def main():
temporaries.append(idx) temporaries.append(idx)
temporaries.append(sub) temporaries.append(sub)
ocr = doOCR(paths['vobsubocr'], listOfSubtitles, duration, temporaries, args.dump) ocr = doOCR(paths['vobsubocr'], list_of_subtitles, duration, temporaries, args.dump)
logger.info(ocr) logger.info(ocr)
# Remux SRT subtitles # Remux SRT subtitles
remuxSRTSubtitles(paths['mkvmerge'], finalWithVideo, args.outputFile, ocr) remuxSRTSubtitles(paths['mkvmerge'], final_with_video, args.outputFile, ocr)
else: else:
copyfile(finalWithVideoName, args.outputFile) copyfile(final_with_video_name, args.outputFile)
else: else:
move(finalWithVideoName, args.outputFile) move(final_with_video_name, args.outputFile)
if not args.keep: if not args.keep:
logger.info("Cleaning temporary files") logger.info("Cleaning temporary files")