diff --git a/core/__pycache__/about_dialog.cpython-39.pyc b/core/__pycache__/about_dialog.cpython-39.pyc new file mode 100644 index 0000000..85d0e96 Binary files /dev/null and b/core/__pycache__/about_dialog.cpython-39.pyc differ diff --git a/core/__pycache__/alert_dialog.cpython-39.pyc b/core/__pycache__/alert_dialog.cpython-39.pyc new file mode 100644 index 0000000..85684aa Binary files /dev/null and b/core/__pycache__/alert_dialog.cpython-39.pyc differ diff --git a/core/__pycache__/confirm_dialog.cpython-39.pyc b/core/__pycache__/confirm_dialog.cpython-39.pyc new file mode 100644 index 0000000..d7d9671 Binary files /dev/null and b/core/__pycache__/confirm_dialog.cpython-39.pyc differ diff --git a/core/__pycache__/download_manager.cpython-39.pyc b/core/__pycache__/download_manager.cpython-39.pyc new file mode 100644 index 0000000..bb96444 Binary files /dev/null and b/core/__pycache__/download_manager.cpython-39.pyc differ diff --git a/core/__pycache__/input_dialog.cpython-39.pyc b/core/__pycache__/input_dialog.cpython-39.pyc new file mode 100644 index 0000000..39388eb Binary files /dev/null and b/core/__pycache__/input_dialog.cpython-39.pyc differ diff --git a/core/__pycache__/main_window.cpython-39.pyc b/core/__pycache__/main_window.cpython-39.pyc new file mode 100644 index 0000000..bfdcf70 Binary files /dev/null and b/core/__pycache__/main_window.cpython-39.pyc differ diff --git a/core/__pycache__/mini_player_dialog.cpython-39.pyc b/core/__pycache__/mini_player_dialog.cpython-39.pyc new file mode 100644 index 0000000..79dae64 Binary files /dev/null and b/core/__pycache__/mini_player_dialog.cpython-39.pyc differ diff --git a/core/__pycache__/options_dialog.cpython-39.pyc b/core/__pycache__/options_dialog.cpython-39.pyc new file mode 100644 index 0000000..018ac32 Binary files /dev/null and b/core/__pycache__/options_dialog.cpython-39.pyc differ diff --git a/core/__pycache__/tray_icon.cpython-39.pyc b/core/__pycache__/tray_icon.cpython-39.pyc new file mode 100644 index 0000000..bb119f9 Binary files /dev/null and b/core/__pycache__/tray_icon.cpython-39.pyc differ diff --git a/core/__pycache__/web_engine_page.cpython-39.pyc b/core/__pycache__/web_engine_page.cpython-39.pyc new file mode 100644 index 0000000..748cfaf Binary files /dev/null and b/core/__pycache__/web_engine_page.cpython-39.pyc differ diff --git a/core/__pycache__/web_engine_view.cpython-39.pyc b/core/__pycache__/web_engine_view.cpython-39.pyc new file mode 100644 index 0000000..8fa140c Binary files /dev/null and b/core/__pycache__/web_engine_view.cpython-39.pyc differ diff --git a/core/about_dialog.py b/core/about_dialog.py index d54f2b5..08d68d1 100644 --- a/core/about_dialog.py +++ b/core/about_dialog.py @@ -1,4 +1,3 @@ -#about_dialog.py from PyQt5.QtWidgets import QDialog from PyQt5.QtCore import Qt from PyQt5.QtGui import QIcon @@ -31,7 +30,7 @@ def __init__( self._init_connect() def _init_content(self): - pass + self.BodyLabel_2.setText(self.version) def _init_connect(self): self.PushButton.clicked.connect(self.close) diff --git a/core/alert_dialog.py b/core/alert_dialog.py index 9f8fce2..868738e 100644 --- a/core/alert_dialog.py +++ b/core/alert_dialog.py @@ -1,4 +1,3 @@ -#alert_dialog.py from PyQt5.QtWidgets import QDialog, QSizePolicy from PyQt5.QtCore import Qt, QUrl from PyQt5.QtGui import QIcon diff --git a/core/confirm_dialog.py b/core/confirm_dialog.py index cb3c6c6..ac0014f 100644 --- a/core/confirm_dialog.py +++ b/core/confirm_dialog.py @@ -1,4 +1,3 @@ -#confirm_dialog.py from PyQt5.QtWidgets import QDialog from PyQt5.QtCore import Qt from PyQt5.QtGui import QIcon diff --git a/core/css/main.css b/core/css/main.css index 8c6a7ee..e75a81d 100644 --- a/core/css/main.css +++ b/core/css/main.css @@ -3,13 +3,13 @@ QMenuBar{ color: white; } QMenuBar::item:selected{ - background-color: rgb(65,65,65); + background-color: rgb(60,60,60); } QMenu { background-color: rgb(43,43,43); color: white; - border: 1px solid rgb(160,160,160); + border: 1px solid rgb(46,46,46); padding: 2px 2px; } QMenu::icon { @@ -22,7 +22,7 @@ QMenu::item { margin: 0px; } QMenu::item:selected { - background-color: rgb(65,65,65); + background-color: rgb(60,60,60); } QMenu::item:disabled:selected { background-color: rgb(53,53,53); @@ -33,13 +33,13 @@ QMenu::item:disabled { } QMenu::separator { height: 1px; - background-color: rgb(128,128,128); + background-color: rgb(64,64,64); margin: 3px 3px; } QToolTip { - background-color: rgb(17,18,20); + background-color: rgb(44,44,44); color: white; - border: 1px solid rgb(63,65,71); + border: 1px solid rgb(55,60,67); padding: 3px 4px; } \ No newline at end of file diff --git a/core/download_manager.py b/core/download_manager.py new file mode 100644 index 0000000..05e36a8 --- /dev/null +++ b/core/download_manager.py @@ -0,0 +1,26 @@ +from PyQt5.QtCore import pyqtSignal, QObject +from subprocess import Popen, PIPE + +class DownloadManager(QObject): + downloadFinished = pyqtSignal(str) + downloadError = pyqtSignal(str) + + def __init__(self, current_dir, settings): + super().__init__() + self.current_dir = current_dir + self.settings = settings + + def download_external_process(self, url, download_path, download_type): + script_dir = self.settings.value("current_dir") + process = Popen( + [f'{self.current_dir}/core/download_script.exe', + url, download_path, download_type, script_dir], + stdout=PIPE, + stderr=PIPE + ) + output, error = process.communicate() + + if process.returncode == 0: + self.downloadFinished.emit(download_path) + else: + self.downloadError.emit(str(error)) \ No newline at end of file diff --git a/core/download_script.exe b/core/download_script.exe index 3e6068b..519ecd8 100644 Binary files a/core/download_script.exe and b/core/download_script.exe differ diff --git a/core/input_dialog.py b/core/input_dialog.py index a0b209a..c0120dd 100644 --- a/core/input_dialog.py +++ b/core/input_dialog.py @@ -1,4 +1,3 @@ -#input_dialog.py from PyQt5.QtWidgets import QDialog from PyQt5.QtCore import Qt from PyQt5.QtGui import QIcon diff --git a/core/js/get_play_pause_state.js b/core/js/get_play_pause_state.js new file mode 100644 index 0000000..6e1e81b --- /dev/null +++ b/core/js/get_play_pause_state.js @@ -0,0 +1 @@ +var video = document.getElementsByTagName('video')[0]; video.paused; \ No newline at end of file diff --git a/core/js/get_track_image.js b/core/js/get_track_image.js new file mode 100644 index 0000000..a5963e3 --- /dev/null +++ b/core/js/get_track_image.js @@ -0,0 +1 @@ +document.querySelector(".image.style-scope.ytmusic-player-bar").getAttribute("src") \ No newline at end of file diff --git a/core/js/next_track.js b/core/js/next_track.js new file mode 100644 index 0000000..18ca2ed --- /dev/null +++ b/core/js/next_track.js @@ -0,0 +1 @@ +document.querySelector(".next-button").click(); \ No newline at end of file diff --git a/core/js/play_pause_track.js b/core/js/play_pause_track.js new file mode 100644 index 0000000..ab8488b --- /dev/null +++ b/core/js/play_pause_track.js @@ -0,0 +1,2 @@ +var video = document.getElementsByTagName('video')[0]; +if (video.paused) video.play(); else video.pause(); \ No newline at end of file diff --git a/core/js/previous_track.js b/core/js/previous_track.js new file mode 100644 index 0000000..b2ee3dc --- /dev/null +++ b/core/js/previous_track.js @@ -0,0 +1 @@ +document.querySelector(".previous-button").click(); \ No newline at end of file diff --git a/core/main_window.py b/core/main_window.py index e874d76..f77f13a 100644 --- a/core/main_window.py +++ b/core/main_window.py @@ -1,24 +1,16 @@ -#main_window.py from PyQt5.QtWidgets import ( QMainWindow, QApplication, QFileDialog, QShortcut ) -from PyQt5.QtGui import ( - QIcon, QKeySequence -) -from PyQt5.QtCore import ( - QSize, Qt, QUrl, pyqtSignal, QObject -) -from PyQt5.QtWebEngineWidgets import ( - QWebEngineView, QWebEnginePage, QWebEngineSettings -) +from PyQt5.QtGui import QIcon, QKeySequence +from PyQt5.QtCore import QSize, Qt, QUrl from PyQt5.QtWinExtras import ( QWinThumbnailToolBar, QWinThumbnailToolButton ) +from PyQt5.QtWebEngineWidgets import QWebEngineSettings from PyQt5 import uic from qfluentwidgets import ( - InfoBar, InfoBarPosition, RoundMenu, Action, - MenuAnimationType, IndeterminateProgressRing, + InfoBar, InfoBarPosition, IndeterminateProgressRing, setTheme, setThemeColor, Theme, SplashScreen, PushButton, ToolTipFilter, ToolTipPosition ) @@ -27,196 +19,15 @@ import sys import requests import webbrowser -import subprocess from threading import Thread -from pytube import YouTube +from core.web_engine_page import WebEnginePage +from core.web_engine_view import WebEngineView +from core.download_manager import DownloadManager from core.options_dialog import OptionsDlg from core.about_dialog import AboutDlg from core.mini_player_dialog import MiniPlayerDlg from core.tray_icon import TrayIcon -from core.alert_dialog import AlertDlg -from core.confirm_dialog import ConfirmDlg -from core.input_dialog import InputDlg - -class WebEnginePage(QWebEnginePage): - def acceptNavigationRequest(self, url, _type, isMainFrame): - if "music.youtube.com" not in url.toString() and "google.com" not in url.toString() and "googlesyndication.com" not in url.toString(): - webbrowser.open_new_tab(url.toString()) - return False - - return QWebEnginePage.acceptNavigationRequest(self, url, _type, isMainFrame) - - def javaScriptAlert(self, qurl, text): - dialog = AlertDlg( - self.parent().name, - self.parent().current_dir, - self.view() - ) - dialog.setText(text) - reply = dialog.exec_() - - def javaScriptConfirm(self, qurl, text): - dialog = ConfirmDlg( - self.parent().name, - self.parent().current_dir, - self.view() - ) - dialog.setText(text) - reply = dialog.exec_() - return reply == True - - def javaScriptPrompt(self, qurl, text, text_value): - dialog = InputDlg( - self.parent().name, - self.parent().current_dir, - self.view() - ) - dialog.setText(text) - dialog.setTextValue(text_value) - if dialog.exec_(): - return (True, dialog.textValue()) - else: - return (False, "") - -class WebEngineView(QWebEngineView): - def contextMenuEvent(self, event): - url = self.window().webview.url().toString() - menu = RoundMenu(parent=self.window()) - - go_back_action = Action("Back", shortcut="Left") - go_back_action.setIcon( - QIcon(f"{self.window().current_dir}/resources/icons/arrow_back_white_24dp.svg") - ) - go_back_action.triggered.connect( - self.window().go_back - ) - menu.addAction(go_back_action) - - go_forward_action = Action("Forward", shortcut="Right") - go_forward_action.setIcon( - QIcon(f"{self.window().current_dir}/resources/icons/arrow_forward_white_24dp.svg") - ) - go_forward_action.triggered.connect( - self.window().go_forward - ) - menu.addAction(go_forward_action) - - go_home_action = Action("Home", shortcut="Ctrl+H") - go_home_action.setIcon( - QIcon(f"{self.window().current_dir}/resources/icons/home_white_24dp.svg") - ) - go_home_action.triggered.connect( - self.window().go_home - ) - menu.addAction(go_home_action) - - go_reload_action = Action("Reload", shortcut="Ctrl+R") - go_reload_action.setIcon( - QIcon(f"{self.window().current_dir}/resources/icons/refresh_white_24dp.svg") - ) - go_reload_action.triggered.connect( - self.window().go_reload - ) - menu.addAction(go_reload_action) - - menu.addSeparator() - - download_menu = RoundMenu("Download...", self) - download_menu.setIcon( - QIcon(f"{self.window().current_dir}/resources/icons/file_download_white_24dp.svg") - ) - menu.addMenu(download_menu) - - download_track_action = Action('track', shortcut="Ctrl+D") - download_track_action.triggered.connect( - lambda: self.window().go_download('track') - ) - download_menu.addAction(download_track_action) - - download_playlist_action = Action('playlist', shortcut="Ctrl+P") - download_playlist_action.triggered.connect( - lambda: self.window().go_download('playlist') - ) - download_menu.addAction(download_playlist_action) - - open_mini_player_action = Action("Mini-Player", shortcut="Ctrl+M") - open_mini_player_action.setIcon( - QIcon(f"{self.window().current_dir}/resources/icons/picture_in_picture_white_24dp.svg") - ) - open_mini_player_action.triggered.connect( - self.window().open_mini_player - ) - menu.addAction(open_mini_player_action) - - menu.addSeparator() - - open_settings_action = Action("Settings", shortcut="Ctrl+S") - open_settings_action.setIcon( - QIcon(f"{self.window().current_dir}/resources/icons/settings_white_24dp.svg") - ) - open_settings_action.triggered.connect( - self.window().open_settings_dialog - ) - menu.addAction(open_settings_action) - - menu.addSeparator() - - bug_report_action = Action("Bug Report") - bug_report_action.setIcon( - QIcon(f"{self.window().current_dir}/resources/icons/bug_report_white_24dp.svg") - ) - bug_report_action.triggered.connect(lambda: - webbrowser.open_new_tab( - "https://github.com/deeffest/Youtube-Music-Desktop-Player/issues/new/choose" - ) - ) - menu.addAction(bug_report_action) - - open_about_action = Action("About...") - open_about_action.setIcon( - QIcon(f"{self.window().current_dir}/resources/icons/info_white_24dp.svg") - ) - open_about_action.triggered.connect( - self.window().open_about_dialog - ) - menu.addAction(open_about_action) - - if not "watch" in url: - download_track_action.setEnabled(False) - open_mini_player_action.setEnabled(False) - if not "playlist" in url: - download_playlist_action.setEnabled(False) - - if not self.page().history().canGoForward(): - go_forward_action.setEnabled(False) - if not self.page().history().canGoBack(): - go_back_action.setEnabled(False) - - menu.exec(event.globalPos(), aniType=MenuAnimationType.DROP_DOWN) - -class DownloadManager(QObject): - downloadFinished = pyqtSignal(str) - downloadError = pyqtSignal(str) - - def __init__(self, current_dir, settings): - super().__init__() - self.current_dir = current_dir - self.settings = settings - - def download_external_process(self, url, download_path, download_type): - script_dir = self.settings.value("current_dir") - process = subprocess.Popen( - [f'{self.current_dir}/core/download_script.exe', url, download_path, download_type, script_dir], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE - ) - output, error = process.communicate() - - if process.returncode == 0: - self.downloadFinished.emit(download_path) - else: - self.downloadError.emit(error.decode('utf-8')) class Window(QMainWindow): def __init__( @@ -487,6 +298,8 @@ def on_load_progress(self, progress): with open(f"{self.current_dir}/core/js/add_styles.js", "r") as js_file: self.webview.page().runJavaScript(js_file.read()) + + self.update_play_pause_icon() else: self.ProgressBar.setValue(progress) if self.ProgressBar.isHidden(): @@ -537,43 +350,49 @@ def create_win_toolbar_buttons(self): self.win_toolbar.addButton(self.tool_btn_next) def previous_track(self): - self.webview.page().runJavaScript( - 'document.querySelector(".previous-button").click();' - ) + with open(f"{self.current_dir}/core/js/previous_track.js", "r") as js_file: + self.webview.page().runJavaScript(js_file.read()) def next_track(self): - self.webview.page().runJavaScript( - 'document.querySelector(".next-button").click();' - ) + with open(f"{self.current_dir}/core/js/next_track.js", "r") as js_file: + self.webview.page().runJavaScript(js_file.read()) def play_pause_track(self): - script = "var video = document.getElementsByTagName('video')[0];" \ - "if (video.paused) video.play(); else video.pause();" - self.webview.page().runJavaScript(script) - + with open(f"{self.current_dir}/core/js/play_pause_track.js", "r") as js_file: + self.webview.page().runJavaScript(js_file.read()) self.update_play_pause_icon() def update_play_pause_icon(self): - self.webview.page().runJavaScript( - "var video = document.getElementsByTagName('video')[0]; video.paused;", - self.set_play_pause_icon - ) - + with open(f"{self.current_dir}/core/js/get_play_pause_state.js", "r") as js_file: + self.webview.page().runJavaScript(js_file.read(), self.set_play_pause_icon) + def set_play_pause_icon(self, is_paused): - if is_paused: - self.tool_btn_play_pause.setIcon(QIcon( - f"{self.current_dir}/resources/icons/win_toolbar_icons/play_arrow_white_24dp.svg" - )) - self.tray_icon.play_pause_action.setIcon(QIcon( - f"{self.current_dir}/resources/icons/play_arrow_white_24dp.svg" - )) + if is_paused: + if "watch" in self.webview.url().toString(): + self.tool_btn_play_pause.setIcon(QIcon( + f"{self.current_dir}/resources/icons/win_toolbar_icons/play_arrow_white_24dp.svg" + )) + self.tray_icon.play_pause_action.setIcon(QIcon( + f"{self.current_dir}/resources/icons/play_arrow_white_24dp.svg" + )) + else: + self.tool_btn_play_pause.setIcon(QIcon( + f"{self.current_dir}/resources/icons/disabled_win_toolbar_icons/play_arrow_white_24dp.svg" + )) + self.tray_icon.play_pause_action.setEnabled(False) else: - self.tool_btn_play_pause.setIcon(QIcon( - f"{self.current_dir}/resources/icons/win_toolbar_icons/pause_white_24dp.svg" - )) - self.tray_icon.play_pause_action.setIcon(QIcon( - f"{self.current_dir}/resources/icons/pause_white_24dp.svg" - )) + if "watch" in self.webview.url().toString(): + self.tool_btn_play_pause.setIcon(QIcon( + f"{self.current_dir}/resources/icons/win_toolbar_icons/pause_white_24dp.svg" + )) + self.tray_icon.play_pause_action.setIcon(QIcon( + f"{self.current_dir}/resources/icons/pause_white_24dp.svg" + )) + else: + self.tool_btn_play_pause.setIcon(QIcon( + f"{self.current_dir}/resources/icons/disabled_win_toolbar_icons/pause_white_24dp.svg" + )) + self.tray_icon.play_pause_action.setEnabled(False) def _init_window(self): self.setWindowTitle(self.name) @@ -614,13 +433,41 @@ def update_url(self, url): if "watch" in url.toString(): self.tool_btn_previous.setEnabled(True) - self.tool_btn_next.setEnabled(True) + self.tool_btn_previous.setIcon(QIcon( + f"{self.current_dir}/resources/icons/win_toolbar_icons/skip_previous_white_24dp.svg" + )) + self.tray_icon.previous_track_action.setEnabled(True) + self.tool_btn_play_pause.setEnabled(True) + self.tool_btn_play_pause.setIcon(QIcon( + f"{self.current_dir}/resources/icons/win_toolbar_icons/pause_white_24dp.svg" + )) + self.tray_icon.play_pause_action.setEnabled(True) + + self.tool_btn_next.setEnabled(True) + self.tool_btn_next.setIcon(QIcon( + f"{self.current_dir}/resources/icons/win_toolbar_icons/skip_next_white_24dp.svg" + )) + self.tray_icon.next_track_action.setEnabled(True) else: self.tool_btn_previous.setEnabled(False) - self.tool_btn_next.setEnabled(False) + self.tool_btn_previous.setIcon(QIcon( + f"{self.current_dir}/resources/icons/disabled_win_toolbar_icons/skip_previous_white_24dp.svg" + )) + self.tray_icon.previous_track_action.setEnabled(False) + self.tool_btn_play_pause.setEnabled(False) - + self.tool_btn_play_pause.setIcon(QIcon( + f"{self.current_dir}/resources/icons/disabled_win_toolbar_icons/pause_white_24dp.svg" + )) + self.tray_icon.play_pause_action.setEnabled(False) + + self.tool_btn_next.setEnabled(False) + self.tool_btn_next.setIcon(QIcon( + f"{self.current_dir}/resources/icons/disabled_win_toolbar_icons/skip_next_white_24dp.svg" + )) + self.tray_icon.next_track_action.setEnabled(False) + def check_for_updates(self, startup=None): response = requests.get( "https://api.github.com/repos/deeffest/Youtube-Music-Desktop-Player/releases/latest" @@ -684,7 +531,7 @@ def resizeEvent(self, event): self.settings.setValue("last_window_size", event.size()) self.label.setMaximumWidth(self.width() * 0.8) - def closeInTray(self): + def close_in_tray(self): sys.exit(0) def closeEvent(self, event): diff --git a/core/mini_player_dialog.py b/core/mini_player_dialog.py index a4b59fb..fd24508 100644 --- a/core/mini_player_dialog.py +++ b/core/mini_player_dialog.py @@ -42,6 +42,7 @@ def __init__( ) self._init_window() + self._init_attributes() self._init_content() self._init_connect() @@ -49,6 +50,7 @@ def __init__( def _init_content(self): self.change_info(self.window.webview.url()) + self.update_play_pause_icon() self.ToolButton.setIcon( QIcon(f"{self.current_dir}/resources/icons/close_white_24dp.svg") @@ -70,27 +72,25 @@ def _init_connect(self): self.ToolButton.clicked.connect(self.close) self.ToolButton_2.clicked.connect(lambda: self.showMinimized()) self.TransparentToolButton_2.clicked.connect(self.play_pause_track) - self.TransparentToolButton.clicked.connect(self.previous_track) - self.TransparentToolButton_3.clicked.connect(self.next_track) - self.window.webview.urlChanged.connect(self.update_info) - self.window.webview.titleChanged.connect(self.change_title) + self.TransparentToolButton.clicked.connect(self.window.previous_track) + self.TransparentToolButton_3.clicked.connect(self.window.next_track) + self.window.webview.titleChanged.connect(self.title_changed) self.PillToolButton.clicked.connect(self.toggle_on_top_hint) - self.tool_btn_previous.clicked.connect(self.previous_track) - self.tool_btn_next.clicked.connect(self.next_track) + self.tool_btn_previous.clicked.connect(self.window.previous_track) + self.tool_btn_next.clicked.connect(self.window.next_track) self.tool_btn_play_pause.clicked.connect(self.play_pause_track) - def play_pause_track(self): - script = "var video = document.getElementsByTagName('video')[0];" \ - "if (video.paused) video.play(); else video.pause();" - self.window.webview.page().runJavaScript(script) + def _init_attributes(self): + self.previous_url = self.window.webview.url() + def play_pause_track(self): + with open(f"{self.current_dir}/core/js/play_pause_track.js", "r") as js_file: + self.window.webview.page().runJavaScript(js_file.read()) self.update_play_pause_icon() def update_play_pause_icon(self): - self.window.webview.page().runJavaScript( - "var video = document.getElementsByTagName('video')[0]; video.paused;", - self.set_play_pause_icon - ) + with open(f"{self.current_dir}/core/js/get_play_pause_state.js", "r") as js_file: + self.window.webview.page().runJavaScript(js_file.read(), self.set_play_pause_icon) def set_play_pause_icon(self, is_paused): if is_paused: @@ -109,10 +109,8 @@ def set_play_pause_icon(self, is_paused): )) def set_track_image(self): - self.window.webview.page().runJavaScript( - 'document.querySelector(".image.style-scope.ytmusic-player-bar").getAttribute("src")', - self.displayImage - ) + with open(f"{self.current_dir}/core/js/get_track_image.js", "r") as js_file: + self.window.webview.page().runJavaScript(js_file.read(), self.displayImage) def displayImage(self, url: str) -> None: if url: @@ -126,16 +124,6 @@ def displayImage(self, url: str) -> None: except Exception as e: print(e) - def previous_track(self): - self.window.webview.page().runJavaScript( - 'document.querySelector(".previous-button").click();' - ) - - def next_track(self): - self.window.webview.page().runJavaScript( - 'document.querySelector(".next-button").click();' - ) - def mouseMoveEvent(self, event: QMouseEvent): if event.buttons() == Qt.LeftButton and self.drag_position: if not self.isDragging: @@ -196,11 +184,13 @@ def update_info(self, url): def change_info(self, url): self.set_track_image() - self.update_play_pause_icon() self.update_info(url) - def change_title(self, title): - self.set_track_image() + def title_changed(self, title): + if self.window.webview.url() != self.previous_url: + self.change_info(self.window.webview.url()) + self.previous_url = self.window.webview.url() + self.update_play_pause_icon() self.setWindowTitle(title) diff --git a/core/options_dialog.py b/core/options_dialog.py index d26063f..99dc3b0 100644 --- a/core/options_dialog.py +++ b/core/options_dialog.py @@ -1,4 +1,3 @@ -#options_dialog.py from PyQt5.QtWidgets import QDialog from PyQt5.QtCore import Qt from PyQt5.QtGui import QIcon diff --git a/core/tray_icon.py b/core/tray_icon.py index 442595b..50f55fe 100644 --- a/core/tray_icon.py +++ b/core/tray_icon.py @@ -58,7 +58,7 @@ def _init_content(self): menu.addSeparator() - self.exit_action = Action('Exit', triggered=self.window.closeInTray) + self.exit_action = Action('Exit', triggered=self.window.close_in_tray) self.exit_action.setIcon(QIcon( f"{self.current_dir}/resources/icons/close_white_24dp.svg")) menu.addAction(self.exit_action) diff --git a/core/ui/about_dialog.ui b/core/ui/about_dialog.ui index ecfb435..db7f850 100644 --- a/core/ui/about_dialog.ui +++ b/core/ui/about_dialog.ui @@ -6,8 +6,8 @@ 0 0 - 480 - 296 + 431 + 306 @@ -81,11 +81,55 @@ - - - <html><head/><body><p><span style=" font-size:18pt;">Youtube Music Desktop Player </span><span style=" font-size:14pt; color:#d3d3d3;">v1.1.1</span></p></body></html> + + + 0 - + + + + <html><head/><body><p><span style=" font-size:18pt;">Youtube Music Desktop Player</span></p></body></html> + + + + + + + 6 + + + + + Current app version: + + + + + + + + + + 1.1.2 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + @@ -102,13 +146,13 @@ 0 - 100 + 110 16777215 - 100 + 110 @@ -120,6 +164,9 @@ <html><head/><body><p><span style=" color:#d3d3d3;">Displays Youtube Music site using QWebEngine to make your music listening experience even more convenient.</span></p><p><span style=" font-size:11pt; color:#d3d3d3;">Created with </span><a href="https://www.riverbankcomputing.com/software/pyqt/intro"><span style=" text-decoration: underline; color:#d3d3d3;">PyQt5</span></a><span style=" font-size:11pt; color:#d3d3d3;"> + </span><a href="https://github.com/zhiyiYo/PyQt-Fluent-Widgets"><span style=" text-decoration: underline; color:#d3d3d3;">PyQt-Fluent-Widgets</span></a><span style=" font-size:11pt; color:#d3d3d3;"> and distributed under the </span><a href="https://www.gnu.org/licenses/gpl-3.0.html"><span style=" text-decoration: underline; color:#d3d3d3;">GNU GPL v3</span></a><span style=" font-size:11pt; color:#d3d3d3;"> license, source code is available on </span><a href="https://github.com/deeffest/Youtube-Music-Desktop-Player"><span style=" text-decoration: underline; color:#d3d3d3;">GitHub</span></a><span style=" font-size:11pt; color:#d3d3d3;">. </span></p></body></html> + + false + true diff --git a/core/web_engine_page.py b/core/web_engine_page.py new file mode 100644 index 0000000..879db74 --- /dev/null +++ b/core/web_engine_page.py @@ -0,0 +1,50 @@ +from PyQt5.QtCore import Qt +from PyQt5.QtWebEngineWidgets import ( + QWebEnginePage +) + +from core.alert_dialog import AlertDlg +from core.confirm_dialog import ConfirmDlg +from core.input_dialog import InputDlg + +class WebEnginePage(QWebEnginePage): + def acceptNavigationRequest(self, url, _type, isMainFrame): + if ("music.youtube.com" not in url.toString() and + "google.com" not in url.toString() and + "googlesyndication.com" not in url.toString()): + webbrowser.open_new_tab(url.toString()) + return False + + return QWebEnginePage.acceptNavigationRequest(self, url, _type, isMainFrame) + + def javaScriptAlert(self, qurl, text): + dialog = AlertDlg( + self.parent().name, + self.parent().current_dir, + self.view() + ) + dialog.setText(text) + reply = dialog.exec_() + + def javaScriptConfirm(self, qurl, text): + dialog = ConfirmDlg( + self.parent().name, + self.parent().current_dir, + self.view() + ) + dialog.setText(text) + reply = dialog.exec_() + return reply == True + + def javaScriptPrompt(self, qurl, text, text_value): + dialog = InputDlg( + self.parent().name, + self.parent().current_dir, + self.view() + ) + dialog.setText(text) + dialog.setTextValue(text_value) + if dialog.exec_(): + return (True, dialog.textValue()) + else: + return (False, "") \ No newline at end of file diff --git a/core/web_engine_view.py b/core/web_engine_view.py new file mode 100644 index 0000000..45f6fc4 --- /dev/null +++ b/core/web_engine_view.py @@ -0,0 +1,131 @@ +from PyQt5.QtCore import Qt +from PyQt5.QtGui import QIcon +from PyQt5.QtWebEngineWidgets import QWebEngineView + +from qfluentwidgets import ( + RoundMenu, Action, MenuAnimationType +) + +import webbrowser + +class WebEngineView(QWebEngineView): + def contextMenuEvent(self, event): + url = self.window().webview.url().toString() + menu = RoundMenu(parent=self.window()) + + go_back_action = Action("Back", shortcut="Left") + go_back_action.setIcon( + QIcon(f"{self.window().current_dir}/resources/icons/arrow_back_white_24dp.svg") + ) + go_back_action.triggered.connect( + self.window().go_back + ) + menu.addAction(go_back_action) + + go_forward_action = Action("Forward", shortcut="Right") + go_forward_action.setIcon( + QIcon(f"{self.window().current_dir}/resources/icons/arrow_forward_white_24dp.svg") + ) + go_forward_action.triggered.connect( + self.window().go_forward + ) + menu.addAction(go_forward_action) + + go_home_action = Action("Home", shortcut="Ctrl+H") + go_home_action.setIcon( + QIcon(f"{self.window().current_dir}/resources/icons/home_white_24dp.svg") + ) + go_home_action.triggered.connect( + self.window().go_home + ) + menu.addAction(go_home_action) + + go_reload_action = Action("Reload", shortcut="Ctrl+R") + go_reload_action.setIcon( + QIcon(f"{self.window().current_dir}/resources/icons/refresh_white_24dp.svg") + ) + go_reload_action.triggered.connect( + self.window().go_reload + ) + menu.addAction(go_reload_action) + + menu.addSeparator() + + download_menu = RoundMenu("Download...", self) + download_menu.setIcon( + QIcon(f"{self.window().current_dir}/resources/icons/file_download_white_24dp.svg") + ) + menu.addMenu(download_menu) + + download_track_action = Action('Track', shortcut="Ctrl+D") + download_track_action.triggered.connect( + lambda: self.window().go_download('track') + ) + download_track_action.setIcon( + QIcon(f"{self.window().current_dir}/resources/icons/audiotrack_white_24dp.svg") + ) + download_menu.addAction(download_track_action) + + download_playlist_action = Action('Playlist', shortcut="Ctrl+P") + download_playlist_action.triggered.connect( + lambda: self.window().go_download('playlist') + ) + download_playlist_action.setIcon( + QIcon(f"{self.window().current_dir}/resources/icons/playlist_play_white_24dp.svg") + ) + download_menu.addAction(download_playlist_action) + + open_mini_player_action = Action("Mini-Player", shortcut="Ctrl+M") + open_mini_player_action.setIcon( + QIcon(f"{self.window().current_dir}/resources/icons/picture_in_picture_white_24dp.svg") + ) + open_mini_player_action.triggered.connect( + self.window().open_mini_player + ) + menu.addAction(open_mini_player_action) + + menu.addSeparator() + + open_settings_action = Action("Settings", shortcut="Ctrl+S") + open_settings_action.setIcon( + QIcon(f"{self.window().current_dir}/resources/icons/settings_white_24dp.svg") + ) + open_settings_action.triggered.connect( + self.window().open_settings_dialog + ) + menu.addAction(open_settings_action) + + menu.addSeparator() + + bug_report_action = Action("Bug Report") + bug_report_action.setIcon( + QIcon(f"{self.window().current_dir}/resources/icons/bug_report_white_24dp.svg") + ) + bug_report_action.triggered.connect(lambda: + webbrowser.open_new_tab( + "https://github.com/deeffest/Youtube-Music-Desktop-Player/issues/new/choose" + ) + ) + menu.addAction(bug_report_action) + + open_about_action = Action("About...") + open_about_action.setIcon( + QIcon(f"{self.window().current_dir}/resources/icons/info_white_24dp.svg") + ) + open_about_action.triggered.connect( + self.window().open_about_dialog + ) + menu.addAction(open_about_action) + + if not "watch" in url: + download_track_action.setEnabled(False) + open_mini_player_action.setEnabled(False) + if not "playlist" in url: + download_playlist_action.setEnabled(False) + + if not self.page().history().canGoForward(): + go_forward_action.setEnabled(False) + if not self.page().history().canGoBack(): + go_back_action.setEnabled(False) + + menu.exec(event.globalPos(), aniType=MenuAnimationType.DROP_DOWN) \ No newline at end of file diff --git a/main.py b/main.py index 2a719c2..249c202 100644 --- a/main.py +++ b/main.py @@ -9,7 +9,7 @@ from core.main_window import Window name = "Youtube Music Desktop Player" -version = "1.1.1" +version = "1.1.2" current_dir = os.path.dirname(os.path.abspath(__file__)) username = getpass.getuser() diff --git a/notes.txt b/notes.txt index e3f41b6..0ae9ca1 100644 --- a/notes.txt +++ b/notes.txt @@ -1,4 +1,4 @@ -1.1.1 Update! +1.1.2 Update! -- Fixed a bug where the loading indicator would not disappear when loading existing music. +- Fixed installer size too large. - Other changes and improvements. \ No newline at end of file diff --git a/requierements.txt b/requierements.txt new file mode 100644 index 0000000..5b076fa --- /dev/null +++ b/requierements.txt @@ -0,0 +1,99 @@ +aiohttp==3.9.3 +aiosignal==1.3.1 +altair==5.2.0 +altgraph==0.17.4 +async-timeout==4.0.3 +attrs==23.2.0 +auto-py-to-exe==2.37.0 +beautifulsoup4==4.12.3 +blinker==1.7.0 +bottle==0.12.25 +bottle-websocket==0.2.9 +Brotli==1.1.0 +cachetools==5.3.2 +certifi==2024.2.2 +cffi==1.16.0 +charset-normalizer==3.3.2 +click==8.1.7 +colorama==0.4.6 +colorthief==0.2.1 +darkdetect==0.8.0 +discord==2.3.2 +discord.py==2.3.2 +Eel==0.16.0 +ffmpeg-python==0.2.0 +Flask==3.0.2 +frozenlist==1.4.1 +future==1.0.0 +gevent==24.2.1 +gevent-websocket==0.10.1 +gitdb==4.0.11 +GitPython==3.1.42 +greenlet==3.0.3 +idna==3.6 +importlib-metadata==7.0.1 +itsdangerous==2.1.2 +Jinja2==3.1.3 +jsonschema==4.21.1 +jsonschema-specifications==2023.12.1 +markdown-it-py==3.0.0 +MarkupSafe==2.1.5 +mdurl==0.1.2 +multidict==6.0.5 +mutagen==1.47.0 +numpy==1.26.4 +packaging==23.2 +pandas==2.2.1 +pefile==2023.2.7 +pillow==10.2.0 +protobuf==4.25.3 +psutil==5.9.8 +py-cpuinfo==9.0.0 +pyarrow==15.0.0 +pycparser==2.21 +pycryptodomex==3.20.0 +pydeck==0.8.1b0 +pygame==2.5.2 +Pygments==2.17.2 +pyinstaller==5.13.2 +pyinstaller-hooks-contrib==2024.1 +pyparsing==3.1.1 +PyQt-Fluent-Widgets==1.5.1 +PyQt5==5.15.10 +PyQt5-Frameless-Window==0.3.8 +PyQt5-Qt5==5.15.2 +PyQt5-sip==12.13.0 +PyQtWebEngine==5.15.6 +PyQtWebEngine-Qt5==5.15.2 +python-dateutil==2.8.2 +pytube==15.0.0 +pytz==2024.1 +pywin32==306 +pywin32-ctypes==0.2.2 +referencing==0.33.0 +requests==2.31.0 +rich==13.7.0 +rpds-py==0.18.0 +scipy==1.12.0 +six==1.16.0 +smmap==5.0.1 +soupsieve==2.5 +tenacity==8.2.3 +tk==0.1.0 +toml==0.10.2 +toolz==0.12.1 +tornado==6.4 +typing_extensions==4.10.0 +tzdata==2024.1 +tzlocal==5.2 +urllib3==2.2.1 +validators==0.22.0 +watchdog==4.0.0 +websockets==12.0 +Werkzeug==3.0.1 +whichcraft==0.6.1 +yarl==1.9.4 +yt-dlp==2023.12.30 +zipp==3.17.0 +zope.event==5.0 +zope.interface==6.2 \ No newline at end of file diff --git a/resources/icons/audiotrack_white_24dp.svg b/resources/icons/audiotrack_white_24dp.svg new file mode 100644 index 0000000..466585c --- /dev/null +++ b/resources/icons/audiotrack_white_24dp.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/resources/icons/disabled_win_toolbar_icons/pause_white_24dp.svg b/resources/icons/disabled_win_toolbar_icons/pause_white_24dp.svg new file mode 100644 index 0000000..3ff8cb9 --- /dev/null +++ b/resources/icons/disabled_win_toolbar_icons/pause_white_24dp.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/resources/icons/disabled_win_toolbar_icons/play_arrow_white_24dp.svg b/resources/icons/disabled_win_toolbar_icons/play_arrow_white_24dp.svg new file mode 100644 index 0000000..7a76990 --- /dev/null +++ b/resources/icons/disabled_win_toolbar_icons/play_arrow_white_24dp.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/resources/icons/disabled_win_toolbar_icons/skip_next_white_24dp.svg b/resources/icons/disabled_win_toolbar_icons/skip_next_white_24dp.svg new file mode 100644 index 0000000..0449319 --- /dev/null +++ b/resources/icons/disabled_win_toolbar_icons/skip_next_white_24dp.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/resources/icons/disabled_win_toolbar_icons/skip_previous_white_24dp.svg b/resources/icons/disabled_win_toolbar_icons/skip_previous_white_24dp.svg new file mode 100644 index 0000000..20f32a7 --- /dev/null +++ b/resources/icons/disabled_win_toolbar_icons/skip_previous_white_24dp.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/resources/icons/playlist_play_white_24dp.svg b/resources/icons/playlist_play_white_24dp.svg new file mode 100644 index 0000000..8d51382 --- /dev/null +++ b/resources/icons/playlist_play_white_24dp.svg @@ -0,0 +1 @@ + \ No newline at end of file