v1.0.0 #1

Merged
jamie merged 5 commits from v1.0.0 into master 2026-01-08 19:55:51 +00:00
2 changed files with 148 additions and 24 deletions
Showing only changes of commit 50e3ce84c8 - Show all commits
+104 -23
View File
@@ -82,12 +82,19 @@ class DatabaseManager:
watch_folder VARCHAR(500) NOT NULL, watch_folder VARCHAR(500) NOT NULL,
ffmpeg_flags TEXT DEFAULT NULL, ffmpeg_flags TEXT DEFAULT NULL,
temp_dir VARCHAR(500) DEFAULT '/temp', temp_dir VARCHAR(500) DEFAULT '/temp',
target_resolution VARCHAR(20),
enabled BOOLEAN DEFAULT TRUE, enabled BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) )
""") """)
# Add target_resolution column if it doesn't exist (for existing users)
try:
cursor.execute("ALTER TABLE encoder_config ADD COLUMN target_resolution VARCHAR(20)")
except:
pass # Column already exists
cursor.execute(""" cursor.execute("""
CREATE TABLE IF NOT EXISTS job_reports ( CREATE TABLE IF NOT EXISTS job_reports (
id INT AUTO_INCREMENT PRIMARY KEY, id INT AUTO_INCREMENT PRIMARY KEY,
@@ -101,10 +108,17 @@ class DatabaseManager:
status ENUM('success', 'failed', 'processing', 'skipped') NOT NULL, status ENUM('success', 'failed', 'processing', 'skipped') NOT NULL,
error_message TEXT, error_message TEXT,
processing_time DECIMAL(10,2), processing_time DECIMAL(10,2),
target_resolution VARCHAR(20),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) )
""") """)
# Add target_resolution column if it doesn't exist (for existing users)
try:
cursor.execute("ALTER TABLE job_reports ADD COLUMN target_resolution VARCHAR(20)")
except:
pass # Column already exists
# New persistent job queue table # New persistent job queue table
cursor.execute(""" cursor.execute("""
CREATE TABLE IF NOT EXISTS job_queue ( CREATE TABLE IF NOT EXISTS job_queue (
@@ -181,20 +195,20 @@ class DatabaseManager:
conn.close() conn.close()
return result return result
def add_config(self, encoder_type, watch_folder, ffmpeg_flags=None, temp_dir='/temp'): def add_config(self, encoder_type, watch_folder, ffmpeg_flags=None, temp_dir='/temp', target_resolution=None):
"""Add a new watch folder configuration""" """Add a new watch folder configuration"""
conn = self.get_connection() conn = self.get_connection()
cursor = conn.cursor() cursor = conn.cursor()
cursor.execute( cursor.execute(
"INSERT INTO encoder_config (encoder_type, watch_folder, ffmpeg_flags, temp_dir) VALUES (%s, %s, %s, %s)", "INSERT INTO encoder_config (encoder_type, watch_folder, ffmpeg_flags, temp_dir, target_resolution) VALUES (%s, %s, %s, %s, %s)",
(encoder_type, watch_folder, ffmpeg_flags, temp_dir) (encoder_type, watch_folder, ffmpeg_flags, temp_dir, target_resolution)
) )
conn.commit() conn.commit()
cursor.close() cursor.close()
conn.close() conn.close()
def add_job_report(self, encoder_type, file_path, original_size, encoded_size, def add_job_report(self, encoder_type, file_path, original_size, encoded_size,
original_format, encoded_format, status, error_message=None, processing_time=None): original_format, encoded_format, status, error_message=None, processing_time=None, target_resolution=None):
"""Add a job report to the database""" """Add a job report to the database"""
if status == 'skipped': if status == 'skipped':
size_saved = 0 size_saved = 0
@@ -205,10 +219,10 @@ class DatabaseManager:
cursor.execute(""" cursor.execute("""
INSERT INTO job_reports INSERT INTO job_reports
(encoder_type, file_path, original_size, encoded_size, size_saved, (encoder_type, file_path, original_size, encoded_size, size_saved,
original_format, encoded_format, status, error_message, processing_time) original_format, encoded_format, status, error_message, processing_time, target_resolution)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
""", (encoder_type, file_path, original_size, encoded_size, size_saved, """, (encoder_type, file_path, original_size, encoded_size, size_saved,
original_format, encoded_format, status, error_message, processing_time)) original_format, encoded_format, status, error_message, processing_time, target_resolution))
conn.commit() conn.commit()
cursor.close() cursor.close()
conn.close() conn.close()
@@ -329,12 +343,38 @@ class VideoEncoder:
logger.error(f"Error checking codec for {file_path}: {e}") logger.error(f"Error checking codec for {file_path}: {e}")
return False return False
def encode_to_h265(self, input_path, output_path, ffmpeg_flags=None): def is_h265(self, file_path):
"""Encode video to H265 MKV format (only 720p and higher), using user ffmpeg_flags if provided""" """Check if video file is H265 encoded"""
try:
result = subprocess.run([
'ffprobe', '-v', 'quiet', '-select_streams', 'v:0',
'-show_entries', 'stream=codec_name', '-of', 'csv=p=0',
file_path
], capture_output=True, text=True)
return result.stdout.strip() == 'hevc'
except Exception as e:
logger.error(f"Error checking codec for {file_path}: {e}")
return False
def get_resolution(self, file_path):
"""Get video resolution (height)"""
try:
result = subprocess.run([
'ffprobe', '-v', 'quiet', '-select_streams', 'v:0',
'-show_entries', 'stream=height', '-of', 'csv=p=0',
file_path
], capture_output=True, text=True)
return int(result.stdout.strip())
except Exception as e:
logger.error(f"Error getting resolution for {file_path}: {e}")
return None
def encode_to_h265(self, input_path, output_path, ffmpeg_flags=None, target_resolution=None):
"""Encode video to H265 MKV format (only 720p and higher), with optional target resolution scaling"""
try: try:
resolution_check = subprocess.run([ resolution_check = subprocess.run([
'ffprobe', '-v', 'quiet', '-select_streams', 'v:0', 'ffprobe', '-v', 'quiet', '-select_streams', 'v:0',
'-show_entries', 'stream=height', '-of', 'csv=p=0', '-show_entries', 'stream=height,width', '-of', 'csv=p=0',
input_path input_path
], capture_output=True, text=True) ], capture_output=True, text=True)
@@ -342,17 +382,31 @@ class VideoEncoder:
return False, "Could not determine video resolution" return False, "Could not determine video resolution"
try: try:
height = int(resolution_check.stdout.strip()) width, height = map(int, resolution_check.stdout.strip().split(','))
if height < 720: if height < 720:
return False, f"Video resolution {height}p is below 720p minimum, skipping encoding" return False, f"Video resolution {height}p is below 720p minimum, skipping encoding"
except ValueError: except ValueError:
return False, "Invalid video height information" return False, "Invalid video resolution information"
cmd = [ cmd = [
'ffmpeg', '-i', input_path, '-c:v', 'libx265', '-c:a', 'copy', 'ffmpeg', '-i', input_path, '-c:v', 'libx265', '-c:a', 'copy',
'-preset', 'medium', '-crf', '28', '-f', 'matroska' '-preset', 'medium', '-crf', '28', '-f', 'matroska'
] ]
# Add scaling filter if target resolution is specified
if target_resolution:
# target_resolution should be in format like "720p", "1080p", "480p"
target_height = int(target_resolution.replace('p', '').lower())
# Only scale down, never scale up
if target_height > 0 and height > target_height:
# Calculate scaled width to maintain aspect ratio
scale_width = int((width / height) * target_height)
# Ensure width is divisible by 2 (required by video codecs)
if scale_width % 2 != 0:
scale_width -= 1
cmd.insert(2, f'scale={scale_width}:{target_height}')
cmd.insert(2, '-vf')
# Insert user flags if provided # Insert user flags if provided
if ffmpeg_flags: if ffmpeg_flags:
import shlex import shlex
@@ -515,14 +569,16 @@ def process_file(encoder_type, file_path, encoder):
original_size = os.path.getsize(file_path) original_size = os.path.getsize(file_path)
file_name = os.path.basename(file_path) file_name = os.path.basename(file_path)
file_dir = os.path.dirname(file_path) file_dir = os.path.dirname(file_path)
# Get ffmpeg_flags and temp_dir for this file's folder # Get ffmpeg_flags, temp_dir, and target_resolution for this file's folder
configs = db_manager.get_config(encoder_type) configs = db_manager.get_config(encoder_type)
ffmpeg_flags = None ffmpeg_flags = None
temp_dir = DEFAULT_TEMP_DIR temp_dir = DEFAULT_TEMP_DIR
target_resolution = None
for config in configs: for config in configs:
if file_path.startswith(config['watch_folder']): if file_path.startswith(config['watch_folder']):
ffmpeg_flags = config.get('ffmpeg_flags') ffmpeg_flags = config.get('ffmpeg_flags')
temp_dir = config.get('temp_dir') or DEFAULT_TEMP_DIR temp_dir = config.get('temp_dir') or DEFAULT_TEMP_DIR
target_resolution = config.get('target_resolution')
break break
if not os.path.exists(temp_dir): if not os.path.exists(temp_dir):
os.makedirs(temp_dir, exist_ok=True) os.makedirs(temp_dir, exist_ok=True)
@@ -530,12 +586,22 @@ def process_file(encoder_type, file_path, encoder):
temp_input = os.path.join(temp_dir, file_name) temp_input = os.path.join(temp_dir, file_name)
shutil.copy2(file_path, temp_input) shutil.copy2(file_path, temp_input)
if encoder_type == 'video': if encoder_type == 'video':
if not encoder.is_h264(temp_input): is_h264 = encoder.is_h264(temp_input)
is_h265 = encoder.is_h265(temp_input)
# Skip if neither H.264 nor H.265
if not is_h264 and not is_h265:
os.remove(temp_input) os.remove(temp_input)
return return
# Skip H.265 files if no target resolution is set
if is_h265 and not target_resolution:
os.remove(temp_input)
return
temp_output = os.path.join(temp_dir, f"temp_{file_name}") temp_output = os.path.join(temp_dir, f"temp_{file_name}")
temp_output = os.path.splitext(temp_output)[0] + '.mkv' temp_output = os.path.splitext(temp_output)[0] + '.mkv'
success, error = encoder.encode_to_h265(temp_input, temp_output, ffmpeg_flags=ffmpeg_flags) success, error = encoder.encode_to_h265(temp_input, temp_output, ffmpeg_flags=ffmpeg_flags, target_resolution=target_resolution)
if success: if success:
encoded_size = os.path.getsize(temp_output) encoded_size = os.path.getsize(temp_output)
valid, validation_error = encoder.validate_encoded_file(temp_input, temp_output) valid, validation_error = encoder.validate_encoded_file(temp_input, temp_output)
@@ -552,7 +618,8 @@ def process_file(encoder_type, file_path, encoder):
db_manager.add_job_report( db_manager.add_job_report(
encoder_type, file_path, original_size, 0, encoder_type, file_path, original_size, 0,
os.path.splitext(file_path)[1], '.mkv', 'failed', os.path.splitext(file_path)[1], '.mkv', 'failed',
error_message=f'Failed to copy encoded file: {copy_exc}' error_message=f'Failed to copy encoded file: {copy_exc}',
target_resolution=target_resolution
) )
logger.error(f"Failed to copy encoded file for {file_path}: {copy_exc}") logger.error(f"Failed to copy encoded file for {file_path}: {copy_exc}")
return return
@@ -560,14 +627,16 @@ def process_file(encoder_type, file_path, encoder):
db_manager.add_job_report( db_manager.add_job_report(
encoder_type, file_path, original_size, encoded_size, encoder_type, file_path, original_size, encoded_size,
os.path.splitext(file_path)[1], '.mkv', 'success', os.path.splitext(file_path)[1], '.mkv', 'success',
processing_time=processing_time processing_time=processing_time,
target_resolution=target_resolution
) )
logger.info(f"Successfully encoded {file_path} to H265") logger.info(f"Successfully encoded {file_path} to H265")
else: else:
db_manager.add_job_report( db_manager.add_job_report(
encoder_type, file_path, original_size, 0, encoder_type, file_path, original_size, 0,
os.path.splitext(file_path)[1], '.mkv', 'failed', os.path.splitext(file_path)[1], '.mkv', 'failed',
error_message=validation_error error_message=validation_error,
target_resolution=target_resolution
) )
logger.error(f"Validation failed for {file_path}: {validation_error}") logger.error(f"Validation failed for {file_path}: {validation_error}")
# Clean up temp files # Clean up temp files
@@ -582,7 +651,8 @@ def process_file(encoder_type, file_path, encoder):
db_manager.add_job_report( db_manager.add_job_report(
encoder_type, file_path, original_size, 0, encoder_type, file_path, original_size, 0,
os.path.splitext(file_path)[1], os.path.splitext(file_path)[1], 'skipped', os.path.splitext(file_path)[1], os.path.splitext(file_path)[1], 'skipped',
error_message=error error_message=error,
target_resolution=target_resolution
) )
logger.info(f"Skipped {file_path}: {error}") logger.info(f"Skipped {file_path}: {error}")
else: else:
@@ -591,7 +661,8 @@ def process_file(encoder_type, file_path, encoder):
db_manager.add_job_report( db_manager.add_job_report(
encoder_type, file_path, original_size, 0, encoder_type, file_path, original_size, 0,
os.path.splitext(file_path)[1], '.mkv', 'failed', os.path.splitext(file_path)[1], '.mkv', 'failed',
error_message=error error_message=error,
target_resolution=target_resolution
) )
logger.error(f"Failed to encode {file_path}: {error}") logger.error(f"Failed to encode {file_path}: {error}")
# Always remove temp input if it still exists # Always remove temp input if it still exists
@@ -658,7 +729,8 @@ def process_file(encoder_type, file_path, encoder):
db_manager.add_job_report( db_manager.add_job_report(
encoder_type, file_path, os.path.getsize(file_path), 0, encoder_type, file_path, os.path.getsize(file_path), 0,
os.path.splitext(file_path)[1], '', 'failed', os.path.splitext(file_path)[1], '', 'failed',
error_message=str(e) error_message=str(e),
target_resolution=target_resolution
) )
except: except:
pass pass
@@ -704,6 +776,7 @@ def scan_folders():
video_configs = db_manager.get_config('video') video_configs = db_manager.get_config('video')
for config in video_configs: for config in video_configs:
folder = config['watch_folder'] folder = config['watch_folder']
target_resolution = config.get('target_resolution')
if os.path.exists(folder): if os.path.exists(folder):
for root, dirs, files in os.walk(folder): for root, dirs, files in os.walk(folder):
for file in files: for file in files:
@@ -713,6 +786,14 @@ def scan_folders():
if (not db_manager.is_in_queue('video', file_path) if (not db_manager.is_in_queue('video', file_path)
and not db_manager.file_already_processed(file_path)): and not db_manager.file_already_processed(file_path)):
db_manager.add_to_queue('video', file_path) db_manager.add_to_queue('video', file_path)
elif target_resolution and video_encoder.is_h265(file_path):
# Also encode H.265 files if current resolution is higher than target
current_height = video_encoder.get_resolution(file_path)
target_height = int(target_resolution.replace('p', '').lower())
if current_height and current_height > target_height:
if (not db_manager.is_in_queue('video', file_path)
and not db_manager.file_already_processed(file_path)):
db_manager.add_to_queue('video', file_path)
audio_configs = db_manager.get_config('audio') audio_configs = db_manager.get_config('audio')
for config in audio_configs: for config in audio_configs:
@@ -836,8 +917,8 @@ def edit_config(config_id):
conn = db_manager.get_connection() conn = db_manager.get_connection()
cursor = conn.cursor() cursor = conn.cursor()
cursor.execute( cursor.execute(
"UPDATE encoder_config SET watch_folder=%s, temp_dir=%s, ffmpeg_flags=%s WHERE id=%s", "UPDATE encoder_config SET watch_folder=%s, temp_dir=%s, ffmpeg_flags=%s, target_resolution=%s WHERE id=%s",
(data.get('watch_folder'), data.get('temp_dir'), data.get('ffmpeg_flags'), config_id) (data.get('watch_folder'), data.get('temp_dir'), data.get('ffmpeg_flags'), data.get('target_resolution'), config_id)
) )
conn.commit() conn.commit()
cursor.close() cursor.close()
+44 -1
View File
@@ -47,6 +47,19 @@
<input type="hidden" name="temp_dir" id="video-temp-input" value="/temp"> <input type="hidden" name="temp_dir" id="video-temp-input" value="/temp">
<div id="video-temp-list" class="bg-gray-800 border border-gray-700 rounded-lg mt-1 p-2 text-white text-sm hidden"></div> <div id="video-temp-list" class="bg-gray-800 border border-gray-700 rounded-lg mt-1 p-2 text-white text-sm hidden"></div>
</div> </div>
<div class="flex flex-col">
<label for="video-target-resolution" class="text-xs text-gray-400 mb-1">Target Resolution
<span class="text-gray-500 text-xs">(Optional)</span>
</label>
<select name="target_resolution" id="video-target-resolution" class="bg-gray-700 border border-gray-600 rounded-lg px-2 py-1 text-white">
<option value="">Default (Keep Original)</option>
<option value="480p">480p</option>
<option value="720p">720p</option>
<option value="1080p">1080p</option>
<option value="1440p">1440p</option>
<option value="2160p">4K (2160p)</option>
</select>
</div>
<button type="submit" class="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg text-sm font-medium"> <button type="submit" class="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg text-sm font-medium">
Add Folder Add Folder
</button> </button>
@@ -99,6 +112,9 @@
{% if config.temp_dir %} {% if config.temp_dir %}
<div class="text-blue-200 text-xs mt-1">Temp Dir: <span class="bg-blue-900 px-1 rounded">{{ config.temp_dir }}</span></div> <div class="text-blue-200 text-xs mt-1">Temp Dir: <span class="bg-blue-900 px-1 rounded">{{ config.temp_dir }}</span></div>
{% endif %} {% endif %}
{% if config.target_resolution %}
<div class="text-green-200 text-xs mt-1">Resolution: <span class="bg-green-900 px-1 rounded">{{ config.target_resolution }}</span></div>
{% endif %}
{% if config.ffmpeg_flags %} {% if config.ffmpeg_flags %}
{% set flags = config.ffmpeg_flags.split() %} {% set flags = config.ffmpeg_flags.split() %}
<div class="text-blue-300 text-xs mt-1"> <div class="text-blue-300 text-xs mt-1">
@@ -207,6 +223,9 @@
{% if config.temp_dir %} {% if config.temp_dir %}
<div class="text-purple-200 text-xs mt-1">Temp Dir: <span class="bg-purple-900 px-1 rounded">{{ config.temp_dir }}</span></div> <div class="text-purple-200 text-xs mt-1">Temp Dir: <span class="bg-purple-900 px-1 rounded">{{ config.temp_dir }}</span></div>
{% endif %} {% endif %}
{% if config.target_resolution %}
<div class="text-green-200 text-xs mt-1">Resolution: <span class="bg-green-900 px-1 rounded">{{ config.target_resolution }}</span></div>
{% endif %}
{% if config.ffmpeg_flags %} {% if config.ffmpeg_flags %}
{% set flags = config.ffmpeg_flags.split() %} {% set flags = config.ffmpeg_flags.split() %}
<div class="text-purple-300 text-xs mt-1"> <div class="text-purple-300 text-xs mt-1">
@@ -254,6 +273,9 @@
</div> </div>
</div> </div>
</div> </div>
</div>
</div>
</div>
<!-- Edit Config Modal --> <!-- Edit Config Modal -->
<div id="edit-config-modal" class="fixed inset-0 z-50 hidden flex items-center justify-center"> <div id="edit-config-modal" class="fixed inset-0 z-50 hidden flex items-center justify-center">
@@ -279,6 +301,19 @@
<input type="hidden" name="temp_dir" id="edit-temp-input" value="/temp"> <input type="hidden" name="temp_dir" id="edit-temp-input" value="/temp">
<div id="edit-temp-list" class="bg-gray-800 border border-gray-700 rounded-lg mt-1 p-2 text-white text-sm hidden z-50" style="position:absolute;"></div> <div id="edit-temp-list" class="bg-gray-800 border border-gray-700 rounded-lg mt-1 p-2 text-white text-sm hidden z-50" style="position:absolute;"></div>
</div> </div>
<div class="mb-4" id="edit-target-resolution-field" style="display:none;">
<label for="edit-target-resolution" class="text-xs text-gray-400 mb-1">Target Resolution
<span class="text-gray-500 text-xs">(Optional)</span>
</label>
<select name="target_resolution" id="edit-target-resolution" class="bg-gray-700 border border-gray-600 rounded-lg px-2 py-1 text-white w-full">
<option value="">Default (Keep Original)</option>
<option value="480p">480p</option>
<option value="720p">720p</option>
<option value="1080p">1080p</option>
<option value="1440p">1440p</option>
<option value="2160p">4K (2160p)</option>
</select>
</div>
<div class="mb-4" id="edit-ffmpeg-flags-video" style="display:none;"> <div class="mb-4" id="edit-ffmpeg-flags-video" style="display:none;">
<label class="block text-xs text-gray-400 mb-1">FFmpeg Flags (Video)</label> <label class="block text-xs text-gray-400 mb-1">FFmpeg Flags (Video)</label>
<div class="flex space-x-2"> <div class="flex space-x-2">
@@ -416,8 +451,11 @@ function openEditModal(config) {
document.getElementById('edit-temp-input').value = config.temp_dir || '/temp'; document.getElementById('edit-temp-input').value = config.temp_dir || '/temp';
// Show/hide flag dropdowns // Show/hide flag dropdowns
if (config.encoder_type === 'video') { if (config.encoder_type === 'video') {
document.getElementById('edit-target-resolution-field').style.display = '';
document.getElementById('edit-ffmpeg-flags-video').style.display = ''; document.getElementById('edit-ffmpeg-flags-video').style.display = '';
document.getElementById('edit-ffmpeg-flags-audio').style.display = 'none'; document.getElementById('edit-ffmpeg-flags-audio').style.display = 'none';
// Set target resolution value
document.getElementById('edit-target-resolution').value = config.target_resolution || '';
// Parse ffmpeg_flags for crf and preset // Parse ffmpeg_flags for crf and preset
let crf = '24', preset = 'medium'; let crf = '24', preset = 'medium';
if (config.ffmpeg_flags) { if (config.ffmpeg_flags) {
@@ -429,6 +467,7 @@ function openEditModal(config) {
document.getElementById('edit-crf').value = crf; document.getElementById('edit-crf').value = crf;
document.getElementById('edit-preset').value = preset; document.getElementById('edit-preset').value = preset;
} else { } else {
document.getElementById('edit-target-resolution-field').style.display = 'none';
document.getElementById('edit-ffmpeg-flags-video').style.display = 'none'; document.getElementById('edit-ffmpeg-flags-video').style.display = 'none';
document.getElementById('edit-ffmpeg-flags-audio').style.display = ''; document.getElementById('edit-ffmpeg-flags-audio').style.display = '';
// Parse ffmpeg_flags for audio bitrate // Parse ffmpeg_flags for audio bitrate
@@ -459,15 +498,18 @@ if (editForm) {
const id = document.getElementById('edit-config-id').value; const id = document.getElementById('edit-config-id').value;
const encoderType = document.getElementById('edit-encoder-type').value; const encoderType = document.getElementById('edit-encoder-type').value;
let ffmpeg_flags = ''; let ffmpeg_flags = '';
let target_resolution = '';
if (encoderType === 'video') { if (encoderType === 'video') {
ffmpeg_flags = `-crf ${document.getElementById('edit-crf').value} -preset ${document.getElementById('edit-preset').value}`; ffmpeg_flags = `-crf ${document.getElementById('edit-crf').value} -preset ${document.getElementById('edit-preset').value}`;
target_resolution = document.getElementById('edit-target-resolution').value;
} else { } else {
ffmpeg_flags = `-b:a ${document.getElementById('edit-audio-bitrate').value}`; ffmpeg_flags = `-b:a ${document.getElementById('edit-audio-bitrate').value}`;
} }
const data = { const data = {
watch_folder: document.getElementById('edit-folder-input').value, watch_folder: document.getElementById('edit-folder-input').value,
temp_dir: document.getElementById('edit-temp-input').value, temp_dir: document.getElementById('edit-temp-input').value,
ffmpeg_flags: ffmpeg_flags ffmpeg_flags: ffmpeg_flags,
target_resolution: target_resolution
}; };
fetch(`/edit_config/${id}`, { fetch(`/edit_config/${id}`, {
method: 'POST', method: 'POST',
@@ -494,6 +536,7 @@ window.allConfigs = [
watch_folder: {{ config.watch_folder|tojson }}, watch_folder: {{ config.watch_folder|tojson }},
temp_dir: {{ config.temp_dir|tojson }}, temp_dir: {{ config.temp_dir|tojson }},
ffmpeg_flags: {{ config.ffmpeg_flags|tojson }}, ffmpeg_flags: {{ config.ffmpeg_flags|tojson }},
target_resolution: {{ config.target_resolution|tojson }},
encoder_type: {{ config.encoder_type|tojson }} encoder_type: {{ config.encoder_type|tojson }}
}, },
{% endfor %} {% endfor %}