diff --git a/gui/gui.py b/gui/gui.py
index 83717e6..f7c924b 100644
--- a/gui/gui.py
+++ b/gui/gui.py
@@ -270,6 +270,10 @@ class Ui_MainWindow(object):
self.output_device_restream_box.setObjectName("output_device_restream_box")
self.verticalLayout.addWidget(self.output_device_restream_box)
self.audio_devices_settings_tab_lay.addWidget(self.restream_options_group, 0, QtCore.Qt.AlignTop)
+ self.use_original_streaming_method_check = QtWidgets.QCheckBox(self.audio_devices_settings_tab)
+ self.use_original_streaming_method_check.setChecked(True)
+ self.use_original_streaming_method_check.setObjectName("use_original_streaming_method_check")
+ self.audio_devices_settings_tab_lay.addWidget(self.use_original_streaming_method_check)
spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.audio_devices_settings_tab_lay.addItem(spacerItem)
self.settings_tabs_widget.addTab(self.audio_devices_settings_tab, "")
@@ -288,6 +292,41 @@ class Ui_MainWindow(object):
self.theme_box.addItem("")
self.theme_box.addItem("")
self.general_settings_tab_lay.addWidget(self.theme_box)
+ self.api_keys_settings_label = QtWidgets.QLabel(self.general_settings_tab)
+ self.api_keys_settings_label.setObjectName("api_keys_settings_label")
+ self.general_settings_tab_lay.addWidget(self.api_keys_settings_label)
+ self.api_keys_settings_tabs_widget = QtWidgets.QTabWidget(self.general_settings_tab)
+ self.api_keys_settings_tabs_widget.setObjectName("api_keys_settings_tabs_widget")
+ self.spotify_api_settings_tab = QtWidgets.QWidget()
+ self.spotify_api_settings_tab.setObjectName("spotify_api_settings_tab")
+ self.spotify_api_settings_tab_lay = QtWidgets.QVBoxLayout(self.spotify_api_settings_tab)
+ self.spotify_api_settings_tab_lay.setContentsMargins(0, 0, 0, 0)
+ self.spotify_api_settings_tab_lay.setObjectName("spotify_api_settings_tab_lay")
+ self.spotify_client_id_label = QtWidgets.QLabel(self.spotify_api_settings_tab)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.spotify_client_id_label.sizePolicy().hasHeightForWidth())
+ self.spotify_client_id_label.setSizePolicy(sizePolicy)
+ self.spotify_client_id_label.setMaximumSize(QtCore.QSize(16777215, 50))
+ self.spotify_client_id_label.setObjectName("spotify_client_id_label")
+ self.spotify_api_settings_tab_lay.addWidget(self.spotify_client_id_label)
+ self.spotify_client_id_box = QtWidgets.QLineEdit(self.spotify_api_settings_tab)
+ self.spotify_client_id_box.setMinimumSize(QtCore.QSize(0, 30))
+ self.spotify_client_id_box.setObjectName("spotify_client_id_box")
+ self.spotify_api_settings_tab_lay.addWidget(self.spotify_client_id_box)
+ self.spotify_client_secret_label = QtWidgets.QLabel(self.spotify_api_settings_tab)
+ self.spotify_client_secret_label.setObjectName("spotify_client_secret_label")
+ self.spotify_api_settings_tab_lay.addWidget(self.spotify_client_secret_label)
+ self.spotify_client_secret_box = QtWidgets.QLineEdit(self.spotify_api_settings_tab)
+ self.spotify_client_secret_box.setMinimumSize(QtCore.QSize(0, 30))
+ self.spotify_client_secret_box.setObjectName("spotify_client_secret_box")
+ self.spotify_api_settings_tab_lay.addWidget(self.spotify_client_secret_box)
+ self.api_keys_settings_tabs_widget.addTab(self.spotify_api_settings_tab, "")
+ self.pusher_settings_tab = QtWidgets.QWidget()
+ self.pusher_settings_tab.setObjectName("pusher_settings_tab")
+ self.api_keys_settings_tabs_widget.addTab(self.pusher_settings_tab, "")
+ self.general_settings_tab_lay.addWidget(self.api_keys_settings_tabs_widget, 0, QtCore.Qt.AlignTop)
spacerItem1 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.general_settings_tab_lay.addItem(spacerItem1)
self.clear_temp_button = QtWidgets.QPushButton(self.general_settings_tab)
@@ -391,11 +430,19 @@ class Ui_MainWindow(object):
self.restream_micro_checkbox.setText(_translate("MainWindow", "Restream microphone"))
self.input_device_restream_label.setText(_translate("MainWindow", "Input microphone"))
self.output_device_restream_label.setText(_translate("MainWindow", "Output device (virtual mic input)"))
+ self.use_original_streaming_method_check.setText(_translate("MainWindow", "Use direct stream method (cuts last second of audio, not recommended if playing short files)"))
self.settings_tabs_widget.setTabText(self.settings_tabs_widget.indexOf(self.audio_devices_settings_tab), _translate("MainWindow", "Audio"))
self.theme_label.setText(_translate("MainWindow", " App theme (restart needed)"))
self.theme_box.setItemText(0, _translate("MainWindow", "Dark gray"))
self.theme_box.setItemText(1, _translate("MainWindow", "Black"))
self.theme_box.setItemText(2, _translate("MainWindow", "Black acrylic"))
+ self.api_keys_settings_label.setText(_translate("MainWindow", "API keys settings"))
+ self.spotify_client_id_label.setText(_translate("MainWindow", "Client id"))
+ self.spotify_client_id_box.setPlaceholderText(_translate("MainWindow", "5f573c9620494bae87890c0f08a60293"))
+ self.spotify_client_secret_label.setText(_translate("MainWindow", "Client secret"))
+ self.spotify_client_secret_box.setPlaceholderText(_translate("MainWindow", "212476d9b0f3472eaa762d90b19b0ba8"))
+ self.api_keys_settings_tabs_widget.setTabText(self.api_keys_settings_tabs_widget.indexOf(self.spotify_api_settings_tab), _translate("MainWindow", "Spotify"))
+ self.api_keys_settings_tabs_widget.setTabText(self.api_keys_settings_tabs_widget.indexOf(self.pusher_settings_tab), _translate("MainWindow", "Pusher"))
self.clear_temp_button.setText(_translate("MainWindow", "Clear KotoPad temporary files (use if sound doesn\'t play correctly)"))
self.settings_tabs_widget.setTabText(self.settings_tabs_widget.indexOf(self.general_settings_tab), _translate("MainWindow", "General"))
import gui.images_rc
diff --git a/gui/gui.ui b/gui/gui.ui
index 5b15d4a..68951ae 100644
--- a/gui/gui.ui
+++ b/gui/gui.ui
@@ -850,6 +850,16 @@ QListWidget:item:selected {
+ -
+
+
+ Use direct stream method (cuts last second of audio, not recommended if playing short files)
+
+
+ true
+
+
+
-
@@ -914,6 +924,93 @@ QListWidget:item:selected {
+ -
+
+
+ API keys settings
+
+
+
+ -
+
+
+
+ Spotify
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+
+ 16777215
+ 50
+
+
+
+ Client id
+
+
+
+ -
+
+
+
+ 0
+ 30
+
+
+
+ 5f573c9620494bae87890c0f08a60293
+
+
+
+ -
+
+
+ Client secret
+
+
+
+ -
+
+
+
+ 0
+ 30
+
+
+
+ 212476d9b0f3472eaa762d90b19b0ba8
+
+
+
+
+
+
+
+ Pusher
+
+
+
+
-
diff --git a/gui/modules/handlers/register.py b/gui/modules/handlers/register.py
index a1c332d..9adb05a 100644
--- a/gui/modules/handlers/register.py
+++ b/gui/modules/handlers/register.py
@@ -12,6 +12,6 @@ from modules.restream.restream import Restreamer
def register_handlers(ui: Ui_MainWindow, MainWindow: QMainWindow, p: Player, rs: Restreamer):
menu.register_handlers(ui)
pads.register_handlers(ui, MainWindow, p)
- player.register_handlers(ui, MainWindow, p)
+ player.register_handlers(ui, p)
settings.register_handlers(ui)
restreammic.register_handlers(ui, MainWindow, rs)
diff --git a/gui/modules/initialize/fill_settings.py b/gui/modules/initialize/fill_settings.py
index 8fca869..4dc6406 100644
--- a/gui/modules/initialize/fill_settings.py
+++ b/gui/modules/initialize/fill_settings.py
@@ -1,6 +1,8 @@
from gui.gui import Ui_MainWindow
from modules.config import Config
+from modules.spotify.config import SpotifyConfig
from modules.restream import get_streaming_devices
+from modules.player.player import get_devices, get_instance, get_player
def fill_settings(ui: Ui_MainWindow):
@@ -8,7 +10,23 @@ def fill_settings(ui: Ui_MainWindow):
ui.theme_box.setCurrentText(Config.get().theme)
- # ui.output_device_play_box.addItems()
+ play_devices = get_devices(get_player(get_instance()))
+
+ ui.output_device_play_box.addItems(play_devices)
+ ui.preview_device_play_box.addItems(play_devices)
+
+ if Config.get().out_device in play_devices.keys():
+ ui.output_device_play_box.setCurrentText(Config.get().out_device)
+ else:
+ for item in play_devices.keys():
+ if '(VB-Audio Virtual Cable)' in item:
+ ui.output_device_play_box.setCurrentText(item)
+ break
+
+ if Config.get().preview_device in play_devices.keys():
+ ui.preview_device_play_box.setCurrentText(Config.get().preview_device)
+ elif 'Default' in play_devices.keys():
+ ui.preview_device_play_box.setCurrentText('Default')
ui.restream_micro_checkbox.setChecked(Config.get().restream)
ui.input_device_restream_box.addItems(get_streaming_devices().in_l)
@@ -19,3 +37,6 @@ def fill_settings(ui: Ui_MainWindow):
if Config.get().out_micro in get_streaming_devices().out_l:
ui.output_device_restream_box.setCurrentText(Config.get().out_micro)
+
+ ui.spotify_client_id_box.setText(SpotifyConfig.get().client_id)
+ ui.spotify_client_secret_box.setText(SpotifyConfig.get().client_secret)
diff --git a/gui/modules/initialize/setup_ui.py b/gui/modules/initialize/setup_ui.py
index 4ca76ed..32fd976 100644
--- a/gui/modules/initialize/setup_ui.py
+++ b/gui/modules/initialize/setup_ui.py
@@ -11,12 +11,6 @@ from modules.restream.restream import Restreamer
def on_load(ui: Ui_MainWindow, MainWindow: QMainWindow):
- """
- Setup all UI elements
- :param ui:
- :param MainWindow:
- :return:
- """
ui.content.setCurrentIndex(0)
MainWindow.setStyleSheet(styles.centralwidget())
@@ -27,11 +21,11 @@ def on_load(ui: Ui_MainWindow, MainWindow: QMainWindow):
ui.timer = QtCore.QTimer(MainWindow)
ui.timer.start(100)
- p = Player()
- rs = Restreamer()
-
fill_settings.fill_settings(ui)
+ p = Player(ui)
+ rs = Restreamer()
+
(lambda: rs.restart(ui) if ui.restream_micro_checkbox.isChecked() else rs.stop())()
register.register_handlers(ui, MainWindow, p, rs)
diff --git a/gui/modules/menu/handlers.py b/gui/modules/menu/handlers.py
index dd517ab..076681a 100644
--- a/gui/modules/menu/handlers.py
+++ b/gui/modules/menu/handlers.py
@@ -3,9 +3,4 @@ from .menu import handle_menu_click
def register_handlers(ui: Ui_MainWindow):
- """
- Register this module handlers
- :param ui:
- :return:
- """
ui.menu.itemClicked.connect(lambda: handle_menu_click(ui.menu.currentItem().text(), ui))
diff --git a/gui/modules/menu/menu.py b/gui/modules/menu/menu.py
index 0dad797..ca2b59f 100644
--- a/gui/modules/menu/menu.py
+++ b/gui/modules/menu/menu.py
@@ -3,10 +3,6 @@ from gui.gui import Ui_MainWindow
def open_menu(ui: Ui_MainWindow) -> None:
- """
- Animates the menu to open and close, using animation from config
- :return:
- """
width = ui.menu.geometry().width()
Ui_MainWindow.animation = QtCore.QPropertyAnimation(ui.menu, b"minimumWidth")
Ui_MainWindow.animation.setDuration(300)
@@ -22,12 +18,6 @@ def open_menu(ui: Ui_MainWindow) -> None:
def handle_menu_click(text: str, ui: Ui_MainWindow) -> None:
- """
- Handles the click on the menu and changes the page
- :param text:
- :param ui:
- :return:
- """
index = {
"Close menu": [lambda _: open_menu(ui), None],
"Pads": [ui.content.setCurrentWidget, ui.pads_page],
diff --git a/gui/modules/player/handlers.py b/gui/modules/player/handlers.py
index af0e9af..c08f0c1 100644
--- a/gui/modules/player/handlers.py
+++ b/gui/modules/player/handlers.py
@@ -1,19 +1,10 @@
from gui.gui import Ui_MainWindow
from PyQt5 import QtGui
-from PyQt5.QtWidgets import QMainWindow
from modules.player.player import Player
from modules.config import Config
-def register_handlers(ui: Ui_MainWindow, MainWindow: QMainWindow, p: Player):
- """
- Register this module handlers
- :param p:
- :param ui:
- :param MainWindow:
- :return:
- """
-
+def register_handlers(ui: Ui_MainWindow, p: Player):
play_icon = QtGui.QIcon()
play_icon.addPixmap(QtGui.QPixmap(":/img/img/play.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
@@ -27,3 +18,5 @@ def register_handlers(ui: Ui_MainWindow, MainWindow: QMainWindow, p: Player):
ui.play_pause_button.setIcon(play_icon) if not p.mediaplayer_out.is_playing()
else ui.play_pause_button.setIcon(pause_icon)))
ui.player_time_slider.sliderPressed.connect(lambda: p.set_position(ui.player_time_slider.value() / 100.0))
+ ui.output_device_play_box.currentTextChanged.connect(lambda: p.update_devices(ui))
+ ui.preview_device_play_box.currentTextChanged.connect(lambda: p.update_devices(ui))
diff --git a/gui/modules/settings/handlers.py b/gui/modules/settings/handlers.py
index e5cbaf4..4a59dc9 100644
--- a/gui/modules/settings/handlers.py
+++ b/gui/modules/settings/handlers.py
@@ -1,15 +1,10 @@
from gui.gui import Ui_MainWindow
from modules.config import Config
+from modules.spotify.config import SpotifyConfig
import shutil
def register_handlers(ui: Ui_MainWindow):
- """
- Register this module handlers
- :param ui:
- :return:
- """
-
ui.theme_box.currentTextChanged.connect(lambda: Config.update("theme", ui.theme_box.currentText()))
ui.restream_micro_checkbox.clicked.connect(lambda: Config.update("restream",
ui.restream_micro_checkbox.isChecked()))
@@ -21,6 +16,20 @@ def register_handlers(ui: Ui_MainWindow):
lambda: Config.update("in_micro", ui.input_device_restream_box.currentText())
)
+ ui.output_device_play_box.currentTextChanged.connect(
+ lambda: Config.update("out_device", ui.output_device_play_box.currentText())
+ )
+ ui.preview_device_play_box.currentTextChanged.connect(
+ lambda: Config.update("preview_device", ui.preview_device_play_box.currentText())
+ )
+
+ ui.spotify_client_id_box.textChanged.connect(
+ lambda: SpotifyConfig.update("client_id", ui.spotify_client_id_box.text())
+ )
+ ui.spotify_client_secret_box.textChanged.connect(
+ lambda: SpotifyConfig.update("client_secret", ui.spotify_client_secret_box.text())
+ )
+
ui.clear_temp_button.clicked.connect(
lambda: shutil.rmtree('temp', ignore_errors=True)
)
diff --git a/modules/player/add_second.py b/modules/player/add_second.py
deleted file mode 100644
index a02cc0d..0000000
--- a/modules/player/add_second.py
+++ /dev/null
@@ -1,30 +0,0 @@
-import pydub
-import validators
-from urllib.request import urlopen
-from io import BytesIO
-from rich import print
-import pafy
-import hashlib
-import os
-
-
-def get_silenced_media(original: str) -> str | None:
- if not os.path.isdir('temp'):
- os.mkdir('temp')
-
- try:
- name = original
- namehash = 'temp\\' + hashlib.md5(name.encode('utf-8')).hexdigest()
- if not os.path.isfile(namehash):
- if validators.url(original):
- if 'youtu' in original:
- original = pafy.new(original).getbestaudio().url
- original = BytesIO(urlopen(original).read())
-
- (pydub.AudioSegment.from_file(original) + pydub.AudioSegment.silent(1500))\
- .export(namehash, format='mp3')
- return namehash
-
- except Exception as e:
- print(e)
- return None
diff --git a/modules/player/convert.py b/modules/player/convert.py
new file mode 100644
index 0000000..ee7a2d1
--- /dev/null
+++ b/modules/player/convert.py
@@ -0,0 +1,42 @@
+import pydub
+import validators
+from io import BytesIO
+from rich import print
+import pafy
+import hashlib
+import os
+from modules.spotify.spotify_dl import Spotify
+from gui.modules.core.popup import popup
+import requests
+
+
+def get_raw_link(url):
+ if validators.url(url):
+ if 'spotify' in url:
+ url = Spotify().get_youtube_url(url)
+ if 'youtu' in url:
+ url = pafy.new(url).audiostreams[0].url
+ else:
+ url = None
+
+ return url
+
+
+def get_silenced_media(original: str) -> str | None:
+ if not os.path.isdir('temp'):
+ os.mkdir('temp')
+
+ try:
+ namehash = 'temp\\' + hashlib.md5(original.encode('utf-8')).hexdigest()
+ if not os.path.isfile(original):
+ if validators.url(original):
+ original = BytesIO(requests.get(get_raw_link(original)).content)
+
+ (pydub.AudioSegment.from_file(original) + pydub.AudioSegment.silent(1500))\
+ .export(namehash, format='mp3')
+ return namehash
+
+ except Exception as e:
+ raise e
+ print(e)
+ return None
diff --git a/modules/player/player.py b/modules/player/player.py
index d5b1a6d..ae025d2 100644
--- a/modules/player/player.py
+++ b/modules/player/player.py
@@ -1,7 +1,7 @@
import vlc
from gui.gui import Ui_MainWindow
from gui.modules.core import popup
-from modules.player.add_second import get_silenced_media
+from modules.player.convert import get_silenced_media
def get_instance() -> vlc.Instance:
@@ -24,14 +24,16 @@ def get_devices(mediaplayer: vlc.MediaPlayer) -> dict:
class Player(object):
- def __init__(self):
+ def __init__(self, ui: Ui_MainWindow):
self.instance_preview = get_instance()
self.instance_out = get_instance()
self.mediaplayer_preview = get_player(self.instance_preview)
- self.mediaplayer_preview.audio_output_device_set(None, get_devices(self.mediaplayer_preview)['Default'])
+ self.mediaplayer_preview.audio_output_device_set(None, get_devices(self.mediaplayer_preview)[
+ ui.preview_device_play_box.currentText()
+ ])
self.mediaplayer_out = get_player(self.instance_out)
self.mediaplayer_out.audio_output_device_set(None, get_devices(
- self.mediaplayer_out)['CABLE Input (VB-Audio Virtual Cable)'])
+ self.mediaplayer_out)[ui.output_device_play_box.currentText()])
def set_media(self, media: str) -> None:
if get_silenced_media(media):
@@ -77,5 +79,10 @@ class Player(object):
self.mediaplayer_preview.set_position(pos)
self.mediaplayer_out.set_position(pos)
- def update_devices(self):
- pass
+ def update_devices(self, ui: Ui_MainWindow):
+ self.mediaplayer_preview.audio_output_device_set(None, get_devices(self.mediaplayer_preview)[
+ ui.preview_device_play_box.currentText()
+ ])
+ self.mediaplayer_out.audio_output_device_set(None, get_devices(self.mediaplayer_out)[
+ ui.output_device_play_box.currentText()
+ ])
diff --git a/modules/spotify/__init__.py b/modules/spotify/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/modules/spotify/config.py b/modules/spotify/config.py
new file mode 100644
index 0000000..447018b
--- /dev/null
+++ b/modules/spotify/config.py
@@ -0,0 +1,51 @@
+import json
+import os
+from dataclasses import dataclass
+from dataclasses_json import dataclass_json
+
+
+@dataclass_json
+@dataclass(frozen=True)
+class SpotConfig:
+ client_id: str
+ client_secret: str
+
+
+class SpotifyConfig:
+ @staticmethod
+ def default():
+ return {
+ "client_id": "5f573c9620494bae87890c0f08a60293",
+ "client_secret": "212476d9b0f3472eaa762d90b19b0ba8"
+ }
+
+ @staticmethod
+ def fix() -> None:
+ try:
+ with open("data/config.spotify", "w") as file:
+ json.dump(SpotifyConfig.default(), file)
+ except FileNotFoundError:
+ if not os.path.exists('data'):
+ os.mkdir('data')
+ SpotifyConfig.fix()
+
+ @staticmethod
+ def get() -> SpotConfig:
+ try:
+ with open("data/config.spotify", "r") as file:
+ return SpotConfig.from_dict(json.load(file))
+ except:
+ SpotifyConfig.fix()
+ return SpotifyConfig.get()
+
+ @staticmethod
+ def update(key: str, value: str | None) -> dict:
+ with open("data/config.spotify", "r") as file:
+ settings = json.load(file)
+
+ settings[key] = value
+
+ with open("data/config.spotify", "w") as file:
+ json.dump(settings, file)
+
+ return settings
diff --git a/modules/spotify/spotify_dl.py b/modules/spotify/spotify_dl.py
new file mode 100644
index 0000000..23e14f0
--- /dev/null
+++ b/modules/spotify/spotify_dl.py
@@ -0,0 +1,57 @@
+import requests
+from urllib.parse import urlencode
+from urllib.request import urlopen
+from urllib.error import HTTPError
+import re
+from base64 import b64encode
+from modules.spotify.config import SpotifyConfig
+
+
+def reencode(text: str):
+ return b64encode(text.encode()).decode()
+
+
+class Spotify(object):
+ def __init__(self):
+ try:
+ headers = {
+ 'Authorization': f'Basic '
+ f'{reencode(f"{SpotifyConfig.get().client_id}:{SpotifyConfig.get().client_secret}")}',
+ }
+
+ data = {
+ 'grant_type': 'client_credentials'
+ }
+
+ r = requests.post('https://accounts.spotify.com/api/token', headers=headers, data=data)
+
+ self.token = r.json()['access_token']
+ except Exception as e:
+ print(e)
+
+ def get_youtube_url(self, url) -> str | None:
+ track_id = url.split('/')[-1].split('?')[0]
+ r = requests.get(f"https://api.spotify.com/v1/tracks/{track_id}",
+ headers={'Authorization': f'Bearer {self.token}'})
+
+ if r.status_code == 400 or r.status_code == 401:
+ return None
+
+ track = r.json()
+
+ song_name = track['name']
+ artists = []
+
+ for artist in track['artists']:
+ artists.append(artist['name'])
+ artist_name = ' '.join(artists)
+
+ try:
+ query_string = urlencode({'search_query': artist_name + ' ' + song_name})
+ htm_content = urlopen('http://www.youtube.com/results?' + query_string)
+ search_results = re.findall(r'/watch\?v=(.{11})', htm_content.read().decode())
+
+ return f'http://www.youtube.com/watch?v={search_results[0]}'
+
+ except HTTPError:
+ return None