diff --git a/api.py b/api.py index 5155fc4..b5e2d70 100644 --- a/api.py +++ b/api.py @@ -1,9 +1,10 @@ -import requests, urllib +import requests, json +from urllib.parse import urlencode from functools import wraps -from pathlib import Path +import api -# (TODO)Move to config? -BASE_URL = Path('http://bokhylle.pvv.ntnu.no:8080/api') +# This will be overwritten by config +BASE_URL = "http://localhost:8080/api" # Exceptions: class APIError(Exception): pass @@ -13,56 +14,57 @@ def request_post(func): @wraps(func) def new_func(*args, **kwargs): url, data = func(*args, **kwargs) - response = requests.post(url, data=data) - json = json.loads(response.text) - if "error" not in json or json["error"] != False: - raise APIError(json["error_msg"]) - return json["success"] + response = requests.post(f"{BASE_URL}/{url}", data=data) + data = json.loads(response.text) + if "error" not in data or data["error"] != False: + print(data) + raise APIError(data["error_msg"]) + return data["success"] return new_func def request_get(func): @wraps(func) def new_func(*args, **kwargs): url = func(*args, **kwargs) - response = requests.get(url) - json = json.loads(response.text) - if "error" not in json or json["error"] != False: - raise APIError(json["error_msg"]) - return json["value"] + response = requests.get(f"{BASE_URL}/{url}") + data = json.loads(response.text) + if "error" not in data or data["error"] != False: + raise APIError(data["error_msg"]) + return data["value"] return new_func # methods: @request_post -def is_playing(path:str): - args = urllib.urlencode(locals()) - return BASE_URL / f"play?{args}", None +def load_path(path:str): + args = urlencode(locals()) + return f"load?{args}", None @request_get def is_playing(): - return BASE_URL / f"play" + return f"play" @request_post def set_playing(play:bool): - args = urllib.urlencode(locals()) - return BASE_URL / f"play?{args}", None + args = urlencode(locals()) + return f"play?{args}", None @request_get def get_volume(): - return BASE_URL / f"volume" + return f"volume" @request_post def set_volume(volume:int):# between 0 and 100 (you may also exceed 100) - args = urllib.urlencode(locals()) - return BASE_URL / f"volume?{args}", None + args = urlencode(locals()) + return f"volume?{args}", None @request_get def get_playlist(): - return BASE_URL / f"playlist" + return f"playlist" @request_post def playlist_next(): - return BASE_URL / f"playlist/next", None + return f"playlist/next", None @request_post def playlist_previous(): - return BASE_URL / f"playlist/previous", None + return f"playlist/previous", None diff --git a/config.py b/config.py index 37c448b..d7b018d 100644 --- a/config.py +++ b/config.py @@ -1,4 +1,5 @@ host = "0.0.0.0" port = 8080 -start_browser = False +start_browser = True multiple_instance = True +api_base = "http://bokhylle.pvv.ntnu.no:8080/api" diff --git a/gui.py b/gui.py new file mode 100644 index 0000000..86ed007 --- /dev/null +++ b/gui.py @@ -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) diff --git a/main.py b/main.py index aaa411d..cac6f8f 100755 --- a/main.py +++ b/main.py @@ -1,117 +1,7 @@ #!/usr/bin/env python3 -import random, os, time, shutil, sys -from threading import Timer -import remi.gui as 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 - +from remi import start +import api +import gui # config must be a object with the attributes:: # config.host: str @@ -125,8 +15,9 @@ def main(config): assert hasattr(config, "multiple_instance") # start the webserver: + api.BASE_URL = config.api_base start( - MyApp, + gui.MyApp, title = "Gregorz", address = config.host, port = config.port, diff --git a/utils.py b/utils.py index 27751ee..9f8a5aa 100644 --- a/utils.py +++ b/utils.py @@ -1,3 +1,4 @@ +from functools import wraps import threading import youtube_dl @@ -17,7 +18,8 @@ def get_youtube_metadata(url, ydl = youtube_dl.YoutubeDL()): return title, "%i:%.2i" % (length//60, length%60) # decorator: -def call_as_thread(func): +def call_as_thread(func): # This will discard any return value! + @wraps(func) def new_func(*args, **kwargs): threading.Thread( target = func,