From e2b3212fe95628ecc0a2e9fb3b9d4291addc223f Mon Sep 17 00:00:00 2001 From: Chris Griffith Date: Sat, 22 Feb 2025 15:08:06 -0600 Subject: [PATCH] Version 5.9.0 (#634) * 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 --- .github/workflows/build.yaml | 4 +- .github/workflows/test.yaml | 6 +- .gitignore | 1 + CHANGES | 13 +++ FastFlix.nsi | 2 +- LICENSE | 2 +- README.md | 2 +- fastflix/application.py | 4 +- fastflix/data/languages.yaml | 108 +++++++++++++++--- fastflix/encoders/common/helpers.py | 14 +++ fastflix/encoders/common/setting_panel.py | 2 +- .../ffmpeg_hevc_nvenc/command_builder.py | 4 + .../ffmpeg_hevc_nvenc/settings_panel.py | 7 +- fastflix/encoders/vvc/settings_panel.py | 8 +- fastflix/flix.py | 5 +- fastflix/models/video.py | 1 + fastflix/program_downloads.py | 32 ++++-- fastflix/version.py | 2 +- fastflix/widgets/container.py | 18 ++- fastflix/widgets/main.py | 36 +++--- fastflix/widgets/video_options.py | 2 +- pyproject.toml | 1 + 22 files changed, 211 insertions(+), 63 deletions(-) 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]