This commit is contained in:
2024-10-01 12:27:23 +00:00
parent 8ec7f9c2d1
commit 82b257d809
4 changed files with 193 additions and 59 deletions

View File

@@ -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))

View File

@@ -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()
#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"src: {sourcePath} tgt: {targetFilename}")
startTime = time.perf_counter()
context = ctx.obj
#click.echo(f"ve={video_encoder}")
click.echo(f"\nVideo encoder: {video_encoder}")
qualityTokens = quality.split(',')
q_list = [q for q in qualityTokens if q.isnumeric()]
click.echo(f"Qualities: {q_list}")
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"
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']}")
ctx.obj['perform_crop'] = (crop != 'none')
if ctx.obj['perform_crop']:
cropTokens = crop.split(',')
if cropTokens and len(cropTokens) == 2:
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']}")
#qualityTokens = quality.split(',')
click.echo(f"\nRunning {len(paths) * len(q_list)} jobs")
#q_list = [q for q in qualityTokens if q.isnumeric()]
#click.echo(q_list)
#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"
se_match = re.compile(SEASON_EPISODE_INDICATOR_MATCH)
s_match = re.compile(SEASON_INDICATOR_MATCH)
e_match = re.compile(EPISODE_INDICATOR_MATCH)
#click.echo(f"a={ctx.obj['bitrates']['stereo']}")
#click.echo(f"ac3={ctx.obj['bitrates']['ac3']}")
#click.echo(f"dts={ctx.obj['bitrates']['dts']}")
for sourcePath in paths:
#performCrop = (crop != 'none')
if not os.path.isfile(sourcePath):
click.echo(f"There is no file with path {sourcePath}, skipping ...")
continue
sourceDirectory = os.path.dirname(sourcePath)
sourceFilename = os.path.basename(sourcePath)
sourcePathTokens = sourceFilename.split('.')
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}")
#if performCrop:
se_result = se_match.search(sourceFilename)
s_result = s_match.search(sourceFilename)
e_result = e_match.search(sourceFilename)
#cropTokens = crop.split(',')
#if cropTokens and len(cropTokens) == 2:
#cropStart, cropLength = crop.split(',')
#else:
#cropStart = DEFAULT_CROP_START
#cropLength = DEFAULT_CROP_LENGTH
#click.echo(f"crop start={cropStart} length={cropLength}")
#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()
#endTime = time.perf_counter()
#click.echo(f"Time elapsed {endTime - startTime}")
#click.confirm('Warning! This file is not compliant to the defined source schema! Do you want to continue?', abort=True)
click.echo('\nDONE\n')
app = ModesApp(ctx.obj)
app.run()
endTime = time.perf_counter()
click.echo(f"Time elapsed {endTime - startTime}")
click.echo(f"app result: {app.getContext()}")
# click.echo(f"app result: {app.getContext()}")