Skip to content

Commit

Permalink
Misc updates
Browse files Browse the repository at this point in the history
  • Loading branch information
Kenshin9977 committed Oct 31, 2023
1 parent 7ca0728 commit e88cc87
Show file tree
Hide file tree
Showing 10 changed files with 190 additions and 77 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# video-dl script
# video-dl (a yt-dlp GUI)

The purpose of this script is to simplify the usage of [yt-dlp](https://github.com/yt-dlp/yt-dlp).
<p align="center">
Expand Down
45 changes: 27 additions & 18 deletions components_handlers/ffmpeg_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from utils.sys_utils import popen


def _post_process_dl(full_name: str, target_vcodec: str, videodl_app) -> None:
def post_process_dl(full_name: str, target_vcodec: str, videodl_app) -> None:
"""
Remux to ensure compatibility with NLEs or reencode to the target video
codec the downloaded file through ffmpeg.
Expand All @@ -24,21 +24,25 @@ def _post_process_dl(full_name: str, target_vcodec: str, videodl_app) -> None:
"""
file_infos = ffprobe(full_name, cmd=FF_PATH.get("ffprobe"))["streams"]
acodec, vcodec = "na", "na"
for i in range(0, min(2, len(file_infos))):
if file_infos[i]["codec_type"] == "audio":
acodec = file_infos[i]["codec_tag_string"]
elif file_infos[i]["codec_type"] == "video":
vcodec = file_infos[i]["codec_tag_string"]
for stream in file_infos:
if stream["codec_type"] == "audio":
acodec = stream["codec_name"]
elif stream["codec_type"] == "video":
vcodec = stream["codec_name"]
big_dimension = 1080 < min(stream["width"], stream["height"])

common_acodecs = ["aac", "mp3", "mp4a"]
# Audio codecs NLE friendly
acodec_nle_friendly = any(re.match(f"{c}", acodec) for c in common_acodecs)
vcodec_nle_friendly = bool(
re.match("avc1", vcodec) and target_vcodec == "x264"
)
vcodec_equivalence = {
"x264": "avc1", "x265": "hevc", "ProRes": "prores", "AV1": "av1"
}
vcodec_is_target = vcodec_equivalence[target_vcodec] == vcodec
_ffmpeg_video(
full_name,
acodec_nle_friendly,
vcodec_nle_friendly,
vcodec_is_target,
big_dimension,
target_vcodec,
videodl_app,
)
Expand Down Expand Up @@ -71,7 +75,8 @@ def ffprobe(filename: str, cmd: str = "ffprobe") -> dict:
def _ffmpeg_video(
path: str,
acodec_nle_friendly: bool,
vcodec_nle_friendly: bool,
vcodec_is_target: bool,
big_dimension: bool,
target_vcodec: str,
videodl_app,
) -> None:
Expand All @@ -83,8 +88,9 @@ def _ffmpeg_video(
path (str): Downloaded file's path
acodec_nle_friendly (bool): Whether or not the audio codec is nle
friendly
vcodec_nle_friendly (bool): Whether or not the audio codec is nle
friendly
vcodec_is_target (bool): Whether or not the video codec is the same
as the targeted one
smallest_dimension (int): Smallest video dimension
target_vcodec (str): The video codec to convert to (if necessary)
videodl_app (VideodlApp): GUIs object
Expand All @@ -94,9 +100,10 @@ def _ffmpeg_video(
"""
ffmpeg_acodec = "aac" if not acodec_nle_friendly else "copy"
new_ext = ".mov" if target_vcodec == "ProRes" else ".mp4"
ffmpeg_vcodec = (
"copy" if vcodec_nle_friendly else fastest_encoder(path, target_vcodec)
)
if vcodec_is_target:
ffmpeg_vcodec, quality_options = "copy", []
else:
ffmpeg_vcodec, quality_options = fastest_encoder(path, target_vcodec)
tmp_path = f"{os.path.splitext(path)[0]}.tmp{new_ext}"
ffmpeg_command = [
FF_PATH.get("ffmpeg"),
Expand All @@ -108,12 +115,14 @@ def _ffmpeg_video(
"-c:v",
ffmpeg_vcodec,
]
if target_vcodec == "ProRes":
if big_dimension:
ffmpeg_command.extend(quality_options)
elif target_vcodec == "ProRes":
ffmpeg_command.extend(["-profile:v", "0", "-qscale:v", "4"])
ffmpeg_command.extend(["-y", tmp_path])
action = (
get_text(GuiField.ff_remux)
if acodec_nle_friendly and vcodec_nle_friendly
if acodec_nle_friendly and vcodec_is_target
else get_text(GuiField.ff_reencode)
)
_progress_ffmpeg(ffmpeg_command, action, path, videodl_app)
Expand Down
118 changes: 76 additions & 42 deletions components_handlers/hwaccel_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,44 +3,78 @@

from sys_vars import FF_PATH
from utils.sys_utils import check_cmd_output
from videodl_exceptions import FFmpegNoValidEncoderFound
from videodl_exceptions import FFmpegNoValidEncoderFound, FileAlreadyInUse

ENCODERS = {
"x264": {
"QuickSync": "h264_qsv",
"NVENC": "h264_nvenc",
"AMF": "h264_amf",
"Apple": "h264_videotoolbox",
"Raspberry": "h264_v4l2m2m",
"CPU": "libx264",
"QuickSync": (
"h264_qsv", ["-global_quality", "20", "-look_ahead", "1"]
),
"NVENC": (
"h264_nvenc", [
"-preset:v",
"p7",
"-tune:v",
"hq",
"-rc:v",
"vbr",
"-cq:v 19",
"-b:v 0",
",-profile:v",
"high"
]
),
"AMF": ("h264_amf", ["-quality", "quality"]),
"Apple": ("h264_videotoolbox", ["-q:v", "35"]),
# doc lacking, might need to change the number
# https://trac.ffmpeg.org/wiki/HWAccelIntro#VideoToolbox
"Raspberry": ("h264_v4l2m2m", []),
"CPU": ("libx264", ["-crf", "20"]),
},
"x265": {
"QuickSync": "hevc_qsv",
"NVENC": "hevc_nvenc",
"AMF": "hevc_amf",
"Apple": "hevc_videotoolbox",
"Raspberry": "hevc_v4l2m2m",
"CPU": "libx265",
"QuickSync": (
"hevc_qsv", ["-global_quality", "20", "-look_ahead", "1"]
),
"NVENC": (
"hevc_nvenc", [
"-preset:v",
"p7",
"-tune:v",
"hq",
"-rc:v",
"vbr",
"-cq:v 19",
"-b:v 0",
",-profile:v",
"high"
]
),
"AMF": ("hevc_amf", ["-quality", "quality"]),
"Apple": ("hevc_videotoolbox", ["-q:v", "35"]),
"Raspberry": ("hevc_v4l2m2m", []),
"CPU": ("libx265", ["-crf", "20"]),
},
"ProRes": {
"QuickSync": None,
"NVENC": None,
"AMF": None,
"Apple": "prores_videotoolbox",
"Raspberry": None,
"CPU": "prores_ks",
"QuickSync": (None, []),
"NVENC": (None, []),
"AMF": (None, []),
"Apple": (
"prores_videotoolbox", ["-profile:v", "0", "-qscale:v", "4"]
),
"Raspberry": (None, []),
"CPU": ("prores_ks", ["-profile:v", "0", "-qscale:v", "4"]),
},
"AV1": {
"QuickSync": None,
"NVENC": None,
"AMF": None,
"Apple": None,
"CPU": "libsvtav1",
"QuickSync": ("av1_qsv", []),
"NVENC": ("av1_nvenc", []),
"AMF": (None, []),
"Apple": (None, []),
"CPU": ("libsvtav1", ["-crf", "23"]),
},
}


def fastest_encoder(path: str, target_vcodec: str) -> str:
def fastest_encoder(path: str, target_vcodec: str) -> tuple[str, str]:
"""
Determine the fastest encoder by trying to encode 1 frame with each
possible encoder for the targeted video codec. As soon as one works it is
Expand All @@ -63,30 +97,30 @@ def fastest_encoder(path: str, target_vcodec: str) -> str:
new_ext = ".mp4" if target_vcodec != "ProRes" else ".mov"
output_path = f"{file_name_ext[0]}.tmp{new_ext}"
vcodecs = ENCODERS[target_vcodec].values()
for vcodec in vcodecs:
ffmpeg_output_args = [output_path, "-y", "-loglevel", "error"]
for vcodec, ffmpeg_quality_options in vcodecs:
if not vcodec:
continue
try:
ffmpeg_basic_args = [
FF_PATH.get("ffmpeg"),
"-hide_banner",
"-i",
path,
"-frames:v",
"1",
"-c:v",
vcodec
]
check_cmd_output(
[
FF_PATH.get("ffmpeg"),
"-hide_banner",
"-i",
path,
"-frames:v",
"1",
"-c:v",
vcodec,
output_path,
"-y",
"-loglevel",
"error",
]
ffmpeg_basic_args + ffmpeg_quality_options + ffmpeg_output_args
)
except subprocess.CalledProcessError:
continue
finally:
try:
if os.path.isfile(output_path):
os.remove(path=output_path)
return vcodec
except PermissionError:
raise FileAlreadyInUse
return vcodec, ffmpeg_quality_options
raise FFmpegNoValidEncoderFound
4 changes: 2 additions & 2 deletions components_handlers/ytdlp_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

from yt_dlp import YoutubeDL

from components_handlers.ffmpeg_handler import _post_process_dl
from components_handlers.ffmpeg_handler import post_process_dl
from videodl_exceptions import PlaylistNotFound

logger = logging.getLogger()
Expand Down Expand Up @@ -48,4 +48,4 @@ def post_download(
ext = infos_ydl["ext"]
media_filename_formated = ydl.prepare_filename(infos_ydl)
full_path = f"{os.path.splitext(media_filename_formated)[0]}.{ext}"
_post_process_dl(full_path, target_vcodec, videodl_app)
post_process_dl(full_path, target_vcodec, videodl_app)
20 changes: 17 additions & 3 deletions gui_flet.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
from lang import set_current_language
from sys_vars import FF_PATH
from utils.sys_utils import APP_VERSION, get_default_download_path
from videodl_exceptions import DownloadCancelled, PlaylistNotFound
from videodl_exceptions import (DownloadCancelled, FFmpegNoValidEncoderFound,
FileAlreadyInUse, PlaylistNotFound)

logger = logging.getLogger()

Expand Down Expand Up @@ -407,7 +408,10 @@ def _update_bar(
if self.cancel_button.disabled:
raise DownloadCancelled
try:
speed = Quantity(d.get("speed"), "B/s").render(prec=2)
if bytes_fieldname == "downloaded_bytes":
speed = Quantity(d.get("speed"), "B/s").render(prec=2)
else:
speed = d.get("speed")
except InvalidNumber:
speed = "-"
try:
Expand Down Expand Up @@ -690,7 +694,7 @@ def _download_clicked(self, event):
except FileExistsError:
self.download_status_text.value = gt(GF.dl_finish)
self.download_status_text.visible = True
self.download_status_text.color = "red"
self.download_status_text.color = "green"
except utils.UnsupportedError:
self.download_status_text.value = gt(GF.unsupported_url)
self.download_status_text.visible = True
Expand All @@ -700,6 +704,16 @@ def _download_clicked(self, event):
self.download_status_text.value = gt(GF.unsupported_url)
self.download_status_text.visible = True
self.download_status_text.color = "red"
except FFmpegNoValidEncoderFound:
logging.error(traceback.format_exc())
self.download_status_text.value = gt(GF.no_encoder)
self.download_status_text.visible = True
self.download_status_text.color = "red"
except FileAlreadyInUse:
logging.error(traceback.format_exc())
self.download_status_text.value = gt(GF.file_in_use)
self.download_status_text.visible = True
self.download_status_text.color = "red"
except Exception as e:
logging.error(traceback.format_exc())
self.download_status_text.value = (
Expand Down
2 changes: 1 addition & 1 deletion gui_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,5 @@
"240p",
"144p",
]
VCODECS = ["x264", "x265", "ProRes"]
VCODECS = ["x264", "x265", "ProRes", "AV1"]
ACODECS = ["AAC", "ALAC", "BEST", "FLAC", "OPUS", "MP3", "VORBIS", "WAV"]
14 changes: 13 additions & 1 deletion lang.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ class GuiField(enum.Enum):
language = enum.auto()
indices_selected = enum.auto()
cookies_none = enum.auto()
unsupported_url = enum.auto()

# Main window properties
width = enum.auto()
Expand All @@ -46,6 +45,9 @@ class GuiField(enum.Enum):
invalid_output_path = enum.auto()
theme = enum.auto()
playlist_not_found = enum.auto()
unsupported_url = enum.auto()
file_in_use = enum.auto()
no_encoder = enum.auto()

# Progress window
download = enum.auto()
Expand Down Expand Up @@ -287,6 +289,16 @@ def get_text(field: GuiField) -> str:
Language.french: "URL non gérée",
Language.german: "Nicht unterstützte URL",
},
GuiField.file_in_use: {
Language.english: "File already in use",
Language.french: "Fichier en cours d'utilisation",
Language.german: "Datei wird bereits verwendet",
},
GuiField.no_encoder: {
Language.english: "No capable encoder found",
Language.french: "Aucun encodeur apte trouvé",
Language.german: "Kein fähiger Encoder gefunden",
},
GuiField.width: {
Language.english: 420,
Language.french: 440,
Expand Down
Loading

0 comments on commit e88cc87

Please sign in to comment.