|
|
|
@ -4,6 +4,8 @@ from .scenario import Scenario
|
|
|
|
|
|
|
|
|
|
from ffx.test.helper import createMediaTestFile
|
|
|
|
|
from ffx.process import executeProcess
|
|
|
|
|
from ffx.database import databaseContext
|
|
|
|
|
from ffx.test.helper import createEmptyDirectory
|
|
|
|
|
|
|
|
|
|
from ffx.file_properties import FileProperties
|
|
|
|
|
|
|
|
|
@ -14,18 +16,98 @@ from ffx.track_type import TrackType
|
|
|
|
|
from ffx.track_disposition import TrackDisposition
|
|
|
|
|
|
|
|
|
|
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):
|
|
|
|
|
|
|
|
|
|
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):
|
|
|
|
|
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):
|
|
|
|
|
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):
|
|
|
|
|
|
|
|
|
@ -44,11 +126,11 @@ class Scenario4(Scenario):
|
|
|
|
|
variantLabel = f"{self.__class__.__name__} Variant {variantIdentifier}"
|
|
|
|
|
|
|
|
|
|
sourceMediaDescriptor: MediaDescriptor = targetYieldObj['payload']
|
|
|
|
|
#presetMediaDescriptor: MediaDescriptor = targetYieldObj['payload']['preset']
|
|
|
|
|
presetMediaDescriptor: MediaDescriptor = presetYieldObj['payload']
|
|
|
|
|
|
|
|
|
|
assertSelectorList: list = targetYieldObj['assertSelectors']
|
|
|
|
|
assertFuncList = targetYieldObj['assertFuncs']
|
|
|
|
|
shouldFail = targetYieldObj['shouldFail']
|
|
|
|
|
assertSelectorList: list = presetYieldObj['assertSelectors']
|
|
|
|
|
assertFuncList = presetYieldObj['assertFuncs']
|
|
|
|
|
shouldFail = presetYieldObj['shouldFail']
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
jellyfinSelectorIndex = assertSelectorList.index('J')
|
|
|
|
@ -61,63 +143,113 @@ class Scenario4(Scenario):
|
|
|
|
|
if self._context['test_variant'] and variantIdentifier != self._context['test_variant']:
|
|
|
|
|
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}")
|
|
|
|
|
|
|
|
|
|
# Phase 1: Setup source files
|
|
|
|
|
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
|
|
|
|
|
commandSequence = [sys.executable,
|
|
|
|
|
self._ffxExecutablePath,
|
|
|
|
|
'convert',
|
|
|
|
|
mediaFilePath,
|
|
|
|
|
'--no-prompt']
|
|
|
|
|
'--database-file',
|
|
|
|
|
self.__testDbFilePath,
|
|
|
|
|
'convert']
|
|
|
|
|
commandSequence += [tfo['filename'] for tfo in testFileList]
|
|
|
|
|
|
|
|
|
|
commandSequence += ['--no-prompt']
|
|
|
|
|
|
|
|
|
|
if not testContext['use_jellyfin']:
|
|
|
|
|
commandSequence += ['--no-jellyfin']
|
|
|
|
|
# if not testContext['use_jellyfin']:
|
|
|
|
|
# commandSequence += ['--no-jellyfin']
|
|
|
|
|
|
|
|
|
|
self._logger.debug(f"{variantLabel}: Test sequence: {commandSequence}")
|
|
|
|
|
|
|
|
|
|
out, err, rc = executeProcess(commandSequence, directory = self._testDirectory)
|
|
|
|
|
|
|
|
|
|
if out:
|
|
|
|
|
self._logger.debug(f"{variantLabel}: Process output: {out}")
|
|
|
|
|
# if out:
|
|
|
|
|
# self._logger.debug(f"{variantLabel}: Process output: {out}")
|
|
|
|
|
if rc:
|
|
|
|
|
self._logger.debug(f"{variantLabel}: Process returned ERROR {rc} ({err})")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 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:
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
), f"Process {'failed' if jobFailed else 'did not fail'}"
|
|
|
|
|
tmdbEpisodeResult = self.__tc.queryEpisode(Scenario4.TEST_SHOW_IDENTIFIER,
|
|
|
|
|
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)
|
|
|
|
|
), f"Result file 'media.webm' in path '{self._testDirectory}' wasn't created"
|
|
|
|
|
assert (os.path.isfile(expectedFilePath)
|
|
|
|
|
), 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']:
|
|
|
|
|
sourceMediaDescriptor.applyJellyfinOrder()
|
|
|
|
|
resultMediaDescriptor.applySourceIndices(sourceMediaDescriptor)
|
|
|
|
|
|
|
|
|
|
resultMediaTracks = resultMediaDescriptor.getAllTrackDescriptors()
|
|
|
|
|
# num tracks differ
|
|
|
|
|
rmd.applySourceIndices(sourceMediaDescriptor)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for assertIndex in range(len(assertSelectorList)):
|
|
|
|
|
|
|
|
|
@ -125,17 +257,17 @@ class Scenario4(Scenario):
|
|
|
|
|
assertFunc = assertFuncList[assertIndex]
|
|
|
|
|
assertVariant = variantList[assertIndex]
|
|
|
|
|
|
|
|
|
|
if assertSelector == 'M':
|
|
|
|
|
assertFunc()
|
|
|
|
|
for variantIndex in range(len(assertVariant)):
|
|
|
|
|
assert (assertVariant[variantIndex].lower() == resultMediaTracks[variantIndex].getType().indicator()
|
|
|
|
|
), f"Stream #{variantIndex} is not of type {resultMediaTracks[variantIndex].getType().label()}"
|
|
|
|
|
|
|
|
|
|
elif assertSelector == 'AD' or assertSelector == 'AT':
|
|
|
|
|
assertFunc({'tracks': resultMediaDescriptor.getAudioTracks()})
|
|
|
|
|
# if assertSelector == 'M':
|
|
|
|
|
# assertFunc()
|
|
|
|
|
# for variantIndex in range(len(assertVariant)):
|
|
|
|
|
# assert (assertVariant[variantIndex].lower() == rmd.getType().indicator()
|
|
|
|
|
# ), f"Stream #{variantIndex} is not of type {rmd.getType().label()}"
|
|
|
|
|
#
|
|
|
|
|
if assertSelector == 'AD' or assertSelector == 'AT':
|
|
|
|
|
assertFunc({'tracks': rmd.getAudioTracks()})
|
|
|
|
|
|
|
|
|
|
elif assertSelector == 'SD' or assertSelector == 'ST':
|
|
|
|
|
assertFunc({'tracks': resultMediaDescriptor.getSubtitleTracks()})
|
|
|
|
|
assertFunc({'tracks': rmd.getSubtitleTracks()})
|
|
|
|
|
|
|
|
|
|
elif type(assertSelector) is str:
|
|
|
|
|
if assertSelector == 'J':
|
|
|
|
@ -148,9 +280,13 @@ class Scenario4(Scenario):
|
|
|
|
|
|
|
|
|
|
self._reportLogger.error(f"{variantLabel}: Test FAILED ({ae})")
|
|
|
|
|
|
|
|
|
|
exit()
|
|
|
|
|
|
|
|
|
|
def run(self):
|
|
|
|
|
MC_list = MediaCombinator.getAllClassReferences()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
MC_list = [MediaCombinator.getClassReference(6)]
|
|
|
|
|
for MC in MC_list:
|
|
|
|
|
self._logger.debug(f"MC={MC.__name__}")
|
|
|
|
|
mc = MC(context = self._context, createPresets = True)
|
|
|
|
|