click
Javanaut 1 year ago
parent e3964b0002
commit d84797e6a5

@ -362,6 +362,32 @@ def generateDispositionTokens(subDescriptor):
return dispositionTokens
def searchSubtitleFiles(dir, prefix):
sesl_match = re.compile(SEASON_EPISODE_STREAM_LANGUAGE_MATCH)
availableFileSubtitles = []
for subtitleFilename in os.listdir(dir):
if subtitleFilename.startswith(prefix) and subtitleFilename.endswith('.' + SUBTITLE_FILE_EXTENSION):
sesl_result = sesl_match.search(subtitleFilename)
if sesl_result is not None:
subtitleFilePath = os.path.join(dir, subtitleFilename)
if os.path.isfile(subtitleFilePath):
subtitleFileDescriptor = {}
subtitleFileDescriptor['path'] = subtitleFilePath
subtitleFileDescriptor['season'] = int(sesl_result.group(1))
subtitleFileDescriptor['episode'] = int(sesl_result.group(2))
subtitleFileDescriptor['stream'] = int(sesl_result.group(3))
subtitleFileDescriptor['language'] = sesl_result.group(4)
availableFileSubtitles.append(subtitleFileDescriptor)
click.echo(f"Found {len(availableFileSubtitles)} subtitles in files\n")
return availableFileSubtitles
@click.group()
@click.pass_context
def ffx(ctx):
@ -434,7 +460,7 @@ def streams(filename):
@click.option("-c", "--clear-metadata", is_flag=True, default=False)
@click.option("-d", "--denoise", is_flag=True, default=False)
@click.option("-j", "--no-jellyfin-tweaks", is_flag=True, default=False)
@click.option("-j", "--jellyfin", is_flag=True, default=False)
@click.option("--dry-run", is_flag=True, default=False)
@ -461,7 +487,7 @@ def convert(ctx,
output_directory,
clear_metadata,
denoise,
no_jellyfin_tweaks,
jellyfin,
dry_run):
"""Batch conversion of audiovideo files in format suitable for web playback, e.g. jellyfin
@ -471,10 +497,10 @@ def convert(ctx,
or if the filename has not changed."""
startTime = time.perf_counter()
context = ctx.obj
click.echo(f"\nVideo encoder: {video_encoder}")
qualityTokens = quality.split(',')
@ -482,7 +508,6 @@ def convert(ctx,
click.echo(f"Qualities: {q_list}")
context['bitrates'] = {}
context['bitrates']['stereo'] = str(stereo_bitrate) if str(stereo_bitrate).endswith('k') else f"{stereo_bitrate}k"
context['bitrates']['ac3'] = str(ac3_bitrate) if str(ac3_bitrate).endswith('k') else f"{ac3_bitrate}k"
@ -492,67 +517,45 @@ def convert(ctx,
click.echo(f"AC3 bitrate: {context['bitrates']['ac3']}")
click.echo(f"DTS bitrate: {context['bitrates']['dts']}")
# Parse subtitle files
context['import_subtitles'] = (subtitle_directory and subtitle_prefix)
se_match = re.compile(SEASON_EPISODE_INDICATOR_MATCH)
e_match = re.compile(EPISODE_INDICATOR_MATCH)
sesl_match = re.compile(SEASON_EPISODE_STREAM_LANGUAGE_MATCH)
availableFileSubtitles = []
if context['import_subtitles']:
for subtitleFilename in os.listdir(subtitle_directory):
if subtitleFilename.startswith(subtitle_prefix) and subtitleFilename.endswith('.' + SUBTITLE_FILE_EXTENSION):
sesl_result = sesl_match.search(subtitleFilename)
if sesl_result is not None:
subtitleFilePath = os.path.join(subtitle_directory, subtitleFilename)
if os.path.isfile(subtitleFilePath):
subtitleFileDescriptor = {}
subtitleFileDescriptor['path'] = subtitleFilePath
subtitleFileDescriptor['season'] = int(sesl_result.group(1))
subtitleFileDescriptor['episode'] = int(sesl_result.group(2))
subtitleFileDescriptor['stream'] = int(sesl_result.group(3))
subtitleFileDescriptor['language'] = sesl_result.group(4)
availableFileSubtitles.append(subtitleFileDescriptor)
click.echo(f"Found {len(availableFileSubtitles)} subtitles in files")
# Parse subtitle files
context['import_subtitles'] = (subtitle_directory and subtitle_prefix)
availableFileSubtitles = searchSubtitleFiles(subtitle_directory, subtitle_prefix) if context['import_subtitles'] else []
subtitleLanguages = subtitle_language
subtitleTitles = subtitle_title
# Overwrite audio tags if set
audioLanguages = audio_language
audioTitles = audio_title
# Overwrite subtitle tags if set
subtitleLanguages = subtitle_language
subtitleTitles = subtitle_title
# Process crop parameters
context['perform_crop'] = (crop != 'none')
if context['perform_crop']:
cropTokens = crop.split(',')
if cropTokens and len(cropTokens) == 2:
context['crop_start'], context['crop_length'] = crop.split(',')
else:
context['crop_start'] = DEFAULT_CROP_START
context['crop_length'] = DEFAULT_CROP_LENGTH
click.echo(f"crop start={context['crop_start']} length={context['crop_length']}")
existingSourcePaths = [p for p in paths if os.path.isfile(p)]
job_index = 0
existingSourcePaths = [p for p in paths if os.path.isfile(p)]
click.echo(f"\nRunning {len(existingSourcePaths) * len(q_list)} jobs")
job_index = 0
for sourcePath in existingSourcePaths:
# Separate basedir, basename and extension for current source file
sourceDirectory = os.path.dirname(sourcePath)
sourceFilename = os.path.basename(sourcePath)
sourcePathTokens = sourceFilename.split('.')
@ -564,9 +567,10 @@ def convert(ctx,
sourceFileBasename = sourceFilename
sourceFilenameExtension = ''
click.echo(f"\nProcessing file {sourcePath}")
# Determine season and episode if present in current filename
season_digits = 2
episode_digits = 2
index_digits = 3
@ -589,7 +593,7 @@ def convert(ctx,
print(f"season={season} episode={episode} file={file_index}")
# File specific tokens
# Assemble target filename tokens
targetFilenameTokens = []
targetFilenameExtension = DEFAULT_FILE_EXTENSION
@ -608,15 +612,17 @@ def convert(ctx,
try:
streamDescriptor = getStreamDescriptor(sourcePath)
sourceStreamDescriptor = getStreamDescriptor(sourcePath)
targetStreamDescriptor = sourceStreamDescriptor.copy()
except Exception:
click.echo(f"File with path {sourcePath} does not contain any audiovisual data, skipping ...")
continue
for aStream in streamDescriptor[STREAM_TYPE_AUDIO]:
for aStream in sourceStreamDescriptor[STREAM_TYPE_AUDIO]:
click.echo(f"audio stream lang={aStream['language']}")
for sStream in streamDescriptor[STREAM_TYPE_SUBTITLE]:
for sStream in sourceStreamDescriptor[STREAM_TYPE_SUBTITLE]:
click.echo(f"subtitle stream lang={sStream['language']}")
@ -639,11 +645,10 @@ def convert(ctx,
if streamIndex <= len(subtitleTitles) -1:
mSubtitles[streamIndex]['title'] = subtitleTitles[streamIndex]
if default_subtitle == -1 or no_jellyfin_tweaks:
matchingSubtitles = mSubtitles
else:
if default_subtitle != -1 and jellyfin:
matchingSubtitles = getReorderedSubstreams(mSubtitles, default_subtitle)
else:
matchingSubtitles = mSubtitles
@ -660,8 +665,8 @@ def convert(ctx,
audioTokens = []
# Source stream descriptors
audioStreams = streamDescriptor[STREAM_TYPE_AUDIO]
subtitleStreams = streamDescriptor[STREAM_TYPE_SUBTITLE]
audioStreams = sourceStreamDescriptor[STREAM_TYPE_AUDIO]
subtitleStreams = sourceStreamDescriptor[STREAM_TYPE_SUBTITLE]
# Set language and title in source stream descriptors if given per command line option
for streamIndex in range(len(audioStreams)):
@ -689,10 +694,7 @@ def convert(ctx,
for streamIndex in range(len(audioStreams)):
audioStreams[streamIndex]['disposition']['default'] = 1 if streamIndex == default_audio else 0
if no_jellyfin_tweaks:
sourceAudioStreams = audioStreams
else:
sourceAudioStreams = getReorderedSubstreams(audioStreams, default_audio)
sourceAudioStreams = getReorderedSubstreams(audioStreams, default_audio) if jellyfin else audioStreams
dispositionTokens += generateDispositionTokens(sourceAudioStreams)
@ -708,10 +710,7 @@ def convert(ctx,
for streamIndex in range(len(subtitleStreams)):
subtitleStreams[streamIndex]['disposition']['default'] = 1 if streamIndex == default_subtitle else 0
if no_jellyfin_tweaks:
sourceSubtitleStreams = subtitleStreams
else:
sourceSubtitleStreams = getReorderedSubstreams(subtitleStreams, default_subtitle)
sourceSubtitleStreams = getReorderedSubstreams(subtitleStreams, default_subtitle) if jellyfin else subtitleStreams
dispositionTokens += generateDispositionTokens(sourceSubtitleStreams)
@ -735,11 +734,18 @@ def convert(ctx,
# Create mapping and ffmpeg options for subtitle streams
subtitleMetadataTokens = []
if context['import_subtitles']:
numMatchingSubtitles = len(matchingSubtitles)
if jellyfin and default_subtitle != -1:
subtitleSequence = getModifiedStreamOrder(numMatchingSubtitles, default_subtitle)
else:
subtitleSequence = range(numMatchingSubtitles)
for fileIndex in range(len(matchingSubtitles)):
for fileIndex in range(numMatchingSubtitles):
# Create mapping for subtitle streams when imported from files
mappingTokens += ['-map', f"{fileIndex+1}:s:0"]
mappingTokens += ['-map', f"{subtitleSequence[fileIndex]+1}:s:0"]
msg = matchingSubtitles[fileIndex]
subtitleMetadataTokens += [f"-metadata:s:s:{fileIndex}", f"language={msg['language']}"]
@ -789,7 +795,7 @@ def convert(ctx,
+ generateAV1Tokens(q, preset) + audioTokens)
if clear_metadata:
commandSequence += generateClearTokens(streamDescriptor)
commandSequence += generateClearTokens(sourceStreamDescriptor)
if context['perform_crop']:
commandSequence += generateCropTokens(context['crop_start'], context['crop_length'])
@ -833,7 +839,7 @@ def convert(ctx,
commandSequence2 += generateVP9Pass2Tokens(q) + audioTokens
if clear_metadata:
commandSequence2 += generateClearTokens(streamDescriptor)
commandSequence2 += generateClearTokens(sourceStreamDescriptor)
if context['perform_crop']:
commandSequence2 += generateCropTokens(context['crop_start'], context['crop_length'])

Loading…
Cancel
Save