From 1c7cf54af2cb837b1b160d90ae8666bc1b3e09a2 Mon Sep 17 00:00:00 2001 From: Jeff Kaufman Date: Thu, 28 Jan 2021 02:08:45 +0000 Subject: [PATCH] add image upload support --- html/demo.js | 64 ++++++++++++++++++++++++++++++++++++++++++++++++- html/index.html | 12 ++++++++++ server.py | 10 ++++++++ upload.py | 22 +++++++++++++---- util.py | 4 +++- 5 files changed, 105 insertions(+), 7 deletions(-) diff --git a/html/demo.js b/html/demo.js index 7f813a9..2ea3082 100644 --- a/html/demo.js +++ b/html/demo.js @@ -333,6 +333,10 @@ function takeLeadClick() { leadButtonState = "stop-singing"; window.backingTrack.style.display = "none"; window.provideLyrics.style.display = "none"; + window.uploadImage.style.display = "none"; + window.imageUploader.style.display = "none"; + window.imageUploadOk.style.display = "none"; + window.imageUploadError.style.display = "none"; window.uploadBackingTrack.style.display = "none"; window.backingTrackUploader.style.display = "none"; window.backingTrackUploadOk.style.display = "none"; @@ -378,7 +382,7 @@ window.backingTrackUploader.addEventListener("change", () => { } const xhr = new XMLHttpRequest(); - xhr.open('POST', server_upload_path(), true); + xhr.open('POST', server_upload_path() + "?type=backingTrack", true); xhr.onreadystatechange = function () { if (this.readyState === XMLHttpRequest.DONE) { window.uploadSpinner.style.display = "none"; @@ -399,6 +403,50 @@ window.backingTrackUploader.addEventListener("change", () => { reader.readAsArrayBuffer(window.backingTrackUploader.files[0]); }); +window.uploadImage.addEventListener("click", () => { + window.imageUploader.style.display = "inline-block"; + window.uploadImage.style.display = "none"; + window.imageUploadOk.style.display = "none"; + window.imageUploadError.style.display = "none"; +}); + +window.imageUploader.addEventListener("change", () => { + if (!window.imageUploader.files.length) { + return; + } + + window.uploadSpinner.style.display = "flex"; + const reader = new FileReader(); + reader.onload = () => { + if (reader.result.byteLength > 16*1000*1000) { + window.imageUploadError.innerText = "Only files under 16MB are supported."; + window.imageUploadError.style.display = "inline-block"; + window.uploadSpinner.style.display = "none"; + return; + } + + const xhr = new XMLHttpRequest(); + xhr.open('POST', server_upload_path() + "?type=image", true); + xhr.onreadystatechange = function () { + if (this.readyState === XMLHttpRequest.DONE) { + window.uploadSpinner.style.display = "none"; + window.uploadImage.style.display = "inline-block"; + window.imageUploader.style.display = "none"; + window.imageUploader.value = null; + if (this.status === 200) { + window.imageUploadOk.style.display = "inline-block"; + singer_client.x_send_metadata("image", "upload"); + } else { + window.imageUploadError.style.display = "inline-block"; + window.imageUploadError.innerText = "Server rejected image."; + } + } + }; + xhr.send(reader.result); + }; + reader.readAsArrayBuffer(window.imageUploader.files[0]); +}); + window.provideLyrics.addEventListener("click", () => { window.lyricsEntry.style.display = "block"; }); @@ -645,6 +693,10 @@ function set_controls() { window.backingTrack.display = "none"; window.provideLyrics.style.display = "none"; + window.uploadImage.style.display = "none"; + window.imageUploader.style.display = "none"; + window.imageUploadOk.style.display = "none"; + window.imageUploadError.style.display = "none"; window.uploadBackingTrack.style.display = "none"; window.backingTrackUploader.style.display = "none"; window.backingTrackUploadOk.style.display = "none"; @@ -1012,10 +1064,12 @@ function update_active_users( window.backingTrack.style.display = "inline-block"; window.backingTrack.selectedIndex = 0; window.provideLyrics.style.display = "inline-block"; + window.uploadImage.style.display = "inline-block"; window.uploadBackingTrack.style.display = "inline-block"; } else if (!imLeading) { window.backingTrack.style.display = "none"; window.provideLyrics.style.display = "none"; + window.uploadImage.style.display = "none"; window.uploadBackingTrack.style.display = "none"; if (hasLeader && song_active()) { window.takeLead.textContent = "Halt Song"; @@ -1641,6 +1695,13 @@ async function start_singing() { if (metadata["lyrics"]) { window.lyricsDisplay.value = metadata["lyrics"]; window.lyricsDisplay.style.display = "block"; + window.imageDisplay.style.display = "none"; + } + + if (metadata["image"]) { + window.imageDisplayImg.src = "user-upload-image?" + metadata["image"]; + window.imageDisplay.style.display = "block"; + window.lyricsDisplay.style.display = "none"; } let startSingingCountdown = null; @@ -1740,6 +1801,7 @@ async function start_singing() { if (!showBuckets) { window.lyricsDisplay.style.display = "none"; + window.imageDisplay.style.display = "none"; } const showBucketingGuide = hasLeader && !song_active(); diff --git a/html/index.html b/html/index.html index 339a5b3..f55124b 100644 --- a/html/index.html +++ b/html/index.html @@ -96,6 +96,10 @@ #backingTrack { display: none; } #provideLyrics { display: none; } +#uploadImage { display: none; } +#imageUploader { display: none; } +#imageUploadOk { display: none; } +#imageUploadError { display: none; } #uploadBackingTrack { display: none; } #backingTrackUploader { display: none; } #backingTrackUploadOk { display: none; } @@ -1064,6 +1068,10 @@

Something else weird is going on

+ + + Image Uploaded. + Image Upload Failed. Track Uploaded. @@ -1102,6 +1110,10 @@

Something else weird is going on

+ +
+ +
diff --git a/server.py b/server.py index 0e95a06..2d11106 100755 --- a/server.py +++ b/server.py @@ -18,6 +18,7 @@ import subprocess import copy import sys +import string from typing import Any, Dict, List, Tuple, Iterable @@ -112,6 +113,7 @@ def reset(self): self.disable_song_video = False self.lyrics = "" + self.image = None if recorder: recorder.reset() @@ -367,6 +369,8 @@ def __init__(self, userid, name, last_heard_server_clock, delay_samples) -> None self.send("disableSongVideo", state.disable_song_video) if state.lyrics: self.send("lyrics", state.lyrics) + if state.image: + self.send("image", state.image) def allocate_twilio_token(self): token = AccessToken(secrets["twilio"]["account_sid"], @@ -1076,12 +1080,18 @@ def handle_post(in_json, in_data) -> Tuple[Any, str]: state.lyrics = in_json["lyrics"] sendall("lyrics", state.lyrics) + if query_params.get("image", None): + state.image = ''.join(random.choices(string.ascii_uppercase, k=10)) + sendall("image", state.image) + # If we are running under Ritual Engine, disable functionality that is not # required in that setting, and would be disruptive if triggered by # accident. if query_params.get("request_lead", None) and not state.server_controlled: assign_delays(userid) state.leader = userid + state.image = "" + sendall("image", "") state.lyrics = "" sendall("lyrics", "") diff --git a/upload.py b/upload.py index c3e27b5..f9afaa1 100644 --- a/upload.py +++ b/upload.py @@ -3,8 +3,9 @@ import subprocess import traceback import sys +import urllib.parse -def decode_and_save(in_data_raw): +def decode_and_save_backing_track(in_data_raw): with tempfile.NamedTemporaryFile() as tmp_upload: tmp_upload.write(in_data_raw) tmp_upload.flush() @@ -13,9 +14,14 @@ def decode_and_save(in_data_raw): "sox", "-t", "mp3", tmp_upload.name, "-r", "48000", - "-t", "wav", util.UPLOAD_FNAME, + "-t", "wav", util.BACKING_TRACK_UPLOAD_FNAME, "remix", "1"]) +def save_image(in_data_raw): + with open(util.IMAGE_UPLOAD_FNAME, "wb") as outf: + outf.write(in_data_raw) + outf.flush() + def application(environ, start_response): try: content_length = int(environ.get('CONTENT_LENGTH', 0)) @@ -28,10 +34,16 @@ def application(environ, start_response): else: query_params = {} - decode_and_save(in_data_raw) + uploadType, = query_params.get("type", [None]) + if uploadType == "backingTrack": + decode_and_save_backing_track(in_data_raw) + elif uploadType == "image": + save_image(in_data_raw) + else: + raise Exception("unknown uploadType %s" % uploadType) - start_response('200 OK', [("Content-Type", "text/plain")]) - return b"ok", + start_response('200 OK', [("Content-Type", "text/plain")]) + return b"ok", except Exception as e: print("ERROR:", query_string, "\n", traceback.\ format_exc(), file=sys.stderr) diff --git a/util.py b/util.py index 8e012f8..9f99bcb 100644 --- a/util.py +++ b/util.py @@ -3,7 +3,9 @@ import json AUDIO_DIR = os.path.join(os.path.dirname(__file__), "audio") -UPLOAD_FNAME = os.path.join(AUDIO_DIR, "User Upload") +BACKING_TRACK_UPLOAD_FNAME = os.path.join(AUDIO_DIR, "User Upload") +IMAGE_UPLOAD_FNAME = os.path.join( + os.path.dirname(__file__), "html", "user-upload-image") def die500(start_response, e): # This is slightly sketchy: this assumes we are currently in the middle