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:
RemixDev 2020-08-22 14:51:15 +02:00
commit 923d57a2b6
5 changed files with 247 additions and 155 deletions

219
deemix-pyweb.py Normal file
View 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()

View file

@ -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')

View file

@ -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()

View file

@ -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"

View file

@ -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)