Move http functions to new javascript file http.js.

This commit is contained in:
voussoir 2022-10-01 14:30:08 -07:00
parent de22dff161
commit 2bd8f42eb0
No known key found for this signature in database
GPG key ID: 5F7554F8C26DACCB
5 changed files with 280 additions and 166 deletions

View file

@ -6,80 +6,100 @@ api.channels = {};
api.channels.add_channel = api.channels.add_channel =
function add_channel(channel_id, callback) function add_channel(channel_id, callback)
{ {
const url = "/add_channel"; return http.post({
const data = {"channel_id": channel_id}; url: "/add_channel",
return common.post(url, data, callback); data: {"channel_id": channel_id},
callback: callback,
});
} }
api.channels.delete_channel = api.channels.delete_channel =
function delete_channel(channel_id, callback) function delete_channel(channel_id, callback)
{ {
const url = `/channel/${channel_id}/delete`; return http.post({
const data = {}; url: `/channel/${channel_id}/delete`,
return common.post(url, data, callback); data: {},
callback: callback,
});
} }
api.channels.refresh_channel = api.channels.refresh_channel =
function refresh_channel(channel_id, force, callback) function refresh_channel(channel_id, force, callback)
{ {
const url = `/channel/${channel_id}/refresh`; return http.post({
const data = {"force": force}; url: `/channel/${channel_id}/refresh`,
return common.post(url, data, callback); data: {"force": force},
callback: callback,
});
} }
api.channels.refresh_all_channels = api.channels.refresh_all_channels =
function refresh_all_channels(force, callback) function refresh_all_channels(force, callback)
{ {
const url = "/refresh_all_channels"; return http.post({
const data = {"force": force}; url: "/refresh_all_channels",
return common.post(url, data, callback); data: {"force": force},
callback: callback,
});
} }
api.channels.set_automark = api.channels.set_automark =
function set_automark(channel_id, state, callback) function set_automark(channel_id, state, callback)
{ {
const url = `/channel/${channel_id}/set_automark`; return http.post({
const data = {"state": state}; url: `/channel/${channel_id}/set_automark`,
return common.post(url, data, callback); data: {"state": state},
callback: callback,
});
} }
api.channels.set_autorefresh = api.channels.set_autorefresh =
function set_autorefresh(channel_id, autorefresh, callback) function set_autorefresh(channel_id, autorefresh, callback)
{ {
const url = `/channel/${channel_id}/set_autorefresh`; return http.post({
const data = {"autorefresh": autorefresh}; url: `/channel/${channel_id}/set_autorefresh`,
return common.post(url, data, callback); data: {"autorefresh": autorefresh},
callback: callback,
});
} }
api.channels.set_download_directory = api.channels.set_download_directory =
function set_download_directory(channel_id, download_directory, callback) function set_download_directory(channel_id, download_directory, callback)
{ {
const url = `/channel/${channel_id}/set_download_directory`; return http.post({
const data = {"download_directory": download_directory}; url: `/channel/${channel_id}/set_download_directory`,
return common.post(url, data, callback); data: {"download_directory": download_directory},
callback: callback,
});
} }
api.channels.set_name = api.channels.set_name =
function set_name(channel_id, name, callback) function set_name(channel_id, name, callback)
{ {
const url = `/channel/${channel_id}/set_name`; return http.post({
const data = {"name": name}; url: `/channel/${channel_id}/set_name`,
return common.post(url, data, callback); data: {"name": name},
callback: callback,
});
} }
api.channels.set_queuefile_extension = api.channels.set_queuefile_extension =
function set_queuefile_extension(channel_id, extension, callback) function set_queuefile_extension(channel_id, extension, callback)
{ {
const url = `/channel/${channel_id}/set_queuefile_extension`; return http.post({
const data = {"extension": extension}; url: `/channel/${channel_id}/set_queuefile_extension`,
return common.post(url, data, callback); data: {"extension": extension},
callback: callback,
});
} }
api.channels.show_download_directory = api.channels.show_download_directory =
function show_download_directory(channel_id, callback) function show_download_directory(channel_id, callback)
{ {
const url = `/channel/${channel_id}/show_download_directory`; return http.post({
return common.post(url, null, callback); url: `/channel/${channel_id}/show_download_directory`,
callback: callback,
});
} }
api.channels.callback_go_to_channels = api.channels.callback_go_to_channels =
@ -101,15 +121,19 @@ api.videos = {};
api.videos.mark_state = api.videos.mark_state =
function mark_state(video_ids, state, callback) function mark_state(video_ids, state, callback)
{ {
const url = "/mark_video_state"; return http.post({
const data = {"video_ids": video_ids, "state": state}; url: "/mark_video_state",
return common.post(url, data, callback); data: {"video_ids": video_ids, "state": state},
callback: callback,
});
} }
api.videos.start_download = api.videos.start_download =
function start_download(video_ids, callback) function start_download(video_ids, callback)
{ {
const url = "/start_download"; return http.post({
const data = {"video_ids": video_ids}; url: "/start_download",
return common.post(url, data, callback); data: {"video_ids": video_ids},
callback: callback,
});
} }

View file

@ -34,6 +34,12 @@ function is_wide_mode()
return getComputedStyle(document.documentElement).getPropertyValue("--wide").trim() === "1"; return getComputedStyle(document.documentElement).getPropertyValue("--wide").trim() === "1";
} }
common.go_to_root =
function go_to_root()
{
window.location.href = "/";
}
common.refresh = common.refresh =
function refresh() function refresh()
{ {
@ -51,146 +57,18 @@ function refresh_or_alert(response)
window.location.reload(); window.location.reload();
} }
////////////////////////////////////////////////////////////////////////////////////////////////////
// HTTP ////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
common.formdata =
function formdata(data)
{
fd = new FormData();
for (let [key, value] of Object.entries(data))
{
if (value === undefined)
{
continue;
}
if (value === null)
{
value = '';
}
fd.append(key, value);
}
return fd;
}
common._request =
function _request(method, url, callback)
{
/*
Perform an HTTP request and call the `callback` with the response.
The response will have the following structure:
{
"meta": {
"completed": true / false,
"status": If the connection failed or request otherwise could not
complete, `status` will be 0. If the request completed,
`status` will be the HTTP response code.
"json_ok": If the server responded with parseable json, `json_ok`
will be true, and that data will be in `response.data`. If the
server response was not parseable json, `json_ok` will be false
and `response.data` will be undefined.
"request_url": The URL exactly as given to this call.
}
"data": {JSON parsed from server response if json_ok}.
}
So, from most lenient to most strict, error catching might look like:
if response.meta.completed
if response.meta.json_ok
if response.meta.status === 200
if response.meta.status === 200 and response.meta.json_ok
*/
const request = new XMLHttpRequest();
const response = {
"meta": {
"completed": false,
"status": 0,
"json_ok": false,
"request_url": url,
},
};
request.onreadystatechange = function()
{
/*
readystate values:
0 UNSENT / ABORTED
1 OPENED
2 HEADERS_RECEIVED
3 LOADING
4 DONE
*/
if (request.readyState != 4)
{return;}
if (callback == null)
{return;}
response.meta.status = request.status;
if (request.status != 0)
{
response.meta.completed = true;
try
{
response.data = JSON.parse(request.responseText);
response.meta.json_ok = true;
}
catch (exc)
{
response.meta.json_ok = false;
}
}
callback(response);
};
const asynchronous = true;
request.open(method, url, asynchronous);
return request;
}
common.get =
function get(url, callback)
{
request = common._request("GET", url, callback);
request.send();
return request;
}
common.post =
function post(url, data, callback)
{
/*
`data`:
a FormData object which you have already filled with values, or a
dictionary from which a FormData will be made, using common.formdata.
*/
if (data instanceof FormData || data === null)
{
;
}
else
{
data = common.formdata(data);
}
request = common._request("POST", url, callback);
request.send(data);
return request;
}
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
// STRING TOOLS //////////////////////////////////////////////////////////////////////////////////// // STRING TOOLS ////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
common.join_and_trail = common.join_and_trail =
function join_and_trail(l, s) function join_and_trail(list, separator)
{ {
if (l.length === 0) if (list.length === 0)
{ {
return ""; return "";
} }
return l.join(s) + s return list.join(separator) + separator
} }
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////

View file

@ -0,0 +1,210 @@
const http = {};
http.HEADERS = {};
http.requests_in_flight = 0;
http.request_queue = {};
http.request_queue.array = [];
http.request_queue.push =
function request_queue_push(func)
{
http.request_queue.array.push(func)
if (http.requests_in_flight == 0)
{
http.request_queue_next();
}
}
http.request_queue.unshift =
function request_queue_unshift(func)
{
http.request_queue.array.unshift(func)
if (http.requests_in_flight == 0)
{
http.request_queue_next();
}
}
http.request_queue.pushleft = http.request_queue.unshift;
http.request_queue_next =
function request_queue_next()
{
if (http.requests_in_flight > 0)
{
return;
}
if (http.request_queue.array.length === 0)
{
return;
}
const func = http.request_queue.array.shift();
func();
}
http.formdata =
function formdata(data)
{
const fd = new FormData();
for (let [key, value] of Object.entries(data))
{
if (value === undefined)
{
continue;
}
if (value === null)
{
value = '';
}
fd.append(key, value);
}
return fd;
}
http._request =
function _request(kwargs)
{
/*
Perform an HTTP request and call the `callback` with the response.
The response will have the following structure:
{
"meta": {
"request": the XMLHttpRequest object,
"completed": true / false,
"status": If the connection failed or request otherwise could not
complete, `status` will be 0. If the request completed,
`status` will be the HTTP response code.
"json_ok": If the server responded with parseable json, `json_ok`
will be true, and that data will be in `response.data`. If the
server response was not parseable json, `json_ok` will be false
and `response.data` will be undefined.
"kwargs": The kwargs exactly as given to this call.
}
"data": {JSON parsed from server response if json_ok},
"retry": function you can call to retry the request.
}
So, from most lenient to most strict, error catching might look like:
if response.meta.completed
if response.meta.json_ok
if response.meta.status === 200
if response.meta.status === 200 and response.meta.json_ok
*/
const request = new XMLHttpRequest();
const response = {
"meta": {
"request": request,
"completed": false,
"status": 0,
"json_ok": false,
"kwargs": kwargs,
},
"retry": function(){http._request(kwargs)},
};
request.onreadystatechange = function()
{
/*
readystate values:
0 UNSENT / ABORTED
1 OPENED
2 HEADERS_RECEIVED
3 LOADING
4 DONE
*/
if (request.readyState != 4)
{
return;
}
http.requests_in_flight -= 1;
setTimeout(http.request_queue_next, 0);
if (kwargs["callback"] == null)
{
return;
}
response.meta.status = request.status;
if (request.status != 0)
{
response.meta.completed = true;
try
{
response.data = JSON.parse(request.responseText);
response.meta.json_ok = true;
}
catch (exc)
{
response.meta.json_ok = false;
}
}
kwargs["callback"](response);
};
// Headers
const asynchronous = "asynchronous" in kwargs ? kwargs["asynchronous"] : true;
request.open(kwargs["method"], kwargs["url"], asynchronous);
for (const [header, value] of Object.entries(http.HEADERS))
{
request.setRequestHeader(header, value);
}
const more_headers = kwargs["headers"] || {};
for (const [header, value] of Object.entries(more_headers))
{
request.setRequestHeader(header, value);
}
if (kwargs["with_credentials"])
{
request.withCredentials = true;
}
// Send
let data = kwargs["data"];
if (data === undefined || data === null)
{
request.send();
}
else if (data instanceof FormData)
{
request.send(data);
}
else if (typeof(data) === "string" || data instanceof String)
{
request.send(data);
}
else
{
request.send(http.formdata(data));
}
http.requests_in_flight += 1;
return request;
}
http.get =
function get(kwargs)
{
kwargs["method"] = "GET";
return http._request(kwargs);
}
http.post =
function post(kwargs)
{
/*
`data`:
a FormData object which you have already filled with values, or a
dictionary from which a FormData will be made, using http.formdata.
*/
kwargs["method"] = "POST";
return http._request(kwargs);
}

View file

@ -11,6 +11,7 @@
<script src="/static/js/common.js"></script> <script src="/static/js/common.js"></script>
<script src="/static/js/api.js"></script> <script src="/static/js/api.js"></script>
<script src="/static/js/hotkeys.js"></script> <script src="/static/js/hotkeys.js"></script>
<script src="/static/js/http.js"></script>
<script src="/static/js/spinners.js"></script> <script src="/static/js/spinners.js"></script>
<style> <style>

View file

@ -10,6 +10,7 @@
<link rel="stylesheet" href="/static/css/ycdl.css"> <link rel="stylesheet" href="/static/css/ycdl.css">
<script src="/static/js/common.js"></script> <script src="/static/js/common.js"></script>
<script src="/static/js/api.js"></script> <script src="/static/js/api.js"></script>
<script src="/static/js/http.js"></script>
<script src="/static/js/spinners.js"></script> <script src="/static/js/spinners.js"></script>
<style> <style>