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
|
||||
|
||||
a = Analysis(['deemix_gui.py'],
|
||||
a = Analysis(['deemix-pyweb.py'],
|
||||
binaries=[],
|
||||
datas=[('webui/public', 'webui/public')],
|
||||
hiddenimports=['engineio.async_drivers.threading', 'pkg_resources.py2_warn'],
|
||||
|
@ -27,7 +27,7 @@ if sys.platform.startswith('darwin'):
|
|||
a.zipfiles,
|
||||
a.datas,
|
||||
[],
|
||||
name='deemix_gui',
|
||||
name='deemix-pyweb',
|
||||
debug=False,
|
||||
bootloader_ignore_signals=False,
|
||||
strip=False,
|
||||
|
@ -37,7 +37,7 @@ if sys.platform.startswith('darwin'):
|
|||
console=False,
|
||||
icon=f"icon.icns")
|
||||
app = BUNDLE(exe,
|
||||
name='deemix_gui.app',
|
||||
name='deemix-pyweb.app',
|
||||
icon="icon.icns",
|
||||
bundle_identifier=None)
|
||||
else:
|
||||
|
@ -45,7 +45,7 @@ else:
|
|||
a.scripts,
|
||||
[],
|
||||
exclude_binaries=True,
|
||||
name='deemix_gui',
|
||||
name='deemix-pyweb',
|
||||
debug=False,
|
||||
bootloader_ignore_signals=False,
|
||||
strip=False,
|
||||
|
@ -59,4 +59,4 @@ else:
|
|||
strip=False,
|
||||
upx=True,
|
||||
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
|
||||
flask
|
||||
flask-socketio
|
||||
pywebview>=3.3.2
|
||||
cefpython3; platform_system == "Windows"
|
||||
pywebview[qt]>=3.3.2; platform_system == "Linux"
|
||||
pyqt5
|
||||
pyqtwebengine
|
||||
|
|
67
server.py
67
server.py
|
@ -25,6 +25,7 @@ from engineio.payload import Payload
|
|||
Payload.max_decode_packets = 500
|
||||
|
||||
app = None
|
||||
gui = None
|
||||
|
||||
class CustomFlask(Flask):
|
||||
jinja_options = Flask.jinja_options.copy()
|
||||
|
@ -288,59 +289,33 @@ def openDownloadsFolder():
|
|||
|
||||
@socketio.on('selectDownloadFolder')
|
||||
def selectDownloadFolder():
|
||||
try:
|
||||
import webview
|
||||
result = webview.windows[0].create_file_dialog(webview.FOLDER_DIALOG, allow_multiple=False)
|
||||
if gui:
|
||||
gui.selectDownloadFolder_trigger.emit()
|
||||
gui._selectDownloadFolder_semaphore.acquire()
|
||||
result = gui.downloadFolder
|
||||
if result:
|
||||
emit('downloadFolderSelected', result)
|
||||
except:
|
||||
print("Can't open folder selection, you're not running pywebview")
|
||||
else:
|
||||
print("Can't open folder selection, you're not running the gui")
|
||||
|
||||
@socketio.on('applogin')
|
||||
def applogin():
|
||||
try:
|
||||
import webview
|
||||
global loginWindow
|
||||
if not loginWindow:
|
||||
@copy_current_request_context
|
||||
def get_ARL():
|
||||
global loginWindow
|
||||
window = webview.windows[loginWindow]
|
||||
loginWindow = False
|
||||
window.loaded -= get_ARL
|
||||
arl = json.loads(window.get_elements("body")[0]['innerText'])['results']
|
||||
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")
|
||||
if gui:
|
||||
if not session['dz'].logged_in:
|
||||
gui.appLogin_trigger.emit()
|
||||
gui._appLogin_semaphore.acquire()
|
||||
if gui.arl:
|
||||
emit('applogin_arl', gui.arl)
|
||||
gui.arl = None
|
||||
else:
|
||||
emit('logged_in', {'status': 2, 'user': session['dz'].user})
|
||||
else:
|
||||
print("Can't open login page, you're not running the gui")
|
||||
|
||||
def run_server(port, host="127.0.0.1", portable=None):
|
||||
global app
|
||||
def run_server(port, host="127.0.0.1", portable=None, mainWindow=None):
|
||||
global app, gui
|
||||
app = deemix(portable)
|
||||
gui = mainWindow
|
||||
print("Starting server at http://" + host + ":" + str(port))
|
||||
socketio.run(server, host=host, port=port)
|
||||
|
||||
|
|
Loading…
Reference in a new issue