Upload index.html
Browse files- templates/index.html +82 -19
templates/index.html
CHANGED
@@ -111,19 +111,20 @@
|
|
111 |
availableVideos.forEach((video) => {
|
112 |
const card = document.createElement("div");
|
113 |
card.className = "bg-white p-4 rounded shadow flex flex-col justify-between";
|
114 |
-
|
115 |
-
//
|
116 |
const videoContainer = document.createElement("div");
|
117 |
videoContainer.className = "relative w-full rounded overflow-hidden";
|
118 |
videoContainer.style.aspectRatio = "9 / 16";
|
119 |
-
|
120 |
-
//
|
121 |
const previewVideo = document.createElement("video");
|
122 |
previewVideo.className = "absolute top-0 left-0 w-full h-full object-cover";
|
123 |
previewVideo.muted = true;
|
124 |
previewVideo.loop = true;
|
125 |
previewVideo.playsInline = true;
|
126 |
previewVideo.autoplay = true;
|
|
|
127 |
if (video.preview_video) {
|
128 |
previewVideo.src = video.preview_video;
|
129 |
} else {
|
@@ -132,19 +133,27 @@
|
|
132 |
}
|
133 |
videoContainer.appendChild(previewVideo);
|
134 |
card.appendChild(videoContainer);
|
135 |
-
|
136 |
// Title
|
137 |
const title = document.createElement("h3");
|
138 |
title.className = "mt-2 font-semibold";
|
139 |
title.textContent = video.title;
|
140 |
card.appendChild(title);
|
141 |
-
|
142 |
-
// Info
|
143 |
const info = document.createElement("p");
|
144 |
info.className = "text-sm text-gray-600";
|
145 |
info.textContent = `Subreddit: ${video.subreddit} | Upvotes: ${video.ups}`;
|
146 |
card.appendChild(info);
|
147 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
148 |
// Checkbox for selection
|
149 |
const checkbox = document.createElement("input");
|
150 |
checkbox.type = "checkbox";
|
@@ -178,14 +187,28 @@
|
|
178 |
});
|
179 |
const data = await res.json();
|
180 |
console.log(data);
|
|
|
|
|
181 |
});
|
182 |
|
183 |
-
//
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
189 |
|
190 |
function renderResults(results) {
|
191 |
resultsList.innerHTML = "";
|
@@ -193,15 +216,28 @@
|
|
193 |
const result = results[id];
|
194 |
const container = document.createElement("div");
|
195 |
container.className = "bg-white p-4 rounded shadow";
|
|
|
196 |
const header = document.createElement("div");
|
197 |
header.className = "flex justify-between items-center";
|
198 |
const title = document.createElement("h3");
|
199 |
title.className = "font-semibold text-lg";
|
200 |
title.textContent = result.generated_title || "Processing...";
|
201 |
header.appendChild(title);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
202 |
container.appendChild(header);
|
|
|
203 |
if (result.status === "completed") {
|
204 |
-
//
|
205 |
const videoContainer = document.createElement("div");
|
206 |
videoContainer.className = "max-w-md mx-auto mt-2";
|
207 |
const videoElem = document.createElement("video");
|
@@ -209,9 +245,15 @@
|
|
209 |
videoElem.controls = true;
|
210 |
videoElem.className = "w-full";
|
211 |
videoElem.style.maxHeight = "500px";
|
|
|
|
|
|
|
|
|
|
|
|
|
212 |
videoContainer.appendChild(videoElem);
|
213 |
container.appendChild(videoContainer);
|
214 |
-
|
215 |
// Caption with a copy button
|
216 |
const captionDiv = document.createElement("div");
|
217 |
captionDiv.className = "mt-2 flex items-center";
|
@@ -238,11 +280,32 @@
|
|
238 |
pendingMsg.className = "text-gray-600 mt-2";
|
239 |
container.appendChild(pendingMsg);
|
240 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
241 |
resultsList.appendChild(container);
|
242 |
}
|
243 |
}
|
244 |
|
245 |
-
// Settings Modal
|
246 |
openSettings.addEventListener("click", async () => {
|
247 |
const res = await fetch("/api/settings");
|
248 |
const settings = await res.json();
|
@@ -270,9 +333,9 @@
|
|
270 |
});
|
271 |
|
272 |
// Connect to the logs WebSocket for live console output
|
273 |
-
const protocol = window.location.protocol ===
|
274 |
const logsSocket = new WebSocket(`${protocol}//${location.host}/ws/logs`);
|
275 |
-
logsSocket.onmessage = function(event) {
|
276 |
const newLog = document.createElement("div");
|
277 |
newLog.textContent = event.data;
|
278 |
consoleOutput.appendChild(newLog);
|
|
|
111 |
availableVideos.forEach((video) => {
|
112 |
const card = document.createElement("div");
|
113 |
card.className = "bg-white p-4 rounded shadow flex flex-col justify-between";
|
114 |
+
|
115 |
+
// Container with enforced 9:16 aspect ratio
|
116 |
const videoContainer = document.createElement("div");
|
117 |
videoContainer.className = "relative w-full rounded overflow-hidden";
|
118 |
videoContainer.style.aspectRatio = "9 / 16";
|
119 |
+
|
120 |
+
// Video element for preview (with controls)
|
121 |
const previewVideo = document.createElement("video");
|
122 |
previewVideo.className = "absolute top-0 left-0 w-full h-full object-cover";
|
123 |
previewVideo.muted = true;
|
124 |
previewVideo.loop = true;
|
125 |
previewVideo.playsInline = true;
|
126 |
previewVideo.autoplay = true;
|
127 |
+
previewVideo.controls = true; // <-- now shows native video controls
|
128 |
if (video.preview_video) {
|
129 |
previewVideo.src = video.preview_video;
|
130 |
} else {
|
|
|
133 |
}
|
134 |
videoContainer.appendChild(previewVideo);
|
135 |
card.appendChild(videoContainer);
|
136 |
+
|
137 |
// Title
|
138 |
const title = document.createElement("h3");
|
139 |
title.className = "mt-2 font-semibold";
|
140 |
title.textContent = video.title;
|
141 |
card.appendChild(title);
|
142 |
+
|
143 |
+
// Info (Subreddit and Upvotes)
|
144 |
const info = document.createElement("p");
|
145 |
info.className = "text-sm text-gray-600";
|
146 |
info.textContent = `Subreddit: ${video.subreddit} | Upvotes: ${video.ups}`;
|
147 |
card.appendChild(info);
|
148 |
+
|
149 |
+
// Display Duration if available
|
150 |
+
if (video.duration) {
|
151 |
+
const durationText = document.createElement("p");
|
152 |
+
durationText.className = "text-sm text-gray-600";
|
153 |
+
durationText.textContent = `Duration: ${video.duration} sec`;
|
154 |
+
card.appendChild(durationText);
|
155 |
+
}
|
156 |
+
|
157 |
// Checkbox for selection
|
158 |
const checkbox = document.createElement("input");
|
159 |
checkbox.type = "checkbox";
|
|
|
187 |
});
|
188 |
const data = await res.json();
|
189 |
console.log(data);
|
190 |
+
// Restart polling for processing results whenever processing begins.
|
191 |
+
startResultsPolling();
|
192 |
});
|
193 |
|
194 |
+
// Function to poll for processing results
|
195 |
+
let pollingInterval;
|
196 |
+
function startResultsPolling() {
|
197 |
+
// Clear any previous interval if exists
|
198 |
+
if (pollingInterval) clearInterval(pollingInterval);
|
199 |
+
pollingInterval = setInterval(async () => {
|
200 |
+
const res = await fetch("/api/results");
|
201 |
+
const results = await res.json();
|
202 |
+
renderResults(results);
|
203 |
+
// TASK 1: Stop polling if there are no pending videos
|
204 |
+
const pendingExists = Object.values(results).some(
|
205 |
+
(r) => r.status === "pending"
|
206 |
+
);
|
207 |
+
if (!pendingExists && Object.keys(results).length > 0) {
|
208 |
+
clearInterval(pollingInterval);
|
209 |
+
}
|
210 |
+
}, 5000);
|
211 |
+
}
|
212 |
|
213 |
function renderResults(results) {
|
214 |
resultsList.innerHTML = "";
|
|
|
216 |
const result = results[id];
|
217 |
const container = document.createElement("div");
|
218 |
container.className = "bg-white p-4 rounded shadow";
|
219 |
+
|
220 |
const header = document.createElement("div");
|
221 |
header.className = "flex justify-between items-center";
|
222 |
const title = document.createElement("h3");
|
223 |
title.className = "font-semibold text-lg";
|
224 |
title.textContent = result.generated_title || "Processing...";
|
225 |
header.appendChild(title);
|
226 |
+
|
227 |
+
// TASK 5: Add a "Copy Title" button
|
228 |
+
if (result.generated_title) {
|
229 |
+
const titleCopyBtn = document.createElement("button");
|
230 |
+
titleCopyBtn.textContent = "Copy Title";
|
231 |
+
titleCopyBtn.className = "bg-blue-500 text-white px-2 py-1 rounded ml-2";
|
232 |
+
titleCopyBtn.addEventListener("click", () => {
|
233 |
+
navigator.clipboard.writeText(result.generated_title);
|
234 |
+
});
|
235 |
+
header.appendChild(titleCopyBtn);
|
236 |
+
}
|
237 |
container.appendChild(header);
|
238 |
+
|
239 |
if (result.status === "completed") {
|
240 |
+
// Processed video display
|
241 |
const videoContainer = document.createElement("div");
|
242 |
videoContainer.className = "max-w-md mx-auto mt-2";
|
243 |
const videoElem = document.createElement("video");
|
|
|
245 |
videoElem.controls = true;
|
246 |
videoElem.className = "w-full";
|
247 |
videoElem.style.maxHeight = "500px";
|
248 |
+
|
249 |
+
// TASK 6: Make the video element draggable.
|
250 |
+
videoElem.setAttribute("draggable", "true");
|
251 |
+
videoElem.addEventListener("dragstart", (e) => {
|
252 |
+
e.dataTransfer.setData("text/uri-list", result.video_url);
|
253 |
+
});
|
254 |
videoContainer.appendChild(videoElem);
|
255 |
container.appendChild(videoContainer);
|
256 |
+
|
257 |
// Caption with a copy button
|
258 |
const captionDiv = document.createElement("div");
|
259 |
captionDiv.className = "mt-2 flex items-center";
|
|
|
280 |
pendingMsg.className = "text-gray-600 mt-2";
|
281 |
container.appendChild(pendingMsg);
|
282 |
}
|
283 |
+
|
284 |
+
// TASK 2: Add a "Delete Video" button to allow removal of processed videos.
|
285 |
+
if (result.status === "completed") {
|
286 |
+
const deleteBtn = document.createElement("button");
|
287 |
+
deleteBtn.textContent = "Delete Video";
|
288 |
+
deleteBtn.className = "bg-red-500 text-white px-2 py-1 rounded mt-2";
|
289 |
+
deleteBtn.addEventListener("click", async () => {
|
290 |
+
if (confirm("Are you sure you want to delete this video?")) {
|
291 |
+
const res = await fetch(`/api/video/${id}`, {
|
292 |
+
method: "DELETE",
|
293 |
+
});
|
294 |
+
if (res.ok) {
|
295 |
+
container.remove();
|
296 |
+
} else {
|
297 |
+
alert("Failed to delete video.");
|
298 |
+
}
|
299 |
+
}
|
300 |
+
});
|
301 |
+
container.appendChild(deleteBtn);
|
302 |
+
}
|
303 |
+
|
304 |
resultsList.appendChild(container);
|
305 |
}
|
306 |
}
|
307 |
|
308 |
+
// Settings Modal handlers
|
309 |
openSettings.addEventListener("click", async () => {
|
310 |
const res = await fetch("/api/settings");
|
311 |
const settings = await res.json();
|
|
|
333 |
});
|
334 |
|
335 |
// Connect to the logs WebSocket for live console output
|
336 |
+
const protocol = window.location.protocol === "https:" ? "wss:" : "ws:";
|
337 |
const logsSocket = new WebSocket(`${protocol}//${location.host}/ws/logs`);
|
338 |
+
logsSocket.onmessage = function (event) {
|
339 |
const newLog = document.createElement("div");
|
340 |
newLog.textContent = event.data;
|
341 |
consoleOutput.appendChild(newLog);
|