Spaces:
Sleeping
Sleeping
Commit
·
11c56f5
1
Parent(s):
21986a1
api routes
Browse files- frontend/public/Aaj Ki Raat _ Stree 2 _ Tamannaah Bhatia _ Sachin-Jigar _ Madhubanti _ Divya _ Amitabh _ 15th August.mp4 +0 -3
- frontend/public/Dil Ka Darzi Song - STREE - VAYU, Prakriti Kakar - Sachin-Jigar - Rajkummar Rao, Shraddha Kapoor (320).mp3 +0 -3
- frontend/src/app/api/get/category/[category]/route.js +25 -0
- frontend/src/app/api/get/category/all/route.js +21 -0
- frontend/src/app/api/get/music/[fileName]/route.js +25 -0
- frontend/src/app/api/get/music/all/route.js +35 -0
- frontend/src/app/api/get/store/route.js +21 -0
- frontend/src/app/category/[category]/CategoryPage.css +0 -0
- frontend/src/app/category/[category]/page.js +76 -0
- frontend/src/app/globals.css +1 -0
- frontend/src/app/indexpage.css +0 -0
- frontend/src/app/page.js +4 -7
- frontend/src/components/Card.css +24 -19
- frontend/src/components/Card.js +5 -3
- frontend/src/components/CategoriesSection.css +24 -0
- frontend/src/components/CategoriesSection.js +57 -0
- frontend/src/components/MusicPlayer.js +3 -2
- frontend/src/context/MusicPlayerContext.js +60 -3
- frontend/src/lib/LBApi.js +89 -0
- frontend/src/lib/config.js +1 -0
frontend/public/Aaj Ki Raat _ Stree 2 _ Tamannaah Bhatia _ Sachin-Jigar _ Madhubanti _ Divya _ Amitabh _ 15th August.mp4
DELETED
|
@@ -1,3 +0,0 @@
|
|
| 1 |
-
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:cfd9855ea9bf3f44c75472ac682fd31171dea80747fabf84b5b26d88409dabc7
|
| 3 |
-
size 32005937
|
|
|
|
|
|
|
|
|
|
|
|
frontend/public/Dil Ka Darzi Song - STREE - VAYU, Prakriti Kakar - Sachin-Jigar - Rajkummar Rao, Shraddha Kapoor (320).mp3
DELETED
|
@@ -1,3 +0,0 @@
|
|
| 1 |
-
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:444cdf1a4bdaf659c762d9150e1dfea6f025e80f8bca0c9000936999c69b0f2c
|
| 3 |
-
size 5905965
|
|
|
|
|
|
|
|
|
|
|
|
frontend/src/app/api/get/category/[category]/route.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { NextResponse } from 'next/server';
|
| 2 |
+
|
| 3 |
+
import {BASE_URL} from "@/lib/config";
|
| 4 |
+
|
| 5 |
+
export async function GET(request, { params }) {
|
| 6 |
+
// Await the params before using them
|
| 7 |
+
const { category } = await params;
|
| 8 |
+
const { page = 1, limit = 10 } = Object.fromEntries(new URL(request.url).searchParams);
|
| 9 |
+
|
| 10 |
+
try {
|
| 11 |
+
const response = await fetch(`${BASE_URL}/api/get/category/${category}?page=${page}&limit=${limit}`);
|
| 12 |
+
|
| 13 |
+
// Check if the response is ok
|
| 14 |
+
if (!response.ok) {
|
| 15 |
+
const errorData = await response.json();
|
| 16 |
+
return NextResponse.json({ error: errorData.error || 'Failed to fetch music from category' }, { status: response.status });
|
| 17 |
+
}
|
| 18 |
+
|
| 19 |
+
const data = await response.json();
|
| 20 |
+
return NextResponse.json(data);
|
| 21 |
+
} catch (error) {
|
| 22 |
+
console.error('Error fetching music from category:', error);
|
| 23 |
+
return NextResponse.json({ error: 'Failed to fetch music from category' }, { status: 500 });
|
| 24 |
+
}
|
| 25 |
+
}
|
frontend/src/app/api/get/category/all/route.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { NextResponse } from 'next/server';
|
| 2 |
+
|
| 3 |
+
import {BASE_URL} from "@/lib/config";
|
| 4 |
+
|
| 5 |
+
export async function GET() {
|
| 6 |
+
try {
|
| 7 |
+
const response = await fetch(`${BASE_URL}/api/get/category/all`);
|
| 8 |
+
|
| 9 |
+
// Check if the response is ok
|
| 10 |
+
if (!response.ok) {
|
| 11 |
+
const errorData = await response.json();
|
| 12 |
+
return NextResponse.json({ error: errorData.error || 'Failed to fetch categories' }, { status: response.status });
|
| 13 |
+
}
|
| 14 |
+
|
| 15 |
+
const data = await response.json();
|
| 16 |
+
return NextResponse.json(data);
|
| 17 |
+
} catch (error) {
|
| 18 |
+
console.error('Error fetching categories:', error);
|
| 19 |
+
return NextResponse.json({ error: 'Failed to fetch categories' }, { status: 500 });
|
| 20 |
+
}
|
| 21 |
+
}
|
frontend/src/app/api/get/music/[fileName]/route.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { NextResponse } from 'next/server';
|
| 2 |
+
|
| 3 |
+
import {BASE_URL} from "@/lib/config";
|
| 4 |
+
|
| 5 |
+
export async function GET(request, { params }) {
|
| 6 |
+
// Get the file name from the URL parameters
|
| 7 |
+
const { fileName } = await params;
|
| 8 |
+
|
| 9 |
+
try {
|
| 10 |
+
// Fetch the music file from the FastAPI backend
|
| 11 |
+
const response = await fetch(`${BASE_URL}/api/get/music/${encodeURIComponent(fileName)}`);
|
| 12 |
+
|
| 13 |
+
// Check if the response is ok
|
| 14 |
+
if (!response.ok) {
|
| 15 |
+
const errorData = await response.json();
|
| 16 |
+
return NextResponse.json({ error: errorData.error || 'Failed to fetch music file' }, { status: response.status });
|
| 17 |
+
}
|
| 18 |
+
|
| 19 |
+
const data = await response.json();
|
| 20 |
+
return NextResponse.json(data);
|
| 21 |
+
} catch (error) {
|
| 22 |
+
console.error('Error fetching music file:', error);
|
| 23 |
+
return NextResponse.json({ error: 'Failed to fetch music file' }, { status: 500 });
|
| 24 |
+
}
|
| 25 |
+
}
|
frontend/src/app/api/get/music/all/route.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { NextResponse } from 'next/server';
|
| 2 |
+
|
| 3 |
+
import {BASE_URL} from "@/lib/config";
|
| 4 |
+
|
| 5 |
+
export async function GET(request) {
|
| 6 |
+
const { searchParams } = new URL(request.url);
|
| 7 |
+
const page = searchParams.get('page');
|
| 8 |
+
const limit = searchParams.get('limit');
|
| 9 |
+
|
| 10 |
+
let url = `${BASE_URL}/api/get/music/all`;
|
| 11 |
+
const params = new URLSearchParams();
|
| 12 |
+
|
| 13 |
+
if (page) params.append("page", page);
|
| 14 |
+
if (limit) params.append("limit", limit);
|
| 15 |
+
|
| 16 |
+
if (params.toString()) {
|
| 17 |
+
url += `?${params.toString()}`;
|
| 18 |
+
}
|
| 19 |
+
|
| 20 |
+
try {
|
| 21 |
+
const response = await fetch(url);
|
| 22 |
+
|
| 23 |
+
// Check if the response is ok
|
| 24 |
+
if (!response.ok) {
|
| 25 |
+
const errorData = await response.json();
|
| 26 |
+
return NextResponse.json({ error: errorData.error || 'Failed to fetch music' }, { status: response.status });
|
| 27 |
+
}
|
| 28 |
+
|
| 29 |
+
const data = await response.json();
|
| 30 |
+
return NextResponse.json(data);
|
| 31 |
+
} catch (error) {
|
| 32 |
+
console.error('Error fetching all music:', error);
|
| 33 |
+
return NextResponse.json({ error: 'Failed to fetch all music' }, { status: 500 });
|
| 34 |
+
}
|
| 35 |
+
}
|
frontend/src/app/api/get/store/route.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { NextResponse } from 'next/server';
|
| 2 |
+
|
| 3 |
+
import {BASE_URL} from "@/lib/config";
|
| 4 |
+
|
| 5 |
+
export async function GET() {
|
| 6 |
+
try {
|
| 7 |
+
const response = await fetch(`${BASE_URL}/api/get/music/store`);
|
| 8 |
+
|
| 9 |
+
// Check if the response is ok
|
| 10 |
+
if (!response.ok) {
|
| 11 |
+
const errorData = await response.json();
|
| 12 |
+
return NextResponse.json({ error: errorData.error || 'Failed to fetch music store' }, { status: response.status });
|
| 13 |
+
}
|
| 14 |
+
|
| 15 |
+
const data = await response.json();
|
| 16 |
+
return NextResponse.json(data);
|
| 17 |
+
} catch (error) {
|
| 18 |
+
console.error('Error fetching music store:', error);
|
| 19 |
+
return NextResponse.json({ error: 'Failed to fetch music store' }, { status: 500 });
|
| 20 |
+
}
|
| 21 |
+
}
|
frontend/src/app/category/[category]/CategoryPage.css
ADDED
|
File without changes
|
frontend/src/app/category/[category]/page.js
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
'use client';
|
| 2 |
+
import Card from "@/components/Card";
|
| 3 |
+
import './CategoryPage.css';
|
| 4 |
+
import { useEffect, useState } from 'react';
|
| 5 |
+
import { use } from 'react';
|
| 6 |
+
|
| 7 |
+
export default function CategoryPage({ params }) {
|
| 8 |
+
const { category } = use(params); // Unwrap params using React.use()
|
| 9 |
+
const [musicItems, setMusicItems] = useState([]);
|
| 10 |
+
const [loading, setLoading] = useState(true);
|
| 11 |
+
const [error, setError] = useState(null);
|
| 12 |
+
const [page, setPage] = useState(1);
|
| 13 |
+
const [hasMore, setHasMore] = useState(true);
|
| 14 |
+
|
| 15 |
+
useEffect(() => {
|
| 16 |
+
const fetchMusicByCategory = async () => {
|
| 17 |
+
try {
|
| 18 |
+
const response = await fetch(`/api/get/category/${category}?page=${page}&limit=10`);
|
| 19 |
+
if (!response.ok) {
|
| 20 |
+
throw new Error("Failed to fetch music");
|
| 21 |
+
}
|
| 22 |
+
const data = await response.json();
|
| 23 |
+
|
| 24 |
+
// Update music items based on response, avoiding duplicates
|
| 25 |
+
setMusicItems(prevItems => {
|
| 26 |
+
const newItems = data.files.filter(file => !prevItems.includes(file));
|
| 27 |
+
return [...prevItems, ...newItems];
|
| 28 |
+
});
|
| 29 |
+
|
| 30 |
+
// Check if there are more files to load
|
| 31 |
+
if (data.files.length < data.limit) {
|
| 32 |
+
setHasMore(false); // No more music to load
|
| 33 |
+
}
|
| 34 |
+
} catch (err) {
|
| 35 |
+
setError(err.message);
|
| 36 |
+
} finally {
|
| 37 |
+
setLoading(false);
|
| 38 |
+
}
|
| 39 |
+
};
|
| 40 |
+
|
| 41 |
+
fetchMusicByCategory();
|
| 42 |
+
}, [category, page]);
|
| 43 |
+
|
| 44 |
+
const loadMore = () => {
|
| 45 |
+
if (hasMore) {
|
| 46 |
+
setPage(prevPage => prevPage + 1);
|
| 47 |
+
}
|
| 48 |
+
};
|
| 49 |
+
|
| 50 |
+
if (loading) {
|
| 51 |
+
return <div>Loading music...</div>;
|
| 52 |
+
}
|
| 53 |
+
|
| 54 |
+
if (error) {
|
| 55 |
+
return <div>Error: {error}</div>;
|
| 56 |
+
}
|
| 57 |
+
|
| 58 |
+
return (
|
| 59 |
+
<div className="category-page font-[family-name:var(--font-geist-sans)]">
|
| 60 |
+
<h1>{category.charAt(0).toUpperCase() + category.slice(1)} Music</h1>
|
| 61 |
+
<div className="card-section">
|
| 62 |
+
{musicItems.map((file, index) => (
|
| 63 |
+
<Card
|
| 64 |
+
key={`${file}-${page}`} // Ensure unique key by appending page number
|
| 65 |
+
src={`/${file}`} // Assuming the base URL is handled correctly
|
| 66 |
+
/>
|
| 67 |
+
))}
|
| 68 |
+
</div>
|
| 69 |
+
{hasMore && (
|
| 70 |
+
<button onClick={loadMore} className="load-more">
|
| 71 |
+
Load More
|
| 72 |
+
</button>
|
| 73 |
+
)}
|
| 74 |
+
</div>
|
| 75 |
+
);
|
| 76 |
+
}
|
frontend/src/app/globals.css
CHANGED
|
@@ -7,6 +7,7 @@
|
|
| 7 |
--foreground: #ededed;
|
| 8 |
--background-secondary: #110652;
|
| 9 |
--foreground-secondary: #1d85b9;
|
|
|
|
| 10 |
}
|
| 11 |
|
| 12 |
body {
|
|
|
|
| 7 |
--foreground: #ededed;
|
| 8 |
--background-secondary: #110652;
|
| 9 |
--foreground-secondary: #1d85b9;
|
| 10 |
+
--background-2: #332a77;
|
| 11 |
}
|
| 12 |
|
| 13 |
body {
|
frontend/src/app/indexpage.css
ADDED
|
File without changes
|
frontend/src/app/page.js
CHANGED
|
@@ -1,14 +1,11 @@
|
|
| 1 |
import Card from "@/components/Card";
|
|
|
|
|
|
|
| 2 |
|
| 3 |
export default function Home() {
|
| 4 |
return (
|
| 5 |
-
<div className="
|
| 6 |
-
<
|
| 7 |
-
<div className="flex gap-4 items-center flex-col sm:flex-row">
|
| 8 |
-
<Card src={"/Aaj Ki Raat _ Stree 2 _ Tamannaah Bhatia _ Sachin-Jigar _ Madhubanti _ Divya _ Amitabh _ 15th August.mp4"} title={"Aaj Ki Raat"}/>
|
| 9 |
-
<Card src={"/Dil Ka Darzi Song - STREE - VAYU, Prakriti Kakar - Sachin-Jigar - Rajkummar Rao, Shraddha Kapoor (320).mp3"} title={"Dil Ka Darzi"}/>
|
| 10 |
-
</div>
|
| 11 |
-
</main>
|
| 12 |
</div>
|
| 13 |
);
|
| 14 |
}
|
|
|
|
| 1 |
import Card from "@/components/Card";
|
| 2 |
+
import CategoriesSection from "@/components/CategoriesSection";
|
| 3 |
+
import './indexpage.css';
|
| 4 |
|
| 5 |
export default function Home() {
|
| 6 |
return (
|
| 7 |
+
<div className="index-page font-[family-name:var(--font-geist-sans)]">
|
| 8 |
+
<CategoriesSection/>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
</div>
|
| 10 |
);
|
| 11 |
}
|
frontend/src/components/Card.css
CHANGED
|
@@ -1,21 +1,26 @@
|
|
| 1 |
-
.music-card{
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
align-items: center;
|
| 9 |
-
gap: 10px;
|
| 10 |
}
|
| 11 |
|
| 12 |
-
.card-title{
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
.music-card {
|
| 2 |
+
width: 180px;
|
| 3 |
+
height: 40;
|
| 4 |
+
background-color: var(--background-2);
|
| 5 |
+
border-radius: 50px;
|
| 6 |
+
display: flex;
|
| 7 |
+
align-items: center;
|
|
|
|
|
|
|
| 8 |
}
|
| 9 |
|
| 10 |
+
.card-title {
|
| 11 |
+
display: -webkit-box;
|
| 12 |
+
-webkit-line-clamp: 1;
|
| 13 |
+
-webkit-box-orient: vertical;
|
| 14 |
+
overflow: hidden;
|
| 15 |
+
text-overflow: ellipsis;
|
| 16 |
+
white-space: normal;
|
| 17 |
+
line-height: 1.2em;
|
| 18 |
+
max-height: 1.2em;
|
| 19 |
+
font-size: 0.8rem;
|
| 20 |
+
cursor: pointer;
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
.card-icon {
|
| 24 |
+
font-size: 40px;
|
| 25 |
+
overflow: visible;
|
| 26 |
+
}
|
frontend/src/components/Card.js
CHANGED
|
@@ -3,9 +3,12 @@ import "./Card.css";
|
|
| 3 |
import { FaCompactDisc } from "react-icons/fa";
|
| 4 |
import { useMusicPlayer } from "@/context/MusicPlayerContext";
|
| 5 |
|
| 6 |
-
const Card = ({ src
|
| 7 |
const { initializePlayer } = useMusicPlayer();
|
| 8 |
|
|
|
|
|
|
|
|
|
|
| 9 |
const handleButtonClick = () => {
|
| 10 |
initializePlayer(src);
|
| 11 |
};
|
|
@@ -13,8 +16,7 @@ const Card = ({ src, title }) => {
|
|
| 13 |
return (
|
| 14 |
<button onClick={handleButtonClick}>
|
| 15 |
<div className="music-card">
|
| 16 |
-
<FaCompactDisc
|
| 17 |
-
|
| 18 |
<label className="card-title">{title}</label>
|
| 19 |
</div>
|
| 20 |
</button>
|
|
|
|
| 3 |
import { FaCompactDisc } from "react-icons/fa";
|
| 4 |
import { useMusicPlayer } from "@/context/MusicPlayerContext";
|
| 5 |
|
| 6 |
+
const Card = ({ src }) => {
|
| 7 |
const { initializePlayer } = useMusicPlayer();
|
| 8 |
|
| 9 |
+
// Extract title from the src by parsing the file name
|
| 10 |
+
const title = src.split('/').pop().split('.')[0]; // Remove path and extension
|
| 11 |
+
|
| 12 |
const handleButtonClick = () => {
|
| 13 |
initializePlayer(src);
|
| 14 |
};
|
|
|
|
| 16 |
return (
|
| 17 |
<button onClick={handleButtonClick}>
|
| 18 |
<div className="music-card">
|
| 19 |
+
<FaCompactDisc className="card-icon" />
|
|
|
|
| 20 |
<label className="card-title">{title}</label>
|
| 21 |
</div>
|
| 22 |
</button>
|
frontend/src/components/CategoriesSection.css
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
.categories-section {
|
| 2 |
+
display: flex;
|
| 3 |
+
gap: 10px;
|
| 4 |
+
flex-wrap: wrap;
|
| 5 |
+
padding: 10px;
|
| 6 |
+
justify-content: center;
|
| 7 |
+
}
|
| 8 |
+
|
| 9 |
+
.category-card {
|
| 10 |
+
width: 120px;
|
| 11 |
+
height: 60px;
|
| 12 |
+
padding: 10px;
|
| 13 |
+
background-color: var(--foreground-secondary);
|
| 14 |
+
border-radius: 5px;
|
| 15 |
+
display: flex;
|
| 16 |
+
justify-content: center;
|
| 17 |
+
align-items: center;
|
| 18 |
+
}
|
| 19 |
+
|
| 20 |
+
.category-title {
|
| 21 |
+
font-size: 1rem;
|
| 22 |
+
text-transform: capitalize;
|
| 23 |
+
font-weight: 600;
|
| 24 |
+
}
|
frontend/src/components/CategoriesSection.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"use client";
|
| 2 |
+
import { useEffect, useState } from "react";
|
| 3 |
+
import "./CategoriesSection.css";
|
| 4 |
+
import Link from "next/link";
|
| 5 |
+
|
| 6 |
+
const CategoriesSection = () => {
|
| 7 |
+
const [categories, setCategories] = useState([]);
|
| 8 |
+
const [loading, setLoading] = useState(true);
|
| 9 |
+
const [error, setError] = useState(null);
|
| 10 |
+
|
| 11 |
+
useEffect(() => {
|
| 12 |
+
const fetchCategories = async () => {
|
| 13 |
+
try {
|
| 14 |
+
const response = await fetch('/api/get/category/all');
|
| 15 |
+
if (!response.ok) {
|
| 16 |
+
throw new Error("Failed to fetch categories");
|
| 17 |
+
}
|
| 18 |
+
const data = await response.json();
|
| 19 |
+
setCategories(data); // Assuming data is ["english", "hindi", "rap"]
|
| 20 |
+
} catch (err) {
|
| 21 |
+
setError(err.message);
|
| 22 |
+
} finally {
|
| 23 |
+
setLoading(false);
|
| 24 |
+
}
|
| 25 |
+
};
|
| 26 |
+
|
| 27 |
+
fetchCategories();
|
| 28 |
+
}, []);
|
| 29 |
+
|
| 30 |
+
if (loading) {
|
| 31 |
+
return <div>Loading categories...</div>;
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
if (error) {
|
| 35 |
+
return <div>Error: {error}</div>;
|
| 36 |
+
}
|
| 37 |
+
|
| 38 |
+
return (
|
| 39 |
+
<div className="categories-section">
|
| 40 |
+
{categories.map((category, index) => (
|
| 41 |
+
<CategoryCard key={index} label={category} />
|
| 42 |
+
))}
|
| 43 |
+
</div>
|
| 44 |
+
);
|
| 45 |
+
};
|
| 46 |
+
|
| 47 |
+
const CategoryCard = ({ label }) => {
|
| 48 |
+
return (
|
| 49 |
+
<Link href={`/category/${label}`}>
|
| 50 |
+
<div className="category-card">
|
| 51 |
+
<label className="category-title">{label}</label>
|
| 52 |
+
</div>
|
| 53 |
+
</Link>
|
| 54 |
+
);
|
| 55 |
+
};
|
| 56 |
+
|
| 57 |
+
export default CategoriesSection;
|
frontend/src/components/MusicPlayer.js
CHANGED
|
@@ -26,6 +26,7 @@ export default function MusicPlayer() {
|
|
| 26 |
togglePlayerSize,
|
| 27 |
setIsPlayerVisible,
|
| 28 |
initializePlayer,
|
|
|
|
| 29 |
} = useMusicPlayer();
|
| 30 |
|
| 31 |
const [currentSrc, setCurrentSrc] = useState(src);
|
|
@@ -227,7 +228,7 @@ export default function MusicPlayer() {
|
|
| 227 |
{isPlayerMaximized && (
|
| 228 |
<div className={`player-controls ${showControls ? "show" : "hide"}`}>
|
| 229 |
<div className="player-controls-top">
|
| 230 |
-
<label className="music-title">{
|
| 231 |
<button className="player-max-button" onClick={togglePlayerSize}>
|
| 232 |
<IoIosArrowDown />
|
| 233 |
</button>
|
|
@@ -325,7 +326,7 @@ export default function MusicPlayer() {
|
|
| 325 |
{!isPlayerMaximized && (
|
| 326 |
<div className="player-controls-mini">
|
| 327 |
<div className="player-mini-control-top">
|
| 328 |
-
<label className="music-title mini">{
|
| 329 |
<button className="player-min-button" onClick={togglePlayerSize}>
|
| 330 |
<IoIosArrowUp />
|
| 331 |
</button>
|
|
|
|
| 26 |
togglePlayerSize,
|
| 27 |
setIsPlayerVisible,
|
| 28 |
initializePlayer,
|
| 29 |
+
fileName,
|
| 30 |
} = useMusicPlayer();
|
| 31 |
|
| 32 |
const [currentSrc, setCurrentSrc] = useState(src);
|
|
|
|
| 228 |
{isPlayerMaximized && (
|
| 229 |
<div className={`player-controls ${showControls ? "show" : "hide"}`}>
|
| 230 |
<div className="player-controls-top">
|
| 231 |
+
<label className="music-title">{fileName}</label>
|
| 232 |
<button className="player-max-button" onClick={togglePlayerSize}>
|
| 233 |
<IoIosArrowDown />
|
| 234 |
</button>
|
|
|
|
| 326 |
{!isPlayerMaximized && (
|
| 327 |
<div className="player-controls-mini">
|
| 328 |
<div className="player-mini-control-top">
|
| 329 |
+
<label className="music-title mini">{fileName}</label>
|
| 330 |
<button className="player-min-button" onClick={togglePlayerSize}>
|
| 331 |
<IoIosArrowUp />
|
| 332 |
</button>
|
frontend/src/context/MusicPlayerContext.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
| 1 |
-
// MusicPlayerContext.js
|
| 2 |
"use client";
|
| 3 |
|
| 4 |
import React, { createContext, useContext, useRef, useState } from "react";
|
|
@@ -8,10 +7,66 @@ const MusicPlayerContext = createContext();
|
|
| 8 |
export const MusicPlayerProvider = ({ children }) => {
|
| 9 |
const videoRef = useRef(null);
|
| 10 |
const [src, setSrc] = useState("");
|
|
|
|
| 11 |
const [isPlayerVisible, setIsPlayerVisible] = useState(false);
|
| 12 |
const [isPlayerMaximized, setIsPlayerMaximized] = useState(false);
|
|
|
|
| 13 |
|
| 14 |
-
const initializePlayer = (source) => {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 15 |
setSrc(source);
|
| 16 |
setIsPlayerVisible(true);
|
| 17 |
if (videoRef.current) {
|
|
@@ -29,9 +84,11 @@ export const MusicPlayerProvider = ({ children }) => {
|
|
| 29 |
initializePlayer,
|
| 30 |
isPlayerVisible,
|
| 31 |
src,
|
|
|
|
| 32 |
isPlayerMaximized,
|
| 33 |
togglePlayerSize,
|
| 34 |
-
setIsPlayerVisible
|
|
|
|
| 35 |
}}
|
| 36 |
>
|
| 37 |
{children}
|
|
|
|
|
|
|
| 1 |
"use client";
|
| 2 |
|
| 3 |
import React, { createContext, useContext, useRef, useState } from "react";
|
|
|
|
| 7 |
export const MusicPlayerProvider = ({ children }) => {
|
| 8 |
const videoRef = useRef(null);
|
| 9 |
const [src, setSrc] = useState("");
|
| 10 |
+
const [fileName, setFileName] = useState(""); // New state for the file name
|
| 11 |
const [isPlayerVisible, setIsPlayerVisible] = useState(false);
|
| 12 |
const [isPlayerMaximized, setIsPlayerMaximized] = useState(false);
|
| 13 |
+
const [loadingProgress, setLoadingProgress] = useState(null); // New state for loading progress
|
| 14 |
|
| 15 |
+
const initializePlayer = async (source) => {
|
| 16 |
+
// Extract the file name from the source path
|
| 17 |
+
const extractedFileName = source.split('/').pop();
|
| 18 |
+
setFileName(extractedFileName); // Set the file name state
|
| 19 |
+
|
| 20 |
+
try {
|
| 21 |
+
// Call the API to get the music file or start the download
|
| 22 |
+
const response = await fetch(`/api/get/music/${encodeURIComponent(extractedFileName)}`);
|
| 23 |
+
const data = await response.json();
|
| 24 |
+
|
| 25 |
+
// If the file is being downloaded, get the progress URL
|
| 26 |
+
if (data.status === "Download started") {
|
| 27 |
+
const progressUrl = data.progress_url;
|
| 28 |
+
checkProgress(progressUrl, extractedFileName);
|
| 29 |
+
} else {
|
| 30 |
+
// If the music file is available, initialize the player
|
| 31 |
+
startPlayer(data.url);
|
| 32 |
+
}
|
| 33 |
+
} catch (error) {
|
| 34 |
+
console.error("Error initializing player:", error);
|
| 35 |
+
}
|
| 36 |
+
};
|
| 37 |
+
|
| 38 |
+
const checkProgress = (progressUrl, fileName) => {
|
| 39 |
+
const intervalId = setInterval(async () => {
|
| 40 |
+
try {
|
| 41 |
+
const response = await fetch(progressUrl);
|
| 42 |
+
const progressData = await response.json();
|
| 43 |
+
|
| 44 |
+
setLoadingProgress(progressData.progress); // Update loading progress state
|
| 45 |
+
|
| 46 |
+
// Check if the download is complete
|
| 47 |
+
if (progressData.progress.status === "Completed") {
|
| 48 |
+
clearInterval(intervalId); // Stop checking progress
|
| 49 |
+
fetchFinalUrl(fileName); // Fetch the final URL
|
| 50 |
+
}
|
| 51 |
+
} catch (error) {
|
| 52 |
+
console.error("Error fetching progress:", error);
|
| 53 |
+
}
|
| 54 |
+
}, 2000); // Check progress every 2 seconds
|
| 55 |
+
};
|
| 56 |
+
|
| 57 |
+
const fetchFinalUrl = async (fileName) => {
|
| 58 |
+
try {
|
| 59 |
+
const response = await fetch(`/api/get/music/${encodeURIComponent(fileName)}`);
|
| 60 |
+
const data = await response.json();
|
| 61 |
+
|
| 62 |
+
// Start the player with the final URL
|
| 63 |
+
startPlayer(data.url);
|
| 64 |
+
} catch (error) {
|
| 65 |
+
console.error("Error fetching final URL:", error);
|
| 66 |
+
}
|
| 67 |
+
};
|
| 68 |
+
|
| 69 |
+
const startPlayer = (source) => {
|
| 70 |
setSrc(source);
|
| 71 |
setIsPlayerVisible(true);
|
| 72 |
if (videoRef.current) {
|
|
|
|
| 84 |
initializePlayer,
|
| 85 |
isPlayerVisible,
|
| 86 |
src,
|
| 87 |
+
fileName, // Export the file name
|
| 88 |
isPlayerMaximized,
|
| 89 |
togglePlayerSize,
|
| 90 |
+
setIsPlayerVisible,
|
| 91 |
+
loadingProgress,
|
| 92 |
}}
|
| 93 |
>
|
| 94 |
{children}
|
frontend/src/lib/LBApi.js
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import {BASE_URL} from "@/lib/config";
|
| 2 |
+
|
| 3 |
+
// Function to get music store
|
| 4 |
+
async function getMusicStore() {
|
| 5 |
+
try {
|
| 6 |
+
const response = await fetch(`${BASE_URL}/api/get/music/store`);
|
| 7 |
+
if (!response.ok) {
|
| 8 |
+
throw new Error("Failed to fetch music store");
|
| 9 |
+
}
|
| 10 |
+
return await response.json();
|
| 11 |
+
} catch (error) {
|
| 12 |
+
console.error("Error fetching music store:", error);
|
| 13 |
+
throw error;
|
| 14 |
+
}
|
| 15 |
+
}
|
| 16 |
+
|
| 17 |
+
// Function to get all music with optional pagination
|
| 18 |
+
async function getAllMusic(page = null, limit = null) {
|
| 19 |
+
let url = `${BASE_URL}/api/get/music/all`;
|
| 20 |
+
const params = new URLSearchParams();
|
| 21 |
+
|
| 22 |
+
if (page) params.append("page", page);
|
| 23 |
+
if (limit) params.append("limit", limit);
|
| 24 |
+
|
| 25 |
+
if (params.toString()) {
|
| 26 |
+
url += `?${params.toString()}`;
|
| 27 |
+
}
|
| 28 |
+
|
| 29 |
+
try {
|
| 30 |
+
const response = await fetch(url);
|
| 31 |
+
if (!response.ok) {
|
| 32 |
+
throw new Error("Failed to fetch music");
|
| 33 |
+
}
|
| 34 |
+
return await response.json();
|
| 35 |
+
} catch (error) {
|
| 36 |
+
console.error("Error fetching all music:", error);
|
| 37 |
+
throw error;
|
| 38 |
+
}
|
| 39 |
+
}
|
| 40 |
+
|
| 41 |
+
// Function to get categories
|
| 42 |
+
async function getCategories() {
|
| 43 |
+
try {
|
| 44 |
+
const response = await fetch(`${BASE_URL}/api/get/category/all`);
|
| 45 |
+
if (!response.ok) {
|
| 46 |
+
throw new Error("Failed to fetch categories");
|
| 47 |
+
}
|
| 48 |
+
return await response.json();
|
| 49 |
+
} catch (error) {
|
| 50 |
+
console.error("Error fetching categories:", error);
|
| 51 |
+
throw error;
|
| 52 |
+
}
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
// Function to get music by category with pagination
|
| 56 |
+
async function getMusicByCategory(category, page = 1, limit = 10) {
|
| 57 |
+
try {
|
| 58 |
+
const response = await fetch(`${BASE_URL}/api/get/category/${category}?page=${page}&limit=${limit}`);
|
| 59 |
+
if (!response.ok) {
|
| 60 |
+
throw new Error("Failed to fetch music from category");
|
| 61 |
+
}
|
| 62 |
+
return await response.json();
|
| 63 |
+
} catch (error) {
|
| 64 |
+
console.error("Error fetching music from category:", error);
|
| 65 |
+
throw error;
|
| 66 |
+
}
|
| 67 |
+
}
|
| 68 |
+
|
| 69 |
+
// Function to get a specific music file by name
|
| 70 |
+
async function getMusicByFileName(fileName) {
|
| 71 |
+
try {
|
| 72 |
+
const response = await fetch(`${BASE_URL}/api/get/music/${encodeURIComponent(fileName)}`);
|
| 73 |
+
if (!response.ok) {
|
| 74 |
+
throw new Error("Failed to fetch music file");
|
| 75 |
+
}
|
| 76 |
+
return await response.json();
|
| 77 |
+
} catch (error) {
|
| 78 |
+
console.error("Error fetching music file:", error);
|
| 79 |
+
throw error;
|
| 80 |
+
}
|
| 81 |
+
}
|
| 82 |
+
|
| 83 |
+
export {
|
| 84 |
+
getMusicStore,
|
| 85 |
+
getAllMusic,
|
| 86 |
+
getCategories,
|
| 87 |
+
getMusicByCategory,
|
| 88 |
+
getMusicByFileName,
|
| 89 |
+
};
|
frontend/src/lib/config.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
export const BASE_URL = "https://hans-r-d-load-balancer.hf.space";
|