Modernized and simplified the app.
This wasn't a crazy rewrite or anything, I just updated it to the new YouTube Transcript and OpenAI API's, as well as super simplifying the code. On top of that, it now works single threaded, just using multiple gunicorn threads for concurrency. It's a lot simplier and cleaner, although not up to my current standards.
This commit is contained in:
@@ -1,71 +1,68 @@
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
const responseArea = document.getElementById('response-area');
|
||||
const responseSection = document.getElementById('response-section');
|
||||
const submitButton = document.getElementById('submit');
|
||||
const urlBox = document.getElementById('url_box');
|
||||
const form = document.getElementById('url-form');
|
||||
|
||||
// Before sending HTMX request, prepare UI and handle empty input
|
||||
document.body.addEventListener('htmx:beforeRequest', function(evt) {
|
||||
if (evt.detail.elt.id === 'url-form') {
|
||||
const url = urlBox.value.trim();
|
||||
if (!url) {
|
||||
evt.detail.shouldCancel = true;
|
||||
responseArea.innerText = 'Please enter a URL.';
|
||||
return;
|
||||
}
|
||||
urlBox.value = '';
|
||||
submitButton.disabled = true;
|
||||
responseArea.innerText = 'Processing...';
|
||||
// Simple URL validation regex (covers http/https and domains)
|
||||
const urlPattern = /^(https?:\/\/)[\w.-]+(\.[\w\.-]+)+[/#?]?.*$/i;
|
||||
|
||||
form.addEventListener('submit', async (evt) => {
|
||||
evt.preventDefault();
|
||||
|
||||
const url = urlBox.value.trim();
|
||||
if (!url) {
|
||||
responseArea.textContent = 'Please enter a URL.';
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
document.body.addEventListener('htmx:afterRequest', function(evt) {
|
||||
if (evt.detail.elt.id === 'url-form') {
|
||||
const text = evt.detail.xhr.responseText.trim();
|
||||
if (text === "Processing started. Check /stream_output for updates.") {
|
||||
streamOutput(responseArea, responseSection, submitButton);
|
||||
} else {
|
||||
responseArea.innerText = text;
|
||||
submitButton.disabled = false;
|
||||
}
|
||||
if (!urlPattern.test(url)) {
|
||||
responseArea.textContent = 'Please enter a valid URL (must start with http:// or https://).';
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
function streamOutput(responseArea, responseSection, submitButton) {
|
||||
// Fetch the streaming output
|
||||
fetch('/stream_output')
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('Network response was not ok');
|
||||
}
|
||||
const reader = response.body.getReader();
|
||||
const decoder = new TextDecoder("utf-8");
|
||||
// Prepare UI
|
||||
urlBox.value = '';
|
||||
submitButton.disabled = true;
|
||||
responseArea.textContent = 'Processing...\n';
|
||||
|
||||
responseArea.innerHTML = "";
|
||||
|
||||
function readStream() {
|
||||
reader.read().then(({ done, value }) => {
|
||||
if (done) {
|
||||
submitButton.disabled = false;
|
||||
return;
|
||||
}
|
||||
const chunk = decoder.decode(value, { stream: true });
|
||||
responseArea.innerHTML += chunk;
|
||||
responseSection.scrollTop = responseSection.scrollHeight;
|
||||
readStream();
|
||||
}).catch(error => {
|
||||
console.error('Error reading stream:', error);
|
||||
responseArea.innerText = 'Error reading stream: ' + error.message;
|
||||
submitButton.disabled = false;
|
||||
});
|
||||
}
|
||||
|
||||
readStream();
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error fetching stream:', error);
|
||||
responseArea.innerText = 'Error fetching stream: ' + error.message;
|
||||
submitButton.disabled = false;
|
||||
try {
|
||||
const response = await fetch('/process-url', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||
body: new URLSearchParams({ url }),
|
||||
});
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Server returned ${response.status}`);
|
||||
}
|
||||
|
||||
const reader = response.body.getReader();
|
||||
const decoder = new TextDecoder('utf-8');
|
||||
responseArea.textContent = ''; // clear before streaming
|
||||
|
||||
async function readChunk() {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) {
|
||||
submitButton.disabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const chunk = decoder.decode(value, { stream: true });
|
||||
responseArea.innerHTML += chunk;
|
||||
responseSection.scrollTop = responseSection.scrollHeight;
|
||||
|
||||
await readChunk();
|
||||
}
|
||||
|
||||
await readChunk();
|
||||
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
responseArea.textContent = `Error: ${err.message}`;
|
||||
submitButton.disabled = false;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user