mirror of
https://gitlab.com/RemixDev/deemix-gui-pyweb.git
synced 2025-01-15 11:05:16 +00:00
Merge pull request 'Moved to QT' (#12) from moving-to-qt into main
Reviewed-on: https://codeberg.org/RemixDev/deemix-pyweb/pulls/12
This commit is contained in:
commit
923d57a2b6
219
deemix-pyweb.py
Normal file
219
deemix-pyweb.py
Normal file
|
@ -0,0 +1,219 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
from PyQt5.QtWidgets import QApplication, QMainWindow, QFileDialog, QDialog, QVBoxLayout
|
||||||
|
from PyQt5.QtWebEngineWidgets import QWebEngineView, QWebEnginePage, QWebEngineProfile
|
||||||
|
from PyQt5.QtCore import QUrl, pyqtSignal
|
||||||
|
from PyQt5.QtGui import QIcon
|
||||||
|
|
||||||
|
import json
|
||||||
|
import webbrowser
|
||||||
|
|
||||||
|
from threading import Thread, Lock, Semaphore
|
||||||
|
import sys
|
||||||
|
import os.path as path
|
||||||
|
from os import makedirs
|
||||||
|
from time import sleep
|
||||||
|
from server import run_server
|
||||||
|
from http.client import HTTPConnection
|
||||||
|
from deemix.utils.localpaths import getConfigFolder
|
||||||
|
|
||||||
|
server_lock = Lock()
|
||||||
|
|
||||||
|
class LoginWindow(QDialog):
|
||||||
|
class CustomPage(QWebEnginePage):
|
||||||
|
def acceptNavigationRequest(self, url, type, main):
|
||||||
|
if url.toString() == "https://www.deezer.com/":
|
||||||
|
url = QUrl('https://www.deezer.com/ajax/gw-light.php?method=user.getArl&input=3&api_version=1.0&api_token=null')
|
||||||
|
self.setUrl(url)
|
||||||
|
return False
|
||||||
|
return super().acceptNavigationRequest(url, type, main)
|
||||||
|
|
||||||
|
def __init__(self, parent):
|
||||||
|
super().__init__(parent)
|
||||||
|
self.webview = QWebEngineView()
|
||||||
|
profile = QWebEngineProfile(self.webview)
|
||||||
|
profile.setPersistentCookiesPolicy(QWebEngineProfile.NoPersistentCookies)
|
||||||
|
self.page = self.CustomPage(profile, self.webview)
|
||||||
|
self.page.loadFinished.connect(self.checkURL)
|
||||||
|
self.webview.setPage(self.page)
|
||||||
|
self.webview.setUrl(QUrl("https://deezer.com/login"))
|
||||||
|
layout = QVBoxLayout()
|
||||||
|
layout.addWidget(self.webview)
|
||||||
|
self.setLayout(layout)
|
||||||
|
self.arl = None
|
||||||
|
self.exec_()
|
||||||
|
|
||||||
|
def checkURL(self, ok):
|
||||||
|
url = self.webview.url().toString()
|
||||||
|
if 'user.getArl' in url:
|
||||||
|
sleep(1)
|
||||||
|
self.webview.page().toPlainText(self.saveARL)
|
||||||
|
|
||||||
|
def saveARL(self, body):
|
||||||
|
if body.startswith("{"):
|
||||||
|
self.arl = json.loads(body)['results']
|
||||||
|
self.accept()
|
||||||
|
|
||||||
|
class MainWindow(QMainWindow):
|
||||||
|
selectDownloadFolder_trigger = pyqtSignal()
|
||||||
|
appLogin_trigger = pyqtSignal()
|
||||||
|
|
||||||
|
class MainWebpage(QWebEnginePage):
|
||||||
|
def __init__(self, parent):
|
||||||
|
super().__init__(parent)
|
||||||
|
actions = [0,1,2,3,10,11,12,13,14,31,16,19,25,26,28,30,32]
|
||||||
|
for a in actions:
|
||||||
|
self.action(a).setVisible(False)
|
||||||
|
|
||||||
|
class ExternalWebpage(QWebEnginePage):
|
||||||
|
def __init__(self, parent):
|
||||||
|
super().__init__(parent)
|
||||||
|
self.urlChanged.connect(self.open_browser)
|
||||||
|
|
||||||
|
def open_browser(self, url):
|
||||||
|
page = self.sender()
|
||||||
|
webbrowser.open(url.toString(), 2, True)
|
||||||
|
page.deleteLater()
|
||||||
|
|
||||||
|
def createWindow(self, _type):
|
||||||
|
page = None
|
||||||
|
if _type == QWebEnginePage.WebBrowserTab:
|
||||||
|
page = self.ExternalWebpage(self)
|
||||||
|
return page
|
||||||
|
|
||||||
|
def __init__(self, title, url, x=None, y=None, w=800, h=600):
|
||||||
|
super().__init__()
|
||||||
|
self.resize(w, h)
|
||||||
|
self.setWindowTitle(title)
|
||||||
|
self.setWindowIcon(QIcon(path.join(appDir, 'icon.ico')))
|
||||||
|
self.setMinimumSize(800, 600)
|
||||||
|
self.webview = QWebEngineView()
|
||||||
|
self.page = self.MainWebpage(self.webview)
|
||||||
|
self.page.loadFinished.connect(self.finishLoading)
|
||||||
|
self.webview.setPage(self.page)
|
||||||
|
self.setCentralWidget(self.webview)
|
||||||
|
self.url = url
|
||||||
|
|
||||||
|
self.downloadFolder = None
|
||||||
|
self.selectDownloadFolder_trigger.connect(self.selectDownloadFolder)
|
||||||
|
self._selectDownloadFolder_semaphore = Semaphore(0)
|
||||||
|
|
||||||
|
self.arl = None
|
||||||
|
self.appLogin_trigger.connect(self.appLogin)
|
||||||
|
self._appLogin_semaphore = Semaphore(0)
|
||||||
|
|
||||||
|
if x is not None and y is not None:
|
||||||
|
self.move(x, y)
|
||||||
|
else:
|
||||||
|
center = QApplication.desktop().availableGeometry().center() - self.rect().center()
|
||||||
|
self.move(center.x(), center.y())
|
||||||
|
|
||||||
|
def showWindow(self):
|
||||||
|
self.webview.setUrl(QUrl(self.url))
|
||||||
|
self.show()
|
||||||
|
|
||||||
|
def selectDownloadFolder(self):
|
||||||
|
filename = QFileDialog.getExistingDirectory(self, "Select Download Folder", options=QFileDialog.ShowDirsOnly)
|
||||||
|
self.downloadFolder = filename
|
||||||
|
self._selectDownloadFolder_semaphore.release()
|
||||||
|
|
||||||
|
def appLogin(self):
|
||||||
|
loginWindow = LoginWindow(self)
|
||||||
|
self.arl = loginWindow.arl
|
||||||
|
self._appLogin_semaphore.release()
|
||||||
|
loginWindow.page.deleteLater()
|
||||||
|
loginWindow.webview.deleteLater()
|
||||||
|
loginWindow.deleteLater()
|
||||||
|
|
||||||
|
def closeEvent(self, event):
|
||||||
|
x = int(self.x())
|
||||||
|
y = int(self.y())
|
||||||
|
w = int(self.width())
|
||||||
|
h = int(self.height())
|
||||||
|
if x < 0: x = 0
|
||||||
|
if y < 0: y = 0
|
||||||
|
with open(path.join(configFolder, '.UIposition'), 'w') as f:
|
||||||
|
f.write("|".join([str(x),str(y),str(w),str(h)]))
|
||||||
|
event.accept()
|
||||||
|
|
||||||
|
def finishLoading(self, ok):
|
||||||
|
if ok: self.webview.page().runJavaScript("window.dispatchEvent(new Event('pywebviewready'))")
|
||||||
|
|
||||||
|
def url_ok(url, port):
|
||||||
|
try:
|
||||||
|
conn = HTTPConnection(url, port)
|
||||||
|
conn.request('GET', '/')
|
||||||
|
r = conn.getresponse()
|
||||||
|
return r.status == 200
|
||||||
|
except:
|
||||||
|
print("Server not started")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def save_position():
|
||||||
|
window = webview.windows[0]
|
||||||
|
x = int(window.x)
|
||||||
|
y = int(window.y)
|
||||||
|
w = int(window.width)
|
||||||
|
h = int(window.height)
|
||||||
|
if x < 0: x = 0
|
||||||
|
if y < 0: y = 0
|
||||||
|
with open(path.join(configFolder, '.UIposition'), 'w') as f:
|
||||||
|
f.write("|".join([str(x),str(y),str(w),str(h)]))
|
||||||
|
|
||||||
|
def get_position():
|
||||||
|
if path.isfile(path.join(configFolder, '.UIposition')):
|
||||||
|
try:
|
||||||
|
with open(path.join(configFolder, '.UIposition'), 'r') as f:
|
||||||
|
(x,y,w,h) = f.read().strip().split("|")
|
||||||
|
x = int(x)
|
||||||
|
y = int(y)
|
||||||
|
w = int(w)
|
||||||
|
h = int(h)
|
||||||
|
if x < 0: x = 0
|
||||||
|
if y < 0: y = 0
|
||||||
|
except:
|
||||||
|
x = None
|
||||||
|
y = None
|
||||||
|
w = 800
|
||||||
|
h = 600
|
||||||
|
else:
|
||||||
|
x = None
|
||||||
|
y = None
|
||||||
|
w = 800
|
||||||
|
h = 600
|
||||||
|
return (x,y,w,h)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
url = "127.0.0.1"
|
||||||
|
port = 6595
|
||||||
|
if len(sys.argv) >= 2:
|
||||||
|
try:
|
||||||
|
port = int(sys.argv[1])
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
portable = None
|
||||||
|
server = False
|
||||||
|
appDir = path.dirname(path.realpath(__file__))
|
||||||
|
if '--portable' in sys.argv:
|
||||||
|
portable = path.join(appDir, 'config')
|
||||||
|
if '--server' in sys.argv or '-s' in sys.argv:
|
||||||
|
server = True
|
||||||
|
|
||||||
|
if not server:
|
||||||
|
configFolder = portable or getConfigFolder()
|
||||||
|
x,y,w,h = get_position()
|
||||||
|
app = QApplication([])
|
||||||
|
window = MainWindow('deemix', 'http://'+url+':'+str(port), x,y,w,h)
|
||||||
|
t = Thread(target=run_server, args=(port, url, portable, window))
|
||||||
|
else:
|
||||||
|
t = Thread(target=run_server, args=(port, url, portable))
|
||||||
|
t.daemon = True
|
||||||
|
t.start()
|
||||||
|
|
||||||
|
if not server:
|
||||||
|
while not url_ok(url, port):
|
||||||
|
sleep(1)
|
||||||
|
window.showWindow()
|
||||||
|
app.exec_()
|
||||||
|
conn = HTTPConnection(url, port)
|
||||||
|
conn.request('GET', '/shutdown')
|
||||||
|
t.join()
|
|
@ -7,7 +7,7 @@ block_cipher = None
|
||||||
|
|
||||||
sys.modules['FixTk'] = None
|
sys.modules['FixTk'] = None
|
||||||
|
|
||||||
a = Analysis(['deemix_gui.py'],
|
a = Analysis(['deemix-pyweb.py'],
|
||||||
binaries=[],
|
binaries=[],
|
||||||
datas=[('webui/public', 'webui/public')],
|
datas=[('webui/public', 'webui/public')],
|
||||||
hiddenimports=['engineio.async_drivers.threading', 'pkg_resources.py2_warn'],
|
hiddenimports=['engineio.async_drivers.threading', 'pkg_resources.py2_warn'],
|
||||||
|
@ -27,7 +27,7 @@ if sys.platform.startswith('darwin'):
|
||||||
a.zipfiles,
|
a.zipfiles,
|
||||||
a.datas,
|
a.datas,
|
||||||
[],
|
[],
|
||||||
name='deemix_gui',
|
name='deemix-pyweb',
|
||||||
debug=False,
|
debug=False,
|
||||||
bootloader_ignore_signals=False,
|
bootloader_ignore_signals=False,
|
||||||
strip=False,
|
strip=False,
|
||||||
|
@ -37,7 +37,7 @@ if sys.platform.startswith('darwin'):
|
||||||
console=False,
|
console=False,
|
||||||
icon=f"icon.icns")
|
icon=f"icon.icns")
|
||||||
app = BUNDLE(exe,
|
app = BUNDLE(exe,
|
||||||
name='deemix_gui.app',
|
name='deemix-pyweb.app',
|
||||||
icon="icon.icns",
|
icon="icon.icns",
|
||||||
bundle_identifier=None)
|
bundle_identifier=None)
|
||||||
else:
|
else:
|
||||||
|
@ -45,7 +45,7 @@ else:
|
||||||
a.scripts,
|
a.scripts,
|
||||||
[],
|
[],
|
||||||
exclude_binaries=True,
|
exclude_binaries=True,
|
||||||
name='deemix_gui',
|
name='deemix-pyweb',
|
||||||
debug=False,
|
debug=False,
|
||||||
bootloader_ignore_signals=False,
|
bootloader_ignore_signals=False,
|
||||||
strip=False,
|
strip=False,
|
||||||
|
@ -59,4 +59,4 @@ else:
|
||||||
strip=False,
|
strip=False,
|
||||||
upx=True,
|
upx=True,
|
||||||
upx_exclude=[],
|
upx_exclude=[],
|
||||||
name='deemix_gui')
|
name='deemix-pyweb')
|
101
deemix_gui.py
101
deemix_gui.py
|
@ -1,101 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
import webview
|
|
||||||
|
|
||||||
from threading import Thread, Lock
|
|
||||||
import sys
|
|
||||||
import os.path as path
|
|
||||||
from os import makedirs
|
|
||||||
from time import sleep
|
|
||||||
from server import run_server
|
|
||||||
from http.client import HTTPConnection
|
|
||||||
from deemix.utils.localpaths import getConfigFolder
|
|
||||||
|
|
||||||
server_lock = Lock()
|
|
||||||
|
|
||||||
def url_ok(url, port):
|
|
||||||
try:
|
|
||||||
conn = HTTPConnection(url, port)
|
|
||||||
conn.request('GET', '/')
|
|
||||||
r = conn.getresponse()
|
|
||||||
return r.status == 200
|
|
||||||
except:
|
|
||||||
print("Server not started")
|
|
||||||
return False
|
|
||||||
|
|
||||||
def save_position():
|
|
||||||
window = webview.windows[0]
|
|
||||||
x = int(window.x)
|
|
||||||
y = int(window.y)
|
|
||||||
w = int(window.width)
|
|
||||||
h = int(window.height)
|
|
||||||
if x < 0: x = 0
|
|
||||||
if y < 0: y = 0
|
|
||||||
with open(path.join(configFolder, '.UIposition'), 'w') as f:
|
|
||||||
f.write("|".join([str(x),str(y),str(w),str(h)]))
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
url = "127.0.0.1"
|
|
||||||
port = 6595
|
|
||||||
if len(sys.argv) >= 2:
|
|
||||||
try:
|
|
||||||
port = int(sys.argv[1])
|
|
||||||
except ValueError:
|
|
||||||
pass
|
|
||||||
portable = None
|
|
||||||
server = False
|
|
||||||
if '--portable' in sys.argv:
|
|
||||||
portable = path.join(path.dirname(path.realpath(__file__)), 'config')
|
|
||||||
if '--server' in sys.argv or '-s' in sys.argv:
|
|
||||||
server = True
|
|
||||||
|
|
||||||
t = Thread(target=run_server, args=(port, url, portable))
|
|
||||||
t.daemon = True
|
|
||||||
t.start()
|
|
||||||
|
|
||||||
if not server:
|
|
||||||
while not url_ok(url, port):
|
|
||||||
sleep(1)
|
|
||||||
if portable:
|
|
||||||
configFolder = portable
|
|
||||||
else:
|
|
||||||
configFolder = getConfigFolder()
|
|
||||||
|
|
||||||
if path.isfile(path.join(configFolder, '.UIposition')):
|
|
||||||
try:
|
|
||||||
with open(path.join(configFolder, '.UIposition'), 'r') as f:
|
|
||||||
(x,y,w,h) = f.read().strip().split("|")
|
|
||||||
x = int(x)
|
|
||||||
y = int(y)
|
|
||||||
w = int(w)
|
|
||||||
h = int(h)
|
|
||||||
if x < 0: x = 0
|
|
||||||
if y < 0: y = 0
|
|
||||||
except:
|
|
||||||
x = None
|
|
||||||
y = None
|
|
||||||
w = 800
|
|
||||||
h = 600
|
|
||||||
else:
|
|
||||||
x = None
|
|
||||||
y = None
|
|
||||||
w = 800
|
|
||||||
h = 600
|
|
||||||
window = webview.create_window('deemix', 'http://'+url+':'+str(port),
|
|
||||||
x=x, y=y, width=w, height=h, text_select=True)
|
|
||||||
window.closing += save_position
|
|
||||||
if sys.platform == "win32":
|
|
||||||
from webview.platforms.cef import settings
|
|
||||||
cacheFolder = path.join(configFolder, 'cefCache')
|
|
||||||
makedirs(cacheFolder, exist_ok=True)
|
|
||||||
settings.update({
|
|
||||||
'persist_session_cookies': True,
|
|
||||||
'cache_path': cacheFolder
|
|
||||||
})
|
|
||||||
webview.start(gui='cef')
|
|
||||||
elif sys.platform == "linux":
|
|
||||||
webview.start(gui='qt')
|
|
||||||
else:
|
|
||||||
webview.start()
|
|
||||||
conn = HTTPConnection(url, port)
|
|
||||||
conn.request('GET', '/shutdown')
|
|
||||||
t.join()
|
|
|
@ -1,6 +1,5 @@
|
||||||
deemix>=1.2.7
|
deemix>=1.2.7
|
||||||
flask
|
flask
|
||||||
flask-socketio
|
flask-socketio
|
||||||
pywebview>=3.3.2
|
pyqt5
|
||||||
cefpython3; platform_system == "Windows"
|
pyqtwebengine
|
||||||
pywebview[qt]>=3.3.2; platform_system == "Linux"
|
|
||||||
|
|
67
server.py
67
server.py
|
@ -25,6 +25,7 @@ from engineio.payload import Payload
|
||||||
Payload.max_decode_packets = 500
|
Payload.max_decode_packets = 500
|
||||||
|
|
||||||
app = None
|
app = None
|
||||||
|
gui = None
|
||||||
|
|
||||||
class CustomFlask(Flask):
|
class CustomFlask(Flask):
|
||||||
jinja_options = Flask.jinja_options.copy()
|
jinja_options = Flask.jinja_options.copy()
|
||||||
|
@ -288,59 +289,33 @@ def openDownloadsFolder():
|
||||||
|
|
||||||
@socketio.on('selectDownloadFolder')
|
@socketio.on('selectDownloadFolder')
|
||||||
def selectDownloadFolder():
|
def selectDownloadFolder():
|
||||||
try:
|
if gui:
|
||||||
import webview
|
gui.selectDownloadFolder_trigger.emit()
|
||||||
result = webview.windows[0].create_file_dialog(webview.FOLDER_DIALOG, allow_multiple=False)
|
gui._selectDownloadFolder_semaphore.acquire()
|
||||||
|
result = gui.downloadFolder
|
||||||
if result:
|
if result:
|
||||||
emit('downloadFolderSelected', result)
|
emit('downloadFolderSelected', result)
|
||||||
except:
|
else:
|
||||||
print("Can't open folder selection, you're not running pywebview")
|
print("Can't open folder selection, you're not running the gui")
|
||||||
|
|
||||||
@socketio.on('applogin')
|
@socketio.on('applogin')
|
||||||
def applogin():
|
def applogin():
|
||||||
try:
|
if gui:
|
||||||
import webview
|
if not session['dz'].logged_in:
|
||||||
global loginWindow
|
gui.appLogin_trigger.emit()
|
||||||
if not loginWindow:
|
gui._appLogin_semaphore.acquire()
|
||||||
@copy_current_request_context
|
if gui.arl:
|
||||||
def get_ARL():
|
emit('applogin_arl', gui.arl)
|
||||||
global loginWindow
|
gui.arl = None
|
||||||
window = webview.windows[loginWindow]
|
else:
|
||||||
loginWindow = False
|
emit('logged_in', {'status': 2, 'user': session['dz'].user})
|
||||||
window.loaded -= get_ARL
|
else:
|
||||||
arl = json.loads(window.get_elements("body")[0]['innerText'])['results']
|
print("Can't open login page, you're not running the gui")
|
||||||
window.destroy()
|
|
||||||
emit('applogin_arl', arl)
|
|
||||||
@copy_current_request_context
|
|
||||||
def check_URL():
|
|
||||||
global loginWindow
|
|
||||||
window = webview.windows[loginWindow]
|
|
||||||
try:
|
|
||||||
url = window.get_current_url()
|
|
||||||
except:
|
|
||||||
url = "https://www.deezer.com/us/login"
|
|
||||||
if not "/login" in url:
|
|
||||||
window.loaded -= check_URL
|
|
||||||
window.loaded += get_ARL
|
|
||||||
window.load_url('https://www.deezer.com/ajax/gw-light.php?method=user.getArl&input=3&api_version=1.0&api_token=null')
|
|
||||||
@copy_current_request_context
|
|
||||||
def on_close():
|
|
||||||
global loginWindow
|
|
||||||
if loginWindow:
|
|
||||||
loginWindow = False
|
|
||||||
if not session['dz'].logged_in:
|
|
||||||
window = webview.create_window('Login into your deezer account', "https://deezer.com/login")
|
|
||||||
loginWindow = len(webview.windows)-1
|
|
||||||
window.loaded += check_URL
|
|
||||||
window.closed += on_close
|
|
||||||
else:
|
|
||||||
emit('logged_in', {'status': 2, 'user': session['dz'].user})
|
|
||||||
except:
|
|
||||||
print("Can't open folder selection, you're not running pywebview")
|
|
||||||
|
|
||||||
def run_server(port, host="127.0.0.1", portable=None):
|
def run_server(port, host="127.0.0.1", portable=None, mainWindow=None):
|
||||||
global app
|
global app, gui
|
||||||
app = deemix(portable)
|
app = deemix(portable)
|
||||||
|
gui = mainWindow
|
||||||
print("Starting server at http://" + host + ":" + str(port))
|
print("Starting server at http://" + host + ":" + str(port))
|
||||||
socketio.run(server, host=host, port=port)
|
socketio.run(server, host=host, port=port)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue