deemix-gui-pyweb/server.py

349 lines
12 KiB
Python
Raw Normal View History

2020-05-29 16:54:35 +00:00
#!/usr/bin/env python3
import logging
import sys
import subprocess
from os import path
import json
2020-04-07 22:19:27 +00:00
from flask import Flask, render_template, request, session, redirect, copy_current_request_context
from flask_socketio import SocketIO, emit
2020-06-14 21:06:32 +00:00
from werkzeug.middleware.proxy_fix import ProxyFix
import app
from deemix.api.deezer import Deezer
from deemix.app.MessageInterface import MessageInterface
# Workaround for MIME type error in certain Windows installs
# https://github.com/pallets/flask/issues/1045#issuecomment-42202749
import mimetypes
mimetypes.add_type('text/css', '.css')
mimetypes.add_type('text/javascript', '.js')
2020-04-07 22:19:27 +00:00
# Makes engineio accept more packets from client, needed for long URL lists in addToQueue requests
# https://github.com/miguelgrinberg/python-engineio/issues/142#issuecomment-545807047
from engineio.payload import Payload
Payload.max_decode_packets = 500
2020-04-08 16:43:35 +00:00
class CustomFlask(Flask):
jinja_options = Flask.jinja_options.copy()
jinja_options.update(dict(
block_start_string='$$',
block_end_string='$$',
variable_start_string='$',
variable_end_string='$',
comment_start_string='$#',
comment_end_string='#$',
))
2020-04-08 16:43:35 +00:00
2020-05-07 12:24:44 +00:00
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__)))
2020-05-07 12:24:44 +00:00
return path.join(base_path, relative_path)
2020-05-25 10:58:41 +00:00
gui_dir = resource_path(path.join('webui', 'public'))
2020-04-08 16:43:35 +00:00
server = CustomFlask(__name__, static_folder=gui_dir, template_folder=gui_dir)
2020-04-07 22:19:27 +00:00
server.config['SEND_FILE_MAX_AGE_DEFAULT'] = 1 # disable caching
2020-05-19 19:14:46 +00:00
socketio = SocketIO(server, async_mode='threading')
2020-06-14 21:06:32 +00:00
server.wsgi_app = ProxyFix(server.wsgi_app, x_for=1, x_proto=1)
class SocketInterface(MessageInterface):
def send(self, message, value=None):
if value:
socketio.emit(message, value)
else:
socketio.emit(message)
socket_interface = SocketInterface()
loginWindow = False
serverLog = logging.getLogger('werkzeug')
serverLog.disabled = True
logging.getLogger('socketio').setLevel(logging.ERROR)
logging.getLogger('engineio').setLevel(logging.ERROR)
#server.logger.disabled = True
2020-04-07 22:19:27 +00:00
firstConnection = True
2020-04-13 17:22:34 +00:00
2020-04-07 22:19:27 +00:00
@server.route('/')
def landing():
return render_template('index.html')
2020-07-27 21:52:53 +00:00
@server.errorhandler(404)
def not_found_handler(e):
return redirect("/")
2020-04-07 22:19:27 +00:00
@server.route('/shutdown')
def closing():
app.shutdown(interface=socket_interface)
2020-05-15 13:42:47 +00:00
socketio.stop()
return 'server closed'
serverwide_arl = "--serverwide-arl" in sys.argv
if serverwide_arl:
print("Server-wide ARL enabled.")
@socketio.on('connect')
def on_connect():
session['dz'] = Deezer()
settings = app.getSettings_link()
spotifyCredentials = app.getSpotifyCredentials()
defaultSettings = app.getDefaultSettings_link()
emit('init_settings', (settings, spotifyCredentials, defaultSettings))
emit('init_autologin')
2020-05-26 10:22:32 +00:00
arl_file_path = path.join(app.configFolder, '.arl')
if serverwide_arl and path.isfile(arl_file_path):
with open(arl_file_path, 'r') as file:
2020-05-07 14:57:23 +00:00
arl = file.readline().rstrip("\n")
2020-05-06 08:24:32 +00:00
login(arl)
queue, queueComplete, queueList, currentItem = app.initDownloadQueue()
emit('init_downloadQueue',
{'queue': queue, 'queueComplete': queueComplete, 'queueList': queueList, 'currentItem': currentItem})
2020-04-29 08:36:30 +00:00
emit('init_home', session['dz'].get_charts())
2020-05-03 13:52:42 +00:00
emit('init_charts', app.get_charts(session['dz']))
2020-04-11 19:55:12 +00:00
@socketio.on('login')
def login(arl, force=False, child=0):
global firstConnection
2020-05-30 19:01:26 +00:00
if child == None:
child = 0
2020-05-31 10:12:45 +00:00
arl = arl.strip()
2020-07-29 14:30:31 +00:00
emit('logging_in')
if not session['dz'].logged_in:
2020-05-30 19:01:26 +00:00
result = session['dz'].login_via_arl(arl, int(child))
else:
if force:
session['dz'] = Deezer()
2020-05-30 19:01:26 +00:00
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})
if firstConnection and result in [1, 3]:
firstConnection = False
app.loadDownloadQueue(session['dz'], socket_interface)
if result != 0:
emit('familyAccounts', session['dz'].childs)
emit('init_favorites', app.getUserFavorites(session['dz']))
@socketio.on('changeAccount')
def changeAccount(child):
2020-05-30 19:01:26 +00:00
emit('accountChanged', session['dz'].change_account(int(child)))
emit('init_favorites', app.getUserFavorites(session['dz']))
2020-04-12 22:14:34 +00:00
@socketio.on('logout')
def logout():
status = 0
if session['dz'].logged_in:
session['dz'] = Deezer()
status = 0
else:
status = 1
emit('logged_out', status)
2020-04-12 22:14:34 +00:00
@socketio.on('mainSearch')
def mainSearch(data):
if data['term'].strip() != "":
emit('mainSearch', app.mainSearch(session['dz'], data['term']))
2020-04-09 14:06:33 +00:00
@socketio.on('search')
def search(data):
if data['term'].strip() != "":
result = app.search(session['dz'], data['term'], data['type'], data['start'], data['nb'])
result['type'] = data['type']
emit('search', result)
2020-04-08 21:52:08 +00:00
@socketio.on('addToQueue')
def addToQueue(data):
result = app.addToQueue_link(session['dz'], data['url'], data['bitrate'], interface=socket_interface)
if result == "Not logged in":
emit('loginNeededToDownload')
2020-04-08 21:52:08 +00:00
2020-04-11 13:43:59 +00:00
@socketio.on('removeFromQueue')
def removeFromQueue(uuid):
app.removeFromQueue_link(uuid, interface=socket_interface)
2020-04-11 13:43:59 +00:00
2020-04-14 14:48:13 +00:00
@socketio.on('removeFinishedDownloads')
def removeFinishedDownloads():
app.removeFinishedDownloads_link(interface=socket_interface)
2020-04-14 14:48:13 +00:00
@socketio.on('cancelAllDownloads')
def cancelAllDownloads():
app.cancelAllDownloads_link(interface=socket_interface)
2020-04-14 14:48:13 +00:00
2020-04-14 17:58:54 +00:00
@socketio.on('saveSettings')
def saveSettings(settings, spotifyCredentials, spotifyUser):
app.saveSettings_link(settings)
app.setSpotifyCredentials(spotifyCredentials)
socketio.emit('updateSettings', (settings, spotifyCredentials))
if spotifyUser != False:
emit('updated_userSpotifyPlaylists', app.updateUserSpotifyPlaylists(spotifyUser))
2020-04-07 22:19:27 +00:00
2020-04-17 17:39:51 +00:00
@socketio.on('getTracklist')
def getTracklist(data):
if data['type'] == 'artist':
artistAPI = session['dz'].get_artist(data['id'])
artistAPI['releases'] = session['dz'].get_artist_discography_gw(data['id'], 100)
emit('show_artist', artistAPI)
elif data['type'] == 'spotifyplaylist':
playlistAPI = app.getSpotifyPlaylistTracklist(data['id'])
for i in range(len(playlistAPI['tracks'])):
playlistAPI['tracks'][i] = playlistAPI['tracks'][i]['track']
playlistAPI['tracks'][i]['selected'] = False
emit('show_spotifyplaylist', playlistAPI)
else:
releaseAPI = getattr(session['dz'], 'get_' + data['type'])(data['id'])
releaseTracksAPI = getattr(session['dz'], 'get_' + data['type'] + '_tracks')(data['id'])['data']
tracks = []
showdiscs = False
if data['type'] == 'album' and len(releaseTracksAPI) and releaseTracksAPI[-1]['disk_number'] != 1:
current_disk = 0
showdiscs = True
for track in releaseTracksAPI:
if showdiscs and int(track['disk_number']) != current_disk:
current_disk = int(track['disk_number'])
tracks.append({'type': 'disc_separator', 'number': current_disk})
track['selected'] = False
tracks.append(track)
releaseAPI['tracks'] = tracks
emit('show_' + data['type'], releaseAPI)
@socketio.on('analyzeLink')
def analyzeLink(link):
(type, data) = app.analyzeLink(session['dz'], link)
if len(data):
emit('analyze_'+type, data)
else:
emit('analyze_notSupported')
2020-04-17 17:39:51 +00:00
2020-05-03 13:52:42 +00:00
@socketio.on('getChartTracks')
def getChartTracks(id):
emit('setChartTracks', session['dz'].get_playlist_tracks(id)['data'])
2020-06-10 16:46:08 +00:00
@socketio.on('update_userFavorites')
def update_userFavorites():
emit('updated_userFavorites', app.getUserFavorites(session['dz']))
@socketio.on('update_userSpotifyPlaylists')
def update_userSpotifyPlaylists(spotifyUser):
if spotifyUser != False:
emit('updated_userSpotifyPlaylists', app.updateUserSpotifyPlaylists(spotifyUser))
@socketio.on('update_userPlaylists')
def update_userPlaylists():
emit('updated_userPlaylists', app.updateUserPlaylists(session['dz']))
@socketio.on('update_userAlbums')
def update_userAlbums():
emit('updated_userAlbums', app.updateUserAlbums(session['dz']))
@socketio.on('update_userArtists')
def update_userArtists():
emit('updated_userArtists', app.updateUserArtists(session['dz']))
@socketio.on('update_userTracks')
def update_userTracks():
emit('updated_userTracks', app.updateUserTracks(session['dz']))
@socketio.on('openDownloadsFolder')
def openDownloadsFolder():
folder = app.getDownloadFolder()
if sys.platform == 'darwin':
subprocess.check_call(['open', folder])
elif sys.platform == 'linux':
subprocess.check_call(['xdg-open', folder])
elif sys.platform == 'win32':
subprocess.check_call(['explorer', folder])
@socketio.on('selectDownloadFolder')
def selectDownloadFolder():
try:
import webview
result = webview.windows[0].create_file_dialog(webview.FOLDER_DIALOG, allow_multiple=False)
if result:
emit('downloadFolderSelected', result)
except:
print("Can't open folder selection, you're not running pywebview")
@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"
2020-08-12 17:25:44 +00:00
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:
2020-08-12 17:25:44 +00:00
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):
2020-05-26 10:22:32 +00:00
app.initialize(portable)
print("Starting server at http://" + host + ":" + str(port))
2020-06-03 10:59:43 +00:00
socketio.run(server, host=host, port=port)
2020-04-07 22:19:27 +00:00
if __name__ == '__main__':
port = 6595
host = "127.0.0.1"
portable = None
if len(sys.argv) >= 2:
try:
port = int(sys.argv[1])
except ValueError:
pass
2020-05-26 10:22:32 +00:00
if '--portable' in sys.argv:
portable = path.join(path.dirname(path.realpath(__file__)), 'config')
2020-06-03 10:59:43 +00:00
if '--host' in sys.argv:
host = str(sys.argv[sys.argv.index("--host")+1])
run_server(port, host, portable)