diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml
index d33b5ad7..6845fbc9 100644
--- a/.github/workflows/build.yaml
+++ b/.github/workflows/build.yaml
@@ -9,7 +9,7 @@ jobs:
build-nix:
strategy:
matrix:
- os: [ ubuntu-20.04, ubuntu-22.04, macos-12, macos-14 ]
+ os: [ ubuntu-24.04, macos-14 ]
runs-on: ${{ matrix.os }}
steps:
@@ -31,7 +31,7 @@ jobs:
- name: Install PySide6 apt Requirements
run: |
sudo apt update
- sudo apt install libopengl0 freeglut3 freeglut3-dev libxcb-icccm4 libxkbcommon-x11-0 libxcb-xkb1 libxcb-render-util0 libxcb-randr0 libxcb-keysyms1 libxcb-image0 libxcb-shape0-dev libxcb-cursor-dev -y
+ sudo apt install libopengl0 freeglut3-dev libxcb-icccm4 libxkbcommon-x11-0 libxcb-xkb1 libxcb-render-util0 libxcb-randr0 libxcb-keysyms1 libxcb-image0 libxcb-shape0-dev libxcb-cursor-dev -y
if: ${{ startsWith(matrix.os, 'ubuntu') }}
- name: Install Python Dependencies
diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml
index 40c125b3..0df8c937 100644
--- a/.github/workflows/test.yaml
+++ b/.github/workflows/test.yaml
@@ -9,7 +9,7 @@ on:
jobs:
lint:
- runs-on: ubuntu-22.04
+ runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
@@ -22,7 +22,7 @@ jobs:
- run: python -m black --check .
test:
- runs-on: ubuntu-22.04
+ runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
@@ -34,7 +34,7 @@ jobs:
- name: Install PySide6 requirements
run: |
sudo apt update
- sudo apt install libopengl0 freeglut3 freeglut3-dev -y
+ sudo apt install libopengl0 freeglut3-dev -y
- name: Install requirements
run: |
diff --git a/.gitignore b/.gitignore
index 0f1e9212..b3e29e06 100644
--- a/.gitignore
+++ b/.gitignore
@@ -123,3 +123,4 @@ icons.py
test.html
iso-639-3.*
build-dir/
+benchmarking/
diff --git a/CHANGES b/CHANGES
index e239d2a2..363cf78c 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,5 +1,18 @@
# Changelog
+## Version 5.9.0
+
+* Adding QP mode for FFmpeg Nvenc encoding
+* Adding more preview positions
+* Adding ultra high quality mode for ffmpeg nvenc encoder
+* Adding Ubuntu 24.04 builds
+* Adding #633 download for stable version of ffmpeg by default on Windows (thanks to Maddie Davis)
+* Fixing #611 Extension type not being selected properly from profiles if encoders isn't switched (thanks to Hankuu)
+* Fixing #628 Custom QP/CRF saved in profile may not be restored correctly (thanks to Gregorio O. DeMojeca)
+* Fixing #631 VVC Level can't be set to 0 anymore (thanks to GT500org)
+* Removing Ubuntu 20.04, 22.04 and Mac 12 builds
+
+
## Version 5.8.2
* Fixing #610 Do not try to divide by zero if HDR metadata has bad values (thanks to Noelle Leigh)
diff --git a/FastFlix.nsi b/FastFlix.nsi
index 7633c1ac..e56b3842 100644
--- a/FastFlix.nsi
+++ b/FastFlix.nsi
@@ -10,7 +10,7 @@
!define PRODUCT_NAME "FastFlix"
!define PRODUCT_AUTHOR "Chris Griffith"
-!define PRODUCT_COPYRIGHT "(c) Chris Griffith 2021-2024"
+!define PRODUCT_COPYRIGHT "(c) Chris Griffith 2019-2025"
VIProductVersion "${PRODUCT_VERSION}"
VIFileVersion "${PRODUCT_VERSION}"
diff --git a/LICENSE b/LICENSE
index 5f8d3b14..08674165 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
The MIT License
-Copyright (c) 2019-2024 Chris Griffith
+Copyright (c) 2019-2025 Chris Griffith
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/README.md b/README.md
index cd72f9ee..3521d2af 100644
--- a/README.md
+++ b/README.md
@@ -139,7 +139,7 @@ Special thanks to [leonardyan](https://github.com/leonardyan) for numerous Chine
# License
-Copyright (C) 2019-2024 Chris Griffith
+Copyright (C) 2019-2025 Chris Griffith
The code itself is licensed under the MIT which you can read in the `LICENSE` file.
Read more about the release licensing in the [docs](docs/README.md) folder.
diff --git a/fastflix/application.py b/fastflix/application.py
index 7757489b..d33a8a30 100644
--- a/fastflix/application.py
+++ b/fastflix/application.py
@@ -11,7 +11,7 @@
from fastflix.models.config import Config, MissingFF
from fastflix.models.fastflix import FastFlix
from fastflix.models.fastflix_app import FastFlixApp
-from fastflix.program_downloads import ask_for_ffmpeg, latest_ffmpeg
+from fastflix.program_downloads import ask_for_ffmpeg, grab_stable_ffmpeg
from fastflix.resources import main_icon, breeze_styles_path
from fastflix.shared import file_date, message, latest_fastflix, DEVMODE
from fastflix.widgets.container import Container
@@ -182,7 +182,7 @@ def app_setup(
except MissingFF as err:
if reusables.win_based and ask_for_ffmpeg():
try:
- ProgressBar(app, [Task(t("Downloading FFmpeg"), latest_ffmpeg)], signal_task=True)
+ ProgressBar(app, [Task(t("Downloading FFmpeg"), grab_stable_ffmpeg)], signal_task=True)
app.fastflix.config.load()
except Exception as err:
logger.exception(str(err))
diff --git a/fastflix/data/languages.yaml b/fastflix/data/languages.yaml
index aa6bd315..3d7edfb8 100644
--- a/fastflix/data/languages.yaml
+++ b/fastflix/data/languages.yaml
@@ -1677,21 +1677,6 @@ Download Cancelled:
ukr: Завантажити Скасовано
kor: 다운로드 취소
ron: Descărcați Anulat
-Download Newest FFmpeg:
- deu: Neuestes FFmpeg herunterladen
- eng: Download Newest FFmpeg
- fra: Télécharger le dernier FFmpeg
- ita: Scarica il nuovo FFmpeg
- spa: Descargar el nuevo FFmpeg
- chs: 下载最新版FFmpeg
- jpn: 最新のFFmpegをダウンロードする
- rus: Скачать новейший FFmpeg
- por: Fazer o download do FFmpeg mais recente
- swe: Hämta Nyaste FFmpeg
- pol: Pobierz Najnowszy FFmpeg
- ukr: Завантажити останню версію FFmpeg
- kor: 최신 FFmpeg 다운로드
- ron: Descărcați cel mai nou FFmpeg
Downloading FFmpeg:
deu: Herunterladen von FFmpeg
eng: Downloading FFmpeg
@@ -7442,7 +7427,8 @@ Drag and Drop to reorder - All items need to be same dimensions:
eng: Drag and Drop to reorder - All items need to be same dimensions
fra: Glissez et déposez pour réorganiser - Tous les éléments doivent avoir les mêmes
dimensions.
- ita: Per riordinare trascina e rilascia - tutti gli elementi devono avere le stesse dimensioni
+ ita: Per riordinare trascina e rilascia - tutti gli elementi devono avere le stesse
+ dimensioni
spa: Arrastrar y soltar para reordenar - Todos los elementos deben tener las mismas
dimensiones
chs: 拖放来重新排序 - 所有项目的尺寸都需要相同
@@ -8824,8 +8810,8 @@ Remove GUI logs and compress conversion logs older than 30 days at exit:
die älter als 30 Tage sind, beim Beenden
fra: Suppression des journaux de l'interface graphique et compression des journaux
de conversion de plus de 30 jours à la sortie.
- ita: Rimuovi registri eventi GUI e comprimeri registri eventi conversione
- più vecchi di 30 giorni all'uscita.
+ ita: Rimuovi registri eventi GUI e comprimeri registri eventi conversione più vecchi
+ di 30 giorni all'uscita.
spa: Elimina los registros GUI y comprime los registros de conversión de más de
30 días al salir.
chs: 在退出时删除GUI日志并压缩超过30天的转换日志
@@ -10685,3 +10671,89 @@ Pattern Match:
ukr: Збіг за зразком
kor: 패턴 일치
ron: Potrivire model
+Download Stable FFmpeg:
+ eng: Download Stable FFmpeg
+ deu: Stable FFmpeg herunterladen
+ fra: Télécharger Stable FFmpeg
+ ita: Scarica FFmpeg stabile
+ spa: Descargar FFmpeg estable
+ jpn: 安定したFFmpegをダウンロード
+ rus: Скачать стабильный FFmpeg
+ por: Descarregar o Stable FFmpeg
+ swe: Ladda ner stabil FFmpeg
+ pol: Pobierz Stable FFmpeg
+ chs: 下载稳定版 FFmpeg
+ ukr: Завантажити стабільний FFmpeg
+ kor: 안정적인 FFmpeg 다운로드
+ ron: Descarcă Stable FFmpeg
+Download Nightly FFmpeg:
+ eng: Download Nightly FFmpeg
+ deu: Neuestes FFmpeg herunterladen
+ fra: Télécharger le dernier FFmpeg
+ ita: Scarica il nuovo FFmpeg
+ spa: Descargar el nuevo FFmpeg
+ chs: 下载最新版FFmpeg
+ jpn: 最新のFFmpegをダウンロードする
+ rus: Скачать новейший FFmpeg
+ por: Fazer o download do FFmpeg mais recente
+ swe: Hämta Nyaste FFmpeg
+ pol: Pobierz Najnowszy FFmpeg
+ ukr: Завантажити останню версію FFmpeg
+ kor: 최신 FFmpeg 다운로드
+ ron: Descărcați cel mai nou FFmpeg
+hq - High Quality, uhq - Ultra High Quality, ll - Low Latency, ull - Ultra Low Latency:
+ eng: hq - High Quality, uhq - Ultra High Quality, ll - Low Latency, ull - Ultra
+ Low Latency
+ deu: hq - Hohe Qualität, uhq - Ultrahohe Qualität, ll - Niedrige Latenzzeit, ull
+ - Ultra-niedrige Latenzzeit
+ fra: hq - Haute qualité, uhq - Ultra haute qualité, ll - Faible latence, ull - Ultra
+ faible latence
+ ita: hq - Alta qualità, uhq - Ultra alta qualità, ll - Bassa latenza, ull - Ultra
+ bassa latenza
+ spa: hq - Alta calidad, uhq - Calidad ultra alta, ll - Baja latencia, ull - Latencia
+ ultra baja
+ jpn: hq - 高画質、uhq - 超高画質、ll - 低遅延、ull - 超低遅延
+ rus: hq - высокое качество, uhq - сверхвысокое качество, ll - низкая задержка, ull
+ - сверхнизкая задержка
+ por: hq - Alta Qualidade, uhq - Ultra Alta Qualidade, ll - Baixa Latência, ull -
+ Ultra Baixa Latência
+ swe: hq - Hög kvalitet, uhq - Ultrahög kvalitet, ll - Låg latens, ull - Ultra låg
+ latens
+ pol: hq - wysoka jakość, uhq - bardzo wysoka jakość, ll - niskie opóźnienie, ull
+ - bardzo niskie opóźnienie
+ chs: hq - 高质量,uhq - 超高质量,ll - 低延迟,ull - 超低延迟
+ ukr: hq - висока якість, uhq - надвисока якість, ll - низька затримка, ull - наднизька
+ затримка
+ kor: hq - 고품질, uhq - 초고화질, ll - 저지연, ull - 초저지연
+ ron: hq - calitate înaltă, uhq - calitate ultra înaltă, ll - latență redusă, ull
+ - latență ultra redusă
+FFmpeg not found!:
+ eng: FFmpeg not found!
+ deu: FFmpeg nicht gefunden!
+ fra: FFmpeg introuvable !
+ ita: FFmpeg non trovato!
+ spa: ¡FFmpeg no encontrado!
+ jpn: FFmpegが見つかりません!
+ rus: FFmpeg не найден!
+ por: FFmpeg não encontrado!
+ swe: FFmpeg hittades inte!
+ pol: Nie znaleziono FFmpeg!
+ chs: 未找到 FFmpeg!
+ ukr: FFmpeg не знайдено!
+ kor: FFmpeg를 찾을 수 없습니다!
+ ron: FFmpeg nu a fost găsit!
+Automatically download FFmpeg?:
+ eng: Automatically download FFmpeg?
+ deu: Automatisches Herunterladen von FFmpeg?
+ fra: Télécharger automatiquement FFmpeg ?
+ ita: Scaricare automaticamente FFmpeg?
+ spa: ¿Descargar automáticamente FFmpeg?
+ jpn: FFmpegを自動的にダウンロードしますか?
+ rus: Автоматически загружать FFmpeg?
+ por: Descarregar automaticamente o FFmpeg?
+ swe: Ladda ner FFmpeg automatiskt?
+ pol: Automatycznie pobierać FFmpeg?
+ chs: 自动下载 FFmpeg?
+ ukr: Автоматично завантажити FFmpeg?
+ kor: FFmpeg를 자동으로 다운로드하시겠습니까?
+ ron: Descărcați automat FFmpeg?
diff --git a/fastflix/encoders/common/helpers.py b/fastflix/encoders/common/helpers.py
index 9db349f4..8797d708 100644
--- a/fastflix/encoders/common/helpers.py
+++ b/fastflix/encoders/common/helpers.py
@@ -99,6 +99,18 @@ def generate_ffmpeg_start(
)
+def rigaya_data(streams, copy_data=False, **_):
+ if not copy_data:
+ return ""
+ datas = []
+ for stream in streams:
+ if stream["codec_type"] == "data":
+ datas.append(str(stream["index"]))
+ if not datas:
+ return ""
+ return f"--data-copy {','.join(datas)}"
+
+
def generate_ending(
audio,
subtitles,
@@ -109,6 +121,7 @@ def generate_ending(
null_ending=False,
output_fps: Union[str, None] = None,
disable_rotate_metadata=False,
+ copy_data=False,
**_,
):
ending = (
@@ -116,6 +129,7 @@ def generate_ending(
f"{'-map_chapters 0' if copy_chapters else '-map_chapters -1'} "
f"{f'-r {output_fps}' if output_fps else ''} "
f"{audio} {subtitles} {cover} "
+ f"{'-map 0:d -c:d copy ' if copy_data else ''}"
)
# In case they use a mp4 container, nix the rotation
diff --git a/fastflix/encoders/common/setting_panel.py b/fastflix/encoders/common/setting_panel.py
index 99cbd81c..dd356076 100644
--- a/fastflix/encoders/common/setting_panel.py
+++ b/fastflix/encoders/common/setting_panel.py
@@ -519,7 +519,7 @@ def update_profile(self):
self.bitrate_radio.setChecked(False)
qp = str(self.app.fastflix.config.encoder_opt(self.profile_name, self.qp_name))
for i, rec in enumerate(self.recommended_qps):
- if rec.startswith(qp):
+ if rec.split(" ")[0] == qp:
self.widgets[self.qp_name].setCurrentIndex(i)
break
else:
diff --git a/fastflix/encoders/ffmpeg_hevc_nvenc/command_builder.py b/fastflix/encoders/ffmpeg_hevc_nvenc/command_builder.py
index 40907940..27c9c9bb 100644
--- a/fastflix/encoders/ffmpeg_hevc_nvenc/command_builder.py
+++ b/fastflix/encoders/ffmpeg_hevc_nvenc/command_builder.py
@@ -25,6 +25,10 @@ def build(fastflix: FastFlix):
if settings.level:
beginning += f"-level:v {settings.level} "
+ if not settings.bitrate:
+ command = (f"{beginning} -qp:v {settings.qp} -preset:v {settings.preset} " f"{settings.extra}") + ending
+ return [Command(command=command, name="Single QP encode", exe="ffmpeg")]
+
pass_log_file = fastflix.current_video.work_path / f"pass_log_file_{secrets.token_hex(10)}"
command_1 = (
diff --git a/fastflix/encoders/ffmpeg_hevc_nvenc/settings_panel.py b/fastflix/encoders/ffmpeg_hevc_nvenc/settings_panel.py
index e8d37a2e..bbe15f9b 100644
--- a/fastflix/encoders/ffmpeg_hevc_nvenc/settings_panel.py
+++ b/fastflix/encoders/ffmpeg_hevc_nvenc/settings_panel.py
@@ -134,8 +134,8 @@ def init_tune(self):
return self._add_combo_box(
label="Tune",
widget_name="tune",
- tooltip="Tune the settings for a particular type of source or situation\nhq - High Quality, ll - Low Latency, ull - Ultra Low Latency",
- options=["hq", "ll", "ull", "lossless"],
+ tooltip="Tune the settings for a particular type of source or situation\nhq - High Quality, uhq - Ultra High Quality, ll - Low Latency, ull - Ultra Low Latency",
+ options=["hq", "uhq", "ll", "ull", "lossless"],
opt="tune",
)
@@ -248,10 +248,9 @@ def init_b_ref_mode(self):
return layout
def init_modes(self):
- layout = self._add_modes(recommended_bitrates, recommended_crfs, qp_name="qp", add_qp=False)
+ layout = self._add_modes(recommended_bitrates, recommended_crfs, qp_name="qp")
self.qp_radio.setChecked(False)
self.bitrate_radio.setChecked(True)
- self.qp_radio.setDisabled(True)
return layout
def mode_update(self):
diff --git a/fastflix/encoders/vvc/settings_panel.py b/fastflix/encoders/vvc/settings_panel.py
index e1f036d0..0bab123b 100644
--- a/fastflix/encoders/vvc/settings_panel.py
+++ b/fastflix/encoders/vvc/settings_panel.py
@@ -115,12 +115,13 @@ def init_tier(self):
)
def init_levels(self):
+ # https://github.com/fraunhoferhhi/vvenc/blob/cf8ba5ed74f8e8c7c9e7b6f81f7fb08bce6241b0/source/Lib/vvenc/vvencCfg.cpp#L159
return self._add_combo_box(
label="IDC Level",
tooltip="Set the IDC level",
widget_name="levelidc",
options=[
- "0",
+ t("Auto"),
"1",
"2",
"2.1",
@@ -135,6 +136,7 @@ def init_levels(self):
"6.1",
"6.2",
"6.3",
+ "15.5",
],
opt="levelidc",
)
@@ -226,12 +228,14 @@ def update_video_encoder_settings(self):
vvc_params_text = self.widgets.vvc_params.text().strip()
+ level = self.widgets.levelidc.currentText() if self.widgets.levelidc.currentIndex() > 0 else None
+
settings = VVCSettings(
preset=self.widgets.preset.currentText(),
max_muxing_queue_size=self.widgets.max_mux.currentText(),
pix_fmt=self.widgets.pix_fmt.currentText().split(":")[1].strip(),
tier=self.widgets.tier.currentText(),
- levelidc=self.widgets.levelidc.currentText(),
+ levelidc=level,
vvc_params=vvc_params_text.split(":") if vvc_params_text else [],
extra=self.ffmpeg_extras,
extra_both_passes=self.widgets.extra_both_passes.isChecked(),
diff --git a/fastflix/flix.py b/fastflix/flix.py
index dd4c7a42..b17dfca0 100644
--- a/fastflix/flix.py
+++ b/fastflix/flix.py
@@ -190,7 +190,10 @@ def ffprobe_configuration(app, config: Config, **_):
def probe(app: FastFlixApp, file: Path) -> Box:
- """Run FFprobe on a file"""
+ """
+ Run FFprobe on a file
+ ffprobe -v quiet -loglevel panic -print_format json -show_format -show_streams
+ """
command = [
f"{app.fastflix.config.ffprobe}",
"-v",
diff --git a/fastflix/models/video.py b/fastflix/models/video.py
index 728f57a2..95921de6 100644
--- a/fastflix/models/video.py
+++ b/fastflix/models/video.py
@@ -109,6 +109,7 @@ class VideoSettings(BaseModel):
brightness: Optional[str] = None
contrast: Optional[str] = None
saturation: Optional[str] = None
+ copy_data: bool = False
video_encoder_settings: Optional[
Union[
x265Settings,
diff --git a/fastflix/program_downloads.py b/fastflix/program_downloads.py
index 4d473c0a..7ceae300 100644
--- a/fastflix/program_downloads.py
+++ b/fastflix/program_downloads.py
@@ -5,6 +5,7 @@
import shutil
import sys
from pathlib import Path
+import re
import requests
import reusables
@@ -43,8 +44,16 @@ def ask_for_ffmpeg():
sys.exit(1)
-def latest_ffmpeg(signal, stop_signal, **_):
+ffmpeg_version_re = re.compile(r"ffmpeg-n(\d+\.\d+)-latest-win64-gpl-")
+
+
+def grab_stable_ffmpeg(signal, stop_signal, **_):
+ return latest_ffmpeg(signal, stop_signal, ffmpeg_version="stable")
+
+
+def latest_ffmpeg(signal, stop_signal, ffmpeg_version="latest", **_):
stop = False
+ logger.debug(f"Downloading {ffmpeg_version} FFmpeg")
def stop_me():
nonlocal stop
@@ -76,13 +85,18 @@ def stop_me():
return
gpl_ffmpeg = None
- for asset in data["assets"]:
- if "master-latest-win64-gpl.zip" in asset["name"]:
- gpl_ffmpeg = asset
- break
- elif asset["name"].startswith("ffmpeg-N-") and asset["name"].endswith("win64-gpl.zip"):
- gpl_ffmpeg = asset
- break
+
+ if ffmpeg_version == "latest":
+ for asset in data["assets"]:
+ if "master-latest-win64-gpl.zip" in asset["name"]:
+ gpl_ffmpeg = asset
+ break
+ else:
+ versions = []
+ for asset in data["assets"]:
+ if ver_match := ffmpeg_version_re.search(asset["name"]):
+ versions.append((float(ver_match.group(1)), asset))
+ gpl_ffmpeg = sorted(versions, key=lambda x: x[0], reverse=True)[0][1]
if not gpl_ffmpeg:
shutil.rmtree(extract_folder, ignore_errors=True)
@@ -93,6 +107,8 @@ def stop_me():
)
raise Exception()
+ logger.debug(f"Downloading version {gpl_ffmpeg['name']}")
+
req = requests.get(gpl_ffmpeg["browser_download_url"], stream=True)
filename = ffmpeg_folder / "ffmpeg-full.zip"
diff --git a/fastflix/version.py b/fastflix/version.py
index 60506bfb..8bd495be 100644
--- a/fastflix/version.py
+++ b/fastflix/version.py
@@ -1,4 +1,4 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-__version__ = "5.8.2"
+__version__ = "5.9.0"
__author__ = "Chris Griffith"
diff --git a/fastflix/widgets/container.py b/fastflix/widgets/container.py
index 271a0f8b..20f52266 100644
--- a/fastflix/widgets/container.py
+++ b/fastflix/widgets/container.py
@@ -16,7 +16,7 @@
from fastflix.language import t
from fastflix.models.config import setting_types, get_preset_defaults
from fastflix.models.fastflix_app import FastFlixApp
-from fastflix.program_downloads import latest_ffmpeg
+from fastflix.program_downloads import latest_ffmpeg, grab_stable_ffmpeg
from fastflix.resources import main_icon, get_icon, changes_file, local_changes_file, local_package_changes_file
from fastflix.shared import clean_logs, error_message, latest_fastflix, message, yes_no_message
from fastflix.widgets.about import About
@@ -244,7 +244,10 @@ def init_menu(self):
)
version_action.triggered.connect(lambda: latest_fastflix(show_new_dialog=True, app=self.app))
- ffmpeg_update_action = QAction(self.si(QtWidgets.QStyle.SP_ArrowDown), t("Download Newest FFmpeg"), self)
+ ffmpeg_update_stable_action = QAction(self.si(QtWidgets.QStyle.SP_ArrowDown), t("Download Stable FFmpeg"), self)
+ ffmpeg_update_stable_action.triggered.connect(self.download_stable_ffmpeg)
+
+ ffmpeg_update_action = QAction(self.si(QtWidgets.QStyle.SP_ArrowDown), t("Download Nightly FFmpeg"), self)
ffmpeg_update_action.triggered.connect(self.download_ffmpeg)
clean_logs_action = QAction(self.si(QtWidgets.QStyle.SP_DialogResetButton), t("Clean Old Logs"), self)
@@ -263,6 +266,7 @@ def init_menu(self):
help_menu.addSeparator()
help_menu.addAction(version_action)
if reusables.win_based:
+ help_menu.addAction(ffmpeg_update_stable_action)
help_menu.addAction(ffmpeg_update_action)
help_menu.addSeparator()
help_menu.addAction(about_action)
@@ -331,13 +335,19 @@ def open_issues(self):
def show_log_dir(self):
OpenFolder(self, str(self.app.fastflix.log_path)).run()
- def download_ffmpeg(self):
+ def download_stable_ffmpeg(self):
+ self.download_ffmpeg(ffmpeg_version="stable")
+
+ def download_ffmpeg(self, ffmpeg_version="latest"):
ffmpeg_folder = Path(user_data_dir("FFmpeg", appauthor=False, roaming=True)) / "bin"
ffmpeg = ffmpeg_folder / "ffmpeg.exe"
ffprobe = ffmpeg_folder / "ffprobe.exe"
try:
self.pb = ProgressBar(
- self.app, [Task(t("Downloading FFmpeg"), latest_ffmpeg)], signal_task=True, can_cancel=True
+ self.app,
+ [Task(t("Downloading FFmpeg"), grab_stable_ffmpeg if ffmpeg_version == "stable" else latest_ffmpeg)],
+ signal_task=True,
+ can_cancel=True,
)
except FastFlixInternalException:
pass
diff --git a/fastflix/widgets/main.py b/fastflix/widgets/main.py
index db164960..6865925f 100644
--- a/fastflix/widgets/main.py
+++ b/fastflix/widgets/main.py
@@ -134,6 +134,7 @@ class MainWidgets(BaseModel):
output_type_combo: QtWidgets.QComboBox = Field(default_factory=QtWidgets.QComboBox)
output_directory_select: QtWidgets.QPushButton = None
model_config = ConfigDict(arbitrary_types_allowed=True)
+ copy_data: QtWidgets.QCheckBox = None
def items(self):
for key in dir(self):
@@ -394,8 +395,8 @@ def init_thumb_time_selector(self):
self.widgets.thumb_time = QtWidgets.QSlider(QtCore.Qt.Horizontal)
self.widgets.thumb_time.setMinimum(1)
- self.widgets.thumb_time.setMaximum(10)
- self.widgets.thumb_time.setValue(2)
+ self.widgets.thumb_time.setMaximum(100)
+ self.widgets.thumb_time.setValue(25)
self.widgets.thumb_time.setTickPosition(QtWidgets.QSlider.TicksBelow)
self.widgets.thumb_time.setTickInterval(1)
self.widgets.thumb_time.setAutoFillBackground(False)
@@ -572,8 +573,21 @@ def init_checkboxes(self):
extra_details_layout = QtWidgets.QVBoxLayout()
extra_details_layout.addWidget(self.widgets.deinterlace)
extra_details_layout.addWidget(self.widgets.remove_hdr)
-
transform_layout.addLayout(extra_details_layout)
+
+ # another_layout = QtWidgets.QVBoxLayout()
+ #
+ # self.widgets.copy_data = QtWidgets.QCheckBox(t("Copy Data"))
+ # self.widgets.copy_data.setChecked(False)
+ # self.widgets.copy_data.toggled.connect(self.page_update)
+ # self.widgets.copy_data.setToolTip(
+ # f'{t("Copy all data streams from the source file.")}'
+ # )
+ #
+ # another_layout.addWidget(self.widgets.copy_data)
+ # another_layout.addWidget(QtWidgets.QWidget())
+ # transform_layout.addLayout(another_layout)
+
return transform_layout
def init_video_track_select(self):
@@ -650,6 +664,7 @@ def set_profile(self):
if self.app.fastflix.current_video:
self.video_options.new_source()
+ self.update_output_type()
finally:
# Hack to prevent a lot of thumbnail generation
self.loading_video = False
@@ -757,18 +772,12 @@ def change_encoder(self):
if not self.initialized or not self.convert_to:
return
self.video_options.change_conversion(self.convert_to)
- self.widgets.output_type_combo.clear()
- self.widgets.output_type_combo.addItems(self.current_encoder.video_extensions)
- self.widgets.output_type_combo.setCurrentText(self.app.fastflix.config.opt("output_type"))
- if not self.app.fastflix.current_video:
- return
-
- last = self.widgets.output_type_combo.currentText()
+ self.update_output_type()
+ def update_output_type(self):
self.widgets.output_type_combo.clear()
self.widgets.output_type_combo.addItems(self.current_encoder.video_extensions)
- if last in {self.widgets.output_type_combo.itemText(i) for i in range(self.widgets.output_type_combo.count())}:
- self.widgets.output_type_combo.setCurrentText(last)
+ self.widgets.output_type_combo.setCurrentText(self.app.fastflix.config.opt("output_type"))
@property
def current_encoder(self):
@@ -1624,7 +1633,7 @@ def remove_hdr(self) -> bool:
@property
def preview_place(self) -> Union[float, int]:
- ticks = self.app.fastflix.current_video.duration / 10
+ ticks = self.app.fastflix.current_video.duration / 100
return (self.widgets.thumb_time.value() - 1) * ticks
@reusables.log_exception("fastflix", show_traceback=False)
@@ -1739,6 +1748,7 @@ def get_all_settings(self):
video_title=self.video_options.advanced.video_title.text(),
video_track_title=self.video_options.advanced.video_track_title.text(),
remove_hdr=self.remove_hdr,
+ # copy_data=self.widgets.copy_data.isChecked(),
)
self.video_options.get_settings()
diff --git a/fastflix/widgets/video_options.py b/fastflix/widgets/video_options.py
index e12bc80c..49831cfd 100644
--- a/fastflix/widgets/video_options.py
+++ b/fastflix/widgets/video_options.py
@@ -163,7 +163,7 @@ def get_settings(self):
try:
del self.app.fastflix.current_video.video_settings.video_encoder_settings
- except KeyError:
+ except (KeyError, AttributeError):
pass
self.current_settings.update_video_encoder_settings()
diff --git a/pyproject.toml b/pyproject.toml
index e8cdbd18..a41a789c 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -38,6 +38,7 @@ dependencies = [
"python-box[all]>=6.0,<7.0",
"requests>=2.28,<3.0",
"reusables>=0.9.6,<0.10.0",
+ "setuptools>=75.8",
]
[project.optional-dependencies]