#387 Suffices
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -2,3 +2,5 @@ __pycache__
|
|||||||
junk/
|
junk/
|
||||||
.vscode
|
.vscode
|
||||||
.ipynb_checkpoints/
|
.ipynb_checkpoints/
|
||||||
|
ansible/inventory/hawaii.yml
|
||||||
|
ansible/inventory/peppermint.yml
|
||||||
|
|||||||
8
ansible/inventory/ffx.yml
Normal file
8
ansible/inventory/ffx.yml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
all:
|
||||||
|
hosts:
|
||||||
|
ffx:
|
||||||
|
ansible_host: <domain>
|
||||||
|
ansible_user: <system user>
|
||||||
|
|
||||||
|
ffxSystemUsername: <system user>
|
||||||
|
ffxHomeDirectory: <home directory>
|
||||||
113
ansible/setup_node.yml
Normal file
113
ansible/setup_node.yml
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
- name: Setup FFX node
|
||||||
|
hosts: all
|
||||||
|
vars:
|
||||||
|
ffxRepoUrl: https://gitea.maveno.de/Javanaut/ffx.git
|
||||||
|
tasks:
|
||||||
|
|
||||||
|
- name: Update system and install packages
|
||||||
|
become: true
|
||||||
|
ansible.builtin.apt:
|
||||||
|
name:
|
||||||
|
- python3-virtualenv
|
||||||
|
- ffmpeg
|
||||||
|
- git
|
||||||
|
- screen
|
||||||
|
update_cache: yes
|
||||||
|
|
||||||
|
- name: Create sync dir
|
||||||
|
become: true
|
||||||
|
file:
|
||||||
|
path: "{{ ffxHomeDirectory }}/.local/var/sync/ffx"
|
||||||
|
state: directory
|
||||||
|
owner: "{{ ffxSystemUsername }}"
|
||||||
|
group: "{{ ffxSystemUsername }}"
|
||||||
|
mode: 0755
|
||||||
|
|
||||||
|
- name: Ensure local etc directory
|
||||||
|
become: true
|
||||||
|
file:
|
||||||
|
path: "{{ ffxHomeDirectory }}/.local/etc"
|
||||||
|
state: directory
|
||||||
|
owner: "{{ ffxSystemUsername }}"
|
||||||
|
group: "{{ ffxSystemUsername }}"
|
||||||
|
mode: 0755
|
||||||
|
|
||||||
|
- name: Ensure local src directory
|
||||||
|
become: true
|
||||||
|
file:
|
||||||
|
path: "{{ ffxHomeDirectory }}/.local/src"
|
||||||
|
state: directory
|
||||||
|
owner: "{{ ffxSystemUsername }}"
|
||||||
|
group: "{{ ffxSystemUsername }}"
|
||||||
|
mode: 0755
|
||||||
|
|
||||||
|
- name: Ensure local share directory
|
||||||
|
become: true
|
||||||
|
file:
|
||||||
|
path: "{{ ffxHomeDirectory }}/.local/share"
|
||||||
|
state: directory
|
||||||
|
owner: "{{ ffxSystemUsername }}"
|
||||||
|
group: "{{ ffxSystemUsername }}"
|
||||||
|
mode: 0755
|
||||||
|
|
||||||
|
- name: Prepare ffx virtualenv
|
||||||
|
become: true
|
||||||
|
become_user: "{{ ffxSystemUsername }}"
|
||||||
|
ansible.builtin.pip:
|
||||||
|
name:
|
||||||
|
- click
|
||||||
|
- textual
|
||||||
|
- sqlalchemy
|
||||||
|
- requests
|
||||||
|
virtualenv: "{{ ffxHomeDirectory }}/.local/share/ffx.venv"
|
||||||
|
|
||||||
|
- name: Clone ffx repository
|
||||||
|
become: true
|
||||||
|
become_user: "{{ ffxSystemUsername }}"
|
||||||
|
ansible.builtin.git:
|
||||||
|
repo: "{{ ffxRepoUrl }}"
|
||||||
|
dest: "{{ ffxHomeDirectory }}/.local/src/ffx"
|
||||||
|
version: dev
|
||||||
|
|
||||||
|
|
||||||
|
- name: Add TMDB API token placeholer to .bashrc
|
||||||
|
become: true
|
||||||
|
become_user: "{{ ffxSystemUsername }}"
|
||||||
|
ansible.builtin.lineinfile:
|
||||||
|
path: "{{ ffxHomeDirectory }}/.bashrc"
|
||||||
|
insertbefore: BOF
|
||||||
|
line: >-
|
||||||
|
export TMDB_API_KEY="<TMDB API token>"
|
||||||
|
|
||||||
|
- name: Add ffx alias to .bashrc
|
||||||
|
become: true
|
||||||
|
become_user: "{{ ffxSystemUsername }}"
|
||||||
|
ansible.builtin.lineinfile:
|
||||||
|
path: "{{ ffxHomeDirectory }}/.bashrc"
|
||||||
|
insertbefore: BOF
|
||||||
|
line: >-
|
||||||
|
alias ffx="{{ ffxHomeDirectory }}/.local/share/ffx.venv/bin/python
|
||||||
|
{{ ffxHomeDirectory }}/.local/src/ffx/bin/ffx.py"
|
||||||
|
|
||||||
|
|
||||||
|
- name: Ensure local sync directory
|
||||||
|
become: true
|
||||||
|
file:
|
||||||
|
path: "{{ ffxHomeDirectory }}/.local/var/sync/ffx"
|
||||||
|
state: directory
|
||||||
|
owner: "{{ ffxSystemUsername }}"
|
||||||
|
group: "{{ ffxSystemUsername }}"
|
||||||
|
mode: 0755
|
||||||
|
|
||||||
|
- name: Create ffx config file
|
||||||
|
become: true
|
||||||
|
become_user: "{{ ffxSystemUsername }}"
|
||||||
|
vars:
|
||||||
|
ffxConfiguration:
|
||||||
|
databasePath: "{{ ffxHomeDirectory }}/.local/var/sync/ffx/ffx.db"
|
||||||
|
ansible.builtin.copy:
|
||||||
|
content: "{{ ffxConfiguration | to_json }}"
|
||||||
|
dest: "{{ ffxHomeDirectory }}/.local/etc/ffx.json"
|
||||||
|
owner: "{{ ffxSystemUsername }}"
|
||||||
|
group: "{{ ffxSystemUsername }}"
|
||||||
|
mode: 0644
|
||||||
79
bin/ffx.py
79
bin/ffx.py
@@ -374,6 +374,10 @@ def convert(ctx,
|
|||||||
|
|
||||||
context['video_encoder'] = VideoEncoder.fromLabel(video_encoder)
|
context['video_encoder'] = VideoEncoder.fromLabel(video_encoder)
|
||||||
|
|
||||||
|
targetFormat = FfxController.DEFAULT_FILE_FORMAT
|
||||||
|
targetExtension = FfxController.DEFAULT_FILE_EXTENSION
|
||||||
|
|
||||||
|
|
||||||
#TODO: #407 Without effect -> remove
|
#TODO: #407 Without effect -> remove
|
||||||
context['use_jellyfin'] = False
|
context['use_jellyfin'] = False
|
||||||
|
|
||||||
@@ -510,14 +514,24 @@ def convert(ctx,
|
|||||||
|
|
||||||
ctx.obj['logger'].info(f"\nProcessing file {sourcePath}")
|
ctx.obj['logger'].info(f"\nProcessing file {sourcePath}")
|
||||||
|
|
||||||
|
targetSuffices = {}
|
||||||
|
|
||||||
|
|
||||||
mediaFileProperties = FileProperties(context, sourceFilename)
|
mediaFileProperties = FileProperties(context, sourceFilename)
|
||||||
|
|
||||||
|
|
||||||
#HINT: -1 if not set
|
#HINT: -1 if not set
|
||||||
showSeason = (cliOverrides['tmdb']['season'] if 'tmdb' in cliOverrides.keys()
|
if 'tmdb' in cliOverrides.keys() and 'season' in cliOverrides['tmdb']:
|
||||||
and 'season' in cliOverrides['tmdb'] else mediaFileProperties.getSeason())
|
showSeason = cliOverrides['tmdb']['season']
|
||||||
showEpisode = (cliOverrides['tmdb']['episode'] if 'tmdb' in cliOverrides.keys()
|
else:
|
||||||
and 'episode' in cliOverrides['tmdb'] else mediaFileProperties.getEpisode())
|
showSeason = mediaFileProperties.getSeason()
|
||||||
|
|
||||||
|
if 'tmdb' in cliOverrides.keys() and 'episode' in cliOverrides['tmdb']:
|
||||||
|
showEpisode = cliOverrides['tmdb']['episode']
|
||||||
|
else:
|
||||||
|
showEpisode = mediaFileProperties.getEpisode()
|
||||||
|
|
||||||
|
|
||||||
ctx.obj['logger'].debug(f"Season={showSeason} Episode={showEpisode}")
|
ctx.obj['logger'].debug(f"Season={showSeason} Episode={showEpisode}")
|
||||||
|
|
||||||
|
|
||||||
@@ -568,6 +582,12 @@ def convert(ctx,
|
|||||||
fc = FfxController(context, targetMediaDescriptor, sourceMediaDescriptor)
|
fc = FfxController(context, targetMediaDescriptor, sourceMediaDescriptor)
|
||||||
|
|
||||||
|
|
||||||
|
indexSeasonDigits = currentShowDescriptor.getIndexSeasonDigits() if not currentPattern is None else ShowDescriptor.DEFAULT_INDEX_SEASON_DIGITS
|
||||||
|
indexEpisodeDigits = currentShowDescriptor.getIndexEpisodeDigits() if not currentPattern is None else ShowDescriptor.DEFAULT_INDEX_EPISODE_DIGITS
|
||||||
|
indicatorSeasonDigits = currentShowDescriptor.getIndicatorSeasonDigits() if not currentPattern is None else ShowDescriptor.DEFAULT_INDICATOR_SEASON_DIGITS
|
||||||
|
indicatorEpisodeDigits = currentShowDescriptor.getIndicatorEpisodeDigits() if not currentPattern is None else ShowDescriptor.DEFAULT_INDICATOR_EPISODE_DIGITS
|
||||||
|
|
||||||
|
|
||||||
# Assemble target filename accordingly depending on TMDB lookup is enabled
|
# Assemble target filename accordingly depending on TMDB lookup is enabled
|
||||||
#HINT: -1 if not set
|
#HINT: -1 if not set
|
||||||
showId = cliOverrides['tmdb']['show'] if 'tmdb' in cliOverrides.keys() and 'show' in cliOverrides['tmdb'] else (-1 if currentShowDescriptor is None else currentShowDescriptor.getId())
|
showId = cliOverrides['tmdb']['show'] if 'tmdb' in cliOverrides.keys() and 'show' in cliOverrides['tmdb'] else (-1 if currentShowDescriptor is None else currentShowDescriptor.getId())
|
||||||
@@ -580,16 +600,8 @@ def convert(ctx,
|
|||||||
sName, showYear = tc.getShowNameAndYear(showId)
|
sName, showYear = tc.getShowNameAndYear(showId)
|
||||||
showName = filterFilename(sName)
|
showName = filterFilename(sName)
|
||||||
showFilenamePrefix = f"{showName} ({str(showYear)})"
|
showFilenamePrefix = f"{showName} ({str(showYear)})"
|
||||||
indexSeasonDigits = ShowDescriptor.DEFAULT_INDEX_SEASON_DIGITS
|
|
||||||
indexEpisodeDigits = ShowDescriptor.DEFAULT_INDEX_EPISODE_DIGITS
|
|
||||||
indicatorSeasonDigits = ShowDescriptor.DEFAULT_INDICATOR_SEASON_DIGITS
|
|
||||||
indicatorEpisodeDigits = ShowDescriptor.DEFAULT_INDICATOR_EPISODE_DIGITS
|
|
||||||
else:
|
else:
|
||||||
showFilenamePrefix = currentShowDescriptor.getFilenamePrefix()
|
showFilenamePrefix = currentShowDescriptor.getFilenamePrefix()
|
||||||
indexSeasonDigits = currentShowDescriptor.getIndexSeasonDigits()
|
|
||||||
indexEpisodeDigits = currentShowDescriptor.getIndexEpisodeDigits()
|
|
||||||
indicatorSeasonDigits = currentShowDescriptor.getIndicatorSeasonDigits()
|
|
||||||
indicatorEpisodeDigits = currentShowDescriptor.getIndicatorEpisodeDigits()
|
|
||||||
|
|
||||||
tmdbEpisodeResult = tc.queryEpisode(showId, showSeason, showEpisode)
|
tmdbEpisodeResult = tc.queryEpisode(showId, showSeason, showEpisode)
|
||||||
|
|
||||||
@@ -607,34 +619,59 @@ def convert(ctx,
|
|||||||
indicatorEpisodeDigits)
|
indicatorEpisodeDigits)
|
||||||
|
|
||||||
|
|
||||||
|
if label:
|
||||||
|
if showSeason > -1 and showEpisode > -1:
|
||||||
|
targetSuffices['se'] = f"S{showSeason:0{indicatorSeasonDigits}d}E{showEpisode:0{indicatorEpisodeDigits}d}"
|
||||||
|
elif showEpisode > -1:
|
||||||
|
targetSuffices['se'] = f"E{showEpisode:0{indicatorEpisodeDigits}d}"
|
||||||
|
else:
|
||||||
|
if 'se' in targetSuffices.keys():
|
||||||
|
del targetSuffices['se']
|
||||||
|
|
||||||
|
|
||||||
ctx.obj['logger'].debug(f"fileBasename={sourceFileBasename}")
|
ctx.obj['logger'].debug(f"fileBasename={sourceFileBasename}")
|
||||||
|
|
||||||
|
|
||||||
for q in q_list:
|
for q in q_list:
|
||||||
|
|
||||||
|
if len(q_list) > 1:
|
||||||
|
targetSuffices['q'] = f"q{q}"
|
||||||
|
|
||||||
ctx.obj['logger'].debug(f"\nRunning job {jobIndex} file={sourcePath} q={q}")
|
ctx.obj['logger'].debug(f"\nRunning job {jobIndex} file={sourcePath} q={q}")
|
||||||
jobIndex += 1
|
jobIndex += 1
|
||||||
|
|
||||||
extra = ['ffx'] if sourceFilenameExtension == FfxController.DEFAULT_FILE_EXTENSION else []
|
|
||||||
|
|
||||||
ctx.obj['logger'].debug(f"label={label if label else 'Falsy'}")
|
ctx.obj['logger'].debug(f"label={label if label else 'Falsy'}")
|
||||||
ctx.obj['logger'].debug(f"sourceFileBasename={sourceFileBasename}")
|
ctx.obj['logger'].debug(f"sourceFileBasename={sourceFileBasename}")
|
||||||
|
|
||||||
targetFileBasename = mediaFileProperties.assembleTargetFileBasename(label,
|
# targetFileBasename = mediaFileProperties.assembleTargetFileBasename(label,
|
||||||
q if len(q_list) > 1 else -1,
|
# q if len(q_list) > 1 else -1,
|
||||||
extraTokens = extra)
|
#
|
||||||
|
targetFileBasename = sourceFileBasename if context['use_tmdb'] and not label else label
|
||||||
|
|
||||||
|
|
||||||
|
targetFilenameTokens = [targetFileBasename]
|
||||||
|
|
||||||
|
if 'se' in targetSuffices.keys():
|
||||||
|
targetFilenameTokens += [targetSuffices['se']]
|
||||||
|
|
||||||
|
if 'q' in targetSuffices.keys():
|
||||||
|
targetFilenameTokens += [targetSuffices['q']]
|
||||||
|
|
||||||
|
|
||||||
#TODO #387
|
#TODO #387
|
||||||
targetFilename = ((f"{sourceFileBasename}_q{q}" if len(q_list) > 1 else sourceFileBasename)
|
# targetFilename = ((f"{sourceFileBasename}_q{q}" if len(q_list) > 1 else sourceFileBasename)
|
||||||
if context['use_tmdb'] else targetFileBasename)
|
# if context['use_tmdb'] else targetFileBasename)
|
||||||
|
|
||||||
|
targetFilename = f"{'_'.join(targetFilenameTokens)}.{targetExtension}"
|
||||||
|
|
||||||
targetPath = os.path.join(output_directory if output_directory else sourceDirectory, targetFilename)
|
targetPath = os.path.join(output_directory if output_directory else sourceDirectory, targetFilename)
|
||||||
|
|
||||||
#TODO: target extension anpassen
|
#TODO: target extension anpassen
|
||||||
ctx.obj['logger'].info(f"Creating file {targetFilename}.webm")
|
ctx.obj['logger'].info(f"Creating file {targetFilename}")
|
||||||
|
|
||||||
fc.runJob(sourcePath,
|
fc.runJob(sourcePath,
|
||||||
targetPath,
|
targetPath,
|
||||||
|
targetFormat,
|
||||||
context['video_encoder'],
|
context['video_encoder'],
|
||||||
q,
|
q,
|
||||||
preset)
|
preset)
|
||||||
|
|||||||
@@ -112,9 +112,12 @@ class FfxController():
|
|||||||
return ['-ss', str(cropStart), '-t', str(cropLength)]
|
return ['-ss', str(cropStart), '-t', str(cropLength)]
|
||||||
|
|
||||||
|
|
||||||
def generateOutputTokens(self, filepath, format, ext):
|
def generateOutputTokens(self, filePathBase, format = '', ext = ''):
|
||||||
outputFilePath = f"{filepath}.{ext}"
|
outputFilePath = f"{filePathBase}{'.'+str(ext) if ext else ''}"
|
||||||
return ['-f', format, outputFilePath]
|
if format:
|
||||||
|
return ['-f', format, outputFilePath]
|
||||||
|
else:
|
||||||
|
return [outputFilePath]
|
||||||
|
|
||||||
|
|
||||||
def generateAudioEncodingTokens(self):
|
def generateAudioEncodingTokens(self):
|
||||||
@@ -245,6 +248,7 @@ class FfxController():
|
|||||||
def runJob(self,
|
def runJob(self,
|
||||||
sourcePath,
|
sourcePath,
|
||||||
targetPath,
|
targetPath,
|
||||||
|
targetFormat: str = '',
|
||||||
videoEncoder: VideoEncoder = VideoEncoder.VP9,
|
videoEncoder: VideoEncoder = VideoEncoder.VP9,
|
||||||
quality: int = DEFAULT_QUALITY,
|
quality: int = DEFAULT_QUALITY,
|
||||||
preset: int = DEFAULT_AV1_PRESET):
|
preset: int = DEFAULT_AV1_PRESET):
|
||||||
@@ -271,8 +275,7 @@ class FfxController():
|
|||||||
commandSequence += FfxController.generateCropTokens()
|
commandSequence += FfxController.generateCropTokens()
|
||||||
|
|
||||||
commandSequence += self.generateOutputTokens(targetPath,
|
commandSequence += self.generateOutputTokens(targetPath,
|
||||||
FfxController.DEFAULT_FILE_FORMAT,
|
targetFormat)
|
||||||
FfxController.DEFAULT_FILE_EXTENSION)
|
|
||||||
|
|
||||||
self.__logger.debug(f"FfxController.runJob() commandSequence:{' '.join(commandSequence)}")
|
self.__logger.debug(f"FfxController.runJob() commandSequence:{' '.join(commandSequence)}")
|
||||||
|
|
||||||
@@ -322,8 +325,7 @@ class FfxController():
|
|||||||
commandSequence2 += self.generateCropTokens()
|
commandSequence2 += self.generateCropTokens()
|
||||||
|
|
||||||
commandSequence2 += self.generateOutputTokens(targetPath,
|
commandSequence2 += self.generateOutputTokens(targetPath,
|
||||||
FfxController.DEFAULT_FILE_FORMAT,
|
targetFormat)
|
||||||
FfxController.DEFAULT_FILE_EXTENSION)
|
|
||||||
|
|
||||||
self.__logger.debug(f"FfxController.runJob() commandSequence2:{' '.join(commandSequence2)}")
|
self.__logger.debug(f"FfxController.runJob() commandSequence2:{' '.join(commandSequence2)}")
|
||||||
|
|
||||||
|
|||||||
@@ -209,53 +209,3 @@ class FileProperties():
|
|||||||
|
|
||||||
def getFileBasename(self):
|
def getFileBasename(self):
|
||||||
return self.__sourceFileBasename
|
return self.__sourceFileBasename
|
||||||
|
|
||||||
|
|
||||||
def assembleTargetFileBasename(self,
|
|
||||||
label: str = "",
|
|
||||||
quality: int = -1,
|
|
||||||
fileIndex: int = -1,
|
|
||||||
indexDigits: int = DEFAULT_INDEX_DIGITS,
|
|
||||||
extraTokens: list = []):
|
|
||||||
|
|
||||||
if 'show_descriptor' in self.context.keys():
|
|
||||||
season_digits = self.context['show_descriptor'][ShowDescriptor.INDICATOR_SEASON_DIGITS_KEY]
|
|
||||||
episode_digits = self.context['show_descriptor'][ShowDescriptor.INDICATOR_EPISODE_DIGITS_KEY]
|
|
||||||
else:
|
|
||||||
season_digits = ShowDescriptor.DEFAULT_INDICATOR_SEASON_DIGITS
|
|
||||||
episode_digits = ShowDescriptor.DEFAULT_INDICATOR_EPISODE_DIGITS
|
|
||||||
|
|
||||||
targetFilenameTokens = []
|
|
||||||
|
|
||||||
# targetFilenameExtension = FfxController.DEFAULT_FILE_EXTENSION if extension is None else str(extension)
|
|
||||||
|
|
||||||
self.__logger.debug(f"assembleTargetFileBasename(): label={label} is {'truthy' if label else 'falsy'}")
|
|
||||||
|
|
||||||
if label:
|
|
||||||
|
|
||||||
targetFilenameTokens = [label]
|
|
||||||
|
|
||||||
if fileIndex > -1:
|
|
||||||
targetFilenameTokens += [f"{fileIndex:0{indexDigits}d}"]
|
|
||||||
elif self.__season > -1 and self.__episode > -1:
|
|
||||||
targetFilenameTokens += [f"S{self.__season:0{season_digits}d}E{self.__episode:0{episode_digits}d}"]
|
|
||||||
elif self.__episode > -1:
|
|
||||||
targetFilenameTokens += [f"E{self.__episode:0{episode_digits}d}"]
|
|
||||||
|
|
||||||
else:
|
|
||||||
targetFilenameTokens = [self.__sourceFileBasename]
|
|
||||||
|
|
||||||
|
|
||||||
if quality != -1:
|
|
||||||
targetFilenameTokens += [f"q{quality}"]
|
|
||||||
|
|
||||||
# In case source and target filenames are the same add an extension to distinct output from input
|
|
||||||
#if not label and self.__sourceFilenameExtension == targetFilenameExtension:
|
|
||||||
# targetFilenameTokens += ['ffx']
|
|
||||||
targetFilenameTokens += extraTokens
|
|
||||||
|
|
||||||
targetFilename = '_'.join(targetFilenameTokens)
|
|
||||||
|
|
||||||
self.__logger.debug(f"assembleTargetFileBasename(): Target filename: {targetFilename}")
|
|
||||||
|
|
||||||
return targetFilename
|
|
||||||
|
|||||||
Reference in New Issue
Block a user