Skip to content

Commit

Permalink
Version 4.9.0 (#328)
Browse files Browse the repository at this point in the history
* Adding #109 Support for AVC and HEVC QSV encoding with rigaya's QSVEncC (thanks to msaintauret)
* Adding #196 Support for AVC and HEVC Apple Videotoolbox encoder (thanks to Kay Singh)
* Adding #323 ignore errors options options for queue (thanks to Don Gafford)
* Adding #331 NVEncC API v10 Quality presets: P1 to P7 (thanks to Wontell)
* Fixing #321 dhdr10_opt not added for x265 commands (thanks to GizmoDudex)
* Fixing #327 FastFlix Duplicates encoding task and encodes same movie to infinity (thanks to Wontell)
* Fixing #324 NVEncC wrong Interlace Value set by FastFlix (thanks to Wontell)
* Fixing #278 FastFlix occasionally getting stuck on a single video in a queue (thanks to kamild1996)
* Fixing #330 "Remove Metadata" only removes video metadata for Rigaya's hardware encoders (thanks to wynterca)
* Fixing level was not being passed to hardware encoders
  • Loading branch information
cdgriffith authored Apr 24, 2022
1 parent 0dec62f commit 2770ca8
Show file tree
Hide file tree
Showing 49 changed files with 2,646 additions and 434 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
path: ${{ env.pythonLocation }}
key: ${{ env.pythonLocation }}-black

- run: pip install black==21.8b0
- run: pip install black==22.3.0
- run: python -m black --check .

test:
Expand Down
9 changes: 3 additions & 6 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,12 @@ repos:
- id: check-toml
- id: detect-private-key
- id: end-of-file-fixer
# - repo: https://github.com/PyCQA/isort
# rev: master
# hooks:
# - id: isort
- repo: https://github.com/psf/black
rev: 21.8b0
rev: 22.3.0
hooks:
- id: black
# - repo: https://github.com/pre-commit/mirrors-mypy
# rev: 'v0.780'
# rev: 'v0.942'
# hooks:
# - id: mypy
# additional_dependencies: [types-pkg-resources, types-requests]
13 changes: 13 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
# Changelog

## Version 4.9.0

* Adding #109 Support for AVC and HEVC QSV encoding with rigaya's QSVEncC (thanks to msaintauret)
* Adding #196 Support for AVC and HEVC Apple Videotoolbox encoder (thanks to Kay Singh)
* Adding #323 ignore errors options options for queue (thanks to Don Gafford)
* Adding #331 NVEncC API v10 Quality presets: P1 to P7 (thanks to Wontell)
* Fixing #321 dhdr10_opt not added for x265 commands (thanks to GizmoDudex)
* Fixing #327 FastFlix Duplicates encoding task and encodes same movie to infinity (thanks to Wontell)
* Fixing #324 NVEncC wrong Interlace Value set by FastFlix (thanks to Wontell)
* Fixing #278 FastFlix occasionally getting stuck on a single video in a queue (thanks to kamild1996)
* Fixing #330 "Remove Metadata" only removes video metadata for Rigaya's hardware encoders (thanks to wynterca)
* Fixing level was not being passed to hardware encoders

## Version 4.8.1

* Fixing #315 HDR10 info not parsed from subsequent video tracks than the first, again (thanks to msaintauret)
Expand Down
12 changes: 11 additions & 1 deletion fastflix/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,20 @@ def init_encoders(app: FastFlixApp, **_):
from fastflix.encoders.svt_av1 import main as svt_av1_plugin
from fastflix.encoders.vp9 import main as vp9_plugin
from fastflix.encoders.webp import main as webp_plugin
from fastflix.encoders.qsvencc_hevc import main as qsvencc_plugin
from fastflix.encoders.qsvencc_avc import main as qsvencc_avc_plugin
from fastflix.encoders.nvencc_hevc import main as nvencc_plugin
from fastflix.encoders.nvencc_avc import main as nvencc_avc_plugin
from fastflix.encoders.vceencc_hevc import main as vceencc_hevc_plugin
from fastflix.encoders.vceencc_avc import main as vceencc_avc_plugin
from fastflix.encoders.hevc_videotoolbox import main as hevc_videotoolbox_plugin
from fastflix.encoders.h264_videotoolbox import main as h264_videotoolbox_plugin

encoders = [
hevc_plugin,
nvenc_plugin,
hevc_videotoolbox_plugin,
h264_videotoolbox_plugin,
av1_plugin,
rav1e_plugin,
svt_av1_plugin,
Expand All @@ -78,9 +84,13 @@ def init_encoders(app: FastFlixApp, **_):
copy_plugin,
]

if app.fastflix.config.qsvencc:
encoders.insert(1, qsvencc_plugin)
encoders.insert(8, qsvencc_avc_plugin)

if app.fastflix.config.nvencc:
encoders.insert(1, nvencc_plugin)
encoders.insert(7, nvencc_avc_plugin)
encoders.insert(8, nvencc_avc_plugin)

if app.fastflix.config.vceencc:
if reusables.win_based:
Expand Down
186 changes: 29 additions & 157 deletions fastflix/conversion_worker.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,14 @@
from fastflix.language import t
from fastflix.shared import file_date
from fastflix.models.video import Video
from fastflix.ff_queue import save_queue


logger = logging.getLogger("fastflix-core")

log_path = Path(user_data_dir("FastFlix", appauthor=False, roaming=True)) / "logs"
after_done_path = Path(user_data_dir("FastFlix", appauthor=False, roaming=True)) / "after_done_logs"

queue_path = Path(user_data_dir("FastFlix", appauthor=False, roaming=True)) / "queue.yaml"
queue_lock_file = Path(user_data_dir("FastFlix", appauthor=False, roaming=True)) / "queue.lock"


CONTINUOUS = 0x80000000
SYSTEM_REQUIRED = 0x00000001

Expand Down Expand Up @@ -53,91 +50,24 @@ def allow_sleep_mode():
logger.debug("System has been allowed to enter sleep mode again")


def get_next_video(queue_list, queue_lock) -> Optional[Video]:
with queue_lock:
logger.debug(f"Retrieving next video from {queue_list}")
for video in queue_list:
if (
not video.status.complete
and not video.status.success
and not video.status.cancelled
and not video.status.error
and not video.status.running
):
logger.debug(f"Next video is {video.uuid} - {video.status}")
return video.copy()


def set_status(
current_video: Video,
queue_list,
queue_lock,
complete=None,
success=None,
cancelled=None,
errored=None,
running=None,
next_command=False,
reset_commands=False,
):
if not current_video:
return

with queue_lock:
for i, video in enumerate(queue_list):
if video.uuid == current_video.uuid:
video_pos = i
break
else:
logger.error(f"Can't find video {current_video.uuid} in queue to update its status: {queue_list}")
return

video_copy = queue_list.pop(video_pos)

if complete is not None:
video_copy.status.complete = complete
if cancelled is not None:
video_copy.status.cancelled = cancelled
if errored is not None:
video_copy.status.error = errored
if success is not None:
video_copy.status.success = success
if running is not None:
video_copy.status.running = running

if complete or cancelled or errored or success:
video_copy.status.running = False

if next_command:
video_copy.status.current_command += 1
if reset_commands:
video_copy.status.current_command = 0

queue_list.insert(video_pos, video_copy)


@reusables.log_exception(log="fastflix-core")
def queue_worker(gui_proc, worker_queue, status_queue, log_queue, queue_list, queue_lock: Lock):
def queue_worker(gui_proc, worker_queue, status_queue, log_queue):
runner = BackgroundRunner(log_queue=log_queue)

# Command looks like (video_uuid, command_uuid, command, work_dir)
after_done_command = ""
gui_died = False
currently_encoding = False
paused = False
video: Optional[Video] = None
video_uuid = None
command_uuid = None
command = None
work_dir = None
log_name = ""

def start_command():
nonlocal currently_encoding
log_queue.put(
f"CLEAR_WINDOW:{video.uuid}:{video.video_settings.conversion_commands[video.status.current_command].uuid}"
)
log_queue.put(f"CLEAR_WINDOW:{video_uuid}:{command_uuid}")
reusables.remove_file_handlers(logger)
new_file_handler = reusables.get_file_handler(
log_path
/ sanitize_filename(
f"flix_conversion_{video.video_settings.video_title or video.video_settings.output_path.stem}_{file_date()}.log"
),
log_path / sanitize_filename(f"flix_conversion_{log_name}_{file_date()}.log"),
level=logging.DEBUG,
log_format="%(asctime)s - %(message)s",
encoding="utf-8",
Expand All @@ -146,67 +76,33 @@ def start_command():
prevent_sleep_mode()
currently_encoding = True
runner.start_exec(
video.video_settings.conversion_commands[video.status.current_command].command,
work_dir=str(video.work_path),
command,
work_dir=work_dir,
)
set_status(video, queue_list=queue_list, queue_lock=queue_lock, running=True)
status_queue.put(("queue",))

# status_queue.put(("running", commands_to_run[0][0], commands_to_run[0][1], runner.started_at.isoformat()))

while True:
if currently_encoding and not runner.is_alive():
reusables.remove_file_handlers(logger)
log_queue.put("STOP_TIMER")
allow_sleep_mode()
currently_encoding = False

if runner.error_detected:
logger.info(t("Error detected while converting"))

# Stop working!
currently_encoding = False
set_status(video, queue_list=queue_list, queue_lock=queue_lock, errored=True)
status_queue.put(("error",))
allow_sleep_mode()
status_queue.put(("error", video_uuid, command_uuid))
if gui_died:
return
continue

# Successfully encoded, do next one if it exists
# First check if the current video has more commands
video.status.current_command += 1
log_queue.put("STOP_TIMER")

if len(video.video_settings.conversion_commands) > video.status.current_command:
logger.debug("About to run next command for this video")
set_status(video, queue_list=queue_list, queue_lock=queue_lock, next_command=True)
status_queue.put(("queue",))
start_command()
continue
else:
logger.debug(f"{video.uuid} has been completed")
set_status(video, queue_list=queue_list, queue_lock=queue_lock, next_command=True, complete=True)
status_queue.put(("queue",))
video = None

if paused:
currently_encoding = False
allow_sleep_mode()
logger.debug(t("Queue has been paused"))
continue

if video := get_next_video(queue_list=queue_list, queue_lock=queue_lock):
start_command()
continue
else:
currently_encoding = False
allow_sleep_mode()
logger.info(t("all conversions complete"))
status_queue.put(("complete",))
if after_done_command:
logger.info(f"{t('Running after done command:')} {after_done_command}")
try:
runner.start_exec(after_done_command, str(after_done_path))
except Exception:
logger.exception("Error occurred while running after done command")
continue
status_queue.put(("complete", video_uuid, command_uuid))
if after_done_command:
logger.info(f"{t('Running after done command:')} {after_done_command}")
try:
runner.start_exec(after_done_command, str(after_done_path))
except Exception:
logger.exception("Error occurred while running after done command")
continue
if gui_died:
return

Expand All @@ -218,7 +114,6 @@ def start_command():
else:
logger.debug(t("Conversion worker shutting down"))
return

try:
request = worker_queue.get(block=True, timeout=0.05)
except Empty:
Expand All @@ -228,39 +123,19 @@ def start_command():
allow_sleep_mode()
return
else:
if request[0] == "add_items":

# Request looks like (queue command, log_dir, (commands))
log_path = Path(request[1])
if not currently_encoding and not paused:
video = get_next_video(queue_list=queue_list, queue_lock=queue_lock)
if video:
start_command()
if request[0] == "execute":
_, video_uuid, command_uuid, command, work_dir, log_name = request
start_command()

if request[0] == "cancel":
logger.debug(t("Cancel has been requested, killing encoding"))
runner.kill()
if video:
set_status(video, queue_list=queue_list, queue_lock=queue_lock, reset_commands=True, cancelled=True)
currently_encoding = False
allow_sleep_mode()
status_queue.put(("cancelled", video.uuid if video else ""))
status_queue.put(("cancelled", video_uuid, command_uuid))
log_queue.put("STOP_TIMER")
video = None

if request[0] == "pause queue":
logger.debug(t("Command worker received request to pause encoding after the current item completes"))
paused = True

if request[0] == "resume queue":
paused = False
logger.debug(t("Command worker received request to resume encoding"))
if not currently_encoding:
if not video:
video = get_next_video(queue_list=queue_list, queue_lock=queue_lock)
if video:
start_command()

if request[0] == "set after done":
after_done_command = request[1]
if after_done_command:
Expand All @@ -274,13 +149,10 @@ def start_command():
runner.pause()
except Exception:
logger.exception("Could not pause command")
else:
status_queue.put(("paused encode",))

if request[0] == "resume encode":
logger.debug(t("Command worker received request to resume paused encode"))
try:
runner.resume()
except Exception:
logger.exception("Could not resume command")
else:
status_queue.put(("resumed encode",))
Binary file added fastflix/data/encoders/icon_h264_toolbox.png
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/encoders/icon_hevc_toolbox.png
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/encoders/icon_qsvencc.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 2770ca8

Please sign in to comment.