Scenario4 inc
This commit is contained in:
@@ -440,10 +440,12 @@ def convert(ctx,
|
|||||||
|
|
||||||
if context['use_tmdb']:
|
if context['use_tmdb']:
|
||||||
|
|
||||||
|
click.echo(f"Querying TMDB for show_id={currentShowDescriptor.getId()} season={mediaFileProperties.getSeason()} episode{mediaFileProperties.getEpisode()}")
|
||||||
tmdbEpisodeResult = tc.queryEpisode(currentShowDescriptor.getId(), mediaFileProperties.getSeason(), mediaFileProperties.getEpisode())
|
tmdbEpisodeResult = tc.queryEpisode(currentShowDescriptor.getId(), mediaFileProperties.getSeason(), mediaFileProperties.getEpisode())
|
||||||
|
click.echo(f"tmdbEpisodeResult={tmdbEpisodeResult}")
|
||||||
|
|
||||||
if tmdbEpisodeResult:
|
if tmdbEpisodeResult:
|
||||||
sourceFileBasename = tc.getEpisodeFileBasename(currentShowDescriptor.getFilenamePrefix(),
|
sourceFileBasename = TmdbController.getEpisodeFileBasename(currentShowDescriptor.getFilenamePrefix(),
|
||||||
tmdbEpisodeResult['name'],
|
tmdbEpisodeResult['name'],
|
||||||
mediaFileProperties.getSeason(),
|
mediaFileProperties.getSeason(),
|
||||||
mediaFileProperties.getEpisode(),
|
mediaFileProperties.getEpisode(),
|
||||||
|
|||||||
@@ -175,12 +175,12 @@ class FfxController():
|
|||||||
# sourceTrackDescriptors = [] if self.__sourceMediaDescriptor is None else self.__sourceMediaDescriptor.getAllTrackDescriptors()
|
# sourceTrackDescriptors = [] if self.__sourceMediaDescriptor is None else self.__sourceMediaDescriptor.getAllTrackDescriptors()
|
||||||
targetTrackDescriptors = self.__targetMediaDescriptor.getAllTrackDescriptors()
|
targetTrackDescriptors = self.__targetMediaDescriptor.getAllTrackDescriptors()
|
||||||
|
|
||||||
|
|
||||||
dispositionTokens = []
|
dispositionTokens = []
|
||||||
|
|
||||||
for trackIndex in range(len(targetTrackDescriptors)):
|
for trackIndex in range(len(targetTrackDescriptors)):
|
||||||
|
|
||||||
td = targetTrackDescriptors[trackIndex]
|
td = targetTrackDescriptors[trackIndex]
|
||||||
|
#sd = sourceTrackDescriptors[trackIndex]
|
||||||
|
|
||||||
#HINT: No dispositions for pgs subtitle tracks that have no external file source
|
#HINT: No dispositions for pgs subtitle tracks that have no external file source
|
||||||
if (td.getExternalSourceFilePath()
|
if (td.getExternalSourceFilePath()
|
||||||
|
|||||||
@@ -43,24 +43,35 @@ class FileProperties():
|
|||||||
|
|
||||||
self.__pc = PatternController(context)
|
self.__pc = PatternController(context)
|
||||||
|
|
||||||
|
# db pattern boruto_[sS]([0-9]+)[eE]([0-9]+).mkv
|
||||||
|
|
||||||
|
# Checking if database contains matching pattern
|
||||||
matchResult = self.__pc.matchFilename(self.__sourceFilename)
|
matchResult = self.__pc.matchFilename(self.__sourceFilename)
|
||||||
|
|
||||||
|
self.__logger.debug(f"FileProperties.__init__(): Match result {matchResult}")
|
||||||
|
|
||||||
self.__pattern: Pattern = matchResult['pattern'] if matchResult else None
|
self.__pattern: Pattern = matchResult['pattern'] if matchResult else None
|
||||||
|
|
||||||
matchedGroups = matchResult['match'].groups() if matchResult else {}
|
if matchResult:
|
||||||
seIndicator = matchedGroups[0] if matchedGroups else self.__sourceFilename
|
databaseMatchedGroups = matchResult['match'].groups()
|
||||||
|
self.__season = databaseMatchedGroups[0]
|
||||||
|
self.__episode = databaseMatchedGroups[1]
|
||||||
|
|
||||||
se_match = re.search(FileProperties.SEASON_EPISODE_INDICATOR_MATCH, seIndicator)
|
else:
|
||||||
e_match = re.search(FileProperties.EPISODE_INDICATOR_MATCH, seIndicator)
|
self.__logger.debug(f"FileProperties.__init__(): Checking file name for indicator {self.__sourceFilename}")
|
||||||
|
|
||||||
self.__season = -1
|
se_match = re.search(FileProperties.SEASON_EPISODE_INDICATOR_MATCH, self.__sourceFilename)
|
||||||
self.__episode = -1
|
e_match = re.search(FileProperties.EPISODE_INDICATOR_MATCH, self.__sourceFilename)
|
||||||
|
|
||||||
if se_match is not None:
|
if se_match is not None:
|
||||||
self.__season = int(se_match.group(1))
|
self.__season = int(se_match.group(1))
|
||||||
self.__episode = int(se_match.group(2))
|
self.__episode = int(se_match.group(2))
|
||||||
elif e_match is not None:
|
elif e_match is not None:
|
||||||
|
self.__season = -1
|
||||||
self.__episode = int(e_match.group(1))
|
self.__episode = int(e_match.group(1))
|
||||||
|
else:
|
||||||
|
self.__season = -1
|
||||||
|
self.__episode = -1
|
||||||
|
|
||||||
|
|
||||||
def getFormatData(self):
|
def getFormatData(self):
|
||||||
|
|||||||
47
bin/ffx/media_controller.py
Normal file
47
bin/ffx/media_controller.py
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
import click, re
|
||||||
|
|
||||||
|
from ffx.model.pattern import Pattern
|
||||||
|
from ffx.media_descriptor import MediaDescriptor
|
||||||
|
|
||||||
|
from ffx.tag_controller import TagController
|
||||||
|
from ffx.track_controller import TrackController
|
||||||
|
|
||||||
|
class MediaController():
|
||||||
|
|
||||||
|
def __init__(self, context):
|
||||||
|
|
||||||
|
self.context = context
|
||||||
|
self.Session = self.context['database']['session'] # convenience
|
||||||
|
|
||||||
|
self.__logger = context['logger']
|
||||||
|
|
||||||
|
self.__tc = TrackController(context = context)
|
||||||
|
self.__tac = TagController(context = context)
|
||||||
|
|
||||||
|
def setPatternMediaDescriptor(self, mediaDescriptor: MediaDescriptor, patternId: int):
|
||||||
|
|
||||||
|
try:
|
||||||
|
|
||||||
|
pid = int(patternId)
|
||||||
|
|
||||||
|
s = self.Session()
|
||||||
|
q = s.query(Pattern).filter(Pattern.id == pid)
|
||||||
|
|
||||||
|
if q.count():
|
||||||
|
pattern = q.first
|
||||||
|
|
||||||
|
for mediaTagKey, mediaTagValue in mediaDescriptor.getTags():
|
||||||
|
self.__tac.updateMediaTag(pid, mediaTagKey, mediaTagValue)
|
||||||
|
for trackDescriptor in mediaDescriptor.getAllTrackDescriptors():
|
||||||
|
self.__tc.addTrack(trackDescriptor, patternId = pid)
|
||||||
|
|
||||||
|
s.commit()
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
except Exception as ex:
|
||||||
|
self.__logger.error(f"MediaController.setPatternMediaDescriptor(): {repr(ex)}")
|
||||||
|
raise click.ClickException(f"MediaController.setPatternMediaDescriptor(): {repr(ex)}")
|
||||||
|
finally:
|
||||||
|
s.close()
|
||||||
@@ -501,3 +501,11 @@ class MediaDescriptor:
|
|||||||
# click.echo(f"Found matching subtitle file {msfd["path"]}\n")
|
# click.echo(f"Found matching subtitle file {msfd["path"]}\n")
|
||||||
self.__logger.debug(f"importSubtitles(): Found matching subtitle file {msfd["path"]}")
|
self.__logger.debug(f"importSubtitles(): Found matching subtitle file {msfd["path"]}")
|
||||||
matchingSubtitleTrackDescriptor[0].setExternalSourceFilePath(msfd["path"])
|
matchingSubtitleTrackDescriptor[0].setExternalSourceFilePath(msfd["path"])
|
||||||
|
|
||||||
|
|
||||||
|
def getConfiguration(self, label: str = ''):
|
||||||
|
yield f"--- {label if label else 'MediaDescriptor '+str(id(self))} {' '.join([str(k)+'='+str(v) for k,v in self.__mediaTags.items()])}"
|
||||||
|
for td in self.getAllTrackDescriptors():
|
||||||
|
yield (f"{td.getIndex()}:{td.getType().indicator()}:{td.getSubIndex()} "
|
||||||
|
+ '|'.join([d.indicator() for d in td.getDispositionSet()])
|
||||||
|
+ ' ' + ' '.join([str(k)+'='+str(v) for k,v in td.getTags().items()]))
|
||||||
|
|||||||
@@ -504,7 +504,7 @@ class MediaDetailsScreen(Screen):
|
|||||||
|
|
||||||
if patternDescriptor:
|
if patternDescriptor:
|
||||||
patternId = self.__pc.addPattern(patternDescriptor)
|
patternId = self.__pc.addPattern(patternDescriptor)
|
||||||
|
if patternId:
|
||||||
self.highlightPattern(False)
|
self.highlightPattern(False)
|
||||||
|
|
||||||
for tagKey, tagValue in self.__currentMediaDescriptor.getTags().items():
|
for tagKey, tagValue in self.__currentMediaDescriptor.getTags().items():
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ class PatternController():
|
|||||||
s.commit()
|
s.commit()
|
||||||
return pattern.getId()
|
return pattern.getId()
|
||||||
else:
|
else:
|
||||||
return None
|
return 0
|
||||||
|
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
raise click.ClickException(f"PatternController.addPattern(): {repr(ex)}")
|
raise click.ClickException(f"PatternController.addPattern(): {repr(ex)}")
|
||||||
@@ -116,7 +116,8 @@ class PatternController():
|
|||||||
s.close()
|
s.close()
|
||||||
|
|
||||||
|
|
||||||
def matchFilename(self, filename : str) -> re.Match:
|
def matchFilename(self, filename : str) -> dict:
|
||||||
|
"""Returns dict {'match': <a regex match obj>, 'pattern': <ffx pattern obj>} or empty dict of no pattern was found"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
s = self.Session()
|
s = self.Session()
|
||||||
@@ -126,7 +127,7 @@ class PatternController():
|
|||||||
|
|
||||||
for pattern in q.all():
|
for pattern in q.all():
|
||||||
patternMatch = re.search(str(pattern.pattern), str(filename))
|
patternMatch = re.search(str(pattern.pattern), str(filename))
|
||||||
if patternMatch:
|
if patternMatch is not None:
|
||||||
matchResult['match'] = patternMatch
|
matchResult['match'] = patternMatch
|
||||||
matchResult['pattern'] = pattern
|
matchResult['pattern'] = pattern
|
||||||
|
|
||||||
|
|||||||
@@ -345,8 +345,7 @@ class PatternDetailsScreen(Screen):
|
|||||||
|
|
||||||
else:
|
else:
|
||||||
patternId = self.__pc.addPattern(patternDescriptor)
|
patternId = self.__pc.addPattern(patternDescriptor)
|
||||||
if patternId is not None:
|
if patternId:
|
||||||
|
|
||||||
self.dismiss(patternDescriptor)
|
self.dismiss(patternDescriptor)
|
||||||
else:
|
else:
|
||||||
#TODO: Meldung
|
#TODO: Meldung
|
||||||
|
|||||||
@@ -55,33 +55,34 @@ class ShowDescriptor():
|
|||||||
else:
|
else:
|
||||||
self.__showYear = -1
|
self.__showYear = -1
|
||||||
|
|
||||||
|
|
||||||
if ShowDescriptor.INDEX_SEASON_DIGITS_KEY in kwargs.keys():
|
if ShowDescriptor.INDEX_SEASON_DIGITS_KEY in kwargs.keys():
|
||||||
if type(kwargs[ShowDescriptor.INDEX_SEASON_DIGITS_KEY]) is not int:
|
if type(kwargs[ShowDescriptor.INDEX_SEASON_DIGITS_KEY]) is not int:
|
||||||
raise TypeError(f"ShowDescriptor.__init__(): Argument {ShowDescriptor.INDEX_SEASON_DIGITS_KEY} is required to be of type int")
|
raise TypeError(f"ShowDescriptor.__init__(): Argument {ShowDescriptor.INDEX_SEASON_DIGITS_KEY} is required to be of type int")
|
||||||
self.__indexSeasonDigits = kwargs[ShowDescriptor.INDEX_SEASON_DIGITS_KEY]
|
self.__indexSeasonDigits = kwargs[ShowDescriptor.INDEX_SEASON_DIGITS_KEY]
|
||||||
else:
|
else:
|
||||||
self.__indexSeasonDigits = -1
|
self.__indexSeasonDigits = ShowDescriptor.DEFAULT_INDEX_SEASON_DIGITS
|
||||||
|
|
||||||
if ShowDescriptor.INDEX_EPISODE_DIGITS_KEY in kwargs.keys():
|
if ShowDescriptor.INDEX_EPISODE_DIGITS_KEY in kwargs.keys():
|
||||||
if type(kwargs[ShowDescriptor.INDEX_EPISODE_DIGITS_KEY]) is not int:
|
if type(kwargs[ShowDescriptor.INDEX_EPISODE_DIGITS_KEY]) is not int:
|
||||||
raise TypeError(f"ShowDescriptor.__init__(): Argument {ShowDescriptor.INDEX_EPISODE_DIGITS_KEY} is required to be of type int")
|
raise TypeError(f"ShowDescriptor.__init__(): Argument {ShowDescriptor.INDEX_EPISODE_DIGITS_KEY} is required to be of type int")
|
||||||
self.__indexEpisodeDigits = kwargs[ShowDescriptor.INDEX_EPISODE_DIGITS_KEY]
|
self.__indexEpisodeDigits = kwargs[ShowDescriptor.INDEX_EPISODE_DIGITS_KEY]
|
||||||
else:
|
else:
|
||||||
self.__indexEpisodeDigits = -1
|
self.__indexEpisodeDigits = ShowDescriptor.DEFAULT_INDEX_EPISODE_DIGITS
|
||||||
|
|
||||||
if ShowDescriptor.INDICATOR_SEASON_DIGITS_KEY in kwargs.keys():
|
if ShowDescriptor.INDICATOR_SEASON_DIGITS_KEY in kwargs.keys():
|
||||||
if type(kwargs[ShowDescriptor.INDICATOR_SEASON_DIGITS_KEY]) is not int:
|
if type(kwargs[ShowDescriptor.INDICATOR_SEASON_DIGITS_KEY]) is not int:
|
||||||
raise TypeError(f"ShowDescriptor.__init__(): Argument {ShowDescriptor.INDICATOR_SEASON_DIGITS_KEY} is required to be of type int")
|
raise TypeError(f"ShowDescriptor.__init__(): Argument {ShowDescriptor.INDICATOR_SEASON_DIGITS_KEY} is required to be of type int")
|
||||||
self.__indicatorSeasonDigits = kwargs[ShowDescriptor.INDICATOR_SEASON_DIGITS_KEY]
|
self.__indicatorSeasonDigits = kwargs[ShowDescriptor.INDICATOR_SEASON_DIGITS_KEY]
|
||||||
else:
|
else:
|
||||||
self.__indicatorSeasonDigits = -1
|
self.__indicatorSeasonDigits = ShowDescriptor.DEFAULT_INDICATOR_SEASON_DIGITS
|
||||||
|
|
||||||
if ShowDescriptor.INDICATOR_EPISODE_DIGITS_KEY in kwargs.keys():
|
if ShowDescriptor.INDICATOR_EPISODE_DIGITS_KEY in kwargs.keys():
|
||||||
if type(kwargs[ShowDescriptor.INDICATOR_EPISODE_DIGITS_KEY]) is not int:
|
if type(kwargs[ShowDescriptor.INDICATOR_EPISODE_DIGITS_KEY]) is not int:
|
||||||
raise TypeError(f"ShowDescriptor.__init__(): Argument {ShowDescriptor.INDICATOR_EPISODE_DIGITS_KEY} is required to be of type int")
|
raise TypeError(f"ShowDescriptor.__init__(): Argument {ShowDescriptor.INDICATOR_EPISODE_DIGITS_KEY} is required to be of type int")
|
||||||
self.__indicatorEpisodeDigits = kwargs[ShowDescriptor.INDICATOR_EPISODE_DIGITS_KEY]
|
self.__indicatorEpisodeDigits = kwargs[ShowDescriptor.INDICATOR_EPISODE_DIGITS_KEY]
|
||||||
else:
|
else:
|
||||||
self.__indicatorEpisodeDigits = -1
|
self.__indicatorEpisodeDigits = ShowDescriptor.DEFAULT_INDICATOR_EPISODE_DIGITS
|
||||||
|
|
||||||
|
|
||||||
def getId(self):
|
def getId(self):
|
||||||
|
|||||||
@@ -17,12 +17,16 @@ class IndicatorCombinator():
|
|||||||
if season == -1 and episode == -1:
|
if season == -1 and episode == -1:
|
||||||
return {
|
return {
|
||||||
'variant': 'S00E00',
|
'variant': 'S00E00',
|
||||||
'indicator': ''
|
'indicator': '',
|
||||||
|
'season': season,
|
||||||
|
'episode': episode
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
return {
|
return {
|
||||||
'variant': f"S{season+1:02d}E{episode+1:02d}",
|
'variant': f"S{season:02d}E{episode:02d}",
|
||||||
'indicator': f"S{season+1:02d}E{episode+1:02d}"
|
'indicator': f"S{season:02d}E{episode:02d}",
|
||||||
|
'season': season,
|
||||||
|
'episode': episode
|
||||||
}
|
}
|
||||||
|
|
||||||
def assertFunc(self, testObj = {}):
|
def assertFunc(self, testObj = {}):
|
||||||
@@ -36,4 +40,4 @@ class IndicatorCombinator():
|
|||||||
yield self.getPayload()
|
yield self.getPayload()
|
||||||
for season in range(IndicatorCombinator.MAX_SEASON):
|
for season in range(IndicatorCombinator.MAX_SEASON):
|
||||||
for episode in range(IndicatorCombinator.MAX_EPISODE):
|
for episode in range(IndicatorCombinator.MAX_EPISODE):
|
||||||
yield self.getPayload(season, episode)
|
yield self.getPayload(season+1, episode+1)
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import os, glob, sys, importlib, glob, inspect
|
|||||||
|
|
||||||
from ffx.test.helper import createEmptyDirectory
|
from ffx.test.helper import createEmptyDirectory
|
||||||
|
|
||||||
|
|
||||||
class Scenario():
|
class Scenario():
|
||||||
"""Scenarios
|
"""Scenarios
|
||||||
|
|
||||||
|
|||||||
@@ -91,8 +91,13 @@ class Scenario2(Scenario):
|
|||||||
if rc:
|
if rc:
|
||||||
self._logger.debug(f"{variantLabel}: Process returned ERROR {rc} ({err})")
|
self._logger.debug(f"{variantLabel}: Process returned ERROR {rc} ({err})")
|
||||||
|
|
||||||
|
|
||||||
# Phase 4: Evaluate results
|
# Phase 4: Evaluate results
|
||||||
|
|
||||||
|
resultFilenames = [rf for rf in self.getFilenamesInTestDirectory() if rf != 'ffmpeg2pass-0.log' and rf != variantFilename]
|
||||||
|
|
||||||
|
self._logger.debug(f"{variantLabel}: Result filenames: {resultFilenames}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
||||||
jobFailed = bool(rc)
|
jobFailed = bool(rc)
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ from .scenario import Scenario
|
|||||||
|
|
||||||
from ffx.test.helper import createMediaTestFile
|
from ffx.test.helper import createMediaTestFile
|
||||||
from ffx.process import executeProcess
|
from ffx.process import executeProcess
|
||||||
|
from ffx.database import databaseContext
|
||||||
|
from ffx.test.helper import createEmptyDirectory
|
||||||
|
|
||||||
from ffx.file_properties import FileProperties
|
from ffx.file_properties import FileProperties
|
||||||
|
|
||||||
@@ -14,18 +16,98 @@ from ffx.track_type import TrackType
|
|||||||
from ffx.track_disposition import TrackDisposition
|
from ffx.track_disposition import TrackDisposition
|
||||||
|
|
||||||
from ffx.test.media_combinator import MediaCombinator
|
from ffx.test.media_combinator import MediaCombinator
|
||||||
|
from ffx.test.indicator_combinator import IndicatorCombinator
|
||||||
|
|
||||||
|
from ffx.show_descriptor import ShowDescriptor
|
||||||
|
from ffx.show_controller import ShowController
|
||||||
|
from ffx.pattern_controller import PatternController
|
||||||
|
from ffx.media_controller import MediaController
|
||||||
|
|
||||||
|
from ffx.tmdb_controller import TmdbController
|
||||||
|
from ffx.tmdb_controller import TMDB_API_KEY_NOT_PRESENT_EXCEPTION
|
||||||
|
|
||||||
class Scenario4(Scenario):
|
class Scenario4(Scenario):
|
||||||
|
|
||||||
|
TEST_SHOW_IDENTIFIER = 83095
|
||||||
|
TEST_SHOW_NAME = 'The Rising of the Shield Hero'
|
||||||
|
TEST_SHOW_YEAR = 2019
|
||||||
|
|
||||||
|
TEST_FILE_LABEL = 'rotsh'
|
||||||
|
TEST_FILE_EXTENSION = 'mkv'
|
||||||
|
|
||||||
|
TEST_PATTERN = f"{TEST_FILE_LABEL}_{FileProperties.SEASON_EPISODE_INDICATOR_MATCH}.{TEST_FILE_EXTENSION}"
|
||||||
|
|
||||||
|
EXPECTED_FILE_EXTENSION = 'webm'
|
||||||
|
|
||||||
|
|
||||||
def __init__(self, context):
|
def __init__(self, context):
|
||||||
super().__init__(context)
|
super().__init__(context)
|
||||||
|
|
||||||
|
self.__tmdbApiKey = os.environ.get('TMDB_API_KEY', None)
|
||||||
|
if self.__tmdbApiKey is None:
|
||||||
|
raise TMDB_API_KEY_NOT_PRESENT_EXCEPTION
|
||||||
|
|
||||||
|
self.__testDbFilePath = os.path.join(self._testDirectory, 'test.db')
|
||||||
|
self.createEmptyTestDatabase()
|
||||||
|
|
||||||
|
self.__ic = IndicatorCombinator(context = context)
|
||||||
|
|
||||||
|
self.__sc = ShowController(context = context)
|
||||||
|
self.__pc = PatternController(context = context)
|
||||||
|
self.__mc = MediaController(context = context)
|
||||||
|
|
||||||
|
self.__tc = TmdbController()
|
||||||
|
|
||||||
|
|
||||||
def getScenario(self):
|
def getScenario(self):
|
||||||
return self.__class__.__name__[8:]
|
return self.__class__.__name__[8:]
|
||||||
|
|
||||||
|
|
||||||
|
def createEmptyTestDatabase(self):
|
||||||
|
|
||||||
|
if not self._context['database'] is None:
|
||||||
|
self._context['database']['engine'].dispose()
|
||||||
|
|
||||||
|
if os.path.isfile(self.__testDbFilePath):
|
||||||
|
os.unlink(self.__testDbFilePath)
|
||||||
|
self._context['database'] = None
|
||||||
|
|
||||||
|
self._logger.debug(f"Creating test db with path {self.__testDbFilePath}")
|
||||||
|
self._context['database'] = databaseContext(databasePath=self.__testDbFilePath)
|
||||||
|
|
||||||
|
def prepareTestDatabase(self, sourceMediaDescriptor: MediaDescriptor):
|
||||||
|
|
||||||
|
if not self._context['database'] is None:
|
||||||
|
self._context['database']['engine'].dispose()
|
||||||
|
|
||||||
|
if os.path.isfile(self.__testDbFilePath):
|
||||||
|
os.unlink(self.__testDbFilePath)
|
||||||
|
self._context['database'] = None
|
||||||
|
|
||||||
|
self._logger.debug(f"Creating test db with path {self.__testDbFilePath}")
|
||||||
|
self._context['database'] = databaseContext(databasePath=self.__testDbFilePath)
|
||||||
|
|
||||||
|
kwargs = {}
|
||||||
|
|
||||||
|
kwargs[ShowDescriptor.ID_KEY] = Scenario4.TEST_SHOW_IDENTIFIER
|
||||||
|
kwargs[ShowDescriptor.NAME_KEY] = Scenario4.TEST_SHOW_NAME
|
||||||
|
kwargs[ShowDescriptor.YEAR_KEY] = Scenario4.TEST_SHOW_YEAR
|
||||||
|
|
||||||
|
self.__testShowDescriptor = ShowDescriptor(**kwargs)
|
||||||
|
|
||||||
|
self._logger.debug(f"Adding test show '{self.__testShowDescriptor.getFilenamePrefix()}' to test db")
|
||||||
|
if not self.__sc.updateShow(self.__testShowDescriptor):
|
||||||
|
raise click.ClickException('Could not create test show in db')
|
||||||
|
|
||||||
|
testPatternDescriptor = {
|
||||||
|
'show_id': Scenario4.TEST_SHOW_IDENTIFIER,
|
||||||
|
'pattern': Scenario4.TEST_PATTERN
|
||||||
|
}
|
||||||
|
patternId = self.__pc.addPattern(testPatternDescriptor)
|
||||||
|
|
||||||
|
if patternId:
|
||||||
|
self.__mc.setPatternMediaDescriptor(sourceMediaDescriptor, patternId)
|
||||||
|
|
||||||
|
|
||||||
def job(self, yieldObj: dict):
|
def job(self, yieldObj: dict):
|
||||||
|
|
||||||
@@ -44,11 +126,11 @@ class Scenario4(Scenario):
|
|||||||
variantLabel = f"{self.__class__.__name__} Variant {variantIdentifier}"
|
variantLabel = f"{self.__class__.__name__} Variant {variantIdentifier}"
|
||||||
|
|
||||||
sourceMediaDescriptor: MediaDescriptor = targetYieldObj['payload']
|
sourceMediaDescriptor: MediaDescriptor = targetYieldObj['payload']
|
||||||
#presetMediaDescriptor: MediaDescriptor = targetYieldObj['payload']['preset']
|
presetMediaDescriptor: MediaDescriptor = presetYieldObj['payload']
|
||||||
|
|
||||||
assertSelectorList: list = targetYieldObj['assertSelectors']
|
assertSelectorList: list = presetYieldObj['assertSelectors']
|
||||||
assertFuncList = targetYieldObj['assertFuncs']
|
assertFuncList = presetYieldObj['assertFuncs']
|
||||||
shouldFail = targetYieldObj['shouldFail']
|
shouldFail = presetYieldObj['shouldFail']
|
||||||
|
|
||||||
try:
|
try:
|
||||||
jellyfinSelectorIndex = assertSelectorList.index('J')
|
jellyfinSelectorIndex = assertSelectorList.index('J')
|
||||||
@@ -61,63 +143,113 @@ class Scenario4(Scenario):
|
|||||||
if self._context['test_variant'] and variantIdentifier != self._context['test_variant']:
|
if self._context['test_variant'] and variantIdentifier != self._context['test_variant']:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
for l in sourceMediaDescriptor.getConfiguration(label = 'sourceMediaDescriptor'):
|
||||||
|
self._logger.debug(l)
|
||||||
|
|
||||||
|
for l in presetMediaDescriptor.getConfiguration(label = 'presetMediaDescriptor'):
|
||||||
|
self._logger.debug(l)
|
||||||
|
|
||||||
self._logger.debug(f"Running Job: {variantLabel}")
|
self._logger.debug(f"Running Job: {variantLabel}")
|
||||||
|
|
||||||
# Phase 1: Setup source files
|
# Phase 1: Setup source files
|
||||||
self.clearTestDirectory()
|
self.clearTestDirectory()
|
||||||
mediaFilePath = createMediaTestFile(mediaDescriptor=sourceMediaDescriptor, directory=self._testDirectory, logger=self._logger, length = 2)
|
self.createEmptyTestDatabase()
|
||||||
|
self.prepareTestDatabase(sourceMediaDescriptor)
|
||||||
|
|
||||||
|
testFileList = []
|
||||||
|
for indicatorObj in [y for y in self.__ic.getYield() if y['indicator']]:
|
||||||
|
|
||||||
|
indicator = indicatorObj['indicator']
|
||||||
|
|
||||||
|
testFileObj = {}
|
||||||
|
testFileObj['season'] = indicatorObj['season']
|
||||||
|
testFileObj['episode'] = indicatorObj['episode']
|
||||||
|
|
||||||
|
testFileObj['basename'] = f"{Scenario4.TEST_FILE_LABEL}_{indicator}"
|
||||||
|
|
||||||
|
testFileObj['path'] = createMediaTestFile(mediaDescriptor = presetMediaDescriptor,
|
||||||
|
directory = self._testDirectory,
|
||||||
|
baseName = testFileObj['basename'],
|
||||||
|
logger=self._logger,
|
||||||
|
length = 2)
|
||||||
|
testFileObj['filename'] = f"{testFileObj['basename']}.{Scenario4.TEST_FILE_EXTENSION}"
|
||||||
|
|
||||||
|
testFileList.append(testFileObj)
|
||||||
|
|
||||||
|
# Phase 2: Prepare database
|
||||||
|
|
||||||
# # Phase 2: Prepare database
|
|
||||||
#
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Phase 3: Run ffx
|
# Phase 3: Run ffx
|
||||||
commandSequence = [sys.executable,
|
commandSequence = [sys.executable,
|
||||||
self._ffxExecutablePath,
|
self._ffxExecutablePath,
|
||||||
'convert',
|
'--database-file',
|
||||||
mediaFilePath,
|
self.__testDbFilePath,
|
||||||
'--no-prompt']
|
'convert']
|
||||||
|
commandSequence += [tfo['filename'] for tfo in testFileList]
|
||||||
|
|
||||||
if not testContext['use_jellyfin']:
|
commandSequence += ['--no-prompt']
|
||||||
commandSequence += ['--no-jellyfin']
|
|
||||||
|
# if not testContext['use_jellyfin']:
|
||||||
|
# commandSequence += ['--no-jellyfin']
|
||||||
|
|
||||||
self._logger.debug(f"{variantLabel}: Test sequence: {commandSequence}")
|
self._logger.debug(f"{variantLabel}: Test sequence: {commandSequence}")
|
||||||
|
|
||||||
out, err, rc = executeProcess(commandSequence, directory = self._testDirectory)
|
out, err, rc = executeProcess(commandSequence, directory = self._testDirectory)
|
||||||
|
|
||||||
if out:
|
# if out:
|
||||||
self._logger.debug(f"{variantLabel}: Process output: {out}")
|
# self._logger.debug(f"{variantLabel}: Process output: {out}")
|
||||||
if rc:
|
if rc:
|
||||||
self._logger.debug(f"{variantLabel}: Process returned ERROR {rc} ({err})")
|
self._logger.debug(f"{variantLabel}: Process returned ERROR {rc} ({err})")
|
||||||
|
|
||||||
|
|
||||||
# Phase 4: Evaluate results
|
# Phase 4: Evaluate results
|
||||||
|
|
||||||
|
resultFilenames = [rf for rf in self.getFilenamesInTestDirectory() if rf.endswith(f".{Scenario4.EXPECTED_FILE_EXTENSION}")]
|
||||||
|
|
||||||
|
self._logger.debug(f"{variantLabel}: Result filenames: {resultFilenames}")
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
||||||
jobFailed = bool(rc)
|
assert not (bool(rc)
|
||||||
|
), f"Process failed"
|
||||||
|
|
||||||
self._logger.debug(f"{variantLabel}: Should fail: {shouldFail} / actually failed: {jobFailed}")
|
for tfo in testFileList:
|
||||||
|
|
||||||
assert (jobFailed == shouldFail
|
tmdbEpisodeResult = self.__tc.queryEpisode(Scenario4.TEST_SHOW_IDENTIFIER,
|
||||||
), f"Process {'failed' if jobFailed else 'did not fail'}"
|
tfo['season'], tfo['episode'])
|
||||||
|
|
||||||
if not jobFailed:
|
expectedFileBasename = TmdbController.getEpisodeFileBasename(self.__testShowDescriptor.getFilenamePrefix(),
|
||||||
|
tmdbEpisodeResult['name'],
|
||||||
|
tfo['season'], tfo['episode'])
|
||||||
|
|
||||||
resultFile = os.path.join(self._testDirectory, 'media.webm')
|
expectedFilename = f"{expectedFileBasename}.{Scenario4.EXPECTED_FILE_EXTENSION}"
|
||||||
|
expectedFilePath = os.path.join(self._testDirectory, expectedFilename)
|
||||||
|
|
||||||
assert (os.path.isfile(resultFile)
|
assert (os.path.isfile(expectedFilePath)
|
||||||
), f"Result file 'media.webm' in path '{self._testDirectory}' wasn't created"
|
), f"Result file '{expectedFilename}' in path '{self._testDirectory}' wasn't created"
|
||||||
|
|
||||||
resultFileProperties = FileProperties(testContext, resultFile)
|
###
|
||||||
resultMediaDescriptor = resultFileProperties.getMediaDescriptor()
|
#
|
||||||
|
|
||||||
|
rfp = FileProperties(testContext, expectedFilePath)
|
||||||
|
self._logger.debug(f"{variantLabel}: Result file properties: {rfp.getFilename()} season={rfp.getSeason()} episode={rfp.getEpisode()}")
|
||||||
|
|
||||||
|
rmd = rfp.getMediaDescriptor()
|
||||||
|
rmt = rmd.getAllTrackDescriptors()
|
||||||
|
|
||||||
|
for l in rmd.getConfiguration(label = 'resultMediaDescriptor'):
|
||||||
|
self._logger.debug(l)
|
||||||
|
|
||||||
if testContext['use_jellyfin']:
|
if testContext['use_jellyfin']:
|
||||||
sourceMediaDescriptor.applyJellyfinOrder()
|
sourceMediaDescriptor.applyJellyfinOrder()
|
||||||
resultMediaDescriptor.applySourceIndices(sourceMediaDescriptor)
|
|
||||||
|
|
||||||
resultMediaTracks = resultMediaDescriptor.getAllTrackDescriptors()
|
# num tracks differ
|
||||||
|
rmd.applySourceIndices(sourceMediaDescriptor)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
for assertIndex in range(len(assertSelectorList)):
|
for assertIndex in range(len(assertSelectorList)):
|
||||||
|
|
||||||
@@ -125,17 +257,17 @@ class Scenario4(Scenario):
|
|||||||
assertFunc = assertFuncList[assertIndex]
|
assertFunc = assertFuncList[assertIndex]
|
||||||
assertVariant = variantList[assertIndex]
|
assertVariant = variantList[assertIndex]
|
||||||
|
|
||||||
if assertSelector == 'M':
|
# if assertSelector == 'M':
|
||||||
assertFunc()
|
# assertFunc()
|
||||||
for variantIndex in range(len(assertVariant)):
|
# for variantIndex in range(len(assertVariant)):
|
||||||
assert (assertVariant[variantIndex].lower() == resultMediaTracks[variantIndex].getType().indicator()
|
# assert (assertVariant[variantIndex].lower() == rmd.getType().indicator()
|
||||||
), f"Stream #{variantIndex} is not of type {resultMediaTracks[variantIndex].getType().label()}"
|
# ), f"Stream #{variantIndex} is not of type {rmd.getType().label()}"
|
||||||
|
#
|
||||||
elif assertSelector == 'AD' or assertSelector == 'AT':
|
if assertSelector == 'AD' or assertSelector == 'AT':
|
||||||
assertFunc({'tracks': resultMediaDescriptor.getAudioTracks()})
|
assertFunc({'tracks': rmd.getAudioTracks()})
|
||||||
|
|
||||||
elif assertSelector == 'SD' or assertSelector == 'ST':
|
elif assertSelector == 'SD' or assertSelector == 'ST':
|
||||||
assertFunc({'tracks': resultMediaDescriptor.getSubtitleTracks()})
|
assertFunc({'tracks': rmd.getSubtitleTracks()})
|
||||||
|
|
||||||
elif type(assertSelector) is str:
|
elif type(assertSelector) is str:
|
||||||
if assertSelector == 'J':
|
if assertSelector == 'J':
|
||||||
@@ -148,9 +280,13 @@ class Scenario4(Scenario):
|
|||||||
|
|
||||||
self._reportLogger.error(f"{variantLabel}: Test FAILED ({ae})")
|
self._reportLogger.error(f"{variantLabel}: Test FAILED ({ae})")
|
||||||
|
|
||||||
|
exit()
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
MC_list = MediaCombinator.getAllClassReferences()
|
|
||||||
|
|
||||||
|
|
||||||
|
MC_list = [MediaCombinator.getClassReference(6)]
|
||||||
for MC in MC_list:
|
for MC in MC_list:
|
||||||
self._logger.debug(f"MC={MC.__name__}")
|
self._logger.debug(f"MC={MC.__name__}")
|
||||||
mc = MC(context = self._context, createPresets = True)
|
mc = MC(context = self._context, createPresets = True)
|
||||||
|
|||||||
@@ -1,19 +1,56 @@
|
|||||||
import os, click, requests, json
|
import os, click, requests, json, time, logging
|
||||||
|
|
||||||
|
class TMDB_REQUEST_EXCEPTION(Exception):
|
||||||
|
def __init__(self, statusCode, statusMessage):
|
||||||
|
errorMessage = f"TMDB query failed with status code {statusCode}: {statusMessage}"
|
||||||
|
super().__init__(errorMessage)
|
||||||
|
|
||||||
|
class TMDB_API_KEY_NOT_PRESENT_EXCEPTION(Exception):
|
||||||
|
def __str__(self):
|
||||||
|
return 'TMDB api key is not available, please set environment variable TMDB_API_KEY'
|
||||||
|
|
||||||
|
class TMDB_EXCESSIVE_USAGE_EXCEPTION(Exception):
|
||||||
|
def __str__(self):
|
||||||
|
return 'Rate limit was triggered too often'
|
||||||
|
|
||||||
|
|
||||||
class TmdbController():
|
class TmdbController():
|
||||||
|
|
||||||
DEFAULT_LANGUAGE = 'de-DE'
|
DEFAULT_LANGUAGE = 'de-DE'
|
||||||
|
|
||||||
def __init__(self):
|
RATE_LIMIT_WAIT_SECONDS = 10
|
||||||
|
RATE_LIMIT_RETRIES = 3
|
||||||
|
|
||||||
try:
|
def __init__(self, context = None):
|
||||||
self.__tmdbApiKey = os.environ['TMDB_API_KEY']
|
self.__context = context
|
||||||
except KeyError:
|
self.__logger = (context['logger'] if context is not None and 'logger' in context.keys()
|
||||||
raise click.ClickException('TMDB api key is not available, please set environment variable TMDB_API_KEY')
|
else logging.getLogger('FFX').addHandler(logging.NullHandler()))
|
||||||
|
|
||||||
|
self.__tmdbApiKey = os.environ.get('TMDB_API_KEY', None)
|
||||||
|
if self.__tmdbApiKey is None:
|
||||||
|
raise TMDB_API_KEY_NOT_PRESENT_EXCEPTION
|
||||||
|
|
||||||
self.tmdbLanguage = TmdbController.DEFAULT_LANGUAGE
|
self.tmdbLanguage = TmdbController.DEFAULT_LANGUAGE
|
||||||
|
|
||||||
|
|
||||||
|
def getTmdbRequest(self, tmdbUrl):
|
||||||
|
retries = TmdbController.RATE_LIMIT_RETRIES
|
||||||
|
while True:
|
||||||
|
response = requests.get(tmdbUrl)
|
||||||
|
if response.status_code == 429:
|
||||||
|
if not retries:
|
||||||
|
raise TMDB_EXCESSIVE_USAGE_EXCEPTION()
|
||||||
|
self.__logger.warning('TMDB Rate limit (status_code 429)')
|
||||||
|
time.sleep(TmdbController.RATE_LIMIT_WAIT_SECONDS)
|
||||||
|
retries -= 1
|
||||||
|
else:
|
||||||
|
jsonResult = response.json()
|
||||||
|
if ('success' in jsonResult.keys()
|
||||||
|
and not jsonResult['success']):
|
||||||
|
raise TMDB_REQUEST_EXCEPTION(jsonResult['status_code'], jsonResult['status_message'])
|
||||||
|
return jsonResult
|
||||||
|
|
||||||
|
|
||||||
def queryShow(self, showId):
|
def queryShow(self, showId):
|
||||||
"""
|
"""
|
||||||
First level keys in the response object:
|
First level keys in the response object:
|
||||||
@@ -55,22 +92,8 @@ class TmdbController():
|
|||||||
|
|
||||||
tmdbUrl = f"https://api.themoviedb.org/3/tv/{showId}{urlParams}"
|
tmdbUrl = f"https://api.themoviedb.org/3/tv/{showId}{urlParams}"
|
||||||
|
|
||||||
#TODO Check for result
|
return self.getTmdbRequest(tmdbUrl)
|
||||||
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()
|
|
||||||
except:
|
|
||||||
return {}
|
|
||||||
|
|
||||||
def queryEpisode(self, showId, season, episode):
|
def queryEpisode(self, showId, season, episode):
|
||||||
"""
|
"""
|
||||||
@@ -94,14 +117,11 @@ class TmdbController():
|
|||||||
|
|
||||||
tmdbUrl = f"https://api.themoviedb.org/3/tv/{showId}/season/{season}/episode/{episode}{urlParams}"
|
tmdbUrl = f"https://api.themoviedb.org/3/tv/{showId}/season/{season}/episode/{episode}{urlParams}"
|
||||||
|
|
||||||
#TODO Check for result
|
return self.getTmdbRequest(tmdbUrl)
|
||||||
try:
|
|
||||||
return requests.get(tmdbUrl).json()
|
|
||||||
except:
|
|
||||||
return {}
|
|
||||||
|
|
||||||
def getEpisodeFileBasename(self,
|
|
||||||
showName,
|
@staticmethod
|
||||||
|
def getEpisodeFileBasename(showName,
|
||||||
episodeName,
|
episodeName,
|
||||||
season,
|
season,
|
||||||
episode,
|
episode,
|
||||||
|
|||||||
@@ -5,25 +5,25 @@ from enum import Enum
|
|||||||
|
|
||||||
class TrackDisposition(Enum):
|
class TrackDisposition(Enum):
|
||||||
|
|
||||||
DEFAULT = {"name": "default", "index": 0}
|
DEFAULT = {"name": "default", "index": 0, "indicator": "DE"}
|
||||||
FORCED = {"name": "forced", "index": 1}
|
FORCED = {"name": "forced", "index": 1, "indicator": "FO"}
|
||||||
|
|
||||||
DUB = {"name": "dub", "index": 2}
|
DUB = {"name": "dub", "index": 2, "indicator": "DB"}
|
||||||
ORIGINAL = {"name": "original", "index": 3}
|
ORIGINAL = {"name": "original", "index": 3, "indicator": "OG"}
|
||||||
COMMENT = {"name": "comment", "index": 4}
|
COMMENT = {"name": "comment", "index": 4, "indicator": "CM"}
|
||||||
LYRICS = {"name": "lyrics", "index": 5}
|
LYRICS = {"name": "lyrics", "index": 5, "indicator": "LY"}
|
||||||
KARAOKE = {"name": "karaoke", "index": 6}
|
KARAOKE = {"name": "karaoke", "index": 6, "indicator": "KA"}
|
||||||
HEARING_IMPAIRED = {"name": "hearing_impaired", "index": 7}
|
HEARING_IMPAIRED = {"name": "hearing_impaired", "index": 7, "indicator": "HI"}
|
||||||
VISUAL_IMPAIRED = {"name": "visual_impaired", "index": 8}
|
VISUAL_IMPAIRED = {"name": "visual_impaired", "index": 8, "indicator": "VI"}
|
||||||
CLEAN_EFFECTS = {"name": "clean_effects", "index": 9}
|
CLEAN_EFFECTS = {"name": "clean_effects", "index": 9, "indicator": "CE"}
|
||||||
ATTACHED_PIC = {"name": "attached_pic", "index": 10}
|
ATTACHED_PIC = {"name": "attached_pic", "index": 10, "indicator": "AP"}
|
||||||
TIMED_THUMBNAILS = {"name": "timed_thumbnails", "index": 11}
|
TIMED_THUMBNAILS = {"name": "timed_thumbnails", "index": 11, "indicator": "TT"}
|
||||||
NON_DIEGETICS = {"name": "non_diegetic", "index": 12}
|
NON_DIEGETICS = {"name": "non_diegetic", "index": 12, "indicator": "ND"}
|
||||||
CAPTIONS = {"name": "captions", "index": 13}
|
CAPTIONS = {"name": "captions", "index": 13, "indicator": "CA"}
|
||||||
DESCRIPTIONS = {"name": "descriptions", "index": 14}
|
DESCRIPTIONS = {"name": "descriptions", "index": 14, "indicator": "DS"}
|
||||||
METADATA = {"name": "metadata", "index": 15}
|
METADATA = {"name": "metadata", "index": 15, "indicator": "MD"}
|
||||||
DEPENDENT = {"name": "dependent", "index": 16}
|
DEPENDENT = {"name": "dependent", "index": 16, "indicator": "DP"}
|
||||||
STILL_IMAGE = {"name": "still_image", "index": 17}
|
STILL_IMAGE = {"name": "still_image", "index": 17, "indicator": "SI"}
|
||||||
|
|
||||||
|
|
||||||
def label(self):
|
def label(self):
|
||||||
@@ -32,6 +32,9 @@ class TrackDisposition(Enum):
|
|||||||
def index(self):
|
def index(self):
|
||||||
return int(self.value['index'])
|
return int(self.value['index'])
|
||||||
|
|
||||||
|
def indicator(self):
|
||||||
|
return str(self.value['indicator'])
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def toFlags(dispositionSet):
|
def toFlags(dispositionSet):
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ from ffx.database import databaseContext
|
|||||||
from ffx.test.helper import createMediaTestFile
|
from ffx.test.helper import createMediaTestFile
|
||||||
|
|
||||||
from ffx.test.scenario import Scenario
|
from ffx.test.scenario import Scenario
|
||||||
|
from ffx.tmdb_controller import TMDB_API_KEY_NOT_PRESENT_EXCEPTION
|
||||||
|
|
||||||
|
|
||||||
@click.group()
|
@click.group()
|
||||||
@@ -20,7 +21,7 @@ def ffx(ctx, verbose, dry_run):
|
|||||||
"""FFX"""
|
"""FFX"""
|
||||||
|
|
||||||
ctx.obj = {}
|
ctx.obj = {}
|
||||||
ctx.obj['database'] = databaseContext(databasePath=None)
|
ctx.obj['database'] = None
|
||||||
ctx.obj['dry_run'] = dry_run
|
ctx.obj['dry_run'] = dry_run
|
||||||
|
|
||||||
ctx.obj['verbosity'] = verbose
|
ctx.obj['verbosity'] = verbose
|
||||||
@@ -85,7 +86,9 @@ def run(ctx, scenario, variant):
|
|||||||
|
|
||||||
for si in Scenario.list():
|
for si in Scenario.list():
|
||||||
|
|
||||||
scen = Scenario.getClassReference(si)(ctx.obj)
|
try:
|
||||||
|
SCEN = Scenario.getClassReference(si)
|
||||||
|
scen = SCEN(ctx.obj)
|
||||||
|
|
||||||
if scenario and scenario != scen.getScenario():
|
if scenario and scenario != scen.getScenario():
|
||||||
continue
|
continue
|
||||||
@@ -94,6 +97,9 @@ def run(ctx, scenario, variant):
|
|||||||
|
|
||||||
scen.run()
|
scen.run()
|
||||||
|
|
||||||
|
except TMDB_API_KEY_NOT_PRESENT_EXCEPTION:
|
||||||
|
ctx.obj['logger'].info(f"TMDB_API_KEY not set: Skipping {SCEN.__class__.__name__}")
|
||||||
|
|
||||||
|
|
||||||
@ffx.command()
|
@ffx.command()
|
||||||
@click.pass_context
|
@click.pass_context
|
||||||
|
|||||||
Reference in New Issue
Block a user