Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
24d0700db2 | ||
|
|
3463c1e371 | ||
|
|
5ca7d6d12c | ||
|
|
77dfb4b1d3 | ||
| 1df2e74566 | |||
| 8f9f77e891 | |||
| 6a03d4d6e2 | |||
| a263c735aa |
226
bin/ffx.py
226
bin/ffx.py
@@ -11,14 +11,23 @@ from ffx.tmdb_controller import TmdbController
|
|||||||
|
|
||||||
from ffx.database import databaseContext
|
from ffx.database import databaseContext
|
||||||
|
|
||||||
|
from ffx.track_descriptor import TrackDescriptor
|
||||||
from ffx.track_type import TrackType
|
from ffx.track_type import TrackType
|
||||||
from ffx.video_encoder import VideoEncoder
|
from ffx.video_encoder import VideoEncoder
|
||||||
|
from ffx.track_disposition import TrackDisposition
|
||||||
|
|
||||||
|
from ffx.process import executeProcess
|
||||||
|
|
||||||
|
|
||||||
VERSION='0.1.1'
|
VERSION='0.1.3'
|
||||||
|
|
||||||
# 0.1.1
|
# 0.1.1
|
||||||
# Bugfixes, TMBD identify shows
|
# Bugfixes, TMBD identify shows
|
||||||
|
# 0.1.2
|
||||||
|
# Bugfixes
|
||||||
|
# 0.1.3
|
||||||
|
# Subtitle file imports
|
||||||
|
|
||||||
|
|
||||||
@click.group()
|
@click.group()
|
||||||
@click.pass_context
|
@click.pass_context
|
||||||
@@ -56,52 +65,98 @@ def inspect(ctx, filename):
|
|||||||
app = FfxApp(ctx.obj)
|
app = FfxApp(ctx.obj)
|
||||||
app.run()
|
app.run()
|
||||||
|
|
||||||
|
#TODO: TrackCodec Klasse
|
||||||
|
CODEC_LOOKUP_TABLE = {
|
||||||
|
'h264': {'format': 'h264', 'extension': 'h264'},
|
||||||
|
'aac': { 'extension': 'aac'},
|
||||||
|
'ac3': {'format': 'ac3', 'extension': 'ac3'},
|
||||||
|
'ass': {'format': 'ass', 'extension': 'ass'},
|
||||||
|
'hdmv_pgs_subtitle': {'format': 'sup', 'extension': 'sup'}
|
||||||
|
}
|
||||||
|
|
||||||
|
def getUnmuxSequence(trackDescriptor: TrackDescriptor, sourcePath, targetPrefix, targetDirectory = ''):
|
||||||
|
|
||||||
|
trackCodec = trackDescriptor.getCodec()
|
||||||
|
|
||||||
|
if not trackCodec in CODEC_LOOKUP_TABLE.keys():
|
||||||
|
return []
|
||||||
|
|
||||||
|
commandTokens = FfxController.COMMAND_TOKENS + ['-i', sourcePath]
|
||||||
|
trackType = trackDescriptor.getType()
|
||||||
|
|
||||||
|
targetPathBase = os.path.join(targetDirectory, targetPrefix) if targetDirectory else targetPrefix
|
||||||
|
|
||||||
|
commandTokens += ['-map',
|
||||||
|
f"0:{trackType.indicator()}:{trackDescriptor.getSubIndex()}",
|
||||||
|
'-c',
|
||||||
|
'copy']
|
||||||
|
|
||||||
|
if 'format' in CODEC_LOOKUP_TABLE[trackCodec].keys():
|
||||||
|
commandTokens += ['-f', CODEC_LOOKUP_TABLE[trackCodec]['format']]
|
||||||
|
|
||||||
|
commandTokens += [f"{targetPathBase}.{CODEC_LOOKUP_TABLE[trackCodec]['extension']}"]
|
||||||
|
|
||||||
|
return commandTokens
|
||||||
|
|
||||||
|
|
||||||
# @ffx.command()
|
@ffx.command()
|
||||||
# @click.pass_context
|
@click.pass_context
|
||||||
#
|
|
||||||
# @click.argument('paths', nargs=-1)
|
@click.argument('paths', nargs=-1)
|
||||||
# @click.option('-l', '--label', type=str, default='', help='Label to be used as filename prefix')
|
@click.option('-l', '--label', type=str, default='', help='Label to be used as filename prefix')
|
||||||
#
|
@click.option("-o", "--output-directory", type=str, default='')
|
||||||
# @click.option('-sd', '--subtitle-directory', type=str, default='', help='Load subtitles from here')
|
@click.option("-s", "--subtitles-only", is_flag=True, default=False)
|
||||||
# @click.option('-sp', '--subtitle-prefix', type=str, default='', help='Subtitle filename prefix')
|
@click.option("--dry-run", is_flag=True, default=False)
|
||||||
#
|
def unmux(ctx,
|
||||||
# @click.option("-o", "--output-directory", type=str, default='')
|
paths,
|
||||||
#
|
label,
|
||||||
# @click.option("--dry-run", is_flag=True, default=False)
|
output_directory,
|
||||||
#
|
subtitles_only,
|
||||||
#
|
dry_run):
|
||||||
# def unmux(ctx,
|
|
||||||
# label,
|
existingSourcePaths = [p for p in paths if os.path.isfile(p)]
|
||||||
# paths,
|
click.echo(f"\nUnmuxing {len(existingSourcePaths)} files")
|
||||||
# subtitle_directory,
|
|
||||||
# subtitle_prefix,
|
for sourcePath in existingSourcePaths:
|
||||||
# output_directory,
|
|
||||||
# dry_run):
|
fp = FileProperties(ctx.obj, sourcePath)
|
||||||
#
|
|
||||||
# existingSourcePaths = [p for p in paths if os.path.isfile(p)]
|
|
||||||
# click.echo(f"\nUnmuxing {len(existingSourcePaths)} files")
|
|
||||||
#
|
|
||||||
# for sourcePath in existingSourcePaths:
|
|
||||||
#
|
|
||||||
# sd = getStreamDescriptor(sourcePath)
|
|
||||||
#
|
|
||||||
# print(f"\nFile {sourcePath}\n")
|
|
||||||
#
|
|
||||||
# for v in sd['video']:
|
|
||||||
#
|
|
||||||
# if v['codec_name'] == 'h264':
|
|
||||||
#
|
|
||||||
# commandSequence = ['ffmpeg', '-i', sourcePath, '-map', '0:v:0', '-c', 'copy', '-f', 'h264']
|
|
||||||
# executeProcess()
|
|
||||||
#
|
|
||||||
# for a in sd['audio']:
|
|
||||||
# print(f"A: {a}\n")
|
|
||||||
# for s in sd['subtitle']:
|
|
||||||
# print(f"S: {s}\n")
|
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
sourceMediaDescriptor = fp.getMediaDescriptor()
|
||||||
|
|
||||||
|
season = fp.getSeason()
|
||||||
|
episode = fp.getEpisode()
|
||||||
|
|
||||||
|
#TODO: Recognition für alle Formate anpassen
|
||||||
|
targetLabel = label if label else fp.getFileBasename()
|
||||||
|
targetIndicator = f"_S{season}E{episode}" if label and season != -1 and episode != -1 else ''
|
||||||
|
|
||||||
|
if label and not targetIndicator:
|
||||||
|
click.echo(f"Skipping file {fp.getFilename()}: Label set but no indicator recognized")
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
click.echo(f"\nUnmuxing file {fp.getFilename()}\n")
|
||||||
|
|
||||||
|
for trackDescriptor in sourceMediaDescriptor.getAllTrackDescriptors():
|
||||||
|
|
||||||
|
if trackDescriptor.getType() == TrackType.SUBTITLE or not subtitles_only:
|
||||||
|
|
||||||
|
# SEASON_EPISODE_STREAM_LANGUAGE_MATCH = '[sS]([0-9]+)[eE]([0-9]+)_([0-9]+)_([a-z]{3})'
|
||||||
|
targetPrefix = f"{targetLabel}{targetIndicator}_{trackDescriptor.getIndex()}_{trackDescriptor.getLanguage().threeLetter()}"
|
||||||
|
|
||||||
|
unmuxSequence = getUnmuxSequence(trackDescriptor, sourcePath, targetPrefix, targetDirectory = output_directory)
|
||||||
|
|
||||||
|
if unmuxSequence:
|
||||||
|
if not dry_run:
|
||||||
|
click.echo(f"Executing unmuxing sequence: {' '.join(unmuxSequence)}")
|
||||||
|
out, err, rc = executeProcess(unmuxSequence)
|
||||||
|
if rc:
|
||||||
|
click.echo(f"Unmuxing of stream {trackDescriptor.getIndex()} failed with error ({rc}) {err}")
|
||||||
|
else:
|
||||||
|
click.echo(f"Skipping stream with unknown codec {trackDescriptor.getCodec()}")
|
||||||
|
except Exception as ex:
|
||||||
|
click.echo(f"Skipping File {sourcePath} ({ex})")
|
||||||
|
|
||||||
|
|
||||||
@ffx.command()
|
@ffx.command()
|
||||||
@@ -157,6 +212,7 @@ def shows(ctx):
|
|||||||
|
|
||||||
@click.option("-t", "--no-tmdb", is_flag=True, default=False)
|
@click.option("-t", "--no-tmdb", is_flag=True, default=False)
|
||||||
@click.option("-j", "--no-jellyfin", is_flag=True, default=False)
|
@click.option("-j", "--no-jellyfin", is_flag=True, default=False)
|
||||||
|
@click.option("-np", "--no-pattern", is_flag=True, default=False)
|
||||||
|
|
||||||
@click.option("--dry-run", is_flag=True, default=False)
|
@click.option("--dry-run", is_flag=True, default=False)
|
||||||
|
|
||||||
@@ -170,6 +226,7 @@ def convert(ctx,
|
|||||||
stereo_bitrate,
|
stereo_bitrate,
|
||||||
ac3_bitrate,
|
ac3_bitrate,
|
||||||
dts_bitrate,
|
dts_bitrate,
|
||||||
|
|
||||||
subtitle_directory,
|
subtitle_directory,
|
||||||
subtitle_prefix,
|
subtitle_prefix,
|
||||||
|
|
||||||
@@ -188,6 +245,7 @@ def convert(ctx,
|
|||||||
denoise,
|
denoise,
|
||||||
no_tmdb,
|
no_tmdb,
|
||||||
no_jellyfin,
|
no_jellyfin,
|
||||||
|
no_pattern,
|
||||||
dry_run):
|
dry_run):
|
||||||
"""Batch conversion of audiovideo files in format suitable for web playback, e.g. jellyfin
|
"""Batch conversion of audiovideo files in format suitable for web playback, e.g. jellyfin
|
||||||
|
|
||||||
@@ -200,12 +258,13 @@ def convert(ctx,
|
|||||||
|
|
||||||
context = ctx.obj
|
context = ctx.obj
|
||||||
|
|
||||||
context['dry_run'] = True # dry_run
|
context['dry_run'] = dry_run
|
||||||
|
|
||||||
context['video_encoder'] = VideoEncoder.fromLabel(video_encoder)
|
context['video_encoder'] = VideoEncoder.fromLabel(video_encoder)
|
||||||
|
|
||||||
context['jellyfin'] = not no_jellyfin
|
context['use_jellyfin'] = not no_jellyfin
|
||||||
context['tmdb'] = not no_tmdb
|
context['use_tmdb'] = not no_tmdb
|
||||||
|
context['use_pattern'] = not no_pattern
|
||||||
|
|
||||||
context['import_subtitles'] = (subtitle_directory and subtitle_prefix)
|
context['import_subtitles'] = (subtitle_directory and subtitle_prefix)
|
||||||
if context['import_subtitles']:
|
if context['import_subtitles']:
|
||||||
@@ -235,11 +294,11 @@ def convert(ctx,
|
|||||||
cTokens = crop.split(',')
|
cTokens = crop.split(',')
|
||||||
if cTokens and len(cTokens) == 2:
|
if cTokens and len(cTokens) == 2:
|
||||||
context['crop_start'] = int(cTokens[0])
|
context['crop_start'] = int(cTokens[0])
|
||||||
context['crop_lenght'] = int(cTokens[1])
|
context['crop_length'] = int(cTokens[1])
|
||||||
click.echo(f"Crop start={context['crop_start']} length={context['crop_length']}")
|
click.echo(f"Crop start={context['crop_start']} length={context['crop_length']}")
|
||||||
|
|
||||||
|
|
||||||
tc = TmdbController()
|
tc = TmdbController() if context['use_tmdb'] else None
|
||||||
|
|
||||||
existingSourcePaths = [p for p in paths if os.path.isfile(p) and p.split('.')[-1] in FfxController.INPUT_FILE_EXTENSIONS]
|
existingSourcePaths = [p for p in paths if os.path.isfile(p) and p.split('.')[-1] in FfxController.INPUT_FILE_EXTENSIONS]
|
||||||
click.echo(f"\nRunning {len(existingSourcePaths) * len(q_list)} jobs")
|
click.echo(f"\nRunning {len(existingSourcePaths) * len(q_list)} jobs")
|
||||||
@@ -262,7 +321,7 @@ def convert(ctx,
|
|||||||
sourceMediaDescriptor = mediaFileProperties.getMediaDescriptor()
|
sourceMediaDescriptor = mediaFileProperties.getMediaDescriptor()
|
||||||
|
|
||||||
#HINT: This is None if the filename did not match anything in database
|
#HINT: This is None if the filename did not match anything in database
|
||||||
currentPattern = mediaFileProperties.getPattern()
|
currentPattern = mediaFileProperties.getPattern() if context['use_pattern'] else None
|
||||||
|
|
||||||
click.echo(f"Pattern matching: {'No' if currentPattern is None else 'Yes'}")
|
click.echo(f"Pattern matching: {'No' if currentPattern is None else 'Yes'}")
|
||||||
|
|
||||||
@@ -276,45 +335,39 @@ def convert(ctx,
|
|||||||
#
|
#
|
||||||
# Query user for the correct sub indices, then configure flags in track descriptors associated with media descriptor accordingly.
|
# Query user for the correct sub indices, then configure flags in track descriptors associated with media descriptor accordingly.
|
||||||
# The correct tokens should then be created by
|
# The correct tokens should then be created by
|
||||||
try:
|
if len([v for v in sourceMediaDescriptor.getVideoTracks() if v.getDispositionFlag(TrackDisposition.DEFAULT)]) > 1:
|
||||||
sourceMediaDescriptor.getDefaultVideoTrack()
|
|
||||||
except ValueError:
|
|
||||||
defaultVideoTrackSubIndex = click.prompt("More than one default video stream detected! Please select stream", type=int)
|
defaultVideoTrackSubIndex = click.prompt("More than one default video stream detected! Please select stream", type=int)
|
||||||
sourceMediaDescriptor.setDefaultSubTrack(TrackType.VIDEO, defaultVideoTrackSubIndex)
|
sourceMediaDescriptor.setDefaultSubTrack(TrackType.VIDEO, defaultVideoTrackSubIndex)
|
||||||
try:
|
|
||||||
sourceMediaDescriptor.getForcedVideoTrack()
|
if len([v for v in sourceMediaDescriptor.getVideoTracks() if v.getDispositionFlag(TrackDisposition.FORCED)]) > 1:
|
||||||
except ValueError:
|
|
||||||
forcedVideoTrackSubIndex = click.prompt("More than one forced video stream detected! Please select stream", type=int)
|
forcedVideoTrackSubIndex = click.prompt("More than one forced video stream detected! Please select stream", type=int)
|
||||||
sourceMediaDescriptor.setForcedSubTrack(TrackType.VIDEO, forcedVideoTrackSubIndex)
|
sourceMediaDescriptor.setForcedSubTrack(TrackType.VIDEO, forcedVideoTrackSubIndex)
|
||||||
try:
|
|
||||||
sourceMediaDescriptor.getDefaultAudioTrack()
|
if len([a for a in sourceMediaDescriptor.getAudioTracks() if a.getDispositionFlag(TrackDisposition.DEFAULT)]) > 1:
|
||||||
except ValueError:
|
|
||||||
defaultAudioTrackSubIndex = click.prompt("More than one default audio stream detected! Please select stream", type=int)
|
defaultAudioTrackSubIndex = click.prompt("More than one default audio stream detected! Please select stream", type=int)
|
||||||
sourceMediaDescriptor.setDefaultSubTrack(TrackType.AUDIO, defaultAudioTrackSubIndex)
|
sourceMediaDescriptor.setDefaultSubTrack(TrackType.AUDIO, defaultAudioTrackSubIndex)
|
||||||
try:
|
|
||||||
sourceMediaDescriptor.getForcedAudioTrack()
|
if len([a for a in sourceMediaDescriptor.getAudioTracks() if a.getDispositionFlag(TrackDisposition.FORCED)]) > 1:
|
||||||
except ValueError:
|
|
||||||
forcedAudioTrackSubIndex = click.prompt("More than one forced audio stream detected! Please select stream", type=int)
|
forcedAudioTrackSubIndex = click.prompt("More than one forced audio stream detected! Please select stream", type=int)
|
||||||
sourceMediaDescriptor.setForcedSubTrack(TrackType.AUDIO, forcedAudioTrackSubIndex)
|
sourceMediaDescriptor.setForcedSubTrack(TrackType.AUDIO, forcedAudioTrackSubIndex)
|
||||||
try:
|
|
||||||
sourceMediaDescriptor.getDefaultSubtitleTrack()
|
if len([s for s in sourceMediaDescriptor.getSubtitleTracks() if s.getDispositionFlag(TrackDisposition.DEFAULT)]) > 1:
|
||||||
except ValueError:
|
|
||||||
defaultSubtitleTrackSubIndex = click.prompt("More than one default subtitle stream detected! Please select stream", type=int)
|
defaultSubtitleTrackSubIndex = click.prompt("More than one default subtitle stream detected! Please select stream", type=int)
|
||||||
sourceMediaDescriptor.setDefaultSubTrack(TrackType.SUBTITLE, defaultSubtitleTrackSubIndex)
|
sourceMediaDescriptor.setDefaultSubTrack(TrackType.SUBTITLE, defaultSubtitleTrackSubIndex)
|
||||||
try:
|
|
||||||
sourceMediaDescriptor.getForcedSubtitleTrack()
|
if len([s for s in sourceMediaDescriptor.getSubtitleTracks() if s.getDispositionFlag(TrackDisposition.FORCED)]) > 1:
|
||||||
except ValueError:
|
|
||||||
forcedSubtitleTrackSubIndex = click.prompt("More than one forced subtitle stream detected! Please select stream", type=int)
|
forcedSubtitleTrackSubIndex = click.prompt("More than one forced subtitle stream detected! Please select stream", type=int)
|
||||||
sourceMediaDescriptor.setForcedSubTrack(TrackType.SUBTITLE, forcedSubtitleTrackSubIndex)
|
sourceMediaDescriptor.setForcedSubTrack(TrackType.SUBTITLE, forcedSubtitleTrackSubIndex)
|
||||||
|
|
||||||
|
|
||||||
if context['import_subtitles']:
|
if context['import_subtitles']:
|
||||||
sourceMediaDescriptor.importSubtitles(context['subtitle_directory'], context['subtitle_prefix'])
|
sourceMediaDescriptor.importSubtitles(context['subtitle_directory'],
|
||||||
|
context['subtitle_prefix'],
|
||||||
|
mediaFileProperties.getSeason(),
|
||||||
|
mediaFileProperties.getEpisode())
|
||||||
|
|
||||||
fc = FfxController(context, sourceMediaDescriptor)
|
fc = FfxController(context, sourceMediaDescriptor)
|
||||||
|
|
||||||
# mappingTokens = fc.generateMetadataTokens()
|
|
||||||
# click.echo(f"Metadata Tokens: {mappingTokens}")
|
|
||||||
|
|
||||||
dispositionTokens = fc.generateDispositionTokens()
|
dispositionTokens = fc.generateDispositionTokens()
|
||||||
click.echo(f"Disposition Tokens: {dispositionTokens}")
|
click.echo(f"Disposition Tokens: {dispositionTokens}")
|
||||||
|
|
||||||
@@ -326,15 +379,14 @@ def convert(ctx,
|
|||||||
# Case pattern matching
|
# Case pattern matching
|
||||||
|
|
||||||
targetMediaDescriptor = currentPattern.getMediaDescriptor()
|
targetMediaDescriptor = currentPattern.getMediaDescriptor()
|
||||||
|
|
||||||
currentShowDescriptor = currentPattern.getShowDescriptor()
|
currentShowDescriptor = currentPattern.getShowDescriptor()
|
||||||
|
|
||||||
|
|
||||||
if context['tmdb']:
|
if context['use_tmdb']:
|
||||||
|
|
||||||
tmdbEpisodeResult = tc.queryEpisode(currentShowDescriptor.getId(), mediaFileProperties.getSeason(), mediaFileProperties.getEpisode())
|
tmdbEpisodeResult = tc.queryEpisode(currentShowDescriptor.getId(), mediaFileProperties.getSeason(), mediaFileProperties.getEpisode())
|
||||||
|
|
||||||
# click.echo(f"{tmdbEpisodeResult}")
|
|
||||||
|
|
||||||
if tmdbEpisodeResult:
|
if tmdbEpisodeResult:
|
||||||
fileBasename = tc.getEpisodeFileBasename(currentShowDescriptor.getFilenamePrefix(),
|
fileBasename = tc.getEpisodeFileBasename(currentShowDescriptor.getFilenamePrefix(),
|
||||||
tmdbEpisodeResult['name'],
|
tmdbEpisodeResult['name'],
|
||||||
@@ -344,19 +396,28 @@ def convert(ctx,
|
|||||||
currentShowDescriptor.getIndexEpisodeDigits(),
|
currentShowDescriptor.getIndexEpisodeDigits(),
|
||||||
currentShowDescriptor.getIndicatorSeasonDigits(),
|
currentShowDescriptor.getIndicatorSeasonDigits(),
|
||||||
currentShowDescriptor.getIndicatorEpisodeDigits())
|
currentShowDescriptor.getIndicatorEpisodeDigits())
|
||||||
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
fileBasename = currentShowDescriptor.getFilenamePrefix()
|
fileBasename = currentShowDescriptor.getFilenamePrefix()
|
||||||
|
|
||||||
click.echo(f"fileBasename={fileBasename}")
|
click.echo(f"fileBasename={fileBasename}")
|
||||||
|
|
||||||
if context['import_subtitles']:
|
if context['import_subtitles']:
|
||||||
targetMediaDescriptor.importSubtitles(context['subtitle_directory'], context['subtitle_prefix'])
|
targetMediaDescriptor.importSubtitles(context['subtitle_directory'],
|
||||||
|
context['subtitle_prefix'],
|
||||||
|
mediaFileProperties.getSeason(),
|
||||||
|
mediaFileProperties.getEpisode())
|
||||||
|
|
||||||
targetMediaDescriptor.setJellyfinOrder(context['jellyfin'])
|
# raise click.ClickException(f"tmd subindices: {[t.getSubIndex() for t in targetMediaDescriptor.getAllTrackDescriptors()]}")
|
||||||
|
# click.echo(f"tmd subindices: {[t.getIndex() for t in targetMediaDescriptor.getAllTrackDescriptors()]} {[t.getSubIndex() for t in targetMediaDescriptor.getAllTrackDescriptors()]} {[t.getDispositionFlag(TrackDisposition.DEFAULT) for t in targetMediaDescriptor.getAllTrackDescriptors()]}")
|
||||||
|
|
||||||
click.echo(f"Input mapping tokens: {targetMediaDescriptor.getInputMappingTokens()}")
|
if context['use_jellyfin']:
|
||||||
|
# Reorder subtracks in types with default the last, then make subindices flat again
|
||||||
|
targetMediaDescriptor.applyJellyfinOrder()
|
||||||
|
|
||||||
|
# click.echo(f"tmd subindices: {[t.getIndex() for t in targetMediaDescriptor.getAllTrackDescriptors()]} {[t.getSubIndex() for t in targetMediaDescriptor.getAllTrackDescriptors()]} {[t.getDispositionFlag(TrackDisposition.DEFAULT) for t in targetMediaDescriptor.getAllTrackDescriptors()]}")
|
||||||
|
# raise click.Abort
|
||||||
|
|
||||||
|
click.echo(f"Input mapping tokens (2nd pass): {targetMediaDescriptor.getInputMappingTokens()}")
|
||||||
|
|
||||||
fc = FfxController(context, targetMediaDescriptor, sourceMediaDescriptor)
|
fc = FfxController(context, targetMediaDescriptor, sourceMediaDescriptor)
|
||||||
|
|
||||||
@@ -379,9 +440,10 @@ def convert(ctx,
|
|||||||
|
|
||||||
extra = ['ffx'] if sourceFilenameExtension == FfxController.DEFAULT_FILE_EXTENSION else []
|
extra = ['ffx'] if sourceFilenameExtension == FfxController.DEFAULT_FILE_EXTENSION else []
|
||||||
|
|
||||||
targetFilename = fileBasename if context['tmdb'] else mediaFileProperties.assembleTargetFileBasename(label if label else fileBasename,
|
targetFilename = (fileBasename if context['use_tmdb']
|
||||||
|
else mediaFileProperties.assembleTargetFileBasename(label if label else fileBasename,
|
||||||
q if len(q_list) > 1 else -1,
|
q if len(q_list) > 1 else -1,
|
||||||
extraTokens = extra)
|
extraTokens = extra))
|
||||||
|
|
||||||
targetPath = os.path.join(output_directory if output_directory else sourceDirectory, targetFilename)
|
targetPath = os.path.join(output_directory if output_directory else sourceDirectory, targetFilename)
|
||||||
|
|
||||||
@@ -392,7 +454,7 @@ def convert(ctx,
|
|||||||
preset,
|
preset,
|
||||||
denoise)
|
denoise)
|
||||||
|
|
||||||
# #click.confirm('Warning! This file is not compliant to the defined source schema! Do you want to continue?', abort=True)
|
#TODO: click.confirm('Warning! This file is not compliant to the defined source schema! Do you want to continue?', abort=True)
|
||||||
|
|
||||||
endTime = time.perf_counter()
|
endTime = time.perf_counter()
|
||||||
click.echo(f"\nDONE\nTime elapsed {endTime - startTime}")
|
click.echo(f"\nDONE\nTime elapsed {endTime - startTime}")
|
||||||
|
|||||||
@@ -107,7 +107,8 @@ class FfxController():
|
|||||||
|
|
||||||
|
|
||||||
def generateOutputTokens(self, filepath, format, ext):
|
def generateOutputTokens(self, filepath, format, ext):
|
||||||
return ['-f', format, f"{filepath}.{ext}"]
|
outputFilePath = f"{filepath}.{ext}"
|
||||||
|
return ['-f', format, outputFilePath]
|
||||||
|
|
||||||
|
|
||||||
def generateAudioEncodingTokens(self):
|
def generateAudioEncodingTokens(self):
|
||||||
@@ -116,7 +117,8 @@ class FfxController():
|
|||||||
audioTokens = []
|
audioTokens = []
|
||||||
|
|
||||||
#sourceAudioTrackDescriptors = [smd for smd in self.__sourceMediaDescriptor.getAllTrackDescriptors() if smd.getType() == TrackType.AUDIO]
|
#sourceAudioTrackDescriptors = [smd for smd in self.__sourceMediaDescriptor.getAllTrackDescriptors() if smd.getType() == TrackType.AUDIO]
|
||||||
targetAudioTrackDescriptors = [rtd for rtd in self.__targetMediaDescriptor.getReorderedTrackDescriptors() if rtd.getType() == TrackType.AUDIO]
|
# targetAudioTrackDescriptors = [rtd for rtd in self.__targetMediaDescriptor.getReorderedTrackDescriptors() if rtd.getType() == TrackType.AUDIO]
|
||||||
|
targetAudioTrackDescriptors = [td for td in self.__targetMediaDescriptor.getAllTrackDescriptors() if td.getType() == TrackType.AUDIO]
|
||||||
|
|
||||||
trackSubIndex = 0
|
trackSubIndex = 0
|
||||||
for trackDescriptor in targetAudioTrackDescriptors:
|
for trackDescriptor in targetAudioTrackDescriptors:
|
||||||
@@ -165,25 +167,25 @@ class FfxController():
|
|||||||
return audioTokens
|
return audioTokens
|
||||||
|
|
||||||
|
|
||||||
|
# -disposition:s:0 default -disposition:s:1 0
|
||||||
def generateDispositionTokens(self):
|
def generateDispositionTokens(self):
|
||||||
"""Source media descriptor is optional"""
|
|
||||||
|
|
||||||
sourceTrackDescriptors = [] if self.__sourceMediaDescriptor is None else self.__sourceMediaDescriptor.getAllTrackDescriptors()
|
# sourceTrackDescriptors = [] if self.__sourceMediaDescriptor is None else self.__sourceMediaDescriptor.getAllTrackDescriptors()
|
||||||
targetTrackDescriptors = self.__targetMediaDescriptor.getReorderedTrackDescriptors()
|
targetTrackDescriptors = self.__targetMediaDescriptor.getAllTrackDescriptors()
|
||||||
|
|
||||||
dispositionTokens = []
|
dispositionTokens = []
|
||||||
|
|
||||||
# for subStreamIndex in range(len(subDescriptor)):
|
# raise click.ClickException(f"ttd subindices: {[t.getSubIndex() for t in targetTrackDescriptors]}")
|
||||||
|
|
||||||
|
#TODO: Sorting here is for the sole purpose to let the tokens appear with ascending subindices. Why necessary? Jellyfin order?
|
||||||
|
# for trackDescriptor in sorted(targetTrackDescriptors.copy(), key=lambda d: d.getSubIndex()):
|
||||||
for trackDescriptor in targetTrackDescriptors:
|
for trackDescriptor in targetTrackDescriptors:
|
||||||
|
|
||||||
# Calculate source sub index. This applies only if a source media descriptor is defined.
|
#HINT: No dispositions for pgs subtitle tracks that have no external file source
|
||||||
if sourceTrackDescriptors:
|
if (trackDescriptor.getExternalSourceFilePath()
|
||||||
changedTargetTrackDescriptor : TrackDescriptor = targetTrackDescriptors[trackDescriptor.getIndex()]
|
or trackDescriptor.getCodec() != TrackDescriptor.CODEC_PGS):
|
||||||
changedTargetTrackSourceIndex = changedTargetTrackDescriptor.getSourceIndex()
|
|
||||||
subIndex = sourceTrackDescriptors[changedTargetTrackSourceIndex].getSubIndex()
|
|
||||||
else:
|
|
||||||
subIndex = trackDescriptor.getSubIndex()
|
|
||||||
|
|
||||||
|
subIndex = trackDescriptor.getSubIndex()
|
||||||
streamIndicator = trackDescriptor.getType().indicator()
|
streamIndicator = trackDescriptor.getType().indicator()
|
||||||
dispositionSet = trackDescriptor.getDispositionSet()
|
dispositionSet = trackDescriptor.getDispositionSet()
|
||||||
|
|
||||||
@@ -195,155 +197,176 @@ class FfxController():
|
|||||||
return dispositionTokens
|
return dispositionTokens
|
||||||
|
|
||||||
|
|
||||||
|
# def generateMetadataTokens(self):
|
||||||
|
# """Source media descriptor is mandatory"""
|
||||||
|
#
|
||||||
|
# metadataTokens = []
|
||||||
|
#
|
||||||
|
# # click.echo(f"source media descriptor: track indices={[d.getIndex() for d in sourceMediaDescriptor.getAllTrackDescriptors()]}")
|
||||||
|
# # click.echo(f"target media descriptor: track indices={[d.getIndex() for d in targetMediaDescriptor.getAllTrackDescriptors()]}")
|
||||||
|
#
|
||||||
|
# # +jellyfin -jellyfin
|
||||||
|
# mediaDifferences = self.__targetMediaDescriptor.compare(self.__sourceMediaDescriptor)
|
||||||
|
#
|
||||||
|
# # media diff {'tracks': {'changed': {4: {'tags': {'added': {'Yolo'}}}}}}
|
||||||
|
#
|
||||||
|
# click.echo(f"media diff {mediaDifferences}")
|
||||||
|
#
|
||||||
|
# if MediaDescriptor.TAGS_KEY in mediaDifferences.keys():
|
||||||
|
#
|
||||||
|
# sourceTags = self.__sourceMediaDescriptor.getTags()
|
||||||
|
# targetTags = self.__targetMediaDescriptor.getTags()
|
||||||
|
#
|
||||||
|
# #TODO: Warum erscheint nur -1 im output?
|
||||||
|
# if DIFF_REMOVED_KEY in mediaDifferences[MediaDescriptor.TAGS_KEY].keys():
|
||||||
|
# # for removedTagKey in mediaDifferences[MediaDescriptor.TAGS_KEY][DIFF_REMOVED_KEY]:
|
||||||
|
# # row = (f"removed media tag: key='{removedTagKey}' value='{sourceTags[removedTagKey]}'",)
|
||||||
|
# # self.differencesTable.add_row(*map(str, row))
|
||||||
|
# pass
|
||||||
|
# #metadataTokens += [f"-map_metadata:g", "-1"]
|
||||||
|
#
|
||||||
|
# #for targetMediaTagKey in targetTags:
|
||||||
|
# #metadataTokens += [f"-metadata:g", f"{targetMediaTagKey}={targetTags[targetMediaTagKey]}"]
|
||||||
|
#
|
||||||
|
# else:
|
||||||
|
#
|
||||||
|
# if DIFF_ADDED_KEY in mediaDifferences[MediaDescriptor.TAGS_KEY].keys():
|
||||||
|
# for addedTagKey in mediaDifferences[MediaDescriptor.TAGS_KEY][DIFF_ADDED_KEY]:
|
||||||
|
# # row = (f"added media tag: key='{addedTagKey}' value='{targetTags[addedTagKey]}'",)
|
||||||
|
# click.echo(f"added metadata key='{addedTagKey}' value='{targetTags[addedTagKey]}'->'{targetTags[addedTagKey]}'")
|
||||||
|
# # self.differencesTable.add_row(*map(str, row))
|
||||||
|
# #pass
|
||||||
|
# metadataTokens += [f"-metadata:g", f"{addedTagKey}={targetTags[addedTagKey]}"]
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# if DIFF_CHANGED_KEY in mediaDifferences[MediaDescriptor.TAGS_KEY].keys():
|
||||||
|
# for changedTagKey in mediaDifferences[MediaDescriptor.TAGS_KEY][DIFF_CHANGED_KEY]:
|
||||||
|
# #row = (f"changed media tag: key='{changedTagKey}' value='{sourceTags[changedTagKey]}'->'{targetTags[changedTagKey]}'",)
|
||||||
|
# click.echo(f"changed metadata key='{changedTagKey}' value='{sourceTags[changedTagKey]}'->'{targetTags[changedTagKey]}'")
|
||||||
|
# # self.differencesTable.add_row(*map(str, row))
|
||||||
|
# #pass
|
||||||
|
# metadataTokens += [f"-metadata:g", f"{changedTagKey}={targetTags[changedTagKey]}"]
|
||||||
|
#
|
||||||
|
# if MediaDescriptor.TRACKS_KEY in mediaDifferences.keys():
|
||||||
|
#
|
||||||
|
# sourceTrackDescriptors = self.__sourceMediaDescriptor.getAllTrackDescriptors()
|
||||||
|
# targetTrackDescriptors = self.__targetMediaDescriptor.getReorderedTrackDescriptors()
|
||||||
|
#
|
||||||
|
# if DIFF_ADDED_KEY in mediaDifferences[MediaDescriptor.TRACKS_KEY].keys():
|
||||||
|
# addedTracksIndices = mediaDifferences[MediaDescriptor.TRACKS_KEY][DIFF_ADDED_KEY]
|
||||||
|
# raise click.ClickException(f"FfxController.generateMetadataTokens(): Adding tracks is not supported. Track indices {addedTracksIndices}")
|
||||||
|
#
|
||||||
|
# #raise click.ClickException(f"add track {mediaDifferences[MediaDescriptor.TRACKS_KEY][DIFF_ADDED_KEY]}")
|
||||||
|
# #for addedTrackIndex in mediaDifferences[MediaDescriptor.TRACKS_KEY][DIFF_ADDED_KEY]:
|
||||||
|
# #addedTrack : Track = targetTrackDescriptors[addedTrackIndex]
|
||||||
|
# # row = (f"added {addedTrack.getType().label()} track: index={addedTrackIndex} lang={addedTrack.getLanguage().threeLetter()}",)
|
||||||
|
# # self.differencesTable.add_row(*map(str, row))
|
||||||
|
#
|
||||||
|
# if DIFF_REMOVED_KEY in mediaDifferences[MediaDescriptor.TRACKS_KEY].keys():
|
||||||
|
# removedTracksIndices = mediaDifferences[MediaDescriptor.TRACKS_KEY][DIFF_ADDED_KEY].keys()
|
||||||
|
# raise click.ClickException(f"FfxController.generateMetadataTokens(): Removing tracks is not supported. Track indices {removedTracksIndices}")
|
||||||
|
# #for removedTrackIndex in mediaDifferences[MediaDescriptor.TRACKS_KEY][DIFF_REMOVED_KEY]:
|
||||||
|
# # row = (f"removed track: index={removedTrackIndex}",)
|
||||||
|
# # self.differencesTable.add_row(*map(str, row))
|
||||||
|
#
|
||||||
|
# # media diff {'tracks': {'changed': {4: {'tags': {'added': {'Yolo'}}}}}}
|
||||||
|
# if DIFF_CHANGED_KEY in mediaDifferences[MediaDescriptor.TRACKS_KEY].keys():
|
||||||
|
# for changedTrackIndex in mediaDifferences[MediaDescriptor.TRACKS_KEY][DIFF_CHANGED_KEY].keys():
|
||||||
|
#
|
||||||
|
# changedTargetTrackDescriptor : TrackDescriptor = targetTrackDescriptors[changedTrackIndex]
|
||||||
|
# changedTargetTrackSourceIndex = changedTargetTrackDescriptor.getSourceIndex()
|
||||||
|
# changedTargetSourceSubIndex = sourceTrackDescriptors[changedTargetTrackSourceIndex].getSubIndex()
|
||||||
|
# # changedSourceTrackDescriptor : TrackDescriptor = sourceTrackDescriptors[changedTargetTrackSourceIndex]
|
||||||
|
# # changedSourceTrackSubIndex = changedSourceTrackDescriptor.getSubIndex()
|
||||||
|
#
|
||||||
|
# changedTrackDiff : dict = mediaDifferences[MediaDescriptor.TRACKS_KEY][DIFF_CHANGED_KEY][changedTrackIndex]
|
||||||
|
#
|
||||||
|
# if MediaDescriptor.TAGS_KEY in changedTrackDiff.keys():
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# if DIFF_REMOVED_KEY in changedTrackDiff[MediaDescriptor.TAGS_KEY]:
|
||||||
|
# #for removedTagKey in changedTrackDiff[MediaDescriptor.TAGS_KEY][DIFF_REMOVED_KEY]:
|
||||||
|
# # row = (f"changed {changedTargetTrackDescriptor.getType().label()} track index={changedTrackIndex} removed key={removedTagKey}",)
|
||||||
|
# # self.differencesTable.add_row(*map(str, row))
|
||||||
|
#
|
||||||
|
# #addedTagValue = targetTrackDescriptors[changedTargetTrackSourceIndex].getTags()[addedTagKey]
|
||||||
|
#
|
||||||
|
# metadataTokens += [f"-map_metadata:s:{changedTargetTrackDescriptor.getType().indicator()}:{changedTargetSourceSubIndex}", "-1"]
|
||||||
|
#
|
||||||
|
# for targetTrackTagKey, targetTrackTagValue in changedTargetTrackDescriptor.getTags():
|
||||||
|
# metadataTokens += [f"-metadata:s:{changedTargetTrackDescriptor.getType().indicator()}:{changedTargetSourceSubIndex}",
|
||||||
|
# f"{targetTrackTagKey}={targetTrackTagValue}"]
|
||||||
|
#
|
||||||
|
# else:
|
||||||
|
#
|
||||||
|
# # media diff {'tracks': {'changed': {4: {'tags': {'added': {'Yolo'}}}}}}
|
||||||
|
# if DIFF_ADDED_KEY in changedTrackDiff[MediaDescriptor.TAGS_KEY]:
|
||||||
|
# for addedTagKey in changedTrackDiff[MediaDescriptor.TAGS_KEY][DIFF_ADDED_KEY]:
|
||||||
|
#
|
||||||
|
# addedTagValue = targetTrackDescriptors[changedTargetTrackSourceIndex].getTags()[addedTagKey]
|
||||||
|
#
|
||||||
|
# # addedTagValue = changedTrackDiff[MediaDescriptor.TAGS_KEY][DIFF_ADDED_KEY][addedTagKey]
|
||||||
|
#
|
||||||
|
# # click.echo(f"addedTagValue={addedTagValue}")
|
||||||
|
# # click.echo(f"sourceTrackDescriptors: subindex={[s.getSubIndex() for s in sourceTrackDescriptors]} sourceindex={[s.getSourceIndex() for s in sourceTrackDescriptors]} tags={[s.getTags() for s in sourceTrackDescriptors]}")
|
||||||
|
# # click.echo(f"targetTrackDescriptors: subindex={[t.getSubIndex() for t in targetTrackDescriptors]} sourceindex={[t.getSourceIndex() for t in targetTrackDescriptors]} tags={[t.getTags() for t in targetTrackDescriptors]}")
|
||||||
|
# # click.echo(f"changed track_index={changedTrackIndex} indicator={changedTargetTrackDescriptor.getType().indicator()} key={addedTagKey} value={addedTagValue} source_index={changedSourceTrackIndex}")
|
||||||
|
#
|
||||||
|
# metadataTokens += [f"-metadata:s:{changedTargetTrackDescriptor.getType().indicator()}:{changedTargetSourceSubIndex}",
|
||||||
|
# f"{addedTagKey}={addedTagValue}"]
|
||||||
|
#
|
||||||
|
# # media diff {'tracks': {'changed': {4: {'tags': {'added': {'Yolo'}}}}}}
|
||||||
|
# if DIFF_CHANGED_KEY in changedTrackDiff[MediaDescriptor.TAGS_KEY]:
|
||||||
|
# for changedTagKey in changedTrackDiff[MediaDescriptor.TAGS_KEY][DIFF_CHANGED_KEY]:
|
||||||
|
#
|
||||||
|
# changedTagValue = targetTrackDescriptors[changedTargetTrackSourceIndex].getTags()[changedTagKey]
|
||||||
|
# # sourceSubIndex = sourceTrackDescriptors[changedTargetTrackSourceIndex].getSubIndex()
|
||||||
|
# # addedTagValue = changedTrackDiff[MediaDescriptor.TAGS_KEY][DIFF_ADDED_KEY][addedTagKey]
|
||||||
|
#
|
||||||
|
# # click.echo(f"addedTagValue={addedTagValue}")
|
||||||
|
# # click.echo(f"sourceTrackDescriptors: subindex={[s.getSubIndex() for s in sourceTrackDescriptors]} sourceindex={[s.getSourceIndex() for s in sourceTrackDescriptors]} tags={[s.getTags() for s in sourceTrackDescriptors]}")
|
||||||
|
# # click.echo(f"targetTrackDescriptors: subindex={[t.getSubIndex() for t in targetTrackDescriptors]} sourceindex={[t.getSourceIndex() for t in targetTrackDescriptors]} tags={[t.getTags() for t in targetTrackDescriptors]}")
|
||||||
|
# # click.echo(f"changed track_index={changedTrackIndex} indicator={changedTargetTrackDescriptor.getType().indicator()} key={addedTagKey} value={addedTagValue} source_index={changedSourceTrackIndex}")
|
||||||
|
#
|
||||||
|
# metadataTokens += [f"-metadata:s:{changedTargetTrackDescriptor.getType().indicator()}:{changedTargetSourceSubIndex}",
|
||||||
|
# f"{changedTagKey}={changedTagValue}"]
|
||||||
|
#
|
||||||
|
# # if TrackDescriptor.DISPOSITION_SET_KEY in changedTrackDiff.keys():
|
||||||
|
#
|
||||||
|
# # if DIFF_ADDED_KEY in changedTrackDiff[TrackDescriptor.DISPOSITION_SET_KEY]:
|
||||||
|
# # for addedDisposition in changedTrackDiff[TrackDescriptor.DISPOSITION_SET_KEY][DIFF_ADDED_KEY]:
|
||||||
|
# # # row = (f"changed {changedTargetTrackDescriptor.getType().label()} track index={changedTrackIndex} added disposition={addedDisposition.label()}",)
|
||||||
|
# # # self.differencesTable.add_row(*map(str, row))
|
||||||
|
# # pass
|
||||||
|
#
|
||||||
|
# # if DIFF_REMOVED_KEY in changedTrackDiff[TrackDescriptor.DISPOSITION_SET_KEY]:
|
||||||
|
# # for removedDisposition in changedTrackDiff[TrackDescriptor.DISPOSITION_SET_KEY][DIFF_REMOVED_KEY]:
|
||||||
|
# # # row = (f"changed {changedTargetTrackDescriptor.getType().label()} track index={changedTrackIndex} removed disposition={removedDisposition.label()}",)
|
||||||
|
# # # self.differencesTable.add_row(*map(str, row))
|
||||||
|
# # pass
|
||||||
|
|
||||||
|
# return metadataTokens
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def generateMetadataTokens(self):
|
def generateMetadataTokens(self):
|
||||||
"""Source media descriptor is mandatory"""
|
|
||||||
|
|
||||||
mappingTokens = []
|
metadataTokens = []
|
||||||
|
|
||||||
# click.echo(f"source media descriptor: track indices={[d.getIndex() for d in sourceMediaDescriptor.getAllTrackDescriptors()]}")
|
for tagKey, tagValue in self.__targetMediaDescriptor.getTags().items():
|
||||||
# click.echo(f"target media descriptor: track indices={[d.getIndex() for d in targetMediaDescriptor.getAllTrackDescriptors()]}")
|
metadataTokens += [f"-metadata:g",
|
||||||
|
f"{tagKey}={tagValue}"]
|
||||||
|
|
||||||
# +jellyfin -jellyfin
|
#HINT: With current ffmpeg version track metadata tags are not passed to the outfile
|
||||||
mediaDifferences = self.__targetMediaDescriptor.compare(self.__sourceMediaDescriptor)
|
for td in self.__targetMediaDescriptor.getAllTrackDescriptors():
|
||||||
|
|
||||||
# media diff {'tracks': {'changed': {4: {'tags': {'added': {'Yolo'}}}}}}
|
for tagKey, tagValue in td.getTags().items():
|
||||||
|
|
||||||
click.echo(f"media diff {mediaDifferences}")
|
metadataTokens += [f"-metadata:s:{td.getType().indicator()}:{td.getSubIndex()}",
|
||||||
|
f"{tagKey}={tagValue}"]
|
||||||
|
|
||||||
if MediaDescriptor.TAGS_KEY in mediaDifferences.keys():
|
return metadataTokens
|
||||||
|
|
||||||
sourceTags = self.__sourceMediaDescriptor.getTags()
|
|
||||||
targetTags = self.__targetMediaDescriptor.getTags()
|
|
||||||
|
|
||||||
|
|
||||||
if DIFF_REMOVED_KEY in mediaDifferences[MediaDescriptor.TAGS_KEY].keys():
|
|
||||||
# for removedTagKey in mediaDifferences[MediaDescriptor.TAGS_KEY][DIFF_REMOVED_KEY]:
|
|
||||||
# row = (f"removed media tag: key='{removedTagKey}' value='{sourceTags[removedTagKey]}'",)
|
|
||||||
# self.differencesTable.add_row(*map(str, row))
|
|
||||||
mappingTokens += [f"-map_metadata:g", "-1"]
|
|
||||||
|
|
||||||
for targetMediaTagKey in targetTags:
|
|
||||||
mappingTokens += [f"-metadata:g", f"{targetMediaTagKey}={targetTags[targetMediaTagKey]}"]
|
|
||||||
|
|
||||||
else:
|
|
||||||
|
|
||||||
if DIFF_ADDED_KEY in mediaDifferences[MediaDescriptor.TAGS_KEY].keys():
|
|
||||||
for addedTagKey in mediaDifferences[MediaDescriptor.TAGS_KEY][DIFF_ADDED_KEY]:
|
|
||||||
# row = (f"added media tag: key='{addedTagKey}' value='{targetTags[addedTagKey]}'",)
|
|
||||||
click.echo(f"added metadata key='{addedTagKey}' value='{targetTags[addedTagKey]}'->'{targetTags[addedTagKey]}'")
|
|
||||||
# self.differencesTable.add_row(*map(str, row))
|
|
||||||
#pass
|
|
||||||
mappingTokens += [f"-metadata:g", f"{addedTagKey}={targetTags[addedTagKey]}"]
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if DIFF_CHANGED_KEY in mediaDifferences[MediaDescriptor.TAGS_KEY].keys():
|
|
||||||
for changedTagKey in mediaDifferences[MediaDescriptor.TAGS_KEY][DIFF_CHANGED_KEY]:
|
|
||||||
#row = (f"changed media tag: key='{changedTagKey}' value='{sourceTags[changedTagKey]}'->'{targetTags[changedTagKey]}'",)
|
|
||||||
click.echo(f"changed metadata key='{changedTagKey}' value='{sourceTags[changedTagKey]}'->'{targetTags[changedTagKey]}'")
|
|
||||||
# self.differencesTable.add_row(*map(str, row))
|
|
||||||
#pass
|
|
||||||
mappingTokens += [f"-metadata:g", f"{changedTagKey}={targetTags[changedTagKey]}"]
|
|
||||||
|
|
||||||
if MediaDescriptor.TRACKS_KEY in mediaDifferences.keys():
|
|
||||||
|
|
||||||
sourceTrackDescriptors = self.__sourceMediaDescriptor.getAllTrackDescriptors()
|
|
||||||
targetTrackDescriptors = self.__targetMediaDescriptor.getReorderedTrackDescriptors()
|
|
||||||
|
|
||||||
if DIFF_ADDED_KEY in mediaDifferences[MediaDescriptor.TRACKS_KEY].keys():
|
|
||||||
addedTracksIndices = mediaDifferences[MediaDescriptor.TRACKS_KEY][DIFF_ADDED_KEY]
|
|
||||||
raise click.ClickException(f"FfxController.generateMetadataTokens(): Adding tracks is not supported. Track indices {addedTracksIndices}")
|
|
||||||
|
|
||||||
#raise click.ClickException(f"add track {mediaDifferences[MediaDescriptor.TRACKS_KEY][DIFF_ADDED_KEY]}")
|
|
||||||
#for addedTrackIndex in mediaDifferences[MediaDescriptor.TRACKS_KEY][DIFF_ADDED_KEY]:
|
|
||||||
#addedTrack : Track = targetTrackDescriptors[addedTrackIndex]
|
|
||||||
# row = (f"added {addedTrack.getType().label()} track: index={addedTrackIndex} lang={addedTrack.getLanguage().threeLetter()}",)
|
|
||||||
# self.differencesTable.add_row(*map(str, row))
|
|
||||||
|
|
||||||
if DIFF_REMOVED_KEY in mediaDifferences[MediaDescriptor.TRACKS_KEY].keys():
|
|
||||||
removedTracksIndices = mediaDifferences[MediaDescriptor.TRACKS_KEY][DIFF_ADDED_KEY].keys()
|
|
||||||
raise click.ClickException(f"FfxController.generateMetadataTokens(): Removing tracks is not supported. Track indices {removedTracksIndices}")
|
|
||||||
#for removedTrackIndex in mediaDifferences[MediaDescriptor.TRACKS_KEY][DIFF_REMOVED_KEY]:
|
|
||||||
# row = (f"removed track: index={removedTrackIndex}",)
|
|
||||||
# self.differencesTable.add_row(*map(str, row))
|
|
||||||
|
|
||||||
# media diff {'tracks': {'changed': {4: {'tags': {'added': {'Yolo'}}}}}}
|
|
||||||
if DIFF_CHANGED_KEY in mediaDifferences[MediaDescriptor.TRACKS_KEY].keys():
|
|
||||||
for changedTrackIndex in mediaDifferences[MediaDescriptor.TRACKS_KEY][DIFF_CHANGED_KEY].keys():
|
|
||||||
|
|
||||||
changedTargetTrackDescriptor : TrackDescriptor = targetTrackDescriptors[changedTrackIndex]
|
|
||||||
changedTargetTrackSourceIndex = changedTargetTrackDescriptor.getSourceIndex()
|
|
||||||
changedTargetSourceSubIndex = sourceTrackDescriptors[changedTargetTrackSourceIndex].getSubIndex()
|
|
||||||
# changedSourceTrackDescriptor : TrackDescriptor = sourceTrackDescriptors[changedTargetTrackSourceIndex]
|
|
||||||
# changedSourceTrackSubIndex = changedSourceTrackDescriptor.getSubIndex()
|
|
||||||
|
|
||||||
changedTrackDiff : dict = mediaDifferences[MediaDescriptor.TRACKS_KEY][DIFF_CHANGED_KEY][changedTrackIndex]
|
|
||||||
|
|
||||||
if MediaDescriptor.TAGS_KEY in changedTrackDiff.keys():
|
|
||||||
|
|
||||||
|
|
||||||
if DIFF_REMOVED_KEY in changedTrackDiff[MediaDescriptor.TAGS_KEY]:
|
|
||||||
#for removedTagKey in changedTrackDiff[MediaDescriptor.TAGS_KEY][DIFF_REMOVED_KEY]:
|
|
||||||
# row = (f"changed {changedTargetTrackDescriptor.getType().label()} track index={changedTrackIndex} removed key={removedTagKey}",)
|
|
||||||
# self.differencesTable.add_row(*map(str, row))
|
|
||||||
|
|
||||||
#addedTagValue = targetTrackDescriptors[changedTargetTrackSourceIndex].getTags()[addedTagKey]
|
|
||||||
|
|
||||||
mappingTokens += [f"-map_metadata:s:{changedTargetTrackDescriptor.getType().indicator()}:{changedTargetSourceSubIndex}", "-1"]
|
|
||||||
|
|
||||||
for targetTrackTagKey, targetTrackTagValue in changedTargetTrackDescriptor.getTags():
|
|
||||||
mappingTokens += [f"-metadata:s:{changedTargetTrackDescriptor.getType().indicator()}:{changedTargetSourceSubIndex}",
|
|
||||||
f"{targetTrackTagKey}={targetTrackTagValue}"]
|
|
||||||
|
|
||||||
else:
|
|
||||||
|
|
||||||
# media diff {'tracks': {'changed': {4: {'tags': {'added': {'Yolo'}}}}}}
|
|
||||||
if DIFF_ADDED_KEY in changedTrackDiff[MediaDescriptor.TAGS_KEY]:
|
|
||||||
for addedTagKey in changedTrackDiff[MediaDescriptor.TAGS_KEY][DIFF_ADDED_KEY]:
|
|
||||||
|
|
||||||
addedTagValue = targetTrackDescriptors[changedTargetTrackSourceIndex].getTags()[addedTagKey]
|
|
||||||
|
|
||||||
# addedTagValue = changedTrackDiff[MediaDescriptor.TAGS_KEY][DIFF_ADDED_KEY][addedTagKey]
|
|
||||||
|
|
||||||
# click.echo(f"addedTagValue={addedTagValue}")
|
|
||||||
# click.echo(f"sourceTrackDescriptors: subindex={[s.getSubIndex() for s in sourceTrackDescriptors]} sourceindex={[s.getSourceIndex() for s in sourceTrackDescriptors]} tags={[s.getTags() for s in sourceTrackDescriptors]}")
|
|
||||||
# click.echo(f"targetTrackDescriptors: subindex={[t.getSubIndex() for t in targetTrackDescriptors]} sourceindex={[t.getSourceIndex() for t in targetTrackDescriptors]} tags={[t.getTags() for t in targetTrackDescriptors]}")
|
|
||||||
# click.echo(f"changed track_index={changedTrackIndex} indicator={changedTargetTrackDescriptor.getType().indicator()} key={addedTagKey} value={addedTagValue} source_index={changedSourceTrackIndex}")
|
|
||||||
|
|
||||||
mappingTokens += [f"-metadata:s:{changedTargetTrackDescriptor.getType().indicator()}:{changedTargetSourceSubIndex}",
|
|
||||||
f"{addedTagKey}={addedTagValue}"]
|
|
||||||
|
|
||||||
# media diff {'tracks': {'changed': {4: {'tags': {'added': {'Yolo'}}}}}}
|
|
||||||
if DIFF_CHANGED_KEY in changedTrackDiff[MediaDescriptor.TAGS_KEY]:
|
|
||||||
for changedTagKey in changedTrackDiff[MediaDescriptor.TAGS_KEY][DIFF_CHANGED_KEY]:
|
|
||||||
|
|
||||||
changedTagValue = targetTrackDescriptors[changedTargetTrackSourceIndex].getTags()[changedTagKey]
|
|
||||||
# sourceSubIndex = sourceTrackDescriptors[changedTargetTrackSourceIndex].getSubIndex()
|
|
||||||
# addedTagValue = changedTrackDiff[MediaDescriptor.TAGS_KEY][DIFF_ADDED_KEY][addedTagKey]
|
|
||||||
|
|
||||||
# click.echo(f"addedTagValue={addedTagValue}")
|
|
||||||
# click.echo(f"sourceTrackDescriptors: subindex={[s.getSubIndex() for s in sourceTrackDescriptors]} sourceindex={[s.getSourceIndex() for s in sourceTrackDescriptors]} tags={[s.getTags() for s in sourceTrackDescriptors]}")
|
|
||||||
# click.echo(f"targetTrackDescriptors: subindex={[t.getSubIndex() for t in targetTrackDescriptors]} sourceindex={[t.getSourceIndex() for t in targetTrackDescriptors]} tags={[t.getTags() for t in targetTrackDescriptors]}")
|
|
||||||
# click.echo(f"changed track_index={changedTrackIndex} indicator={changedTargetTrackDescriptor.getType().indicator()} key={addedTagKey} value={addedTagValue} source_index={changedSourceTrackIndex}")
|
|
||||||
|
|
||||||
mappingTokens += [f"-metadata:s:{changedTargetTrackDescriptor.getType().indicator()}:{changedTargetSourceSubIndex}",
|
|
||||||
f"{changedTagKey}={changedTagValue}"]
|
|
||||||
|
|
||||||
# if TrackDescriptor.DISPOSITION_SET_KEY in changedTrackDiff.keys():
|
|
||||||
|
|
||||||
# if DIFF_ADDED_KEY in changedTrackDiff[TrackDescriptor.DISPOSITION_SET_KEY]:
|
|
||||||
# for addedDisposition in changedTrackDiff[TrackDescriptor.DISPOSITION_SET_KEY][DIFF_ADDED_KEY]:
|
|
||||||
# # row = (f"changed {changedTargetTrackDescriptor.getType().label()} track index={changedTrackIndex} added disposition={addedDisposition.label()}",)
|
|
||||||
# # self.differencesTable.add_row(*map(str, row))
|
|
||||||
# pass
|
|
||||||
|
|
||||||
# if DIFF_REMOVED_KEY in changedTrackDiff[TrackDescriptor.DISPOSITION_SET_KEY]:
|
|
||||||
# for removedDisposition in changedTrackDiff[TrackDescriptor.DISPOSITION_SET_KEY][DIFF_REMOVED_KEY]:
|
|
||||||
# # row = (f"changed {changedTargetTrackDescriptor.getType().label()} track index={changedTrackIndex} removed disposition={removedDisposition.label()}",)
|
|
||||||
# # self.differencesTable.add_row(*map(str, row))
|
|
||||||
# pass
|
|
||||||
|
|
||||||
return mappingTokens
|
|
||||||
|
|
||||||
|
|
||||||
def runJob(self,
|
def runJob(self,
|
||||||
@@ -390,11 +413,11 @@ class FfxController():
|
|||||||
if videoEncoder == VideoEncoder.VP9:
|
if videoEncoder == VideoEncoder.VP9:
|
||||||
|
|
||||||
commandSequence1 = (commandTokens
|
commandSequence1 = (commandTokens
|
||||||
+ self.__targetMediaDescriptor.getInputMappingTokens()
|
+ self.__targetMediaDescriptor.getInputMappingTokens(only_video=True)
|
||||||
+ self.generateVP9Pass1Tokens(int(quality)))
|
+ self.generateVP9Pass1Tokens(int(quality)))
|
||||||
|
|
||||||
if self.__context['perform_crop']:
|
if self.__context['perform_crop']:
|
||||||
commandSequence1 += FfxController.generateCropTokens()
|
commandSequence1 += self.generateCropTokens()
|
||||||
|
|
||||||
commandSequence1 += FfxController.NULL_TOKENS
|
commandSequence1 += FfxController.NULL_TOKENS
|
||||||
|
|
||||||
@@ -420,7 +443,7 @@ class FfxController():
|
|||||||
commandSequence2 += self.generateVP9Pass2Tokens(int(quality)) + self.generateAudioEncodingTokens()
|
commandSequence2 += self.generateVP9Pass2Tokens(int(quality)) + self.generateAudioEncodingTokens()
|
||||||
|
|
||||||
if self.__context['perform_crop']:
|
if self.__context['perform_crop']:
|
||||||
commandSequence2 += FfxController.generateCropTokens()
|
commandSequence2 += self.generateCropTokens()
|
||||||
|
|
||||||
commandSequence2 += self.generateOutputTokens(targetPath,
|
commandSequence2 += self.generateOutputTokens(targetPath,
|
||||||
FfxController.DEFAULT_FILE_FORMAT,
|
FfxController.DEFAULT_FILE_FORMAT,
|
||||||
@@ -429,4 +452,7 @@ class FfxController():
|
|||||||
click.echo(f"Command 2: {' '.join(commandSequence2)}")
|
click.echo(f"Command 2: {' '.join(commandSequence2)}")
|
||||||
|
|
||||||
if not self.__context['dry_run']:
|
if not self.__context['dry_run']:
|
||||||
executeProcess(commandSequence2)
|
out, err, rc = executeProcess(commandSequence2)
|
||||||
|
if rc:
|
||||||
|
raise click.ClickException(f"Command resulted in error: rc={rc} error={err}")
|
||||||
|
|
||||||
|
|||||||
@@ -186,6 +186,13 @@ class FileProperties():
|
|||||||
return int(self.__episode)
|
return int(self.__episode)
|
||||||
|
|
||||||
|
|
||||||
|
def getFilename(self):
|
||||||
|
return self.__sourceFilename
|
||||||
|
|
||||||
|
def getFileBasename(self):
|
||||||
|
return self.__sourceFileBasename
|
||||||
|
|
||||||
|
|
||||||
def assembleTargetFileBasename(self,
|
def assembleTargetFileBasename(self,
|
||||||
label: str = "",
|
label: str = "",
|
||||||
quality: int = -1,
|
quality: int = -1,
|
||||||
|
|||||||
@@ -61,15 +61,6 @@ class MediaDescriptor:
|
|||||||
else:
|
else:
|
||||||
self.__trackDescriptors = []
|
self.__trackDescriptors = []
|
||||||
|
|
||||||
if MediaDescriptor.CLEAR_TAGS_FLAG_KEY in kwargs.keys():
|
|
||||||
if type(kwargs[MediaDescriptor.CLEAR_TAGS_FLAG_KEY]) is not bool:
|
|
||||||
raise TypeError(
|
|
||||||
f"MediaDescriptor.__init__(): Argument {MediaDescriptor.CLEAR_TAGS_FLAG_KEY} is required to be of type bool"
|
|
||||||
)
|
|
||||||
self.__clearTags = kwargs[MediaDescriptor.CLEAR_TAGS_FLAG_KEY]
|
|
||||||
else:
|
|
||||||
self.__clearTags = False
|
|
||||||
|
|
||||||
if MediaDescriptor.JELLYFIN_ORDER_FLAG_KEY in kwargs.keys():
|
if MediaDescriptor.JELLYFIN_ORDER_FLAG_KEY in kwargs.keys():
|
||||||
if type(kwargs[MediaDescriptor.JELLYFIN_ORDER_FLAG_KEY]) is not bool:
|
if type(kwargs[MediaDescriptor.JELLYFIN_ORDER_FLAG_KEY]) is not bool:
|
||||||
raise TypeError(
|
raise TypeError(
|
||||||
@@ -79,77 +70,6 @@ class MediaDescriptor:
|
|||||||
else:
|
else:
|
||||||
self.__jellyfinOrder = False
|
self.__jellyfinOrder = False
|
||||||
|
|
||||||
def getDefaultVideoTrack(self):
|
|
||||||
videoDefaultTracks = [
|
|
||||||
v
|
|
||||||
for v in self.getVideoTracks()
|
|
||||||
if TrackDisposition.DEFAULT in v.getDispositionSet()
|
|
||||||
]
|
|
||||||
if len(videoDefaultTracks) > 1:
|
|
||||||
raise ValueError(
|
|
||||||
"MediaDescriptor.getDefaultVideoTrack(): More than one default video track is not supported"
|
|
||||||
)
|
|
||||||
return videoDefaultTracks[0] if videoDefaultTracks else None
|
|
||||||
|
|
||||||
def getForcedVideoTrack(self):
|
|
||||||
videoForcedTracks = [
|
|
||||||
v
|
|
||||||
for v in self.getVideoTracks()
|
|
||||||
if TrackDisposition.FORCED in v.getDispositionSet()
|
|
||||||
]
|
|
||||||
if len(videoForcedTracks) > 1:
|
|
||||||
raise ValueError(
|
|
||||||
"MediaDescriptor.getForcedVideoTrack(): More than one forced video track is not supported"
|
|
||||||
)
|
|
||||||
return videoForcedTracks[0] if videoForcedTracks else None
|
|
||||||
|
|
||||||
def getDefaultAudioTrack(self):
|
|
||||||
audioDefaultTracks = [
|
|
||||||
a
|
|
||||||
for a in self.getAudioTracks()
|
|
||||||
if TrackDisposition.DEFAULT in a.getDispositionSet()
|
|
||||||
]
|
|
||||||
if len(audioDefaultTracks) > 1:
|
|
||||||
raise ValueError(
|
|
||||||
"MediaDescriptor.getDefaultAudioTrack(): More than one default audio track is not supported"
|
|
||||||
)
|
|
||||||
return audioDefaultTracks[0] if audioDefaultTracks else None
|
|
||||||
|
|
||||||
def getForcedAudioTrack(self):
|
|
||||||
audioForcedTracks = [
|
|
||||||
a
|
|
||||||
for a in self.getAudioTracks()
|
|
||||||
if TrackDisposition.FORCED in a.getDispositionSet()
|
|
||||||
]
|
|
||||||
if len(audioForcedTracks) > 1:
|
|
||||||
raise ValueError(
|
|
||||||
"MediaDescriptor.getForcedAudioTrack(): More than one forced audio track is not supported"
|
|
||||||
)
|
|
||||||
return audioForcedTracks[0] if audioForcedTracks else None
|
|
||||||
|
|
||||||
def getDefaultSubtitleTrack(self):
|
|
||||||
subtitleDefaultTracks = [
|
|
||||||
s
|
|
||||||
for s in self.getSubtitleTracks()
|
|
||||||
if TrackDisposition.DEFAULT in s.getDispositionSet()
|
|
||||||
]
|
|
||||||
if len(subtitleDefaultTracks) > 1:
|
|
||||||
raise ValueError(
|
|
||||||
"MediaDescriptor.getDefaultSubtitleTrack(): More than one default subtitle track is not supported"
|
|
||||||
)
|
|
||||||
return subtitleDefaultTracks[0] if subtitleDefaultTracks else None
|
|
||||||
|
|
||||||
def getForcedSubtitleTrack(self):
|
|
||||||
subtitleForcedTracks = [
|
|
||||||
s
|
|
||||||
for s in self.getSubtitleTracks()
|
|
||||||
if TrackDisposition.FORCED in s.getDispositionSet()
|
|
||||||
]
|
|
||||||
if len(subtitleForcedTracks) > 1:
|
|
||||||
raise ValueError(
|
|
||||||
"MediaDescriptor.getForcedSubtitleTrack(): More than one forced subtitle track is not supported"
|
|
||||||
)
|
|
||||||
return subtitleForcedTracks[0] if subtitleForcedTracks else None
|
|
||||||
|
|
||||||
def setDefaultSubTrack(self, trackType: TrackType, subIndex: int):
|
def setDefaultSubTrack(self, trackType: TrackType, subIndex: int):
|
||||||
for t in self.getAllTrackDescriptors():
|
for t in self.getAllTrackDescriptors():
|
||||||
@@ -165,56 +85,66 @@ class MediaDescriptor:
|
|||||||
TrackDisposition.FORCED, t.getSubIndex() == int(subIndex)
|
TrackDisposition.FORCED, t.getSubIndex() == int(subIndex)
|
||||||
)
|
)
|
||||||
|
|
||||||
def checkDefaultAndForcedDispositions(self):
|
|
||||||
try:
|
|
||||||
self.getDefaultVideoTrack()
|
|
||||||
self.getForcedVideoTrack()
|
|
||||||
self.getDefaultAudioTrack()
|
|
||||||
self.getForcedAudioTrack()
|
|
||||||
self.getDefaultSubtitleTrack()
|
|
||||||
self.getForcedSubtitleTrack()
|
|
||||||
return True
|
|
||||||
except ValueError:
|
|
||||||
return False
|
|
||||||
|
|
||||||
def getReorderedTrackDescriptors(self):
|
def checkConfiguration(self):
|
||||||
|
|
||||||
videoTracks = self.sortSubIndices(self.getVideoTracks())
|
videoTracks = self.getVideoTracks()
|
||||||
audioTracks = self.sortSubIndices(self.getAudioTracks())
|
audioTracks = self.getAudioTracks()
|
||||||
subtitleTracks = self.sortSubIndices(self.getSubtitleTracks())
|
subtitleTracks = self.getSubtitleTracks()
|
||||||
|
|
||||||
videoDefaultTrack = self.getDefaultVideoTrack()
|
if len([v for v in videoTracks if v.getDispositionFlag(TrackDisposition.DEFAULT)]) > 1:
|
||||||
self.getForcedVideoTrack()
|
raise ValueError('More than one default video track')
|
||||||
audioDefaultTrack = self.getDefaultAudioTrack()
|
if len([a for a in audioTracks if a.getDispositionFlag(TrackDisposition.DEFAULT)]) > 1:
|
||||||
self.getForcedAudioTrack()
|
raise ValueError('More than one default audio track')
|
||||||
subtitleDefaultTrack = self.getDefaultSubtitleTrack()
|
if len([s for s in subtitleTracks if s.getDispositionFlag(TrackDisposition.DEFAULT)]) > 1:
|
||||||
self.getForcedSubtitleTrack()
|
raise ValueError('More than one default subtitle track')
|
||||||
|
|
||||||
if self.__jellyfinOrder:
|
if len([v for v in videoTracks if v.getDispositionFlag(TrackDisposition.FORCED)]) > 1:
|
||||||
if not videoDefaultTrack is None:
|
raise ValueError('More than one forced video track')
|
||||||
videoTracks.append(
|
if len([a for a in audioTracks if a.getDispositionFlag(TrackDisposition.FORCED)]) > 1:
|
||||||
videoTracks.pop(videoTracks.index(videoDefaultTrack))
|
raise ValueError('More than one forced audio track')
|
||||||
)
|
if len([s for s in subtitleTracks if s.getDispositionFlag(TrackDisposition.FORCED)]) > 1:
|
||||||
if not audioDefaultTrack is None:
|
raise ValueError('More than one forced subtitle track')
|
||||||
audioTracks.append(
|
|
||||||
audioTracks.pop(audioTracks.index(audioDefaultTrack))
|
|
||||||
)
|
|
||||||
if not subtitleDefaultTrack is None:
|
|
||||||
subtitleTracks.append(
|
|
||||||
subtitleTracks.pop(subtitleTracks.index(subtitleDefaultTrack))
|
|
||||||
)
|
|
||||||
|
|
||||||
reorderedTrackDescriptors = videoTracks + audioTracks + subtitleTracks
|
trackDescriptors = videoTracks + audioTracks + subtitleTracks
|
||||||
orderedSourceTrackSequence = [
|
sourceIndices = [
|
||||||
t.getSourceIndex() for t in reorderedTrackDescriptors
|
t.getSourceIndex() for t in trackDescriptors
|
||||||
]
|
]
|
||||||
|
if len(set(sourceIndices)) < len(trackDescriptors):
|
||||||
|
raise ValueError('Multiple streams originating from the same source stream')
|
||||||
|
|
||||||
if len(set(orderedSourceTrackSequence)) < len(orderedSourceTrackSequence):
|
|
||||||
raise ValueError(
|
|
||||||
f"Multiple streams originating from the same source stream not supported"
|
|
||||||
)
|
|
||||||
|
|
||||||
return reorderedTrackDescriptors
|
def applyJellyfinOrder(self):
|
||||||
|
"""Reorder subtracks in types with default the last, then make subindices flat again"""
|
||||||
|
|
||||||
|
# videoTracks = self.sortSubIndices(self.getVideoTracks())
|
||||||
|
# audioTracks = self.sortSubIndices(self.getAudioTracks())
|
||||||
|
# subtitleTracks = self.sortSubIndices(self.getSubtitleTracks())
|
||||||
|
|
||||||
|
self.checkConfiguration()
|
||||||
|
|
||||||
|
# from self.__trackDescriptors
|
||||||
|
videoTracks = self.getVideoTracks()
|
||||||
|
audioTracks = self.getAudioTracks()
|
||||||
|
subtitleTracks = self.getSubtitleTracks()
|
||||||
|
|
||||||
|
defaultVideoTracks = [v for v in videoTracks if v.getDispositionFlag(TrackDisposition.DEFAULT)]
|
||||||
|
defaultAudioTracks = [a for a in audioTracks if a.getDispositionFlag(TrackDisposition.DEFAULT)]
|
||||||
|
defaultSubtitleTracks = [s for s in subtitleTracks if s.getDispositionFlag(TrackDisposition.DEFAULT)]
|
||||||
|
|
||||||
|
if defaultVideoTracks:
|
||||||
|
videoTracks.append(videoTracks.pop(videoTracks.index(defaultVideoTracks[0])))
|
||||||
|
self.sortSubIndices(videoTracks)
|
||||||
|
if defaultAudioTracks:
|
||||||
|
audioTracks.append(audioTracks.pop(audioTracks.index(defaultAudioTracks[0])))
|
||||||
|
self.sortSubIndices(audioTracks)
|
||||||
|
if defaultSubtitleTracks:
|
||||||
|
subtitleTracks.append(subtitleTracks.pop(subtitleTracks.index(defaultSubtitleTracks[0])))
|
||||||
|
self.sortSubIndices(subtitleTracks)
|
||||||
|
|
||||||
|
self.__trackDescriptors = videoTracks + audioTracks + subtitleTracks
|
||||||
|
self.sortIndices(self.__trackDescriptors)
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def fromFfprobe(cls, formatData, streamData):
|
def fromFfprobe(cls, formatData, streamData):
|
||||||
@@ -253,43 +183,46 @@ class MediaDescriptor:
|
|||||||
def getTags(self):
|
def getTags(self):
|
||||||
return self.__mediaTags
|
return self.__mediaTags
|
||||||
|
|
||||||
|
|
||||||
def sortSubIndices(
|
def sortSubIndices(
|
||||||
self, descriptors: List[TrackDescriptor]
|
self, descriptors: List[TrackDescriptor]
|
||||||
) -> List[TrackDescriptor]:
|
) -> List[TrackDescriptor]:
|
||||||
subIndex = 0
|
subIndex = 0
|
||||||
for t in descriptors:
|
for d in descriptors:
|
||||||
t.setSubIndex(subIndex)
|
d.setSubIndex(subIndex)
|
||||||
subIndex += 1
|
subIndex += 1
|
||||||
return descriptors
|
return descriptors
|
||||||
|
|
||||||
|
def sortIndices(
|
||||||
|
self, descriptors: List[TrackDescriptor]
|
||||||
|
) -> List[TrackDescriptor]:
|
||||||
|
index = 0
|
||||||
|
for d in descriptors:
|
||||||
|
d.setIndex(index)
|
||||||
|
index += 1
|
||||||
|
return descriptors
|
||||||
|
|
||||||
|
|
||||||
def getAllTrackDescriptors(self) -> List[TrackDescriptor]:
|
def getAllTrackDescriptors(self) -> List[TrackDescriptor]:
|
||||||
return self.getVideoTracks() + self.getAudioTracks() + self.getSubtitleTracks()
|
return self.getVideoTracks() + self.getAudioTracks() + self.getSubtitleTracks()
|
||||||
|
|
||||||
def getVideoTracks(self) -> List[TrackDescriptor]:
|
def getVideoTracks(self) -> List[TrackDescriptor]:
|
||||||
return [
|
return [
|
||||||
v for v in self.__trackDescriptors.copy() if v.getType() == TrackType.VIDEO
|
v for v in self.__trackDescriptors if v.getType() == TrackType.VIDEO
|
||||||
]
|
]
|
||||||
|
|
||||||
def getAudioTracks(self) -> List[TrackDescriptor]:
|
def getAudioTracks(self) -> List[TrackDescriptor]:
|
||||||
return [
|
return [
|
||||||
a for a in self.__trackDescriptors.copy() if a.getType() == TrackType.AUDIO
|
a for a in self.__trackDescriptors if a.getType() == TrackType.AUDIO
|
||||||
]
|
]
|
||||||
|
|
||||||
def getSubtitleTracks(self) -> List[TrackDescriptor]:
|
def getSubtitleTracks(self) -> List[TrackDescriptor]:
|
||||||
return [
|
return [
|
||||||
s
|
s
|
||||||
for s in self.__trackDescriptors.copy()
|
for s in self.__trackDescriptors
|
||||||
if s.getType() == TrackType.SUBTITLE
|
if s.getType() == TrackType.SUBTITLE
|
||||||
]
|
]
|
||||||
|
|
||||||
def getJellyfin(self):
|
|
||||||
return self.__jellyfinOrder
|
|
||||||
|
|
||||||
def setJellyfinOrder(self, state):
|
|
||||||
self.__jellyfinOrder = state
|
|
||||||
|
|
||||||
def getClearTags(self):
|
|
||||||
return self.__clearTags
|
|
||||||
|
|
||||||
def compare(self, vsMediaDescriptor: Self):
|
def compare(self, vsMediaDescriptor: Self):
|
||||||
|
|
||||||
@@ -301,6 +234,10 @@ class MediaDescriptor:
|
|||||||
vsTags = vsMediaDescriptor.getTags()
|
vsTags = vsMediaDescriptor.getTags()
|
||||||
tags = self.getTags()
|
tags = self.getTags()
|
||||||
|
|
||||||
|
# tags ist leer
|
||||||
|
# click.echo(f"tags={tags} vsTags={vsTags}")
|
||||||
|
# raise click.Abort
|
||||||
|
|
||||||
# HINT: Some tags differ per file, for example creation_time, so these are removed before diff
|
# HINT: Some tags differ per file, for example creation_time, so these are removed before diff
|
||||||
for emt in MediaDescriptor.EXCLUDED_MEDIA_TAGS:
|
for emt in MediaDescriptor.EXCLUDED_MEDIA_TAGS:
|
||||||
if emt in tags.keys():
|
if emt in tags.keys():
|
||||||
@@ -317,7 +254,7 @@ class MediaDescriptor:
|
|||||||
|
|
||||||
# Target track configuration (from DB)
|
# Target track configuration (from DB)
|
||||||
# tracks = self.getAllTrackDescriptors()
|
# tracks = self.getAllTrackDescriptors()
|
||||||
tracks = self.getReorderedTrackDescriptors()
|
tracks = self.getAllTrackDescriptors() # filtern
|
||||||
numTracks = len(tracks)
|
numTracks = len(tracks)
|
||||||
|
|
||||||
# Current track configuration (of file)
|
# Current track configuration (of file)
|
||||||
@@ -371,14 +308,15 @@ class MediaDescriptor:
|
|||||||
|
|
||||||
def getImportFileTokens(self, use_sub_index: bool = True):
|
def getImportFileTokens(self, use_sub_index: bool = True):
|
||||||
|
|
||||||
reorderedTrackDescriptors = self.getReorderedTrackDescriptors()
|
# reorderedTrackDescriptors = self.getReorderedTrackDescriptors()
|
||||||
importFileTokens = []
|
importFileTokens = []
|
||||||
|
|
||||||
for rtd in reorderedTrackDescriptors:
|
#for rtd in reorderedTrackDescriptors:
|
||||||
|
for td in self.__trackDescriptors:
|
||||||
|
|
||||||
importedFilePath = rtd.getExternalSourceFilePath()
|
importedFilePath = td.getExternalSourceFilePath()
|
||||||
|
|
||||||
if not importedFilePath is None:
|
if importedFilePath:
|
||||||
importFileTokens += [
|
importFileTokens += [
|
||||||
"-i",
|
"-i",
|
||||||
importedFilePath,
|
importedFilePath,
|
||||||
@@ -386,39 +324,51 @@ class MediaDescriptor:
|
|||||||
|
|
||||||
return importFileTokens
|
return importFileTokens
|
||||||
|
|
||||||
def getInputMappingTokens(self, use_sub_index: bool = True):
|
|
||||||
|
|
||||||
reorderedTrackDescriptors = self.getReorderedTrackDescriptors()
|
def getInputMappingTokens(self, use_sub_index: bool = True, only_video: bool = False):
|
||||||
|
|
||||||
|
# reorderedTrackDescriptors = self.getReorderedTrackDescriptors()
|
||||||
inputMappingTokens = []
|
inputMappingTokens = []
|
||||||
|
|
||||||
filePointer = 1
|
filePointer = 1
|
||||||
for rtd in reorderedTrackDescriptors:
|
#for rtd in reorderedTrackDescriptors:
|
||||||
|
for td in self.__trackDescriptors:
|
||||||
|
|
||||||
importedFilePath = rtd.getExternalSourceFilePath()
|
trackType = td.getType()
|
||||||
|
|
||||||
|
if (trackType == TrackType.VIDEO or not only_video):
|
||||||
|
|
||||||
|
importedFilePath = td.getExternalSourceFilePath()
|
||||||
|
|
||||||
trackType = rtd.getType()
|
|
||||||
if use_sub_index:
|
if use_sub_index:
|
||||||
if importedFilePath is None:
|
|
||||||
inputMappingTokens += [
|
if importedFilePath:
|
||||||
"-map",
|
|
||||||
f"0:{trackType.indicator()}:{rtd.getSubIndex()}",
|
|
||||||
]
|
|
||||||
else:
|
|
||||||
inputMappingTokens += [
|
inputMappingTokens += [
|
||||||
"-map",
|
"-map",
|
||||||
f"{filePointer}:{trackType.indicator()}:0",
|
f"{filePointer}:{trackType.indicator()}:0",
|
||||||
]
|
]
|
||||||
filePointer += 1
|
filePointer += 1
|
||||||
|
|
||||||
else:
|
else:
|
||||||
inputMappingTokens += ["-map", f"0:{rtd.getIndex()}"]
|
|
||||||
|
if td.getCodec() != TrackDescriptor.CODEC_PGS:
|
||||||
|
inputMappingTokens += [
|
||||||
|
"-map",
|
||||||
|
f"0:{trackType.indicator()}:{td.getSubIndex()}",
|
||||||
|
]
|
||||||
|
|
||||||
|
else:
|
||||||
|
if td.getCodec() != TrackDescriptor.CODEC_PGS:
|
||||||
|
inputMappingTokens += ["-map", f"0:{td.getIndex()}"]
|
||||||
|
|
||||||
return inputMappingTokens
|
return inputMappingTokens
|
||||||
|
|
||||||
def searchSubtitleFiles(searchDirectory, prefix):
|
def searchSubtitleFiles(self, searchDirectory, prefix):
|
||||||
|
|
||||||
sesl_match = re.compile(MediaDescriptor.SEASON_EPISODE_STREAM_LANGUAGE_MATCH)
|
sesl_match = re.compile(MediaDescriptor.SEASON_EPISODE_STREAM_LANGUAGE_MATCH)
|
||||||
|
|
||||||
availableFileSubtitleDescriptors = []
|
subtitleFileDescriptors = []
|
||||||
for subtitleFilename in os.listdir(searchDirectory):
|
for subtitleFilename in os.listdir(searchDirectory):
|
||||||
if subtitleFilename.startswith(prefix) and subtitleFilename.endswith(
|
if subtitleFilename.startswith(prefix) and subtitleFilename.endswith(
|
||||||
"." + MediaDescriptor.SUBTITLE_FILE_EXTENSION
|
"." + MediaDescriptor.SUBTITLE_FILE_EXTENSION
|
||||||
@@ -432,48 +382,48 @@ class MediaDescriptor:
|
|||||||
subtitleFileDescriptor["path"] = subtitleFilePath
|
subtitleFileDescriptor["path"] = subtitleFilePath
|
||||||
subtitleFileDescriptor["season"] = int(sesl_result.group(1))
|
subtitleFileDescriptor["season"] = int(sesl_result.group(1))
|
||||||
subtitleFileDescriptor["episode"] = int(sesl_result.group(2))
|
subtitleFileDescriptor["episode"] = int(sesl_result.group(2))
|
||||||
subtitleFileDescriptor["stream"] = int(sesl_result.group(3))
|
subtitleFileDescriptor["index"] = int(sesl_result.group(3))
|
||||||
subtitleFileDescriptor["language"] = sesl_result.group(4)
|
subtitleFileDescriptor["language"] = sesl_result.group(4)
|
||||||
|
|
||||||
availableFileSubtitleDescriptors.append(subtitleFileDescriptor)
|
subtitleFileDescriptors.append(subtitleFileDescriptor)
|
||||||
|
|
||||||
click.echo(
|
click.echo(f"Available subtitle files {subtitleFileDescriptors}\n")
|
||||||
f"Found {len(availableFileSubtitleDescriptors)} subtitles in files\n"
|
|
||||||
)
|
|
||||||
|
|
||||||
return availableFileSubtitleDescriptors
|
return subtitleFileDescriptors
|
||||||
|
|
||||||
def importSubtitles(
|
|
||||||
self, searchDirectory, prefix, season: int = -1, episode: int = -1
|
|
||||||
):
|
|
||||||
|
|
||||||
availableFileSubtitleDescriptors = self.searchSubtitleFiles(
|
def importSubtitles(self, searchDirectory, prefix, season: int = -1, episode: int = -1):
|
||||||
searchDirectory, prefix
|
|
||||||
)
|
click.echo(f"Season: {season} Episode: {episode}")
|
||||||
|
|
||||||
|
availableFileSubtitleDescriptors = self.searchSubtitleFiles(searchDirectory, prefix)
|
||||||
|
|
||||||
|
click.echo(f"availableFileSubtitleDescriptors: {availableFileSubtitleDescriptors}")
|
||||||
|
|
||||||
subtitleTracks = self.getSubtitleTracks()
|
subtitleTracks = self.getSubtitleTracks()
|
||||||
|
|
||||||
|
click.echo(f"subtitleTracks: {[s.getIndex() for s in subtitleTracks]}")
|
||||||
|
|
||||||
# if len(availableFileSubtitleDescriptors) != len(subtitleTracks):
|
# if len(availableFileSubtitleDescriptors) != len(subtitleTracks):
|
||||||
# raise click.ClickException(f"MediaDescriptor.importSubtitles(): Number if subtitle files not matching number of subtitle tracks")
|
# raise click.ClickException(f"MediaDescriptor.importSubtitles(): Number if subtitle files not matching number of subtitle tracks")
|
||||||
|
|
||||||
matchingFileSubtitleDescriptors = (
|
matchingSubtitleFileDescriptors = (
|
||||||
sorted(
|
sorted(
|
||||||
[
|
[
|
||||||
d
|
d
|
||||||
for d in availableFileSubtitleDescriptors
|
for d in availableFileSubtitleDescriptors
|
||||||
if d["season"] == int(season) and d["episode"] == int(episode)
|
if d["season"] == int(season) and d["episode"] == int(episode)
|
||||||
],
|
],
|
||||||
key=lambda d: d["stream"],
|
key=lambda d: d["index"],
|
||||||
)
|
)
|
||||||
if availableFileSubtitleDescriptors
|
if availableFileSubtitleDescriptors
|
||||||
else []
|
else []
|
||||||
)
|
)
|
||||||
|
|
||||||
for mfsd in matchingFileSubtitleDescriptors:
|
click.echo(f"matchingSubtitleFileDescriptors: {matchingSubtitleFileDescriptors}")
|
||||||
matchingSubtitleTrackDescriptor = [
|
|
||||||
s for s in subtitleTracks if s.getIndex() == mfsd["stream"]
|
for msfd in matchingSubtitleFileDescriptors:
|
||||||
]
|
matchingSubtitleTrackDescriptor = [s for s in subtitleTracks if s.getIndex() == msfd["index"]]
|
||||||
if matchingSubtitleTrackDescriptor:
|
if matchingSubtitleTrackDescriptor:
|
||||||
matchingSubtitleTrackDescriptor[0].setExternalSourceFilePath(
|
click.echo(f"Found matching subtitle file {msfd["path"]}\n")
|
||||||
mfsd["path"]
|
matchingSubtitleTrackDescriptor[0].setExternalSourceFilePath(msfd["path"])
|
||||||
)
|
|
||||||
|
|||||||
@@ -156,6 +156,7 @@ class MediaDetailsScreen(Screen):
|
|||||||
#HINT: This is None if the filename did not match anything in database
|
#HINT: This is None if the filename did not match anything in database
|
||||||
self.__currentPattern = self.__mediaFileProperties.getPattern()
|
self.__currentPattern = self.__mediaFileProperties.getPattern()
|
||||||
|
|
||||||
|
# keine tags vorhanden
|
||||||
self.__targetMediaDescriptor = self.__currentPattern.getMediaDescriptor() if self.__currentPattern is not None else None
|
self.__targetMediaDescriptor = self.__currentPattern.getMediaDescriptor() if self.__currentPattern is not None else None
|
||||||
|
|
||||||
# Enumerating differences between media descriptors
|
# Enumerating differences between media descriptors
|
||||||
@@ -392,8 +393,8 @@ class MediaDetailsScreen(Screen):
|
|||||||
|
|
||||||
# 7
|
# 7
|
||||||
yield Static(" ")
|
yield Static(" ")
|
||||||
yield Button("Select Default", id="select_default_button")
|
yield Button("Set Default", id="select_default_button")
|
||||||
yield Button("Select Forced", id="select_forced_button")
|
yield Button("Set Forced", id="select_forced_button")
|
||||||
yield Static(" ")
|
yield Static(" ")
|
||||||
# 8
|
# 8
|
||||||
yield Static("Streams")
|
yield Static("Streams")
|
||||||
@@ -403,8 +404,15 @@ class MediaDetailsScreen(Screen):
|
|||||||
yield Footer()
|
yield Footer()
|
||||||
|
|
||||||
|
|
||||||
def getPatternFromInput(self):
|
def getPatternDescriptorFromInput(self):
|
||||||
return str(self.query_one("#pattern_input", Input).value)
|
"""Returns show id and pattern from corresponding inputs"""
|
||||||
|
patternDescriptor = {}
|
||||||
|
try:
|
||||||
|
patternDescriptor['show_id'] = self.getSelectedShowDescriptor().getId()
|
||||||
|
patternDescriptor['pattern'] = str(self.query_one("#pattern_input", Input).value)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
return patternDescriptor
|
||||||
|
|
||||||
|
|
||||||
def on_button_pressed(self, event: Button.Pressed) -> None:
|
def on_button_pressed(self, event: Button.Pressed) -> None:
|
||||||
@@ -492,19 +500,25 @@ class MediaDetailsScreen(Screen):
|
|||||||
if showRowIndex is not None:
|
if showRowIndex is not None:
|
||||||
self.showsTable.move_cursor(row=showRowIndex)
|
self.showsTable.move_cursor(row=showRowIndex)
|
||||||
|
|
||||||
patternDescriptor = {}
|
patternDescriptor = self.getPatternDescriptorFromInput()
|
||||||
patternDescriptor['show_id'] = showDescriptor.getId()
|
|
||||||
patternDescriptor['pattern'] = self.getPatternFromInput()
|
if patternDescriptor:
|
||||||
self.__pc.addPattern(patternDescriptor)
|
patternId = self.__pc.addPattern(patternDescriptor)
|
||||||
|
|
||||||
self.highlightPattern(False)
|
self.highlightPattern(False)
|
||||||
|
|
||||||
self.action_update_pattern()
|
for tagKey, tagValue in self.__currentMediaDescriptor.getTags().items():
|
||||||
|
self.__tac.updateMediaTag(patternId, tagKey, tagValue)
|
||||||
|
|
||||||
|
for trackDescriptor in self.__currentMediaDescriptor.getAllTrackDescriptors():
|
||||||
|
self.__tc.addTrack(trackDescriptor, patternId = patternId)
|
||||||
|
|
||||||
|
|
||||||
def action_new_pattern(self):
|
def action_new_pattern(self):
|
||||||
|
|
||||||
if not self.__currentMediaDescriptor.checkDefaultAndForcedDispositions():
|
try:
|
||||||
|
self.__currentMediaDescriptor.checkConfiguration()
|
||||||
|
except ValueError:
|
||||||
return
|
return
|
||||||
|
|
||||||
selectedShowDescriptor = self.getSelectedShowDescriptor()
|
selectedShowDescriptor = self.getSelectedShowDescriptor()
|
||||||
@@ -521,13 +535,10 @@ class MediaDetailsScreen(Screen):
|
|||||||
"""When updating the database the actions must reverse the difference (eq to diff db->file)"""
|
"""When updating the database the actions must reverse the difference (eq to diff db->file)"""
|
||||||
|
|
||||||
if self.__currentPattern is not None:
|
if self.__currentPattern is not None:
|
||||||
inputPattern = self.getPatternFromInput()
|
patternDescriptor = self.getPatternDescriptorFromInput()
|
||||||
if self.__currentPattern.getPattern() != inputPattern:
|
if (patternDescriptor
|
||||||
patternDescriptor = {}
|
and self.__currentPattern.getPattern() != patternDescriptor['pattern']):
|
||||||
patternDescriptor['show_id'] = self.getSelectedShowDescriptor().getId()
|
return self.__pc.updatePattern(self.__currentPattern.getId(), patternDescriptor)
|
||||||
patternDescriptor['pattern'] = inputPattern
|
|
||||||
self.__pc.updatePattern(self.__currentPattern.getId(), patternDescriptor)
|
|
||||||
|
|
||||||
|
|
||||||
self.loadProperties()
|
self.loadProperties()
|
||||||
|
|
||||||
@@ -614,7 +625,7 @@ class MediaDetailsScreen(Screen):
|
|||||||
def action_edit_pattern(self):
|
def action_edit_pattern(self):
|
||||||
|
|
||||||
patternDescriptor = {}
|
patternDescriptor = {}
|
||||||
patternDescriptor['show_id'] = self.getSelectedShow()['id']
|
patternDescriptor['show_id'] = self.getSelectedShowDescriptor().getId()
|
||||||
patternDescriptor['pattern'] = self.getPatternFromInput()
|
patternDescriptor['pattern'] = self.getPatternFromInput()
|
||||||
|
|
||||||
if patternDescriptor['pattern']:
|
if patternDescriptor['pattern']:
|
||||||
@@ -624,7 +635,7 @@ class MediaDetailsScreen(Screen):
|
|||||||
if selectedPatternId is None:
|
if selectedPatternId is None:
|
||||||
raise click.ClickException(f"MediaDetailsScreen.action_edit_pattern(): Pattern to edit has no id")
|
raise click.ClickException(f"MediaDetailsScreen.action_edit_pattern(): Pattern to edit has no id")
|
||||||
|
|
||||||
self.app.push_screen(PatternDetailsScreen(patternId = selectedPatternId, showId = self.getSelectedShow()['id']), self.handle_edit_pattern) # <-
|
self.app.push_screen(PatternDetailsScreen(patternId = selectedPatternId, showId = self.getSelectedShowDescriptor().getId()), self.handle_edit_pattern) # <-
|
||||||
|
|
||||||
|
|
||||||
def handle_edit_pattern(self, screenResult):
|
def handle_edit_pattern(self, screenResult):
|
||||||
|
|||||||
@@ -62,7 +62,13 @@ class Pattern(Base):
|
|||||||
|
|
||||||
kwargs[MediaDescriptor.TRACK_DESCRIPTOR_LIST_KEY] = []
|
kwargs[MediaDescriptor.TRACK_DESCRIPTOR_LIST_KEY] = []
|
||||||
|
|
||||||
|
# Set ordered subindices
|
||||||
|
subIndexCounter = {}
|
||||||
for track in self.tracks:
|
for track in self.tracks:
|
||||||
kwargs[MediaDescriptor.TRACK_DESCRIPTOR_LIST_KEY].append(track.getDescriptor())
|
trackType = track.getType()
|
||||||
|
if not trackType in subIndexCounter.keys():
|
||||||
|
subIndexCounter[trackType] = 0
|
||||||
|
kwargs[MediaDescriptor.TRACK_DESCRIPTOR_LIST_KEY].append(track.getDescriptor(subIndex = subIndexCounter[trackType]))
|
||||||
|
subIndexCounter[trackType] += 1
|
||||||
|
|
||||||
return MediaDescriptor(**kwargs)
|
return MediaDescriptor(**kwargs)
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ class Track(Base):
|
|||||||
|
|
||||||
disposition_flags = Column(Integer)
|
disposition_flags = Column(Integer)
|
||||||
|
|
||||||
|
codec_name = Column(String)
|
||||||
audio_layout = Column(Integer)
|
audio_layout = Column(Integer)
|
||||||
|
|
||||||
|
|
||||||
@@ -133,6 +134,7 @@ class Track(Base):
|
|||||||
|
|
||||||
return cls(pattern_id = patternId,
|
return cls(pattern_id = patternId,
|
||||||
track_type = trackType,
|
track_type = trackType,
|
||||||
|
codec_name = streamObj[TrackDescriptor.FFPROBE_CODEC_NAME_KEY],
|
||||||
disposition_flags = sum([2**t.index() for (k,v) in streamObj[TrackDescriptor.FFPROBE_DISPOSITION_KEY].items()
|
disposition_flags = sum([2**t.index() for (k,v) in streamObj[TrackDescriptor.FFPROBE_DISPOSITION_KEY].items()
|
||||||
if v and (t := TrackDisposition.find(k)) is not None]),
|
if v and (t := TrackDisposition.find(k)) is not None]),
|
||||||
audio_layout = AudioLayout.identify(streamObj))
|
audio_layout = AudioLayout.identify(streamObj))
|
||||||
@@ -150,6 +152,9 @@ class Track(Base):
|
|||||||
def getType(self):
|
def getType(self):
|
||||||
return TrackType.fromIndex(self.track_type)
|
return TrackType.fromIndex(self.track_type)
|
||||||
|
|
||||||
|
def getCodec(self):
|
||||||
|
return str(self.codec_name)
|
||||||
|
|
||||||
def getIndex(self):
|
def getIndex(self):
|
||||||
return int(self.index) if self.index is not None else -1
|
return int(self.index) if self.index is not None else -1
|
||||||
|
|
||||||
@@ -198,6 +203,8 @@ class Track(Base):
|
|||||||
kwargs[TrackDescriptor.SUB_INDEX_KEY] = subIndex
|
kwargs[TrackDescriptor.SUB_INDEX_KEY] = subIndex
|
||||||
|
|
||||||
kwargs[TrackDescriptor.TRACK_TYPE_KEY] = self.getType()
|
kwargs[TrackDescriptor.TRACK_TYPE_KEY] = self.getType()
|
||||||
|
kwargs[TrackDescriptor.CODEC_NAME_KEY] = self.getCodec()
|
||||||
|
|
||||||
kwargs[TrackDescriptor.DISPOSITION_SET_KEY] = self.getDispositionSet()
|
kwargs[TrackDescriptor.DISPOSITION_SET_KEY] = self.getDispositionSet()
|
||||||
kwargs[TrackDescriptor.TAGS_KEY] = self.getTags()
|
kwargs[TrackDescriptor.TAGS_KEY] = self.getTags()
|
||||||
|
|
||||||
|
|||||||
@@ -16,14 +16,14 @@ class PatternController():
|
|||||||
try:
|
try:
|
||||||
|
|
||||||
s = self.Session()
|
s = self.Session()
|
||||||
q = s.query(Pattern).filter(Pattern.show_id == int(patternDescriptor['show_id']), Pattern.pattern == str(patternDescriptor['pattern']))
|
q = s.query(Pattern).filter(Pattern.show_id == int(patternDescriptor['show_id']))
|
||||||
|
|
||||||
if not q.count():
|
if not q.count():
|
||||||
pattern = Pattern(show_id = int(patternDescriptor['show_id']),
|
pattern = Pattern(show_id = int(patternDescriptor['show_id']),
|
||||||
pattern = str(patternDescriptor['pattern']))
|
pattern = str(patternDescriptor['pattern']))
|
||||||
s.add(pattern)
|
s.add(pattern)
|
||||||
s.commit()
|
s.commit()
|
||||||
return int(pattern.getId())
|
return pattern.getId()
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
import subprocess
|
import subprocess
|
||||||
|
from typing import List
|
||||||
|
|
||||||
def executeProcess(commandSequence):
|
def executeProcess(commandSequence: List[str]):
|
||||||
process = subprocess.Popen(commandSequence, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
# process = subprocess.Popen([t.encode('utf-8') for t in commandSequence], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||||
|
process = subprocess.Popen(commandSequence, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='utf-8')
|
||||||
output, error = process.communicate()
|
output, error = process.communicate()
|
||||||
return output.decode('utf-8'), error.decode('utf-8'), process.returncode
|
# return output.decode('utf-8'), error.decode('utf-8'), process.returncode
|
||||||
|
return output, error, process.returncode
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import os, click, requests
|
import os, click, requests, json
|
||||||
|
|
||||||
|
|
||||||
class TmdbController():
|
class TmdbController():
|
||||||
@@ -57,6 +57,17 @@ class TmdbController():
|
|||||||
|
|
||||||
#TODO Check for result
|
#TODO Check for result
|
||||||
try:
|
try:
|
||||||
|
#TODO: Content Type aware processing
|
||||||
|
# response = requests.get(tmdbUrl)
|
||||||
|
# response.encoding = 'utf-8'
|
||||||
|
# return response.json()
|
||||||
|
# response = requests.get(tmdbUrl)
|
||||||
|
|
||||||
|
# contentType = response.headers.get('Content-Type')
|
||||||
|
# print(content_type) # Example: 'application/json; charset=UTF-8'
|
||||||
|
|
||||||
|
# decoded_content = response.content.decode('utf-8')
|
||||||
|
# return json.loads(decoded_content)
|
||||||
return requests.get(tmdbUrl).json()
|
return requests.get(tmdbUrl).json()
|
||||||
except:
|
except:
|
||||||
return {}
|
return {}
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ class TrackController():
|
|||||||
s = self.Session()
|
s = self.Session()
|
||||||
track = Track(pattern_id = patId,
|
track = Track(pattern_id = patId,
|
||||||
track_type = int(trackDescriptor.getType().index()),
|
track_type = int(trackDescriptor.getType().index()),
|
||||||
|
codec_name = str(trackDescriptor.getCodec()),
|
||||||
index = int(trackDescriptor.getIndex()),
|
index = int(trackDescriptor.getIndex()),
|
||||||
source_index = int(trackDescriptor.getSourceIndex()),
|
source_index = int(trackDescriptor.getSourceIndex()),
|
||||||
disposition_flags = int(TrackDisposition.toFlags(trackDescriptor.getDispositionSet())),
|
disposition_flags = int(TrackDisposition.toFlags(trackDescriptor.getDispositionSet())),
|
||||||
@@ -66,6 +67,7 @@ class TrackController():
|
|||||||
track : Track = q.first()
|
track : Track = q.first()
|
||||||
|
|
||||||
track.track_type = int(trackDescriptor.getType().index())
|
track.track_type = int(trackDescriptor.getType().index())
|
||||||
|
track.codec_name = str(trackDescriptor.getCodec())
|
||||||
track.audio_layout = int(trackDescriptor.getAudioLayout().index())
|
track.audio_layout = int(trackDescriptor.getAudioLayout().index())
|
||||||
|
|
||||||
track.disposition_flags = int(TrackDisposition.toFlags(trackDescriptor.getDispositionSet()))
|
track.disposition_flags = int(TrackDisposition.toFlags(trackDescriptor.getDispositionSet()))
|
||||||
|
|||||||
@@ -21,12 +21,16 @@ class TrackDescriptor:
|
|||||||
TAGS_KEY = "tags"
|
TAGS_KEY = "tags"
|
||||||
|
|
||||||
TRACK_TYPE_KEY = "track_type"
|
TRACK_TYPE_KEY = "track_type"
|
||||||
|
CODEC_NAME_KEY = "codec_name"
|
||||||
AUDIO_LAYOUT_KEY = "audio_layout"
|
AUDIO_LAYOUT_KEY = "audio_layout"
|
||||||
|
|
||||||
FFPROBE_INDEX_KEY = "index"
|
FFPROBE_INDEX_KEY = "index"
|
||||||
FFPROBE_DISPOSITION_KEY = "disposition"
|
FFPROBE_DISPOSITION_KEY = "disposition"
|
||||||
FFPROBE_TAGS_KEY = "tags"
|
FFPROBE_TAGS_KEY = "tags"
|
||||||
FFPROBE_CODEC_TYPE_KEY = "codec_type"
|
FFPROBE_CODEC_TYPE_KEY = "codec_type"
|
||||||
|
FFPROBE_CODEC_NAME_KEY = "codec_name"
|
||||||
|
|
||||||
|
CODEC_PGS = 'hdmv_pgs_subtitle'
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
|
|
||||||
@@ -55,7 +59,7 @@ class TrackDescriptor:
|
|||||||
)
|
)
|
||||||
self.__externalSourceFilePath = kwargs[TrackDescriptor.EXTERNAL_SOURCE_FILE_PATH_KEY]
|
self.__externalSourceFilePath = kwargs[TrackDescriptor.EXTERNAL_SOURCE_FILE_PATH_KEY]
|
||||||
else:
|
else:
|
||||||
self.__externalSourceFilePath = None
|
self.__externalSourceFilePath = ''
|
||||||
|
|
||||||
if TrackDescriptor.INDEX_KEY in kwargs.keys():
|
if TrackDescriptor.INDEX_KEY in kwargs.keys():
|
||||||
if type(kwargs[TrackDescriptor.INDEX_KEY]) is not int:
|
if type(kwargs[TrackDescriptor.INDEX_KEY]) is not int:
|
||||||
@@ -92,6 +96,15 @@ class TrackDescriptor:
|
|||||||
else:
|
else:
|
||||||
self.__trackType = TrackType.UNKNOWN
|
self.__trackType = TrackType.UNKNOWN
|
||||||
|
|
||||||
|
if TrackDescriptor.CODEC_NAME_KEY in kwargs.keys():
|
||||||
|
if type(kwargs[TrackDescriptor.CODEC_NAME_KEY]) is not str:
|
||||||
|
raise TypeError(
|
||||||
|
f"TrackDesciptor.__init__(): Argument {TrackDescriptor.CODEC_NAME_KEY} is required to be of type str"
|
||||||
|
)
|
||||||
|
self.__codecName = kwargs[TrackDescriptor.CODEC_NAME_KEY]
|
||||||
|
else:
|
||||||
|
self.__codecName = ''
|
||||||
|
|
||||||
if TrackDescriptor.TAGS_KEY in kwargs.keys():
|
if TrackDescriptor.TAGS_KEY in kwargs.keys():
|
||||||
if type(kwargs[TrackDescriptor.TAGS_KEY]) is not dict:
|
if type(kwargs[TrackDescriptor.TAGS_KEY]) is not dict:
|
||||||
raise TypeError(
|
raise TypeError(
|
||||||
@@ -187,6 +200,8 @@ class TrackDescriptor:
|
|||||||
kwargs[TrackDescriptor.SUB_INDEX_KEY] = subIndex
|
kwargs[TrackDescriptor.SUB_INDEX_KEY] = subIndex
|
||||||
|
|
||||||
kwargs[TrackDescriptor.TRACK_TYPE_KEY] = trackType
|
kwargs[TrackDescriptor.TRACK_TYPE_KEY] = trackType
|
||||||
|
kwargs[TrackDescriptor.CODEC_NAME_KEY] = str(streamObj[TrackDescriptor.FFPROBE_CODEC_NAME_KEY])
|
||||||
|
|
||||||
kwargs[TrackDescriptor.DISPOSITION_SET_KEY] = (
|
kwargs[TrackDescriptor.DISPOSITION_SET_KEY] = (
|
||||||
{
|
{
|
||||||
t
|
t
|
||||||
@@ -226,6 +241,9 @@ class TrackDescriptor:
|
|||||||
def getIndex(self):
|
def getIndex(self):
|
||||||
return self.__index
|
return self.__index
|
||||||
|
|
||||||
|
def setIndex(self, index):
|
||||||
|
self.__index = index
|
||||||
|
|
||||||
def getSourceIndex(self):
|
def getSourceIndex(self):
|
||||||
return self.__sourceIndex
|
return self.__sourceIndex
|
||||||
|
|
||||||
@@ -238,6 +256,9 @@ class TrackDescriptor:
|
|||||||
def getType(self):
|
def getType(self):
|
||||||
return self.__trackType
|
return self.__trackType
|
||||||
|
|
||||||
|
def getCodec(self):
|
||||||
|
return self.__codecName
|
||||||
|
|
||||||
def getLanguage(self):
|
def getLanguage(self):
|
||||||
if "language" in self.__trackTags.keys():
|
if "language" in self.__trackTags.keys():
|
||||||
return IsoLanguage.findThreeLetter(self.__trackTags["language"])
|
return IsoLanguage.findThreeLetter(self.__trackTags["language"])
|
||||||
|
|||||||
Reference in New Issue
Block a user