Skip to content

Commit

Permalink
#17 added FLAC for playlists
Browse files Browse the repository at this point in the history
  • Loading branch information
rohankishore authored Jun 24, 2024
1 parent ac9b777 commit f67d913
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 50 deletions.
39 changes: 16 additions & 23 deletions youtility/cut.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import logging
import sys

from PyQt6.QtCore import Qt, QThread, pyqtSignal, QObject
from PyQt6.QtCore import Qt, QThread, pyqtSignal, QObject, pyqtSlot
from PyQt6.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, \
QSpacerItem, QLabel, QFileDialog
from pytube import YouTube
from qfluentwidgets import (LineEdit,
ListWidget, PushButton, MessageBox, ProgressBar, TextEdit)

logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')


class Stream(QObject):
new_text = pyqtSignal(str)
Expand All @@ -20,7 +23,6 @@ def flush(self):

class DownloaderThread(QThread):
download_finished = pyqtSignal()
on_progress = pyqtSignal(int)
new_text = pyqtSignal(str)

def __init__(self, link, start_time, end_time, save_path):
Expand All @@ -45,7 +47,6 @@ def run(self):
'verbose': True,
'download_ranges': download_range_func(None, [(start_time, end_time)]),
'force_keyframes_at_cuts': True,
'progress_hooks': [self.progress_hook],
}

# Redirect stdout and stderr
Expand Down Expand Up @@ -75,14 +76,6 @@ def show_message_box(self, title, message):
w.yesButton.setText('OK')
w.exec()

def progress_hook(self, d):
if d['status'] == 'downloading':
total_bytes = d.get('total_bytes') or d.get('total_bytes_estimate')
downloaded_bytes = d.get('downloaded_bytes')
if total_bytes and downloaded_bytes:
progress = int(downloaded_bytes / total_bytes * 100)
self.on_progress.emit(progress)

def hhmmss_to_seconds(self, hhmmss):
h, m, s = map(int, hhmmss.split(':'))
return h * 3600 + m * 60 + s
Expand Down Expand Up @@ -130,9 +123,6 @@ def __init__(self):

self.main_layout.addSpacerItem(spacer_item_medium)

self.progress_bar = ProgressBar()
self.main_layout.addWidget(self.progress_bar)

self.main_layout.addSpacerItem(spacer_item_medium)

# Console Output
Expand All @@ -151,12 +141,17 @@ def __init__(self):
self.download_button.clicked.connect(self.download)
self.button_layout.addWidget(self.download_button)

# GIF Loading Screen
self.gif_layout = QHBoxLayout()
self.main_layout.addLayout(self.gif_layout)
self.loading_label = QLabel()
self.main_layout.addWidget(self.loading_label)

self.main_layout.addSpacerItem(spacer_item_medium)
self.main_layout.addSpacerItem(spacer_item_medium)
self.main_layout.addSpacerItem(spacer_item_medium)
self.main_layout.addSpacerItem(spacer_item_medium)

disclaimer = QLabel("<b>***</b> <b><i> This feature uses YT-DLP and requires <a href='https://ffmpeg.org/download.html'>ffmpeg</a> and will take slightly longer time to render, and the quality is also NOT adjustable.</i></b>")
self.main_layout.addWidget(disclaimer)

self.count_layout = QHBoxLayout()
self.download_list_widget = ListWidget()
self.count_layout.addWidget(self.download_list_widget)
Expand All @@ -175,14 +170,12 @@ def download(self):
if save_path:
thread = DownloaderThread(link, start_time, end_time, save_path)
thread.download_finished.connect(self.show_download_finished_message)
thread.on_progress.connect(self.update_progress_bar)
thread.new_text.connect(self.append_text)
thread.start()

def update_progress_bar(self, value):
self.progress_bar.setValue(value)

@pyqtSlot(str)
def append_text(self, text):
logging.debug(f"Appending text: {text}")
self.console_output.append(text)

def init_timers(self):
Expand All @@ -197,8 +190,8 @@ def init_timers(self):
length = (video.length)
length = self.seconds_to_hhmmss(length)
self.end_time.setText(length)
except:
pass
except Exception as e:
logging.error(f"Failed to initialize timers: {e}", exc_info=True)

def hhmmss_to_seconds(self, hhmmss):
h, m, s = map(int, hhmmss.split(':'))
Expand Down
9 changes: 3 additions & 6 deletions youtility/downloader.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,6 @@ def __init__(self, link, quality, download_captions, copy_thumbnail_link, dwnld_
self.filename = filename

def run(self):
def get_gif():
gifs = ["loading.gif", "loading_2.gif"]
gif = random.choice(gifs)
gif_path = "resources/misc/" + gif
return gif_path

caption_file_path = os.path.join(self.save_path, "captions.xml")

self.loading_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
Expand Down Expand Up @@ -161,10 +155,13 @@ def __init__(self):
self.quality_menu.setPlaceholderText("Video Quality (Enter link to view)")
# self.quality_menu.addItems(["2160p", "1440p", "1080p", "720p", "480p", "360p", "240p", "144p"])
self.quality_layout.addWidget(self.quality_menu)

self.options_layout.addSpacerItem(spacer_item_medium)
self.thumbnail_url_checkbox = CheckBox('Copy Thumbnail Link', self)

self.audio_only_checkbox = CheckBox('Download Audio Only', self)
self.audio_only_checkbox.stateChanged.connect(self.update_audio_format)

self.captions_checkbox = CheckBox('Download Captions', self)
self.captions_checkbox.stateChanged.connect(self.trigger_captions_list)

Expand Down
54 changes: 39 additions & 15 deletions youtility/playlist.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import json
import os
import random
import re
import subprocess

import pytube.exceptions
from PyQt6.QtCore import Qt, QThread, pyqtSignal
Expand All @@ -9,7 +11,7 @@
QSpacerItem, QLabel, QListWidgetItem
from pytube import Playlist
from qfluentwidgets import (LineEdit,
CheckBox, ListWidget, TextEdit, PushButton)
CheckBox, ListWidget, TextEdit, PushButton, ComboBox)

with open("resources/misc/config.json", "r") as themes_file:
_themes = json.load(themes_file)
Expand All @@ -21,7 +23,7 @@ class DownloaderThread(QThread):
download_finished = pyqtSignal()

def __init__(self, link, quality, dwnld_list_widget, quality_menu,
loading_label, main_window, save_path, progress_text, mp3_only, folder_path=None,
loading_label, main_window, save_path, progress_text, mp3_only, filename, audio_format, folder_path=None,
copy_thumbnail_link=None):
super().__init__()
self.link = link
Expand All @@ -35,6 +37,8 @@ def __init__(self, link, quality, dwnld_list_widget, quality_menu,
self.progress_textbox = progress_text
self.main_window = main_window
self.mp3_only = mp3_only
self.audio_format = audio_format
self.filename = filename

def run(self):
def get_gif():
Expand Down Expand Up @@ -73,14 +77,22 @@ def get_gif():
self.progress_textbox.append(
'Downloading: {} with URL: {}'.format((video.title + " -audio"), video.watch_url))
self.progress_textbox.append("\n")

filtered_streams = video.streams.filter(only_audio=True).first()
filtered_streams.download(output_path=self.save_path, filename=(video.title + ".mp3"))
self.progress_textbox.append('Downloaded: {}'.format(video.title))

# selected_stream = filtered_streams.filter(only_audio=True).first()

filtered_streams.download(output_path=self.save_path)
if self.audio_format == "FLAC":
input_file = os.path.join(self.save_path, (video.title + ".mp3")).replace("\\", "/")
output_file = os.path.join(self.save_path, (video.title + ".flac")).replace("\\", "/")

self.progress_textbox.append('Downloaded: {}'.format(video.title))
# Run the ffmpeg command to convert mp4 to flac
ffmpeg_command = f'ffmpeg -i "{input_file}" "{output_file}"'
try:
subprocess.run(ffmpeg_command, shell=True, check=True)
os.remove(input_file)
except subprocess.CalledProcessError as e:
print(f"Error during conversion: {e}")
self.list_item.setText((title + " - Download failed during conversion"))

self.download_finished.emit()
self.list_item.setText((title + " - Downloaded"))
Expand All @@ -94,6 +106,7 @@ def __init__(self):
spacer_item_medium = QSpacerItem(0, 20)

self.setObjectName("Playlist")
self.audio_format_choice = ComboBox()

self.main_layout = QVBoxLayout()
self.main_layout.setAlignment(Qt.AlignmentFlag.AlignTop)
Expand All @@ -114,7 +127,7 @@ def __init__(self):
self.options_layout = QHBoxLayout()
self.main_layout.addLayout(self.quality_layout)
self.main_layout.addLayout(self.options_layout)
self.quality_menu = QComboBox()
self.quality_menu = ComboBox()
self.quality_menu.setPlaceholderText("Video Quality (Applies to all videos)")
if progressive == "True":
self.quality_menu.addItems(["720p", "480p", "360p", "240p", "144p"])
Expand All @@ -124,11 +137,13 @@ def __init__(self):
self.options_layout.addSpacerItem(spacer_item_medium)
self.thumbnail_url_checkbox = CheckBox('Copy Thumbnail URL', self)
self.audio_only_checkbox = CheckBox('Download Audio Only', self)
self.audio_only_checkbox.stateChanged.connect(self.audio_format_init)

self.options_group = QGroupBox("Additional Options")
self.options_group_layout = QVBoxLayout(self.options_group)
self.options_group_layout.addWidget(self.thumbnail_url_checkbox)
self.options_group_layout.addWidget(self.audio_only_checkbox)
self.options_group_layout.addSpacerItem(spacer_item_medium)
self.options_layout.addWidget(self.options_group)

self.main_layout.addSpacerItem(spacer_item_small)
Expand All @@ -146,15 +161,10 @@ def __init__(self):
self.download_button.clicked.connect(self.download)
self.button_layout.addWidget(self.download_button)

# GIF Loading Screen
self.gif_layout = QHBoxLayout()
self.main_layout.addLayout(self.gif_layout)
self.loading_label = QLabel()
self.main_layout.addWidget(self.loading_label)

# Progress Area
self.count_layout = QHBoxLayout()
# Create a QListWidget to display downloading status
self.download_list_widget = ListWidget()
self.download_list_text = TextEdit()
self.download_list_text.setReadOnly(True)
Expand All @@ -165,6 +175,16 @@ def __init__(self):
self.setLayout(self.main_layout)
self.caption_list = None

def audio_format_init(self):
if self.audio_only_checkbox.isChecked():
audio_formats = ["MP3", "FLAC"]
self.audio_format_choice = ComboBox()
self.audio_format_choice.setCurrentText(_themes["def-audio-format"])
self.audio_format_choice.addItems(audio_formats)
self.options_group_layout.addWidget(self.audio_format_choice)
else:
self.audio_format_choice.hide()

def get_quality(self):
url = self.link_entry.text()
set_progressive = True
Expand Down Expand Up @@ -198,8 +218,10 @@ def download(self):
except pytube.exceptions.RegexMatchError:
title = "Untitled"

# Open file dialog to get save path
audio_format = self.audio_format_choice.currentText()
save_path, _ = QFileDialog.getSaveFileName(self, "Save file", title)
filename = os.path.basename(save_path)
filename_without_extension, _ = os.path.splitext(filename)

self.downloader_thread = DownloaderThread(
link=link,
Expand All @@ -210,7 +232,9 @@ def download(self):
quality_menu=self.quality_menu,
main_window=self,
progress_text=self.download_list_text,
mp3_only=mp3_only
mp3_only=mp3_only,
audio_format=audio_format,
filename=filename_without_extension
)
self.downloader_thread.download_finished.connect(self.show_download_finished_message)
self.downloader_thread.start()
Expand Down
16 changes: 10 additions & 6 deletions youtility/settings.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import json

from PyQt6.QtWidgets import QWidget, QVBoxLayout, QGroupBox, QPushButton, QComboBox
from qfluentwidgets import (LineEdit, StrongBodyLabel, MessageBox, CheckBox)
from PyQt6.QtWidgets import QWidget, QVBoxLayout, QGroupBox, QPushButton, QComboBox, QSpacerItem
from qfluentwidgets import (LineEdit, StrongBodyLabel, MessageBox, CheckBox, ComboBox)

with open("resources/misc/config.json", "r") as themes_file:
_themes = json.load(themes_file)
Expand All @@ -14,6 +14,10 @@ def __init__(self):
self.initUI()

def initUI(self):

spacer_item_small = QSpacerItem(0, 10)
spacer_item_medium = QSpacerItem(0, 20)

layout = QVBoxLayout()
layout.addStretch()

Expand All @@ -36,15 +40,15 @@ def initUI(self):

def_sub_format_label = StrongBodyLabel("Default Subtitle Format: ", self)
pref_layout.addWidget(def_sub_format_label)
self.def_sub_format = QComboBox()
self.def_sub_format = ComboBox()
self.def_sub_format.addItems(["SRT", "XML"])
self.def_sub_format.setCurrentText(_themes["def_sub_format"])
pref_layout.addWidget(self.def_sub_format)

set_progressive_label = StrongBodyLabel("Allow higher res downloads (audio may be missing): ", self)
pref_layout.addWidget(set_progressive_label)
pref_layout.addSpacerItem(spacer_item_medium)

self.set_progressive = CheckBox()
self.set_progressive.setText("Allow")
self.set_progressive.setText("Allow higher res downloads (audio may be missing): ")
if _themes["progressive"] == "False":
self.set_progressive.setChecked(True)
else:
Expand Down

0 comments on commit f67d913

Please sign in to comment.