From e192c66157ab95762bd1b5f209de161d44205ec7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Tronel?= Date: Sun, 26 Oct 2025 21:01:50 +0100 Subject: [PATCH] More linting: camel case for variable names, f-format strings. --- removeads.py | 692 ++++++++++++++++++++++++++------------------------- 1 file changed, 351 insertions(+), 341 deletions(-) diff --git a/removeads.py b/removeads.py index 2fcfe7f..3e60b2a 100755 --- a/removeads.py +++ b/removeads.py @@ -114,7 +114,7 @@ def getFrameRate(ffprobe, inputFile): interlaced = False params = [ffprobe, '-loglevel', 'quiet', '-select_streams', 'v', '-show_frames', - '-read_intervals', '00%+30', '-of', 'json', '/proc/self/fd/%d' % infd] + '-read_intervals', '00%+30', '-of', 'json', f'/proc/self/fd/{infd:d}'] env = {**os.environ, 'LANG': 'C'} with Popen(params, stdout=PIPE, close_fds=False, env=env) as ffprobe: out, _ = ffprobe.communicate() @@ -130,10 +130,8 @@ def getFrameRate(ffprobe, inputFile): min_ts = ts if max_ts is None: max_ts = ts - if ts < min_ts: - min_ts = ts - if ts > max_ts: - max_ts = ts + min_ts = min(min_ts, ts) + max_ts = max(max_ts, ts) nb_frames1+=1 if 'duration_time' in frame: mean_duration+=float(frame['duration_time']) @@ -215,13 +213,13 @@ def extractSRT(mkvextract, fileName, subtitles, langs): ocrlang ='osd' if len(subtitles[lang]) == 1: - params.append('%d:%s' % (subtitles[lang][0], lang)) - res.append(('%s.idx' % lang, '%s.sub' % lang, lang, ocrlang)) + params.append(f'{subtitles[lang][0]:d}:{lang}') + res.append((f'{lang}.idx', f'{lang}.sub', lang, ocrlang)) else: count = 1 for track in subtitles[lang]: - params.append('%d:%s-%d' % (track, lang, count)) - res.append(('%s-%d.idx' % (lang,count), '%s-%d.sub' % (lang,count), lang, ocrlang)) + params.append(f'{track:d}:{lang}-{count:d}') + res.append((f'{lang}-{count:d}.idx', f'{lang}-{count:d}.sub', lang, ocrlang)) count = count+1 logger.debug('Executing %s', params) @@ -259,7 +257,7 @@ def doOCR(vobsubocr, idxs, duration, temporaries, dumpMemFD=False): res = [] for idx_name, _, lang, iso in idxs: - srtname = '%s.srt' % os.path.splitext(idx_name)[0] + srtname = f'{os.path.splitext(idx_name)[0]}.srt' # Tesseract seems to recognize the three dots ... as "su" ldots = re.compile('^su\n$') # Timestamps produced by vobsubocr: 01:52:19,861 --> 01:52:21,641 @@ -351,7 +349,7 @@ def getCodecPrivateDataFromMKV(mkvinfo, inputFile): # 64 00 28 ac d9 40 78 04 4f dc d4 04 04 05 00 00 92 ef 00 1d ad a6 1f 16 2d 96 01 00 06 68 fb\ # a3 cb 22 c0 fd f8 f8 00 at 406 size 51 data size 48 - with Popen([mkvinfo, '-z', '-X', '-P', '/proc/self/fd/%d' % infd ], stdout=PIPE, + with Popen([mkvinfo, '-z', '-X', '-P', f'/proc/self/fd/{infd:d}'], stdout=PIPE, close_fds=False, env=env) as mkvinfo: out, _ = mkvinfo.communicate() out = out.decode('utf8') @@ -499,7 +497,7 @@ def parseRBSPTrailingBits(buf, bit_position): bit_position, one = readBit(buf, bit_position) if one==0: - raise Exception('Stop bit should be equal to one. Read: %d' % one) + raise Exception(f'Stop bit should be equal to one. Read: {one:d}') while bit_position%8 != 0: bit_position, zero = readBit(buf, bit_position) if zero==1: @@ -560,7 +558,7 @@ def RBSP2SODB(buf): # Reverse operation SODB to RBSP. def SODB2RBSP(buf): logger = logging.getLogger(__name__) - logger.debug('SODB: %s' % hexdump.dump(buf, sep=':')) + logger.debug('SODB: %s', hexdump.dump(buf, sep=':')) res = buf for b in [ b'\x03', b'\x00', b'\x01', b'\x02']: @@ -905,8 +903,8 @@ class SPS: logger.error('Unknow profile: %x', self.profile_idc) return [] - level = '%d.%d' % (floor(self.level_idc/10), self.level_idc % 10) - x264opts.extend(['sps-id=%d' % self.seq_parameter_set_id] ) + level = f'{floor(self.level_idc/10):d}.{self.level_idc % 10:d}' + x264opts.extend([f'sps-id={self.seq_parameter_set_id:d}'] ) if self.bit_depth_chroma_minus8 not in [0,1,2,4,6,8]: logger.error('Bit depth of chrominance is not supported: %d', @@ -930,7 +928,7 @@ class SPS: logger.error('Unknow chrominance format: %x', self.chroma_format_idc) return [] - res = ['-profile:v:%d' % videoID, self.profile_idc, '-level:v:%d' % videoID, level] + res = [f'-profile:v:{videoID:d}', self.profile_idc, f'-level:v:{videoID:d}', level] return res def fromBytes(self, buf): @@ -942,13 +940,13 @@ class SPS: # NAL Unit SPS bit_position, zero = readBit(buf, bit_position) if zero != 0: - raise Exception('Reserved bit is not equal to 0: %d' % zero ) + raise Exception(f'Reserved bit is not equal to 0: {zero:d}') bit_position, nal_ref_idc = readBits(buf, bit_position,2) if nal_ref_idc != 3: - raise Exception('NAL ref idc is not equal to 3: %d' % nal_ref_idc ) + raise Exception(f'NAL ref idc is not equal to 3: {nal_ref_idc:d}') bit_position, nal_unit_type = readBits(buf, bit_position,5) if nal_unit_type != 7: - raise Exception('NAL unit type is not a SPS: %d' % nal_unit_type ) + raise Exception(f'NAL unit type is not a SPS: {nal_unit_type:d}') bit_position, self.profile_idc = readByte(buf, bit_position) bit_position, self.constraint_set0_flag = readBit(buf,bit_position) @@ -959,7 +957,7 @@ class SPS: bit_position, self.constraint_set5_flag = readBit(buf,bit_position) bit_position, v = readBits(buf, bit_position, 2) if v!=0: - raise Exception('Reserved bits different from 0b00: %x' % v) + raise Exception(f'Reserved bits different from 0b00: {v:x}') bit_position, self.level_idc = readByte(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]: @@ -992,7 +990,8 @@ class SPS: elif self.pic_order_cnt_type == 1: bit_position, self.delta_pic_order_always_zero_flag = readBoolean(buf, bit_position) bit_position, self.offset_for_non_ref_pic = readSignedExpGolomb(buf, bit_position) - bit_position, self.offset_for_top_to_bottom_field = readSignedExpGolomb(buf, bit_position) + bit_position, self.offset_for_top_to_bottom_field = readSignedExpGolomb(buf, + bit_position) bit_position, self.num_ref_frames_in_pic_order_cnt_cycle =\ readUnsignedExpGolomb(buf, bit_position) for i in range(0, self.num_ref_frames_in_pic_order_cnt_cycle): @@ -1051,7 +1050,8 @@ class SPS: bit_position = writeBit(buf, bit_position, self.separate_colour_plane_flag) bit_position = writeUnsignedExpGolomb(buf, bit_position, self.bit_depth_luma_minus8) bit_position = writeUnsignedExpGolomb(buf, bit_position, self.bit_depth_chroma_minus8) - bit_position = writeBoolean(buf, bit_position, self.qpprime_y_zero_transform_bypass_flag ) + bit_position = writeBoolean(buf, bit_position, + self.qpprime_y_zero_transform_bypass_flag) bit_position = writeBoolean(buf, bit_position, self.seq_scaling_matrix_present_flag) if self.seq_scaling_matrix_present_flag: nb_matrices = 12 if self.chroma_format_idc == 3 else 8 @@ -1083,7 +1083,8 @@ class SPS: bit_position = writeUnsignedExpGolomb(buf, bit_position, self.max_num_ref_frames) bit_position = writeBoolean(buf, bit_position, self.gaps_in_frame_num_value_allowed_flag) bit_position = writeUnsignedExpGolomb(buf, bit_position, self.pic_width_in_mbs_minus1) - bit_position = writeUnsignedExpGolomb(buf, bit_position, self.pic_height_in_map_units_minus1) + bit_position = writeUnsignedExpGolomb(buf, bit_position, + self.pic_height_in_map_units_minus1) bit_position = writeBoolean(buf, bit_position, self.frame_mbs_only_flag) if not self.frame_mbs_only_flag: bit_position = writeBoolean(buf, bit_position, self.mb_adaptive_frame_field_flag) @@ -1144,19 +1145,19 @@ class PPS: # PPS are located at byte boundary def fromBytes(self, buf, chroma_format_idc): logger = logging.getLogger(__name__) - logger.debug('Parsing: %s' % (hexdump.dump(buf,sep=':'))) + logger.debug('Parsing: %s', (hexdump.dump(buf,sep=':'))) bit_position=0 # NAL Unit PPS bit_position, zero = readBit(buf, bit_position) if zero != 0: - raise Exception('Reserved bit is not equal to 0: %d' % zero ) + raise Exception(f'Reserved bit is not equal to 0: {zero:d}') bit_position, nal_ref_idc = readBits(buf, bit_position,2) if nal_ref_idc != 3: - raise Exception('NAL ref idc is not equal to 3: %d' % nal_ref_idc ) + raise Exception(f'NAL ref idc is not equal to 3: {nal_ref_idc:d}') bit_position, nal_unit_type = readBits(buf, bit_position,5) if nal_unit_type != 8: - raise Exception('NAL unit type is not a PPS: %d' % nal_unit_type ) + raise Exception(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.seq_parameter_set_id = readUnsignedExpGolomb(buf, bit_position) @@ -1178,7 +1179,8 @@ class PPS: bit_position, v = readUnsignedExpGolomb(buf, bit_position) self.bottom_right[i] = v elif self.slice_group_map_type in [3,4,5]: - bit_position, self.slice_group_change_direction_flag = readBoolean(buf, bit_position) + bit_position, self.slice_group_change_direction_flag = readBoolean(buf, + bit_position) bit_position, self.slice_group_change_rate_minus1 =\ readUnsignedExpGolomb(buf, bit_position) elif self.slice_group_map_type == 6: @@ -1222,7 +1224,8 @@ class PPS: self.pic_scaling_list.append(matrix) else: self.pic_scaling_list.append([]) - bit_position, self.second_chroma_qp_index_offset = readSignedExpGolomb(buf, bit_position) + bit_position, self.second_chroma_qp_index_offset = readSignedExpGolomb(buf, + bit_position) logger.info("parse RBSP") bit_position = parseRBSPTrailingBits(buf, bit_position) @@ -1259,7 +1262,8 @@ class PPS: v = self.bottom_right[i] bit_position = writeUnsignedExpGolomb(buf, bit_position, v) elif self.slice_group_map_type in [3,4,5]: - bit_position = writeBoolean(buf, bit_position, self.slice_group_change_direction_flag) + bit_position = writeBoolean(buf, bit_position, + self.slice_group_change_direction_flag) bit_position = writeUnsignedExpGolomb(buf, bit_position, self.slice_group_change_rate_minus1) elif self.slice_group_map_type == 6: @@ -1293,16 +1297,16 @@ class PPS: nb_matrices = 6 for i in range(0, nb_matrices): matrix = self.pic_scaling_list[i] - logger.info("Retrieved pic scaling matrix: %s %d" % (matrix, len(matrix))) + logger.info("Retrieved pic scaling matrix: %s %d", matrix, len(matrix)) present = len(matrix)!=0 - logger.info("Matrix is present: %s" % present) + logger.info("Matrix is present: %s", present) bit_position = writeBoolean(buf, bit_position, present) if present: if i<6: - logger.info("Writing matrix: %s" % matrix) + logger.info("Writing matrix: %s", matrix) bit_position = writeScalingList(buf, bit_position, 16, matrix) else: - logger.info("Writing matrix: %s" % matrix) + logger.info("Writing matrix: %s", matrix) bit_position = writeScalingList(buf, bit_position, 64, matrix) bit_position = writeSignedExpGolomb(buf, bit_position, self.second_chroma_qp_index_offset) @@ -1334,7 +1338,7 @@ class AVCDecoderConfiguration: def fromBytes(self, buf): logger = logging.getLogger(__name__) - logger.debug('Parsing: %s' % (hexdump.dump(buf,sep=':'))) + logger.debug('Parsing: %s', (hexdump.dump(buf,sep=':'))) bit_position = 0 bit_position, self.configurationVersion = readByte(buf, bit_position) bit_position, self.AVCProfileIndication = readByte(buf, bit_position) @@ -1342,17 +1346,17 @@ class AVCDecoderConfiguration: bit_position, self.AVCLevelIndication = readByte(buf, bit_position) bit_position, v = readBits(buf, bit_position, 6) if v != 0b111111: - raise Exception('Reserved bits are not equal to 0b111111: %x' % v ) + raise Exception(f'Reserved bits are not equal to 0b111111: {v:x}') bit_position, self.lengthSizeMinusOne = readBits(buf, bit_position, 2) bit_position, v = readBits(buf, bit_position, 3) if v != 0b111: - raise Exception('Reserved bits are not equal to 0b111: %x' % v) + raise Exception(f'Reserved bits are not equal to 0b111: {v:x}') 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): bit_position, length = readWord(buf, bit_position) if bit_position % 8 != 0: - raise Exception('SPS is not located at a byte boundary: %d' % bit_position ) + raise Exception(f'SPS is not located at a byte boundary: {bit_position:d}') sps = SPS() sodb = RBSP2SODB(buf[floor(bit_position/8):]) @@ -1371,11 +1375,11 @@ class AVCDecoderConfiguration: logger.debug('Bit position:%d. Reading one byte of: %s', bit_position, hexdump.dump(buf[floor(bit_position/8):], sep=':')) bit_position, self.numOfPictureParameterSets = readByte(buf, bit_position) - logger.debug('Number of PPS: %d' % self.numOfPictureParameterSets) + logger.debug('Number of PPS: %d', self.numOfPictureParameterSets) for _ in range(0,self.numOfPictureParameterSets): bit_position, length = readWord(buf, bit_position) if bit_position % 8 != 0: - raise Exception('PPS is not located at a byte boundary: %d' % bit_position ) + raise Exception('PPS is not located at a byte boundary: {bit_position:d}') pps = PPS() sodb = RBSP2SODB(buf[floor(bit_position/8):]) @@ -1396,15 +1400,15 @@ class AVCDecoderConfiguration: if self.AVCProfileIndication in [100, 110, 122, 144]: bit_position, reserved = readBits(buf, bit_position, 6) if reserved != 0b111111: - raise Exception('Reserved bits are different from 111111: %x' % reserved) + raise Exception(f'Reserved bits are different from 111111: {reserved:x}') bit_position, self.chroma_format = readBits(buf, bit_position, 2) bit_position, reserved = readBits(buf, bit_position, 5) if reserved != 0b11111: - raise Exception('Reserved bits are different from 11111: %x' % reserved) + raise Exception(f'Reserved bits are different from 11111: {reserved:x}') bit_position, self.bit_depth_luma_minus8 = readBits(buf, bit_position, 3) bit_position, reserved = readBits(buf, bit_position, 5) if reserved != 0b11111: - raise Exception('Reserved bits are different from 11111: %x' % reserved) + raise Exception(f'Reserved bits are different from 11111: {reserved:x}') bit_position, self.bit_depth_chroma_minus8 = readBits(buf, bit_position, 3) bit_position, self.numOfSequenceParameterSetExt = readByte(buf, bit_position) for _ in range(0, self.numOfSequenceParameterSetExt): @@ -1434,17 +1438,17 @@ class AVCDecoderConfiguration: rbsp = SODB2RBSP(sodb) rbsp_length = len(rbsp) - logger.debug('SODB length: %d RBSP length:%d' % (sodb_length, rbsp_length)) + logger.debug('SODB length: %d RBSP length:%d', sodb_length, rbsp_length) bit_position = writeWord(buf, bit_position, rbsp_length) buf.extend(rbsp) bit_position+=rbsp_length*8 - logger.debug('2. Buffer: %s' % hexdump.dump(buf, sep=':')) + logger.debug('2. Buffer: %s', hexdump.dump(buf, sep=':')) bit_position = writeByte(buf, bit_position, self.numOfPictureParameterSets) for ppsid in self.pps: - logger.debug('Writing PPS: %d' % ppsid) + logger.debug('Writing PPS: %d', ppsid) pps = self.pps[ppsid] # TODO: does chroma_format should come from self ? sodb = pps.toBytes(self.chroma_format) @@ -1452,7 +1456,7 @@ class AVCDecoderConfiguration: rbsp = SODB2RBSP(sodb) rbsp_length = len(rbsp) - logger.debug('SODB length: %d RBSP length:%d' % (sodb_length, rbsp_length)) + logger.debug('SODB length: %d RBSP length:%d', sodb_length, rbsp_length) bit_position = writeWord(buf, bit_position, rbsp_length) buf.extend(rbsp) @@ -1505,8 +1509,8 @@ class AVCDecoderConfiguration: if spsid in self.sps: localsps = self.sps[spsid] if sps!=localsps: - raise Exception('Profile are not compatible. They contain two different SPS\ - with the same identifier (%d): %s\n%s\n' % (spsid, localsps, sps)) + raise Exception(f'Profile are not compatible. They contain two different SPS\ + with the same identifier ({spsid:d}): {localsps}\n{sps}\n') self.sps[spsid] = sps self.numOfSequenceParameterSets = len(self.sps) @@ -1516,8 +1520,8 @@ class AVCDecoderConfiguration: if ppsid in self.pps: localpps = self.pps[ppsid] if pps!=localpps: - raise Exception('Profile are not compatible. They contain two different PPS\ - with the same identifier (%d): %s\n%s\n' % (ppsid, localpps, pps)) + raise Exception(f'Profile are not compatible. They contain two different PPS\ + with the same identifier ({ppsid:d}): {localpps}\n{pps}\n') self.pps[ppsid] = pps self.numOfPictureParameterSets = len(self.pps) @@ -1526,9 +1530,9 @@ class AVCDecoderConfiguration: def parseCodecPrivate(codecPrivateData): if codecPrivateData[0] != 0x63: - raise Exception('Matroska header is wrong: %x' % codecPrivateData[0]) + raise Exception(f'Matroska header is wrong: {codecPrivateData[0]:x}') if codecPrivateData[1] != 0xA2: - raise Exception('Matroska header is wrong: %x' % codecPrivateData[1]) + raise Exception(f'Matroska header is wrong: {codecPrivateData[1]:x}') length = codecPrivateData[2] if length == 0: raise Exception('Matroska length cannot start with zero byte.') @@ -1557,14 +1561,14 @@ def getAvcConfigFromH264(inputFile): bit_position = 0 bit_position, start_code = readLong(sodb, bit_position) if start_code != 1: - raise Exception('Starting code not detected: %x' % start_code) + raise Exception(f'Starting code not detected: {start_code:x}') sps = SPS() bit_length = sps.fromBytes(sodb[4:]) bit_position+=bit_length bit_position, start_code = readLong(sodb, bit_position) if start_code != 1: - raise Exception('Starting code not detected: %x' % start_code) + raise Exception('Starting code not detected: {start_code:x}') pps = PPS() bit_length = pps.fromBytes(sodb[floor(bit_position/8):], sps.chroma_format_idc) logger.debug(pps) @@ -1603,7 +1607,7 @@ def parseMKVTree(mkvinfo, inputFile): env = {**os.environ, 'LANG': 'C'} elements = {} - with Popen([mkvinfo, '-z', '-X', '-P', '/proc/self/fd/%d' % infd ], stdout=PIPE, + with Popen([mkvinfo, '-z', '-X', '-P', f'/proc/self/fd/{infd:d}'], stdout=PIPE, close_fds=False, env=env) as mkvinfo: out, _ = mkvinfo.communicate() out = out.decode('utf8') @@ -1753,7 +1757,7 @@ def changeEBMLElementSize(inputFile, position, addendum): buf = read(infd, 1) size_head = int.from_bytes(buf, byteorder='big') - logger.info('First byte of size: %x' % size_head) + logger.info('First byte of size: %x', size_head) mask=128 found = False for i in range(1,9): @@ -1771,33 +1775,33 @@ def changeEBMLElementSize(inputFile, position, addendum): logger.info('Size of data size: %d.', size_of_data_size) lseek(infd, position, SEEK_SET) - oldSizeBuf = read(infd, size_of_data_size) + old_size_buf = read(infd, size_of_data_size) max_size = 2**(size_of_data_size*7)-2 - size_of_data = int.from_bytes(oldSizeBuf, byteorder='big') - logger.info('Size of data with mask: %x mask: %d.' % (size_of_data, mask)) + size_of_data = int.from_bytes(old_size_buf, byteorder='big') + logger.info('Size of data with mask: %x mask: %d.', size_of_data, mask) size_of_data-= (mask<<((size_of_data_size-1)*8)) logger.info('Found element at position: %d, size of type: %d size of data: %d \ maximal size: %d.', initial_position, type_size, size_of_data, max_size) - newSize = size_of_data+addendum + new_size = size_of_data+addendum delta = 0 - if newSize > max_size: + if new_size > max_size: # TODO: Test this code ... - new_encoded_size = getEBMLLength(newSize) - sizeOfNewEncodedSize = len(new_encoded_size) - if sizeOfNewEncodedSize <= size_of_data_size: + new_encoded_size = getEBMLLength(new_size) + size_of_new_encoded_size = len(new_encoded_size) + if size_of_new_encoded_size <= size_of_data_size: logger.error('New encoded size is smaller (%d) or equal than previous size (%d).\ - This should not happen.', sizeOfNewEncodedSize, size_of_data_size) + This should not happen.', size_of_new_encoded_size, size_of_data_size) exit(-1) # The difference of length between old size field and new one. - delta = sizeOfNewEncodedSize - size_of_data_size - fileLength = fstat(infd).st_size + delta = size_of_new_encoded_size - size_of_data_size + file_length = fstat(infd).st_size # We seek after actual length field lseek(infd, position+size_of_data_size, SEEK_SET) # We read the rest of file - tail = read(infd, fileLength-(position+size_of_data_size)) + tail = read(infd, file_length-(position+size_of_data_size)) # We increase file length - ftruncate(infd, fileLength+delta) + ftruncate(infd, file_length+delta) # We go to the beginning of length field lseek(infd, position, SEEK_SET) # We write the new length field @@ -1805,13 +1809,13 @@ def changeEBMLElementSize(inputFile, position, addendum): # We overwrite the rest of file with its previous content that has been offset. write(infd, tail) else: - size = newSize + ((128>>(size_of_data_size-1))<<((size_of_data_size-1)*8)) - newSizeBuf = (size).to_bytes(size_of_data_size, byteorder='big') + size = new_size + ((128>>(size_of_data_size-1))<<((size_of_data_size-1)*8)) + new_size_buf = (size).to_bytes(size_of_data_size, byteorder='big') - logger.info('Old encoded size: %s New encoded size: %s', hexdump.dump(oldSizeBuf,sep=':'), - hexdump.dump(newSizeBuf, sep=':')) + logger.info('Old encoded size: %s New encoded size: %s', hexdump.dump(old_size_buf,sep=':'), + hexdump.dump(new_size_buf, sep=':')) lseek(infd, position, SEEK_SET) - write(infd, newSizeBuf) + write(infd, new_size_buf) # We return the potential increase in size of the file if the length field had to be increased. return delta @@ -1822,15 +1826,15 @@ def changeCodecPrivateData(mkvinfo, inputFile, codecData): infd = inputFile.fileno() lseek(infd, 0, SEEK_SET) - currentLength = fstat(infd).st_size - logger.info('Current size of file: %d' % currentLength) - position, currentData = getCodecPrivateDataFromMKV(mkvinfo, inputFile) - currentDataLength = len(currentData) - futureLength = currentLength - currentDataLength + len(codecData) - logger.info('Expected size of file: %d' % futureLength) + current_length = fstat(infd).st_size + logger.info('Current size of file: %d', current_length) + position, current_data = getCodecPrivateDataFromMKV(mkvinfo, inputFile) + current_data_length = len(current_data) + future_length = current_length - current_data_length + len(codecData) + logger.info('Expected size of file: %d', future_length) - logger.info('Current data at position %d: %s' % (position, hexdump.dump(currentData, sep=":"))) - logger.info('Future data: %s' % hexdump.dump(codecData, sep=":")) + logger.info('Current data at position %d: %s', position, hexdump.dump(current_data, sep=":")) + logger.info('Future data: %s', hexdump.dump(codecData, sep=":")) elements = parseMKVTree(mkvinfo, inputFile) @@ -1838,7 +1842,7 @@ def changeCodecPrivateData(mkvinfo, inputFile, codecData): for key in elements: pos, size = elements[key] if pos == position: - logger.info('Codec private data key: %s' % key) + logger.info('Codec private data key: %s', key) found = True break @@ -1846,34 +1850,34 @@ def changeCodecPrivateData(mkvinfo, inputFile, codecData): logger.error('Impossible to retrieve the key of codec private data') exit(-1) - if currentLength < futureLength: - lseek(infd, position+currentDataLength, SEEK_SET) - tail = read(infd, currentLength-(position+currentDataLength)) + if current_length < future_length: + lseek(infd, position+current_data_length, SEEK_SET) + tail = read(infd, current_length-(position+current_data_length)) # We extend the file at the end with zeroes - ftruncate(infd, futureLength) + ftruncate(infd, future_length) lseek(infd, position+len(codecData), SEEK_SET) write(infd, tail) lseek(infd, position, SEEK_SET) write(infd, codecData) - elif currentLength == futureLength: + elif current_length == future_length: # Almost nothing to do except overwriting old private codec data with new ones. lseek(infd, position, SEEK_SET) write(infd, codecData) else: - lseek(infd, position+currentDataLength, SEEK_SET) - tail = read(infd, currentLength-(position+currentDataLength)) + lseek(infd, position+current_data_length, SEEK_SET) + tail = read(infd, current_length-(position+current_data_length)) lseek(infd, position+len(codecData), SEEK_SET) write(infd, tail) lseek(infd, position, SEEK_SET) write(infd, codecData) # We reduce the length of file. - ftruncate(infd, futureLength) + ftruncate(infd, future_length) # We have to modify the tree elements up to the root that contains the codec private data. keys = key.split('.') logger.info(keys) - delta = futureLength-currentLength + delta = future_length-current_length # if there is no modification of the private codec data, no need to change anything. if delta != 0: for _ in range(0, len(keys)-1): @@ -1894,7 +1898,7 @@ def getFormat(ffprobe, inputFile): lseek(infd, 0, SEEK_SET) set_inheritable(infd, True) with Popen([ffprobe, '-loglevel', 'quiet', '-show_format', '-of', 'json', '-i', - '/proc/self/fd/%d' % infd], stdout=PIPE, close_fds=False) as ffprobe: + f'/proc/self/fd/{infd:d}'], stdout=PIPE, close_fds=False) as ffprobe: out, _ = ffprobe.communicate() out = json.load(BytesIO(out)) if 'format' in out: @@ -1912,7 +1916,7 @@ def getMovieDuration(ffprobe, inputFile): lseek(infd, 0, SEEK_SET) set_inheritable(infd, True) with Popen([ffprobe, '-loglevel', 'quiet', '-show_format', '-of', 'json', '-i', - '/proc/self/fd/%d' % infd], stdout=PIPE, close_fds=False) as ffprobe: + f'/proc/self/fd/{infd:d}'], stdout=PIPE, close_fds=False) as ffprobe: out, _ = ffprobe.communicate() out = json.load(BytesIO(out)) if 'format' in out and 'duration' in out['format']: @@ -1932,7 +1936,7 @@ def getVideoDimensions(ffprobe, inputFile): lseek(infd, 0, SEEK_SET) set_inheritable(infd, True) with Popen([ffprobe, '-loglevel', 'quiet', '-select_streams', 'v:0', '-show_entries',\ - 'stream=width,height', '-of', 'json', '-i', '/proc/self/fd/%d' % infd],\ + 'stream=width,height', '-of', 'json', '-i', f'/proc/self/fd/{infd:d}'],\ stdout=PIPE, close_fds=False) as ffprobe: out, _ = ffprobe.communicate() out = json.load(BytesIO(out)) @@ -1952,7 +1956,7 @@ def getStreams(ffprobe, inputFile): lseek(infd, 0, SEEK_SET) set_inheritable(infd, True) with Popen([ffprobe, '-loglevel', 'quiet', '-show_streams', '-of', 'json', '-i', - '/proc/self/fd/%d' % infd], stdout=PIPE, close_fds=False) as ffprobe: + f'/proc/self/fd/{infd:d}'], stdout=PIPE, close_fds=False) as ffprobe: out, _ = ffprobe.communicate() out = json.load(BytesIO(out)) if 'streams' in out: @@ -1969,7 +1973,7 @@ def withSubtitles(ffprobe, inputFile): lseek(infd, 0, SEEK_SET) set_inheritable(infd, True) with Popen([ffprobe, '-loglevel', 'quiet', '-show_streams', '-of', 'json', '-i', - '/proc/self/fd/%d' % infd], stdout=PIPE, close_fds=False) as ffprobe: + f'/proc/self/fd/{infd:d}'], stdout=PIPE, close_fds=False) as ffprobe: out, _ = ffprobe.communicate() out = json.load(BytesIO(out)) if 'streams' in out: @@ -2123,8 +2127,9 @@ def ffmpegConvert(ffmpeg, ffprobe, inputFile, inputFormat, outputFile, outputFor else: log = [ '-loglevel', 'quiet' ] - params = [ffmpeg, '-y',]+log+['-progress', '/dev/stdout', '-canvas_size', '%dx%d' % - (width, height), '-f', inputFormat, '-i', '/proc/self/fd/%d' % infd, '-map', '0:v', + params = [ffmpeg, '-y',]+log+['-progress', '/dev/stdout', '-canvas_size', + f'{width:d}x{height:d}', '-f', inputFormat, + '-i', f'/proc/self/fd/{infd:d}', '-map', '0:v', '-map', '0:a'] if subtitles: params.extend(['-map', '0:s']) @@ -2132,7 +2137,7 @@ def ffmpegConvert(ffmpeg, ffprobe, inputFile, inputFormat, outputFile, outputFor '-acodec', 'copy']) if subtitles: params.extend(['-scodec', 'dvdsub']) - params.extend(['-r:0', '25', '-f', outputFormat, '/proc/self/fd/%d' % outfd]) + params.extend(['-r:0', '25', '-f', outputFormat, f'/proc/self/fd/{outfd:d}']) logger.debug('Executing %s', params) @@ -2182,9 +2187,9 @@ def getFramesInStream(ffprobe, inputFile, begin, end, streamKind, subStreamId=0) infd = inputFile.fileno() set_inheritable(infd, True) - command = [ffprobe, '-loglevel', 'quiet', '-read_intervals', ('%s%%%s' %(begin, end)), - '-show_entries', 'frame', '-select_streams', '%s:%d' % (streamKind, subStreamId), - '-of', 'json', '/proc/self/fd/%d' % infd] + command = [ffprobe, '-loglevel', 'quiet', '-read_intervals', f'{begin}%{end}','-show_entries', + 'frame', '-select_streams', f'{streamKind}:{subStreamId:d}','-of', 'json', + f'/proc/self/fd/{infd:d}'] logger.debug('Executing: %s', command) with Popen(command, stdout=PIPE, close_fds=False) as ffprobe: @@ -2192,7 +2197,7 @@ def getFramesInStream(ffprobe, inputFile, begin, end, streamKind, subStreamId=0) frames = json.load(BytesIO(out)) status = ffprobe.wait() if status != 0: - logger.error('ffprobe failed with status code: %d' % status) + logger.error('ffprobe failed with status code: %d', status) return None # Sort frames by timestamp @@ -2212,7 +2217,7 @@ def getFramesInStream(ffprobe, inputFile, begin, end, streamKind, subStreamId=0) return res else: - logger.error('Impossible to retrieve frames inside file around [%s,%s]' % (begin, end)) + logger.error('Impossible to retrieve frames inside file around [%s,%s]', begin, end) return None # TODO: Finish implementation of this function and use it. @@ -2234,14 +2239,14 @@ def getNearestIDRFrame(ffprobe, inputFile, timestamp, before=True, delta=timedel idrs = [] # Retains only IDR frame - with Popen([ffprobe, '-loglevel', 'quiet', '-read_intervals', ('%s%%%s' %(tbegin, tend)), - '-skip_frame', 'nokey', '-show_entries', 'frame', '-select_streams', 'v:0', '-of', - 'json', '/proc/self/fd/%d' % infd], stdout=PIPE, close_fds=False) as ffprobe: + with Popen([ffprobe, '-loglevel', 'quiet', '-read_intervals', f'{tbegin}%{tend}','-skip_frame', + 'nokey', '-show_entries', 'frame', '-select_streams', 'v:0', '-of', 'json', + f'/proc/self/fd/{infd:d}'], stdout=PIPE, close_fds=False) as ffprobe: out, _ = ffprobe.communicate() frames = json.load(BytesIO(out)) status = ffprobe.wait() if status != 0: - logger.error('ffprobe failed with status code: %d' % status) + logger.error('ffprobe failed with status code: %d', status) return None if 'frames' in frames: @@ -2296,7 +2301,7 @@ def getNearestIFrame(ffprobe, inputFile, timestamp, before=True, deltaMax=timede for frame in iframes: ts = getTSFrame(frame) if ts is None: - logger.warning('I-frame with no timestamp: %s' % frame) + logger.warning('I-frame with no timestamp: %s', frame) continue if before and ts <= timestamp: @@ -2308,7 +2313,7 @@ def getNearestIFrame(ffprobe, inputFile, timestamp, before=True, deltaMax=timede break if found: - logger.info("Found i-frame at: %s" % iframe) + logger.info("Found i-frame at: %s", iframe) break else: delta+=timedelta(seconds=1) @@ -2316,31 +2321,31 @@ def getNearestIFrame(ffprobe, inputFile, timestamp, before=True, deltaMax=timede if iframe is not None: its = getTSFrame(iframe) - nbFrames = 0 + nb_frames = 0 for frame in frames: ts = getTSFrame(frame) if ts is None: - logger.warning('Frame without timestamp: %s' % frame) + logger.warning('Frame without timestamp: %s', frame) continue if before: if its <= ts and ts <= timestamp: - logger.info("Retrieve a frame between %s and %s at %s" % (its, timestamp, ts)) - nbFrames = nbFrames+1 + logger.info("Retrieve a frame between %s and %s at %s", its, timestamp, ts) + nb_frames = nb_frames+1 else: if timestamp <= ts and ts <= its: - logger.info("Retrieve a frame between %s and %s at %s" % (timestamp, ts, its)) - nbFrames = nbFrames+1 + logger.info("Retrieve a frame between %s and %s at %s", timestamp, ts, its) + nb_frames = nb_frames+1 else: - logger.error("Impossible to find I-frame between: %s and %s" % (tbegin, tend)) + logger.error("Impossible to find I-frame between: %s and %s", tbegin, tend) return 0, None - return(nbFrames, iframe) + return(nb_frames, iframe) def extractMKVPart(mkvmerge, inputFile, outputFile, begin, end): logger = logging.getLogger(__name__) - logger.info('Extract video between I-frames at %s and %s' % (begin,end)) + logger.info('Extract video between I-frames at %s and %s', begin,end) infd = inputFile.fileno() outfd = outputFile.fileno() lseek(infd, 0, SEEK_SET) @@ -2350,8 +2355,8 @@ def extractMKVPart(mkvmerge, inputFile, outputFile, begin, end): env = {**os.environ, 'LANG': 'C'} warnings = [] - command = [mkvmerge, '-o', '/proc/self/fd/%d' % outfd, '--split', 'parts:%s-%s' % (begin, end), - '/proc/self/fd/%d' % infd] + command = [mkvmerge, '-o', f'/proc/self/fd/{outfd:d}', '--split', f'parts:{begin}-{end}', + f'/proc/self/fd/{infd:d}'] logger.debug('Executing: %s', command) with Popen(command, stdout=PIPE, close_fds=False, env=env) as mkvmerge: @@ -2387,35 +2392,35 @@ def extractPictures(ffmpeg, inputFile, begin, nbFrames, width=640, height=480): set_inheritable(outfd, True) # PPM header # "P6\nWIDTH HEIGHT\n255\n" - headerLen=2+1+ceil(log(width, 10))+1+ceil(log(height, 10))+1+3+1 - logger.debug('Header length: %d' % headerLen) - imageLength = width*height*3+headerLen - length = imageLength*nbFrames - logger.debug("Estimated length: %d" % length) + header_len=2+1+ceil(log(width, 10))+1+ceil(log(height, 10))+1+3+1 + logger.debug('Header length: %d', header_len) + image_length = width*height*3+header_len + length = image_length*nbFrames + logger.debug("Estimated length: %d", length) - command = [ffmpeg, '-loglevel', 'quiet' ,'-y', '-ss', '%s'%begin, '-i', '/proc/self/fd/%d' % - infd, '-s', '%dx%d'%(width, height), '-vframes', '%d'%nbFrames, '-c:v', 'ppm', - '-f', 'image2pipe', '/proc/self/fd/%d' % outfd ] + command = [ffmpeg, '-loglevel', 'quiet' ,'-y', '-ss', f'{begin}', '-i', f'/proc/self/fd/{infd}', + '-s', f'{width:d}x{height:d}', '-vframes', f'{nbFrames:d}', '-c:v', 'ppm','-f', + 'image2pipe', f'/proc/self/fd/{outfd:d}'] logger.debug('Executing: %s', command) images = bytes() with Popen(command, stdout=PIPE, close_fds=False) as ffmpeg: status = ffmpeg.wait() if status != 0: - logger.error('Conversion failed with status code: %d' % status) + logger.error('Conversion failed with status code: %d', status) return None, None lseek(outfd, 0, SEEK_SET) images = read(outfd,length) if len(images) != length: - logger.error("Received %d bytes but %d were expected." % (len(images), length)) + logger.error("Received %d bytes but %d were expected.", len(images), length) return None, None lseek(outfd, 0, SEEK_SET) return images, outfd -def extractSound(ffmpeg, inputFile, begin, outputFileName, packetDuration, subChannel=0, - nbPackets=0, sampleRate=48000, nbChannels=2): +def extractSound(ffmpeg, inputFile, begin, outputFileName, packet_duration, subChannel=0, + nb_packets=0, sample_rate=48000, nb_channels=2): logger = logging.getLogger(__name__) outfd = memfd_create(outputFileName, flags=0) @@ -2424,18 +2429,18 @@ def extractSound(ffmpeg, inputFile, begin, outputFileName, packetDuration, subCh set_inheritable(infd, True) set_inheritable(outfd, True) sound = bytes() - length = int(nbChannels*sampleRate*4*nbPackets*packetDuration/1000) + length = int(nb_channels*sample_rate*4*nb_packets*packet_duration/1000) - command = [ffmpeg, '-y', '-loglevel', 'quiet', '-ss', '%s'%begin, '-i', '/proc/self/fd/%d' % - infd, '-frames:a:%d' % subChannel, '%d' % (nbPackets+1), '-c:a', 'pcm_s32le', - '-sample_rate', '%d' % sampleRate, '-channels', '%d' % nbChannels, '-f', 's32le', - '/proc/self/fd/%d' % outfd] + command = [ffmpeg, '-y', '-loglevel', 'quiet', '-ss', f'{begin}', '-i', f'/proc/self/fd/{infd}', + f'-frames:a:{subChannel:d}', f'{nb_packets+1:d}', '-c:a', 'pcm_s32le', + '-sample_rate', f'{sample_rate:d}', '-channels', f'{nb_channels:d}', '-f', 's32le', + f'/proc/self/fd/{outfd:d}'] logger.debug('Executing: %s', command) with Popen(command, stdout=PIPE, close_fds=False) as ffmpeg: status = ffmpeg.wait() if status != 0: - logger.error('Sound extraction returns error code: %d' % status) + logger.error('Sound extraction returns error code: %d', status) return None, None lseek(outfd, 0, SEEK_SET) @@ -2443,8 +2448,8 @@ def extractSound(ffmpeg, inputFile, begin, outputFileName, packetDuration, subCh if len(sound) != length: logger.info("Received %d bytes but %d were expected (channels=%d, freq=%d, packets=%d,\ - duration=%d ms).", len(sound), length, nbChannels, sampleRate, nbPackets, - packetDuration) + duration=%d ms).", len(sound), length, nb_channels, sample_rate, nb_packets, + packet_duration) return None, None return sound, outfd @@ -2456,10 +2461,10 @@ def dumpPPM(pictures, prefix, temporaries): pos = 0 picture = 0 - logger.debug('Dumping %d pictures: %s' % (len(pictures),prefix)) + logger.debug('Dumping %d pictures: %s', len(pictures),prefix) while pos 0: - packetDuration = getPacketDuration(packets[0]) - if packetDuration is None: + streamKind='a', subStreamId=audio_id) + nb_packets = len(packets) + logger.debug("Found %d packets to be extracted from audio track.", nb_packets) + if nb_packets > 0: + packet_duration = getPacketDuration(packets[0]) + if packet_duration is None: return None else: - packetDuration = 0 + packet_duration = 0 - logger.info("Extracting %d packets of audio stream: a:%d" , nbPackets, audioID) - tmpname = '%s-%d.pcm' % (filesPrefix,audioID) + logger.info("Extracting %d packets of audio stream: a:%d" , nb_packets, audio_id) + tmpname = f'{filesPrefix}-{audio_id:d}.pcm' - soundBytes, memfd = extractSound(ffmpeg=ffmpeg, inputFile=inputFile, begin=begin, - nbPackets=nbPackets, packetDuration=packetDuration, - outputFileName=tmpname, sampleRate=sampleRate, - nbChannels=nbChannels) + sound_bytes, memfd = extractSound(ffmpeg=ffmpeg, inputFile=inputFile, begin=begin, + nb_packets=nb_packets, + packet_duration=packet_duration, + outputFileName=tmpname, sample_rate=sample_rate, + nb_channels=nb_channels) - if soundBytes is None: + if sound_bytes is None: + logger.error('Impossible to extract sound track') exit(-1) memfds.append(memfd) @@ -2620,115 +2629,115 @@ def extractAllStreams(ffmpeg, ffprobe, inputFile, begin, end, streams, filesPref try: output = open(tmpname,'w') except IOError: - logger.error('Impossible to create file: %s' % tmpname) + logger.error('Impossible to create file: %s', tmpname) return None outfd = output.fileno() pos = 0 - while pos < len(soundBytes): - pos+=write(outfd, soundBytes[pos:]) + while pos < len(sound_bytes): + pos+=write(outfd, sound_bytes[pos:]) temporaries.append(output) # We rewind to zero the memory file descriptor lseek(memfd, 0, SEEK_SET) set_inheritable(memfd, True) - genericInputParams.extend(['-f', 's32le', '-ar', '%d'%sampleRate, '-ac', - '%d'%nbChannels, '-i', '/proc/self/fd/%d' % memfd]) - genericCodecParams.extend(['-c:a:%d' % audioID, codec, '-b:a:%d' % audioID, - '%d' % bitRate]) - audioID=audioID+1 + generic_input_params.extend(['-f', 's32le', '-ar', f'{sample_rate:d}', '-ac', + f'{nb_channels:d}', '-i', f'/proc/self/fd/{memfd:d}']) + generic_codec_params.extend([f'-c:a:{audio_id:d}', codec, f'-b:a:{audio_id:d}', + f'{bit_rate:d}']) + audio_id=audio_id+1 elif stream['codec_type'] == 'subtitle': - logger.info("Extracting a subtitle stream: s:%d" % subTitleID) + logger.info("Extracting a subtitle stream: s:%d", subtitle_id) codec = stream['codec_name'] - genericInputParams.extend(['-i', './empty.idx']) + generic_input_params.extend(['-i', './empty.idx']) if 'tags' in stream: if 'language' in stream['tags']: - genericCodecParams.extend(['-metadata:s:s:%d' % subTitleID, 'language=%s' % - stream['tags']['language']]) - genericCodecParams.extend(['-c:s:%d' % subTitleID, 'copy']) - subTitleID=subTitleID+1 + generic_codec_params.extend([f'-metadata:s:s:{subtitle_id:d}', + f'language={stream['tags']['language']}']) + generic_codec_params.extend([f'-c:s:{subtitle_id:d}', 'copy']) + subtitle_id=subtitle_id+1 else: - logger.error("Unknown stream type: %s" % stream['codec_type']) + logger.error("Unknown stream type: %s", stream['codec_type']) # Create a new MKV movie with all streams (except videos) that have been extracted. - genericEncoderParams.extend(genericInputParams) + generic_encoder_params.extend(generic_input_params) - for index in range(0,audioID+subTitleID): - genericEncoderParams.extend(['-map', '%d' % index]) - genericEncoderParams.extend(genericCodecParams) + for index in range(0,audio_id+subtitle_id): + generic_encoder_params.extend(['-map', f'{index:d}']) + generic_encoder_params.extend(generic_codec_params) - mkvFileName = '%s.mkv' % filesPrefix + mkv_filename = f'{filesPrefix}.mkv' try: - mkvOutput = open(mkvFileName,'wb+') + mkv_output = open(mkv_filename,'wb+') except IOError: - logger.error('Impossible to create file: %s' % mkvFileName) + logger.error('Impossible to create file: %s', mkv_filename) return None - mkvoutfd = mkvOutput.fileno() + mkvoutfd = mkv_output.fileno() set_inheritable(mkvoutfd, True) - genericEncoderParams.extend(['-f', 'matroska', '/proc/self/fd/%d' % mkvoutfd]) + generic_encoder_params.extend(['-f', 'matroska', f'/proc/self/fd/{mkvoutfd:d}']) - logger.info('Encoding all streams (except video) into a MKV file: %s' % mkvFileName) - logger.debug('Executing: %s' % genericEncoderParams) - with Popen(genericEncoderParams, stdout=PIPE, close_fds=False) as ffmpeg: + logger.info('Encoding all streams (except video) into a MKV file: %s', mkv_filename) + logger.debug('Executing: %s', generic_encoder_params) + with Popen(generic_encoder_params, stdout=PIPE, close_fds=False) as ffmpeg: status = ffmpeg.wait() if status != 0: - logger.error('Encoding failed with status code: %d' % status) + logger.error('Encoding failed with status code: %d', status) return None - temporaries.append(mkvOutput) + temporaries.append(mkv_output) - h264FileName = '%s.h264' % filesPrefix + h264_filename = f'{filesPrefix}.h264' try: - h264Output = open(h264FileName,'wb+') + h264_output = open(h264_filename,'wb+') except IOError: - logger.error('Impossible to create file: %s' % h264FileName) + logger.error('Impossible to create file: %s', h264_filename) return None - h264outfd = h264Output.fileno() + h264outfd = h264_output.fileno() set_inheritable(h264outfd, True) - videoEncoderParams.extend(videoInputParams) - videoEncoderParams.extend(videoCodecParams) + video_encoder_params.extend(video_input_params) + video_encoder_params.extend(video_codec_params) - videoEncoderParams.extend([ '-x264opts', 'keyint=1:sps-id=%d' % 1,'-bsf:v', + video_encoder_params.extend([ '-x264opts', f'keyint=1:sps-id={1:d}','-bsf:v', 'h264_mp4toannexb,dump_extra=freq=keyframe,h264_metadata=\ overscan_appropriate_flag=1:sample_aspect_ratio=1:video_format=\ 0:chroma_sample_loc_type=0','-f', 'h264', - '/proc/self/fd/%d' % h264outfd]) + f'/proc/self/fd/{h264outfd:d}']) - logger.info('Encoding video into a H264 file: %s' % h264FileName) - logger.debug('Executing: %s' % videoEncoderParams) - with Popen(videoEncoderParams, stdout=PIPE, close_fds=False) as ffmpeg: + logger.info('Encoding video into a H264 file: %s', h264_filename) + logger.debug('Executing: %s', video_encoder_params) + with Popen(video_encoder_params, stdout=PIPE, close_fds=False) as ffmpeg: status = ffmpeg.wait() if status != 0: - logger.error('Encoding failed with status code: %d' % status) + logger.error('Encoding failed with status code: %d', status) return None - temporaries.append(h264Output) + temporaries.append(h264_output) - h264TSFileName = '%s-ts.txt' % filesPrefix + h264_ts_filename = f'{filesPrefix}-ts.txt' try: - h264TSOutput = open(h264TSFileName,'w+') + h264_ts_output = open(h264_ts_filename,'w+') except IOError: - logger.error('Impossible to create file: %s', h264TSFileName) + logger.error('Impossible to create file: %s', h264_ts_filename) return None - h264TSOutput.write('# timestamp format v2\n') + h264_ts_output.write('# timestamp format v2\n') ts = 0 for _ in range(0,nbFrames): ts = ts+ceil(1000/frameRate) - h264TSOutput.write('%d\n' % ts) - h264TSOutput.flush() - h264TSOutput.seek(0) + h264_ts_output.write(f'{ts:d}\n') + h264_ts_output.flush() + h264_ts_output.seek(0) - temporaries.append(h264TSOutput) + temporaries.append(h264_ts_output) for memfd in memfds: close(memfd) - return h264Output, h264TSOutput, mkvOutput + return h264_output, h264_ts_output, mkv_output else: # Nothing to be done. We are already at a i-frame boundary. @@ -2753,39 +2762,39 @@ def mergeMKVs(mkvmerge, inputs, outputName, concatenate=True, timestamps=None): # Timestamps of merged tracks are modified by the length of the preceding track. # The default mode ('file') is using the largest timestamp of the whole file which may create # desynchronize video and sound. - mergeParams = [mkvmerge, '--append-mode', 'track'] + merge_params = [mkvmerge, '--append-mode', 'track'] first = True - partNum = 0 + partnum = 0 for mkv in inputs: if mkv !=None: fd = mkv.fileno() fds.append(fd) set_inheritable(fd, True) # If we pass a timestamps file associated with the considered track, use it. - if timestamps is not None and partNum in timestamps: - tsfd = timestamps[partNum].fileno() + if timestamps is not None and partnum in timestamps: + tsfd = timestamps[partnum].fileno() lseek(tsfd, 0, SEEK_SET) fds.append(tsfd) set_inheritable(tsfd, True) - mergeParams.extend(['--timestamps', ('%d:/proc/self/fd/%d' % (partNum, tsfd))]) + merge_params.extend(['--timestamps', f'{partnum:d}:/proc/self/fd/{tsfd:d}']) if first: - mergeParams.append('/proc/self/fd/%d' % fd) + merge_params.append(f'/proc/self/fd/{fd:d}') first = False elif concatenate: - mergeParams.append('+/proc/self/fd/%d' % fd) + merge_params.append(f'+/proc/self/fd/{fd:d}') else: - mergeParams.append('/proc/self/fd/%d' % fd) - partNum+=1 + merge_params.append(f'/proc/self/fd/{fd:d}') + partnum+=1 - mergeParams.extend(['-o', '/proc/self/fd/%d' % outfd]) + merge_params.extend(['-o', f'/proc/self/fd/{outfd:d}']) # We merge all files. warnings = [] env = {**os.environ, 'LANG': 'C'} - logger.debug('Executing: LANG=C %s', mergeParams) + logger.debug('Executing: LANG=C %s', merge_params) - with Popen(mergeParams, stdout=PIPE, close_fds=False, env=env) as mkvmerge: + with Popen(merge_params, stdout=PIPE, close_fds=False, env=env) as mkvmerge: pb = tqdm(TextIOWrapper(mkvmerge.stdout, encoding="utf-8"), total=100, unit='%', desc='Merging') for line in pb: @@ -2819,7 +2828,7 @@ def findSubtitlesTracks(ffprobe, inputFile): lseek(infd, 0, SEEK_SET) set_inheritable(infd, True) - command = [ffprobe, '-loglevel','quiet', '-i', '/proc/self/fd/%d' % infd, '-select_streams', + command = [ffprobe, '-loglevel','quiet', '-i', f'/proc/self/fd/{infd:d}', '-select_streams', 's', '-show_entries', 'stream=index:stream_tags=language', '-of', 'json'] logger.debug('Executing: %s', command) @@ -2848,11 +2857,12 @@ def extractTrackFromMKV(mkvextract, inputFile, index, outputFile, timestamps): lseek(tsfd, 0, SEEK_SET) set_inheritable(tsfd, True) - params = [ mkvextract, '/proc/self/fd/%d' % infd, 'tracks', '%d:/proc/self/fd/%d' % - (index, outfd), 'timestamps_v2', '%d:/proc/self/fd/%d' % (index, tsfd)] + params = [ mkvextract, f'/proc/self/fd/{infd:d}', 'tracks', + f'{index:d}:/proc/self/fd/{outfd:d}', 'timestamps_v2', + f'{index:d}:/proc/self/fd/{tsfd:d}'] env = {**os.environ, 'LANG': 'C'} - logger.debug('Executing: LANG=C %s' % params) + logger.debug('Executing: LANG=C %s', params) with Popen(params, stdout=PIPE, close_fds=False, env=env) as extract: pb = tqdm(TextIOWrapper(extract.stdout, encoding="utf-8"), total=100, unit='%', @@ -2886,7 +2896,7 @@ def removeVideoTracksFromMKV(mkvmerge, inputFile, outputFile): set_inheritable(infd, True) set_inheritable(outfd, True) - params = [ mkvmerge, '-o', '/proc/self/fd/%d' % outfd, '-D', '/proc/self/fd/%d' % infd] + params = [ mkvmerge, '-o', f'/proc/self/fd/{outfd:d}', '-D', f'/proc/self/fd/{infd:d}'] logger.debug('Executing: LANG=C %s', params) env = {**os.environ, 'LANG': 'C'} @@ -2918,7 +2928,7 @@ def remuxSRTSubtitles(mkvmerge, inputFile, outputFileName, subtitles): try: out = open(outputFileName, 'w') except IOError: - logger.error('Impossible to create file: %s' % outputFileName) + logger.error('Impossible to create file: %s', outputFileName) return None outfd = out.fileno() @@ -2927,18 +2937,18 @@ def remuxSRTSubtitles(mkvmerge, inputFile, outputFileName, subtitles): set_inheritable(infd, True) set_inheritable(outfd, True) - mkvmergeParams = [mkvmerge, '/proc/self/fd/%d' % infd] + mkv_merge_params = [mkvmerge, f'/proc/self/fd/{infd:d}'] for fd, lang in subtitles: lseek(fd, 0, SEEK_SET) set_inheritable(fd, True) - mkvmergeParams.extend(['--language', '0:%s' % lang, '/proc/self/fd/%d' % fd]) + mkv_merge_params.extend(['--language', f'0:{lang}', f'/proc/self/fd/{fd:d}']) - mkvmergeParams.extend(['-o', '/proc/self/fd/%d' % outfd]) + mkv_merge_params.extend(['-o', f'/proc/self/fd/{outfd:d}']) warnings = [] env = {**os.environ, 'LANG': 'C'} - logger.info('Remux subtitles: %s' % mkvmergeParams) - with Popen(mkvmergeParams, stdout=PIPE, close_fds=False, env=env) as mkvmerge: + logger.info('Remux subtitles: %s', mkv_merge_params) + with Popen(mkv_merge_params, stdout=PIPE, close_fds=False, env=env) as mkvmerge: pb = tqdm(TextIOWrapper(mkvmerge.stdout, encoding="utf-8"), total=100, unit='%', desc='Remux subtitles:') for line in pb: @@ -2963,17 +2973,17 @@ def remuxSRTSubtitles(mkvmerge, inputFile, outputFileName, subtitles): def concatenateH264Parts(h264parts, output): logger = logging.getLogger(__name__) - totalLength = 0 + total_length = 0 for h264 in h264parts: fd = h264.fileno() - totalLength += fstat(fd).st_size + total_length += fstat(fd).st_size - logger.info('Total length: %d', totalLength) + logger.info('Total length: %d', total_length) outfd = output.fileno() lseek(outfd, 0, SEEK_SET) - pb = tqdm(total=totalLength, unit='bytes', desc='Concatenation') + pb = tqdm(total=total_length, unit='bytes', desc='Concatenation') for h264 in h264parts: fd = h264.fileno() lseek(fd, 0, SEEK_SET) @@ -2983,9 +2993,9 @@ def concatenateH264Parts(h264parts, output): break pos = 0 while pos < len(buf): - nbBytes = write(outfd, buf[pos:]) - pb.update(nbBytes) - pos += nbBytes + nb_bytes = write(outfd, buf[pos:]) + pb.update(nb_bytes) + pos += nb_bytes def concatenateH264TSParts(h264TSParts, output): logger = logging.getLogger(__name__) @@ -3001,10 +3011,10 @@ def concatenateH264TSParts(h264TSParts, output): else: # TODO: take framerate into account offset = last + 40 - logger.debug('Parsing file: %s. Offset=%d' % (part, offset)) + logger.debug('Parsing file: %s. Offset=%d', part, offset) isheader = part.readline() if (not isheader) or (isheader != header): - logger.error('Impossible to find a valid header: "%s"' % isheader) + logger.error('Impossible to find a valid header: "%s"', isheader) exit(-1) while True: line = part.readline() @@ -3012,7 +3022,7 @@ def concatenateH264TSParts(h264TSParts, output): break ts = offset + float(line) last = max(last,ts) - output.write('%f\n' % ts) + output.write(f'{ts:f}\n') if first: first = False @@ -3023,19 +3033,19 @@ def doCoarseProcessing(ffmpeg, ffprobe, mkvmerge, inputFile, begin, end, nbFrame logger = logging.getLogger(__name__) # Internal video with all streams (video, audio and subtitles) - internalMKVName = '%s.mkv' % filesPrefix + internal_mkv_name = f'{filesPrefix}.mkv' try: - internalMKV = open(internalMKVName, 'w+') + internal_mkv = open(internal_mkv_name, 'w+') except IOError: - logger.error('Impossible to create file: %s', internalMKVName) + logger.error('Impossible to create file: %s', internal_mkv_name) exit(-1) # Extract internal part of MKV - extractMKVPart(mkvmerge=mkvmerge, inputFile=inputFile, outputFile=internalMKV, begin=begin, + extractMKVPart(mkvmerge=mkvmerge, inputFile=inputFile, outputFile=internal_mkv, begin=begin, end=end) - temporaries.append(internalMKV) + temporaries.append(internal_mkv) pass @@ -3043,7 +3053,7 @@ def main(): logger = logging.getLogger(__name__) coloredlogs.install() parser = argparse.ArgumentParser() - parser.add_argument("-i", "--input", dest='inputFile', type=str, required=True, + parser.add_argument("-i", "--input", dest='input_file', type=str, required=True, help="Input file to process (can be .ts, .mp4 or .mkv).") parser.add_argument("-o", "--output", dest='outputFile', type=str, required=True, help="Output MKV file to produce.") @@ -3067,7 +3077,7 @@ def main(): help="Override frame rate estimator.") args = parser.parse_args() - logger.info('Arguments: %s' % args) + logger.info('Arguments: %s', args) if args.verbose: logger.info('Setting logging to debug mode') @@ -3096,7 +3106,7 @@ def main(): for interval in intervals: ts1, ts2 = parseTimeInterval(interval) if ts1 is None or ts2 is None: - logger.error("Illegal time interval: %s" % interval) + logger.error("Illegal time interval: %s", interval) exit(-1) parts.append((ts1,ts2)) @@ -3112,36 +3122,36 @@ def main(): exit(-1) prevts = ts2 - nbParts = len(parts) + nb_parts = len(parts) temporaries = [] - basename = os.path.splitext(os.path.basename(args.inputFile))[0] + basename = os.path.splitext(os.path.basename(args.input_file))[0] mp4filename = basename+'.mp4' mkvfilename = basename+'.mkv' try: - inputFile = open(args.inputFile, mode='r') + input_file = open(args.input_file, mode='r') except IOError: - logger.error("Impossible to open %s" % args.inputFile) + logger.error("Impossible to open %s", args.input_file) exit(-1) - formatOfFile = getFormat(paths['ffprobe'], inputFile) + formatOfFile = getFormat(paths['ffprobe'], input_file) if formatOfFile is None: exit(-1) duration = timedelta(seconds=float(formatOfFile['duration'])) - logger.info("Durée de l'enregistrement: %s" % duration) + logger.info("Durée de l'enregistrement: %s", duration) if args.framerate is None: - frameRate = getFrameRate(paths['ffprobe'], inputFile) + frameRate = getFrameRate(paths['ffprobe'], input_file) if frameRate is None: logger.error('Impossible to estimate frame rate !') exit(-1) else: frameRate = args.framerate - logger.info('Frame rate: %.1f fps' % frameRate) + logger.info('Frame rate: %.1f fps', frameRate) found = False for f in SupportedFormat: @@ -3158,7 +3168,7 @@ def main(): logger.info("Converting TS to MP4 (to fix timestamps).") try: with open(mp4filename, 'w+') as mp4: - ffmpegConvert(paths['ffmpeg'], paths['ffprobe'], inputFile, 'mpegts', mp4, 'mp4', + ffmpegConvert(paths['ffmpeg'], paths['ffprobe'], input_file, 'mpegts', mp4, 'mp4', duration) temporaries.append(mp4) logger.info("Converting MP4 to MKV.") @@ -3169,7 +3179,7 @@ def main(): ffmpegConvert(paths['ffmpeg'], paths['ffprobe'], mp4, 'mp4', mkv, 'matroska', duration) - if nbParts > 0: + if nb_parts > 0: temporaries.append(mkv) except IOError: logger.error('') @@ -3180,17 +3190,17 @@ def main(): mkv = open(mkvfilename, 'w+') except IOError: logger.error('') - ffmpegConvert(paths['ffmpeg'], paths['ffprobe'], inputFile, 'mp4', mkv, 'matroska', + ffmpegConvert(paths['ffmpeg'], paths['ffprobe'], input_file, 'mp4', mkv, 'matroska', duration) - if nbParts > 0: + if nb_parts > 0: temporaries.append(mkv) else: logger.info("Already in MKV") - mkv = inputFile + mkv = input_file streams = getStreams(paths['ffprobe'], mkv) - logger.debug('Streams: %s' % streams) + logger.debug('Streams: %s', streams) mainVideo = None nbVideos = 0 for stream in streams: @@ -3223,7 +3233,7 @@ def main(): # We check if the parse and dump operations are idempotent. privateData = dumpCodecPrivateData(mainAvcConfig) - logger.debug('Redump AVC configuration: %s' % hexdump.dump(privateData, sep=':')) + logger.debug('Redump AVC configuration: %s', hexdump.dump(privateData, sep=':')) # In rare occasion, the PPS has trailing zeroes that do not seem to be related to useful data # but they differ from the private data we generate that do not contain them. # In that case we try to redecode our own private data to see if both AVC configurations are @@ -3231,7 +3241,7 @@ def main(): if mainCodecPrivateData != privateData: logger.warning('Difference detected in bitstream !!') isoAvcConfig = parseCodecPrivate(privateData) - logger.debug('Reread AVC configuration: %s' % isoAvcConfig) + logger.debug('Reread AVC configuration: %s', isoAvcConfig) # If there exists a difference between our own reconstructed AVC configuration and the # original one, we abandon if isoAvcConfig != mainAvcConfig: @@ -3319,7 +3329,7 @@ def main(): end=headIFrameTS, nbFrames=nbHeadFrames-1, frameRate=frameRate, - filesPrefix='part-%d-head'%(partnum), + filesPrefix=f'part-{partnum:d}-head', streams=streams, width=width, height=height, temporaries=temporaries, @@ -3337,13 +3347,13 @@ def main(): # Creating MKV file that corresponds to current part between I-frames # Internal video with all streams (video, audio and subtitles) - internalMKVName = 'part-%d-internal.mkv' % partnum + internalMKVName = f'part-{partnum:d}-internal.mkv' # Internal video stream as a raw H264 stream - internalH264Name = 'part-%d-internal.h264' % partnum + internalH264Name = f'part-{partnum:d}-internal.h264' # Internal video timestamps - internalH264TSName = 'part-%d-internal-ts.txt' % partnum + internalH264TSName = f'part-{partnum:d}-internal-ts.txt' # Internal video with only audio and subtitles streams - internalNoVideoMKVName = 'part-%d-internal-novideo.mkv' % partnum + internalNoVideoMKVName = f'part-{partnum:d}-internal-novideo.mkv' try: internalMKV = open(internalMKVName, 'w+') @@ -3400,7 +3410,7 @@ def main(): inputFile=mkv, begin=tailIFrameTS, end=ts2, nbFrames=nbTailFrames, frameRate=frameRate, - filesPrefix='part-%d-tail'%(partnum), + filesPrefix=f'part-{partnum:d}-tail', streams=streams, width=width, height=height, temporaries=temporaries, @@ -3416,8 +3426,8 @@ def main(): h264TS.append(h264TailTS) logger.info('Merging MKV: %s', subparts) - part = mergeMKVs(mkvmerge=paths['mkvmerge'], inputs=subparts, outputName="part-%d.mkv" % - partnum, concatenate=True) + part = mergeMKVs(mkvmerge=paths['mkvmerge'], inputs=subparts, + outputName=f'part-{partnum:d}.mkv', concatenate=True) mkvparts.append(part) temporaries.append(part) @@ -3434,7 +3444,7 @@ def main(): nbMKVParts = len(mkvparts) if nbMKVParts > 0: try: - fullH264 = open('%s-full.h264' % basename, 'w+') + fullH264 = open(f'{basename}-full.h264', 'w+') except IOError: logger.error('Impossible to create file full H264 stream.') exit(-1) @@ -3444,7 +3454,7 @@ def main(): temporaries.append(fullH264) try: - fullH264TS = open('%s-ts.txt' % basename, 'w+') + fullH264TS = open(f'{basename}-ts.txt', 'w+') except IOError: logger.error('Impossible to create file containing all video timestamps.') exit(-1) @@ -3453,11 +3463,11 @@ def main(): concatenateH264TSParts(h264TSParts=h264TS, output=fullH264TS) temporaries.append(fullH264TS) - finalNoVideoName = '%s-novideo.mkv' % basename - finalWithVideoName = '%s-video.mkv' % basename + finalNoVideoName = f'{basename}-novideo.mkv' + finalWithVideoName = f'{basename}-video.mkv' if nbMKVParts > 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, concatenate=True) elif nbMKVParts == 1: @@ -3470,7 +3480,7 @@ def main(): try: finalNoVideo = open(finalNoVideoName, 'r') except IOError: - logger.error('Impossible to open file: %s.' % finalNoVideoName) + logger.error('Impossible to open file: %s.', finalNoVideoName) exit(-1) temporaries.append(finalNoVideo) @@ -3482,7 +3492,7 @@ def main(): outputName=finalWithVideoName, concatenate=False, timestamps={0: fullH264TS}) finalCodecPrivateData = dumpCodecPrivateData(mainAvcConfig) - logger.debug('Final codec private data: %s' % hexdump.dump(finalCodecPrivateData, sep=':')) + logger.debug('Final codec private data: %s', hexdump.dump(finalCodecPrivateData, sep=':')) logger.info('Changing codec private data with the new one.') changeCodecPrivateData(paths['mkvinfo'], finalWithVideo, finalCodecPrivateData) @@ -3495,7 +3505,7 @@ def main(): temporaries.append(finalWithVideo) duration = getMovieDuration(paths['ffprobe'], finalWithVideo) supportedLangs = getTesseractSupportedLang(paths['tesseract']) - logger.info('Supported lang: %s' % supportedLangs) + logger.info('Supported lang: %s', supportedLangs) logger.info('Find subtitles tracks and language.') subtitles = findSubtitlesTracks(paths['ffprobe'], finalWithVideo) logger.info(subtitles) @@ -3550,13 +3560,13 @@ def main(): logger.info("Cleaning temporary files") for f in temporaries: path = os.path.realpath(f.name) - logger.info("Removing: %s" % path) + logger.info("Removing: %s", path) f.close() unlink(path) d = datetime(1,1,1) for c in checks: - logger.info("Please check cut smoothness at %s" % (c+d).strftime("%H:%M:%S")) + logger.info("Please check cut smoothness at %s", (c+d).strftime("%H:%M:%S")) if __name__ == "__main__": main()