The pywebview app for deemix-webui
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

245 lines
8.0 KiB

  1. #!/usr/bin/env python3
  2. from PyQt5.QtWidgets import QApplication, QMainWindow, QFileDialog, QDialog, QVBoxLayout
  3. from PyQt5.QtWebEngineWidgets import QWebEngineView, QWebEnginePage, QWebEngineProfile
  4. from PyQt5.QtCore import QUrl, pyqtSignal
  5. from PyQt5.QtGui import QIcon
  6. import json
  7. import webbrowser
  8. from threading import Thread, Lock, Semaphore
  9. import sys
  10. import os.path as path
  11. from os import makedirs
  12. from time import sleep
  13. from server import run_server
  14. from http.client import HTTPConnection
  15. from deemix.utils.localpaths import getConfigFolder
  16. server_lock = Lock()
  17. if sys.platform == "win32":
  18. import ctypes
  19. myappid = 'RemixDev.deemix'
  20. ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(myappid)
  21. class LoginWindow(QDialog):
  22. class CustomPage(QWebEnginePage):
  23. def acceptNavigationRequest(self, url, type, main):
  24. if url.toString() == "https://www.deezer.com/":
  25. url = QUrl('https://www.deezer.com/ajax/gw-light.php?method=user.getArl&input=3&api_version=1.0&api_token=null')
  26. self.setUrl(url)
  27. return False
  28. return super().acceptNavigationRequest(url, type, main)
  29. def __init__(self, parent):
  30. super().__init__(parent)
  31. self.webview = QWebEngineView()
  32. profile = QWebEngineProfile(self.webview)
  33. profile.setPersistentCookiesPolicy(QWebEngineProfile.NoPersistentCookies)
  34. self.page = self.CustomPage(profile, self.webview)
  35. self.page.loadFinished.connect(self.checkURL)
  36. self.webview.setPage(self.page)
  37. self.webview.setUrl(QUrl("https://deezer.com/login"))
  38. layout = QVBoxLayout()
  39. layout.addWidget(self.webview)
  40. self.setLayout(layout)
  41. self.arl = None
  42. self.exec_()
  43. def checkURL(self, ok):
  44. url = self.webview.url().toString()
  45. if 'user.getArl' in url:
  46. sleep(1)
  47. self.webview.page().toPlainText(self.saveARL)
  48. def saveARL(self, body):
  49. if body.startswith("{"):
  50. self.arl = json.loads(body)['results']
  51. self.accept()
  52. class MainWindow(QMainWindow):
  53. selectDownloadFolder_trigger = pyqtSignal()
  54. appLogin_trigger = pyqtSignal()
  55. class MainWebpage(QWebEnginePage):
  56. def __init__(self, parent):
  57. super().__init__(parent)
  58. actions = [
  59. QWebEnginePage.Back,
  60. QWebEnginePage.Forward,
  61. QWebEnginePage.Stop,
  62. QWebEnginePage.Reload,
  63. QWebEnginePage.ReloadAndBypassCache,
  64. QWebEnginePage.PasteAndMatchStyle,
  65. QWebEnginePage.OpenLinkInThisWindow,
  66. QWebEnginePage.OpenLinkInNewWindow,
  67. QWebEnginePage.OpenLinkInNewTab,
  68. QWebEnginePage.OpenLinkInNewBackgroundTab,
  69. QWebEnginePage.DownloadLinkToDisk,
  70. QWebEnginePage.DownloadImageToDisk,
  71. QWebEnginePage.DownloadMediaToDisk,
  72. QWebEnginePage.InspectElement,
  73. QWebEnginePage.RequestClose,
  74. QWebEnginePage.SavePage
  75. ]
  76. for a in actions:
  77. self.action(a).setVisible(False)
  78. class ExternalWebpage(QWebEnginePage):
  79. def __init__(self, parent):
  80. super().__init__(parent)
  81. self.urlChanged.connect(self.open_browser)
  82. def open_browser(self, url):
  83. page = self.sender()
  84. webbrowser.open(url.toString(), 2, True)
  85. page.deleteLater()
  86. def createWindow(self, _type):
  87. page = None
  88. if _type == QWebEnginePage.WebBrowserTab:
  89. page = self.ExternalWebpage(self)
  90. return page
  91. def __init__(self, title, url, x=None, y=None, w=800, h=600):
  92. super().__init__()
  93. startMaximized = False
  94. if w == -1 or h == -1:
  95. self.resize(800, 600)
  96. startMaximized = True
  97. else:
  98. self.resize(w, h)
  99. self.setWindowTitle(title)
  100. self.setWindowIcon(QIcon(path.join(appDir, 'icon.ico')))
  101. self.setMinimumSize(800, 600)
  102. self.webview = QWebEngineView()
  103. self.page = self.MainWebpage(self.webview)
  104. self.page.loadFinished.connect(self.finishLoading)
  105. self.webview.setPage(self.page)
  106. self.setCentralWidget(self.webview)
  107. self.url = url
  108. if dev:
  109. self.dev_tools = QWebEngineView()
  110. self.webview.page().setDevToolsPage(self.dev_tools.page())
  111. self.dev_tools.show()
  112. self.downloadFolder = None
  113. self.selectDownloadFolder_trigger.connect(self.selectDownloadFolder)
  114. self._selectDownloadFolder_semaphore = Semaphore(0)
  115. self.arl = None
  116. self.appLogin_trigger.connect(self.appLogin)
  117. self._appLogin_semaphore = Semaphore(0)
  118. if x is None or y is None or startMaximized:
  119. center = QApplication.desktop().availableGeometry().center() - self.rect().center()
  120. self.move(center.x(), center.y())
  121. else:
  122. self.move(x, y)
  123. if startMaximized: self.showMaximized()
  124. def showWindow(self):
  125. self.webview.setUrl(QUrl(self.url))
  126. self.show()
  127. def selectDownloadFolder(self):
  128. filename = QFileDialog.getExistingDirectory(self, "Select Download Folder", options=QFileDialog.ShowDirsOnly)
  129. self.downloadFolder = filename
  130. self._selectDownloadFolder_semaphore.release()
  131. def appLogin(self):
  132. loginWindow = LoginWindow(self)
  133. self.arl = loginWindow.arl
  134. self._appLogin_semaphore.release()
  135. loginWindow.page.deleteLater()
  136. loginWindow.webview.deleteLater()
  137. loginWindow.deleteLater()
  138. def closeEvent(self, event):
  139. x = int(self.x())
  140. y = int(self.y())
  141. w = int(self.width())
  142. h = int(self.height())
  143. if x < 0: x = 0
  144. if y < 0: y = 0
  145. if self.isMaximized():
  146. w = -1
  147. h = -1
  148. with open(path.join(configFolder, '.UIposition'), 'w') as f:
  149. f.write("|".join([str(x),str(y),str(w),str(h)]))
  150. event.accept()
  151. def finishLoading(self, ok):
  152. if ok: self.webview.page().runJavaScript("window.dispatchEvent(new Event('pywebviewready'))")
  153. def url_ok(url, port):
  154. try:
  155. conn = HTTPConnection(url, port)
  156. conn.request('GET', '/')
  157. r = conn.getresponse()
  158. return r.status == 200
  159. except:
  160. print("Server not started")
  161. return False
  162. def get_position():
  163. if path.isfile(path.join(configFolder, '.UIposition')):
  164. try:
  165. with open(path.join(configFolder, '.UIposition'), 'r') as f:
  166. (x,y,w,h) = f.read().strip().split("|")
  167. x = int(x)
  168. y = int(y)
  169. w = int(w)
  170. h = int(h)
  171. if x < 0: x = 0
  172. if y < 0: y = 0
  173. except:
  174. x = None
  175. y = None
  176. w = 800
  177. h = 600
  178. else:
  179. x = None
  180. y = None
  181. w = 800
  182. h = 600
  183. return (x,y,w,h)
  184. if __name__ == '__main__':
  185. url = "127.0.0.1"
  186. port = 6595
  187. if len(sys.argv) >= 2:
  188. try:
  189. port = int(sys.argv[1])
  190. except ValueError:
  191. pass
  192. portable = None
  193. appDir = path.dirname(path.realpath(__file__))
  194. if '--portable' in sys.argv:
  195. portable = path.join(appDir, 'config')
  196. server = '--server' in sys.argv or '-s' in sys.argv
  197. dev = '--dev' in sys.argv
  198. if not server:
  199. configFolder = portable or getConfigFolder()
  200. x,y,w,h = get_position()
  201. app = QApplication([])
  202. window = MainWindow('deemix', 'http://'+url+':'+str(port), x,y,w,h)
  203. t = Thread(target=run_server, args=(port, url, portable, window))
  204. else:
  205. t = Thread(target=run_server, args=(port, url, portable))
  206. t.daemon = True
  207. t.start()
  208. if not server:
  209. while not url_ok(url, port):
  210. sleep(1)
  211. window.showWindow()
  212. app.exec_()
  213. conn = HTTPConnection(url, port)
  214. conn.request('GET', '/shutdown')
  215. t.join()