click-textual
Javanaut 1 year ago
parent 8ec7f9c2d1
commit 82b257d809

@ -0,0 +1,32 @@
import os
from ffx.pattern_controller import PatternController
from ffx.model.show import Base
from sqlalchemy import create_engine, Column, Integer, String, ForeignKey
from sqlalchemy.orm import relationship, sessionmaker, Mapped, backref
filename = 'Boruto.Naruto.Next.Generations.S01E256.GerEngSub.AAC.1080p.WebDL.x264-Tanuki.mkv'
# Data 'input' variable
context = {}
# Initialize DB
homeDir = os.path.expanduser("~")
ffxVarDir = os.path.join(homeDir, '.local', 'var', 'ffx')
if not os.path.exists(ffxVarDir):
os.makedirs(ffxVarDir)
context['database_url'] = f"sqlite:///{os.path.join(ffxVarDir, 'ffx.db')}"
context['database_engine'] = create_engine(context['database_url'])
context['database_session'] = sessionmaker(bind=context['database_engine'])
Base.metadata.create_all(context['database_engine'])
pc = PatternController(context)
print(pc.matchFilename(filename))

@ -1,10 +1,10 @@
#! /usr/bin/python3
import os, sys, subprocess, json, click, time
import os, sys, subprocess, json, click, time, re
from textual.app import App, ComposeResult
from textual.screen import Screen
from textual.widgets import Header, Footer, Placeholder
from textual.widgets import Header, Footer, Placeholder, Label
VERSION='0.1.0'
@ -35,7 +35,7 @@ MKVMERGE_METADATA_KEYS = ['BPS',
'_STATISTICS_WRITING_DATE_UTC',
'_STATISTICS_TAGS']
FILE_EXTENSION = ['mkv', 'mp4', 'avi', 'flv', 'webm']
FILE_EXTENSIONS = ['mkv', 'mp4', 'avi', 'flv', 'webm']
COMMAND_TOKENS = ['ffmpeg', '-y', '-i']
@ -50,6 +50,9 @@ STREAM_LAYOUT_5_1 = '5.1(side)'
STREAM_LAYOUT_STEREO = 'stereo'
STREAM_LAYOUT_6CH = '6ch'
SEASON_EPISODE_INDICATOR_MATCH = '([sS][0-9]+)([eE][0-9]+)'
SEASON_INDICATOR_MATCH = '([sS][0-9]+)'
EPISODE_INDICATOR_MATCH = '([eE][0-9]+)'
class DashboardScreen(Screen):
@ -65,6 +68,14 @@ class DashboardScreen(Screen):
yield Placeholder("Dashboard Screen")
yield Footer()
class WarningScreen(Screen):
def __init__(self):
super().__init__()
context = self.app.getContext()
def compose(self) -> ComposeResult:
yield Label("Warning! This file is not compliant to the defined source schema!")
yield Footer()
class SettingsScreen(Screen):
def __init__(self):
@ -87,29 +98,31 @@ class HelpScreen(Screen):
class ModesApp(App):
BINDINGS = [
("d", "switch_mode('dashboard')", "Dashboard"),
("s", "switch_mode('settings')", "Settings"),
("h", "switch_mode('help')", "Help"),
("q", "quit()", "Quit"),
# ("d", "switch_mode('dashboard')", "Dashboard"),
# ("s", "switch_mode('settings')", "Settings"),
# ("h", "switch_mode('help')", "Help"),
]
MODES = {
"warning": WarningScreen,
"dashboard": DashboardScreen,
"settings": SettingsScreen,
"help": HelpScreen,
}
def __init__(self, context = {}):
super().__init__()
self.context = context
def on_mount(self) -> None:
self.switch_mode("dashboard")
self.switch_mode("warning")
def getContext(self):
return self.context
def executeProcess(commandSequence):
process = subprocess.Popen(commandSequence, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
@ -354,8 +367,10 @@ def streams(filename):
@click.option("-c", "--clear-metadata", is_flag=True, default=False)
@click.option("-d", "--denoise", is_flag=True, default=False)
@click.option("-o", "--output-directory", type=str, default='')
def convert(ctx, paths, label, video_encoder, quality, preset, stereo_bitrate, ac3_bitrate, dts_bitrate, crop, clear_metadata, default_subtitle, forced_audio, default_audio, denoise):
def convert(ctx, paths, label, video_encoder, quality, preset, stereo_bitrate, ac3_bitrate, dts_bitrate, crop, clear_metadata, default_subtitle, forced_audio, default_audio, denoise, output_directory):
"""Batch conversion of audiovideo files in format suitable for web playback, e.g. jellyfin
Files found under PATHS will be converted according to parameters.
@ -363,56 +378,79 @@ def convert(ctx, paths, label, video_encoder, quality, preset, stereo_bitrate, a
Suffices will we appended to filename in case of multiple created files
or if the filename has not changed."""
#startTime = time.perf_counter()
startTime = time.perf_counter()
context = ctx.obj
#sourcePath = paths[0]
#targetFilename = paths[1]
#if not os.path.isfile(sourcePath):
# raise click.ClickException(f"There is no file with path {sourcePath}")
click.echo(f"\nVideo encoder: {video_encoder}")
#click.echo(f"src: {sourcePath} tgt: {targetFilename}")
qualityTokens = quality.split(',')
q_list = [q for q in qualityTokens if q.isnumeric()]
click.echo(f"Qualities: {q_list}")
#click.echo(f"ve={video_encoder}")
ctx.obj['bitrates'] = {}
ctx.obj['bitrates']['stereo'] = str(stereo_bitrate) if str(stereo_bitrate).endswith('k') else f"{stereo_bitrate}k"
ctx.obj['bitrates']['ac3'] = str(ac3_bitrate) if str(ac3_bitrate).endswith('k') else f"{ac3_bitrate}k"
ctx.obj['bitrates']['dts'] = str(dts_bitrate) if str(dts_bitrate).endswith('k') else f"{dts_bitrate}k"
#qualityTokens = quality.split(',')
click.echo(f"Stereo bitrate: {ctx.obj['bitrates']['stereo']}")
click.echo(f"AC3 bitrate: {ctx.obj['bitrates']['ac3']}")
click.echo(f"DTS bitrate: {ctx.obj['bitrates']['dts']}")
#q_list = [q for q in qualityTokens if q.isnumeric()]
ctx.obj['perform_crop'] = (crop != 'none')
#click.echo(q_list)
if ctx.obj['perform_crop']:
#ctx.obj['bitrates'] = {}
#ctx.obj['bitrates']['stereo'] = str(stereo_bitrate) if str(stereo_bitrate).endswith('k') else f"{stereo_bitrate}k"
#ctx.obj['bitrates']['ac3'] = str(ac3_bitrate) if str(ac3_bitrate).endswith('k') else f"{ac3_bitrate}k"
#ctx.obj['bitrates']['dts'] = str(dts_bitrate) if str(dts_bitrate).endswith('k') else f"{dts_bitrate}k"
cropTokens = crop.split(',')
if cropTokens and len(cropTokens) == 2:
#click.echo(f"a={ctx.obj['bitrates']['stereo']}")
#click.echo(f"ac3={ctx.obj['bitrates']['ac3']}")
#click.echo(f"dts={ctx.obj['bitrates']['dts']}")
ctx.obj['crop_start'], ctx.obj['crop_length'] = crop.split(',')
else:
ctx.obj['crop_start'] = DEFAULT_CROP_START
ctx.obj['crop_length'] = DEFAULT_CROP_LENGTH
click.echo(f"crop start={ctx.obj['crop_start']} length={ctx.obj['crop_length']}")
#performCrop = (crop != 'none')
click.echo(f"\nRunning {len(paths) * len(q_list)} jobs")
#if performCrop:
#cropTokens = crop.split(',')
se_match = re.compile(SEASON_EPISODE_INDICATOR_MATCH)
s_match = re.compile(SEASON_INDICATOR_MATCH)
e_match = re.compile(EPISODE_INDICATOR_MATCH)
for sourcePath in paths:
#if cropTokens and len(cropTokens) == 2:
if not os.path.isfile(sourcePath):
click.echo(f"There is no file with path {sourcePath}, skipping ...")
continue
#cropStart, cropLength = crop.split(',')
#else:
#cropStart = DEFAULT_CROP_START
#cropLength = DEFAULT_CROP_LENGTH
sourceDirectory = os.path.dirname(sourcePath)
sourceFilename = os.path.basename(sourcePath)
sourcePathTokens = sourceFilename.split('.')
#click.echo(f"crop start={cropStart} length={cropLength}")
if sourcePathTokens[-1] in FILE_EXTENSIONS:
sourceFileBasename = '.'.join(sourcePathTokens[:-1])
sourceFilenameExtension = sourcePathTokens[-1]
else:
sourceFileBasename = sourceFilename
sourceFilenameExtension = ''
#click.echo(f"dir={sourceDirectory} base={sourceFileBasename} ext={sourceFilenameExtension}")
click.echo(f"\nProcessing file {sourcePath}")
se_result = se_match.search(sourceFilename)
s_result = s_match.search(sourceFilename)
e_result = e_match.search(sourceFilename)
#click.echo(f"\nRunning {len(q_list)} jobs")
#streamDescriptor = getStreamDescriptor(sourcePath)
@ -497,15 +535,18 @@ def convert(ctx, paths, label, video_encoder, quality, preset, stereo_bitrate, a
#executeProcess(commandSequence2)
#click.echo('\nDONE\n')
#app = ModesApp(ctx.obj)
#app.run()
#click.confirm('Warning! This file is not compliant to the defined source schema! Do you want to continue?', abort=True)
click.echo('\nDONE\n')
#endTime = time.perf_counter()
#click.echo(f"Time elapsed {endTime - startTime}")
endTime = time.perf_counter()
click.echo(f"Time elapsed {endTime - startTime}")
app = ModesApp(ctx.obj)
app.run()
click.echo(f"app result: {app.getContext()}")
# click.echo(f"app result: {app.getContext()}")

@ -1,6 +1,6 @@
import os, re, click
class FilenameController():
class FileProperties():
FILE_EXTENSIONS = ['mkv', 'mp4', 'avi', 'flv', 'webm']
@ -23,11 +23,6 @@ class FilenameController():
self.__sourceFilenameExtension = ''
# Determine season and episode if present in current filename
season_digits = 2
episode_digits = 2
index_digits = 3
se_match = re.compile(FilenameController.SEASON_EPISODE_INDICATOR_MATCH)
e_match = re.compile(FilenameController.EPISODE_INDICATOR_MATCH)

@ -0,0 +1,66 @@
import os, re, click
class FileProperties():
FILE_EXTENSIONS = ['mkv', 'mp4', 'avi', 'flv', 'webm']
SEASON_EPISODE_INDICATOR_MATCH = '[sS]([0-9]+)[eE]([0-9]+)'
EPISODE_INDICATOR_MATCH = '[eE]([0-9]+)'
def ___init__(self, sourcePath, ):
# Separate basedir, basename and extension for current source file
self.__sourceDirectory = os.path.dirname(sourcePath)
self.__sourceFilename = os.path.basename(sourcePath)
sourcePathTokens = self.__sourceFilename.split('.')
if sourcePathTokens[-1] in FilenameController.FILE_EXTENSIONS:
self.__sourceFileBasename = '.'.join(sourcePathTokens[:-1])
self.__sourceFilenameExtension = sourcePathTokens[-1]
else:
self.__sourceFileBasename = self.__sourceFilename
self.__sourceFilenameExtension = ''
se_match = re.compile(FilenameController.SEASON_EPISODE_INDICATOR_MATCH)
e_match = re.compile(FilenameController.EPISODE_INDICATOR_MATCH)
se_result = se_match.search(self.__sourceFilename)
e_result = e_match.search(self.__sourceFilename)
self.__season = -1
self.__episode = -1
file_index = 0
if se_result is not None:
self.__season = int(se_result.group(1))
self.__episode = int(se_result.group(2))
elif e_result is not None:
self.__episode = int(e_result.group(1))
else:
file_index += 1
matchingFileSubtitleDescriptors = sorted([d for d in availableFileSubtitleDescriptors if d['season'] == season and d['episode'] == episode], key=lambda d: d['stream']) if availableFileSubtitleDescriptors else []
print(f"season={season} episode={episode} file={file_index}")
# Assemble target filename tokens
targetFilenameTokens = []
targetFilenameExtension = DEFAULT_FILE_EXTENSION
if label:
targetFilenameTokens = [label]
if season > -1 and episode > -1:
targetFilenameTokens += [f"S{season:0{season_digits}d}E{episode:0{episode_digits}d}"]
elif episode > -1:
targetFilenameTokens += [f"E{episode:0{episode_digits}d}"]
else:
targetFilenameTokens += [f"{file_index:0{index_digits}d}"]
else:
targetFilenameTokens = [sourceFileBasename]
Loading…
Cancel
Save