Moved version and deezer check from server to app

This commit is contained in:
RemixDev 2020-09-27 19:49:54 +02:00
parent 0be75208fb
commit 4f1d17f7c7
3 changed files with 158 additions and 156 deletions

87
app.py
View file

@ -1,4 +1,7 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import eventlet
requests = eventlet.import_patched('requests')
from deemix.api.deezer import Deezer from deemix.api.deezer import Deezer
from deemix.app.settings import Settings from deemix.app.settings import Settings
from deemix.app.queuemanager import QueueManager from deemix.app.queuemanager import QueueManager
@ -10,6 +13,35 @@ from deemix.utils.localpaths import getConfigFolder
import os.path as path import os.path as path
import json import json
from datetime import datetime
def resource_path(relative_path):
""" Get absolute path to resource, works for dev and for PyInstaller """
try:
# PyInstaller creates a temp folder and stores path in _MEIPASS
base_path = sys._MEIPASS
except Exception:
base_path = path.dirname(path.abspath(path.realpath(__file__)))
return path.join(base_path, relative_path)
class LoginStatus():
"""Login status codes"""
NOT_AVAILABLE = -1
"""Deezer is not Available in your country"""
FAILED = 0
"""Login Failed"""
SUCCESS = 1
"""Login Successfull"""
ALREADY_LOGGED = 2
"""Already logged in"""
FORCED_SUCCESS = 3
"""Forced Login Successfull"""
class deemix: class deemix:
def __init__(self, portable): def __init__(self, portable):
@ -21,6 +53,50 @@ class deemix:
self.chartsList = [] self.chartsList = []
self.homeCache = None self.homeCache = None
self.currentVersion = None
self.latestVersion = None
self.updateAvailable = False
self.isDeezerAvailable = True
def checkForUpdates(self):
commitFile = resource_path('version.txt')
if path.isfile(commitFile):
print("Checking for updates...")
with open(commitFile, 'r') as f:
self.currentVersion = f.read().strip()
try:
latestVersion = requests.get("https://deemix.app/pyweb/latest")
latestVersion.raise_for_status()
self.latestVersion = latestVersion.text.strip()
except:
self.latestVersion = None
self.updateAvailable = self.compare_versions()
if self.updateAvailable:
print("Update available! Commit: "+self.latestVersion)
else:
print("You're running the latest version")
def compareVersions():
if not self.latestVersion or not self.currentVersion:
return False
(currentDate, currentCommit) = tuple(self.currentVersion.split('-'))
(latestDate, latestCommit) = tuple(self.latestVersion.split('-'))
currentDate = currentDate.split('.')
latestDate = latestDate.split('.')
current = datetime(int(currentDate[0]), int(currentDate[1]), int(currentDate[2]))
latest = datetime(int(latestDate[0]), int(latestDate[1]), int(latestDate[2]))
if latest > current:
return True
elif latest == current:
return latestCommit != currentCommit
else:
return False
def checkDeezerAvailability(self):
body = requests.get("https://www.deezer.com/", headers={'Cookie': 'dz_lang=en; Domain=deezer.com; Path=/; Secure; hostOnly=false;'}).text
title = body[body.find('<title>')+7:body.find('</title>')]
self.isDeezerAvailable = title.strip() != "Deezer will soon be available in your country."
def shutdown(self, interface=None): def shutdown(self, interface=None):
if self.set.settings['saveDownloadQueue']: if self.set.settings['saveDownloadQueue']:
self.qm.saveQueue(self.configFolder) self.qm.saveQueue(self.configFolder)
@ -43,6 +119,11 @@ class deemix:
f.write(arl) f.write(arl)
return arl return arl
def login(self, dz, arl, child):
if not dz.logged_in:
return int(dz.login_via_arl(arl, child))
else:
return LoginStatus.ALREADY_LOGGED
def restoreDownloadQueue(self, dz, interface=None): def restoreDownloadQueue(self, dz, interface=None):
self.qm.loadQueue(self.configFolder, self.set.settings, interface) self.qm.loadQueue(self.configFolder, self.set.settings, interface)
@ -149,24 +230,22 @@ class deemix:
url = url.split(";") url = url.split(";")
self.qm.addToQueue(dz, url, self.set.settings, bitrate, interface, ack) self.qm.addToQueue(dz, url, self.set.settings, bitrate, interface, ack)
def removeFromQueue(self, uuid, interface=None): def removeFromQueue(self, uuid, interface=None):
self.qm.removeFromQueue(uuid, interface) self.qm.removeFromQueue(uuid, interface)
def cancelAllDownloads(self, interface=None): def cancelAllDownloads(self, interface=None):
self.qm.cancelAllDownloads(interface) self.qm.cancelAllDownloads(interface)
def removeFinishedDownloads(self, interface=None): def removeFinishedDownloads(self, interface=None):
self.qm.removeFinishedDownloads(interface) self.qm.removeFinishedDownloads(interface)
def initDownloadQueue(self): def initDownloadQueue(self):
(queue, queueComplete, queueList, currentItem) = self.qm.getQueue() (queue, queueComplete, queueList, currentItem) = self.qm.getQueue()
return (queue, queueComplete, queueList, currentItem) return (queue, queueComplete, queueList, currentItem)
def analyzeLink(self, dz, link): def analyzeLink(self, dz, link):
if 'deezer.page.link' in link:
link = requests.get(link).url
type = getTypeFromLink(link) type = getTypeFromLink(link)
relID = getIDFromLink(link, type) relID = getIDFromLink(link, type)
if type in ["track", "album"]: if type in ["track", "album"]:

View file

@ -227,7 +227,7 @@ if __name__ == '__main__':
configFolder = portable or getConfigFolder() configFolder = portable or getConfigFolder()
x,y,w,h = get_position() x,y,w,h = get_position()
window = MainWindow('deemix', 'http://'+url+':'+str(port), x,y,w,h) window = MainWindow('deemix', 'http://'+url+':'+str(port), x,y,w,h)
t = Thread(target=run_server, args=(port, url, portable, window)) t = Thread(target=run_server, args=(url, port, portable, window))
else: else:
t = Thread(target=run_server, args=(port, url, portable)) t = Thread(target=run_server, args=(port, url, portable))
t.daemon = True t.daemon = True

225
server.py
View file

@ -9,16 +9,13 @@ import eventlet
from eventlet import tpool from eventlet import tpool
from eventlet.green import subprocess from eventlet.green import subprocess
requests = eventlet.import_patched('requests') requests = eventlet.import_patched('requests')
urlopen = eventlet.import_patched('urllib.request').urlopen
from datetime import datetime
from flask import Flask, render_template, request, session, redirect, copy_current_request_context from flask import Flask, render_template, request, session, redirect, copy_current_request_context
from flask_socketio import SocketIO, emit from flask_socketio import SocketIO, emit
from werkzeug.middleware.proxy_fix import ProxyFix from werkzeug.middleware.proxy_fix import ProxyFix
from deemix import __version__ as deemixVersion from deemix import __version__ as deemix_version
from app import deemix from app import deemix, LoginStatus, resource_path
from deemix.api.deezer import Deezer from deemix.api.deezer import Deezer
from deemix.app.messageinterface import MessageInterface from deemix.app.messageinterface import MessageInterface
@ -33,10 +30,21 @@ mimetypes.add_type('text/javascript', '.js')
from engineio.payload import Payload from engineio.payload import Payload
Payload.max_decode_packets = 500 Payload.max_decode_packets = 500
app = None # Disable logging
gui = None serverLog = logging.getLogger('werkzeug')
arl = None serverLog.disabled = True
logging.getLogger('socketio').setLevel(logging.ERROR)
logging.getLogger('engineio').setLevel(logging.ERROR)
#server.logger.disabled = True
class SocketInterface(MessageInterface):
def send(self, message, value=None):
if value:
socketio.emit(message, value)
else:
socketio.emit(message)
# This allows the frontend to use vue.js
class CustomFlask(Flask): class CustomFlask(Flask):
jinja_options = Flask.jinja_options.copy() jinja_options = Flask.jinja_options.copy()
jinja_options.update(dict( jinja_options.update(dict(
@ -48,99 +56,31 @@ class CustomFlask(Flask):
comment_end_string='#$', comment_end_string='#$',
)) ))
def resource_path(relative_path): # Retrocompatibility with old versions of the app
""" Get absolute path to resource, works for dev and for PyInstaller """ # Check for public folder and fallback to webui
try: GUI_DIR = resource_path(path.join('webui', 'public'))
# PyInstaller creates a temp folder and stores path in _MEIPASS if not path.exists(GUI_DIR):
base_path = sys._MEIPASS GUI_DIR = resource_path('webui')
except Exception: if not path.isfile(path.join(GUI_DIR, 'index.html')):
base_path = path.dirname(path.abspath(path.realpath(__file__)))
return path.join(base_path, relative_path)
gui_dir = resource_path(path.join('webui', 'public'))
if not path.exists(gui_dir):
gui_dir = resource_path('webui')
if not path.isfile(path.join(gui_dir, 'index.html')):
sys.exit("WebUI not found, please download and add a WebUI") sys.exit("WebUI not found, please download and add a WebUI")
server = CustomFlask(__name__, static_folder=gui_dir, template_folder=gui_dir, static_url_path="")
server = CustomFlask(__name__, static_folder=GUI_DIR, template_folder=GUI_DIR, static_url_path="")
server.config['SEND_FILE_MAX_AGE_DEFAULT'] = 1 # disable caching server.config['SEND_FILE_MAX_AGE_DEFAULT'] = 1 # disable caching
socketio = SocketIO(server) socketio = SocketIO(server)
server.wsgi_app = ProxyFix(server.wsgi_app, x_for=1, x_proto=1) server.wsgi_app = ProxyFix(server.wsgi_app, x_for=1, x_proto=1)
class SocketInterface(MessageInterface): app = None
def send(self, message, value=None): gui = None
if value: arl = None
socketio.emit(message, value)
else:
socketio.emit(message)
socket_interface = SocketInterface() socket_interface = SocketInterface()
loginWindow = False first_connection = True
serverLog = logging.getLogger('werkzeug') def shutdown():
serverLog.disabled = True
logging.getLogger('socketio').setLevel(logging.ERROR)
logging.getLogger('engineio').setLevel(logging.ERROR)
#server.logger.disabled = True
firstConnection = True
currentVersion = None
latestVersion = None
updateAvailable = False
def compare_versions(currentVersion, latestVersion):
if not latestVersion or not currentVersion:
return False
(currentDate, currentCommit) = tuple(currentVersion.split('-'))
(latestDate, latestCommit) = tuple(latestVersion.split('-'))
currentDate = currentDate.split('.')
latestDate = latestDate.split('.')
current = datetime(int(currentDate[0]), int(currentDate[1]), int(currentDate[2]))
latest = datetime(int(latestDate[0]), int(latestDate[1]), int(latestDate[2]))
if latest > current:
return True
elif latest == current:
return latestCommit != currentCommit
else:
return False
def check_for_updates():
global currentVersion, latestVersion, updateAvailable
commitFile = resource_path('version.txt')
if path.isfile(commitFile):
print("Checking for updates...")
with open(commitFile, 'r') as f:
currentVersion = f.read().strip()
try:
latestVersion = requests.get("https://deemix.app/pyweb/latest")
latestVersion.raise_for_status()
latestVersion = latestVersion.text.strip()
except:
latestVersion = None
if currentVersion and latestVersion:
updateAvailable = compare_versions(currentVersion, latestVersion)
if updateAvailable:
print("Update available! Commit: "+latestVersion)
else:
print("You're running the latest version")
is_deezer_available = True
def check_deezer_availability():
body = requests.get("https://www.deezer.com/", headers={'Cookie': 'dz_lang=en; Domain=deezer.com; Path=/; Secure; hostOnly=false;'}).text
title = body[body.find('<title>')+7:body.find('</title>')]
return title.strip() != "Deezer will soon be available in your country."
def shutdown(interface=None):
if app is not None: if app is not None:
app.shutdown(interface=interface) app.shutdown(socket_interface)
socketio.stop() socketio.stop()
def shutdown_handler(signalnum, frame):
shutdown()
@server.route('/') @server.route('/')
def landing(): def landing():
return render_template('index.html') return render_template('index.html')
@ -151,27 +91,29 @@ def not_found_handler(e):
@server.route('/shutdown') @server.route('/shutdown')
def closing(): def closing():
shutdown(interface=socket_interface) shutdown()
return 'Server Closed' return 'Server Closed'
serverwide_arl = "--serverwide-arl" in sys.argv
if serverwide_arl:
print("Server-wide ARL enabled.")
@socketio.on('connect') @socketio.on('connect')
def on_connect(): def on_connect():
session['dz'] = Deezer() session['dz'] = Deezer()
(settings, spotifyCredentials, defaultSettings) = app.getAllSettings() (settings, spotifyCredentials, defaultSettings) = app.getAllSettings()
session['dz'].set_accept_language(settings.get('tagsLanguage')) session['dz'].set_accept_language(settings.get('tagsLanguage'))
emit('init_settings', (settings, spotifyCredentials, defaultSettings)) emit('init_settings', (settings, spotifyCredentials, defaultSettings))
emit('init_update',
{'currentCommit': currentVersion, if first_connection:
'latestCommit': latestVersion, app.checkForUpdates()
'updateAvailable': updateAvailable, app.checkDeezerAvailability()
'deemixVersion': deemixVersion}
emit('init_update',{
'currentCommit': app.currentVersion,
'latestCommit': app.latestVersion,
'updateAvailable': app.updateAvailable,
'deemixVersion': deemix_version
}
) )
if serverwide_arl: if arl:
login(arl) login(arl)
else: else:
emit('init_autologin') emit('init_autologin')
@ -184,9 +126,11 @@ def on_connect():
'queueList': queueList, 'queueList': queueList,
'currentItem': currentItem 'currentItem': currentItem
}) })
emit('init_home', app.get_home(session['dz'])) emit('init_home', app.get_home(session['dz']))
emit('init_charts', app.get_charts(session['dz'])) emit('init_charts', app.get_charts(session['dz']))
if not is_deezer_available:
if not app.isDeezerAvailable:
emit('deezerNotAvailable') emit('deezerNotAvailable')
@socketio.on('get_home_data') @socketio.on('get_home_data')
@ -207,49 +151,38 @@ def get_settings_data():
@socketio.on('login') @socketio.on('login')
def login(arl, force=False, child=0): def login(arl, force=False, child=0):
global firstConnection, is_deezer_available global first_connection
if not is_deezer_available:
emit('logged_in', {'status': -1, 'arl': arl, 'user': session['dz'].user}) if not app.isDeezerAvailable:
emit('logged_in', {'status': LoginStatus.NOT_AVAILABLE, 'arl': arl, 'user': session['dz'].user})
return return
if child == None:
child = 0 if child == None: child = 0
arl = arl.strip() arl = arl.strip()
emit('logging_in') emit('logging_in')
if not session['dz'].logged_in:
result = session['dz'].login_via_arl(arl, int(child)) if force: session['dz'] = Deezer()
else: result = app.login(session['dz'], arl, int(child))
if force: if force and result == LoginStatus.SUCCESS: result = LoginStatus.FORCED_SUCCESS
session['dz'] = Deezer()
result = session['dz'].login_via_arl(arl, int(child))
if result == 1:
result = 3
else:
result = 2
emit('logged_in', {'status': result, 'arl': arl, 'user': session['dz'].user}) emit('logged_in', {'status': result, 'arl': arl, 'user': session['dz'].user})
if firstConnection and result in [1, 3]: if first_connection and result in [LoginStatus.SUCCESS, LoginStatus.FORCED_SUCCESS]:
firstConnection = False first_connection = False
app.restoreDownloadQueue(session['dz'], socket_interface) app.restoreDownloadQueue(session['dz'], socket_interface)
if result != 0: if result != 0:
emit('familyAccounts', session['dz'].childs) emit('familyAccounts', session['dz'].childs)
emit('init_favorites', app.getUserFavorites(session['dz'])) emit('init_favorites', app.getUserFavorites(session['dz']))
@socketio.on('changeAccount') @socketio.on('changeAccount')
def changeAccount(child): def changeAccount(child):
emit('accountChanged', session['dz'].change_account(int(child))) emit('accountChanged', session['dz'].change_account(int(child)))
emit('init_favorites', app.getUserFavorites(session['dz'])) emit('init_favorites', app.getUserFavorites(session['dz']))
@socketio.on('logout') @socketio.on('logout')
def logout(): def logout():
status = 0
if session['dz'].logged_in: if session['dz'].logged_in:
session['dz'] = Deezer() session['dz'] = Deezer()
status = 0 emit('logged_out')
else:
status = 1
emit('logged_out', status)
@socketio.on('mainSearch') @socketio.on('mainSearch')
def mainSearch(data): def mainSearch(data):
@ -258,7 +191,6 @@ def mainSearch(data):
result['ack'] = data.get('ack') result['ack'] = data.get('ack')
emit('mainSearch', result) emit('mainSearch', result)
@socketio.on('search') @socketio.on('search')
def search(data): def search(data):
if data['term'].strip() != "": if data['term'].strip() != "":
@ -292,29 +224,22 @@ def newReleases(data):
def queueRestored(): def queueRestored():
app.queueRestored(session['dz'], socket_interface) app.queueRestored(session['dz'], socket_interface)
@socketio.on('addToQueue') @socketio.on('addToQueue')
def addToQueue(data): def addToQueue(data):
result = app.addToQueue(session['dz'], data['url'], data['bitrate'], interface=socket_interface, ack=data.get('ack')) result = app.addToQueue(session['dz'], data['url'], data['bitrate'], interface=socket_interface, ack=data.get('ack'))
if result == "Not logged in":
emit('loginNeededToDownload')
@socketio.on('removeFromQueue') @socketio.on('removeFromQueue')
def removeFromQueue(uuid): def removeFromQueue(uuid):
app.removeFromQueue(uuid, interface=socket_interface) app.removeFromQueue(uuid, interface=socket_interface)
@socketio.on('removeFinishedDownloads') @socketio.on('removeFinishedDownloads')
def removeFinishedDownloads(): def removeFinishedDownloads():
app.removeFinishedDownloads(interface=socket_interface) app.removeFinishedDownloads(interface=socket_interface)
@socketio.on('cancelAllDownloads') @socketio.on('cancelAllDownloads')
def cancelAllDownloads(): def cancelAllDownloads():
app.cancelAllDownloads(interface=socket_interface) app.cancelAllDownloads(interface=socket_interface)
@socketio.on('saveSettings') @socketio.on('saveSettings')
def saveSettings(settings, spotifyCredentials, spotifyUser): def saveSettings(settings, spotifyCredentials, spotifyUser):
app.saveSettings(settings, session['dz']) app.saveSettings(settings, session['dz'])
@ -323,7 +248,6 @@ def saveSettings(settings, spotifyCredentials, spotifyUser):
if spotifyUser != False: if spotifyUser != False:
emit('updated_userSpotifyPlaylists', app.updateUserSpotifyPlaylists(spotifyUser)) emit('updated_userSpotifyPlaylists', app.updateUserSpotifyPlaylists(spotifyUser))
@socketio.on('getTracklist') @socketio.on('getTracklist')
def getTracklist(data): def getTracklist(data):
if data['type'] == 'artist': if data['type'] == 'artist':
@ -355,8 +279,6 @@ def getTracklist(data):
@socketio.on('analyzeLink') @socketio.on('analyzeLink')
def analyzeLink(link): def analyzeLink(link):
if 'deezer.page.link' in link:
link = urlopen(link).url
(type, data) = app.analyzeLink(session['dz'], link) (type, data) = app.analyzeLink(session['dz'], link)
if len(data): if len(data):
emit('analyze_'+type, data) emit('analyze_'+type, data)
@ -411,7 +333,7 @@ def selectDownloadFolder():
emit('downloadFolderSelected', result) emit('downloadFolderSelected', result)
else: else:
print("Can't open folder selection, you're not running the gui") print("Can't open folder selection, you're not running the gui")
def doSelectDowloadFolder(): def doSelectDowloadFolder():
gui.selectDownloadFolder_trigger.emit() gui.selectDownloadFolder_trigger.emit()
gui._selectDownloadFolder_semaphore.acquire() gui._selectDownloadFolder_semaphore.acquire()
@ -429,41 +351,42 @@ def applogin():
emit('logged_in', {'status': 2, 'user': session['dz'].user}) emit('logged_in', {'status': 2, 'user': session['dz'].user})
else: else:
print("Can't open login page, you're not running the gui") print("Can't open login page, you're not running the gui")
def dologin(): def dologin():
gui.appLogin_trigger.emit() gui.appLogin_trigger.emit()
gui._appLogin_semaphore.acquire() gui._appLogin_semaphore.acquire()
return gui.arl return gui.arl
def run_server(port, host="127.0.0.1", portable=None, mainWindow=None): def run_server(host="127.0.0.1", port=6595, portable=None, guiWindow=None, server_arl=False):
global app, gui, arl, is_deezer_available global app, gui, arl
app = deemix(portable) app = deemix(portable)
gui = mainWindow gui = guiWindow
if serverwide_arl: if server_arl:
print("Server-wide ARL enabled.")
arl = app.getConfigArl() arl = app.getConfigArl()
check_for_updates()
if not check_deezer_availability():
is_deezer_available = False
print("Deezer is not available in your country, you should use a VPN to use this app.")
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)
def shutdown_handler(signalnum, frame):
shutdown()
if __name__ == '__main__': if __name__ == '__main__':
port = 6595
host = "127.0.0.1" host = "127.0.0.1"
portable = None port = 6595
if len(sys.argv) >= 2: if len(sys.argv) >= 2:
try: try:
port = int(sys.argv[1]) port = int(sys.argv[1])
except ValueError: except ValueError:
pass pass
portable = None
if '--portable' in sys.argv: if '--portable' in sys.argv:
portable = path.join(path.dirname(path.realpath(__file__)), 'config') portable = path.join(path.dirname(path.realpath(__file__)), 'config')
if '--host' in sys.argv: if '--host' in sys.argv:
host = str(sys.argv[sys.argv.index("--host")+1]) host = str(sys.argv[sys.argv.index("--host")+1])
serverwide_arl = "--serverwide-arl" in sys.argv
signal.signal(signal.SIGINT, shutdown_handler) signal.signal(signal.SIGINT, shutdown_handler)
signal.signal(signal.SIGTERM, shutdown_handler) signal.signal(signal.SIGTERM, shutdown_handler)
run_server(port, host, portable) run_server(host, port, portable, server_arl=serverwide_arl)