Merge pull request #229 from exislow/196-feature-do-not-add-the-2-5-second-pause-until-the-next-file-if-the-current-file-is-skipped

196 feature do not add the 2 5 second pause until the next file if the current file is skipped
This commit is contained in:
exislow
2024-11-13 12:36:35 +01:00
committed by GitHub
9 changed files with 190 additions and 185 deletions
-7
View File
@@ -27,13 +27,6 @@ class MediaType(StrEnum):
ARTIST: str = "artist" ARTIST: str = "artist"
class SkipExisting(StrEnum):
Disabled: str = "False"
Filename: str = "exact"
ExtensionIgnore: str = "extension_ignore"
Append: str = "append"
class CoverDimensions(StrEnum): class CoverDimensions(StrEnum):
Px80: str = "80" Px80: str = "80"
Px160: str = "160" Px160: str = "160"
+2 -2
View File
@@ -9,7 +9,7 @@ from tidalapi import Quality as QualityAudio
from tidal_dl_ng import __version__ from tidal_dl_ng import __version__
from tidal_dl_ng.config import Settings from tidal_dl_ng.config import Settings
from tidal_dl_ng.constants import CoverDimensions, QualityVideo, SkipExisting from tidal_dl_ng.constants import CoverDimensions, QualityVideo
from tidal_dl_ng.model.cfg import HelpSettings from tidal_dl_ng.model.cfg import HelpSettings
from tidal_dl_ng.model.cfg import Settings as ModelSettings from tidal_dl_ng.model.cfg import Settings as ModelSettings
from tidal_dl_ng.model.meta import ReleaseLatest from tidal_dl_ng.model.meta import ReleaseLatest
@@ -157,7 +157,6 @@ class DialogPreferences(QtWidgets.QDialog):
def _init_comboboxes(self): def _init_comboboxes(self):
self.parameters_combo = [ self.parameters_combo = [
("skip_existing", SkipExisting),
("quality_audio", QualityAudio), ("quality_audio", QualityAudio),
("quality_video", QualityVideo), ("quality_video", QualityVideo),
("metadata_cover_dimension", CoverDimensions), ("metadata_cover_dimension", CoverDimensions),
@@ -173,6 +172,7 @@ class DialogPreferences(QtWidgets.QDialog):
"extract_flac", "extract_flac",
"metadata_cover_embed", "metadata_cover_embed",
"cover_album_file", "cover_album_file",
"skip_existing",
] ]
def gui_populate(self): def gui_populate(self):
+133 -100
View File
@@ -12,13 +12,15 @@ import ffmpeg
import m3u8 import m3u8
import requests import requests
from constants import CHUNK_SIZE, COVER_NAME from constants import CHUNK_SIZE, COVER_NAME
from requests.adapters import HTTPAdapter, Retry
from requests.exceptions import HTTPError from requests.exceptions import HTTPError
from rich.progress import Progress, TaskID from rich.progress import Progress, TaskID
from tidalapi import Album, Mix, Playlist, Session, Track, UserPlaylist, Video from tidalapi import Album, Mix, Playlist, Session, Track, UserPlaylist, Video
from tidalapi.exceptions import TooManyRequests
from tidalapi.media import AudioExtensions, Codec, Quality, StreamManifest, VideoExtensions from tidalapi.media import AudioExtensions, Codec, Quality, StreamManifest, VideoExtensions
from tidal_dl_ng.config import Settings from tidal_dl_ng.config import Settings
from tidal_dl_ng.constants import EXTENSION_LYRICS, REQUESTS_TIMEOUT_SEC, MediaType, QualityVideo, SkipExisting from tidal_dl_ng.constants import EXTENSION_LYRICS, REQUESTS_TIMEOUT_SEC, MediaType, QualityVideo
from tidal_dl_ng.helper.decryption import decrypt_file, decrypt_security_token from tidal_dl_ng.helper.decryption import decrypt_file, decrypt_security_token
from tidal_dl_ng.helper.exceptions import MediaMissing from tidal_dl_ng.helper.exceptions import MediaMissing
from tidal_dl_ng.helper.path import check_file_exists, format_path_media, path_file_sanitize, url_to_filename from tidal_dl_ng.helper.path import check_file_exists, format_path_media, path_file_sanitize, url_to_filename
@@ -53,7 +55,7 @@ class RequestsClient:
class Download: class Download:
settings: Settings settings: Settings
session: Session session: Session
skip_existing: SkipExisting = SkipExisting.Disabled skip_existing: bool = False
fn_logger: Callable fn_logger: Callable
progress_gui: ProgressBars progress_gui: ProgressBars
progress: Progress progress: Progress
@@ -63,7 +65,7 @@ class Download:
session: Session, session: Session,
path_base: str, path_base: str,
fn_logger: Callable, fn_logger: Callable,
skip_existing: SkipExisting = SkipExisting.Disabled, skip_existing: bool = False,
progress_gui: ProgressBars = None, progress_gui: ProgressBars = None,
progress: Progress = None, progress: Progress = None,
): ):
@@ -91,7 +93,7 @@ class Download:
self, self,
media: Track | Video, media: Track | Video,
path_file: pathlib.Path, path_file: pathlib.Path,
) -> pathlib.Path: ) -> (bool, pathlib.Path):
media_name: str = name_builder_item(media) media_name: str = name_builder_item(media)
urls: [str] urls: [str]
path_base: pathlib.Path = path_file.parent path_base: pathlib.Path = path_file.parent
@@ -163,22 +165,22 @@ class Download:
# If this is NOT the case, but any other URL has resulted in an error, # If this is NOT the case, but any other URL has resulted in an error,
# mark the whole thing as corrupt. # mark the whole thing as corrupt.
result_segments = False result_segments = False
self.fn_logger.error(f"Something went wrong while downloading {media_name}. File is corrupt!")
# Bring list into right order, so segments can be easily merged. tmp_path_file_decrypted: pathlib.Path = path_file
dl_segment_results.sort(key=lambda x: x.id_segment)
# TODO: Implement error handling on corrupt segments (also in the following method.
reult_merge: bool = self._segments_merge(path_file, dl_segment_results)
tmp_path_file_decrypted: pathlib.Path = pathlib.Path("")
if reult_merge: # Only if no error happened while downloading.
if isinstance(media, Track) and stream_manifest.is_encrypted: if result_segments:
# Bring list into right order, so segments can be easily merged.
dl_segment_results.sort(key=lambda x: x.id_segment)
reult_merge: bool = self._segments_merge(path_file, dl_segment_results)
if reult_merge and isinstance(media, Track) and stream_manifest.is_encrypted:
key, nonce = decrypt_security_token(stream_manifest.encryption_key) key, nonce = decrypt_security_token(stream_manifest.encryption_key)
tmp_path_file_decrypted = path_file + "_decrypted" tmp_path_file_decrypted = path_file.with_suffix("_decrypted")
decrypt_file(path_file, tmp_path_file_decrypted, key, nonce) decrypt_file(path_file, tmp_path_file_decrypted, key, nonce)
else:
tmp_path_file_decrypted = path_file
return tmp_path_file_decrypted return result_segments, tmp_path_file_decrypted
def _segments_merge(self, path_file, dl_segment_results) -> bool: def _segments_merge(self, path_file, dl_segment_results) -> bool:
result: bool result: bool
@@ -208,11 +210,18 @@ class Download:
result: bool = False result: bool = False
path_segment: pathlib.Path = path_base / url_to_filename(url) path_segment: pathlib.Path = path_base / url_to_filename(url)
# Calculate the segment ID based on the file name within the URL. # Calculate the segment ID based on the file name within the URL.
id_segment: int = int(path_segment.stem) id_segment: int = int(str(path_segment.stem).split("_")[-1])
error: HTTPError | None = None
# Retry download on failed segments, with an exponential delay between retries
s = requests.Session()
retries = Retry(total=5, backoff_factor=1) # , status_forcelist=[ 502, 503, 504 ])
s.mount("https://", HTTPAdapter(max_retries=retries))
try: try:
# Create the request object with stream=True, so the content won't be loaded into memory at once. # Create the request object with stream=True, so the content won't be loaded into memory at once.
r = requests.get(url, stream=True, timeout=REQUESTS_TIMEOUT_SEC) r = s.get(url, stream=True, timeout=REQUESTS_TIMEOUT_SEC)
r.raise_for_status() r.raise_for_status()
@@ -224,15 +233,18 @@ class Download:
self.progress.advance(p_task) self.progress.advance(p_task)
result = True result = True
except HTTPError: except Exception as e:
# TODO: Maybe return e as well? error = e
self.progress.advance(p_task) self.progress.advance(p_task)
finally: self.fn_logger.exception(e)
# To send the progress to the GUI, we need to emit the percentage.
if not progress_to_stdout:
self.progress_gui.item.emit(self.progress.tasks[p_task].percentage)
return DownloadSegmentResult(result=result, url=url, path_segment=path_segment, id_segment=id_segment) # To send the progress to the GUI, we need to emit the percentage.
if not progress_to_stdout:
self.progress_gui.item.emit(self.progress.tasks[p_task].percentage)
return DownloadSegmentResult(
result=result, url=url, path_segment=path_segment, id_segment=id_segment, error=error
)
def item( def item(
self, self,
@@ -273,50 +285,66 @@ class Download:
return False, "" return False, ""
# Get extension. # Create file name and path
file_extension: str file_extension_dummy: str = AudioExtensions.FLAC
do_flac_extract = False file_name_relative = format_path_media(file_template, media)
path_media_dst: pathlib.Path = (
(pathlib.Path(self.path_base).expanduser() / (file_name_relative + file_extension_dummy))
.resolve()
.absolute()
)
if isinstance(media, Track): # Sanitize final path_file to fit into OS boundaries.
# If a quality is explicitly set, change it. path_media_dst = pathlib.Path(path_file_sanitize(str(path_media_dst), adapt=True))
if quality_audio:
quality_audio_old: Quality = self.adjust_quality_audio(quality_audio)
file_extension = media.get_stream().get_stream_manifest().file_extension # Compute if and how downloads need to be skipped.
# Use M4A extension for MP4 audio tracks, because it looks better and is completely interchangeable. if self.skip_existing:
file_extension = AudioExtensions.M4A if file_extension == AudioExtensions.MP4 else file_extension skip_file: bool = check_file_exists(path_media_dst, extension_ignore=True)
else:
skip_file: bool = False
if self.settings.data.extract_flac: if not skip_file:
if ( # Get extension.
media.get_stream().get_stream_manifest().codecs.upper() == Codec.FLAC file_extension: str
do_flac_extract = False
# If a quality is explicitly set, change it and remember the previously set quality.
quality_audio_old: Quality = self.adjust_quality_audio(quality_audio) if quality_audio else quality_audio
quality_video_old: QualityVideo = (
self.adjust_quality_video(quality_video) if quality_video else quality_video
)
if isinstance(media, Track):
try:
media_stream = media.get_stream()
except TooManyRequests:
self.fn_logger.exception(
f"Too many requests against TIDAL backend. Skipping '{name_builder_item(media)}'. "
f"Consider to activate delay between downloads."
)
return False, ""
except:
self.fn_logger.exception(f"Something went wrong. Skipping '{name_builder_item(media)}'.")
return False, ""
file_extension = media_stream.get_stream_manifest().file_extension
# Use M4A extension for MP4 audio tracks, because it looks better and is completely interchangeable.
file_extension = AudioExtensions.M4A if file_extension == AudioExtensions.MP4 else file_extension
if self.settings.data.extract_flac and (
media_stream.get_stream_manifest().codecs.upper() == Codec.FLAC
and file_extension != AudioExtensions.FLAC and file_extension != AudioExtensions.FLAC
): ):
file_extension = AudioExtensions.FLAC file_extension = AudioExtensions.FLAC
do_flac_extract = True do_flac_extract = True
elif isinstance(media, Video): elif isinstance(media, Video):
if quality_video: file_extension = AudioExtensions.MP4 if self.settings.data.video_convert_mp4 else VideoExtensions.TS
quality_video_old: QualityVideo = self.adjust_quality_video(quality_video)
file_extension = AudioExtensions.MP4 if self.settings.data.video_convert_mp4 else VideoExtensions.TS # Compute file name and create destination directory
path_media_dst = path_media_dst.with_suffix(file_extension)
os.makedirs(path_media_dst.parent, exist_ok=True)
# Create file name and path
file_name_relative = format_path_media(file_template, media)
path_media_dst = os.path.abspath(
os.path.normpath(os.path.join(os.path.expanduser(self.path_base), file_name_relative))
)
# Sanitize final path_file to fit into OS boundaries.
uniquify: bool = self.skip_existing == SkipExisting.Append
path_media_dst = path_file_sanitize(path_media_dst + file_extension, adapt=True, uniquify=uniquify)
# Compute if and how downloads need to be skipped.
if self.skip_existing in (SkipExisting.ExtensionIgnore, SkipExisting.Filename):
extension_ignore: bool = self.skip_existing == SkipExisting.ExtensionIgnore
file_exists: bool = check_file_exists(path_media_dst, extension_ignore=extension_ignore)
else:
file_exists: bool = False
if not file_exists:
# Create a temp directory and file. # Create a temp directory and file.
with tempfile.TemporaryDirectory(ignore_cleanup_errors=True) as tmp_path_dir: with tempfile.TemporaryDirectory(ignore_cleanup_errors=True) as tmp_path_dir:
tmp_path_file: pathlib.Path = pathlib.Path(tmp_path_dir) / str(uuid4()) tmp_path_file: pathlib.Path = pathlib.Path(tmp_path_dir) / str(uuid4())
@@ -325,54 +353,54 @@ class Download:
tmp_path_file.touch() tmp_path_file.touch()
# Download media. # Download media.
tmp_path_file = self._download(media=media, path_file=tmp_path_file) result_download, tmp_path_file = self._download(media=media, path_file=tmp_path_file)
# Convert video from TS to MP4 if result_download:
if isinstance(media, Video) and self.settings.data.video_convert_mp4: # Convert video from TS to MP4
# Convert `*.ts` file to `*.mp4` using ffmpeg if isinstance(media, Video) and self.settings.data.video_convert_mp4:
tmp_path_file = self._video_convert(tmp_path_file) # Convert `*.ts` file to `*.mp4` using ffmpeg
tmp_path_file = self._video_convert(tmp_path_file)
# Extract FLAC from MP4 container using ffmpeg # Extract FLAC from MP4 container using ffmpeg
if isinstance(media, Track) and self.settings.data.extract_flac and do_flac_extract: if isinstance(media, Track) and self.settings.data.extract_flac and do_flac_extract:
tmp_path_file = self._extract_flac(tmp_path_file) tmp_path_file = self._extract_flac(tmp_path_file)
tmp_path_lyrics: pathlib.Path | None = None tmp_path_lyrics: pathlib.Path | None = None
tmp_path_cover: pathlib.Path | None = None tmp_path_cover: pathlib.Path | None = None
# Write metadata to file. # Write metadata to file.
if not isinstance(media, Video): if not isinstance(media, Video):
result_metadata, tmp_path_lyrics, tmp_path_cover = self.metadata_write( result_metadata, tmp_path_lyrics, tmp_path_cover = self.metadata_write(
media, tmp_path_file, is_parent_album media, tmp_path_file, is_parent_album
) )
# Move lyrics file
if self.settings.data.lyrics_file and not isinstance(media, Video) and tmp_path_lyrics:
self._move_lyrics(tmp_path_lyrics, path_media_dst)
# Move cover file
if self.settings.data.cover_album_file and tmp_path_cover:
self._move_cover(tmp_path_cover, path_media_dst)
# Move final file to the configured destination directory. # Move final file to the configured destination directory.
os.makedirs(os.path.dirname(path_media_dst), exist_ok=True)
shutil.move(tmp_path_file, path_media_dst) shutil.move(tmp_path_file, path_media_dst)
# Move lyrics file if quality_audio:
if self.settings.data.lyrics_file and not isinstance(media, Video): # Set quality back to the global user value
self._move_lyrics(tmp_path_lyrics, path_media_dst) self.adjust_quality_audio(quality_audio_old)
# Move cover file if quality_video:
if self.settings.data.cover_album_file: # Set quality back to the global user value
self._move_cover(tmp_path_cover, path_media_dst) self.adjust_quality_video(quality_video_old)
else: else:
self.fn_logger.debug(f"Download skipped, since file exists: '{path_media_dst}'") self.fn_logger.debug(f"Download skipped, since file exists: '{path_media_dst}'")
status_download: bool = not file_exists status_download: bool = not skip_file
if quality_audio:
# Set quality back to the global user value
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. # 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. # Only use this, if you have a list of several Track items.
if download_delay: if download_delay and not skip_file:
time_sleep: float = round(random.SystemRandom().uniform(2, 5), 1) time_sleep: float = round(random.SystemRandom().uniform(1.5, 4), 1)
self.fn_logger.debug(f"Next download will start in {time_sleep} seconds.") self.fn_logger.debug(f"Next download will start in {time_sleep} seconds.")
time.sleep(time_sleep) time.sleep(time_sleep)
@@ -397,7 +425,7 @@ class Download:
result: bool result: bool
# Check if the file was downloaded # Check if the file was downloaded
if path_file_source and os.path.isfile(path_file_source): if path_file_source and path_file_source.is_file():
# Move it. # Move it.
shutil.move(path_file_source, path_file_destination) shutil.move(path_file_source, path_file_destination)
@@ -407,16 +435,16 @@ class Download:
return result return result
def _move_lyrics(self, path_lyrics: pathlib.Path, file_media_dst: str) -> bool: def _move_lyrics(self, path_lyrics: pathlib.Path, file_media_dst: pathlib.Path) -> bool:
# Build tmp lyrics filename # Build tmp lyrics filename
path_file_lyrics: str = os.path.splitext(file_media_dst)[0] + EXTENSION_LYRICS path_file_lyrics: pathlib.Path = file_media_dst.with_suffix(EXTENSION_LYRICS)
result: bool = self._move_file(path_lyrics, path_file_lyrics) result: bool = self._move_file(path_lyrics, path_file_lyrics)
return result return result
def _move_cover(self, path_cover: pathlib.Path, file_media_dst: str) -> bool: def _move_cover(self, path_cover: pathlib.Path, file_media_dst: pathlib.Path) -> bool:
# Build tmp lyrics filename # Build tmp lyrics filename
path_file_cover: pathlib.Path = pathlib.Path(file_media_dst).parent.absolute() / COVER_NAME path_file_cover: pathlib.Path = file_media_dst.parent / COVER_NAME
result: bool = self._move_file(path_cover, path_file_cover) result: bool = self._move_file(path_cover, path_file_cover)
return result return result
@@ -575,7 +603,7 @@ class Download:
while not self.progress.finished: while not self.progress.finished:
for item_media in items: for item_media in items:
# Download the item. # Download the item.
status_download, result_path_file = self.item( status, result_path_file = self.item(
media=item_media, media=item_media,
file_template=file_name_relative, file_template=file_name_relative,
quality_audio=quality_audio, quality_audio=quality_audio,
@@ -594,7 +622,7 @@ class Download:
path_file_out: pathlib.Path = path_file.with_suffix(AudioExtensions.MP4) path_file_out: pathlib.Path = path_file.with_suffix(AudioExtensions.MP4)
result, _ = ( result, _ = (
ffmpeg.input(path_file) ffmpeg.input(path_file)
.output(path_file_out, map=0, c="copy", loglevel="quiet") .output(str(path_file_out), map=0, c="copy", loglevel="quiet")
.run(cmd=self.settings.data.path_binary_ffmpeg) .run(cmd=self.settings.data.path_binary_ffmpeg)
) )
@@ -605,7 +633,12 @@ class Download:
result, _ = ( result, _ = (
ffmpeg.input(path_media_src) ffmpeg.input(path_media_src)
.output( .output(
path_media_out, map=0, movflags="use_metadata_tags", acodec="copy", map_metadata="0:g", loglevel="quiet" str(path_media_out),
map=0,
movflags="use_metadata_tags",
acodec="copy",
map_metadata="0:g",
loglevel="quiet",
) )
.run(cmd=self.settings.data.path_binary_ffmpeg) .run(cmd=self.settings.data.path_binary_ffmpeg)
) )
+6 -6
View File
@@ -1,9 +1,9 @@
import math import math
import os import os
import pathlib
import posixpath import posixpath
import re import re
import sys import sys
from pathlib import Path, PosixPath
from urllib.parse import unquote, urlsplit from urllib.parse import unquote, urlsplit
from pathvalidate import sanitize_filename, sanitize_filepath from pathvalidate import sanitize_filename, sanitize_filepath
@@ -197,7 +197,7 @@ def get_format_template(
def path_file_sanitize(path_file: str, adapt: bool = False, uniquify: bool = False) -> (bool, str): def path_file_sanitize(path_file: str, adapt: bool = False, uniquify: bool = False) -> (bool, str):
# Split into path and filename # Split into path and filename
pathname, filename = os.path.split(path_file) pathname, filename = os.path.split(path_file)
file_extension: str = Path(path_file).suffix file_extension: str = pathlib.Path(path_file).suffix
# Sanitize path # Sanitize path
try: try:
@@ -207,7 +207,7 @@ def path_file_sanitize(path_file: str, adapt: bool = False, uniquify: bool = Fal
except ValidationError: except ValidationError:
# If adaption of path is allowed in case of an error set path to HOME. # If adaption of path is allowed in case of an error set path to HOME.
if adapt: if adapt:
pathname_sanitized: str = Path.home() pathname_sanitized: str = str(pathlib.Path.home())
else: else:
raise raise
@@ -274,10 +274,10 @@ def file_unique_suffix(path_file: str, seperator: str = "_") -> str:
return unique_suffix return unique_suffix
def check_file_exists(path_file: str, extension_ignore: bool = False) -> bool: def check_file_exists(path_file: pathlib.Path, extension_ignore: bool = False) -> bool:
if extension_ignore: if extension_ignore:
path_file_stem: str = Path(path_file).stem path_file_stem: str = pathlib.Path(path_file).stem
path_parent: PosixPath = Path(path_file).parent path_parent: pathlib.Path = pathlib.Path(path_file).parent
path_files: [str] = [] path_files: [str] = []
for extension in AudioExtensions: for extension in AudioExtensions:
+3
View File
@@ -21,3 +21,6 @@ class LoggerWrapped:
def critical(self, value): def critical(self, value):
self.fn_print(value) self.fn_print(value)
def exception(self, value):
self.fn_print(value)
+3 -7
View File
@@ -3,13 +3,13 @@ from dataclasses import dataclass
from dataclasses_json import dataclass_json from dataclasses_json import dataclass_json
from tidalapi import Quality from tidalapi import Quality
from tidal_dl_ng.constants import CoverDimensions, QualityVideo, SkipExisting from tidal_dl_ng.constants import CoverDimensions, QualityVideo
@dataclass_json @dataclass_json
@dataclass @dataclass
class Settings: class Settings:
skip_existing: SkipExisting = SkipExisting.Disabled skip_existing: bool = True
lyrics_embed: bool = False lyrics_embed: bool = False
lyrics_file: bool = False lyrics_file: bool = False
# TODO: Implement API KEY selection. # TODO: Implement API KEY selection.
@@ -43,11 +43,7 @@ class Settings:
@dataclass_json @dataclass_json
@dataclass @dataclass
class HelpSettings: class HelpSettings:
skip_existing: str = ( skip_existing: str = "Skip download if file already exists."
"Do not download, if file already exists. Possible option false = do not skip, "
"'exact' = if filename already exists, 'extension_ignore' = skip even if a file with a "
"different file extension exists."
)
album_cover_save: str = "Safe cover to album folder." album_cover_save: str = "Safe cover to album folder."
lyrics_embed: str = "Embed lyrics in audio file, if lyrics are available." lyrics_embed: str = "Embed lyrics in audio file, if lyrics are available."
lyrics_file: str = "Save lyrics to separate *.lrc file, if lyrics are available." lyrics_file: str = "Save lyrics to separate *.lrc file, if lyrics are available."
+3
View File
@@ -1,6 +1,8 @@
import pathlib import pathlib
from dataclasses import dataclass from dataclasses import dataclass
from requests import HTTPError
@dataclass @dataclass
class DownloadSegmentResult: class DownloadSegmentResult:
@@ -8,3 +10,4 @@ class DownloadSegmentResult:
url: str url: str
path_segment: pathlib.Path path_segment: pathlib.Path
id_segment: int id_segment: int
error: HTTPError | None = None
+22 -30
View File
@@ -153,6 +153,24 @@ class Ui_DialogSettings:
self.lv_flags.addLayout(self.lh_flags_4) self.lv_flags.addLayout(self.lh_flags_4)
self.horizontalLayout = QHBoxLayout()
self.horizontalLayout.setObjectName("horizontalLayout")
self.lv_flag_skip_existing = QVBoxLayout()
self.lv_flag_skip_existing.setObjectName("lv_flag_skip_existing")
self.cb_skip_existing = QCheckBox(self.gb_flags)
self.cb_skip_existing.setObjectName("cb_skip_existing")
self.lv_flag_skip_existing.addWidget(self.cb_skip_existing)
self.horizontalLayout.addLayout(self.lv_flag_skip_existing)
self.verticalLayout_3 = QVBoxLayout()
self.verticalLayout_3.setObjectName("verticalLayout_3")
self.horizontalLayout.addLayout(self.verticalLayout_3)
self.lv_flags.addLayout(self.horizontalLayout)
self.lv_main.addWidget(self.gb_flags) self.lv_main.addWidget(self.gb_flags)
self.gb_choices = QGroupBox(DialogSettings) self.gb_choices = QGroupBox(DialogSettings)
@@ -164,38 +182,13 @@ class Ui_DialogSettings:
self.gb_choices.setSizePolicy(sizePolicy3) self.gb_choices.setSizePolicy(sizePolicy3)
self.lv_choices = QVBoxLayout(self.gb_choices) self.lv_choices = QVBoxLayout(self.gb_choices)
self.lv_choices.setObjectName("lv_choices") self.lv_choices.setObjectName("lv_choices")
self.lh_choices_skip_existing = QHBoxLayout()
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.Policy.Fixed, QSizePolicy.Policy.Fixed)
sizePolicy4.setHorizontalStretch(0)
sizePolicy4.setVerticalStretch(0)
sizePolicy4.setHeightForWidth(self.l_icon_skip_existing.sizePolicy().hasHeightForWidth())
self.l_icon_skip_existing.setSizePolicy(sizePolicy4)
self.lh_choices_skip_existing.addWidget(self.l_icon_skip_existing)
self.l_skip_existing = QLabel(self.gb_choices)
self.l_skip_existing.setObjectName("l_skip_existing")
sizePolicy4.setHeightForWidth(self.l_skip_existing.sizePolicy().hasHeightForWidth())
self.l_skip_existing.setSizePolicy(sizePolicy4)
self.lh_choices_skip_existing.addWidget(self.l_skip_existing)
self.c_skip_existing = QComboBox(self.gb_choices)
self.c_skip_existing.setObjectName("c_skip_existing")
self.lh_choices_skip_existing.addWidget(self.c_skip_existing)
self.lh_choices_skip_existing.setStretch(2, 50)
self.lv_choices.addLayout(self.lh_choices_skip_existing)
self.lh_choices_quality_audio = QHBoxLayout() self.lh_choices_quality_audio = QHBoxLayout()
self.lh_choices_quality_audio.setObjectName("lh_choices_quality_audio") self.lh_choices_quality_audio.setObjectName("lh_choices_quality_audio")
self.l_icon_quality_audio = QLabel(self.gb_choices) self.l_icon_quality_audio = QLabel(self.gb_choices)
self.l_icon_quality_audio.setObjectName("l_icon_quality_audio") self.l_icon_quality_audio.setObjectName("l_icon_quality_audio")
sizePolicy4 = QSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed)
sizePolicy4.setHorizontalStretch(0)
sizePolicy4.setVerticalStretch(0)
sizePolicy4.setHeightForWidth(self.l_icon_quality_audio.sizePolicy().hasHeightForWidth()) sizePolicy4.setHeightForWidth(self.l_icon_quality_audio.sizePolicy().hasHeightForWidth())
self.l_icon_quality_audio.setSizePolicy(sizePolicy4) self.l_icon_quality_audio.setSizePolicy(sizePolicy4)
@@ -532,9 +525,8 @@ class Ui_DialogSettings:
self.cb_extract_flac.setText(QCoreApplication.translate("DialogSettings", "CheckBox", None)) self.cb_extract_flac.setText(QCoreApplication.translate("DialogSettings", "CheckBox", None))
self.cb_metadata_cover_embed.setText(QCoreApplication.translate("DialogSettings", "CheckBox", None)) self.cb_metadata_cover_embed.setText(QCoreApplication.translate("DialogSettings", "CheckBox", None))
self.cb_cover_album_file.setText(QCoreApplication.translate("DialogSettings", "CheckBox", None)) self.cb_cover_album_file.setText(QCoreApplication.translate("DialogSettings", "CheckBox", None))
self.cb_skip_existing.setText(QCoreApplication.translate("DialogSettings", "CheckBox", None))
self.gb_choices.setTitle(QCoreApplication.translate("DialogSettings", "Choices", 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))
self.l_icon_quality_audio.setText(QCoreApplication.translate("DialogSettings", "TextLabel", None)) self.l_icon_quality_audio.setText(QCoreApplication.translate("DialogSettings", "TextLabel", None))
self.l_quality_audio.setText(QCoreApplication.translate("DialogSettings", "TextLabel", None)) self.l_quality_audio.setText(QCoreApplication.translate("DialogSettings", "TextLabel", None))
self.l_icon_quality_video.setText(QCoreApplication.translate("DialogSettings", "TextLabel", None)) self.l_icon_quality_video.setText(QCoreApplication.translate("DialogSettings", "TextLabel", None))
+18 -33
View File
@@ -210,6 +210,24 @@
</item> </item>
</layout> </layout>
</item> </item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<layout class="QVBoxLayout" name="lv_flag_skip_existing">
<item>
<widget class="QCheckBox" name="cb_skip_existing">
<property name="text">
<string>CheckBox</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_3"/>
</item>
</layout>
</item>
</layout> </layout>
</widget> </widget>
</item> </item>
@@ -225,39 +243,6 @@
<string>Choices</string> <string>Choices</string>
</property> </property>
<layout class="QVBoxLayout" name="lv_choices"> <layout class="QVBoxLayout" name="lv_choices">
<item>
<layout class="QHBoxLayout" name="lh_choices_skip_existing" stretch="0,0,50">
<item>
<widget class="QLabel" name="l_icon_skip_existing">
<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_skip_existing">
<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="QComboBox" name="c_skip_existing"/>
</item>
</layout>
</item>
<item> <item>
<layout class="QHBoxLayout" name="lh_choices_quality_audio" stretch="0,0,50"> <layout class="QHBoxLayout" name="lh_choices_quality_audio" stretch="0,0,50">
<item> <item>