From 658c676be8370af09b11ae6b5ae27ac5511f12b4 Mon Sep 17 00:00:00 2001 From: arianagiroux Date: Sun, 26 Jun 2022 19:15:59 -0600 Subject: [PATCH 1/7] Initial api access functionality --- .gitignore | 3 ++- index.html | 11 ++++++--- resources/script.js | 54 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 4 deletions(-) create mode 100644 resources/script.js diff --git a/.gitignore b/.gitignore index c03ba5e..09a6a8a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /bower_components/ -/node_modules/ \ No newline at end of file +/node_modules/ +resources/data.js \ No newline at end of file diff --git a/index.html b/index.html index f9f6b80..d18f666 100644 --- a/index.html +++ b/index.html @@ -13,9 +13,14 @@
-
+
+

Current Viewers: . Session Peak Viewers: .

+

Stream Online?

+
- + + + - + \ No newline at end of file diff --git a/resources/script.js b/resources/script.js new file mode 100644 index 0000000..4c61440 --- /dev/null +++ b/resources/script.js @@ -0,0 +1,54 @@ +const headers = new Headers(); +// note, userName and streamKey are both derived in a file called data.js +headers.append('Authorization', 'Basic ' + btoa(userName + ":" + streamKey)); + +async function getEndpoint(url = '') { + const response = await fetch(url, {method:'GET', headers: headers}) + const data = await response.json() + return data + } + + async function postEndpoint(url = '', data = {}) { + const response = await fetch(url, { + method: 'POST', + body: JSON.stringify(data), // serialized data + headers: headers // authentication header + }) + return response + } + + async function getStatus() { + return getEndpoint(api_url + 'admin/status') + } + + async function updateElements(data) { + var {online, viewerCount, sessionPeakViewerCount} = data + updateViewers(viewerCount); + updateOnline(online); + updateSessionPeak(sessionPeakViewerCount); + +} + +async function updateOnline(online) { + document.getElementById('online').innerHTML = online +} +async function updateViewers(viewers) { + document.getElementById('currentViewers').innerHTML = viewers +} +async function updateSessionPeak(viewers) { + document.getElementById('sessionPeak').innerHTML = viewers +} + +async function Main() { + // update visual elements using data from 'api/admin/status' + updateElements(await getStatus()) + + // update the broadcast title + // postEndpoint(`` + // api_url + 'admin/config/streamtitle', + // {value: "testing" + Math.floor(Math.random() * 1000)} + // ).then(response => response.status) + // .then(data => console.log(data)) +} + +Main() \ No newline at end of file From 8f27f8d216bc4de260ce8f35b70e9f58700e859e Mon Sep 17 00:00:00 2001 From: arianagiroux Date: Tue, 19 Jul 2022 14:56:41 -0600 Subject: [PATCH 2/7] Cleans up the visual design of the web-page. --- index.html | 21 +++++++++++++++++++++ resources/script.js | 6 ++++-- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/index.html b/index.html index d18f666..e34cf2b 100644 --- a/index.html +++ b/index.html @@ -9,11 +9,28 @@ +
+
+

Stream Name Stream Title

+
+
+ +
+
+

Viewers

+

Current Viewers: 0

+

Total Viewers During Stream:

0

+
+
+ +
+
+

Current Viewers: . Session Peak Viewers: .

Stream Online?

@@ -23,4 +40,8 @@ + + + + \ No newline at end of file diff --git a/resources/script.js b/resources/script.js index 4c61440..f7780fd 100644 --- a/resources/script.js +++ b/resources/script.js @@ -41,7 +41,9 @@ async function updateSessionPeak(viewers) { async function Main() { // update visual elements using data from 'api/admin/status' - updateElements(await getStatus()) + // updateElements(await getStatus()) + const response = await fetch(api_url + 'yp', {mode:'no-cors'}) + const data = await response.json() // update the broadcast title // postEndpoint(`` @@ -51,4 +53,4 @@ async function Main() { // .then(data => console.log(data)) } -Main() \ No newline at end of file +// Main() From 90746d58b26527ecf1951de297adabaaff885383 Mon Sep 17 00:00:00 2001 From: arianagiroux Date: Thu, 21 Jul 2022 14:59:43 -0600 Subject: [PATCH 3/7] Updates .gitignore --- .gitignore | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 09a6a8a..2c65419 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ /bower_components/ /node_modules/ -resources/data.js \ No newline at end of file +resources/data.* +venv +*.sw* +.ropeproject From 2306bbc2e77ad794e44710b688a6e8461ede0231 Mon Sep 17 00:00:00 2001 From: arianagiroux Date: Thu, 4 Aug 2022 17:33:59 -0600 Subject: [PATCH 4/7] Updates get_status endpoint for compatibility - get_status endpoint now actually accesses two different endpoints from the owncast server api, as some of the endpoints have changed since development began - adds various documentation to app.py --- app.py | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/app.py b/app.py index 80e7555..4d3de8c 100755 --- a/app.py +++ b/app.py @@ -55,28 +55,48 @@ app = Flask(__name__) def render(data={}): + """ Template rendering function using mustache (via the pypi chevron + implementation. + + :returns: A full HTML template with relevant data from the server. + """ return chevron.render(template=open('index.mustache', 'r'), data=data) @app.route("/api/serverstatus", methods=['GET']) def getServerStatus(): + """ Obtains useful information from the server from either /api/yp or + /api/status. + + :returns: the collected data as a JSON formatted dict. + """ response = session.get(stream_data['stream_url'] + '/api/yp') response_data = response.json() - return json.dumps({ + data = { 'name': response_data['name'], 'online': response_data['online'], 'overallMaxViewerCount': response_data['overallMaxViewerCount'], 'sessionMaxViewerCount': response_data['sessionMaxViewerCount'], - 'streamTitle': response_data['streamTitle'], 'viewerCount': response_data['viewerCount'], 'description': response_data['description'], 'tags': response_data['tags'], 'nsfw': response_data['nsfw'], - }) + } + + response = session.get(stream_data['stream_url'] + '/api/status') + response_data = response.json() + + data['streamTitle'] = response_data['streamTitle'] + + return json.dumps(data) @app.route("/api/update/streamtitle", methods=['POST']) def updateStreamTitle(): + """ An endpoint to allow the user to update the stream title. + + :returns: the status code of the post request made to the server. + """ response = session.post( stream_data['stream_url'] + '/api/admin/config/streamtitle', data=json.dumps({'value': request.get_json(force=True)}) @@ -108,6 +128,10 @@ def updateServerNSFW(boolean: bool): @app.route("/") def index(): + """ A simple initial endpoint that loads in relevant data from the server. + + :returns: the rendered template (see app.render for more) + """ return render(json.loads(getServerStatus())) From 08287a9758184f5cf456456c5d84e44631f4e065 Mon Sep 17 00:00:00 2001 From: arianagiroux Date: Thu, 4 Aug 2022 17:36:01 -0600 Subject: [PATCH 5/7] Simplified javascript function for updating UI - breaks uiUpdate() into two functions - uiUpdateOnce(), which contains the bulk of the UI updating code - continuousUiUpdate(), which runs the aforementioned function once every second. - updates updateStreamTitle() to conform to this new change - removes call to Main(), replacing it with a call to the new continuousUiUpdate() --- static/script.js | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/static/script.js b/static/script.js index d9e4f3f..ec6c773 100644 --- a/static/script.js +++ b/static/script.js @@ -4,29 +4,35 @@ async function getEndpoint(url = '') { return data; } -async function uiUpdate() { +async function uiUpdateOnce() { + var data = await getEndpoint('http://127.0.0.1:5000/api/serverstatus'); + document.getElementById("streamTitle").innerHTML = data.streamTitle; + document.getElementById("currentViewers").innerHTML = data.viewerCount; + document.getElementById("sessionMaxViewerCount").innerHTML = data.sessionMaxViewerCount; + document.getElementById("overallMaxViewerCount").innerHTML = data.overallMaxViewerCount; + document.getElementById("tags").innerHTML = data.tags; +} + +async function continuousUiUpdate() { window.setInterval(async () => { - data = await getEndpoint('http://127.0.0.1:5000/api/serverstatus'); - document.getElementById("streamTitle").innerHTML = data.streamTitle - document.getElementById("currentViewers").innerHTML = data.viewerCount - document.getElementById("sessionMaxViewerCount").innerHTML = data.sessionMaxViewerCount - document.getElementById("overallMaxViewerCount").innerHTML = data.overallMaxViewerCount - document.getElementById("tags").innerHTML = data.tags - }, 1000) + uiUpdateOnce(); + }, 1000); } async function updateStreamTitle(value) { + // this function simply passes data along to the python script, and allows + // python to handle data processing response = await fetch('http://127.0.0.1:5000/api/update/streamtitle', { method:'POST', body:JSON.stringify(value) } ); - await uiUpdate() + await uiUpdateOnce(); } async function Main() { await uiUpdate() } -Main() +continuousUiUpdate() From ea96df1774caede359111a98a2c2fc559fd39334 Mon Sep 17 00:00:00 2001 From: arianagiroux Date: Thu, 4 Aug 2022 17:40:31 -0600 Subject: [PATCH 6/7] Implements function to pass tags updates to python - also removes main function from javascript file --- static/script.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/static/script.js b/static/script.js index ec6c773..c5c5a60 100644 --- a/static/script.js +++ b/static/script.js @@ -31,8 +31,16 @@ async function updateStreamTitle(value) { await uiUpdateOnce(); } -async function Main() { - await uiUpdate() +async function updateTags(value) { + // this function simply passes data along to the python script, and allows + // python to handle data processing + response = await fetch('http://127.0.0.1:5000/api/update/servertags', + { + method:'POST', + body:JSON.stringify(value) + } + ); + await uiUpdateOnce(); } continuousUiUpdate() From 03f2f966d7708f3abcffd80a1958fc5f4df9d7e9 Mon Sep 17 00:00:00 2001 From: arianagiroux Date: Thu, 4 Aug 2022 17:41:40 -0600 Subject: [PATCH 7/7] Updates python to update tags on owncast server - removes the nsfw tag function - adds functionality to the tags update endpoint to process raw data from client --- app.py | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/app.py b/app.py index 4d3de8c..1b3c2f5 100755 --- a/app.py +++ b/app.py @@ -105,25 +105,19 @@ def updateStreamTitle(): @app.route('/api/update/servertags', methods=['POST']) -def updateServerTags(tags_list: list): +def updateServerTags(): + """ An endpoint to allow the user to update the stream title. + + :returns: the status code of the post request made to the server. + """ + values = [i.strip() for i in request.get_json(force=True).split(',')] response = session.post( stream_data['stream_url'] + '/api/admin/config/tags', data=json.dumps({ - 'value': tags_list + 'value': values }) ) - return response.status_code, json.loads(response.text) - - -@app.route('/api/update/nsfw', methods=['POST']) -def updateServerNSFW(boolean: bool): - response = session.post( - stream_data['stream_url'] + '/api/admin/config/nsfw', - data=json.dumps({ - 'value': boolean - }) - ) - return response.status_code, json.loads(response.text) + return Response(status=response.status_code) @app.route("/")