+ Ajouter une vidéo +
+ +{videoTitle || "Titre de la vidéo"}
+ ++ {videoDescription || "Description de la vidéo"} +
+ +diff --git a/.gitignore b/.gitignore index e69de29..87da954 100644 --- a/.gitignore +++ b/.gitignore @@ -0,0 +1,9 @@ +/backend/app/uploads/ +# Ignore all files in the uploads directory + +/frontend/node_modules +/backend/node_modules +# Ignore node_modules directories in both frontend and backend + +/frontend/dist +# Ignore the build output directory for the frontend \ No newline at end of file diff --git a/backend/app/controllers/video.controller.js b/backend/app/controllers/video.controller.js index 0cd24e3..e12d18e 100644 --- a/backend/app/controllers/video.controller.js +++ b/backend/app/controllers/video.controller.js @@ -49,11 +49,12 @@ export async function upload(req, res) { logger.write("try to upload video"); const releaseDate = new Date(Date.now()).toISOString(); - const query = `INSERT INTO videos (title, thumbnail, description, channel, visibility, file, slug, format, release_date) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`; - await client.query(query, [video.title, 'null', video.description, video.channel, video.visibility, video.file, video.slug, video.format, releaseDate]); + const query = `INSERT INTO videos (title, thumbnail, description, channel, visibility, file, slug, format, release_date) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) RETURNING id`; + const idResult = await client.query(query, [video.title, 'null', video.description, video.channel, video.visibility, video.file, video.slug, video.format, releaseDate]); + const id = idResult.rows[0].id; logger.write("successfully uploaded video", 200); await client.end() - res.status(200).json({"message": "Successfully uploaded video"}); + res.status(200).json({"message": "Successfully uploaded video", "id":id}); } diff --git a/backend/logs/access.log b/backend/logs/access.log index 5d41eeb..cd06a78 100644 --- a/backend/logs/access.log +++ b/backend/logs/access.log @@ -3691,3 +3691,135 @@ [2025-07-24 21:50:12.955] [undefined] GET(/:id/similar): successfully get similar videos for video 3 with status 200 [2025-07-24 21:50:12.973] [undefined] GET(/:id/views): try to add views for video 3 [2025-07-24 21:50:12.980] [undefined] GET(/:id/views): successfully added views for video 3 with status 200 +[2025-07-25 19:54:37.439] [undefined] GET(/:id): try to get channel with id 1 +[2025-07-25 19:54:37.449] [undefined] GET(/:id/stats): try to get stats +[2025-07-25 19:54:37.452] [undefined] GET(/:id): Successfully get channel with id 1 with status 200 +[2025-07-25 19:54:37.458] [undefined] GET(/:id/stats): Successfully get stats with status 200 +[2025-07-26 08:12:37.895] [undefined] GET(/:id/channel): try to retrieve channel of user 1 +[2025-07-26 08:12:37.896] [undefined] GET(/:id/history): try to retrieve history of user 1 +[2025-07-26 08:12:37.898] [undefined] GET(/:id/channel): successfully retrieved channel of user 1 with status 200 +[2025-07-26 08:12:37.901] [undefined] GET(/:id/history): successfully retrieved history of user 1 with status 200 +[2025-07-26 08:12:37.908] [undefined] GET(/user/:id): Playlists retrieved for user with id 1 with status 200 +[2025-07-26 08:12:38.691] [undefined] GET(/:id): try to get channel with id 1 +[2025-07-26 08:12:38.701] [undefined] GET(/:id/stats): try to get stats +[2025-07-26 08:12:38.702] [undefined] GET(/:id): Successfully get channel with id 1 with status 200 +[2025-07-26 08:12:38.711] [undefined] GET(/:id/stats): Successfully get stats with status 200 +[2025-07-26 08:45:16.047] [undefined] POST(/): failed due to invalid values with status 400 +[2025-07-26 08:48:28.235] [undefined] GET(/:id/channel): try to retrieve channel of user 1 +[2025-07-26 08:48:28.237] [undefined] GET(/:id/channel): successfully retrieved channel of user 1 with status 200 +[2025-07-26 08:48:31.269] [undefined] GET(/:id/channel): try to retrieve channel of user 1 +[2025-07-26 08:48:31.272] [undefined] GET(/:id/channel): successfully retrieved channel of user 1 with status 200 +[2025-07-26 08:49:37.594] [undefined] GET(/:id/channel): try to retrieve channel of user 1 +[2025-07-26 08:49:37.596] [undefined] GET(/:id/channel): successfully retrieved channel of user 1 with status 200 +[2025-07-26 08:49:56.943] [undefined] POST(/): failed due to invalid values with status 400 +[2025-07-26 08:50:18.564] [undefined] GET(/:id/channel): try to retrieve channel of user 1 +[2025-07-26 08:50:18.567] [undefined] GET(/:id/channel): successfully retrieved channel of user 1 with status 200 +[2025-07-26 08:50:32.509] [undefined] POST(/): failed due to invalid values with status 400 +[2025-07-26 08:53:06.249] [undefined] GET(/:id/channel): try to retrieve channel of user 1 +[2025-07-26 08:53:06.251] [undefined] GET(/:id/channel): successfully retrieved channel of user 1 with status 200 +[2025-07-26 08:53:22.601] [undefined] POST(/): failed due to invalid values with status 400 +[2025-07-26 08:55:33.886] [undefined] GET(/:id/channel): try to retrieve channel of user 1 +[2025-07-26 08:55:33.888] [undefined] GET(/:id/channel): successfully retrieved channel of user 1 with status 200 +[2025-07-26 08:55:42.316] [undefined] POST(/): try to upload video with status undefined +[2025-07-26 08:55:42.320] [undefined] POST(/): successfully uploaded video with status 200 +[2025-07-26 08:55:52.499] [undefined] GET(/:id): try to get video 4 +[2025-07-26 08:55:52.508] [undefined] GET(/:id): successfully get video 4 with status 200 +[2025-07-26 08:55:52.520] [undefined] GET(/:id/similar): try to get similar videos for video 4 +[2025-07-26 08:55:52.527] [undefined] GET(/:id/similar): No tags found for video 4 with status 404 +[2025-07-26 08:55:52.562] [undefined] GET(/:id/views): try to add views for video 4 +[2025-07-26 08:55:52.576] [undefined] GET(/:id/views): successfully added views for video 4 with status 200 +[2025-07-26 08:56:02.055] [undefined] GET(/:id/channel): try to retrieve channel of user 1 +[2025-07-26 08:56:02.057] [undefined] GET(/:id/channel): successfully retrieved channel of user 1 with status 200 +[2025-07-26 08:56:02.060] [undefined] GET(/:id/history): try to retrieve history of user 1 +[2025-07-26 08:56:02.063] [undefined] GET(/:id/history): successfully retrieved history of user 1 with status 200 +[2025-07-26 08:56:02.069] [undefined] GET(/user/:id): Playlists retrieved for user with id 1 with status 200 +[2025-07-26 08:56:02.787] [undefined] GET(/:id): try to get channel with id 1 +[2025-07-26 08:56:02.796] [undefined] GET(/:id/stats): try to get stats +[2025-07-26 08:56:02.799] [undefined] GET(/:id): Successfully get channel with id 1 with status 200 +[2025-07-26 08:56:02.805] [undefined] GET(/:id/stats): Successfully get stats with status 200 +[2025-07-26 08:56:03.535] [undefined] GET(/:id/channel): try to retrieve channel of user 1 +[2025-07-26 08:56:03.537] [undefined] GET(/:id/channel): successfully retrieved channel of user 1 with status 200 +[2025-07-26 08:57:44.716] [undefined] GET(/:id/channel): try to retrieve channel of user 1 +[2025-07-26 08:57:44.719] [undefined] GET(/:id/channel): successfully retrieved channel of user 1 with status 200 +[2025-07-26 08:57:54.363] [undefined] POST(/): try to upload video with status undefined +[2025-07-26 08:57:54.366] [undefined] POST(/): successfully uploaded video with status 200 +[2025-07-26 08:58:08.735] [undefined] GET(/:id/channel): try to retrieve channel of user 1 +[2025-07-26 08:58:08.739] [undefined] GET(/:id/channel): successfully retrieved channel of user 1 with status 200 +[2025-07-26 08:58:18.190] [undefined] POST(/): try to upload video with status undefined +[2025-07-26 08:58:18.194] [undefined] POST(/): successfully uploaded video with status 200 +[2025-07-26 09:00:32.892] [undefined] GET(/:id/channel): try to retrieve channel of user 1 +[2025-07-26 09:00:32.894] [undefined] GET(/:id/channel): successfully retrieved channel of user 1 with status 200 +[2025-07-26 09:00:45.499] [undefined] POST(/): try to upload video with status undefined +[2025-07-26 09:00:45.503] [undefined] POST(/): successfully uploaded video with status 200 +[2025-07-26 09:00:45.551] [undefined] POST(/thumbnail): failed due to invalid values with status 400 +[2025-07-26 09:01:41.512] [undefined] GET(/:id/channel): try to retrieve channel of user 1 +[2025-07-26 09:01:41.514] [undefined] GET(/:id/channel): successfully retrieved channel of user 1 with status 200 +[2025-07-26 09:01:56.701] [undefined] POST(/): try to upload video with status undefined +[2025-07-26 09:01:56.704] [undefined] POST(/): successfully uploaded video with status 200 +[2025-07-26 09:01:56.793] [undefined] POST(/thumbnail): try to add thumbnail to video 8 +[2025-07-26 09:01:56.798] [undefined] POST(/thumbnail): successfully uploaded thumbnail with status 200 +[2025-07-26 09:02:29.458] [undefined] GET(/:id/channel): try to retrieve channel of user 1 +[2025-07-26 09:02:29.460] [undefined] GET(/:id/channel): successfully retrieved channel of user 1 with status 200 +[2025-07-26 09:02:45.369] [undefined] POST(/): try to upload video with status undefined +[2025-07-26 09:02:45.372] [undefined] POST(/): successfully uploaded video with status 200 +[2025-07-26 09:02:45.443] [undefined] POST(/thumbnail): try to add thumbnail to video 9 +[2025-07-26 09:02:45.446] [undefined] POST(/thumbnail): successfully uploaded thumbnail with status 200 +[2025-07-26 09:03:38.478] [undefined] GET(/:id/channel): try to retrieve channel of user 1 +[2025-07-26 09:03:38.479] [undefined] GET(/:id/channel): successfully retrieved channel of user 1 with status 200 +[2025-07-26 09:03:54.208] [undefined] POST(/): failed due to invalid values with status 400 +[2025-07-26 09:04:01.982] [undefined] POST(/): try to upload video with status undefined +[2025-07-26 09:04:01.985] [undefined] POST(/): successfully uploaded video with status 200 +[2025-07-26 09:04:02.052] [undefined] POST(/thumbnail): try to add thumbnail to video 10 +[2025-07-26 09:04:02.055] [undefined] POST(/thumbnail): successfully uploaded thumbnail with status 200 +[2025-07-26 09:04:02.074] [undefined] PUT(/:id/tags): try to add tags to video 10 +[2025-07-26 09:04:02.084] [undefined] PUT(/:id/tags): successfully added tags to video 10 with status 200 +[2025-07-26 09:04:08.248] [undefined] GET(/:id): try to get video 10 +[2025-07-26 09:04:08.257] [undefined] GET(/:id): successfully get video 10 with status 200 +[2025-07-26 09:04:08.270] [undefined] GET(/:id/similar): try to get similar videos for video 10 +[2025-07-26 09:04:08.279] [undefined] GET(/:id/similar): successfully get similar videos for video 10 with status 200 +[2025-07-26 09:04:08.339] [undefined] GET(/:id/views): try to add views for video 10 +[2025-07-26 09:04:08.347] [undefined] GET(/:id/views): successfully added views for video 10 with status 200 +[2025-07-26 09:04:12.622] [undefined] GET(/:id): try to get video 10 +[2025-07-26 09:04:12.632] [undefined] GET(/:id): successfully get video 10 with status 200 +[2025-07-26 09:04:12.648] [undefined] GET(/:id/similar): try to get similar videos for video 10 +[2025-07-26 09:04:12.657] [undefined] GET(/:id/similar): successfully get similar videos for video 10 with status 200 +[2025-07-26 09:04:12.701] [undefined] GET(/:id/views): try to add views for video 10 +[2025-07-26 09:04:12.709] [undefined] GET(/:id/views): successfully added views for video 10 with status 200 +[2025-07-26 09:04:23.159] [undefined] GET(/:id/channel): try to retrieve channel of user 1 +[2025-07-26 09:04:23.161] [undefined] GET(/:id/history): try to retrieve history of user 1 +[2025-07-26 09:04:23.163] [undefined] GET(/:id/channel): successfully retrieved channel of user 1 with status 200 +[2025-07-26 09:04:23.166] [undefined] GET(/:id/history): successfully retrieved history of user 1 with status 200 +[2025-07-26 09:04:23.172] [undefined] GET(/user/:id): Playlists retrieved for user with id 1 with status 200 +[2025-07-26 09:04:25.131] [undefined] GET(/:id/channel): try to retrieve channel of user 1 +[2025-07-26 09:04:25.133] [undefined] GET(/:id/history): try to retrieve history of user 1 +[2025-07-26 09:04:25.134] [undefined] GET(/:id/channel): successfully retrieved channel of user 1 with status 200 +[2025-07-26 09:04:25.137] [undefined] GET(/:id/history): successfully retrieved history of user 1 with status 200 +[2025-07-26 09:04:25.144] [undefined] GET(/user/:id): Playlists retrieved for user with id 1 with status 200 +[2025-07-26 09:04:26.249] [undefined] GET(/:id): try to get channel with id 1 +[2025-07-26 09:04:26.258] [undefined] GET(/:id/stats): try to get stats +[2025-07-26 09:04:26.260] [undefined] GET(/:id): Successfully get channel with id 1 with status 200 +[2025-07-26 09:04:26.268] [undefined] GET(/:id/stats): Successfully get stats with status 200 +[2025-07-26 09:04:34.109] [undefined] GET(/:id): try to get video 4 +[2025-07-26 09:04:34.112] [undefined] GET(/:id/likes/day): try to get likes per day +[2025-07-26 09:04:34.122] [undefined] GET(/:id/likes/day): successfully retrieved likes per day with status 200 +[2025-07-26 09:04:34.126] [undefined] GET(/:id): successfully get video 4 with status 200 +[2025-07-26 09:04:41.682] [undefined] GET(/:id): try to get video 4 +[2025-07-26 09:04:41.684] [undefined] GET(/:id/likes/day): try to get likes per day +[2025-07-26 09:04:41.705] [undefined] GET(/:id/likes/day): successfully retrieved likes per day with status 200 +[2025-07-26 09:04:41.709] [undefined] GET(/:id): successfully get video 4 with status 200 +[2025-07-26 09:04:51.790] [undefined] POST(/thumbnail): try to add thumbnail to video 4 +[2025-07-26 09:04:51.795] [undefined] POST(/thumbnail): successfully uploaded thumbnail with status 200 +[2025-07-26 09:05:01.430] [undefined] PUT(/:id/tags): try to add tags to video 4 +[2025-07-26 09:05:01.440] [undefined] PUT(/:id/tags): successfully added tags to video 4 with status 200 +[2025-07-26 09:05:09.306] [undefined] GET(/:id): try to get video 8 +[2025-07-26 09:05:09.314] [undefined] GET(/:id): successfully get video 8 with status 200 +[2025-07-26 09:05:09.339] [undefined] GET(/:id/similar): try to get similar videos for video 8 +[2025-07-26 09:05:09.348] [undefined] GET(/:id/similar): No tags found for video 8 with status 404 +[2025-07-26 09:05:09.363] [undefined] GET(/:id/views): try to add views for video 8 +[2025-07-26 09:05:09.374] [undefined] GET(/:id/views): successfully added views for video 8 with status 200 +[2025-07-26 09:05:11.418] [undefined] GET(/:id): try to get video 8 +[2025-07-26 09:05:11.428] [undefined] GET(/:id): successfully get video 8 with status 200 +[2025-07-26 09:05:11.439] [undefined] GET(/:id/similar): try to get similar videos for video 8 +[2025-07-26 09:05:11.448] [undefined] GET(/:id/similar): No tags found for video 8 with status 404 +[2025-07-26 09:05:11.474] [undefined] GET(/:id/views): try to add views for video 8 +[2025-07-26 09:05:11.481] [undefined] GET(/:id/views): successfully added views for video 8 with status 200 diff --git a/frontend/src/pages/AddVideo.jsx b/frontend/src/pages/AddVideo.jsx new file mode 100644 index 0000000..310d4e8 --- /dev/null +++ b/frontend/src/pages/AddVideo.jsx @@ -0,0 +1,274 @@ +import Navbar from "../components/Navbar.jsx"; +import {useEffect, useState} from "react"; +import Tag from "../components/Tag.jsx"; + + +export default function AddVideo() { + + const user = JSON.parse(localStorage.getItem("user")); + const token = localStorage.getItem("token"); + + const [videoTitle, setVideoTitle] = useState(""); + const [videoDescription, setVideoDescription] = useState(""); + const [videoTags, setVideoTags] = useState([]); + const [visibility, setVisibility] = useState("public"); // Default visibility + const [videoThumbnail, setVideoThumbnail] = useState(null); + const [videoFile, setVideoFile] = useState(null); + const [channel, setChannel] = useState(null); // Assuming user.channel is the channel ID + + useEffect(() => { + fetchChannel(); + }, []) + + const fetchChannel = async () => { + try { + const response = await fetch(`/api/users/${user.id}/channel`, { + headers: { + "Authorization": `Bearer ${token}` // Assuming you have a token for authentication + }, + }); + if (!response.ok) { + throw new Error("Erreur lors de la récupération de la chaîne"); + } + const data = await response.json(); + setChannel(data.channel); + } catch (error) { + console.error("Erreur lors de la récupération de la chaîne :", error); + } + } + + const handleTagKeyDown = (e) => { + if (e.key === 'Enter' && videoTags.length < 10) { + e.preventDefault(); + const newTag = e.target.value.trim(); + if (newTag && !videoTags.includes(newTag)) { + setVideoTags([...videoTags, newTag]); + e.target.value = ''; + } + } + } + const handleTagRemove = (tagToRemove) => { + setVideoTags(videoTags.filter(tag => tag !== tagToRemove)); + }; + + // This function handles the submission of the video form + const handleSubmit = async (e) => { + e.preventDefault(); + + if (!videoTitle || !videoDescription || !videoThumbnail || !videoFile) { + alert("Veuillez remplir tous les champs requis."); + return; + } + if (!channel || !channel.id) { + alert("Erreur: aucune chaîne trouvée. Veuillez actualiser la page."); + return; + } + if (videoTags.length > 10) { + alert("Vous ne pouvez pas ajouter plus de 10 tags."); + return; + } + + const formData = new FormData(); + formData.append("title", videoTitle); + formData.append("description", videoDescription); + formData.append("channel", channel.id.toString()); // Ensure it's a string + formData.append("visibility", visibility); + formData.append("file", videoFile); + + const request = await fetch("/api/videos", { + method: "POST", + headers: { + "Authorization": `Bearer ${token}` // Assuming you have a token for authentication + }, + body: formData + }); + if (!request.ok) { + const errorData = await request.json(); + console.error("Backend validation errors:", errorData); + + // Display specific validation errors if available + if (errorData.errors && errorData.errors.length > 0) { + const errorMessages = errorData.errors.map(error => + `${error.path}: ${error.msg}` + ).join('\n'); + alert(`Erreurs de validation:\n${errorMessages}`); + } else { + alert(`Erreur lors de l'ajout de la vidéo : ${errorData.message || 'Erreur inconnue'}`); + } + return; + } + + // If the video was successfully created, we can now upload the thumbnail + const response = await request.json(); + const videoId = response.id; + const thumbnailFormData = new FormData(); + thumbnailFormData.append("video", videoId); + thumbnailFormData.append("file", videoThumbnail); + thumbnailFormData.append("channel", channel.id.toString()); + const thumbnailRequest = await fetch("/api/videos/thumbnail", { + method: "POST", + headers: { + "Authorization": `Bearer ${token}` // Assuming you have a token for authentication + }, + body: thumbnailFormData + }); + if (!thumbnailRequest.ok) { + const errorData = await thumbnailRequest.json(); + console.error("Backend validation errors:", errorData); + alert(`Erreur lors de l'ajout de la miniature : ${errorData.message || 'Erreur inconnue'}`); + return; + } + + // if the thumbnail was successfully uploaded, we can send the tags + const tagsRequest = await fetch(`/api/videos/${videoId}/tags`, { + method: "PUT", + headers: { + "Content-Type": "application/json", + "Authorization": `Bearer ${token}` // Assuming you have a token for authentication + }, + body: JSON.stringify({ tags: videoTags, channel: channel.id.toString() }) // Ensure channel ID is a string + }); + if (!tagsRequest.ok) { + const errorData = await tagsRequest.json(); + console.error("Backend validation errors:", errorData); + alert(`Erreur lors de l'ajout des tags : ${errorData.message || 'Erreur inconnue'}`); + return; + } + // If everything is successful, redirect to the video management page + alert("Vidéo ajoutée avec succès !"); + + + + }; + + return ( +
+ {videoDescription || "Description de la vidéo"} +
+ +