diff --git a/data/users.json b/data/users.json
index 0d3d075..6e4f265 100644
--- a/data/users.json
+++ b/data/users.json
@@ -6,12 +6,16 @@
"subscribers": [
"ma",
"miles"
+ ],
+ "watchHistory": [
+ 1757455619505
]
},
{
"id": 1757455788998,
"username": "ma",
"password": "$2b$10$FX01zPi0FXvHcXtcsX6t0.w0QHxIkkByhlX8tdx4fboQpnXtn.9a2",
- "subscribers": []
+ "subscribers": [],
+ "watchHistory": []
}
]
\ No newline at end of file
diff --git a/data/videos.json b/data/videos.json
index c721c36..44ab658 100644
--- a/data/videos.json
+++ b/data/videos.json
@@ -1,27 +1,12 @@
[
- {
- "id": 1757455491733,
- "title": "yapping-yapping-level-today.mp4",
- "filename": "1757455491493.mp4",
- "uploader": "miles",
- "likes": [
- "miles"
- ],
- "comments": [
- {
- "user": "miles",
- "text": "hi"
- }
- ],
- "thumbnail": "1757455491496.png"
- },
{
"id": 1757455619505,
"title": "SysDVR_2025_08_31_16_54_21.mp4",
"filename": "1757455619060.mp4",
"uploader": "miles",
"likes": [
- "ma"
+ "ma",
+ "miles"
],
"comments": [
{
@@ -33,7 +18,8 @@
"text": "hi"
}
],
- "thumbnail": "1757455619233.png"
+ "thumbnail": "1757455619233.png",
+ "_score": 0.2
},
{
"id": 1757455624902,
@@ -54,6 +40,7 @@
"text": "no"
}
],
- "thumbnail": "1757455624790.png"
+ "thumbnail": "1757455624790.png",
+ "_score": 0.4
}
]
\ No newline at end of file
diff --git a/public/admin.html b/public/admin.html
new file mode 100644
index 0000000..63504cd
--- /dev/null
+++ b/public/admin.html
@@ -0,0 +1,27 @@
+
+
+
+ Admin Panel - MiniTube
+
+
+
+
+ Admin Panel
+
+ Delete User Account
+
+
+
+
+
+
diff --git a/public/admin.js b/public/admin.js
new file mode 100644
index 0000000..cee9e65
--- /dev/null
+++ b/public/admin.js
@@ -0,0 +1,27 @@
+// Admin Panel JS
+
+document.getElementById('deleteUserForm').addEventListener('submit', async (e) => {
+ e.preventDefault();
+ const username = document.getElementById('deleteUsername').value.trim();
+ if (!username) return;
+ const res = await fetch('/api/admin/delete-user', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ username })
+ });
+ const data = await res.json();
+ document.getElementById('userDeleteMsg').textContent = data.success ? 'User deleted.' : (data.error || 'Error.');
+});
+
+document.getElementById('deleteVideoForm').addEventListener('submit', async (e) => {
+ e.preventDefault();
+ const id = document.getElementById('deleteVideoId').value.trim();
+ if (!id) return;
+ const res = await fetch('/api/admin/delete-video', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ id })
+ });
+ const data = await res.json();
+ document.getElementById('videoDeleteMsg').textContent = data.success ? 'Video deleted.' : (data.error || 'Error.');
+});
diff --git a/public/app.js b/public/app.js
index 2b3f9b5..9c9e4da 100644
--- a/public/app.js
+++ b/public/app.js
@@ -1,3 +1,102 @@
+// -------------------- Recommended Videos (Personalized) --------------------
+async function loadRecommendedIndex() {
+ const list = document.getElementById('recommendedVideos');
+ if (!list) return;
+ let videos = [];
+ let res;
+ try {
+ res = await fetch('/api/recommended');
+ if (res.ok) {
+ videos = await res.json();
+ } else {
+ res = await fetch('/api/videos');
+ videos = await res.json();
+ }
+ } catch {
+ res = await fetch('/api/videos');
+ videos = await res.json();
+ }
+ // Show top 6
+ const top = videos.slice(0, 6);
+ list.innerHTML = '';
+ for (const v of top) {
+ const li = document.createElement('li');
+ li.style.display = 'flex';
+ li.style.alignItems = 'center';
+ li.style.marginBottom = '1em';
+ li.innerHTML = `
+
+
+
+
${v.title}
+
by ${v.uploader}
+
${v.likes?.length || 0} likes
+
+
+ `;
+ list.appendChild(li);
+ }
+}
+// -------------------- Watch Later & History (localStorage) --------------------
+function getWatchLater() {
+ return JSON.parse(localStorage.getItem('watchLater') || '[]');
+}
+function setWatchLater(arr) {
+ localStorage.setItem('watchLater', JSON.stringify(arr));
+}
+function addToWatchLater(video) {
+ let arr = getWatchLater();
+ if (!arr.find(v => v.id === video.id)) {
+ arr.unshift(video);
+ setWatchLater(arr.slice(0, 20));
+ }
+ renderWatchLater();
+}
+function removeFromWatchLater(id) {
+ let arr = getWatchLater().filter(v => v.id !== id);
+ setWatchLater(arr);
+ renderWatchLater();
+}
+function renderWatchLater() {
+ const list = document.getElementById('watchLaterList');
+ if (!list) return;
+ const arr = getWatchLater();
+ list.innerHTML = '';
+ arr.forEach(v => {
+ const li = document.createElement('li');
+ li.innerHTML = `${v.title} `;
+ list.appendChild(li);
+ });
+ list.querySelectorAll('.removeLater').forEach(btn => {
+ btn.onclick = e => removeFromWatchLater(btn.dataset.id);
+ });
+}
+
+function getWatchHistory() {
+ return JSON.parse(localStorage.getItem('watchHistory') || '[]');
+}
+function setWatchHistory(arr) {
+ localStorage.setItem('watchHistory', JSON.stringify(arr));
+}
+function addToWatchHistory(video) {
+ let arr = getWatchHistory();
+ arr = arr.filter(v => v.id !== video.id);
+ arr.unshift(video);
+ setWatchHistory(arr.slice(0, 20));
+ renderWatchHistory();
+}
+function renderWatchHistory() {
+ const list = document.getElementById('watchHistoryList');
+ if (!list) return;
+ const arr = getWatchHistory();
+ list.innerHTML = '';
+ arr.forEach(v => {
+ const li = document.createElement('li');
+ li.innerHTML = `${v.title}`;
+ list.appendChild(li);
+ });
+}
+
// -------------------- Load Videos on Homepage --------------------
async function loadVideos() {
try {
@@ -13,9 +112,20 @@ async function loadVideos() {
${v.title}
by ${v.uploader}
+
`;
list.appendChild(li);
});
+ // Add event listeners for Watch Later
+ list.querySelectorAll('.addLater').forEach(btn => {
+ btn.onclick = async e => {
+ const id = btn.dataset.id;
+ const v = videos.find(v => v.id == id);
+ if (v) addToWatchLater(v);
+ };
+ });
+ renderWatchLater();
+ renderWatchHistory();
} catch (err) {
console.error("Error loading videos:", err);
}
@@ -93,6 +203,9 @@ document.addEventListener("DOMContentLoaded", () => {
// Homepage video list
loadVideos();
+ // Recommended sidebar
+ loadRecommendedIndex();
+
// Login form
const loginForm = document.getElementById("loginForm");
if (loginForm) loginForm.addEventListener("submit", loginUser);
@@ -108,4 +221,8 @@ document.addEventListener("DOMContentLoaded", () => {
// Upload form
const uploadForm = document.getElementById("uploadForm");
if (uploadForm) uploadForm.addEventListener("submit", uploadVideo);
+
+ // Render watch later/history on all pages
+ renderWatchLater();
+ renderWatchHistory();
});
diff --git a/public/index.html b/public/index.html
index d719925..ff87742 100644
--- a/public/index.html
+++ b/public/index.html
@@ -6,12 +6,22 @@
- MiniTube
-
-
-
-
-
-
+
+
+ MiniTube
+
+
+
+
+
+
+