Make play, pause, next, previous, load and playlist view functional

This commit is contained in:
Peder Bergebakken Sundt 2018-02-27 00:06:15 +01:00
parent 9c6f59035d
commit 30d3120a09
5 changed files with 165 additions and 142 deletions

54
api.py
View File

@ -1,9 +1,10 @@
import requests, urllib import requests, json
from urllib.parse import urlencode
from functools import wraps from functools import wraps
from pathlib import Path import api
# (TODO)Move to config? # This will be overwritten by config
BASE_URL = Path('http://bokhylle.pvv.ntnu.no:8080/api') BASE_URL = "http://localhost:8080/api"
# Exceptions: # Exceptions:
class APIError(Exception): pass class APIError(Exception): pass
@ -13,56 +14,57 @@ def request_post(func):
@wraps(func) @wraps(func)
def new_func(*args, **kwargs): def new_func(*args, **kwargs):
url, data = func(*args, **kwargs) url, data = func(*args, **kwargs)
response = requests.post(url, data=data) response = requests.post(f"{BASE_URL}/{url}", data=data)
json = json.loads(response.text) data = json.loads(response.text)
if "error" not in json or json["error"] != False: if "error" not in data or data["error"] != False:
raise APIError(json["error_msg"]) print(data)
return json["success"] raise APIError(data["error_msg"])
return data["success"]
return new_func return new_func
def request_get(func): def request_get(func):
@wraps(func) @wraps(func)
def new_func(*args, **kwargs): def new_func(*args, **kwargs):
url = func(*args, **kwargs) url = func(*args, **kwargs)
response = requests.get(url) response = requests.get(f"{BASE_URL}/{url}")
json = json.loads(response.text) data = json.loads(response.text)
if "error" not in json or json["error"] != False: if "error" not in data or data["error"] != False:
raise APIError(json["error_msg"]) raise APIError(data["error_msg"])
return json["value"] return data["value"]
return new_func return new_func
# methods: # methods:
@request_post @request_post
def is_playing(path:str): def load_path(path:str):
args = urllib.urlencode(locals()) args = urlencode(locals())
return BASE_URL / f"play?{args}", None return f"load?{args}", None
@request_get @request_get
def is_playing(): def is_playing():
return BASE_URL / f"play" return f"play"
@request_post @request_post
def set_playing(play:bool): def set_playing(play:bool):
args = urllib.urlencode(locals()) args = urlencode(locals())
return BASE_URL / f"play?{args}", None return f"play?{args}", None
@request_get @request_get
def get_volume(): def get_volume():
return BASE_URL / f"volume" return f"volume"
@request_post @request_post
def set_volume(volume:int):# between 0 and 100 (you may also exceed 100) def set_volume(volume:int):# between 0 and 100 (you may also exceed 100)
args = urllib.urlencode(locals()) args = urlencode(locals())
return BASE_URL / f"volume?{args}", None return f"volume?{args}", None
@request_get @request_get
def get_playlist(): def get_playlist():
return BASE_URL / f"playlist" return f"playlist"
@request_post @request_post
def playlist_next(): def playlist_next():
return BASE_URL / f"playlist/next", None return f"playlist/next", None
@request_post @request_post
def playlist_previous(): def playlist_previous():
return BASE_URL / f"playlist/previous", None return f"playlist/previous", None

View File

@ -1,4 +1,5 @@
host = "0.0.0.0" host = "0.0.0.0"
port = 8080 port = 8080
start_browser = False start_browser = True
multiple_instance = True multiple_instance = True
api_base = "http://bokhylle.pvv.ntnu.no:8080/api"

127
gui.py Normal file
View File

@ -0,0 +1,127 @@
import random, os, time, shutil, sys
from threading import Timer
import remi.gui as gui
from remi import App
from utils import Namespace, call_as_thread, get_youtube_metadata
import api
#globals:
COLOR_BLUE = "rgb(33, 150, 243)"
COLOR_BLUE_SHADOW = "rgba(33, 150, 243, 0.75)"
class MyApp(App):
def __init__(self, *args):
res_path = os.path.join(os.path.dirname(__file__), 'res')
super(MyApp, self).__init__(*args, static_file_path=res_path)
def main(self):
container = gui.VBox(width=512)
container.style["margin-left"] = "auto"
container.style["margin-right"] = "auto"
#logo:
container.append(gui.Image('/res/logo.jpg', width=512))
#playback controls
playbackContainer = gui.HBox()#; container.append(playbackContainer)
self.playback = Namespace()
for i in ("previous", "play", "next"):
button = gui.Button(i.capitalize(), margin="5px")
setattr(self.playback, i, button)
playbackContainer.append(button)
button.set_on_click_listener(getattr(self,'playback_%s' % i))
self.playback.playing = gui.Label("Now playing: None")
self.playback.slider = gui.Slider(0, 0, 100, 1, width="85%", height=20, margin='10px')
container.append(self.playback.playing)
container.append(playbackContainer)
container.append(self.playback.slider)
#playlist
self.playlist = Namespace()
self.playlist.table = gui.Table(width="100%", margin="10px")
self.playlist.table.append_from_list([['#', 'Name', "length"]], fill_title=True)
container.append(self.playlist.table)
#input
container.append(gui.Label("Add songs:"))
inputContainer = gui.HBox(width=512)
self.input = Namespace()
self.input.field = gui.TextInput(single_line=True, height="20px", margin="5px")
self.input.field.style["border"] = "1px solid %s" % COLOR_BLUE
self.input.field.style["box-shadow"] = "0px 0px 5px 0px %s" % COLOR_BLUE_SHADOW
self.input.submit = gui.Button("Submit!", margin="5px")
self.input.field.set_on_enter_listener(self.input_submit)
self.input.submit.set_on_click_listener(self.input_submit)
inputContainer.append(self.input.field)
inputContainer.append(self.input.submit)
container.append(inputContainer)
#return the container
self.mainLoop()
return container
def mainLoop(self):
#self.playback.slider.get_value()
self.playback_update()
self.playlist_update()
Timer(0.7, self.mainLoop).start()
# events:
@call_as_thread
def playback_previous(self, widget):
api.playlist_previous()
@call_as_thread
def playback_play(self, widget):# toggle playblack
if api.is_playing():
self.playback.play.set_text("Play")
api.set_playing(False)
else:
self.playback.play.set_text("Pause")
api.set_playing(True)
@call_as_thread
def playback_next(self, widget):
api.playlist_next()
@call_as_thread
def input_submit(self, widget, value=None):
if value is None:
value = self.input.field.get_text()
self.input.field.set_text("")
# (TODO):
# title, length = utils.get_youtube_metadata(value)
api.load_path(value)
# playback steps:
@call_as_thread
def playback_update(self):
play_label = "Pause" if api.is_playing() else "Play"
playback_pos = random.randrange(0,100)
#print(dir(self.playback.play))
self.playback.play.set_text(play_label)
self.playback.slider.set_value(playback_pos)
@call_as_thread
def volume_update(self):
self.volume.slider.set_value(api.get_volume())
@call_as_thread
def playlist_update(self):
playlist = api.get_playlist()
table = []
for item in playlist:
table.append([
item["index"],
item["filename"],
"05:00" + ("#"*("current" in item))
])
self.playlist.table.empty(keep_title=True)
self.playlist.table.append_from_list(table)

119
main.py
View File

@ -1,117 +1,7 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import random, os, time, shutil, sys from remi import start
from threading import Timer import api
import remi.gui as gui import gui
from remi import start, App
from utils import Namespace
#globals:
COLOR_BLUE = "rgb(33, 150, 243)"
COLOR_BLUE_SHADOW = "rgba(33, 150, 243, 0.75)"
class MyApp(App):
def __init__(self, *args):
res_path = os.path.join(os.path.dirname(__file__), 'res')
super(MyApp, self).__init__(*args, static_file_path=res_path)
def main(self):
container = gui.VBox(width=512)
container.style["margin-left"] = "auto"
container.style["margin-right"] = "auto"
#logo:
container.append(gui.Image('/res/logo.jpg', width=512))
#playback controls
playbackContainer = gui.HBox()#; container.append(playbackContainer)
self.playback = Namespace()
for i in ("previous", "play", "next"):
button = gui.Button(i.capitalize(), margin="5px")
setattr(self.playback, i, button)
playbackContainer.append(button)
button.set_on_click_listener(getattr(self,'playback_%s' % i))
self.playback.playing = gui.Label("Now playing: None")
self.playback.slider = gui.Slider(0, 0, 100, 1, width="85%", height=20, margin='10px')
container.append(self.playback.playing)
container.append(playbackContainer)
container.append(self.playback.slider)
#playlist
self.playlist = Namespace()
self.playlist.table = gui.Table(width="100%", margin="10px")
self.playlist.table.append_from_list([['#', 'Name', "length"]], fill_title=True)
container.append(self.playlist.table)
self.playlist.queue = []#[i] = [source, name, length]
#input
container.append(gui.Label("Add songs:"))
inputContainer = gui.HBox(width=512)
self.input = Namespace()
self.input.field = gui.TextInput(single_line=True, height="20px", margin="5px")
self.input.field.style["border"] = "1px solid %s" % COLOR_BLUE
self.input.field.style["box-shadow"] = "0px 0px 5px 0px %s" % COLOR_BLUE_SHADOW
self.input.submit = gui.Button("Submit!", margin="5px")
self.input.field.set_on_enter_listener(self.input_submit)
self.input.submit.set_on_click_listener(self.input_submit)
inputContainer.append(self.input.field)
inputContainer.append(self.input.submit)
container.append(inputContainer)
#return the container
self.mainLoop()
return container
def mainLoop(self):
#self.playback.slider.get_value()
self.playback_update()
self.playlist.table.empty(keep_title=True)
self.playlist.table.append_from_list(self.playlist_update())
Timer(0.7, self.mainLoop).start()
# events:
def playback_previous(self, widget): pass
def playback_play(self, widget):# toggle playblack
pass
def playback_next(self, widget):
source, name, length = self.playlist.queue.pop(0)
pass
def input_submit(self, widget, value=None):
if not value:
value = self.input.field.get_text()
self.input.field.set_text("")
title, length = get_youtube_metadata(value)
self.playlist.queue.append([value, title, length])
# playback steps:
def playback_update(self):
#talk to mpv, see wether the song is being played still
if 0:#if done:
self.playback_next()
self.playback.slider.set_value(0)
else:
self.playback.slider.set_value(100)
return
def playlist_update(self):
#out = [['#', 'Name', "length"]]
out = []
for i, (source, name, length) in enumerate(self.playlist.queue):
out.append([str(i+1), name, length])
return out
# config must be a object with the attributes:: # config must be a object with the attributes::
# config.host: str # config.host: str
@ -125,8 +15,9 @@ def main(config):
assert hasattr(config, "multiple_instance") assert hasattr(config, "multiple_instance")
# start the webserver: # start the webserver:
api.BASE_URL = config.api_base
start( start(
MyApp, gui.MyApp,
title = "Gregorz", title = "Gregorz",
address = config.host, address = config.host,
port = config.port, port = config.port,

View File

@ -1,3 +1,4 @@
from functools import wraps
import threading import threading
import youtube_dl import youtube_dl
@ -17,7 +18,8 @@ def get_youtube_metadata(url, ydl = youtube_dl.YoutubeDL()):
return title, "%i:%.2i" % (length//60, length%60) return title, "%i:%.2i" % (length//60, length%60)
# decorator: # decorator:
def call_as_thread(func): def call_as_thread(func): # This will discard any return value!
@wraps(func)
def new_func(*args, **kwargs): def new_func(*args, **kwargs):
threading.Thread( threading.Thread(
target = func, target = func,