Refactor/code cleanup (#37)

* fix imports
remove unused imports
use full import path
sort import (1. Standard Library, 2. Third-Party, 3. Local) in alphabetic.

* - remove: Method 3 from src.core.ytsage_downlader:cleanup_subtitle_file
  - it could delete the subtitle file of other movies if it present in same directory as it scane recursively.

- refactor: migrate from os.path to pathlib.Path for path handling
  - Replaced os.path methods with pathlib.Path to improve readability,
  - avoid repeatation.
  - cross-platform compatibility, and maintain cleaner code.

- improve: enhance code readability
  - Standardized string literals to use double quotes for consistency
  - Removed unnecessary spaces to maintain cleaner formatting
  - Applied code formatting for better readability and maintainability

* - add: ytsage_constants.py file for one place to store all constants.

- imporve: return type hint for function.

- remove: src/gui/ytsage_gui_dialogs.py file to avoid repetation
  - src/gui/dialogs is renamed to src/gui/ytsage_gui_dialogs for same naming convection. (future import will remains same)
  - use of src/gui/ytsage_gui_dialogs/__init__.py to import the dilogs modules.

- change: variable self.parent to self._parent so it does not overwrite the parent()
  - add type hint checking.

* - refactor: QMetaObject.invokeMethod to Signal
  - I encounter error with incokeMethod. Could not solve it.
  - So, Changed it to Signal to match app code language.

- implement: the ytsage_constants.py to code
  - remove: unnecessary logic
  - remove: repetitive code logic.

- update: yt-dlp logic for src\gui\ytsage_gui_dialogs\ytsage_dialogs_update:_update_binary
  - yt-dlp update logic will use `yt-dlp -U`

* refactor: remove unused imports and streamline code formatting across multiple files
This commit is contained in:
Viren Hirpara
2025-08-26 18:57:12 +05:30
committed by GitHub
parent 6652d7f00f
commit 9c13b4b61c
21 changed files with 2805 additions and 2628 deletions
+136 -112
View File
@@ -1,58 +1,67 @@
from PySide6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout,
QHBoxLayout, QLineEdit, QPushButton, QTableWidget,
QTableWidgetItem, QProgressBar, QLabel, QFileDialog,
QHeaderView, QStyle, QStyleFactory, QComboBox, QTextEdit,
QDialog, QPlainTextEdit, QCheckBox, QButtonGroup, QScrollArea,
QSizePolicy)
from PySide6.QtCore import Qt, Signal, QObject, QThread
from PySide6.QtGui import QIcon, QPalette, QColor, QPixmap
from PySide6.QtCore import QObject, Qt, Signal
from PySide6.QtGui import QColor
from PySide6.QtWidgets import QCheckBox, QHBoxLayout, QHeaderView, QSizePolicy, QTableWidget, QTableWidgetItem, QWidget
class FormatSignals(QObject):
format_update = Signal(list)
class FormatTableMixin:
def setup_format_table(self):
def setup_format_table(self) -> QTableWidget:
self.format_signals = FormatSignals()
# Format table with improved styling
self.format_table = QTableWidget()
self.format_table.setColumnCount(8)
self.format_table.setHorizontalHeaderLabels(['Select', 'Quality', 'Extension', 'Resolution', 'File Size', 'Codec', 'Audio', 'Notes'])
self.format_table.setHorizontalHeaderLabels(
[
"Select",
"Quality",
"Extension",
"Resolution",
"File Size",
"Codec",
"Audio",
"Notes",
]
)
# Enable alternating row colors
self.format_table.setAlternatingRowColors(True)
# Set specific column widths and resize modes
self.format_table.horizontalHeader().setSectionResizeMode(0, QHeaderView.ResizeMode.Fixed) # Select
self.format_table.setColumnWidth(0, 50) # Select column width
self.format_table.horizontalHeader().setSectionResizeMode(1, QHeaderView.ResizeMode.Fixed) # Quality
self.format_table.setColumnWidth(1, 100) # Quality width
self.format_table.horizontalHeader().setSectionResizeMode(2, QHeaderView.ResizeMode.Fixed) # Extension
self.format_table.setColumnWidth(2, 80) # Extension width
self.format_table.horizontalHeader().setSectionResizeMode(3, QHeaderView.ResizeMode.Fixed) # Resolution
self.format_table.setColumnWidth(3, 100) # Resolution width
self.format_table.horizontalHeader().setSectionResizeMode(4, QHeaderView.ResizeMode.Fixed) # File Size
self.format_table.setColumnWidth(4, 100) # File Size width
self.format_table.horizontalHeader().setSectionResizeMode(5, QHeaderView.ResizeMode.Fixed) # Codec
self.format_table.setColumnWidth(5, 150) # Codec width
self.format_table.horizontalHeader().setSectionResizeMode(6, QHeaderView.ResizeMode.Fixed) # Audio
self.format_table.setColumnWidth(6, 120) # Audio width
self.format_table.horizontalHeader().setSectionResizeMode(7, QHeaderView.ResizeMode.Stretch) # Notes (will stretch)
# Set vertical header (row numbers) visible to false
self.format_table.verticalHeader().setVisible(False)
# Set selection mode to no selection (since we're using checkboxes)
self.format_table.setSelectionMode(QTableWidget.SelectionMode.NoSelection)
self.format_table.setStyleSheet("""
self.format_table.setStyleSheet(
"""
QTableWidget {
background-color: #1b2021;
border: 2px solid #1b2021;
@@ -96,27 +105,28 @@ class FormatTableMixin:
QWidget {
background-color: transparent;
}
""")
"""
)
# Store format checkboxes and formats
self.format_checkboxes = []
self.all_formats = []
# Set table size policies
self.format_table.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
# Set minimum and maximum heights
self.format_table.setMinimumHeight(200)
# Connect the signal
self.format_signals.format_update.connect(self._update_format_table)
return self.format_table
def filter_formats(self):
if not hasattr(self, 'all_formats'):
def filter_formats(self) -> None:
if not hasattr(self, "all_formats"):
return
# Clear current table
self.format_table.setRowCount(0)
self.format_checkboxes.clear()
@@ -124,44 +134,46 @@ class FormatTableMixin:
# Determine which formats to show
filtered_formats = []
if hasattr(self, 'video_button') and self.video_button.isChecked():
filtered_formats.extend([f for f in self.all_formats
if f.get('vcodec') != 'none'
and f.get('filesize') is not None])
if hasattr(self, "video_button") and self.video_button.isChecked(): # type: ignore[reportAttributeAccessIssue]
filtered_formats.extend([f for f in self.all_formats if f.get("vcodec") != "none" and f.get("filesize") is not None])
if hasattr(self, 'audio_button') and self.audio_button.isChecked():
filtered_formats.extend([f for f in self.all_formats
if (f.get('vcodec') == 'none'
or 'audio only' in f.get('format_note', '').lower())
and f.get('acodec') != 'none'
and f.get('filesize') is not None])
if hasattr(self, "audio_button") and self.audio_button.isChecked(): # type: ignore[reportAttributeAccessIssue]
filtered_formats.extend(
[
f
for f in self.all_formats
if (f.get("vcodec") == "none" or "audio only" in f.get("format_note", "").lower())
and f.get("acodec") != "none"
and f.get("filesize") is not None
]
)
# Sort formats by quality
def get_quality(f):
if f.get('vcodec') != 'none':
res = f.get('resolution', '0x0').split('x')[-1]
if f.get("vcodec") != "none":
res = f.get("resolution", "0x0").split("x")[-1]
try:
return int(res)
except ValueError:
return 0
else:
return f.get('abr', 0)
return f.get("abr", 0)
filtered_formats.sort(key=get_quality, reverse=True)
# Update table with filtered formats
self.format_signals.format_update.emit(filtered_formats)
def _update_format_table(self, formats):
def _update_format_table(self, formats) -> None:
self.format_table.setRowCount(0)
self.format_checkboxes.clear()
is_playlist_mode = hasattr(self, 'is_playlist') and self.is_playlist
is_playlist_mode = hasattr(self, "is_playlist") and self.is_playlist # type: ignore[reportAttributeAccessIssue]
# Configure columns based on mode
if is_playlist_mode:
self.format_table.setColumnCount(5)
self.format_table.setHorizontalHeaderLabels(['Select', 'Quality', 'Resolution', 'Notes', 'Audio'])
self.format_table.setHorizontalHeaderLabels(["Select", "Quality", "Resolution", "Notes", "Audio"])
# Configure column visibility and resizing for playlist mode
self.format_table.setColumnHidden(5, True)
@@ -175,14 +187,25 @@ class FormatTableMixin:
self.format_table.horizontalHeader().setSectionResizeMode(2, QHeaderView.ResizeMode.Stretch)
self.format_table.horizontalHeader().setSectionResizeMode(3, QHeaderView.ResizeMode.Stretch)
self.format_table.horizontalHeader().setSectionResizeMode(4, QHeaderView.ResizeMode.Stretch)
else:
self.format_table.setColumnCount(8)
self.format_table.setHorizontalHeaderLabels(['Select', 'Quality', 'Extension', 'Resolution', 'File Size', 'Codec', 'Audio', 'Notes'])
self.format_table.setHorizontalHeaderLabels(
[
"Select",
"Quality",
"Extension",
"Resolution",
"File Size",
"Codec",
"Audio",
"Notes",
]
)
# Ensure all columns are visible
for i in range(2, 8):
self.format_table.setColumnHidden(i, False)
self.format_table.setColumnHidden(i, False)
# Reapply resize modes for non-playlist mode if needed (optional, might be okay without)
self.format_table.horizontalHeader().setSectionResizeMode(0, QHeaderView.ResizeMode.Fixed)
self.format_table.setColumnWidth(0, 50)
@@ -199,20 +222,22 @@ class FormatTableMixin:
self.format_table.horizontalHeader().setSectionResizeMode(6, QHeaderView.ResizeMode.Fixed)
self.format_table.setColumnWidth(6, 120)
self.format_table.horizontalHeader().setSectionResizeMode(7, QHeaderView.ResizeMode.Stretch)
# Find best quality format for recommendations (only needed for non-playlist mode notes)
best_video_size = 0
if not is_playlist_mode:
best_video_size = max((f.get('filesize', 0) for f in formats if f.get('vcodec') != 'none'), default=0)
best_video_size = max(
(f.get("filesize", 0) for f in formats if f.get("vcodec") != "none"),
default=0,
)
for f in formats:
row = self.format_table.rowCount()
self.format_table.insertRow(row)
# Column 0: Select Checkbox (Always shown)
checkbox = QCheckBox()
checkbox.format_id = str(f.get('format_id', ''))
checkbox.format_id = str(f.get("format_id", "")) # type: ignore[reportAttributeAccessIssue]
checkbox.clicked.connect(lambda checked, cb=checkbox: self.handle_checkbox_click(cb))
self.format_checkboxes.append(checkbox)
checkbox_widget = QWidget()
@@ -229,84 +254,83 @@ class FormatTableMixin:
quality_item = QTableWidgetItem(quality_text)
# Set color based on quality
if "Best" in quality_text:
quality_item.setForeground(QColor('#00ff00')) # Green for best quality
quality_item.setForeground(QColor("#00ff00")) # Green for best quality
elif "High" in quality_text:
quality_item.setForeground(QColor('#00cc00')) # Light green for high quality
quality_item.setForeground(QColor("#00cc00")) # Light green for high quality
elif "Medium" in quality_text:
quality_item.setForeground(QColor('#ffaa00')) # Orange for medium quality
quality_item.setForeground(QColor("#ffaa00")) # Orange for medium quality
elif "Low" in quality_text:
quality_item.setForeground(QColor('#ff5555')) # Red for low quality
quality_item.setForeground(QColor("#ff5555")) # Red for low quality
self.format_table.setItem(row, 1, quality_item)
# --- Populate columns common to both modes (Moved outside the 'if not is_playlist_mode' block) ---
# Column 2: Resolution (Always shown)
resolution = f.get('resolution', 'N/A')
if f.get('vcodec') == 'none':
resolution = 'Audio only'
resolution = f.get("resolution", "N/A")
if f.get("vcodec") == "none":
resolution = "Audio only"
self.format_table.setItem(row, 2, QTableWidgetItem(resolution))
# Column 3: Notes for playlist mode, Extension for normal mode
if is_playlist_mode:
# Get notes for playlist mode
notes = self._get_format_notes(f)
notes_item = QTableWidgetItem(notes)
if "✨ Recommended" in notes:
notes_item.setForeground(QColor('#00ff00')) # Green for recommended
notes_item.setForeground(QColor("#00ff00")) # Green for recommended
elif "💾 Storage friendly" in notes:
notes_item.setForeground(QColor('#00ccff')) # Blue for storage friendly
notes_item.setForeground(QColor("#00ccff")) # Blue for storage friendly
elif "📱 Mobile friendly" in notes:
notes_item.setForeground(QColor('#ff9900')) # Orange for mobile
notes_item.setForeground(QColor("#ff9900")) # Orange for mobile
self.format_table.setItem(row, 3, notes_item)
else:
# Extension for normal mode (column 2)
self.format_table.setItem(row, 2, QTableWidgetItem(f.get('ext', '').upper()))
self.format_table.setItem(row, 2, QTableWidgetItem(f.get("ext", "").upper()))
# Column 4 in playlist mode, Column 6 in normal mode: Audio Status
needs_audio = f.get('acodec') == 'none' and f.get('vcodec') != 'none' # Only mark video-only as needing merge
audio_status = "Will merge audio" if needs_audio else ("✓ Has Audio" if f.get('vcodec') != 'none' else "Audio Only")
needs_audio = f.get("acodec") == "none" and f.get("vcodec") != "none" # Only mark video-only as needing merge
audio_status = "Will merge audio" if needs_audio else ("✓ Has Audio" if f.get("vcodec") != "none" else "Audio Only")
audio_item = QTableWidgetItem(audio_status)
if needs_audio:
audio_item.setForeground(QColor('#ffa500'))
audio_item.setForeground(QColor("#ffa500"))
elif audio_status == "Audio Only":
audio_item.setForeground(QColor('#cccccc')) # Neutral color for audio only
else: # Has Audio (Video+Audio)
audio_item.setForeground(QColor('#00cc00')) # Green for included audio
audio_item.setForeground(QColor("#cccccc")) # Neutral color for audio only
else: # Has Audio (Video+Audio)
audio_item.setForeground(QColor("#00cc00")) # Green for included audio
# Set item for correct column based on mode
audio_column_index = 4 if is_playlist_mode else 6
self.format_table.setItem(row, audio_column_index, audio_item)
# --- Populate columns only shown in non-playlist mode ---
if not is_playlist_mode:
# Column 3: Resolution
self.format_table.setItem(row, 3, QTableWidgetItem(resolution))
# Column 4: File Size
filesize = f"{f.get('filesize', 0) / 1024 / 1024:.2f} MB"
self.format_table.setItem(row, 4, QTableWidgetItem(filesize))
# Column 5: Codec
if f.get('vcodec') == 'none':
codec = f.get('acodec', 'N/A')
if f.get("vcodec") == "none":
codec = f.get("acodec", "N/A")
else:
codec = f"{f.get('vcodec', 'N/A')}"
if f.get('acodec') != 'none':
if f.get("acodec") != "none":
codec += f" / {f.get('acodec', 'N/A')}"
self.format_table.setItem(row, 5, QTableWidgetItem(codec))
# Column 7: Notes
notes = self._get_format_notes(f)
notes_item = QTableWidgetItem(notes)
if "✨ Recommended" in notes:
notes_item.setForeground(QColor('#00ff00')) # Green for recommended
notes_item.setForeground(QColor("#00ff00")) # Green for recommended
elif "💾 Storage friendly" in notes:
notes_item.setForeground(QColor('#00ccff')) # Blue for storage friendly
notes_item.setForeground(QColor("#00ccff")) # Blue for storage friendly
elif "📱 Mobile friendly" in notes:
notes_item.setForeground(QColor('#ff9900')) # Orange for mobile
notes_item.setForeground(QColor("#ff9900")) # Orange for mobile
self.format_table.setItem(row, 7, notes_item)
def handle_checkbox_click(self, clicked_checkbox):
def handle_checkbox_click(self, clicked_checkbox) -> None:
for checkbox in self.format_checkboxes:
if checkbox != clicked_checkbox:
checkbox.setChecked(False)
@@ -317,15 +341,15 @@ class FormatTableMixin:
return checkbox.format_id
return None
def update_format_table(self, formats):
def update_format_table(self, formats) -> None:
self.all_formats = formats
self.format_signals.format_update.emit(formats)
def get_quality_label(self, format_info):
def get_quality_label(self, format_info) -> str:
"""Determine quality label based on format information"""
if format_info.get('vcodec') == 'none':
if format_info.get("vcodec") == "none":
# Audio quality
abr = format_info.get('abr', 0)
abr = format_info.get("abr", 0)
if abr >= 256:
return "Best Audio"
elif abr >= 192:
@@ -337,13 +361,13 @@ class FormatTableMixin:
else:
# Video quality
height = 0
resolution = format_info.get('resolution', '')
resolution = format_info.get("resolution", "")
if resolution:
try:
height = int(resolution.split('x')[1])
height = int(resolution.split("x")[1])
except:
pass
if height >= 2160:
return "Best (4K)"
elif height >= 1440:
@@ -357,20 +381,20 @@ class FormatTableMixin:
else:
return "Low Quality"
def _get_format_notes(self, format_info):
def _get_format_notes(self, format_info) -> str:
"""Generate helpful format notes based on format info."""
notes = []
# Add storage indicator with more granular categories
file_size = format_info.get('filesize') or format_info.get('filesize_approx', 0)
resolution = format_info.get('resolution', '')
file_size = format_info.get("filesize") or format_info.get("filesize_approx", 0)
resolution = format_info.get("resolution", "")
height = 0
if resolution:
try:
height = int(resolution.split('x')[1])
height = int(resolution.split("x")[1])
except:
pass
# Better file size categories
if file_size > 50 * 1024 * 1024: # Over 50MB
notes.append("Large size")
@@ -380,20 +404,20 @@ class FormatTableMixin:
notes.append("Standard size")
else: # Under 5MB
notes.append("Small size")
# Add codec quality indicator
vcodec = format_info.get('vcodec', '')
if vcodec != 'none':
if 'avc1' in vcodec: # H.264
vcodec = format_info.get("vcodec", "")
if vcodec != "none":
if "avc1" in vcodec: # H.264
notes.append("Compatible")
elif 'av01' in vcodec: # AV1
elif "av01" in vcodec: # AV1
notes.append("Efficient")
elif 'vp9' in vcodec: # VP9
elif "vp9" in vcodec: # VP9
notes.append("High quality")
# Add quick mobile compatibility check
if 'avc1' in vcodec and file_size < 8 * 1024 * 1024:
if "avc1" in vcodec and file_size < 8 * 1024 * 1024:
notes.append("Mobile")
# Return simple string
return "".join(notes)
return "".join(notes)