Patch: Fix to StreamerRoute to correctly send user to UserPage/VideoPage;
Update: Display Category Thumbnails
@@ -6,7 +6,7 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Team Software Project</title>
|
||||
</head>
|
||||
<body class="h-screen">
|
||||
<body class="min-h-screen h-full">
|
||||
<div id="root" class="h-full bg-gradient-to-tr from-[#07001F] via-[#1D0085] to-[#CC00AF]"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
|
||||
|
Before Width: | Height: | Size: 844 KiB |
|
Before Width: | Height: | Size: 3.3 MiB |
|
Before Width: | Height: | Size: 95 KiB |
|
Before Width: | Height: | Size: 194 KiB |
BIN
frontend/public/images/thumbnails/categories/art.webp
Normal file
|
After Width: | Height: | Size: 508 KiB |
BIN
frontend/public/images/thumbnails/categories/chatting.webp
Normal file
|
After Width: | Height: | Size: 3.1 MiB |
BIN
frontend/public/images/thumbnails/categories/cooking.webp
Normal file
|
After Width: | Height: | Size: 71 KiB |
BIN
frontend/public/images/thumbnails/categories/gaming.webp
Normal file
|
After Width: | Height: | Size: 149 KiB |
|
Before Width: | Height: | Size: 100 KiB After Width: | Height: | Size: 100 KiB |
BIN
frontend/public/images/thumbnails/categories/sports.webp
Normal file
|
After Width: | Height: | Size: 95 KiB |
@@ -27,6 +27,7 @@ const ListItem: React.FC<ListItemProps> = ({
|
||||
thumbnail,
|
||||
onItemClick,
|
||||
}) => {
|
||||
console.log(title, "thumbnail", thumbnail);
|
||||
return (
|
||||
<div
|
||||
className="flex flex-col bg-gray-800 rounded-lg overflow-hidden cursor-pointer hover:bg-gray-700 transition-colors"
|
||||
@@ -35,7 +36,7 @@ const ListItem: React.FC<ListItemProps> = ({
|
||||
<div className="relative w-full pt-[56.25%]">
|
||||
{thumbnail ? (
|
||||
<img
|
||||
src={`images/` + thumbnail}
|
||||
src={thumbnail}
|
||||
alt={title}
|
||||
className="absolute top-0 left-0 w-full h-full object-cover"
|
||||
/>
|
||||
@@ -74,6 +75,7 @@ const ListRow: React.FC<ListRowProps> = ({
|
||||
title={item.title}
|
||||
streamer={item.type === "stream" ? (item.streamer) : undefined}
|
||||
viewers={item.viewers}
|
||||
thumbnail={item.thumbnail}
|
||||
onItemClick={() =>
|
||||
onClick?.(item.id, item.streamer || item.title)
|
||||
}
|
||||
|
||||
@@ -13,8 +13,7 @@ const StreamerRoute: React.FC = () => {
|
||||
try {
|
||||
const response = await fetch(`/api/streamer/${streamerName}/status`);
|
||||
const data = await response.json();
|
||||
console.log("Stream status:", data);
|
||||
setIsLive(data.status === "live");
|
||||
setIsLive(data.is_live);
|
||||
} catch (error) {
|
||||
console.error("Error checking stream status:", error);
|
||||
setIsLive(false);
|
||||
@@ -36,7 +35,7 @@ const StreamerRoute: React.FC = () => {
|
||||
}
|
||||
|
||||
// streamId=0 is a special case for the streamer's latest stream
|
||||
return isLive ? <VideoPage streamId={0} /> : <UserPage profile={streamerName} />;
|
||||
return isLive ? <VideoPage streamId={0} /> : (streamerName ? <UserPage username={streamerName} /> : <div>Error: Streamer not found</div>);
|
||||
};
|
||||
|
||||
export default StreamerRoute;
|
||||
|
||||
@@ -41,12 +41,12 @@ export function StreamsProvider({ children }: { children: React.ReactNode }) {
|
||||
// Streams
|
||||
fetch(fetch_url[0])
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
const extractedData: StreamItem[] = data.streams.map((stream: any) => ({
|
||||
.then((data: StreamItem[]) => {
|
||||
const extractedData: StreamItem[] = data.map((stream: any) => ({
|
||||
type: "stream",
|
||||
id: stream.stream_id,
|
||||
title: stream.title,
|
||||
streamer: stream.user_id,
|
||||
streamer: stream.username,
|
||||
viewers: stream.num_viewers,
|
||||
thumbnail: stream.thumbnail,
|
||||
}));
|
||||
@@ -56,15 +56,17 @@ export function StreamsProvider({ children }: { children: React.ReactNode }) {
|
||||
// Categories
|
||||
fetch(fetch_url[1])
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
const extractedData: CategoryItem[] = data.categories.map(
|
||||
.then((data: CategoryItem[]) => {
|
||||
const extractedData: CategoryItem[] = data.map(
|
||||
(category: any) => ({
|
||||
type: "category",
|
||||
id: category.category_id,
|
||||
title: category.category_name,
|
||||
viewers: category.num_viewers,
|
||||
thumbnail: `/images/thumbnails/categories/${category.category_name.toLowerCase().replace(/ /g, "_")}.webp`
|
||||
})
|
||||
);
|
||||
console.log(extractedData);
|
||||
setFeaturedCategories(extractedData);
|
||||
});
|
||||
}, []);
|
||||
|
||||
@@ -52,7 +52,7 @@ const VideoPage: React.FC<VideoPageProps> = ({ streamId }) => {
|
||||
console.error("Failed to load stream data:", res.statusText);
|
||||
}
|
||||
res.json().then((data) => {
|
||||
if (!data.validStream) navigate(`/`);
|
||||
// if (!data.validStream) navigate(`/`);
|
||||
console.log(`Loading stream data for ${streamerName}`);
|
||||
setStreamData({
|
||||
streamId: data.streamId,
|
||||
|
||||
165
package-lock.json
generated
@@ -1,165 +0,0 @@
|
||||
{
|
||||
"name": "cs3305-team11",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"dependencies": {
|
||||
"lucide-react": "^0.474.0",
|
||||
"react-router-dom": "^7.1.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^19.0.8",
|
||||
"@types/react-dom": "^19.0.3",
|
||||
"typescript": "^5.7.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/cookie": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz",
|
||||
"integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/react": {
|
||||
"version": "19.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.8.tgz",
|
||||
"integrity": "sha512-9P/o1IGdfmQxrujGbIMDyYaaCykhLKc0NGCtYcECNUr9UAaDe4gwvV9bR6tvd5Br1SG0j+PBpbKr2UYY8CwqSw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"csstype": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/react-dom": {
|
||||
"version": "19.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.0.3.tgz",
|
||||
"integrity": "sha512-0Knk+HJiMP/qOZgMyNFamlIjw9OFCsyC2ZbigmEEyXXixgre6IQpm/4V+r3qH4GC1JPvRJKInw+on2rV6YZLeA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@types/react": "^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/cookie": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz",
|
||||
"integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/csstype": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
||||
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lucide-react": {
|
||||
"version": "0.474.0",
|
||||
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.474.0.tgz",
|
||||
"integrity": "sha512-CmghgHkh0OJNmxGKWc0qfPJCYHASPMVSyGY8fj3xgk4v84ItqDg64JNKFZn5hC6E0vHi6gxnbCgwhyVB09wQtA==",
|
||||
"license": "ISC",
|
||||
"peerDependencies": {
|
||||
"react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react": {
|
||||
"version": "19.0.0",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-19.0.0.tgz",
|
||||
"integrity": "sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-dom": {
|
||||
"version": "19.0.0",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.0.0.tgz",
|
||||
"integrity": "sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"scheduler": "^0.25.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-router": {
|
||||
"version": "7.1.3",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-7.1.3.tgz",
|
||||
"integrity": "sha512-EezYymLY6Guk/zLQ2vRA8WvdUhWFEj5fcE3RfWihhxXBW7+cd1LsIiA3lmx+KCmneAGQuyBv820o44L2+TtkSA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/cookie": "^0.6.0",
|
||||
"cookie": "^1.0.1",
|
||||
"set-cookie-parser": "^2.6.0",
|
||||
"turbo-stream": "2.4.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=18",
|
||||
"react-dom": ">=18"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/react-router-dom": {
|
||||
"version": "7.1.3",
|
||||
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.1.3.tgz",
|
||||
"integrity": "sha512-qQGTE+77hleBzv9SIUIkGRvuFBQGagW+TQKy53UTZAO/3+YFNBYvRsNIZ1GT17yHbc63FylMOdS+m3oUriF1GA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"react-router": "7.1.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=18",
|
||||
"react-dom": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/scheduler": {
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.25.0.tgz",
|
||||
"integrity": "sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/set-cookie-parser": {
|
||||
"version": "2.7.1",
|
||||
"resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz",
|
||||
"integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/turbo-stream": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/turbo-stream/-/turbo-stream-2.4.0.tgz",
|
||||
"integrity": "sha512-FHncC10WpBd2eOmGwpmQsWLDoK4cqsA/UT/GqNoaKOQnT8uzhtCbg3EoUDMvqpOSAI0S26mr0rkjzbOO6S3v1g==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.7.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz",
|
||||
"integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.17"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
package.json
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"devDependencies": {
|
||||
"@types/react": "^19.0.8",
|
||||
"@types/react-dom": "^19.0.3",
|
||||
"typescript": "^5.7.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"lucide-react": "^0.474.0",
|
||||
"react-router-dom": "^7.1.3"
|
||||
}
|
||||
}
|
||||
@@ -42,11 +42,11 @@ def get_recommended_streams() -> list[dict]:
|
||||
@stream_bp.route('/get_categories')
|
||||
def get_categories() -> list[dict]:
|
||||
"""
|
||||
Returns a list of streams in the most popular category
|
||||
Returns a list of most watched categories
|
||||
"""
|
||||
|
||||
category_data = most_popular_category()
|
||||
streams = recommendations_based_on_category(category_data["category_id"])
|
||||
streams = recommendations_based_on_category(category_data['category_id'])
|
||||
return jsonify(streams)
|
||||
|
||||
@login_required
|
||||
|
||||
@@ -49,3 +49,11 @@ CREATE TABLE streams
|
||||
FOREIGN KEY (category_id) REFERENCES categories(category_id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
|
||||
SELECT users.user_id, title, username, num_viewers, category_name
|
||||
FROM streams
|
||||
JOIN users ON users.user_id = streams.user_id
|
||||
JOIN categories ON streams.category_id = categories.category_id
|
||||
ORDER BY num_viewers DESC
|
||||
LIMIT 25;
|
||||
@@ -61,7 +61,7 @@ def default_recommendations():
|
||||
JOIN users ON users.user_id = streams.user_id
|
||||
JOIN categories ON streams.category_id = categories.category_id
|
||||
ORDER BY num_viewers DESC
|
||||
LIMIT 25
|
||||
LIMIT 25;
|
||||
""")
|
||||
db.close_connection()
|
||||
return data
|
||||
|
||||