summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorRoland Reichwein <mail@reichwein.it>2022-11-22 17:07:44 +0100
committerRoland Reichwein <mail@reichwein.it>2022-11-22 17:07:44 +0100
commit2a4d96188afa83110b30931559732d4fd9bacab2 (patch)
tree9513d272f2baba29d3b22b04c78ffa4bf61f58bd
parent15b3424c72fb3af3eb47e00eeee27730e8fa0b75 (diff)
First working whiteboard
-rwxr-xr-xMakefile8
-rw-r--r--html/index.html7
-rw-r--r--html/whiteboard.js158
-rw-r--r--whiteboard.cpp148
4 files changed, 147 insertions, 174 deletions
diff --git a/Makefile b/Makefile
index 2d14170..640a081 100755
--- a/Makefile
+++ b/Makefile
@@ -36,9 +36,15 @@ ifeq ($(CXXFLAGS),)
CXXFLAGS=-g -O2
endif
-CXXFLAGS+=-Wall -fPIE -std=c++20 -Wpedantic -gdwarf-4
+CXXFLAGS+=-Wall -fPIE -Wpedantic -gdwarf-4
LDFLAGS+=-pie
+ifeq ($(CXX),g++-9)
+CXXFLAGS+=-std=c++17
+else
+CXXFLAGS+=-std=c++20
+endif
+
ifeq ($(CXX),clang++-10)
LIBS+= \
-fuse-ld=lld-10 \
diff --git a/html/index.html b/html/index.html
index 780660a..64eae90 100644
--- a/html/index.html
+++ b/html/index.html
@@ -14,12 +14,7 @@
<textarea rows="30" cols="80" id="board" name="board"></textarea>
<br/>
<br/>
- <h2>Contact</h2>
- Roland Reichwein<br/>
- Hauptstr. 101a<br/>
- 82008 Unterhaching<br/>
- <a href="mailto:mail@reichwein.it">mail@reichwein.it</a><br/>
- <a href="https://www.reichwein.it">https://www.reichwein.it</a><br/>
+ Reichwein.IT Whiteboard by <a href="https://www.reichwein.it">https://www.reichwein.it</a><br/>
</div>
<a id="download-a" hidden></a>
diff --git a/html/whiteboard.js b/html/whiteboard.js
index dd81ff0..2b35180 100644
--- a/html/whiteboard.js
+++ b/html/whiteboard.js
@@ -1,40 +1,81 @@
// started on main page load
function init() {
-
- // Connect "Enter" in text field with Button click
- var url = document.getElementById("url");
- url.addEventListener("keyup", function(event) {
- if (event.keyCode === 13) {
- event.preventDefault();
- on_start();
- }
- });
+ init_board();
}
-function set_status(message) {
- if (message == "")
- message = "&nbsp;";
+function init_board() {
+ var xhr = new XMLHttpRequest();
+
+ // run on data received back
+ xhr.onreadystatechange = function() {
+ if (this.readyState == 3) {
+ //set_status("Please wait while downloading " + filename + " ...");
+ return;
+ }
+ if (this.readyState != 4) {
+ return;
+ }
+ if (this.status != 200) {
+ //set_status("Server Error while retrieving " + filename + ", status: " + this.status + " " + this.statusText);
+ return;
+ }
+
+ var file = new Blob([this.response]);
+ reader = new FileReader();
+ reader.onload = function() {
+ var board = document.getElementById("board");
+ board.innerHTML = reader.result;
+
+ // Initialization done. Now we can start modifying.
+ board.addEventListener("input", function() {on_modify(); });
+
+ setInterval(function() {checkupdate();}, 2000);
+ }
+
+ reader.readAsBinaryString(file);
+
+ //set_status(""); // OK
+ }
+
+ var parser = new DOMParser();
+ var xmlDocument = parser.parseFromString("<request></request>", "text/xml");
+
+ var requestElement = xmlDocument.getElementsByTagName("request")[0];
+
+ var commandElement = xmlDocument.createElement("command");
+ commandElement.appendChild(document.createTextNode("getfile"));
+ requestElement.appendChild(commandElement);
+
+ var idElement = xmlDocument.createElement("id");
+ idElement.appendChild(document.createTextNode("id1"));
+ requestElement.appendChild(idElement);
+
+ xhr.open("POST", "whiteboard.fcgi", true);
+ xhr.setRequestHeader("Content-type", "text/xml");
+ xhr.responseType = 'blob';
+ xhr.send(xmlDocument);
- document.getElementById("status").innerHTML = message;
+ //set_status("Please wait while server prepares " + filename + " ...");
}
-// started on button click: get filename
-function on_start() {
+function on_modify() {
var xhr = new XMLHttpRequest();
-
+
// run on data received back
xhr.onreadystatechange = function() {
+ if (this.readyState == 3) {
+ //set_status("Please wait while downloading " + filename + " ...");
+ return;
+ }
if (this.readyState != 4) {
return;
}
if (this.status != 200) {
- set_status("Server Error while retrieving filename, " + filename + ", status: " + this.status + " " + this.statusText);
+ //set_status("Server Error while retrieving " + filename + ", status: " + this.status + " " + this.statusText);
return;
}
- var filename = this.responseText;
-
- get_file(filename);
+ //set_status(""); // OK
}
var parser = new DOMParser();
@@ -43,50 +84,65 @@ function on_start() {
var requestElement = xmlDocument.getElementsByTagName("request")[0];
var commandElement = xmlDocument.createElement("command");
- commandElement.appendChild(document.createTextNode("getfilename"));
+ commandElement.appendChild(document.createTextNode("modify"));
requestElement.appendChild(commandElement);
- var urlElement = xmlDocument.createElement("url");
- urlElement.appendChild(document.createTextNode(document.getElementById("url").value));
- requestElement.appendChild(urlElement);
+ var idElement = xmlDocument.createElement("id");
+ idElement.appendChild(document.createTextNode("id1"));
+ requestElement.appendChild(idElement);
+
+ var dataElement = xmlDocument.createElement("data");
+ dataElement.appendChild(document.createTextNode(document.getElementById("board").value));
+ requestElement.appendChild(dataElement);
- var formatElement = xmlDocument.createElement("format");
- formatElement.appendChild(document.createTextNode(document.getElementById("mp3").checked ? "mp3" : "mp4"));
- requestElement.appendChild(formatElement);
-
xhr.open("POST", "whiteboard.fcgi", true);
xhr.setRequestHeader("Content-type", "text/xml");
- xhr.responseType = 'text';
+ xhr.responseType = 'blob';
xhr.send(xmlDocument);
- set_status("Please wait while retrieving filename...");
+ //set_status("Please wait while server prepares " + filename + " ...");
}
-// started on button click: get file
-function get_file(filename) {
- var xhr = new XMLHttpRequest();
+// checksum of string
+function checksum32(s) {
+ var result = 0;
+ for (var i = 0; i < s.length; i++) {
+ result = ((((result >>> 1) | ((result & 1) << 31)) | 0) ^ (s.charCodeAt(i) & 0xFF)) | 0;
+ }
+ return (result & 0x7FFFFFFF) | 0;
+}
+// gets called by regular polling
+function checkupdate() {
+ var xhr = new XMLHttpRequest();
+
// run on data received back
xhr.onreadystatechange = function() {
if (this.readyState == 3) {
- set_status("Please wait while downloading " + filename + " ...");
+ //set_status("Please wait while downloading " + filename + " ...");
return;
}
if (this.readyState != 4) {
return;
}
if (this.status != 200) {
- set_status("Server Error while retrieving " + filename + ", status: " + this.status + " " + this.statusText);
+ //set_status("Server Error while retrieving " + filename + ", status: " + this.status + " " + this.statusText);
return;
}
- var a = document.getElementById("download-a");
- a.setAttribute("download", filename);
- var file = new Blob([this.response]);
- a.href = window.URL.createObjectURL(file);
- a.click();
-
- set_status(""); // OK
+ // no change if response is text/plain
+ if (this.getResponseHeader("Content-Type") == "application/octet-stream") {
+ var file = new Blob([this.response]);
+ reader = new FileReader();
+ reader.onload = function() {
+ var board = document.getElementById("board");
+ board.value = reader.result;
+ }
+
+ reader.readAsBinaryString(file);
+ }
+
+ //set_status(""); // OK
}
var parser = new DOMParser();
@@ -95,22 +151,22 @@ function get_file(filename) {
var requestElement = xmlDocument.getElementsByTagName("request")[0];
var commandElement = xmlDocument.createElement("command");
- commandElement.appendChild(document.createTextNode("getfile"));
+ commandElement.appendChild(document.createTextNode("checkupdate"));
requestElement.appendChild(commandElement);
- var urlElement = xmlDocument.createElement("url");
- urlElement.appendChild(document.createTextNode(document.getElementById("url").value));
- requestElement.appendChild(urlElement);
+ var idElement = xmlDocument.createElement("id");
+ idElement.appendChild(document.createTextNode("id1"));
+ requestElement.appendChild(idElement);
+
+ var checksumElement = xmlDocument.createElement("checksum");
+ checksumElement.appendChild(document.createTextNode(checksum32(document.getElementById("board").value)));
+ requestElement.appendChild(checksumElement);
- var formatElement = xmlDocument.createElement("format");
- formatElement.appendChild(document.createTextNode(document.getElementById("mp3").checked ? "mp3" : "mp4"));
- requestElement.appendChild(formatElement);
-
xhr.open("POST", "whiteboard.fcgi", true);
xhr.setRequestHeader("Content-type", "text/xml");
xhr.responseType = 'blob';
xhr.send(xmlDocument);
- set_status("Please wait while server prepares " + filename + " ...");
+ //set_status("Please wait while server prepares " + filename + " ...");
}
diff --git a/whiteboard.cpp b/whiteboard.cpp
index cecee9e..b6b3a8d 100644
--- a/whiteboard.cpp
+++ b/whiteboard.cpp
@@ -25,38 +25,15 @@ using namespace std::string_literals;
namespace fs = std::filesystem;
namespace {
-
- class TempDir
- {
- private:
- fs::path m_path;
- fs::path m_oldpath;
- public:
- TempDir()
- {
- char templ[] = "/tmp/downtubeXXXXXX";
- char *temp = mkdtemp(templ);
- if (temp == nullptr) {
- throw std::runtime_error("Can't create temporary directory.");
- }
-
- m_path = temp;
- m_oldpath = fs::current_path();
- fs::current_path(m_path);
- }
- ~TempDir()
- {
- fs::current_path(m_oldpath);
- fs::remove_all(m_path);
+ uint32_t checksum32(const std::string& s) {
+ uint32_t result{0};
+ for (int i = 0; i < s.size(); i++) {
+ result = ((result >> 1) | ((result & 1) << 31)) ^ (s[i] & 0xFF);
}
-
- const fs::path& getPath() const {return m_path;}
- };
-
- std::regex re{"https?://(www\\.youtube\\.com/watch\\?v=|youtu\\.be/)[[:alnum:]_&=-]+", std::regex::extended}; // www.youtube.com/watch?v=ItjMIxS3-rI or https://youtu.be/ItjMIxS3-rI
-
-} // anonymous namespace
+ return result & 0x7FFFFFFF;
+ }
+}
int main(void)
{
@@ -81,6 +58,8 @@ int main(void)
return 1;
}
+ std::string filedata {""}; // initial content
+
while (FCGX_Accept_r(&request) >= 0) {
try {
char* method = FCGX_GetParam("REQUEST_METHOD", request.envp);
@@ -99,107 +78,44 @@ int main(void)
pt::ptree xml;
std::istringstream ss{xmlData};
- pt::xml_parser::read_xml(ss, xml, pt::xml_parser::no_comments | pt::xml_parser::trim_whitespace);
+ pt::xml_parser::read_xml(ss, xml);
- std::string url {xml.get<std::string>("request.url")};
- std::string format {xml.get<std::string>("request.format")};
std::string command {xml.get<std::string>("request.command")};
+ std::string id {xml.get<std::string>("request.id")};
- //FCGX_PutS("Content-Type: text/plain\r\n\r\n", request.out);
- //FCGX_FPrintF(request.out, "url: %s\r\n", url.c_str());
- //FCGX_FPrintF(request.out, "format: %s\r\n", format.c_str()); // mp3, mp4
-
- if (format != "mp3" && format != "mp4") {
- throw std::runtime_error("Bad format: "s + format);
- }
-
- if (!std::regex_match(url, re)) {
- throw std::runtime_error("Bad URL");
- }
-
- // remove trailing "&..."
- size_t and_pos {url.find('&')};
- if (and_pos != std::string::npos)
- url = url.substr(0, and_pos);
-
- //FCGX_FPrintF(request.out, "command: %s\r\n", command.c_str());
-
- TempDir tempDir;
-
- //FCGX_FPrintF(request.out, "path: %s\r\n", tempDir.getPath().string().c_str());
-
- if (command == "getfilename") {
- //std::string cmd{"youtube-dl -o '%(title)s."s + format + "' --get-filename --no-call-home --restrict-filenames "s + url + " > filename.txt"};
- // Recoding to MP4 is too slow currently. So keep original format for now
- std::string cmd{"xyoutube-dl -o '%(title)s.%(ext)s' --get-filename --no-call-home --restrict-filenames "s + url + " > filename.txt"};
- if (system(cmd.c_str()))
- throw std::runtime_error("Can't guess filename");
-
- std::string filename {File::getFile("filename.txt")};
- boost::algorithm::trim(filename);
-
- if (format == "mp3")
- filename = fs::path{filename}.stem().string() + ".mp3";
-
+ if (command == "modify") {
+ std::string data {xml.get<std::string>("request.data")};
+ filedata = data;
FCGX_PutS("Content-Type: text/plain\r\n\r\n", request.out);
- FCGX_FPrintF(request.out, "%s", filename.c_str());
} else if (command == "getfile") {
- if (format == "mp3") {
- std::string cmd{"xyoutube-dl --no-warnings --no-call-home --no-progress -x --audio-format mp3 -o 'audio.%(ext)s' --restrict-filenames "s + url};
- system(cmd.c_str()); // Ignore error - "ERROR: Stream #1:0 -> #0:1 (copy)" - seems to be ok
-
- std::string filedata {File::getFile("audio.mp3")}; // may throw
- if (filedata.size() > 30000000)
- throw std::runtime_error("File too big");
-
- FCGX_PutS("Content-Type: application/octet-stream\r\n", request.out);
- FCGX_FPrintF(request.out, "Content-Length: %d\r\n\r\n", filedata.size());
- FCGX_PutStr(filedata.c_str(), filedata.size(), request.out);
- } else if (format == "mp4") {
- //std::string cmd{"youtube-dl --no-warnings --no-call-home --no-progress --recode-video mp4 -o video.mp4 --restrict-filenames "s + url};
- // Recoding to MP4 is too slow currently. So keep original format for now
- std::string cmd{"xyoutube-dl --no-warnings --no-call-home --no-progress -o video.mp4 --restrict-filenames "s + url};
- system(cmd.c_str()); // Ignore error
-
- // youtube-dl gets it wrong and creates, e.g. video.mkv.
- // So find it and load it
- fs::directory_iterator di{fs::current_path()};
- fs::path filename;
- for (const auto& i: di) {
- if (boost::algorithm::starts_with(i.path().filename().string(), "video."s)) {
- filename = i.path().filename().string();
- break;
- }
- }
-
- if (filename.empty()) {
- throw std::runtime_error("No video file found.");
- }
-
- std::string filedata {File::getFile(filename)}; // may throw
-
- if (filedata.size() > 300000000)
- throw std::runtime_error("File too big");
-
+ if (filedata.size() > 30000000)
+ throw std::runtime_error("File too big");
+
+ FCGX_PutS("Content-Type: application/octet-stream\r\n", request.out);
+ FCGX_FPrintF(request.out, "Content-Length: %d\r\n\r\n", filedata.size());
+ FCGX_PutStr(filedata.c_str(), filedata.size(), request.out);
+ } else if (command == "checkupdate") {
+ std::string checksum_s {xml.get<std::string>("request.checksum")};
+ uint32_t checksum{static_cast<uint32_t>(stoul(checksum_s))};
+
+ //std::cout << "Checksum JS: " << checksum_s << std::endl;
+ //std::cout << "Checksum C++: " << checksum32(filedata) << std::endl;
+
+ if (checksum != checksum32(filedata)) {
+ //std::cout << "Sending change..." << std::endl;
FCGX_PutS("Content-Type: application/octet-stream\r\n", request.out);
FCGX_FPrintF(request.out, "Content-Length: %d\r\n\r\n", filedata.size());
FCGX_PutStr(filedata.c_str(), filedata.size(), request.out);
} else {
- throw std::runtime_error("Bad format for unknown reason: "s + format); // should have been caught above already!
+ //std::cout << "No change..." << std::endl;
+ FCGX_PutS("Content-Type: text/plain\r\n\r\n", request.out);
+ FCGX_PutS("No change.\r\n", request.out);
}
} else {
throw std::runtime_error("Bad command: "s + command);
}
- // Name:
- // youtube-dl -o "%(title)s.mp3" --get-filename --no-call-home --restrict-filenames $SOURCE > filename.txt
- //
- // MP3:
- // youtube-dl --no-warnings --no-call-home --no-progress -x --audio-format mp3 --embed-thumbnail -o "%(title)s.(ext)s" --restrict-filenames $SOURCE
- //
- // MP4:
- // youtube-dl --no-warnings --no-call-home --no-progress --recode-video mp4 -o "%(title)s.(ext)s" --restrict-filenames $SOURCE
} else {
throw std::runtime_error("Unsupported method.\r\n");
}