merging tmdb und ffx complete crudest
This commit is contained in:
@@ -228,8 +228,7 @@ def convert(ctx,
|
|||||||
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_lenght'] = 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']}")
|
|
||||||
|
|
||||||
|
|
||||||
# ## Conversion parameters
|
# ## Conversion parameters
|
||||||
@@ -304,6 +303,9 @@ def convert(ctx,
|
|||||||
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']:
|
||||||
|
sourceMediaDescriptor.importSubtitles(context['subtitle_directory'], context['subtitle_prefix'])
|
||||||
|
|
||||||
fc = FfxController(context, sourceMediaDescriptor)
|
fc = FfxController(context, sourceMediaDescriptor)
|
||||||
|
|
||||||
# mappingTokens = fc.generateMetadataTokens()
|
# mappingTokens = fc.generateMetadataTokens()
|
||||||
@@ -315,9 +317,6 @@ def convert(ctx,
|
|||||||
audioTokens = fc.generateAudioEncodingTokens()
|
audioTokens = fc.generateAudioEncodingTokens()
|
||||||
click.echo(f"Audio Tokens: {audioTokens}")
|
click.echo(f"Audio Tokens: {audioTokens}")
|
||||||
|
|
||||||
audioTokens = fc
|
|
||||||
click.echo(f"Audio Tokens: {audioTokens}")
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
|
||||||
# Case pattern matching
|
# Case pattern matching
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ from ffx.audio_layout import AudioLayout
|
|||||||
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.process import executeProcess
|
from ffx.process import executeProcess
|
||||||
from ffx.file_properties import FileProperties
|
|
||||||
|
|
||||||
class FfxController():
|
class FfxController():
|
||||||
|
|
||||||
@@ -360,7 +360,7 @@ class FfxController():
|
|||||||
if videoEncoder == VideoEncoder.AV1:
|
if videoEncoder == VideoEncoder.AV1:
|
||||||
|
|
||||||
commandSequence = (commandTokens
|
commandSequence = (commandTokens
|
||||||
#+ subtitleImportFileTokens
|
+ self.__targetMediaDescriptor.getImportFileTokens()
|
||||||
+ self.__targetMediaDescriptor.getInputMappingTokens()
|
+ self.__targetMediaDescriptor.getInputMappingTokens()
|
||||||
+ self.generateDispositionTokens())
|
+ self.generateDispositionTokens())
|
||||||
|
|
||||||
@@ -404,7 +404,7 @@ class FfxController():
|
|||||||
executeProcess(commandSequence1)
|
executeProcess(commandSequence1)
|
||||||
|
|
||||||
commandSequence2 = (commandTokens
|
commandSequence2 = (commandTokens
|
||||||
#+ subtitleImportFileTokens
|
+ self.__targetMediaDescriptor.getImportFileTokens()
|
||||||
+ self.__targetMediaDescriptor.getInputMappingTokens()
|
+ self.__targetMediaDescriptor.getInputMappingTokens()
|
||||||
+ self.generateDispositionTokens())
|
+ self.generateDispositionTokens())
|
||||||
|
|
||||||
|
|||||||
@@ -17,9 +17,6 @@ class FileProperties():
|
|||||||
SEASON_EPISODE_INDICATOR_MATCH = '[sS]([0-9]+)[eE]([0-9]+)'
|
SEASON_EPISODE_INDICATOR_MATCH = '[sS]([0-9]+)[eE]([0-9]+)'
|
||||||
EPISODE_INDICATOR_MATCH = '[eE]([0-9]+)'
|
EPISODE_INDICATOR_MATCH = '[eE]([0-9]+)'
|
||||||
|
|
||||||
SEASON_EPISODE_STREAM_LANGUAGE_MATCH = '[sS]([0-9]+)[eE]([0-9]+)_([0-9]+)_([a-z]{3})'
|
|
||||||
SUBTITLE_FILE_EXTENSION = 'vtt'
|
|
||||||
|
|
||||||
DEFAULT_INDEX_DIGITS = 3
|
DEFAULT_INDEX_DIGITS = 3
|
||||||
|
|
||||||
def __init__(self, context, sourcePath):
|
def __init__(self, context, sourcePath):
|
||||||
|
|||||||
@@ -1,118 +1,174 @@
|
|||||||
import os, re, click
|
import os
|
||||||
|
import re
|
||||||
|
import click
|
||||||
|
|
||||||
from typing import List, Self
|
from typing import List, Self
|
||||||
|
|
||||||
from ffx.track_type import TrackType
|
from ffx.track_type import TrackType
|
||||||
from ffx.track_disposition import TrackDisposition
|
from ffx.track_disposition import TrackDisposition
|
||||||
from ffx.file_properties import FileProperties
|
|
||||||
|
|
||||||
from ffx.track_descriptor import TrackDescriptor
|
from ffx.track_descriptor import TrackDescriptor
|
||||||
|
|
||||||
from ffx.helper import dictDiff, DIFF_ADDED_KEY, DIFF_CHANGED_KEY, DIFF_REMOVED_KEY
|
from ffx.helper import dictDiff, DIFF_ADDED_KEY, DIFF_CHANGED_KEY, DIFF_REMOVED_KEY
|
||||||
|
|
||||||
|
|
||||||
class MediaDescriptor():
|
class MediaDescriptor:
|
||||||
"""This class represents the structural content of a media file including streams and metadata"""
|
"""This class represents the structural content of a media file including streams and metadata"""
|
||||||
|
|
||||||
CONTEXT_KEY = 'context'
|
CONTEXT_KEY = "context"
|
||||||
|
|
||||||
TAGS_KEY = 'tags'
|
TAGS_KEY = "tags"
|
||||||
TRACKS_KEY = 'tracks'
|
TRACKS_KEY = "tracks"
|
||||||
|
|
||||||
TRACK_DESCRIPTOR_LIST_KEY = 'track_descriptors'
|
TRACK_DESCRIPTOR_LIST_KEY = "track_descriptors"
|
||||||
CLEAR_TAGS_FLAG_KEY = 'clear_tags'
|
CLEAR_TAGS_FLAG_KEY = "clear_tags"
|
||||||
|
|
||||||
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"
|
||||||
|
|
||||||
JELLYFIN_ORDER_FLAG_KEY = 'jellyfin_order'
|
JELLYFIN_ORDER_FLAG_KEY = "jellyfin_order"
|
||||||
|
|
||||||
EXCLUDED_MEDIA_TAGS = [
|
EXCLUDED_MEDIA_TAGS = ["creation_time"]
|
||||||
'creation_time'
|
|
||||||
]
|
SEASON_EPISODE_STREAM_LANGUAGE_MATCH = '[sS]([0-9]+)[eE]([0-9]+)_([0-9]+)_([a-z]{3})'
|
||||||
|
SUBTITLE_FILE_EXTENSION = 'vtt'
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
|
|
||||||
if MediaDescriptor.TAGS_KEY in kwargs.keys():
|
if MediaDescriptor.TAGS_KEY in kwargs.keys():
|
||||||
if type(kwargs[MediaDescriptor.TAGS_KEY]) is not dict:
|
if type(kwargs[MediaDescriptor.TAGS_KEY]) is not dict:
|
||||||
raise TypeError(f"MediaDescriptor.__init__(): Argument {MediaDescriptor.TAGS_KEY} is required to be of type dict")
|
raise TypeError(
|
||||||
self.__mediaTags = kwargs[MediaDescriptor.TAGS_KEY]
|
f"MediaDescriptor.__init__(): Argument {
|
||||||
|
MediaDescriptor.TAGS_KEY} is required to be of type dict"
|
||||||
|
)
|
||||||
|
self.__mediaTags = kwargs[MediaDescriptor.TAGS_KEY]
|
||||||
else:
|
else:
|
||||||
self.__mediaTags = {}
|
self.__mediaTags = {}
|
||||||
|
|
||||||
if MediaDescriptor.TRACK_DESCRIPTOR_LIST_KEY in kwargs.keys():
|
if MediaDescriptor.TRACK_DESCRIPTOR_LIST_KEY in kwargs.keys():
|
||||||
if type(kwargs[MediaDescriptor.TRACK_DESCRIPTOR_LIST_KEY]) is not list: # Use List typehint for TrackDescriptor as well if it works
|
if (
|
||||||
raise TypeError(f"MediaDescriptor.__init__(): Argument {MediaDescriptor.TRACK_DESCRIPTOR_LIST_KEY} is required to be of type list")
|
type(kwargs[MediaDescriptor.TRACK_DESCRIPTOR_LIST_KEY]) is not list
|
||||||
for d in kwargs[MediaDescriptor.TRACK_DESCRIPTOR_LIST_KEY]:
|
): # Use List typehint for TrackDescriptor as well if it works
|
||||||
if type(d) is not TrackDescriptor:
|
raise TypeError(
|
||||||
raise TypeError(f"TrackDesciptor.__init__(): All elements of argument list {MediaDescriptor.TRACK_DESCRIPTOR_LIST_KEY} are required to be of type TrackDescriptor")
|
f"MediaDescriptor.__init__(): Argument {
|
||||||
self.__trackDescriptors = kwargs[MediaDescriptor.TRACK_DESCRIPTOR_LIST_KEY]
|
MediaDescriptor.TRACK_DESCRIPTOR_LIST_KEY} is required to be of type list"
|
||||||
|
)
|
||||||
|
for d in kwargs[MediaDescriptor.TRACK_DESCRIPTOR_LIST_KEY]:
|
||||||
|
if type(d) is not TrackDescriptor:
|
||||||
|
raise TypeError(
|
||||||
|
f"TrackDesciptor.__init__(): All elements of argument list {
|
||||||
|
MediaDescriptor.TRACK_DESCRIPTOR_LIST_KEY} are required to be of type TrackDescriptor"
|
||||||
|
)
|
||||||
|
self.__trackDescriptors = kwargs[MediaDescriptor.TRACK_DESCRIPTOR_LIST_KEY]
|
||||||
else:
|
else:
|
||||||
self.__trackDescriptors = []
|
self.__trackDescriptors = []
|
||||||
|
|
||||||
if MediaDescriptor.CLEAR_TAGS_FLAG_KEY in kwargs.keys():
|
if MediaDescriptor.CLEAR_TAGS_FLAG_KEY in kwargs.keys():
|
||||||
if type(kwargs[MediaDescriptor.CLEAR_TAGS_FLAG_KEY]) is not bool:
|
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")
|
raise TypeError(
|
||||||
self.__clearTags = kwargs[MediaDescriptor.CLEAR_TAGS_FLAG_KEY]
|
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:
|
else:
|
||||||
self.__clearTags = False
|
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(f"MediaDescriptor.__init__(): Argument {MediaDescriptor.JELLYFIN_ORDER_FLAG_KEY} is required to be of type bool")
|
raise TypeError(
|
||||||
self.__jellyfinOrder = kwargs[MediaDescriptor.JELLYFIN_ORDER_FLAG_KEY]
|
f"MediaDescriptor.__init__(): Argument {
|
||||||
|
MediaDescriptor.JELLYFIN_ORDER_FLAG_KEY} is required to be of type bool"
|
||||||
|
)
|
||||||
|
self.__jellyfinOrder = kwargs[MediaDescriptor.JELLYFIN_ORDER_FLAG_KEY]
|
||||||
else:
|
else:
|
||||||
self.__jellyfinOrder = False
|
self.__jellyfinOrder = False
|
||||||
|
|
||||||
|
|
||||||
def getDefaultVideoTrack(self):
|
def getDefaultVideoTrack(self):
|
||||||
videoDefaultTracks = [v for v in self.getVideoTracks() if TrackDisposition.DEFAULT in v.getDispositionSet()]
|
videoDefaultTracks = [
|
||||||
|
v
|
||||||
|
for v in self.getVideoTracks()
|
||||||
|
if TrackDisposition.DEFAULT in v.getDispositionSet()
|
||||||
|
]
|
||||||
if len(videoDefaultTracks) > 1:
|
if len(videoDefaultTracks) > 1:
|
||||||
raise ValueError('MediaDescriptor.getDefaultVideoTrack(): More than one default video track is not supported')
|
raise ValueError(
|
||||||
|
"MediaDescriptor.getDefaultVideoTrack(): More than one default video track is not supported"
|
||||||
|
)
|
||||||
return videoDefaultTracks[0] if videoDefaultTracks else None
|
return videoDefaultTracks[0] if videoDefaultTracks else None
|
||||||
|
|
||||||
def getForcedVideoTrack(self):
|
def getForcedVideoTrack(self):
|
||||||
videoForcedTracks = [v for v in self.getVideoTracks() if TrackDisposition.FORCED in v.getDispositionSet()]
|
videoForcedTracks = [
|
||||||
|
v
|
||||||
|
for v in self.getVideoTracks()
|
||||||
|
if TrackDisposition.FORCED in v.getDispositionSet()
|
||||||
|
]
|
||||||
if len(videoForcedTracks) > 1:
|
if len(videoForcedTracks) > 1:
|
||||||
raise ValueError('MediaDescriptor.getForcedVideoTrack(): More than one forced video track is not supported')
|
raise ValueError(
|
||||||
|
"MediaDescriptor.getForcedVideoTrack(): More than one forced video track is not supported"
|
||||||
|
)
|
||||||
return videoForcedTracks[0] if videoForcedTracks else None
|
return videoForcedTracks[0] if videoForcedTracks else None
|
||||||
|
|
||||||
def getDefaultAudioTrack(self):
|
def getDefaultAudioTrack(self):
|
||||||
audioDefaultTracks = [a for a in self.getAudioTracks() if TrackDisposition.DEFAULT in a.getDispositionSet()]
|
audioDefaultTracks = [
|
||||||
|
a
|
||||||
|
for a in self.getAudioTracks()
|
||||||
|
if TrackDisposition.DEFAULT in a.getDispositionSet()
|
||||||
|
]
|
||||||
if len(audioDefaultTracks) > 1:
|
if len(audioDefaultTracks) > 1:
|
||||||
raise ValueError('MediaDescriptor.getDefaultAudioTrack(): More than one default audio track is not supported')
|
raise ValueError(
|
||||||
|
"MediaDescriptor.getDefaultAudioTrack(): More than one default audio track is not supported"
|
||||||
|
)
|
||||||
return audioDefaultTracks[0] if audioDefaultTracks else None
|
return audioDefaultTracks[0] if audioDefaultTracks else None
|
||||||
|
|
||||||
def getForcedAudioTrack(self):
|
def getForcedAudioTrack(self):
|
||||||
audioForcedTracks = [a for a in self.getAudioTracks() if TrackDisposition.FORCED in a.getDispositionSet()]
|
audioForcedTracks = [
|
||||||
|
a
|
||||||
|
for a in self.getAudioTracks()
|
||||||
|
if TrackDisposition.FORCED in a.getDispositionSet()
|
||||||
|
]
|
||||||
if len(audioForcedTracks) > 1:
|
if len(audioForcedTracks) > 1:
|
||||||
raise ValueError('MediaDescriptor.getForcedAudioTrack(): More than one forced audio track is not supported')
|
raise ValueError(
|
||||||
|
"MediaDescriptor.getForcedAudioTrack(): More than one forced audio track is not supported"
|
||||||
|
)
|
||||||
return audioForcedTracks[0] if audioForcedTracks else None
|
return audioForcedTracks[0] if audioForcedTracks else None
|
||||||
|
|
||||||
def getDefaultSubtitleTrack(self):
|
def getDefaultSubtitleTrack(self):
|
||||||
subtitleDefaultTracks = [s for s in self.getSubtitleTracks() if TrackDisposition.DEFAULT in s.getDispositionSet()]
|
subtitleDefaultTracks = [
|
||||||
|
s
|
||||||
|
for s in self.getSubtitleTracks()
|
||||||
|
if TrackDisposition.DEFAULT in s.getDispositionSet()
|
||||||
|
]
|
||||||
if len(subtitleDefaultTracks) > 1:
|
if len(subtitleDefaultTracks) > 1:
|
||||||
raise ValueError('MediaDescriptor.getDefaultSubtitleTrack(): More than one default subtitle track is not supported')
|
raise ValueError(
|
||||||
|
"MediaDescriptor.getDefaultSubtitleTrack(): More than one default subtitle track is not supported"
|
||||||
|
)
|
||||||
return subtitleDefaultTracks[0] if subtitleDefaultTracks else None
|
return subtitleDefaultTracks[0] if subtitleDefaultTracks else None
|
||||||
|
|
||||||
def getForcedSubtitleTrack(self):
|
def getForcedSubtitleTrack(self):
|
||||||
subtitleForcedTracks = [s for s in self.getSubtitleTracks() if TrackDisposition.FORCED in s.getDispositionSet()]
|
subtitleForcedTracks = [
|
||||||
|
s
|
||||||
|
for s in self.getSubtitleTracks()
|
||||||
|
if TrackDisposition.FORCED in s.getDispositionSet()
|
||||||
|
]
|
||||||
if len(subtitleForcedTracks) > 1:
|
if len(subtitleForcedTracks) > 1:
|
||||||
raise ValueError('MediaDescriptor.getForcedSubtitleTrack(): More than one forced subtitle track is not supported')
|
raise ValueError(
|
||||||
|
"MediaDescriptor.getForcedSubtitleTrack(): More than one forced subtitle track is not supported"
|
||||||
|
)
|
||||||
return subtitleForcedTracks[0] if subtitleForcedTracks else None
|
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():
|
||||||
if t.getType() == trackType:
|
if t.getType() == trackType:
|
||||||
t.setDispositionFlag(TrackDisposition.DEFAULT, t.getSubIndex() == int(subIndex))
|
t.setDispositionFlag(
|
||||||
|
TrackDisposition.DEFAULT, t.getSubIndex() == int(subIndex)
|
||||||
|
)
|
||||||
|
|
||||||
def setForcedSubTrack(self, trackType : TrackType, subIndex : int):
|
def setForcedSubTrack(self, trackType: TrackType, subIndex: int):
|
||||||
for t in self.getAllTrackDescriptors():
|
for t in self.getAllTrackDescriptors():
|
||||||
if t.getType() == trackType:
|
if t.getType() == trackType:
|
||||||
t.setDispositionFlag(TrackDisposition.FORCED, t.getSubIndex() == int(subIndex))
|
t.setDispositionFlag(
|
||||||
|
TrackDisposition.FORCED, t.getSubIndex() == int(subIndex)
|
||||||
|
)
|
||||||
|
|
||||||
def getReorderedTrackDescriptors(self):
|
def getReorderedTrackDescriptors(self):
|
||||||
|
|
||||||
@@ -129,32 +185,43 @@ class MediaDescriptor():
|
|||||||
|
|
||||||
if self.__jellyfinOrder:
|
if self.__jellyfinOrder:
|
||||||
if not videoDefaultTrack is None:
|
if not videoDefaultTrack is None:
|
||||||
videoTracks.append(videoTracks.pop(videoTracks.index(videoDefaultTrack)))
|
videoTracks.append(
|
||||||
|
videoTracks.pop(videoTracks.index(videoDefaultTrack))
|
||||||
|
)
|
||||||
if not audioDefaultTrack is None:
|
if not audioDefaultTrack is None:
|
||||||
audioTracks.append(audioTracks.pop(audioTracks.index(audioDefaultTrack)))
|
audioTracks.append(
|
||||||
|
audioTracks.pop(audioTracks.index(audioDefaultTrack))
|
||||||
|
)
|
||||||
if not subtitleDefaultTrack is None:
|
if not subtitleDefaultTrack is None:
|
||||||
subtitleTracks.append(subtitleTracks.pop(subtitleTracks.index(subtitleDefaultTrack)))
|
subtitleTracks.append(
|
||||||
|
subtitleTracks.pop(subtitleTracks.index(subtitleDefaultTrack))
|
||||||
|
)
|
||||||
|
|
||||||
reorderedTrackDescriptors = videoTracks + audioTracks + subtitleTracks
|
reorderedTrackDescriptors = videoTracks + audioTracks + subtitleTracks
|
||||||
orderedSourceTrackSequence = [t.getSourceIndex() for t in reorderedTrackDescriptors]
|
orderedSourceTrackSequence = [
|
||||||
|
t.getSourceIndex() for t in reorderedTrackDescriptors
|
||||||
|
]
|
||||||
|
|
||||||
if len(set(orderedSourceTrackSequence)) < len(orderedSourceTrackSequence):
|
if len(set(orderedSourceTrackSequence)) < len(orderedSourceTrackSequence):
|
||||||
raise ValueError(f"Multiple streams originating from the same source stream not supported")
|
raise ValueError(
|
||||||
|
f"Multiple streams originating from the same source stream not supported"
|
||||||
|
)
|
||||||
|
|
||||||
return reorderedTrackDescriptors
|
return reorderedTrackDescriptors
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def fromFfprobe(cls, formatData, streamData):
|
def fromFfprobe(cls, formatData, streamData):
|
||||||
|
|
||||||
kwargs = {}
|
kwargs = {}
|
||||||
|
|
||||||
if MediaDescriptor.FFPROBE_TAGS_KEY in formatData.keys():
|
if MediaDescriptor.FFPROBE_TAGS_KEY in formatData.keys():
|
||||||
kwargs[MediaDescriptor.TAGS_KEY] = formatData[MediaDescriptor.FFPROBE_TAGS_KEY]
|
kwargs[MediaDescriptor.TAGS_KEY] = formatData[
|
||||||
|
MediaDescriptor.FFPROBE_TAGS_KEY
|
||||||
|
]
|
||||||
|
|
||||||
kwargs[MediaDescriptor.TRACK_DESCRIPTOR_LIST_KEY] = []
|
kwargs[MediaDescriptor.TRACK_DESCRIPTOR_LIST_KEY] = []
|
||||||
|
|
||||||
#TODO: Evtl obsolet
|
# TODO: Evtl obsolet
|
||||||
subIndexCounters = {}
|
subIndexCounters = {}
|
||||||
|
|
||||||
for streamObj in streamData:
|
for streamObj in streamData:
|
||||||
@@ -165,40 +232,48 @@ class MediaDescriptor():
|
|||||||
if trackType != TrackType.UNKNOWN:
|
if trackType != TrackType.UNKNOWN:
|
||||||
|
|
||||||
if trackType not in subIndexCounters.keys():
|
if trackType not in subIndexCounters.keys():
|
||||||
subIndexCounters[trackType] = 0
|
subIndexCounters[trackType] = 0
|
||||||
|
|
||||||
kwargs[MediaDescriptor.TRACK_DESCRIPTOR_LIST_KEY].append(TrackDescriptor.fromFfprobe(streamObj,
|
kwargs[MediaDescriptor.TRACK_DESCRIPTOR_LIST_KEY].append(
|
||||||
subIndex=subIndexCounters[trackType]))
|
TrackDescriptor.fromFfprobe(
|
||||||
|
streamObj, subIndex=subIndexCounters[trackType]
|
||||||
|
)
|
||||||
|
)
|
||||||
subIndexCounters[trackType] += 1
|
subIndexCounters[trackType] += 1
|
||||||
|
|
||||||
|
|
||||||
return cls(**kwargs)
|
return cls(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
def getTags(self):
|
def getTags(self):
|
||||||
return self.__mediaTags
|
return self.__mediaTags
|
||||||
|
|
||||||
|
def sortSubIndices(
|
||||||
def sortSubIndices(self, descriptors : List[TrackDescriptor]) -> List[TrackDescriptor]:
|
self, descriptors: List[TrackDescriptor]
|
||||||
|
) -> List[TrackDescriptor]:
|
||||||
subIndex = 0
|
subIndex = 0
|
||||||
for t in descriptors:
|
for t in descriptors:
|
||||||
t.setSubIndex(subIndex)
|
t.setSubIndex(subIndex)
|
||||||
subIndex += 1
|
subIndex += 1
|
||||||
return descriptors
|
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 [v for v in self.__trackDescriptors.copy() if v.getType() == TrackType.VIDEO]
|
return [
|
||||||
|
v for v in self.__trackDescriptors.copy() if v.getType() == TrackType.VIDEO
|
||||||
|
]
|
||||||
|
|
||||||
def getAudioTracks(self) -> List[TrackDescriptor]:
|
def getAudioTracks(self) -> List[TrackDescriptor]:
|
||||||
return [a for a in self.__trackDescriptors.copy() if a.getType() == TrackType.AUDIO]
|
return [
|
||||||
|
a for a in self.__trackDescriptors.copy() if a.getType() == TrackType.AUDIO
|
||||||
|
]
|
||||||
|
|
||||||
def getSubtitleTracks(self) -> List[TrackDescriptor]:
|
def getSubtitleTracks(self) -> List[TrackDescriptor]:
|
||||||
return [s for s in self.__trackDescriptors.copy() if s.getType() == TrackType.SUBTITLE]
|
return [
|
||||||
|
s
|
||||||
|
for s in self.__trackDescriptors.copy()
|
||||||
|
if s.getType() == TrackType.SUBTITLE
|
||||||
|
]
|
||||||
|
|
||||||
def getJellyfin(self):
|
def getJellyfin(self):
|
||||||
return self.__jellyfinOrder
|
return self.__jellyfinOrder
|
||||||
@@ -209,33 +284,33 @@ class MediaDescriptor():
|
|||||||
def getClearTags(self):
|
def getClearTags(self):
|
||||||
return self.__clearTags
|
return self.__clearTags
|
||||||
|
|
||||||
|
def compare(self, vsMediaDescriptor: Self):
|
||||||
|
|
||||||
def compare(self, vsMediaDescriptor : Self):
|
|
||||||
|
|
||||||
if not isinstance(vsMediaDescriptor, self.__class__):
|
if not isinstance(vsMediaDescriptor, self.__class__):
|
||||||
raise click.ClickException(f"MediaDescriptor.compare(): Argument is required to be of type {self.__class__}")
|
raise click.ClickException(
|
||||||
|
f"MediaDescriptor.compare(): Argument is required to be of type {
|
||||||
|
self.__class__}"
|
||||||
|
)
|
||||||
|
|
||||||
vsTags = vsMediaDescriptor.getTags()
|
vsTags = vsMediaDescriptor.getTags()
|
||||||
tags = self.getTags()
|
tags = self.getTags()
|
||||||
|
|
||||||
#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():
|
||||||
del tags[emt]
|
del tags[emt]
|
||||||
if emt in vsTags.keys():
|
if emt in vsTags.keys():
|
||||||
del vsTags[emt]
|
del vsTags[emt]
|
||||||
|
|
||||||
tagsDiff = dictDiff(vsTags, tags)
|
tagsDiff = dictDiff(vsTags, tags)
|
||||||
|
|
||||||
compareResult = {}
|
compareResult = {}
|
||||||
|
|
||||||
if tagsDiff:
|
if tagsDiff:
|
||||||
compareResult[MediaDescriptor.TAGS_KEY] = tagsDiff
|
compareResult[MediaDescriptor.TAGS_KEY] = tagsDiff
|
||||||
|
|
||||||
|
|
||||||
# Target track configuration (from DB)
|
# Target track configuration (from DB)
|
||||||
#tracks = self.getAllTrackDescriptors()
|
# tracks = self.getAllTrackDescriptors()
|
||||||
tracks = self.getReorderedTrackDescriptors()
|
tracks = self.getReorderedTrackDescriptors()
|
||||||
numTracks = len(tracks)
|
numTracks = len(tracks)
|
||||||
|
|
||||||
@@ -245,95 +320,154 @@ class MediaDescriptor():
|
|||||||
|
|
||||||
maxNumOfTracks = max(numVsTracks, numTracks)
|
maxNumOfTracks = max(numVsTracks, numTracks)
|
||||||
|
|
||||||
|
|
||||||
trackCompareResult = {}
|
trackCompareResult = {}
|
||||||
|
|
||||||
for tp in range(maxNumOfTracks):
|
for tp in range(maxNumOfTracks):
|
||||||
|
|
||||||
# inspect/update funktionier nur so
|
# inspect/update funktionier nur so
|
||||||
if self.__jellyfinOrder:
|
if self.__jellyfinOrder:
|
||||||
vsTrackIndex = tracks[tp].getSourceIndex()
|
vsTrackIndex = tracks[tp].getSourceIndex()
|
||||||
else:
|
else:
|
||||||
vsTrackIndex = tp
|
vsTrackIndex = tp
|
||||||
# vsTrackIndex = tracks[tp].getSourceIndex()
|
# vsTrackIndex = tracks[tp].getSourceIndex()
|
||||||
|
|
||||||
# Will trigger if tracks are missing in file
|
# Will trigger if tracks are missing in file
|
||||||
if tp > (numVsTracks - 1):
|
if tp > (numVsTracks - 1):
|
||||||
if DIFF_ADDED_KEY not in trackCompareResult.keys():
|
if DIFF_ADDED_KEY not in trackCompareResult.keys():
|
||||||
trackCompareResult[DIFF_ADDED_KEY] = set()
|
trackCompareResult[DIFF_ADDED_KEY] = set()
|
||||||
trackCompareResult[DIFF_ADDED_KEY].add(tracks[tp].getIndex())
|
trackCompareResult[DIFF_ADDED_KEY].add(tracks[tp].getIndex())
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Will trigger if tracks are missing in DB definition
|
# Will trigger if tracks are missing in DB definition
|
||||||
# New tracks will be added per update via this way
|
# New tracks will be added per update via this way
|
||||||
if tp > (numTracks - 1):
|
if tp > (numTracks - 1):
|
||||||
if DIFF_REMOVED_KEY not in trackCompareResult.keys():
|
if DIFF_REMOVED_KEY not in trackCompareResult.keys():
|
||||||
trackCompareResult[DIFF_REMOVED_KEY] = {}
|
trackCompareResult[DIFF_REMOVED_KEY] = {}
|
||||||
trackCompareResult[DIFF_REMOVED_KEY][vsTracks[vsTrackIndex].getIndex()] = vsTracks[vsTrackIndex]
|
trackCompareResult[DIFF_REMOVED_KEY][
|
||||||
continue
|
vsTracks[vsTrackIndex].getIndex()
|
||||||
|
] = vsTracks[vsTrackIndex]
|
||||||
|
continue
|
||||||
|
|
||||||
# assumption is made here that the track order will not change for all files of a sequence
|
# assumption is made here that the track order will not change for all files of a sequence
|
||||||
trackDiff = tracks[tp].compare(vsTracks[vsTrackIndex])
|
trackDiff = tracks[tp].compare(vsTracks[vsTrackIndex])
|
||||||
|
|
||||||
if trackDiff:
|
if trackDiff:
|
||||||
if DIFF_CHANGED_KEY not in trackCompareResult.keys():
|
if DIFF_CHANGED_KEY not in trackCompareResult.keys():
|
||||||
trackCompareResult[DIFF_CHANGED_KEY] = {}
|
trackCompareResult[DIFF_CHANGED_KEY] = {}
|
||||||
trackCompareResult[DIFF_CHANGED_KEY][vsTracks[vsTrackIndex].getIndex()] = trackDiff
|
trackCompareResult[DIFF_CHANGED_KEY][
|
||||||
|
vsTracks[vsTrackIndex].getIndex()
|
||||||
|
] = trackDiff
|
||||||
|
|
||||||
if trackCompareResult:
|
if trackCompareResult:
|
||||||
compareResult[MediaDescriptor.TRACKS_KEY] = trackCompareResult
|
compareResult[MediaDescriptor.TRACKS_KEY] = trackCompareResult
|
||||||
|
|
||||||
return compareResult
|
return compareResult
|
||||||
|
|
||||||
|
def getImportFileTokens(self, use_sub_index: bool = True):
|
||||||
|
|
||||||
|
reorderedTrackDescriptors = self.getReorderedTrackDescriptors()
|
||||||
|
importFileTokens = []
|
||||||
|
|
||||||
def getInputMappingTokens(self, use_sub_index : bool = True):
|
for rtd in reorderedTrackDescriptors:
|
||||||
|
|
||||||
|
importedFilePath = rtd.getExternalSourceFilePath()
|
||||||
|
|
||||||
|
if not importedFilePath is None:
|
||||||
|
importFileTokens += [
|
||||||
|
"-i",
|
||||||
|
importedFilePath,
|
||||||
|
]
|
||||||
|
|
||||||
|
return importFileTokens
|
||||||
|
|
||||||
|
def getInputMappingTokens(self, use_sub_index: bool = True):
|
||||||
|
|
||||||
reorderedTrackDescriptors = self.getReorderedTrackDescriptors()
|
reorderedTrackDescriptors = self.getReorderedTrackDescriptors()
|
||||||
inputMappingTokens = []
|
inputMappingTokens = []
|
||||||
|
|
||||||
|
filePointer = 1
|
||||||
for rtd in reorderedTrackDescriptors:
|
for rtd in reorderedTrackDescriptors:
|
||||||
trackType = rtd.getType()
|
|
||||||
if use_sub_index:
|
importedFilePath = rtd.getExternalSourceFilePath()
|
||||||
inputMappingTokens += ['-map', f"0:{trackType.indicator()}:{rtd.getSubIndex()}"]
|
|
||||||
|
trackType = rtd.getType()
|
||||||
|
if use_sub_index:
|
||||||
|
if importedFilePath is None:
|
||||||
|
inputMappingTokens += [
|
||||||
|
"-map",
|
||||||
|
f"0:{trackType.indicator()}:{rtd.getSubIndex()}",
|
||||||
|
]
|
||||||
else:
|
else:
|
||||||
inputMappingTokens += ['-map', f"0:{rtd.getIndex()}"]
|
inputMappingTokens += [
|
||||||
|
"-map",
|
||||||
|
f"{filePointer}:{trackType.indicator()}:0",
|
||||||
|
]
|
||||||
|
filePointer += 1
|
||||||
|
else:
|
||||||
|
inputMappingTokens += ["-map", f"0:{rtd.getIndex()}"]
|
||||||
|
|
||||||
return inputMappingTokens
|
return inputMappingTokens
|
||||||
|
|
||||||
|
|
||||||
# matchingFileSubtitleDescriptors = sorted([d for d in availableFileSubtitleDescriptors if d['season'] == season and d['episode'] == episode], key=lambda d: d['stream']) if availableFileSubtitleDescriptors else []
|
|
||||||
|
|
||||||
|
|
||||||
def searchSubtitleFiles(searchDirectory, prefix):
|
def searchSubtitleFiles(searchDirectory, prefix):
|
||||||
|
|
||||||
sesl_match = re.compile(FileProperties.SEASON_EPISODE_STREAM_LANGUAGE_MATCH)
|
sesl_match = re.compile(MediaDescriptor.SEASON_EPISODE_STREAM_LANGUAGE_MATCH)
|
||||||
|
|
||||||
availableFileSubtitleDescriptors = []
|
availableFileSubtitleDescriptors = []
|
||||||
for subtitleFilename in os.listdir(searchDirectory):
|
for subtitleFilename in os.listdir(searchDirectory):
|
||||||
if subtitleFilename.startswith(prefix) and subtitleFilename.endswith('.' + FileProperties.SUBTITLE_FILE_EXTENSION):
|
if subtitleFilename.startswith(prefix) and subtitleFilename.endswith(
|
||||||
|
"." + MediaDescriptor.SUBTITLE_FILE_EXTENSION
|
||||||
|
):
|
||||||
sesl_result = sesl_match.search(subtitleFilename)
|
sesl_result = sesl_match.search(subtitleFilename)
|
||||||
if sesl_result is not None:
|
if sesl_result is not None:
|
||||||
subtitleFilePath = os.path.join(searchDirectory, subtitleFilename)
|
subtitleFilePath = os.path.join(searchDirectory, subtitleFilename)
|
||||||
if os.path.isfile(subtitleFilePath):
|
if os.path.isfile(subtitleFilePath):
|
||||||
|
|
||||||
subtitleFileDescriptor = {}
|
subtitleFileDescriptor = {}
|
||||||
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["stream"] = int(sesl_result.group(3))
|
||||||
subtitleFileDescriptor['language'] = sesl_result.group(4)
|
subtitleFileDescriptor["language"] = sesl_result.group(4)
|
||||||
|
|
||||||
availableFileSubtitleDescriptors.append(subtitleFileDescriptor)
|
availableFileSubtitleDescriptors.append(subtitleFileDescriptor)
|
||||||
|
|
||||||
click.echo(f"Found {len(availableFileSubtitleDescriptors)} subtitles in files\n")
|
click.echo(
|
||||||
|
f"Found {len(availableFileSubtitleDescriptors)} subtitles in files\n"
|
||||||
|
)
|
||||||
|
|
||||||
return availableFileSubtitleDescriptors
|
return availableFileSubtitleDescriptors
|
||||||
|
|
||||||
|
def importSubtitles(
|
||||||
|
self, searchDirectory, prefix, season: int = -1, episode: int = -1
|
||||||
|
):
|
||||||
|
|
||||||
def importSubtitles(self, searchDirectory, prefix):
|
availableFileSubtitleDescriptors = self.searchSubtitleFiles(
|
||||||
|
searchDirectory, prefix
|
||||||
|
)
|
||||||
|
|
||||||
availableFileSubtitleDescriptors = self.searchSubtitleFiles(searchDirectory, prefix)
|
subtitleTracks = self.getSubtitleTracks()
|
||||||
|
|
||||||
if len(availableFileSubtitleDescriptors) != len(self.getSubtitleTracks()):
|
# 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 = (
|
||||||
|
sorted(
|
||||||
|
[
|
||||||
|
d
|
||||||
|
for d in availableFileSubtitleDescriptors
|
||||||
|
if d["season"] == int(season) and d["episode"] == int(episode)
|
||||||
|
],
|
||||||
|
key=lambda d: d["stream"],
|
||||||
|
)
|
||||||
|
if availableFileSubtitleDescriptors
|
||||||
|
else []
|
||||||
|
)
|
||||||
|
|
||||||
|
for mfsd in matchingFileSubtitleDescriptors:
|
||||||
|
matchingSubtitleTrackDescriptor = [
|
||||||
|
s for s in subtitleTracks if s.getIndex() == mfsd["stream"]
|
||||||
|
]
|
||||||
|
if matchingSubtitleTrackDescriptor:
|
||||||
|
matchingSubtitleTrackDescriptor[0].setExternalSourceFilePath(
|
||||||
|
mfsd["path"]
|
||||||
|
)
|
||||||
|
|||||||
@@ -8,227 +8,287 @@ from .track_disposition import TrackDisposition
|
|||||||
from .helper import dictDiff, setDiff
|
from .helper import dictDiff, setDiff
|
||||||
|
|
||||||
|
|
||||||
class TrackDescriptor():
|
class TrackDescriptor:
|
||||||
|
|
||||||
ID_KEY = 'id'
|
ID_KEY = "id"
|
||||||
INDEX_KEY = 'index'
|
INDEX_KEY = "index"
|
||||||
SOURCE_INDEX_KEY = 'source_index'
|
SOURCE_INDEX_KEY = "source_index"
|
||||||
SUB_INDEX_KEY = 'sub_index'
|
SUB_INDEX_KEY = "sub_index"
|
||||||
PATTERN_ID_KEY = 'pattern_id'
|
PATTERN_ID_KEY = "pattern_id"
|
||||||
|
EXTERNAL_SOURCE_FILE_PATH_KEY = "external_source_file"
|
||||||
|
|
||||||
DISPOSITION_SET_KEY = 'disposition_set'
|
DISPOSITION_SET_KEY = "disposition_set"
|
||||||
TAGS_KEY = 'tags'
|
TAGS_KEY = "tags"
|
||||||
|
|
||||||
TRACK_TYPE_KEY = 'track_type'
|
TRACK_TYPE_KEY = "track_type"
|
||||||
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"
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
|
|
||||||
if TrackDescriptor.ID_KEY in kwargs.keys():
|
if TrackDescriptor.ID_KEY in kwargs.keys():
|
||||||
if type(kwargs[TrackDescriptor.ID_KEY]) is not int:
|
if type(kwargs[TrackDescriptor.ID_KEY]) is not int:
|
||||||
raise TypeError(f"TrackDesciptor.__init__(): Argument {TrackDescriptor.ID_KEY} is required to be of type int")
|
raise TypeError(
|
||||||
self.__trackId = kwargs[TrackDescriptor.ID_KEY]
|
f"TrackDesciptor.__init__(): Argument {TrackDescriptor.ID_KEY} is required to be of type int"
|
||||||
else:
|
)
|
||||||
self.__trackId = -1
|
self.__trackId = kwargs[TrackDescriptor.ID_KEY]
|
||||||
|
else:
|
||||||
|
self.__trackId = -1
|
||||||
|
|
||||||
if TrackDescriptor.PATTERN_ID_KEY in kwargs.keys():
|
if TrackDescriptor.PATTERN_ID_KEY in kwargs.keys():
|
||||||
if type(kwargs[TrackDescriptor.PATTERN_ID_KEY]) is not int:
|
if type(kwargs[TrackDescriptor.PATTERN_ID_KEY]) is not int:
|
||||||
raise TypeError(f"TrackDesciptor.__init__(): Argument {TrackDescriptor.PATTERN_ID_KEY} is required to be of type int")
|
raise TypeError(
|
||||||
self.__patternId = kwargs[TrackDescriptor.PATTERN_ID_KEY]
|
f"TrackDesciptor.__init__(): Argument {TrackDescriptor.PATTERN_ID_KEY} is required to be of type int"
|
||||||
else:
|
)
|
||||||
self.__patternId = -1
|
self.__patternId = kwargs[TrackDescriptor.PATTERN_ID_KEY]
|
||||||
|
else:
|
||||||
|
self.__patternId = -1
|
||||||
|
|
||||||
if TrackDescriptor.INDEX_KEY in kwargs.keys():
|
if TrackDescriptor.EXTERNAL_SOURCE_FILE_PATH_KEY in kwargs.keys():
|
||||||
if type(kwargs[TrackDescriptor.INDEX_KEY]) is not int:
|
if type(kwargs[TrackDescriptor.EXTERNAL_SOURCE_FILE_PATH_KEY]) is not str:
|
||||||
raise TypeError(f"TrackDesciptor.__init__(): Argument {TrackDescriptor.INDEX_KEY} is required to be of type int")
|
raise TypeError(
|
||||||
self.__index = kwargs[TrackDescriptor.INDEX_KEY]
|
f"TrackDesciptor.__init__(): Argument {TrackDescriptor.EXTERNAL_SOURCE_FILE_PATH_KEY} is required to be of type str"
|
||||||
else:
|
)
|
||||||
self.__index = -1
|
self.__externalSourceFilePath = kwargs[TrackDescriptor.EXTERNAL_SOURCE_FILE_PATH_KEY]
|
||||||
|
else:
|
||||||
|
self.__externalSourceFilePath = None
|
||||||
|
|
||||||
if TrackDescriptor.SOURCE_INDEX_KEY in kwargs.keys() and type(kwargs[TrackDescriptor.SOURCE_INDEX_KEY]) is int:
|
if TrackDescriptor.INDEX_KEY in kwargs.keys():
|
||||||
self.__sourceIndex = kwargs[TrackDescriptor.SOURCE_INDEX_KEY]
|
if type(kwargs[TrackDescriptor.INDEX_KEY]) is not int:
|
||||||
else:
|
raise TypeError(
|
||||||
self.__sourceIndex = self.__index
|
f"TrackDesciptor.__init__(): Argument {TrackDescriptor.INDEX_KEY} is required to be of type int"
|
||||||
|
)
|
||||||
|
self.__index = kwargs[TrackDescriptor.INDEX_KEY]
|
||||||
|
else:
|
||||||
|
self.__index = -1
|
||||||
|
|
||||||
if TrackDescriptor.SUB_INDEX_KEY in kwargs.keys():
|
if (
|
||||||
if type(kwargs[TrackDescriptor.SUB_INDEX_KEY]) is not int:
|
TrackDescriptor.SOURCE_INDEX_KEY in kwargs.keys()
|
||||||
raise TypeError(f"TrackDesciptor.__init__(): Argument {TrackDescriptor.SUB_INDEX_KEY} is required to be of type int")
|
and type(kwargs[TrackDescriptor.SOURCE_INDEX_KEY]) is int
|
||||||
self.__subIndex = kwargs[TrackDescriptor.SUB_INDEX_KEY]
|
):
|
||||||
else:
|
self.__sourceIndex = kwargs[TrackDescriptor.SOURCE_INDEX_KEY]
|
||||||
self.__subIndex = -1
|
else:
|
||||||
|
self.__sourceIndex = self.__index
|
||||||
|
|
||||||
if TrackDescriptor.TRACK_TYPE_KEY in kwargs.keys():
|
if TrackDescriptor.SUB_INDEX_KEY in kwargs.keys():
|
||||||
if type(kwargs[TrackDescriptor.TRACK_TYPE_KEY]) is not TrackType:
|
if type(kwargs[TrackDescriptor.SUB_INDEX_KEY]) is not int:
|
||||||
raise TypeError(f"TrackDesciptor.__init__(): Argument {TrackDescriptor.TRACK_TYPE_KEY} is required to be of type TrackType")
|
raise TypeError(
|
||||||
self.__trackType = kwargs[TrackDescriptor.TRACK_TYPE_KEY]
|
f"TrackDesciptor.__init__(): Argument {TrackDescriptor.SUB_INDEX_KEY} is required to be of type int"
|
||||||
else:
|
)
|
||||||
self.__trackType = TrackType.UNKNOWN
|
self.__subIndex = kwargs[TrackDescriptor.SUB_INDEX_KEY]
|
||||||
|
else:
|
||||||
|
self.__subIndex = -1
|
||||||
|
|
||||||
if TrackDescriptor.TAGS_KEY in kwargs.keys():
|
if TrackDescriptor.TRACK_TYPE_KEY in kwargs.keys():
|
||||||
if type(kwargs[TrackDescriptor.TAGS_KEY]) is not dict:
|
if type(kwargs[TrackDescriptor.TRACK_TYPE_KEY]) is not TrackType:
|
||||||
raise TypeError(f"TrackDesciptor.__init__(): Argument {TrackDescriptor.TAGS_KEY} is required to be of type dict")
|
raise TypeError(
|
||||||
self.__trackTags = kwargs[TrackDescriptor.TAGS_KEY]
|
f"TrackDesciptor.__init__(): Argument {TrackDescriptor.TRACK_TYPE_KEY} is required to be of type TrackType"
|
||||||
else:
|
)
|
||||||
self.__trackTags = {}
|
self.__trackType = kwargs[TrackDescriptor.TRACK_TYPE_KEY]
|
||||||
|
else:
|
||||||
|
self.__trackType = TrackType.UNKNOWN
|
||||||
|
|
||||||
if TrackDescriptor.DISPOSITION_SET_KEY in kwargs.keys():
|
if TrackDescriptor.TAGS_KEY in kwargs.keys():
|
||||||
if type(kwargs[TrackDescriptor.DISPOSITION_SET_KEY]) is not set:
|
if type(kwargs[TrackDescriptor.TAGS_KEY]) is not dict:
|
||||||
raise TypeError(f"TrackDesciptor.__init__(): Argument {TrackDescriptor.DISPOSITION_SET_KEY} is required to be of type set")
|
raise TypeError(
|
||||||
for d in kwargs[TrackDescriptor.DISPOSITION_SET_KEY]:
|
f"TrackDesciptor.__init__(): Argument {TrackDescriptor.TAGS_KEY} is required to be of type dict"
|
||||||
if type(d) is not TrackDisposition:
|
)
|
||||||
raise TypeError(f"TrackDesciptor.__init__(): All elements of argument set {TrackDescriptor.DISPOSITION_SET_KEY} is required to be of type TrackDisposition")
|
self.__trackTags = kwargs[TrackDescriptor.TAGS_KEY]
|
||||||
self.__dispositionSet = kwargs[TrackDescriptor.DISPOSITION_SET_KEY]
|
else:
|
||||||
else:
|
self.__trackTags = {}
|
||||||
self.__dispositionSet = set()
|
|
||||||
|
|
||||||
if TrackDescriptor.AUDIO_LAYOUT_KEY in kwargs.keys():
|
if TrackDescriptor.DISPOSITION_SET_KEY in kwargs.keys():
|
||||||
if type(kwargs[TrackDescriptor.AUDIO_LAYOUT_KEY]) is not AudioLayout:
|
if type(kwargs[TrackDescriptor.DISPOSITION_SET_KEY]) is not set:
|
||||||
raise TypeError(f"TrackDesciptor.__init__(): Argument {TrackDescriptor.AUDIO_LAYOUT_KEY} is required to be of type AudioLayout")
|
raise TypeError(
|
||||||
self.__audioLayout = kwargs[TrackDescriptor.AUDIO_LAYOUT_KEY]
|
f"TrackDesciptor.__init__(): Argument {TrackDescriptor.DISPOSITION_SET_KEY} is required to be of type set"
|
||||||
else:
|
)
|
||||||
self.__audioLayout = AudioLayout.LAYOUT_UNDEFINED
|
for d in kwargs[TrackDescriptor.DISPOSITION_SET_KEY]:
|
||||||
|
if type(d) is not TrackDisposition:
|
||||||
|
raise TypeError(
|
||||||
|
f"TrackDesciptor.__init__(): All elements of argument set {TrackDescriptor.DISPOSITION_SET_KEY} is required to be of type TrackDisposition"
|
||||||
|
)
|
||||||
|
self.__dispositionSet = kwargs[TrackDescriptor.DISPOSITION_SET_KEY]
|
||||||
|
else:
|
||||||
|
self.__dispositionSet = set()
|
||||||
|
|
||||||
|
if TrackDescriptor.AUDIO_LAYOUT_KEY in kwargs.keys():
|
||||||
|
if type(kwargs[TrackDescriptor.AUDIO_LAYOUT_KEY]) is not AudioLayout:
|
||||||
|
raise TypeError(
|
||||||
|
f"TrackDesciptor.__init__(): Argument {TrackDescriptor.AUDIO_LAYOUT_KEY} is required to be of type AudioLayout"
|
||||||
|
)
|
||||||
|
self.__audioLayout = kwargs[TrackDescriptor.AUDIO_LAYOUT_KEY]
|
||||||
|
else:
|
||||||
|
self.__audioLayout = AudioLayout.LAYOUT_UNDEFINED
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def fromFfprobe(cls, streamObj, subIndex : int = -1):
|
def fromFfprobe(cls, streamObj, subIndex: int = -1):
|
||||||
"""Processes ffprobe stream data as array with elements according to the following example
|
"""Processes ffprobe stream data as array with elements according to the following example
|
||||||
|
{
|
||||||
|
"index": 4,
|
||||||
|
"codec_name": "hdmv_pgs_subtitle",
|
||||||
|
"codec_long_name": "HDMV Presentation Graphic Stream subtitles",
|
||||||
|
"codec_type": "subtitle",
|
||||||
|
"codec_tag_string": "[0][0][0][0]",
|
||||||
|
"codec_tag": "0x0000",
|
||||||
|
"r_frame_rate": "0/0",
|
||||||
|
"avg_frame_rate": "0/0",
|
||||||
|
"time_base": "1/1000",
|
||||||
|
"start_pts": 0,
|
||||||
|
"start_time": "0.000000",
|
||||||
|
"duration_ts": 1421035,
|
||||||
|
"duration": "1421.035000",
|
||||||
|
"disposition": {
|
||||||
|
"default": 1,
|
||||||
|
"dub": 0,
|
||||||
|
"original": 0,
|
||||||
|
"comment": 0,
|
||||||
|
"lyrics": 0,
|
||||||
|
"karaoke": 0,
|
||||||
|
"forced": 0,
|
||||||
|
"hearing_impaired": 0,
|
||||||
|
"visual_impaired": 0,
|
||||||
|
"clean_effects": 0,
|
||||||
|
"attached_pic": 0,
|
||||||
|
"timed_thumbnails": 0,
|
||||||
|
"non_diegetic": 0,
|
||||||
|
"captions": 0,
|
||||||
|
"descriptions": 0,
|
||||||
|
"metadata": 0,
|
||||||
|
"dependent": 0,
|
||||||
|
"still_image": 0
|
||||||
|
},
|
||||||
|
"tags": {
|
||||||
|
"language": "ger",
|
||||||
|
"title": "German Full"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
trackType = (
|
||||||
|
TrackType.fromLabel(streamObj["codec_type"])
|
||||||
|
if "codec_type" in streamObj.keys()
|
||||||
|
else TrackType.UNKNOWN
|
||||||
|
)
|
||||||
|
|
||||||
|
if trackType != TrackType.UNKNOWN:
|
||||||
|
|
||||||
|
kwargs = {}
|
||||||
|
|
||||||
|
kwargs[TrackDescriptor.INDEX_KEY] = (
|
||||||
|
int(streamObj[TrackDescriptor.FFPROBE_INDEX_KEY])
|
||||||
|
if TrackDescriptor.FFPROBE_INDEX_KEY in streamObj.keys()
|
||||||
|
else -1
|
||||||
|
)
|
||||||
|
kwargs[TrackDescriptor.SOURCE_INDEX_KEY] = kwargs[TrackDescriptor.INDEX_KEY]
|
||||||
|
kwargs[TrackDescriptor.SUB_INDEX_KEY] = subIndex
|
||||||
|
|
||||||
|
kwargs[TrackDescriptor.TRACK_TYPE_KEY] = trackType
|
||||||
|
kwargs[TrackDescriptor.DISPOSITION_SET_KEY] = (
|
||||||
{
|
{
|
||||||
"index": 4,
|
t
|
||||||
"codec_name": "hdmv_pgs_subtitle",
|
for d in (
|
||||||
"codec_long_name": "HDMV Presentation Graphic Stream subtitles",
|
k
|
||||||
"codec_type": "subtitle",
|
for (k, v) in streamObj[
|
||||||
"codec_tag_string": "[0][0][0][0]",
|
TrackDescriptor.FFPROBE_DISPOSITION_KEY
|
||||||
"codec_tag": "0x0000",
|
].items()
|
||||||
"r_frame_rate": "0/0",
|
if v
|
||||||
"avg_frame_rate": "0/0",
|
)
|
||||||
"time_base": "1/1000",
|
if (t := TrackDisposition.find(d)) is not None
|
||||||
"start_pts": 0,
|
|
||||||
"start_time": "0.000000",
|
|
||||||
"duration_ts": 1421035,
|
|
||||||
"duration": "1421.035000",
|
|
||||||
"disposition": {
|
|
||||||
"default": 1,
|
|
||||||
"dub": 0,
|
|
||||||
"original": 0,
|
|
||||||
"comment": 0,
|
|
||||||
"lyrics": 0,
|
|
||||||
"karaoke": 0,
|
|
||||||
"forced": 0,
|
|
||||||
"hearing_impaired": 0,
|
|
||||||
"visual_impaired": 0,
|
|
||||||
"clean_effects": 0,
|
|
||||||
"attached_pic": 0,
|
|
||||||
"timed_thumbnails": 0,
|
|
||||||
"non_diegetic": 0,
|
|
||||||
"captions": 0,
|
|
||||||
"descriptions": 0,
|
|
||||||
"metadata": 0,
|
|
||||||
"dependent": 0,
|
|
||||||
"still_image": 0
|
|
||||||
},
|
|
||||||
"tags": {
|
|
||||||
"language": "ger",
|
|
||||||
"title": "German Full"
|
|
||||||
}
|
}
|
||||||
}
|
if TrackDescriptor.FFPROBE_DISPOSITION_KEY in streamObj.keys()
|
||||||
"""
|
else set()
|
||||||
|
)
|
||||||
|
kwargs[TrackDescriptor.TAGS_KEY] = (
|
||||||
|
streamObj[TrackDescriptor.FFPROBE_TAGS_KEY]
|
||||||
|
if TrackDescriptor.FFPROBE_TAGS_KEY in streamObj.keys()
|
||||||
|
else {}
|
||||||
|
)
|
||||||
|
kwargs[TrackDescriptor.AUDIO_LAYOUT_KEY] = (
|
||||||
|
AudioLayout.identify(streamObj)
|
||||||
|
if trackType == TrackType.AUDIO
|
||||||
|
else AudioLayout.LAYOUT_UNDEFINED
|
||||||
|
)
|
||||||
|
|
||||||
trackType = TrackType.fromLabel(streamObj['codec_type']) if 'codec_type' in streamObj.keys() else TrackType.UNKNOWN
|
return cls(**kwargs)
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
if trackType != TrackType.UNKNOWN:
|
def getId(self):
|
||||||
|
return self.__trackId
|
||||||
|
|
||||||
kwargs = {}
|
def getPatternId(self):
|
||||||
|
return self.__patternId
|
||||||
|
|
||||||
kwargs[TrackDescriptor.INDEX_KEY] = int(streamObj[TrackDescriptor.FFPROBE_INDEX_KEY]) if TrackDescriptor.FFPROBE_INDEX_KEY in streamObj.keys() else -1
|
def getIndex(self):
|
||||||
kwargs[TrackDescriptor.SOURCE_INDEX_KEY] = kwargs[TrackDescriptor.INDEX_KEY]
|
return self.__index
|
||||||
kwargs[TrackDescriptor.SUB_INDEX_KEY] = subIndex
|
|
||||||
|
|
||||||
kwargs[TrackDescriptor.TRACK_TYPE_KEY] = trackType
|
def getSourceIndex(self):
|
||||||
kwargs[TrackDescriptor.DISPOSITION_SET_KEY] = {t for d in (k for (k,v) in streamObj[TrackDescriptor.FFPROBE_DISPOSITION_KEY].items() if v)
|
return self.__sourceIndex
|
||||||
if (t := TrackDisposition.find(d)) if t is not None} if TrackDescriptor.FFPROBE_DISPOSITION_KEY in streamObj.keys() else set()
|
|
||||||
kwargs[TrackDescriptor.TAGS_KEY] = streamObj[TrackDescriptor.FFPROBE_TAGS_KEY] if TrackDescriptor.FFPROBE_TAGS_KEY in streamObj.keys() else {}
|
|
||||||
kwargs[TrackDescriptor.AUDIO_LAYOUT_KEY] = AudioLayout.identify(streamObj) if trackType == TrackType.AUDIO else AudioLayout.LAYOUT_UNDEFINED
|
|
||||||
|
|
||||||
return cls(**kwargs)
|
def getSubIndex(self):
|
||||||
else:
|
return self.__subIndex
|
||||||
return None
|
|
||||||
|
|
||||||
|
def setSubIndex(self, subIndex):
|
||||||
|
self.__subIndex = subIndex
|
||||||
|
|
||||||
def getId(self):
|
def getType(self):
|
||||||
return self.__trackId
|
return self.__trackType
|
||||||
|
|
||||||
def getPatternId(self):
|
def getLanguage(self):
|
||||||
return self.__patternId
|
if "language" in self.__trackTags.keys():
|
||||||
|
return IsoLanguage.findThreeLetter(self.__trackTags["language"])
|
||||||
|
else:
|
||||||
|
return IsoLanguage.UNDEFINED
|
||||||
|
|
||||||
def getIndex(self):
|
def getTitle(self):
|
||||||
return self.__index
|
if "title" in self.__trackTags.keys():
|
||||||
|
return str(self.__trackTags["title"])
|
||||||
|
else:
|
||||||
|
return ""
|
||||||
|
|
||||||
def getSourceIndex(self):
|
def getAudioLayout(self):
|
||||||
return self.__sourceIndex
|
return self.__audioLayout
|
||||||
|
|
||||||
|
def getTags(self):
|
||||||
|
return self.__trackTags
|
||||||
|
|
||||||
def getSubIndex(self):
|
def getDispositionSet(self):
|
||||||
return self.__subIndex
|
return self.__dispositionSet
|
||||||
|
|
||||||
def setSubIndex(self, subIndex):
|
def getDispositionFlag(self, disposition: TrackDisposition) -> bool:
|
||||||
self.__subIndex = subIndex
|
return bool(disposition in self.__dispositionSet)
|
||||||
|
|
||||||
def getType(self):
|
def setDispositionFlag(self, disposition: TrackDisposition, state: bool):
|
||||||
return self.__trackType
|
if state:
|
||||||
|
self.__dispositionSet.add(disposition)
|
||||||
|
else:
|
||||||
|
self.__dispositionSet.discard(disposition)
|
||||||
|
|
||||||
def getLanguage(self):
|
def compare(self, vsTrackDescriptor):
|
||||||
if 'language' in self.__trackTags.keys():
|
|
||||||
return IsoLanguage.findThreeLetter(self.__trackTags['language'])
|
|
||||||
else:
|
|
||||||
return IsoLanguage.UNDEFINED
|
|
||||||
|
|
||||||
def getTitle(self):
|
compareResult = {}
|
||||||
if 'title' in self.__trackTags.keys():
|
|
||||||
return str(self.__trackTags['title'])
|
|
||||||
else:
|
|
||||||
return ''
|
|
||||||
|
|
||||||
def getAudioLayout(self):
|
tagsDiffResult = dictDiff(vsTrackDescriptor.getTags(), self.getTags())
|
||||||
return self.__audioLayout
|
|
||||||
|
|
||||||
|
if tagsDiffResult:
|
||||||
|
compareResult[TrackDescriptor.TAGS_KEY] = tagsDiffResult
|
||||||
|
|
||||||
def getTags(self):
|
vsDispositions = vsTrackDescriptor.getDispositionSet()
|
||||||
return self.__trackTags
|
dispositions = self.getDispositionSet()
|
||||||
|
|
||||||
def getDispositionSet(self):
|
dispositionDiffResult = setDiff(vsDispositions, dispositions)
|
||||||
return self.__dispositionSet
|
|
||||||
|
|
||||||
|
if dispositionDiffResult:
|
||||||
|
compareResult[TrackDescriptor.DISPOSITION_SET_KEY] = dispositionDiffResult
|
||||||
|
|
||||||
def getDispositionFlag(self, disposition : TrackDisposition) -> bool:
|
return compareResult
|
||||||
return bool(disposition in self.__dispositionSet)
|
|
||||||
|
|
||||||
def setDispositionFlag(self, disposition : TrackDisposition, state : bool):
|
def setExternalSourceFilePath(self, filePath: str):
|
||||||
if state:
|
self.__externalSourceFilePath = str(filePath)
|
||||||
self.__dispositionSet.add(disposition)
|
|
||||||
else:
|
|
||||||
self.__dispositionSet.discard(disposition)
|
|
||||||
|
|
||||||
|
def getExternalSourceFilePath(self):
|
||||||
def compare(self, vsTrackDescriptor):
|
return self.__externalSourceFilePath
|
||||||
|
|
||||||
compareResult = {}
|
|
||||||
|
|
||||||
tagsDiffResult = dictDiff(vsTrackDescriptor.getTags(), self.getTags())
|
|
||||||
|
|
||||||
if tagsDiffResult:
|
|
||||||
compareResult[TrackDescriptor.TAGS_KEY] = tagsDiffResult
|
|
||||||
|
|
||||||
vsDispositions = vsTrackDescriptor.getDispositionSet()
|
|
||||||
dispositions = self.getDispositionSet()
|
|
||||||
|
|
||||||
dispositionDiffResult = setDiff(vsDispositions, dispositions)
|
|
||||||
|
|
||||||
if dispositionDiffResult:
|
|
||||||
compareResult[TrackDescriptor.DISPOSITION_SET_KEY] = dispositionDiffResult
|
|
||||||
|
|
||||||
return compareResult
|
|
||||||
|
|||||||
Reference in New Issue
Block a user