Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
rev: v6.0.0
hooks:
- id: mixed-line-ending
- id: trailing-whitespace
exclude: README.md
- id: requirements-txt-fixer
- id: check-yaml
- id: check-json
- id: fix-encoding-pragma
- id: check-byte-order-marker
- id: fix-byte-order-marker
- id: debug-statements
- id: check-added-large-files
exclude: |
Expand All @@ -24,7 +23,7 @@ repos:
- id: end-of-file-fixer
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.12.1
rev: v0.15.1
hooks:
# Run the linter.
- id: ruff-check
Expand Down
29 changes: 29 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
@@ -1,5 +1,34 @@
# Changelog

## Version 6.1.0

* Adding #717 Output Naming settings tab with template editor, clickable variable chips, live preview, and validation for customizing output filenames with 23 pre-encode variables (thanks to roxerqermik)
* Adding #660 Data & Attachments tab for per-track control of data streams (timecodes, navigation) and non-image attachments (fonts) (thanks to techguru0)
* Adding #706 language and disposition metadata parsing from auto-detected external subtitle filenames (e.g., video.forced.deu.srt) (thanks to mpek)
* Adding #706 auto-detection of external subtitle files (.srt, .ass, .ssa, .vtt, .sup, .sub, .idx) when loading a video, configurable in Settings (thanks to mpek)
* Adding #698 external subtitle support for Rigaya encoders (NVEncC, QSVEncC, VCEEncC) via --sub-source (thanks to Augusto7743)
* Adding always-on ffprobe validation of output files after every encode to catch silent failures
* Adding Visual Crop window for dragging crop edges directly on a video frame preview, with live overlay and divisible-by-8 snapping on save
* Adding bottom status bar with animated icon showing encoding state, progress bar, and status messages
* Adding startup tasks (FFmpeg config, GPU detect, HDR10+ download) running through the status bar with main window visible
* Adding Terms and Agreements dialog shown on first startup requiring user acceptance before proceeding
* Fixing #719 Unable to save/load film grain setting for SVT-AV1 (thanks to gabriel101x)
* Fixing #716 Maximize button not working (thanks to roxerqermik and 19Battlestar65)
* Fixing #349 NVEncC audio conversion losing multichannel layout for EAC3 (thanks to Wontell)
* Fixing #384 Remove HDR leaving Dolby Vision metadata traces in Rigaya encoder output (thanks to end2endzone)
* Fixing #511 UI labels and buttons truncated in non-English translations by auto-shrinking text to fit (thanks to PegHorse)
* Fixing #514 excessive memory usage when adding directory of files (thanks to gxcreator)
* Fixing #548 incorrect aspect ratio for DVD sources with non-square pixels on auto resolution (thanks to DCNerds)
* Fixing #600 anime subtitle size increasing during burn-in encoding (thanks to TinderboxUK)
* Fixing #693 subtitle tracks losing title metadata during encoding (thanks to mpissarello)
* Fixing #715 WINDOWS_BUILD.md needed updated to show Python 3.13 (thanks to Jack L)
* Fixing #720 custom profile resolution settings (Height, Width, Long Edge, Custom, explicit like 640x480) being ignored when loading a video, defaulting to source resolution instead of profile resolution (thanks to Xoanon88)
* Fixing UI scaling for Source/Folder/Filename text boxes, file extension dropdown, Resolution label, Start/End Time controls, and Crop input fields being too small
* Fixing profile load/save for VVC period and threads, VP9 auto alt ref, lag in frames, AQ mode, and sharpness, rav1e photon noise, and AOM-AV1 denoise settings using integer as combo box index instead of matching by value
* Fixing VCEEncC pre-analysis lookahead setting reading from wrong widget (pa_initqpsc instead of pa_lookahead) in HEVC, AV1, and AVC encoders
* Fixing Settings window staying on top and freezing when notification dialogs appeared behind it
* Fixing tab bar scroll arrows being too small and both stuck on the right side - now larger with left arrow on far left

## Version 6.0.1

* Fixing Dolby Vision copy for Rigaya encoders (NVEncC, QSVEncC, VCEEncC) by adding --dolby-vision-profile copy alongside --dolby-vision-rpu copy
Expand Down
2 changes: 1 addition & 1 deletion WINDOWS_BUILD.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ This guide explains how to build FastFlix executables on Windows.

## Prerequisites

1. **Python 3.12 or higher**
1. **Python 3.13 or higher**
- Download from [python.org](https://www.python.org/downloads/)
- Make sure to check "Add Python to PATH" during installation

Expand Down
121 changes: 98 additions & 23 deletions fastflix/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@
from fastflix.program_downloads import ask_for_ffmpeg, grab_stable_ffmpeg, download_hdr10plus_tool
from fastflix.resources import main_icon, breeze_styles_path
from fastflix.shared import file_date, message, latest_fastflix, DEVMODE, yes_no_message
from fastflix.ui_constants import FONTS
from fastflix.widgets.container import Container
from fastflix.widgets.progress_bar import ProgressBar, Task
from fastflix.widgets.status_bar import Task, STATE_IDLE, STATE_ERROR
from fastflix.gpu_detect import automatic_rigaya_download

logger = logging.getLogger("fastflix")
Expand All @@ -41,10 +42,14 @@ def create_app(enable_scaling):
main_app = FastFlixApp(sys.argv)
main_app.allWindows()
main_app.setApplicationDisplayName("FastFlix")

# On Linux, ensure an icon theme is set so QFileDialog toolbar icons appear
if sys.platform == "linux" and not QtGui.QIcon.themeName():
QtGui.QIcon.setThemeName("breeze")
available_fonts = QtGui.QFontDatabase().families()
font_preference = ["Roboto", "Segoe UI", "Ubuntu", "Open Sans", "Sans Serif"]
selected_font = next((f for f in font_preference if f in available_fonts), "Sans Serif")
my_font = QtGui.QFont(selected_font, 9)
my_font = QtGui.QFont(selected_font, FONTS.SMALL)
main_app.setFont(my_font)
icon = QtGui.QIcon()
icon.addFile(main_icon, QtCore.QSize(16, 16))
Expand Down Expand Up @@ -203,24 +208,30 @@ def app_setup(
f"{app.fastflix.config.config_path}",
title="Upgraded",
)
missing_ff = False
try:
app.fastflix.config.load(portable_mode=portable_mode)
except MissingFF as err:
if reusables.win_based and ask_for_ffmpeg():
try:
ProgressBar(app, [Task(t("Downloading FFmpeg"), grab_stable_ffmpeg)], signal_task=True)
app.fastflix.config.load()
except Exception as err:
logger.exception(str(err))
sys.exit(1)
# User wants to download FFmpeg — will be handled after Container is shown
missing_ff = "download"
else:
logger.error(f"Could not find {err} location, please manually set in {app.fastflix.config.config_path}")
sys.exit(1)
missing_ff = str(err)
logger.warning(f"FFmpeg not found during config load: {err}")
except Exception:
# TODO give edit / delete options
logger.exception(t("Could not load config file!"))
sys.exit(1)

if not app.fastflix.config.terms_accepted:
from fastflix.widgets.terms_agreement import TermsAgreementDialog

dialog = TermsAgreementDialog()
if dialog.exec() == QtWidgets.QDialog.Accepted:
app.fastflix.config.terms_accepted = True
app.fastflix.config.save()
else:
sys.exit(0)

if app.fastflix.config.theme != "system":
file = QtCore.QFile(str(breeze_styles_path / app.fastflix.config.theme / "stylesheet.qss"))
file.open(QtCore.QFile.OpenModeFlag.ReadOnly | QtCore.QFile.OpenModeFlag.Text)
Expand All @@ -239,8 +250,65 @@ def app_setup(

app.setStyleSheet(data)

# On Linux/KDE, applying a custom stylesheet can disrupt the platform
# icon theme for standard dialog icons (e.g., QFileDialog toolbar).
# Re-asserting the icon theme after stylesheet application restores them.
if sys.platform == "linux":
theme_name = QtGui.QIcon.themeName() or "breeze"
QtGui.QIcon.setThemeName(theme_name)

logger.setLevel(app.fastflix.config.logging_level)

# Initialize empty encoder/audio lists so Container can be created before startup tasks
if app.fastflix.encoders is None:
app.fastflix.encoders = {}
if app.fastflix.audio_encoders is None:
app.fastflix.audio_encoders = []

# Create and show Container immediately (UI starts disabled via Main.__init__)
container = Container(app)
container.show()

cursor_pos = QtGui.QCursor.pos()
screen = QtGui.QGuiApplication.screenAt(cursor_pos) or QtGui.QGuiApplication.primaryScreen()
screen_geometry = screen.availableGeometry()
container.move(screen_geometry.center() - container.rect().center())

# Disable entire window during startup tasks
container.setEnabled(False)
app.processEvents()

# Handle missing FFmpeg
if missing_ff:
if missing_ff == "download":
# Download FFmpeg through status bar
try:
container.status_bar.run_tasks(
[Task(t("Downloading FFmpeg"), grab_stable_ffmpeg)],
signal_task=True,
persist_complete=True,
)
app.fastflix.config.load()
except Exception as err:
logger.exception(str(err))
container.status_bar.set_state(
STATE_ERROR,
t("FFmpeg not found") + " — " + t("configure in Settings") + " (Ctrl+S)",
)
container.setEnabled(True)
return app
else:
logger.error(
f"Could not find {missing_ff} location, please manually set in {app.fastflix.config.config_path}"
)
container.status_bar.set_state(
STATE_ERROR,
t("FFmpeg not found") + " — " + t("configure in Settings") + " (Ctrl+S)",
)
container.setEnabled(True)
return app

# GPU detect and HDR10+ download (Windows only, with user permission dialogs)
if platform.system() == "Windows":
if app.fastflix.config.auto_gpu_check is None:
app.fastflix.config.auto_gpu_check = yes_no_message(
Expand All @@ -250,9 +318,14 @@ def app_setup(
title="Allow Optional Downloads",
)
if app.fastflix.config.auto_gpu_check:
ProgressBar(
app, [Task(name=t("Detect GPUs"), command=automatic_rigaya_download)], signal_task=True, can_cancel=True
)
try:
container.status_bar.run_tasks(
[Task(name=t("Detect GPUs"), command=automatic_rigaya_download)],
signal_task=True,
can_cancel=True,
)
except Exception:
logger.exception("Failed to detect GPUs")

if app.fastflix.config.auto_hdr10plus_check is None and not app.fastflix.config.hdr10plus_parser:
app.fastflix.config.auto_hdr10plus_check = yes_no_message(
Expand All @@ -263,8 +336,7 @@ def app_setup(
)
if app.fastflix.config.auto_hdr10plus_check:
try:
ProgressBar(
app,
container.status_bar.run_tasks(
[Task(t("Downloading HDR10+ Tool"), download_hdr10plus_tool)],
signal_task=True,
can_cancel=True,
Expand All @@ -277,6 +349,7 @@ def app_setup(

app.fastflix.config.save()

# Run startup tasks (FFmpeg config, encoder init) through status bar
startup_tasks = [
Task(t("Gather FFmpeg version"), ffmpeg_configuration),
Task(t("Gather FFprobe version"), ffprobe_configuration),
Expand All @@ -286,17 +359,19 @@ def app_setup(
]

try:
ProgressBar(app, startup_tasks)
container.status_bar.run_tasks(startup_tasks, persist_complete=True)
except Exception:
logger.exception(f"{t('Could not start FastFlix')}!")
sys.exit(1)
container.status_bar.set_state(STATE_ERROR, t("Could not start FastFlix"))
container.setEnabled(True)
return app

container = Container(app)
container.show()
# Encoders are now populated — initialize the encoder UI
container.main.init_encoders_ui()

# container.move(QtGui.QGuiApplication.primaryScreen().availableGeometry().center() - container.rect().center())
screen_geometry = QtGui.QGuiApplication.primaryScreen().availableGeometry()
container.move(screen_geometry.center() - container.rect().center())
# Re-enable UI after startup tasks complete
container.setEnabled(True)
container.status_bar.set_state(STATE_IDLE)

if not app.fastflix.config.disable_version_check:
QtCore.QTimer.singleShot(500, lambda: latest_fastflix(app=app, show_new_dialog=False))
Expand Down
15 changes: 15 additions & 0 deletions fastflix/data/icons/black/crop.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions fastflix/data/icons/black/onyx-data.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions fastflix/data/icons/selected/onyx-data.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 15 additions & 0 deletions fastflix/data/icons/white/crop.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added fastflix/data/icons/white/onyx-data.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading