diff --git a/backend/app/controllers/search.controller.js b/backend/app/controllers/search.controller.js new file mode 100644 index 0000000..f937d99 --- /dev/null +++ b/backend/app/controllers/search.controller.js @@ -0,0 +1,90 @@ +import {getClient} from "../utils/database.js"; + +export async function search(req, res) { + try { + console.log(req.query); + const query = req.query.q; + const type = req.query.type || 'all'; + const offset = req.query.offset || 0; + const limit = req.query.limit || 20; + const client = await getClient(); + + + if (!query) { + return res.status(400).json({ message: "Query parameter 'q' is required" }); + } + + if (type === 'videos') { + // Search video in database based on the query, video title, tags and author + const videoNameQuery = `SELECT id FROM videos WHERE title ILIKE $1 OFFSET $3 LIMIT $2`; + const videoNameResult = await client.query(videoNameQuery, [`%${query}%`, limit, offset]); + + // Search video from tags + const tagQuery = `SELECT id FROM tags WHERE name ILIKE $1 OFFSET $3 LIMIT $2`; + const tagResult = await client.query(tagQuery, [`%${query}%`, limit, offset]); + const tags = tagResult.rows.map(tag => tag.name); + + for (const tag of tags) { + const videoTagQuery = `SELECT id FROM videos WHERE id IN (SELECT video FROM video_tags WHERE tag = (SELECT id FROM tags WHERE name = $1)) OFFSET $3 LIMIT $2`; + const videoTagResult = await client.query(videoTagQuery, [tag, limit, offset]); + videoNameResult.rows.push(...videoTagResult.rows); + } + + // Search video from author + const authorQuery = `SELECT videos.id FROM videos JOIN channels c ON videos.channel = c.id WHERE c.name ILIKE $1`; + const authorResult = await client.query(authorQuery, [`%${query}%`]); + + for (const author of authorResult.rows) { + if (!videoNameResult.rows.some(video => video.id === author.id)) { + videoNameResult.rows.push(author); + } + } + + const videos = []; + + for (let video of videoNameResult.rows) { + video = video.id; // Extracting the video ID + let videoDetails = {}; + + // Fetching video details + const videoDetailsQuery = `SELECT id, title, description, thumbnail, channel, release_date FROM videos WHERE id = $1`; + const videoDetailsResult = await client.query(videoDetailsQuery, [video]); + if (videoDetailsResult.rows.length === 0) { + continue; // Skip if no video details found + } + + videoDetails = videoDetailsResult.rows[0]; + // Setting the type + videoDetails.type = 'video'; + + // Fetching views and likes + const viewsQuery = `SELECT COUNT(*) AS view_count FROM history WHERE video = $1`; + const viewsResult = await client.query(viewsQuery, [video]); + videoDetails.views = viewsResult.rows[0].view_count; + + // 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 = $1`; + const creatorResult = await client.query(creatorQuery, [video]); + videoDetails.creator = creatorResult.rows[0]; + + // GET CREATOR PROFILE PICTURE + const profilePictureQuery = `SELECT picture FROM users WHERE id = $1`; + const profilePictureResult = await client.query(profilePictureQuery, [videoDetails.creator.owner]); + videoDetails.creator.profile_picture = profilePictureResult.rows[0].picture; + + videos.push(videoDetails); + + } + + + + return res.status(200).json(videos); + + } + + + } catch (error) { + console.error("Error in search controller:", error); + res.status(500).json({ message: "Internal server error" }); + } +} \ No newline at end of file diff --git a/backend/app/routes/search.route.js b/backend/app/routes/search.route.js new file mode 100644 index 0000000..ea24b0a --- /dev/null +++ b/backend/app/routes/search.route.js @@ -0,0 +1,8 @@ +import { Router } from 'express'; +import {search} from "../controllers/search.controller.js"; + +const router = Router(); + +router.get('/', search) + +export default router; \ No newline at end of file diff --git a/backend/requests/video.http b/backend/requests/video.http index 4c9aea6..f62d269 100644 --- a/backend/requests/video.http +++ b/backend/requests/video.http @@ -27,4 +27,7 @@ Authorization: Bearer {{token}} "Redstone" ], "channel": 2 -} \ No newline at end of file +} + +### +GET http://localhost/api/search?q=minecraft&type=videos&offset=0&limit=10 \ No newline at end of file diff --git a/backend/server.js b/backend/server.js index c588fb8..6647ab1 100644 --- a/backend/server.js +++ b/backend/server.js @@ -10,6 +10,7 @@ import cors from "cors"; import PlaylistRoute from "./app/routes/playlist.route.js"; import {initDb} from "./app/utils/database.js"; import MediaRoutes from "./app/routes/media.routes.js"; +import SearchRoute from "./app/routes/search.route.js"; console.clear(); dotenv.config(); @@ -32,6 +33,7 @@ app.use("/api/comments/", CommentRoute); app.use("/api/playlists", PlaylistRoute); app.use("/api/recommendations", RecommendationRoute); app.use("/api/media", MediaRoutes); +app.use("/api/search", SearchRoute); const port = process.env.PORT; diff --git a/frontend/src/assets/svg/edit.svg b/frontend/src/assets/svg/edit.svg new file mode 100644 index 0000000..b302306 --- /dev/null +++ b/frontend/src/assets/svg/edit.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/components/Navbar.jsx b/frontend/src/components/Navbar.jsx index 174f22a..27249f1 100644 --- a/frontend/src/components/Navbar.jsx +++ b/frontend/src/components/Navbar.jsx @@ -19,15 +19,17 @@ export default function Navbar({ isSearchPage = false }) { {isAuthenticated ? ( <>
  • Abonnements
  • -
  • - {user?.username} - {user?.picture && ( - Profile - )} +
  • + + {user?.username} + {user?.picture && ( + Profile + )} +
  • + + + + { /* Right side */} + + {/* Channel */} + + {/* Playlists */} + + {/* History */} + + + + + ) + +} \ No newline at end of file diff --git a/frontend/src/pages/Video.jsx b/frontend/src/pages/Video.jsx index 6f8e2d0..9320f90 100644 --- a/frontend/src/pages/Video.jsx +++ b/frontend/src/pages/Video.jsx @@ -311,7 +311,7 @@ export default function Video() {
    -
    +
    {video ? ( <> {/* Video player section */} diff --git a/frontend/src/routes/routes.jsx b/frontend/src/routes/routes.jsx index 0274f6b..e366840 100644 --- a/frontend/src/routes/routes.jsx +++ b/frontend/src/routes/routes.jsx @@ -3,6 +3,7 @@ import Login from '../pages/Login.jsx' import Register from '../pages/Register.jsx' import Video from '../pages/Video.jsx' import ProtectedRoute from '../components/ProtectedRoute.jsx' +import Account from "../pages/Account.jsx"; const routes = [ { path: "/", element: }, @@ -26,6 +27,14 @@ const routes = [ path: "/video/:id", element: