diff --git a/backend/app/controllers/media.controller.js b/backend/app/controllers/media.controller.js index ad8bf7d..8a1db6e 100644 --- a/backend/app/controllers/media.controller.js +++ b/backend/app/controllers/media.controller.js @@ -54,4 +54,22 @@ export async function getThumbnail(req, res) { console.error("Error fetching thumbnail:", error); res.status(500).json({ error: "Internal server error while fetching thumbnail." }); } +} + +export async function getVideo(req, res) { + const file = req.params.file; + const filePath = path.join('/app/app/uploads/videos', file); + + try { + res.sendFile(filePath, (err) => { + if (err) { + console.error("Error sending video:", err); + } else { + console.log("Video sent successfully."); + } + }); + } catch (error) { + console.error("Error fetching video:", error); + res.status(500).json({error: "Internal server error while fetching video."}); + } } \ No newline at end of file diff --git a/backend/app/controllers/recommendation.controller.js b/backend/app/controllers/recommendation.controller.js index 7567cb8..4aea0e3 100644 --- a/backend/app/controllers/recommendation.controller.js +++ b/backend/app/controllers/recommendation.controller.js @@ -53,6 +53,33 @@ export async function getTrendingVideos(req, res) { `; let result = await client.query(queryTrendingVideos); const trendingVideos = result.rows; + + for (let video of trendingVideos) { + // Get the number of views for each video + let viewsQuery = `SELECT COUNT(*) AS view_count FROM history WHERE video = $1;`; + let viewsResult = await client.query(viewsQuery, [video.id]); + video.views = viewsResult.rows[0].view_count; + + // Get the creator of each video + let creatorQuery = `SELECT c.id, c.name FROM channels c JOIN videos v ON c.id = v.channel WHERE v.id = $1;`; + let creatorResult = await client.query(creatorQuery, [video.id]); + if (creatorResult.rows.length > 0) { + video.creator = creatorResult.rows[0]; + } else { + video.creator = {id: null, name: "Unknown"}; + } + + // GET THE PROFILE PICTURE OF THE CREATOR + let profilePictureQuery = `SELECT u.picture FROM users u JOIN channels c ON u.id = c.owner WHERE c.id = $1;`; + let profilePictureResult = await client.query(profilePictureQuery, [video.creator.id]); + if (profilePictureResult.rows.length > 0) { + video.creator.profilePicture = profilePictureResult.rows[0].picture; + } else { + video.creator.profilePicture = null; // Default or placeholder image can be set here + } + + } + res.status(200).json(trendingVideos); } catch (error) { console.error("Error fetching trending videos:", error); diff --git a/backend/app/controllers/video.controller.js b/backend/app/controllers/video.controller.js index 0f31563..df02075 100644 --- a/backend/app/controllers/video.controller.js +++ b/backend/app/controllers/video.controller.js @@ -87,6 +87,40 @@ export async function getById(req, res) { const query = `SELECT * FROM videos WHERE id = ${id}`; const result = await client.query(query); const video = result.rows[0]; + + // GET VIEWS AND LIKES COUNT + const viewsQuery = `SELECT COUNT(*) AS view_count FROM history WHERE video = ${id}`; + const viewsResult = await client.query(viewsQuery); + const likesQuery = `SELECT COUNT(*) AS like_count FROM likes WHERE video = ${id}`; + const likesResult = await client.query(likesQuery); + video.views = viewsResult.rows[0].view_count; + video.likes = likesResult.rows[0].like_count; + + // GET COMMENTS + const commentsQuery = `SELECT c.id, c.content, c.created_at, u.username FROM comments c JOIN users u ON c.author = u.id WHERE c.video = ${id}`; + const commentsResult = await client.query(commentsQuery); + video.comments = commentsResult.rows; + + // GET CREATOR + const creatorQuery = `SELECT c.id, c.name, c.owner FROM channels c JOIN videos v ON c.id = v.channel WHERE v.id = ${id}`; + const creatorResult = await client.query(creatorQuery); + video.creator = creatorResult.rows[0]; + + // GET CREATOR PROFILE PICTURE + const profilePictureQuery = `SELECT picture FROM users WHERE id = ${video.creator.owner}`; + const profilePictureResult = await client.query(profilePictureQuery); + video.creator.profile_picture = profilePictureResult.rows[0].picture; + + // GET CREATOR SUBSCRIBERS COUNT + const subscribersQuery = `SELECT COUNT(*) AS subscriber_count FROM subscriptions WHERE channel = ${video.creator.id}`; + const subscribersResult = await client.query(subscribersQuery); + video.creator.subscribers = subscribersResult.rows[0].subscriber_count; + + // GET TAGS + const tagsQuery = `SELECT t.name FROM tags t JOIN video_tags vt ON t.id = vt.tag WHERE vt.video = ${id}`; + const tagsResult = await client.query(tagsQuery); + video.tags = tagsResult.rows.map(tag => tag.name); + logger.write("successfully get video " + id, 200); res.status(200).json(video); } diff --git a/backend/app/routes/media.routes.js b/backend/app/routes/media.routes.js index 0d25e83..44e0a60 100644 --- a/backend/app/routes/media.routes.js +++ b/backend/app/routes/media.routes.js @@ -1,11 +1,11 @@ import {Router} from 'express'; -import {getProfilePicture, getThumbnail} from "../controllers/media.controller.js"; +import {getProfilePicture, getThumbnail, getVideo} from "../controllers/media.controller.js"; const router = Router(); router.get("/profile/:file", getProfilePicture); -//router.get("/video/:file", getVideo); +router.get("/video/:file", getVideo); router.get("/thumbnail/:file", getThumbnail); diff --git a/backend/app/routes/video.route.js b/backend/app/routes/video.route.js index 4c4ec74..0b09a49 100644 --- a/backend/app/routes/video.route.js +++ b/backend/app/routes/video.route.js @@ -33,7 +33,7 @@ router.post("/", [videoUpload.single('file'), addLogger, isTokenValid, VideoCrea router.post("/thumbnail", [thumbnailUpload.single('file'), addLogger, isTokenValid, VideoThumbnail.video, Video.channel, validator, doChannelExistBody, isOwner, doVideoExists], uploadThumbnail ) // GET BY ID -router.get("/:id", [addLogger, isTokenValid, Video.id, validator, doVideoExistsParam], getById); +router.get("/:id", [addLogger, Video.id, validator, doVideoExistsParam], getById); // GET BY CHANNEL router.get("/channel/:id", [addLogger, isTokenValid, Channel.id, validator, doChannelExists], getByChannel); diff --git a/backend/logs/access.log b/backend/logs/access.log index c01bf83..cb31ffe 100644 --- a/backend/logs/access.log +++ b/backend/logs/access.log @@ -119,3 +119,299 @@ [2025-07-18 21:10:44.250] [undefined] POST(/login): Successfully logged in with status 200 [2025-07-18 21:24:17.066] [undefined] POST(/login): try to login with username 'sacha' [2025-07-18 21:24:17.118] [undefined] POST(/login): Successfully logged in with status 200 +[2025-07-19 15:14:40.732] [undefined] POST(/): try to upload video with status undefined +[2025-07-19 15:14:40.735] [undefined] POST(/): successfully uploaded video with status 200 +[2025-07-19 15:14:54.976] [undefined] POST(/thumbnail): try to add thumbnail to video 3 +[2025-07-19 15:14:54.979] [undefined] POST(/thumbnail): successfully uploaded thumbnail with status 200 +[2025-07-19 15:43:29.882] [undefined] POST(/login): try to login with username 'astria' +[2025-07-19 15:43:29.931] [undefined] POST(/login): Successfully logged in with status 200 +[2025-07-19 15:48:36.098] [undefined] GET(/:id): Invalid token with status 401 +[2025-07-19 15:48:40.465] [undefined] GET(/:id): Invalid token with status 401 +[2025-07-19 15:49:34.548] [undefined] GET(/:id): try to get video 2 +[2025-07-19 15:49:34.555] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 15:52:40.713] [undefined] GET(/:id): try to get video 2 +[2025-07-19 15:53:16.543] [undefined] GET(/:id): try to get video 2 +[2025-07-19 15:53:16.553] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 15:53:55.670] [undefined] GET(/:id): try to get video 2 +[2025-07-19 15:53:55.677] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 15:54:19.757] [undefined] GET(/:id): try to get video 2 +[2025-07-19 15:54:19.764] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 15:54:24.800] [undefined] GET(/:id): try to get video 2 +[2025-07-19 15:54:24.807] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 15:57:18.070] [undefined] GET(/:id): try to get video 2 +[2025-07-19 15:57:18.077] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 15:57:18.097] [undefined] GET(/:id): try to get video 2 +[2025-07-19 15:57:18.104] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 15:57:18.115] [undefined] GET(/:id): try to get video 2 +[2025-07-19 15:57:18.121] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 15:57:18.137] [undefined] GET(/:id): try to get video 2 +[2025-07-19 15:57:18.144] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 15:57:18.155] [undefined] GET(/:id): try to get video 2 +[2025-07-19 15:57:18.161] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 15:57:18.172] [undefined] GET(/:id): try to get video 2 +[2025-07-19 15:57:18.178] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 15:57:18.189] [undefined] GET(/:id): try to get video 2 +[2025-07-19 15:57:18.195] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 15:57:18.206] [undefined] GET(/:id): try to get video 2 +[2025-07-19 15:57:18.213] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 15:57:18.224] [undefined] GET(/:id): try to get video 2 +[2025-07-19 15:57:18.230] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 15:57:18.242] [undefined] GET(/:id): try to get video 2 +[2025-07-19 15:57:18.249] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 15:57:18.275] [undefined] GET(/:id): try to get video 2 +[2025-07-19 15:57:18.282] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 15:57:18.293] [undefined] GET(/:id): try to get video 2 +[2025-07-19 15:57:18.300] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 15:57:18.310] [undefined] GET(/:id): try to get video 2 +[2025-07-19 15:57:18.317] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 15:57:18.337] [undefined] GET(/:id): try to get video 2 +[2025-07-19 15:57:18.344] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 15:57:18.354] [undefined] GET(/:id): try to get video 2 +[2025-07-19 15:57:18.361] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 15:57:18.373] [undefined] GET(/:id): try to get video 2 +[2025-07-19 15:57:18.379] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 15:57:18.411] [undefined] GET(/:id): try to get video 2 +[2025-07-19 15:57:18.417] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 15:57:18.428] [undefined] GET(/:id): try to get video 2 +[2025-07-19 15:57:18.436] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 15:57:18.457] [undefined] GET(/:id): try to get video 2 +[2025-07-19 15:57:18.463] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 15:57:18.475] [undefined] GET(/:id): try to get video 2 +[2025-07-19 15:57:18.482] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 15:57:18.494] [undefined] GET(/:id): try to get video 2 +[2025-07-19 15:57:18.501] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 15:57:18.518] [undefined] GET(/:id): try to get video 2 +[2025-07-19 15:57:18.525] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 15:57:18.537] [undefined] GET(/:id): try to get video 2 +[2025-07-19 15:57:18.544] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 15:57:18.554] [undefined] GET(/:id): try to get video 2 +[2025-07-19 15:57:18.561] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 15:57:18.578] [undefined] GET(/:id): try to get video 2 +[2025-07-19 15:57:18.585] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 15:57:18.596] [undefined] GET(/:id): try to get video 2 +[2025-07-19 15:57:18.603] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 15:57:18.615] [undefined] GET(/:id): try to get video 2 +[2025-07-19 15:57:18.622] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 15:57:18.639] [undefined] GET(/:id): try to get video 2 +[2025-07-19 15:57:18.646] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 15:57:18.657] [undefined] GET(/:id): try to get video 2 +[2025-07-19 15:57:18.663] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 15:57:18.675] [undefined] GET(/:id): try to get video 2 +[2025-07-19 15:57:18.682] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 15:57:18.705] [undefined] GET(/:id): try to get video 2 +[2025-07-19 15:57:18.711] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 15:57:18.722] [undefined] GET(/:id): try to get video 2 +[2025-07-19 15:57:18.729] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 15:57:18.739] [undefined] GET(/:id): try to get video 2 +[2025-07-19 15:57:18.744] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 15:57:18.764] [undefined] GET(/:id): try to get video 2 +[2025-07-19 15:57:18.771] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 15:57:18.782] [undefined] GET(/:id): try to get video 2 +[2025-07-19 15:57:18.788] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 15:57:18.797] [undefined] GET(/:id): try to get video 2 +[2025-07-19 15:57:18.803] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 15:57:18.820] [undefined] GET(/:id): try to get video 2 +[2025-07-19 15:57:18.826] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 15:57:18.836] [undefined] GET(/:id): try to get video 2 +[2025-07-19 15:57:18.843] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 15:57:18.852] [undefined] GET(/:id): try to get video 2 +[2025-07-19 15:57:18.858] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 15:57:18.867] [undefined] GET(/:id): try to get video 2 +[2025-07-19 15:57:18.874] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 15:57:18.884] [undefined] GET(/:id): try to get video 2 +[2025-07-19 15:57:18.890] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 15:57:18.899] [undefined] GET(/:id): try to get video 2 +[2025-07-19 15:57:18.905] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 15:57:18.914] [undefined] GET(/:id): try to get video 2 +[2025-07-19 15:57:18.921] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 15:57:18.931] [undefined] GET(/:id): try to get video 2 +[2025-07-19 15:57:18.937] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 15:58:14.286] [undefined] GET(/:id): try to get video 2 +[2025-07-19 15:58:14.295] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 15:58:25.380] [undefined] GET(/:id): try to get video 2 +[2025-07-19 15:58:25.387] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 16:00:23.446] [undefined] GET(/:id): try to get video 2 +[2025-07-19 16:00:23.456] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 16:03:22.242] [undefined] GET(/:id): try to get video 2 +[2025-07-19 16:03:22.251] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 16:04:14.786] [undefined] GET(/:id): try to get video 2 +[2025-07-19 16:04:14.796] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 16:04:23.626] [undefined] GET(/:id): try to get video 2 +[2025-07-19 16:04:23.634] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 16:05:35.520] [undefined] GET(/:id): try to get video 2 +[2025-07-19 16:05:35.527] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 16:06:05.466] [undefined] GET(/:id): try to get video 2 +[2025-07-19 16:06:05.472] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 16:06:56.551] [undefined] GET(/:id): try to get video 2 +[2025-07-19 16:06:56.557] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 16:07:18.833] [undefined] GET(/:id): try to get video 2 +[2025-07-19 16:07:18.841] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 16:08:57.061] [undefined] GET(/:id): try to get video 2 +[2025-07-19 16:08:57.068] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 16:10:51.224] [undefined] GET(/:id): try to get video 2 +[2025-07-19 16:10:51.231] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 16:11:03.522] [undefined] GET(/:id): try to get video 2 +[2025-07-19 16:11:03.530] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 16:11:16.054] [undefined] GET(/:id): try to get video 2 +[2025-07-19 16:11:16.060] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 16:11:49.282] [undefined] GET(/:id): try to get video 2 +[2025-07-19 16:11:49.288] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 16:12:06.493] [undefined] GET(/:id): try to get video 2 +[2025-07-19 16:12:06.499] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 16:12:20.606] [undefined] GET(/:id): try to get video 2 +[2025-07-19 16:12:20.613] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 16:12:49.479] [undefined] GET(/:id): try to get video 2 +[2025-07-19 16:12:49.485] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 16:13:01.725] [undefined] GET(/:id): try to get video 2 +[2025-07-19 16:13:01.732] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 16:14:41.215] [undefined] GET(/:id): try to get video 2 +[2025-07-19 16:14:41.222] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 16:15:19.018] [undefined] GET(/:id): try to get video 2 +[2025-07-19 16:15:19.026] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 16:15:32.287] [undefined] GET(/:id): try to get video 2 +[2025-07-19 16:15:32.294] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 16:15:44.463] [undefined] GET(/:id): try to get video 2 +[2025-07-19 16:15:44.471] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 16:16:38.876] [undefined] GET(/:id): try to get video 2 +[2025-07-19 16:16:38.882] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 16:17:34.301] [undefined] GET(/:id): try to get video 2 +[2025-07-19 16:17:34.308] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 16:17:46.051] [undefined] GET(/:id): try to get video 2 +[2025-07-19 16:17:46.058] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 16:18:47.128] [undefined] GET(/:id): try to get video 2 +[2025-07-19 16:18:47.135] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 16:19:09.271] [undefined] GET(/:id): try to get video 2 +[2025-07-19 16:19:09.277] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 16:19:35.974] [undefined] GET(/:id): try to get video 2 +[2025-07-19 16:19:35.981] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 16:20:18.884] [undefined] GET(/:id): try to get video 2 +[2025-07-19 16:20:18.891] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 16:20:50.151] [undefined] GET(/:id): try to get video 2 +[2025-07-19 16:20:50.157] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 16:21:07.517] [undefined] GET(/:id): try to get video 2 +[2025-07-19 16:21:07.527] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 16:21:14.602] [undefined] GET(/:id): try to get video 2 +[2025-07-19 16:21:14.610] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 16:21:21.513] [undefined] GET(/:id): try to get video 2 +[2025-07-19 16:21:21.520] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 16:21:44.213] [undefined] GET(/:id): try to get video 2 +[2025-07-19 16:21:44.219] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 16:22:48.209] [undefined] GET(/:id): try to get video 2 +[2025-07-19 16:22:48.215] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 16:22:54.655] [undefined] GET(/:id): try to get video 3 +[2025-07-19 16:22:54.662] [undefined] GET(/:id): successfully get video 3 with status 200 +[2025-07-19 16:24:54.112] [undefined] GET(/:id): try to get video 3 +[2025-07-19 16:24:54.119] [undefined] GET(/:id): successfully get video 3 with status 200 +[2025-07-19 16:24:58.700] [undefined] GET(/:id): try to get video 3 +[2025-07-19 16:24:58.712] [undefined] GET(/:id): successfully get video 3 with status 200 +[2025-07-19 16:26:34.718] [undefined] GET(/:id): try to get video 3 +[2025-07-19 16:26:34.725] [undefined] GET(/:id): successfully get video 3 with status 200 +[2025-07-19 16:26:48.648] [undefined] GET(/:id): try to get video 3 +[2025-07-19 16:26:48.656] [undefined] GET(/:id): successfully get video 3 with status 200 +[2025-07-19 16:27:34.841] [undefined] GET(/:id): try to get video 3 +[2025-07-19 16:27:34.848] [undefined] GET(/:id): successfully get video 3 with status 200 +[2025-07-19 16:27:43.336] [undefined] GET(/:id): try to get video 3 +[2025-07-19 16:27:43.343] [undefined] GET(/:id): successfully get video 3 with status 200 +[2025-07-19 16:31:05.829] [undefined] GET(/:id): try to get video 3 +[2025-07-19 16:31:05.841] [undefined] GET(/:id): successfully get video 3 with status 200 +[2025-07-19 16:32:29.981] [undefined] GET(/:id): try to get video 3 +[2025-07-19 16:32:29.989] [undefined] GET(/:id): successfully get video 3 with status 200 +[2025-07-19 16:32:46.293] [undefined] GET(/:id): try to get video 3 +[2025-07-19 16:32:46.302] [undefined] GET(/:id): successfully get video 3 with status 200 +[2025-07-19 16:33:28.372] [undefined] GET(/:id): try to get video 3 +[2025-07-19 16:33:28.381] [undefined] GET(/:id): successfully get video 3 with status 200 +[2025-07-19 16:34:43.065] [undefined] GET(/:id): try to get video 3 +[2025-07-19 16:34:43.073] [undefined] GET(/:id): successfully get video 3 with status 200 +[2025-07-19 16:35:35.922] [undefined] GET(/:id): try to get video 3 +[2025-07-19 16:35:35.929] [undefined] GET(/:id): successfully get video 3 with status 200 +[2025-07-19 16:36:08.537] [undefined] GET(/:id): try to get video 3 +[2025-07-19 16:36:08.545] [undefined] GET(/:id): successfully get video 3 with status 200 +[2025-07-19 16:37:31.020] [undefined] GET(/:id): try to get video 3 +[2025-07-19 16:37:31.027] [undefined] GET(/:id): successfully get video 3 with status 200 +[2025-07-19 16:37:42.940] [undefined] GET(/:id): try to get video 3 +[2025-07-19 16:37:42.947] [undefined] GET(/:id): successfully get video 3 with status 200 +[2025-07-19 16:38:06.260] [undefined] GET(/:id): try to get video 3 +[2025-07-19 16:38:06.268] [undefined] GET(/:id): successfully get video 3 with status 200 +[2025-07-19 16:38:31.413] [undefined] GET(/:id): try to get video 3 +[2025-07-19 16:38:31.420] [undefined] GET(/:id): successfully get video 3 with status 200 +[2025-07-19 16:38:41.363] [undefined] GET(/:id): try to get video 3 +[2025-07-19 16:38:41.370] [undefined] GET(/:id): successfully get video 3 with status 200 +[2025-07-19 16:39:32.126] [undefined] GET(/:id): try to get video 3 +[2025-07-19 16:39:32.133] [undefined] GET(/:id): successfully get video 3 with status 200 +[2025-07-19 16:39:51.345] [undefined] GET(/:id): try to get video 3 +[2025-07-19 16:39:51.353] [undefined] GET(/:id): successfully get video 3 with status 200 +[2025-07-19 16:40:24.085] [undefined] GET(/:id): try to get video 3 +[2025-07-19 16:40:24.092] [undefined] GET(/:id): successfully get video 3 with status 200 +[2025-07-19 16:43:25.763] [undefined] GET(/:id): try to get video 3 +[2025-07-19 16:43:25.769] [undefined] GET(/:id): successfully get video 3 with status 200 +[2025-07-19 16:43:48.531] [undefined] GET(/:id): try to get video 3 +[2025-07-19 16:43:48.538] [undefined] GET(/:id): successfully get video 3 with status 200 +[2025-07-19 16:44:02.412] [undefined] GET(/:id): try to get video 3 +[2025-07-19 16:44:02.421] [undefined] GET(/:id): successfully get video 3 with status 200 +[2025-07-19 16:44:03.840] [undefined] POST(/:id/subscribe): Invalid token with status 401 +[2025-07-19 16:44:51.156] [undefined] GET(/:id): try to get video 3 +[2025-07-19 16:44:51.163] [undefined] GET(/:id): successfully get video 3 with status 200 +[2025-07-19 16:44:52.817] [undefined] POST(/:id/subscribe): try to toggle subscription for channel with id 2 +[2025-07-19 16:46:18.109] [undefined] GET(/:id): try to get video 3 +[2025-07-19 16:46:18.116] [undefined] GET(/:id): successfully get video 3 with status 200 +[2025-07-19 16:46:19.593] [undefined] POST(/:id/subscribe): try to toggle subscription for channel with id 2 +[2025-07-19 16:46:19.600] [undefined] POST(/:id/subscribe): Successfully subscribed to channel with status 200 +[2025-07-19 16:47:01.900] [undefined] GET(/:id): try to get video 3 +[2025-07-19 16:47:01.908] [undefined] GET(/:id): successfully get video 3 with status 200 +[2025-07-19 16:47:17.720] [undefined] GET(/:id): try to get video 3 +[2025-07-19 16:47:17.727] [undefined] GET(/:id): successfully get video 3 with status 200 +[2025-07-19 16:48:15.720] [undefined] GET(/:id): try to get video 3 +[2025-07-19 16:48:15.728] [undefined] GET(/:id): successfully get video 3 with status 200 +[2025-07-19 16:48:32.578] [undefined] GET(/:id): try to get video 3 +[2025-07-19 16:48:32.586] [undefined] GET(/:id): successfully get video 3 with status 200 +[2025-07-19 16:50:13.518] [undefined] GET(/:id): try to get video 2 +[2025-07-19 16:50:13.526] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 16:50:16.236] [undefined] GET(/:id): try to get video 3 +[2025-07-19 16:50:16.244] [undefined] GET(/:id): successfully get video 3 with status 200 +[2025-07-19 16:50:28.980] [undefined] GET(/:id): try to get video 3 +[2025-07-19 16:50:28.988] [undefined] GET(/:id): successfully get video 3 with status 200 +[2025-07-19 16:51:06.208] [undefined] GET(/:id): try to get video 3 +[2025-07-19 16:51:06.216] [undefined] GET(/:id): successfully get video 3 with status 200 +[2025-07-19 16:51:17.351] [undefined] GET(/:id): try to get video 3 +[2025-07-19 16:51:17.359] [undefined] GET(/:id): successfully get video 3 with status 200 +[2025-07-19 16:51:24.016] [undefined] GET(/:id): try to get video 3 +[2025-07-19 16:51:24.024] [undefined] GET(/:id): successfully get video 3 with status 200 +[2025-07-19 16:51:45.734] [undefined] GET(/:id): try to get video 3 +[2025-07-19 16:51:45.742] [undefined] GET(/:id): successfully get video 3 with status 200 +[2025-07-19 16:52:19.574] [undefined] GET(/:id): try to get video 3 +[2025-07-19 16:52:19.582] [undefined] GET(/:id): successfully get video 3 with status 200 +[2025-07-19 16:52:36.473] [undefined] GET(/:id): try to get video 3 +[2025-07-19 16:52:36.480] [undefined] GET(/:id): successfully get video 3 with status 200 +[2025-07-19 16:53:22.564] [undefined] GET(/:id): try to get video 3 +[2025-07-19 16:53:22.572] [undefined] GET(/:id): successfully get video 3 with status 200 +[2025-07-19 16:53:55.967] [undefined] GET(/:id): try to get video 3 +[2025-07-19 16:53:55.975] [undefined] GET(/:id): successfully get video 3 with status 200 +[2025-07-19 16:54:24.653] [undefined] GET(/:id): try to get video 3 +[2025-07-19 16:54:24.661] [undefined] GET(/:id): successfully get video 3 with status 200 +[2025-07-19 16:54:30.471] [undefined] GET(/:id): try to get video 3 +[2025-07-19 16:54:30.479] [undefined] GET(/:id): successfully get video 3 with status 200 +[2025-07-19 16:54:36.682] [undefined] GET(/:id): try to get video 3 +[2025-07-19 16:54:36.690] [undefined] GET(/:id): successfully get video 3 with status 200 +[2025-07-19 16:54:50.298] [undefined] GET(/:id): try to get video 3 +[2025-07-19 16:54:50.307] [undefined] GET(/:id): successfully get video 3 with status 200 +[2025-07-19 16:55:01.575] [undefined] POST(/login): try to login with username 'astria' +[2025-07-19 16:55:01.627] [undefined] POST(/login): Successfully logged in with status 200 +[2025-07-19 16:55:04.312] [undefined] GET(/:id): try to get video 3 +[2025-07-19 16:55:04.319] [undefined] GET(/:id): successfully get video 3 with status 200 +[2025-07-19 16:55:12.078] [undefined] GET(/:id): try to get video 3 +[2025-07-19 16:56:21.497] [undefined] GET(/:id): try to get video 3 +[2025-07-19 16:56:21.509] [undefined] GET(/:id): successfully get video 3 with status 200 +[2025-07-19 16:56:53.274] [undefined] GET(/:id): try to get video 3 +[2025-07-19 16:56:53.282] [undefined] GET(/:id): successfully get video 3 with status 200 +[2025-07-19 16:56:54.853] [undefined] GET(/:id/like): try to toggle like on video 3 +[2025-07-19 16:56:54.861] [undefined] GET(/:id/like): no likes found adding likes for video 3 with status 200 +[2025-07-19 16:56:56.605] [undefined] GET(/:id): try to get video 3 +[2025-07-19 16:56:56.613] [undefined] GET(/:id): successfully get video 3 with status 200 +[2025-07-19 16:57:32.256] [undefined] GET(/:id): try to get video 2 +[2025-07-19 16:57:32.263] [undefined] GET(/:id): successfully get video 2 with status 200 +[2025-07-19 17:02:09.826] [undefined] POST(/thumbnail): try to add thumbnail to video 3 +[2025-07-19 17:02:09.828] [undefined] POST(/thumbnail): successfully uploaded thumbnail with status 200 +[2025-07-19 17:02:23.623] [undefined] POST(/thumbnail): try to add thumbnail to video 2 +[2025-07-19 17:02:23.626] [undefined] POST(/thumbnail): successfully uploaded thumbnail with status 200 +[2025-07-19 17:02:29.733] [undefined] GET(/:id): try to get video 3 +[2025-07-19 17:02:29.739] [undefined] GET(/:id): successfully get video 3 with status 200 diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 36d6a25..5b114a4 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -6,3 +6,4 @@ RUN npm install COPY . . EXPOSE 5173 CMD ["npm", "run", "dev"] + diff --git a/frontend/rebuild.sh b/frontend/rebuild.sh new file mode 100755 index 0000000..1513236 --- /dev/null +++ b/frontend/rebuild.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +echo "🧹 Nettoyage des caches..." +rm -rf dist +rm -rf node_modules/.vite +rm -rf node_modules/.cache + +echo "📦 Build de l'application..." +npm run build + +echo "🔄 Redémarrage du container frontend..." +cd ../ +sudo docker-compose restart frontend + +echo "✅ Build terminé et container redémarré !" diff --git a/frontend/src/assets/svg/play.svg b/frontend/src/assets/svg/play.svg new file mode 100644 index 0000000..1dd584c --- /dev/null +++ b/frontend/src/assets/svg/play.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/components/Recommendations.jsx b/frontend/src/components/Recommendations.jsx index 108ce3f..6a118dc 100644 --- a/frontend/src/components/Recommendations.jsx +++ b/frontend/src/components/Recommendations.jsx @@ -2,9 +2,18 @@ export default function Recommendations({videos}) { + + return (

Recommendations

+
+
+ {videos && videos.map((video, index) => ( + + ))} +
+
) diff --git a/frontend/src/components/TopCreators.jsx b/frontend/src/components/TopCreators.jsx new file mode 100644 index 0000000..aa3305e --- /dev/null +++ b/frontend/src/components/TopCreators.jsx @@ -0,0 +1,18 @@ + + +export default function TopCreators({ creators }) { + return ( +
+

Top Creators

+
+ {creators && creators.map((creator, index) => ( +
+ {creator.name} +

{creator.name}

+ {creator.subscribers} subscribers +
+ ))} +
+
+ ); +} \ No newline at end of file diff --git a/frontend/src/components/TrendingVideos.jsx b/frontend/src/components/TrendingVideos.jsx new file mode 100644 index 0000000..2b2304f --- /dev/null +++ b/frontend/src/components/TrendingVideos.jsx @@ -0,0 +1,17 @@ +import VideoCard from "./VideoCard.jsx"; + + +export default function TrendingVideos({ videos }) { + + return ( +
+

Tendances

+
+ {videos && videos.map((video, index) => ( + + ))} +
+
+ ); + +} \ No newline at end of file diff --git a/frontend/src/components/VideoCard.jsx b/frontend/src/components/VideoCard.jsx index 531d448..058c57d 100644 --- a/frontend/src/components/VideoCard.jsx +++ b/frontend/src/components/VideoCard.jsx @@ -1,12 +1,27 @@ - +import { useNavigate } from 'react-router-dom'; export default function VideoCard({ video }) { + const navigation = useNavigate(); + const handleClick = () => { + navigation(`/video/${video.id}`, { + state: { video } + }) + } return ( -
- {video.title} -

{video.title}

-

{video.description}

- {video.views} views +
+
+ {video.title} +
+

{video.title}

+
+ {video.title} + {video.creator.name} + {video.views} vues +
); } \ No newline at end of file diff --git a/frontend/src/index.css b/frontend/src/index.css index 1c25b9e..9a9cfa1 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -10,6 +10,27 @@ src: url('assets/fonts/Montserrat/Montserrat.ttf') format('truetype'); } +.glassmorphism { + border-radius: 15px; + border: 2px solid rgba(239, 239, 239, 0.60); + background: linear-gradient(93deg, rgba(239, 239, 239, 0.06) 0%, rgba(239, 239, 239, 0.01) 100%); + backdrop-filter: blur(27.5px); +} + +.glassmorphism-rounded-sm { + border-radius: 5px; + border: 1px solid rgba(239, 239, 239, 0.60); + background: linear-gradient(93deg, rgba(239, 239, 239, 0.06) 0%, rgba(239, 239, 239, 0.01) 100%); + backdrop-filter: blur(27.5px); +} + +.glassmorphism-rounded-md { + border-radius: 10px; + border: 1px solid rgba(239, 239, 239, 0.60); + background: linear-gradient(93deg, rgba(239, 239, 239, 0.06) 0%, rgba(239, 239, 239, 0.01) 100%); + backdrop-filter: blur(27.5px); +} + @theme { /* Fonts */ --font-inter: 'Inter', sans-serif; diff --git a/frontend/src/pages/Home.jsx b/frontend/src/pages/Home.jsx index c8afa3c..78123cd 100644 --- a/frontend/src/pages/Home.jsx +++ b/frontend/src/pages/Home.jsx @@ -3,6 +3,8 @@ import HeroImage from '../assets/img/hero.png'; import Recommendations from "../components/Recommendations.jsx"; import {useState, useEffect} from "react"; import { useAuth } from '../contexts/AuthContext'; +import TopCreators from "../components/TopCreators.jsx"; +import TrendingVideos from "../components/TrendingVideos.jsx"; export default function Home() { const { isAuthenticated, user } = useAuth(); @@ -23,6 +25,17 @@ export default function Home() { } finally { setLoading(false); } + + try { + const trendingResponse = await fetch('/api/recommendations/trending'); + const trendingData = await trendingResponse.json(); + setTrendingVideos(trendingData); + } catch (error) { + console.error('Error fetching trending videos:', error); + } finally { + setLoading(false); + } + }; fetchData(); }, []); @@ -31,35 +44,46 @@ export default function Home() {
-
- +
+ + {/* Hero section */} +
+ - {isAuthenticated ? ( -

- Bienvenue {user?.username} ! -

- ) : ( - <> + {isAuthenticated ? (

- Regarder des vidéos comme jamais auparavant + Bienvenue {user?.username} !

- - - )} -
+ ) : ( + <> +

+ Regarder des vidéos comme jamais auparavant +

+ + + )} +
+ + {/* Recommendations section */} + + + {/* Top Creators section */} + - + {/* Trending Videos section */} + +
); diff --git a/frontend/src/pages/Video.jsx b/frontend/src/pages/Video.jsx new file mode 100644 index 0000000..2634449 --- /dev/null +++ b/frontend/src/pages/Video.jsx @@ -0,0 +1,263 @@ +import {useNavigate, useParams} from "react-router-dom"; +import {useEffect, useState, useRef} from "react"; +import Navbar from "../components/Navbar.jsx"; +import { useAuth } from "../contexts/AuthContext.jsx"; + + +export default function Video() { + // This component can be used to display a video player or video-related content. + const {id} = useParams(); + const { user, isAuthenticated } = useAuth(); + const videoRef = useRef(null); + const controllerRef = useRef(null); + const navigation = useNavigate(); + + const [video, setVideo] = useState(null); + const [currentTime, setCurrentTime] = useState(0); + const [duration, setDuration] = useState(0); + const [progress, setProgress] = useState(0); + const [showControls, setShowControls] = useState(false); + + useEffect(() => { + const fetchVideo = async () => { + try { + const response = await fetch(`/api/videos/${id}`); + if (!response.ok) { + throw new Error('Network response was not ok'); + } + const videoData = await response.json(); + setVideo(videoData); + } catch (error) { + console.error('Error fetching video:', error); + } + } + fetchVideo(); + }, [id]); + + const handlePlayPause = () => { + if (videoRef.current) { + if (videoRef.current.paused) { + videoRef.current.play(); + } else { + videoRef.current.pause(); + } + } + }; + + const handleTimeUpdate = () => { + if (videoRef.current) { + const current = videoRef.current.currentTime; + const total = videoRef.current.duration; + + setCurrentTime(current); + setDuration(total); + setProgress((current / total) * 100); + } + }; + + const handleLoadedMetadata = () => { + if (videoRef.current) { + setDuration(videoRef.current.duration); + } + }; + + const handleTimeBarClick = (event) => { + if (videoRef.current) { + const timeBar = event.currentTarget; + const clickX = event.nativeEvent.offsetX; + const totalWidth = timeBar.offsetWidth; + const percentage = clickX / totalWidth; + const newTime = percentage * duration; + + videoRef.current.currentTime = newTime; + setCurrentTime(newTime); + setProgress(percentage * 100); + } + }; + + const formatTime = (time) => { + if (isNaN(time)) return "0:00"; + + const minutes = Math.floor(time / 60); + const seconds = Math.floor(time % 60); + return `${minutes}:${seconds.toString().padStart(2, '0')}`; + }; + + const handlePlaying = () => { + if (videoRef.current) { + console.log(`Video is playing at ${videoRef.current.currentTime} seconds`); + } + } + + const handleMouseEnter = () => { + setShowControls(true); + }; + + const handleMouseLeave = () => { + setShowControls(false); + }; + + const handleSubscribe = async () => { + if (!isAuthenticated) { + navigation('/login'); + return; + } + + try { + // Retrieve the token from localStorage + const token = localStorage.getItem('token'); + + if (!token) { + navigation('/login'); + return; + } + + const response = await fetch(`/api/channels/${video.creator.id}/subscribe`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${token}` + }, + body: JSON.stringify({ + userId: user.id + }) + }); + + if (!response.ok) { + throw new Error('Failed to subscribe'); + } + + const data = await response.json(); + console.log('Subscription successful:', data); + + // You could update the UI here to show subscription status + alert('Successfully subscribed!'); + + } catch (error) { + console.error('Error subscribing:', error); + alert('Failed to subscribe. Please try again.'); + } + }; + + const handleLike = async () => { + if (!isAuthenticated) { + navigation('/login'); + return; + } + + try { + // Retrieve the token from localStorage + const token = localStorage.getItem('token'); + + if (!token) { + navigation('/login'); + return; + } + + const response = await fetch(`/api/videos/${id}/like`, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${token}` + } + }); + + if (!response.ok) { + throw new Error('Failed to like video'); + } + + } catch (error) { + console.error('Error liking video:', error); + } + } + + return ( +
+ + +
+ {video ? ( +
+ +
+ + + {/* Video controls */} +
+ + + + +
+ {/* Time display */} +
+ {formatTime(currentTime)} + {formatTime(duration)} +
+ + {/* Time bar */} +
+
+
+
+ +
+
+ +

{video.title}

+ +
+ {video.creator?.name +
+

{video.creator?.name}

+

{video.creator?.subscribers || 0} abonnés

+
+ + +

{video.likes}

+
+ + +
+ ): ( +

Loading

+ )} + +
+ +
+ ); +} \ No newline at end of file diff --git a/frontend/src/routes/routes.jsx b/frontend/src/routes/routes.jsx index 3d75fc9..0274f6b 100644 --- a/frontend/src/routes/routes.jsx +++ b/frontend/src/routes/routes.jsx @@ -1,6 +1,7 @@ import Home from '../pages/Home.jsx' import Login from '../pages/Login.jsx' import Register from '../pages/Register.jsx' +import Video from '../pages/Video.jsx' import ProtectedRoute from '../components/ProtectedRoute.jsx' const routes = [ @@ -21,6 +22,10 @@ const routes = [ ) }, + { + path: "/video/:id", + element: