mirror of
https://github.com/BreizhHardware/Jellystat.git
synced 2026-01-18 16:27:20 +01:00
generalized Genre stat card
Added Genre stat card to libary views #292 bug fixes on Genre chart view
This commit is contained in:
@@ -516,7 +516,7 @@ router.post("/getViewsByHour", async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
router.get("/getGenreStats", async (req, res) => {
|
||||
router.get("/getGenreUserStats", async (req, res) => {
|
||||
try {
|
||||
const { size = 50, page = 1, userid } = req.query;
|
||||
|
||||
@@ -579,6 +579,69 @@ router.get("/getGenreStats", async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
router.get("/getGenreLibraryStats", async (req, res) => {
|
||||
try {
|
||||
const { size = 50, page = 1, libraryid } = req.query;
|
||||
|
||||
if (libraryid === undefined) {
|
||||
res.status(400);
|
||||
res.send("No Library ID provided");
|
||||
return;
|
||||
}
|
||||
|
||||
const values = [];
|
||||
const query = {
|
||||
select: ["COALESCE(g.genre, 'No Genre') AS genre", `SUM(a."PlaybackDuration") AS duration`, "COUNT(*) AS plays"],
|
||||
table: "jf_playback_activity_with_metadata",
|
||||
alias: "a",
|
||||
joins: [
|
||||
{
|
||||
type: "inner",
|
||||
table: "jf_library_items",
|
||||
alias: "i",
|
||||
conditions: [{ first: "a.NowPlayingItemId", operator: "=", second: "i.Id" }],
|
||||
},
|
||||
{
|
||||
type: "left",
|
||||
table: `
|
||||
LATERAL (
|
||||
SELECT
|
||||
jsonb_array_elements_text(
|
||||
CASE
|
||||
WHEN jsonb_array_length(COALESCE(i."Genres", '[]'::jsonb)) = 0 THEN '["No Genre"]'::jsonb
|
||||
ELSE i."Genres"
|
||||
END
|
||||
) AS genre
|
||||
)
|
||||
`,
|
||||
alias: "g",
|
||||
conditions: [{ first: 1, operator: "=", value: 1, wrap: false }],
|
||||
},
|
||||
],
|
||||
|
||||
where: [[{ column: "a.ParentId", operator: "=", value: `$${values.length + 1}` }]],
|
||||
group_by: [`COALESCE(g.genre, 'No Genre')`],
|
||||
order_by: "genre",
|
||||
pageNumber: page,
|
||||
pageSize: size,
|
||||
};
|
||||
|
||||
values.push(libraryid);
|
||||
|
||||
query.values = values;
|
||||
|
||||
const result = await dbHelper.query(query);
|
||||
|
||||
const response = { current_page: page, pages: result.pages, size: size, results: result.results };
|
||||
|
||||
res.send(response);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
res.status(503);
|
||||
res.send(error);
|
||||
}
|
||||
});
|
||||
|
||||
// Handle other routes
|
||||
router.use((req, res) => {
|
||||
res.status(404).send({ error: "Not Found" });
|
||||
|
||||
@@ -16,6 +16,7 @@ import { Tabs, Tab, Button, ButtonGroup } from "react-bootstrap";
|
||||
import { Trans } from "react-i18next";
|
||||
import LibraryOptions from "./library/library-options";
|
||||
import GlobalStats from "./general/globalStats";
|
||||
import GenreLibraryStats from "./library/genre-library-stats.jsx";
|
||||
|
||||
function LibraryInfo() {
|
||||
const { LibraryId } = useParams();
|
||||
@@ -110,6 +111,8 @@ function LibraryInfo() {
|
||||
title={<Trans i18nKey="LIBRARY_INFO.LIBRARY_STATS" />}
|
||||
/>
|
||||
|
||||
<GenreLibraryStats LibraryId={LibraryId} />
|
||||
|
||||
{!data.archived && (
|
||||
<ErrorBoundary>
|
||||
<RecentlyAdded LibraryId={LibraryId} />
|
||||
|
||||
77
src/pages/components/library/genre-library-stats.jsx
Normal file
77
src/pages/components/library/genre-library-stats.jsx
Normal file
@@ -0,0 +1,77 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { Row, Col } from "react-bootstrap";
|
||||
import GenreStatCard from "../statCards/genre-stat-card.jsx";
|
||||
import { Trans } from "react-i18next";
|
||||
import "../../css/genres.css";
|
||||
import Config from "../../../lib/config";
|
||||
import axios from "../../../lib/axios_instance";
|
||||
|
||||
function GenreLibraryStats(props) {
|
||||
const [data, setData] = useState();
|
||||
const [config, setConfig] = useState();
|
||||
|
||||
useEffect(() => {
|
||||
const fetchConfig = async () => {
|
||||
try {
|
||||
const newConfig = await Config.getConfig();
|
||||
setConfig(newConfig);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
};
|
||||
|
||||
const fetchData = async () => {
|
||||
if (config) {
|
||||
try {
|
||||
const itemData = await axios.get(`/stats/getGenreLibraryStats`, {
|
||||
params: {
|
||||
libraryid: props.LibraryId,
|
||||
},
|
||||
headers: {
|
||||
Authorization: `Bearer ${config.token}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
const results = itemData.data.results || [];
|
||||
setData(results);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (!data) {
|
||||
fetchData();
|
||||
}
|
||||
|
||||
if (!config) {
|
||||
fetchConfig();
|
||||
}
|
||||
|
||||
const intervalId = setInterval(fetchData, 60000 * 5);
|
||||
return () => clearInterval(intervalId);
|
||||
}, [data, config, props.LibraryId]);
|
||||
|
||||
if (!data || data.length == 0 || !config) {
|
||||
return <></>;
|
||||
}
|
||||
return (
|
||||
<div className="genre">
|
||||
<h1 className="my-3">
|
||||
<Trans i18nKey="GENRES" />
|
||||
</h1>
|
||||
<div className="genre-container d-flex flex-row justify-content-between">
|
||||
<Row className="w-100 pb-5">
|
||||
<Col className="p-0 auto">
|
||||
<GenreStatCard data={data} dataKey="duration" />
|
||||
</Col>
|
||||
<Col className="p-0 auto">
|
||||
<GenreStatCard data={data} dataKey="plays" />
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default GenreLibraryStats;
|
||||
@@ -24,7 +24,9 @@ function GenreStatCard(props) {
|
||||
<div className="radial-tooltip">
|
||||
<p className="tooltip-header">{payload[0].payload.genre}</p>
|
||||
<p>
|
||||
{props.dataKey == "duration" ? formatTotalWatchTime(payload[0].value) : payload[0].value} {i18next.t("UNITS.PLAYS")}
|
||||
{props.dataKey == "duration"
|
||||
? formatTotalWatchTime(payload[0].value)
|
||||
: `${payload[0].value} ${i18next.t("UNITS.PLAYS")}`}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
@@ -12,7 +12,7 @@ import { Trans } from "react-i18next";
|
||||
import baseUrl from "../../lib/baseurl";
|
||||
import GlobalStats from "./general/globalStats";
|
||||
import ActivityTimeline from "../activity_time_line";
|
||||
import GenreStats from "./user-info/genre-stats.jsx";
|
||||
import GenreUserStats from "./user-info/genre-user-stats.jsx";
|
||||
|
||||
function UserInfo() {
|
||||
const { UserId } = useParams();
|
||||
@@ -125,7 +125,7 @@ function UserInfo() {
|
||||
endpoint={"getGlobalUserStats"}
|
||||
title={<Trans i18nKey="USERS_PAGE.USER_STATS" />}
|
||||
/>
|
||||
<GenreStats UserId={UserId} />
|
||||
<GenreUserStats UserId={UserId} />
|
||||
<LastPlayed UserId={UserId} />
|
||||
</Tab>
|
||||
<Tab eventKey="tabActivity" className="bg-transparent">
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { Row, Col } from "react-bootstrap";
|
||||
import GenreStatCard from "./genre-stat-card.jsx";
|
||||
import GenreStatCard from "../statCards/genre-stat-card.jsx";
|
||||
import { Trans } from "react-i18next";
|
||||
import "../../css/genres.css";
|
||||
import Config from "../../../lib/config";
|
||||
import axios from "../../../lib/axios_instance";
|
||||
|
||||
function GenreStats(props) {
|
||||
function GenreUserStats(props) {
|
||||
const [data, setData] = useState();
|
||||
const [config, setConfig] = useState();
|
||||
|
||||
@@ -23,7 +23,7 @@ function GenreStats(props) {
|
||||
const fetchData = async () => {
|
||||
if (config) {
|
||||
try {
|
||||
const itemData = await axios.get(`/stats/getGenreStats`, {
|
||||
const itemData = await axios.get(`/stats/getGenreUserStats`, {
|
||||
params: {
|
||||
userid: props.UserId,
|
||||
},
|
||||
@@ -74,4 +74,4 @@ function GenreStats(props) {
|
||||
);
|
||||
}
|
||||
|
||||
export default GenreStats;
|
||||
export default GenreUserStats;
|
||||
Reference in New Issue
Block a user