✨ FLAC can be extracted from MP4 containers (see options). Downgrade on HI_RES download is possible (see options). Fixes #93 #86

This commit is contained in:
Robert Honz
2024-04-21 19:18:05 +02:00
parent 78670a38ba
commit b07c3374b2
7 changed files with 226 additions and 114 deletions
+2
View File
@@ -160,6 +160,8 @@ class DialogPreferences(QtWidgets.QDialog):
"video_download",
"download_delay",
"video_convert_mp4",
"extract_flac",
"downgrade_on_hi_res",
]
def gui_populate(self):
+71 -17
View File
@@ -12,7 +12,7 @@ import requests
from requests.exceptions import HTTPError
from rich.progress import Progress, TaskID
from tidalapi import Album, Mix, Playlist, Session, Track, UserPlaylist, Video
from tidalapi.media import AudioExtensions, Quality, StreamManifest, VideoExtensions
from tidalapi.media import AudioExtensions, Codec, Quality, StreamManifest, VideoExtensions, VideoQuality
from tidal_dl_ng.config import Settings, Tidal
from tidal_dl_ng.constants import EXTENSION_LYRICS, REQUESTS_TIMEOUT_SEC, MediaType, SkipExisting
@@ -70,11 +70,15 @@ class Download:
self.progress = progress
self.path_base = path_base
if not self.settings.data.path_binary_ffmpeg and self.settings.data.video_convert_mp4:
if not self.settings.data.path_binary_ffmpeg and (
self.settings.data.video_convert_mp4 or self.settings.data.extract_flac
):
self.settings.data.video_convert_mp4 = False
self.settings.data.extract_flac = False
self.fn_logger.error(
"FFmpeg in path is not set. Videos can be downloaded but will not be processed. "
"Make sure FFmpeg is installed and the path to the binary is configured ('path_binary_ffmpeg')."
"FFmpeg is not set. Videos can be downloaded but will not be processed. FLAC cannot be extracted from MP4 containers. "
"Make sure FFmpeg is installed and the path to the binary is configured (`path_binary_ffmpeg`)."
)
def _download(
@@ -174,7 +178,8 @@ class Download:
media_type: MediaType = None,
video_download: bool = True,
download_delay: bool = False,
quality: Quality | None = None,
quality_audio: Quality | None = None,
quality_video: VideoQuality | None = None,
) -> (bool, str):
try:
if media_id and media_type:
@@ -203,17 +208,34 @@ class Download:
return False, ""
# If a quality is explicitly set, change it.
if quality:
quality_old: Quality = self.quality_adjust(quality)
# Get extension.
file_extension: str
if isinstance(media, Track):
# If hi_res is forbidden in settings and highest track quality is hi_res, downgrade to high_lossless.
if self.settings.data.downgrade_on_hires and media.get_stream().audio_quality == Quality.hi_res:
quality_audio = Quality.high_lossless
# If a quality is explicitly set, change it.
if quality_audio:
quality_audio_old: Quality = self.adjust_quality_audio(quality_audio)
file_extension = media.get_stream().get_stream_manifest().file_extension
if self.settings.data.extract_flac:
do_flac_extract = False
if (
media.get_stream().get_stream_manifest().codecs.upper() == Codec.FLAC
and file_extension != AudioExtensions.FLAC
):
file_extension = AudioExtensions.FLAC
do_flac_extract = True
elif isinstance(media, Video):
file_extension = VideoExtensions.TS
if quality_video:
quality_video_old = self.adjust_quality_video(quality_video)
file_extension = AudioExtensions.MP4 if self.settings.data.video_convert_mp4 else VideoExtensions.TS
# Create file name and path
file_name_relative = format_path_media(file_template, media)
@@ -235,14 +257,18 @@ class Download:
if not file_exists:
# Create a temp directory and file.
with tempfile.TemporaryDirectory(ignore_cleanup_errors=True) as tmp_path_dir:
tmp_path_file = os.path.join(tmp_path_dir, str(uuid4()) + file_extension)
tmp_path_file = os.path.join(tmp_path_dir, str(uuid4()))
# Download media.
tmp_path_file = self._download(media=media, path_file=tmp_path_file)
# Convert video from TS to MP4
if isinstance(media, Video) and self.settings.data.video_convert_mp4:
# Convert `*.ts` file to `*.mp4` using ffmpeg
tmp_path_file = self._video_convert(tmp_path_file)
path_file = os.path.splitext(path_file)[0] + ".mp4"
# Extract FLAC from MP4 container using ffmpeg
if self.settings.data.extract_flac and do_flac_extract:
tmp_path_file = self._extract_flac(tmp_path_file, media.isrc)
# Move final file to the configured destination directory.
os.makedirs(os.path.dirname(path_file), exist_ok=True)
@@ -256,9 +282,13 @@ class Download:
status_download: bool = not file_exists
if quality:
if quality_audio:
# Set quality back to the global user value
self.quality_adjust(quality_old)
self.adjust_quality_audio(quality_audio_old)
if quality_video:
# Set quality back to the global user value
self.adjust_quality_video(quality_video_old)
# Whether a file was downloaded or skipped and the download delay is enabled, wait until the next download.
# Only use this, if you have a list of several Track items.
@@ -270,7 +300,7 @@ class Download:
return status_download, path_file
def quality_adjust(self, quality) -> Quality:
def adjust_quality_audio(self, quality) -> Quality:
# Save original quality settings
quality_old: Quality = self.session.audio_quality
self.session.audio_quality = quality
@@ -284,6 +314,13 @@ class Download:
return quality_old
def adjust_quality_video(self, quality) -> VideoQuality:
quality_old: VideoQuality = self.session.video_quality
self.session.video_quality = quality
return quality_old
def _move_lyrics(self, file_media_dst: str, file_media_src: str):
# Build tmp lyrics filename
tmp_lyrics_file_path: str = file_media_src + EXTENSION_LYRICS
@@ -405,7 +442,7 @@ class Download:
for media in items:
# Download the item.
status_download, result_path_file = self.item(
media=media, file_template=file_name_relative, quality=quality
media=media, file_template=file_name_relative, quality_audio=quality
)
# Advance progress bar.
@@ -422,11 +459,28 @@ class Download:
time.sleep(time_sleep)
def _video_convert(self, path_file: str) -> str:
path_file_out = os.path.splitext(path_file)[0] + AudioExtensions.MP4
path_file_out = path_file + AudioExtensions.MP4
result, _ = ffmpeg.input(path_file).output(path_file_out, map=0, c="copy").run()
return path_file_out
def _extract_flac(self, path_file: str, isrc: str) -> str:
path_file_out = path_file + AudioExtensions.FLAC
result, _ = (
ffmpeg.input(path_file)
.output(
path_file_out,
map=0,
movflags="use_metadata_tags",
acodec="copy",
map_metadata="0:g",
metadata=f"isrc={isrc}",
)
.run()
)
return path_file_out
def _extract_video_stream(self, m3u8_variant: m3u8.M3U8, quality: int) -> (m3u8.M3U8 | bool, str):
m3u8_playlist: m3u8.M3U8 | bool = False
resolution_best: int = 0
+1 -1
View File
@@ -864,7 +864,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
if isinstance(media, Track | Video):
result_dl, path_file = dl.item(
media=media, file_template=file_template, download_delay=delay_track, quality=quality
media=media, file_template=file_template, download_delay=delay_track, quality_audio=quality
)
elif isinstance(media, Album | Playlist | Mix):
dl.items(
+1 -1
View File
@@ -141,7 +141,7 @@ class Metadata:
self.m.tags["\xa9day"] = self.date
self.m.tags["\xa9wrt"] = ", ".join(self.composer) if self.composer else ""
self.m.tags["\xa9lyr"] = self.lyrics
self.m.tags["ISRC"] = self.isrc
self.m.tags["isrc"] = self.isrc
@staticmethod
def cover_data(url: str = None, path_file: str = None) -> str | bytes:
+6
View File
@@ -35,6 +35,8 @@ class Settings:
video_convert_mp4: bool = True
path_binary_ffmpeg: str = ""
metadata_cover_dimension: CoverDimensions = CoverDimensions.Px320
extract_flac: bool = True
downgrade_on_hi_res: bool = False
@dataclass_json
@@ -74,6 +76,10 @@ class HelpSettings:
metadata_cover_dimension: str = (
"The dimensions of the cover image embedded into the track. Possible values: 320x320, 640x640x 1280x1280."
)
extract_flac: str = "Extract FLAC audio tracks from MP4 containers and save them as `*.flac` (uses FFmpeg)."
downgrade_on_hi_res: bool = (
"Never download HI_RES (MQA) quality. Downgrade to the next best available quality instead."
)
@dataclass_json
+58 -37
View File
@@ -1,7 +1,7 @@
################################################################################
## Form generated from reading UI file 'dialog_settings.ui'
##
## Created by: Qt User Interface Compiler version 6.6.1
## Created by: Qt User Interface Compiler version 6.7.0
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
@@ -27,7 +27,7 @@ class Ui_DialogSettings:
if not DialogSettings.objectName():
DialogSettings.setObjectName("DialogSettings")
DialogSettings.resize(640, 682)
sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
sizePolicy = QSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
sizePolicy.setHorizontalStretch(100)
sizePolicy.setVerticalStretch(100)
sizePolicy.setHeightForWidth(DialogSettings.sizePolicy().hasHeightForWidth())
@@ -41,7 +41,7 @@ class Ui_DialogSettings:
self.lv_main.setContentsMargins(12, 12, 12, 12)
self.gb_flags = QGroupBox(DialogSettings)
self.gb_flags.setObjectName("gb_flags")
sizePolicy1 = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
sizePolicy1 = QSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
sizePolicy1.setHorizontalStretch(100)
sizePolicy1.setVerticalStretch(100)
sizePolicy1.setHeightForWidth(self.gb_flags.sizePolicy().hasHeightForWidth())
@@ -52,56 +52,56 @@ class Ui_DialogSettings:
self.lv_flags.setObjectName("lv_flags")
self.lh_flags_1 = QHBoxLayout()
self.lh_flags_1.setObjectName("lh_flags_1")
self.lv_flags_video_download = QVBoxLayout()
self.lv_flags_video_download.setObjectName("lv_flags_video_download")
self.lv_flag_video_download = QVBoxLayout()
self.lv_flag_video_download.setObjectName("lv_flag_video_download")
self.cb_video_download = QCheckBox(self.gb_flags)
self.cb_video_download.setObjectName("cb_video_download")
sizePolicy1.setHeightForWidth(self.cb_video_download.sizePolicy().hasHeightForWidth())
self.cb_video_download.setSizePolicy(sizePolicy1)
self.lv_flags_video_download.addWidget(self.cb_video_download)
self.lv_flag_video_download.addWidget(self.cb_video_download)
self.lh_flags_1.addLayout(self.lv_flags_video_download)
self.lh_flags_1.addLayout(self.lv_flag_video_download)
self.lv_flags_video_convert = QVBoxLayout()
self.lv_flags_video_convert.setObjectName("lv_flags_video_convert")
self.lv_flag_video_convert = QVBoxLayout()
self.lv_flag_video_convert.setObjectName("lv_flag_video_convert")
self.cb_video_convert_mp4 = QCheckBox(self.gb_flags)
self.cb_video_convert_mp4.setObjectName("cb_video_convert_mp4")
sizePolicy1.setHeightForWidth(self.cb_video_convert_mp4.sizePolicy().hasHeightForWidth())
self.cb_video_convert_mp4.setSizePolicy(sizePolicy1)
self.lv_flags_video_convert.addWidget(self.cb_video_convert_mp4)
self.lv_flag_video_convert.addWidget(self.cb_video_convert_mp4)
self.lh_flags_1.addLayout(self.lv_flags_video_convert)
self.lh_flags_1.addLayout(self.lv_flag_video_convert)
self.lv_flags.addLayout(self.lh_flags_1)
self.lh_flags_2 = QHBoxLayout()
self.lh_flags_2.setObjectName("lh_flags_2")
self.lh_flags_lyrics_embed = QHBoxLayout()
self.lh_flags_lyrics_embed.setObjectName("lh_flags_lyrics_embed")
self.lh_flag_lyrics_embed = QHBoxLayout()
self.lh_flag_lyrics_embed.setObjectName("lh_flag_lyrics_embed")
self.cb_lyrics_embed = QCheckBox(self.gb_flags)
self.cb_lyrics_embed.setObjectName("cb_lyrics_embed")
sizePolicy2 = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
sizePolicy2 = QSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
sizePolicy2.setHorizontalStretch(0)
sizePolicy2.setVerticalStretch(0)
sizePolicy2.setHeightForWidth(self.cb_lyrics_embed.sizePolicy().hasHeightForWidth())
self.cb_lyrics_embed.setSizePolicy(sizePolicy2)
self.lh_flags_lyrics_embed.addWidget(self.cb_lyrics_embed)
self.lh_flag_lyrics_embed.addWidget(self.cb_lyrics_embed)
self.lh_flags_2.addLayout(self.lh_flags_lyrics_embed)
self.lh_flags_2.addLayout(self.lh_flag_lyrics_embed)
self.lh_flags_lyrics_file = QHBoxLayout()
self.lh_flags_lyrics_file.setObjectName("lh_flags_lyrics_file")
self.lh_flag_lyrics_file = QHBoxLayout()
self.lh_flag_lyrics_file.setObjectName("lh_flag_lyrics_file")
self.cb_lyrics_file = QCheckBox(self.gb_flags)
self.cb_lyrics_file.setObjectName("cb_lyrics_file")
sizePolicy1.setHeightForWidth(self.cb_lyrics_file.sizePolicy().hasHeightForWidth())
self.cb_lyrics_file.setSizePolicy(sizePolicy1)
self.lh_flags_lyrics_file.addWidget(self.cb_lyrics_file)
self.lh_flag_lyrics_file.addWidget(self.cb_lyrics_file)
self.lh_flags_2.addLayout(self.lh_flags_lyrics_file)
self.lh_flags_2.addLayout(self.lh_flag_lyrics_file)
self.lv_flags.addLayout(self.lh_flags_2)
@@ -118,18 +118,37 @@ class Ui_DialogSettings:
self.lh_flag_3.addLayout(self.lv_flag_download_delay)
self.verticalLayout_4 = QVBoxLayout()
self.verticalLayout_4.setObjectName("verticalLayout_4")
self.lv_flag_extract_flac = QVBoxLayout()
self.lv_flag_extract_flac.setObjectName("lv_flag_extract_flac")
self.cb_extract_flac = QCheckBox(self.gb_flags)
self.cb_extract_flac.setObjectName("cb_extract_flac")
sizePolicy2.setHeightForWidth(self.cb_extract_flac.sizePolicy().hasHeightForWidth())
self.cb_extract_flac.setSizePolicy(sizePolicy2)
self.lh_flag_3.addLayout(self.verticalLayout_4)
self.lv_flag_extract_flac.addWidget(self.cb_extract_flac)
self.lh_flag_3.addLayout(self.lv_flag_extract_flac)
self.lv_flags.addLayout(self.lh_flag_3)
self.horizontalLayout = QHBoxLayout()
self.horizontalLayout.setObjectName("horizontalLayout")
self.lv_flag_downgrade_on_hi_res = QVBoxLayout()
self.lv_flag_downgrade_on_hi_res.setObjectName("lv_flag_downgrade_on_hi_res")
self.cb_downgrade_on_hi_res = QCheckBox(self.gb_flags)
self.cb_downgrade_on_hi_res.setObjectName("cb_downgrade_on_hi_res")
self.lv_flag_downgrade_on_hi_res.addWidget(self.cb_downgrade_on_hi_res)
self.horizontalLayout.addLayout(self.lv_flag_downgrade_on_hi_res)
self.lv_flags.addLayout(self.horizontalLayout)
self.lv_main.addWidget(self.gb_flags)
self.gb_choices = QGroupBox(DialogSettings)
self.gb_choices.setObjectName("gb_choices")
sizePolicy3 = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed)
sizePolicy3 = QSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Fixed)
sizePolicy3.setHorizontalStretch(0)
sizePolicy3.setVerticalStretch(0)
sizePolicy3.setHeightForWidth(self.gb_choices.sizePolicy().hasHeightForWidth())
@@ -140,7 +159,7 @@ class Ui_DialogSettings:
self.lh_choices_skip_existing.setObjectName("lh_choices_skip_existing")
self.l_icon_skip_existing = QLabel(self.gb_choices)
self.l_icon_skip_existing.setObjectName("l_icon_skip_existing")
sizePolicy4 = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
sizePolicy4 = QSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed)
sizePolicy4.setHorizontalStretch(0)
sizePolicy4.setVerticalStretch(0)
sizePolicy4.setHeightForWidth(self.l_icon_skip_existing.sizePolicy().hasHeightForWidth())
@@ -233,7 +252,7 @@ class Ui_DialogSettings:
self.c_metadata_cover_dimension = QComboBox(self.gb_choices)
self.c_metadata_cover_dimension.setObjectName("c_metadata_cover_dimension")
sizePolicy5 = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed)
sizePolicy5 = QSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Fixed)
sizePolicy5.setHorizontalStretch(10)
sizePolicy5.setVerticalStretch(0)
sizePolicy5.setHeightForWidth(self.c_metadata_cover_dimension.sizePolicy().hasHeightForWidth())
@@ -311,7 +330,7 @@ class Ui_DialogSettings:
self.lh_path_fmt_album.setObjectName("lh_path_fmt_album")
self.l_icon_format_album = QLabel(self.gb_path)
self.l_icon_format_album.setObjectName("l_icon_format_album")
sizePolicy6 = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred)
sizePolicy6 = QSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Preferred)
sizePolicy6.setHorizontalStretch(0)
sizePolicy6.setVerticalStretch(0)
sizePolicy6.setHeightForWidth(self.l_icon_format_album.sizePolicy().hasHeightForWidth())
@@ -386,22 +405,22 @@ class Ui_DialogSettings:
self.verticalLayout = QVBoxLayout()
self.verticalLayout.setObjectName("verticalLayout")
self.horizontalLayout_3 = QHBoxLayout()
self.horizontalLayout_3.setObjectName("horizontalLayout_3")
self.horizontalLayout_10 = QHBoxLayout()
self.horizontalLayout_10.setObjectName("horizontalLayout_10")
self.le_download_base_path = QLineEdit(self.gb_path)
self.le_download_base_path.setObjectName("le_download_base_path")
sizePolicy2.setHeightForWidth(self.le_download_base_path.sizePolicy().hasHeightForWidth())
self.le_download_base_path.setSizePolicy(sizePolicy2)
self.le_download_base_path.setDragEnabled(True)
self.horizontalLayout_3.addWidget(self.le_download_base_path)
self.horizontalLayout_10.addWidget(self.le_download_base_path)
self.pb_download_base_path = QPushButton(self.gb_path)
self.pb_download_base_path.setObjectName("pb_download_base_path")
self.horizontalLayout_3.addWidget(self.pb_download_base_path)
self.horizontalLayout_10.addWidget(self.pb_download_base_path)
self.verticalLayout.addLayout(self.horizontalLayout_3)
self.verticalLayout.addLayout(self.horizontalLayout_10)
self.horizontalLayout_7 = QHBoxLayout()
self.horizontalLayout_7.setObjectName("horizontalLayout_7")
@@ -450,22 +469,22 @@ class Ui_DialogSettings:
self.verticalLayout.addLayout(self.horizontalLayout_8)
self.horizontalLayout_31 = QHBoxLayout()
self.horizontalLayout_31.setObjectName("horizontalLayout_31")
self.horizontalLayout_3 = QHBoxLayout()
self.horizontalLayout_3.setObjectName("horizontalLayout_3")
self.le_path_binary_ffmpeg = QLineEdit(self.gb_path)
self.le_path_binary_ffmpeg.setObjectName("le_path_binary_ffmpeg")
sizePolicy2.setHeightForWidth(self.le_path_binary_ffmpeg.sizePolicy().hasHeightForWidth())
self.le_path_binary_ffmpeg.setSizePolicy(sizePolicy2)
self.le_path_binary_ffmpeg.setDragEnabled(True)
self.horizontalLayout_31.addWidget(self.le_path_binary_ffmpeg)
self.horizontalLayout_3.addWidget(self.le_path_binary_ffmpeg)
self.pb_path_binary_ffmpeg = QPushButton(self.gb_path)
self.pb_path_binary_ffmpeg.setObjectName("pb_path_binary_ffmpeg")
self.horizontalLayout_31.addWidget(self.pb_path_binary_ffmpeg)
self.horizontalLayout_3.addWidget(self.pb_path_binary_ffmpeg)
self.verticalLayout.addLayout(self.horizontalLayout_31)
self.verticalLayout.addLayout(self.horizontalLayout_3)
self.horizontalLayout_2.addLayout(self.verticalLayout)
@@ -501,6 +520,8 @@ class Ui_DialogSettings:
self.cb_lyrics_embed.setText(QCoreApplication.translate("DialogSettings", "CheckBox", None))
self.cb_lyrics_file.setText(QCoreApplication.translate("DialogSettings", "CheckBox", None))
self.cb_download_delay.setText(QCoreApplication.translate("DialogSettings", "CheckBox", None))
self.cb_extract_flac.setText(QCoreApplication.translate("DialogSettings", "CheckBox", None))
self.cb_downgrade_on_hi_res.setText(QCoreApplication.translate("DialogSettings", "CheckBox", None))
self.gb_choices.setTitle(QCoreApplication.translate("DialogSettings", "Choices", None))
self.l_icon_skip_existing.setText(QCoreApplication.translate("DialogSettings", "TextLabel", None))
self.l_skip_existing.setText(QCoreApplication.translate("DialogSettings", "TextLabel", None))
+87 -58
View File
@@ -70,7 +70,7 @@
<item>
<layout class="QHBoxLayout" name="lh_flags_1">
<item>
<layout class="QVBoxLayout" name="lv_flags_video_download">
<layout class="QVBoxLayout" name="lv_flag_video_download">
<item>
<widget class="QCheckBox" name="cb_video_download">
<property name="sizePolicy">
@@ -87,7 +87,7 @@
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="lv_flags_video_convert">
<layout class="QVBoxLayout" name="lv_flag_video_convert">
<item>
<widget class="QCheckBox" name="cb_video_convert_mp4">
<property name="sizePolicy">
@@ -108,7 +108,7 @@
<item>
<layout class="QHBoxLayout" name="lh_flags_2">
<item>
<layout class="QHBoxLayout" name="lh_flags_lyrics_embed">
<layout class="QHBoxLayout" name="lh_flag_lyrics_embed">
<item>
<widget class="QCheckBox" name="cb_lyrics_embed">
<property name="sizePolicy">
@@ -128,7 +128,7 @@
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="lh_flags_lyrics_file">
<layout class="QHBoxLayout" name="lh_flag_lyrics_file">
<item>
<widget class="QCheckBox" name="cb_lyrics_file">
<property name="sizePolicy">
@@ -166,7 +166,36 @@
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_4"/>
<layout class="QVBoxLayout" name="lv_flag_extract_flac">
<item>
<widget class="QCheckBox" name="cb_extract_flac">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>CheckBox</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<layout class="QVBoxLayout" name="lv_flag_downgrade_on_hi_res">
<item>
<widget class="QCheckBox" name="cb_downgrade_on_hi_res">
<property name="text">
<string>CheckBox</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
@@ -519,41 +548,41 @@
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="lh_path_binary_ffmpeg">
<item>
<widget class="QLabel" name="l_icon_path_binary_ffmpeg">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="l_path_binary_ffmpeg">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
</layout>
</item>
<layout class="QHBoxLayout" name="lh_path_binary_ffmpeg">
<item>
<widget class="QLabel" name="l_icon_path_binary_ffmpeg">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="l_path_binary_ffmpeg">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<layout class="QHBoxLayout" name="horizontalLayout_10">
<item>
<widget class="QLineEdit" name="le_download_base_path">
<property name="sizePolicy">
@@ -619,29 +648,29 @@
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLineEdit" name="le_path_binary_ffmpeg">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="dragEnabled">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pb_path_binary_ffmpeg">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLineEdit" name="le_path_binary_ffmpeg">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="dragEnabled">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pb_path_binary_ffmpeg">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>