From 1ed0a1600747793b70e95664923c0ad7f4117786 Mon Sep 17 00:00:00 2001 From: Chris Griffith Date: Tue, 11 May 2021 15:14:35 -0500 Subject: [PATCH] Version 4.2.1 (#222) * Adding support for source_directory and output_name_format in config file (thanks to cwills75) * Fixing #129 no subtitles should be enabled, removing need for mkvpropedit (thanks to wiedemanu) * Fixing network paths not working on Windows * Changing difference between requirements and requirements-build and removing the build version --- .github/workflows/build_linux.yaml | 4 +- .github/workflows/build_linux_legacy.yaml | 4 +- .github/workflows/build_mac.yaml | 4 +- .github/workflows/build_windows.yaml | 4 +- CHANGES | 7 +++ FastFlix_Nix_OneFile.spec | 2 +- FastFlix_Windows_Installer.spec | 2 +- FastFlix_Windows_OneFile.spec | 2 +- fastflix/command_runner.py | 54 +---------------------- fastflix/encoders/common/subtitles.py | 6 ++- fastflix/entry.py | 2 +- fastflix/flix.py | 8 ++-- fastflix/models/config.py | 11 +++-- fastflix/version.py | 2 +- fastflix/widgets/background_tasks.py | 27 +----------- fastflix/widgets/main.py | 35 +++++++-------- requirements-build.txt | 15 ------- requirements.txt | 28 ++++++------ 18 files changed, 66 insertions(+), 151 deletions(-) delete mode 100644 requirements-build.txt diff --git a/.github/workflows/build_linux.yaml b/.github/workflows/build_linux.yaml index 10e67d10..24281b0a 100644 --- a/.github/workflows/build_linux.yaml +++ b/.github/workflows/build_linux.yaml @@ -27,8 +27,8 @@ jobs: - name: Insatll requirements run: | python -m pip install --upgrade pip setuptools --ignore-installed - python -m pip install --upgrade wheel typing_extensions - python -m pip install -r requirements-build.txt + python -m pip install --upgrade wheel typing_extensions pyinstaller==4.3 + python -m pip install -r requirements.txt python -m pip install --upgrade pyqt5-tools - name: Grab iso-639 lists diff --git a/.github/workflows/build_linux_legacy.yaml b/.github/workflows/build_linux_legacy.yaml index 50b6e6d0..5468c791 100644 --- a/.github/workflows/build_linux_legacy.yaml +++ b/.github/workflows/build_linux_legacy.yaml @@ -27,8 +27,8 @@ jobs: - name: Insatll requirements run: | python -m pip install --upgrade pip setuptools --ignore-installed - python -m pip install --upgrade wheel typing_extensions - python -m pip install -r requirements-build.txt + python -m pip install --upgrade wheel typing_extensions pyinstaller==4.3 + python -m pip install -r requirements.txt python -m pip install --upgrade pyqt5-tools - name: Grab iso-639 lists diff --git a/.github/workflows/build_mac.yaml b/.github/workflows/build_mac.yaml index 03f8d163..73f7606f 100644 --- a/.github/workflows/build_mac.yaml +++ b/.github/workflows/build_mac.yaml @@ -27,8 +27,8 @@ jobs: - name: Insatll requirements run: | python -m pip install --upgrade pip setuptools --ignore-installed - python -m pip install --upgrade wheel typing_extensions - python -m pip install -r requirements-build.txt + python -m pip install --upgrade wheel typing_extensions pyinstaller==4.3 + python -m pip install -r requirements.txt - name: Grab iso-639 lists run: | diff --git a/.github/workflows/build_windows.yaml b/.github/workflows/build_windows.yaml index 1dfea57f..89784c79 100644 --- a/.github/workflows/build_windows.yaml +++ b/.github/workflows/build_windows.yaml @@ -30,8 +30,8 @@ jobs: shell: cmd run: | python -m pip install --upgrade pip setuptools --ignore-installed - python -m pip install --upgrade pypiwin32 wheel typing_extensions - python -m pip install -r requirements-build.txt + python -m pip install --upgrade pypiwin32 wheel typing_extensions pyinstaller==4.2 + python -m pip install -r requirements.txt - name: Grab iso-639 lists shell: powershell diff --git a/CHANGES b/CHANGES index e9914983..8c0bc1c5 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,12 @@ # Changelog +## Version 4.2.1 + +* Adding support for source_directory and output_name_format in config file (thanks to cwills75) +* Fixing #129 no subtitles should be enabled, removing need for mkvpropedit (thanks to wiedemanu) +* Fixing network paths not working on Windows +* Changing difference between requirements and requirements-build and removing the build version + ## Version 4.2.0 * Adding #109 NVENC HEVC support based on FFmpeg (thanks to Zeid164) diff --git a/FastFlix_Nix_OneFile.spec b/FastFlix_Nix_OneFile.spec index 0cd04122..c12fd25f 100644 --- a/FastFlix_Nix_OneFile.spec +++ b/FastFlix_Nix_OneFile.spec @@ -12,7 +12,7 @@ for root, dirs, files in os.walk('fastflix'): all_fastflix_files.append((os.path.join(root,file), root)) all_imports = collect_submodules('pydantic') + ['dataclasses', 'colorsys', 'typing_extensions'] -with open("requirements-build.txt", "r") as reqs: +with open("requirements.txt", "r") as reqs: for line in reqs: package = line.split("=")[0].split(">")[0].split("<")[0].replace('"', '').replace("'", '').strip() if package not in ("pyinstaller", "pypiwin32"): diff --git a/FastFlix_Windows_Installer.spec b/FastFlix_Windows_Installer.spec index c1fc2d71..a23c7fee 100644 --- a/FastFlix_Windows_Installer.spec +++ b/FastFlix_Windows_Installer.spec @@ -12,7 +12,7 @@ for root, dirs, files in os.walk('fastflix'): all_fastflix_files.append((os.path.join(root,file), root)) all_imports = collect_submodules('pydantic') + ['dataclasses', 'colorsys', 'typing_extensions'] -with open("requirements-build.txt", "r") as reqs: +with open("requirements.txt", "r") as reqs: for line in reqs: package = line.split("=")[0].split(">")[0].split("<")[0].replace('"', '').replace("'", '').strip() if package not in ("pyinstaller", "pypiwin32"): diff --git a/FastFlix_Windows_OneFile.spec b/FastFlix_Windows_OneFile.spec index 1634a192..c1d7208e 100644 --- a/FastFlix_Windows_OneFile.spec +++ b/FastFlix_Windows_OneFile.spec @@ -12,7 +12,7 @@ for root, dirs, files in os.walk('fastflix'): all_fastflix_files.append((os.path.join(root,file), root)) all_imports = collect_submodules('pydantic') + ['dataclasses', 'colorsys', 'typing_extensions'] -with open("requirements-build.txt", "r") as reqs: +with open("requirements.txt", "r") as reqs: for line in reqs: package = line.split("=")[0].split(">")[0].split("<")[0].replace('"', '').replace("'", '').strip() if package not in ("pyinstaller"): diff --git a/fastflix/command_runner.py b/fastflix/command_runner.py index de8ec6e3..a110e000 100644 --- a/fastflix/command_runner.py +++ b/fastflix/command_runner.py @@ -19,7 +19,6 @@ class BackgroundRunner: def __init__(self, log_queue): self.process = None - self.process_two = None self.killed = False self.output_file = None self.error_output_file = None @@ -45,7 +44,7 @@ def start_exec(self, command, work_dir: str = None, shell: bool = False, errors= self.success_message = successes logger.info(f"Running command: {command}") self.process = Popen( - shlex.split(command) if not shell and isinstance(command, str) else command, + shlex.split(command.replace("\\", "\\\\")) if not shell and isinstance(command, str) else command, shell=shell, cwd=work_dir, stdout=open(self.output_file, "w"), @@ -58,39 +57,6 @@ def start_exec(self, command, work_dir: str = None, shell: bool = False, errors= Thread(target=self.read_output).start() - def start_piped_exec(self, command_one, command_two, work_dir, errors=(), successes=()): - self.clean() - logger.info(f"Running commands: {' '.join(command_one)} | {' '.join(command_two)}") - Path(work_dir).mkdir(exist_ok=True, parents=True) - self.output_file = Path(work_dir) / f"encoder_output_{secrets.token_hex(6)}.log" - self.error_output_file = Path(work_dir) / f"encoder_error_output_{secrets.token_hex(6)}.log" - self.output_file.touch(exist_ok=True) - self.error_output_file.touch(exist_ok=True) - self.error_message = errors - self.success_message = successes - - self.process = Popen( - command_one, - cwd=work_dir, - stdout=PIPE, - stderr=PIPE, - stdin=PIPE, # FFmpeg can try to read stdin and wrecks havoc on linux - ) - - self.process_two = Popen( - command_two, - cwd=work_dir, - stdout=open(self.output_file, "w"), - stderr=open(self.error_output_file, "w"), - stdin=self.process.stdout, - encoding="utf-8", - ) - - self.error_detected = False - self.started_at = datetime.datetime.now(datetime.timezone.utc) - - Thread(target=self.read_output).start() - def read_output(self): with open(self.output_file, "r", encoding="utf-8", errors="ignore") as out_file, open( self.error_output_file, "r", encoding="utf-8", errors="ignore" @@ -141,31 +107,17 @@ def read(self, limit=None): def is_alive(self): if not self.process: return False - if self.process_two: - # TODO make sure process 1 dies cleanly - return True if self.process_two.poll() is None else False return True if self.process.poll() is None else False def clean(self): self.kill(log=False) self.process = None - self.process_two = None self.error_detected = False self.success_detected = False self.killed = False self.started_at = None def kill(self, log=True): - if self.process_two and self.process_two.poll() is None: - if log: - logger.info(f"Killing worker process {self.process_two.pid}") - try: - self.process_two.terminate() - self.process_two.kill() - except Exception as err: - if log: - logger.exception(f"Couldn't terminate process: {err}") - if self.process and self.process.poll() is None: if log: logger.info(f"Killing worker process {self.process.pid}") @@ -182,15 +134,11 @@ def kill(self, log=True): self.killed = True def pause(self): - if self.process_two: - return False if not self.process: return False self.process.suspend() def resume(self): - if self.process_two: - return False if not self.process: return False self.process.resume() diff --git a/fastflix/encoders/common/subtitles.py b/fastflix/encoders/common/subtitles.py index 3b935f86..612a2c9b 100644 --- a/fastflix/encoders/common/subtitles.py +++ b/fastflix/encoders/common/subtitles.py @@ -12,6 +12,7 @@ def build_subtitle( command_list = [] burn_in_track = None burn_in_type = None + subs_enabled = False for track in subtitle_tracks: if track.burn_in: burn_in_track = track.index @@ -21,8 +22,11 @@ def build_subtitle( command_list.append(f"-map {subtitle_file_index}:{track.index} -c:{outdex} copy ") if track.disposition: command_list.append(f"-disposition:{outdex} {track.disposition}") + if track.disposition in ("default", "forced"): + subs_enabled = True else: command_list.append(f"-disposition:{outdex} 0") command_list.append(f"-metadata:s:{outdex} language='{track.language}'") - + if not subs_enabled: + command_list.append("-default_mode infer_no_subs") return " ".join(command_list), burn_in_track, burn_in_type diff --git a/fastflix/entry.py b/fastflix/entry.py index 881284e7..1abef821 100644 --- a/fastflix/entry.py +++ b/fastflix/entry.py @@ -27,7 +27,7 @@ def separate_app_process(worker_queue, status_queue, log_queue, queue_list, queue_lock): - """ This prevents any QT components being imported in the main process""" + """This prevents any QT components being imported in the main process""" from fastflix.application import start_app freeze_support() diff --git a/fastflix/flix.py b/fastflix/flix.py index a1ce47ad..ceb2a07f 100644 --- a/fastflix/flix.py +++ b/fastflix/flix.py @@ -89,7 +89,7 @@ def execute(command: List, work_dir: Union[Path, str] = None, timeout: int = Non def ffmpeg_configuration(app, config: Config, **_): - """ Extract the version and libraries available from the specified version of FFmpeg """ + """Extract the version and libraries available from the specified version of FFmpeg""" res = execute([f"{config.ffmpeg}", "-version"]) if res.returncode != 0: raise FlixError(f'"{config.ffmpeg}" file not found') @@ -108,7 +108,7 @@ def ffmpeg_configuration(app, config: Config, **_): def ffprobe_configuration(app, config: Config, **_): - """ Extract the version of ffprobe """ + """Extract the version of ffprobe""" res = execute([f"{config.ffprobe}", "-version"]) if res.returncode != 0: raise FlixError(f'"{config.ffprobe}" file not found') @@ -120,7 +120,7 @@ def ffprobe_configuration(app, config: Config, **_): def probe(app: FastFlixApp, file: Path) -> Box: - """ Run FFprobe on a file """ + """Run FFprobe on a file""" command = [ f"{app.fastflix.config.ffprobe}", "-v", @@ -273,7 +273,7 @@ def get_auto_crop( def detect_interlaced(app: FastFlixApp, config: Config, source: Path, **_): - """ http://www.aktau.be/2013/09/22/detecting-interlaced-video-with-ffmpeg/ """ + """http://www.aktau.be/2013/09/22/detecting-interlaced-video-with-ffmpeg/""" # Interlaced # [Parsed_idet_0 @ 00000] Repeated Fields: Neither: 815 Top: 88 Bottom: 98 diff --git a/fastflix/models/config.py b/fastflix/models/config.py index 6c776837..ae41c1c9 100644 --- a/fastflix/models/config.py +++ b/fastflix/models/config.py @@ -122,11 +122,12 @@ class Config(BaseModel): ffmpeg: Path = Field(default_factory=lambda: find_ffmpeg_file("ffmpeg")) ffprobe: Path = Field(default_factory=lambda: find_ffmpeg_file("ffprobe")) hdr10plus_parser: Optional[Path] = Field(default_factory=lambda: where("hdr10plus_parser")) - mkvpropedit: Optional[Path] = Field(default_factory=lambda: where("mkvpropedit")) nvencc: Optional[Path] = Field(default_factory=lambda: where("NVEncC")) output_directory: Optional[Path] = False + source_directory: Optional[Path] = False + output_name_format: str = "{source}-fastflix-{rand_4}.{ext}" flat_ui: bool = True - language: str = "en" + language: str = "eng" logging_level: int = 10 crop_detect_points: int = 10 continue_on_failure: bool = True @@ -194,7 +195,7 @@ def load(self): "there may be non-recoverable errors while loading it." ) - paths = ("work_path", "ffmpeg", "ffprobe", "hdr10plus_parser", "mkvpropedit", "nvencc", "output_directory") + paths = ("work_path", "ffmpeg", "ffprobe", "hdr10plus_parser", "nvencc", "output_directory", "source_directory") for key, value in data.items(): if key == "profiles": self.profiles = {} @@ -233,10 +234,8 @@ def load(self): raise err from None if not self.hdr10plus_parser: self.hdr10plus_parser = where("hdr10plus_parser") - if not self.mkvpropedit: - self.mkvpropedit = where("mkvpropedit") if not self.nvencc: - self.mkvpropedit = where("NVEncC") + self.nvencc = where("NVEncC") self.profiles.update(get_preset_defaults()) if self.selected_profile not in self.profiles: diff --git a/fastflix/version.py b/fastflix/version.py index a323c5f2..8d965533 100644 --- a/fastflix/version.py +++ b/fastflix/version.py @@ -1,4 +1,4 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -__version__ = "4.2.0" +__version__ = "4.2.1" __author__ = "Chris Griffith" diff --git a/fastflix/widgets/background_tasks.py b/fastflix/widgets/background_tasks.py index 85b71a4e..b23bfa2d 100644 --- a/fastflix/widgets/background_tasks.py +++ b/fastflix/widgets/background_tasks.py @@ -12,7 +12,7 @@ logger = logging.getLogger("fastflix") -__all__ = ["ThumbnailCreator", "ExtractSubtitleSRT", "SubtitleFix", "ExtractHDR10"] +__all__ = ["ThumbnailCreator", "ExtractSubtitleSRT", "ExtractHDR10"] class ThumbnailCreator(QtCore.QThread): @@ -40,31 +40,6 @@ def run(self): self.main.thumbnail_complete.emit(1) -class SubtitleFix(QtCore.QThread): - def __init__(self, main, mkv_prop_edit, video_path): - super().__init__(main) - self.main = main - self.mkv_prop_edit = mkv_prop_edit - self.video_path = video_path - - def run(self): - output_file = clean_file_string(self.video_path) - self.main.thread_logging_signal.emit(f'INFO:{t("Will fix first subtitle track to not be default")}') - try: - result = run( - [self.mkv_prop_edit, output_file, "--edit", "track:s1", "--set", "flag-default=0"], - stdout=PIPE, - stderr=STDOUT, - ) - except Exception as err: - self.main.thread_logging_signal.emit(f'ERROR:{t("Could not fix first subtitle track")} - {err}') - else: - if result.returncode != 0: - self.main.thread_logging_signal.emit( - f'WARNING:{t("Could not fix first subtitle track")}: {result.stdout}' - ) - - class ExtractSubtitleSRT(QtCore.QThread): def __init__(self, app: FastFlixApp, main, index, signal): super().__init__(main) diff --git a/fastflix/widgets/main.py b/fastflix/widgets/main.py index af7618e2..12e1fdc0 100644 --- a/fastflix/widgets/main.py +++ b/fastflix/widgets/main.py @@ -1,6 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- import copy +import datetime import importlib.machinery # Needed for pyinstaller import logging import math @@ -46,7 +47,7 @@ ) from fastflix.shared import error_message, message, time_to_number, yes_no_message, clean_file_string from fastflix.windows_tools import show_windows_notification -from fastflix.widgets.background_tasks import SubtitleFix, ThumbnailCreator +from fastflix.widgets.background_tasks import ThumbnailCreator from fastflix.widgets.progress_bar import ProgressBar, Task from fastflix.widgets.video_options import VideoOptions @@ -815,7 +816,8 @@ def open_file(self): filter="Video Files (*.mkv *.mp4 *.m4v *.mov *.avi *.divx *.webm *.mpg *.mp2 *.mpeg *.mpe *.mpv *.ogg *.m4p" " *.wmv *.mov *.qt *.flv *.hevc *.gif *.webp *.vob *.ogv *.ts *.mts *.m2ts *.yuv *.rm *.svi *.3gp *.3g2)", directory=str( - self.app.fastflix.current_video.source.parent if self.app.fastflix.current_video else Path.home() + self.app.fastflix.config.source_directory + or (self.app.fastflix.current_video.source.parent if self.app.fastflix.current_video else Path.home()) ), ) if not filename or not filename[0]: @@ -846,11 +848,20 @@ def open_file(self): @property def generate_output_filename(self): + source = self.input_video.stem + iso_datetime = datetime.datetime.now().isoformat().replace(":", "-").split(".")[0] + rand_4 = secrets.token_hex(2) + rand_8 = secrets.token_hex(4) + ext = self.current_encoder.video_extension + out_loc = f"{Path('~').expanduser()}{os.sep}" if self.app.fastflix.config.output_directory: - return f"{self.app.fastflix.config.output_directory / self.input_video.stem}-fastflix-{secrets.token_hex(2)}.{self.current_encoder.video_extension}" + out_loc = f"{self.app.fastflix.config.output_directory}{os.sep}" if self.input_video: - return f"{self.input_video.parent / self.input_video.stem}-fastflix-{secrets.token_hex(2)}.{self.current_encoder.video_extension}" - return f"{Path('~').expanduser()}{os.sep}fastflix-{secrets.token_hex(2)}.{self.current_encoder.video_extension}" + out_loc = f"{self.input_video.parent}{os.sep}" + + gen_string = self.app.fastflix.config.output_name_format or "{source}-fastflix-{rand_4}.{ext}" + + return out_loc + gen_string.format(source=source, datetime=iso_datetime, rand_4=rand_4, rand_8=rand_8, ext=ext) @property def output_video(self): @@ -1785,20 +1796,6 @@ def dragMoveEvent(self, event): def status_update(self): logger.debug(f"Updating queue from command worker") - - with self.app.fastflix.queue_lock: - fixed_vids = [] - for i, video in enumerate(self.app.fastflix.queue): - if video.status.complete and not video.status.subtitle_fixed: - if video.video_settings.subtitle_tracks and not video.video_settings.subtitle_tracks[0].disposition: - if mkv_prop_edit := shutil.which("mkvpropedit"): - worker = SubtitleFix(self, mkv_prop_edit, video.video_settings.output_path) - worker.start() - fixed_vids.append(i) - for index in fixed_vids: - video = self.app.fastflix.queue.pop(index) - video.status.subtitle_fixed = True - self.app.fastflix.queue.insert(index, video) save_queue(self.app.fastflix.queue, self.app.fastflix.queue_path, self.app.fastflix.config) self.video_options.update_queue() diff --git a/requirements-build.txt b/requirements-build.txt deleted file mode 100644 index 3ca1cc45..00000000 --- a/requirements-build.txt +++ /dev/null @@ -1,15 +0,0 @@ -appdirs==1.4.4 -colorama==0.4.4 -coloredlogs==15.0 -iso639-lang==0.0.9 -mistune==0.8.4 -pathvalidate==2.3.2 -psutil==5.8.0 -pydantic==1.8.1 -pyinstaller==4.2 -pyqt5==5.15.3 -python-box==5.3.0 -qtpy==1.9.0 -requests==2.25.1 -reusables==0.9.6 -ruamel.yaml<0.16 diff --git a/requirements.txt b/requirements.txt index 7b86ccba..2bee3726 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,14 +1,14 @@ -appdirs -colorama -coloredlogs -iso639-lang -mistune -pathvalidate -psutil -pydantic -pyqt5 -python-box -qtpy -requests -reusables -ruamel.yaml +appdirs==1.4.4 +colorama==0.4.4 +coloredlogs==15.0 +iso639-lang==0.0.9 +mistune==0.8.4 +pathvalidate==2.3.2 +psutil==5.8.0 +pydantic==1.8.1 +pyqt5==5.15.3 +python-box==5.3.0 +qtpy==1.9.0 +requests==2.25.1 +reusables==0.9.6 +ruamel.yaml<0.16