mirror of
https://github.com/BreizhHardware/Jellystat.git
synced 2026-01-18 16:27:20 +01:00
Fix to address #229 Base urls. Alot of hacky code here. hope it doesnt break
This commit is contained in:
10
.vscode/launch.json
vendored
10
.vscode/launch.json
vendored
@@ -14,8 +14,14 @@
|
||||
"request": "launch",
|
||||
"command": "npm run start",
|
||||
"cwd": "${workspaceFolder}"
|
||||
}
|
||||
,
|
||||
},
|
||||
{
|
||||
"type": "node-terminal",
|
||||
"name": "Run Script: start client",
|
||||
"request": "launch",
|
||||
"command": "npm run start-client",
|
||||
"cwd": "${workspaceFolder}"
|
||||
},
|
||||
{
|
||||
"type": "node-terminal",
|
||||
"name": "Run Script: start-server",
|
||||
|
||||
@@ -2,9 +2,11 @@ const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
async function writeEnvVariables() {
|
||||
//Define sensitive variables that should not be exposed
|
||||
const excludedVariables = ["JS_GEOLITE_LICENSE_KEY", "JS_USER", "JS_PASSWORD"];
|
||||
// Fetch environment variables that start with JS_
|
||||
const envVariables = Object.keys(process.env).reduce((acc, key) => {
|
||||
if (key.startsWith("JS_")) {
|
||||
if (key.startsWith("JS_") && !excludedVariables.includes(key)) {
|
||||
acc[key] = process.env[key];
|
||||
}
|
||||
return acc;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// core
|
||||
require("dotenv").config();
|
||||
const http = require("http");
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const express = require("express");
|
||||
const compression = require("compression");
|
||||
@@ -36,9 +37,20 @@ const writeEnvVariables = require("./classes/env");
|
||||
const app = express();
|
||||
const db = knex(knexConfig.development);
|
||||
|
||||
const ensureSlashes = (url) => {
|
||||
if (!url.startsWith("/")) {
|
||||
url = "/" + url;
|
||||
}
|
||||
if (url.endsWith("/")) {
|
||||
url = url.slice(0, -1);
|
||||
}
|
||||
return url;
|
||||
};
|
||||
|
||||
const PORT = 3000;
|
||||
const LISTEN_IP = "0.0.0.0";
|
||||
const JWT_SECRET = process.env.JWT_SECRET;
|
||||
const BASE_NAME = process.env.JS_BASE_URL ? ensureSlashes(process.env.JS_BASE_URL) : "";
|
||||
|
||||
if (JWT_SECRET === undefined) {
|
||||
console.log("JWT Secret cannot be undefined");
|
||||
@@ -68,8 +80,60 @@ function typeInferenceMiddleware(req, res, next) {
|
||||
|
||||
app.use(typeInferenceMiddleware);
|
||||
|
||||
const findFile = (dir, fileName) => {
|
||||
const files = fs.readdirSync(dir);
|
||||
for (const file of files) {
|
||||
const fullPath = path.join(dir, file);
|
||||
const stat = fs.statSync(fullPath);
|
||||
if (stat.isDirectory()) {
|
||||
const result = findFile(fullPath, fileName);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
} else if (file === fileName) {
|
||||
return fullPath;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const root = path.join(__dirname, "..", "dist");
|
||||
|
||||
//hacky middleware to handle basename changes for UI
|
||||
|
||||
app.use((req, res, next) => {
|
||||
// Ignore requests containing 'socket.io'
|
||||
if (req.url.includes("socket.io")) {
|
||||
return next();
|
||||
}
|
||||
|
||||
const fileRegex = /\/([^\/]+\.(css|ico|js|json|png))$/;
|
||||
const match = req.url.match(fileRegex);
|
||||
if (match) {
|
||||
// Extract the file name
|
||||
const fileName = match[1];
|
||||
|
||||
//Exclude translation.json from this hack as it messes up the translations by returning the first file regardless of language chosen
|
||||
if (fileName != "translation.json") {
|
||||
// Find the file
|
||||
const filePath = findFile(root, fileName);
|
||||
if (filePath) {
|
||||
return res.sendFile(filePath);
|
||||
} else {
|
||||
return res.status(404).send("File not found");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (BASE_NAME && req.url.startsWith(BASE_NAME) && req.url !== BASE_NAME) {
|
||||
req.url = req.url.slice(BASE_NAME.length);
|
||||
// console.log("URL: " + req.url);
|
||||
}
|
||||
next();
|
||||
});
|
||||
|
||||
// initiate routes
|
||||
app.use("/auth", authRouter, () => {
|
||||
app.use(`/auth`, authRouter, () => {
|
||||
/* #swagger.tags = ['Auth'] */
|
||||
}); // mount the API router at /auth
|
||||
app.use("/proxy", proxyRouter, () => {
|
||||
@@ -99,9 +163,11 @@ app.use("/swagger", swaggerUi.serve, swaggerUi.setup(swaggerDocument));
|
||||
|
||||
// for deployment of static page
|
||||
writeEnvVariables().then(() => {
|
||||
const root = path.join(__dirname, "..", "dist");
|
||||
app.use(express.static(root));
|
||||
app.get("*", (req, res) => {
|
||||
app.get("*", (req, res, next) => {
|
||||
if (req.url.includes("socket.io")) {
|
||||
return next();
|
||||
}
|
||||
res.sendFile(path.join(__dirname, "..", "dist", "index.html"));
|
||||
});
|
||||
});
|
||||
@@ -163,7 +229,7 @@ try {
|
||||
db.migrate.latest().then(() => {
|
||||
const server = http.createServer(app);
|
||||
|
||||
setupWebSocketServer(server);
|
||||
setupWebSocketServer(server, BASE_NAME);
|
||||
server.listen(PORT, LISTEN_IP, async () => {
|
||||
console.log(`[JELLYSTAT] Server listening on http://127.0.0.1:${PORT}`);
|
||||
ActivityMonitor.ActivityMonitor(1000);
|
||||
|
||||
@@ -1,27 +1,30 @@
|
||||
// ws.js
|
||||
const socketIO = require('socket.io');
|
||||
const socketIO = require("socket.io");
|
||||
|
||||
let io; // Store the socket.io server instance
|
||||
|
||||
const setupWebSocketServer = (server) => {
|
||||
io = socketIO(server);
|
||||
const setupWebSocketServer = (server, namespacePath) => {
|
||||
io = socketIO(server, { path: namespacePath + "/socket.io" }); // Create the socket.io server
|
||||
|
||||
io.on('connection', (socket) => {
|
||||
// console.log('Client connected');
|
||||
|
||||
socket.on('message', (message) => {
|
||||
// console.log(`Received: ${message}`);
|
||||
io.on("connection", (socket) => {
|
||||
console.log("Client connected to namespace:", namespacePath);
|
||||
|
||||
socket.on("message", (message) => {
|
||||
console.log(`Received: ${message}`);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const sendToAllClients = (message) => {
|
||||
io.emit('message', message);
|
||||
if (io) {
|
||||
io.emit("message", message);
|
||||
}
|
||||
};
|
||||
|
||||
const sendUpdate = (tag, message) => {
|
||||
if (io) {
|
||||
io.emit(tag, message);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = { setupWebSocketServer, sendToAllClients, sendUpdate };
|
||||
|
||||
10
index.html
10
index.html
@@ -2,23 +2,23 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<link rel="icon" href="favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta name="description" content="Jellyfin stats for the masses" />
|
||||
<link rel="apple-touch-icon" href="/icon-b-192.png" />
|
||||
<script src="/env.js"></script>
|
||||
<link rel="apple-touch-icon" href="icon-b-192.png" />
|
||||
<script src="env.js"></script>
|
||||
<!--
|
||||
manifest.json provides metadata used when your web app is installed on a
|
||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||
-->
|
||||
<link rel="manifest" href="/manifest.json" />
|
||||
<link rel="manifest" href="manifest.json" />
|
||||
<!--
|
||||
Notice the use of %PUBLIC_URL% in the tags above.
|
||||
It will be replaced with the URL of the `public` folder during the build.
|
||||
Only files inside the `public` folder can be referenced from the HTML.
|
||||
|
||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||
Unlike "favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import "./App.css";
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { Routes, Route } from "react-router-dom";
|
||||
import axios from "axios";
|
||||
import axios from "./lib/axios_instance";
|
||||
|
||||
import socket from "./socket";
|
||||
import { ToastContainer, toast } from "react-toastify";
|
||||
|
||||
@@ -13,20 +13,7 @@ import LanguageDetector from "i18next-browser-languagedetector";
|
||||
import { initReactI18next } from "react-i18next";
|
||||
|
||||
import Loading from "./pages/components/general/loading.jsx";
|
||||
import routes from "./routes.jsx";
|
||||
|
||||
const baseUrl = window.env.JS_BASE_URL ?? import.meta.env.JS_BASE_URL ?? "/";
|
||||
let validBaseUrls = [...new Set([baseUrl, ...routes.map((route) => "/" + route.path.split("/")[1])])];
|
||||
if (baseUrl != "/") {
|
||||
validBaseUrls = validBaseUrls.filter((url) => url != "/");
|
||||
}
|
||||
const locationBase = "/" + window.location.pathname.split("/")[1];
|
||||
console.log("Base URL: ", baseUrl);
|
||||
console.log("Valid Base URLs: ", validBaseUrls);
|
||||
|
||||
if (!validBaseUrls.includes(locationBase)) {
|
||||
window.location.assign(baseUrl);
|
||||
}
|
||||
import baseUrl from "./lib/baseurl.jsx";
|
||||
|
||||
i18n
|
||||
.use(Backend)
|
||||
@@ -35,6 +22,9 @@ i18n
|
||||
.init({
|
||||
fallbackLng: "en-UK",
|
||||
debug: false,
|
||||
backend: {
|
||||
loadPath: `${baseUrl}/locales/{{lng}}/{{ns}}.json`,
|
||||
},
|
||||
detection: {
|
||||
order: ["cookie", "localStorage", "sessionStorage", "navigator", "htmlTag", "querystring", "path", "subdomain"],
|
||||
cache: ["cookie"],
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import Axios from "axios";
|
||||
import baseUrl from "./baseurl";
|
||||
|
||||
const axios = Axios.create();
|
||||
const axios = Axios.create({ baseURL: baseUrl });
|
||||
|
||||
export default axios;
|
||||
|
||||
11
src/lib/baseurl.jsx
Normal file
11
src/lib/baseurl.jsx
Normal file
@@ -0,0 +1,11 @@
|
||||
const ensureSlashes = (url) => {
|
||||
if (!url.startsWith("/")) {
|
||||
url = "/" + url;
|
||||
}
|
||||
if (url.endsWith("/")) {
|
||||
url = url.slice(0, -1);
|
||||
}
|
||||
return url;
|
||||
};
|
||||
const baseUrl = window.env?.JS_BASE_URL ? ensureSlashes(window.env?.JS_BASE_URL) : "";
|
||||
export default baseUrl;
|
||||
@@ -1,4 +1,4 @@
|
||||
import axios from "axios";
|
||||
import axios from "../lib/axios_instance";
|
||||
|
||||
class Config {
|
||||
async fetchConfig() {
|
||||
|
||||
@@ -1,20 +1,17 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import axios from "axios";
|
||||
import Row from 'react-bootstrap/Row';
|
||||
import Col from 'react-bootstrap/Col';
|
||||
import axios from "../lib/axios_instance";
|
||||
import Row from "react-bootstrap/Row";
|
||||
import Col from "react-bootstrap/Col";
|
||||
import Loading from "./components/general/loading";
|
||||
|
||||
|
||||
import "./css/about.css";
|
||||
import { Card } from "react-bootstrap";
|
||||
import { Trans } from "react-i18next";
|
||||
|
||||
export default function SettingsAbout() {
|
||||
|
||||
const token = localStorage.getItem('token');
|
||||
const token = localStorage.getItem("token");
|
||||
const [data, setData] = useState();
|
||||
useEffect(() => {
|
||||
|
||||
const fetchVersion = () => {
|
||||
if (token) {
|
||||
const url = `/api/CheckForUpdates`;
|
||||
@@ -35,8 +32,7 @@ export default function SettingsAbout() {
|
||||
}
|
||||
};
|
||||
|
||||
if(!data)
|
||||
{
|
||||
if (!data) {
|
||||
fetchVersion();
|
||||
}
|
||||
|
||||
@@ -44,47 +40,43 @@ export default function SettingsAbout() {
|
||||
return () => clearInterval(intervalId);
|
||||
}, [data, token]);
|
||||
|
||||
|
||||
if(!data)
|
||||
{
|
||||
if (!data) {
|
||||
return <Loading />;
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<div className="tasks">
|
||||
<h1 className="py-3"><Trans i18nKey={"ABOUT_PAGE.ABOUT_JELLYSTAT"}/></h1>
|
||||
<h1 className="py-3">
|
||||
<Trans i18nKey={"ABOUT_PAGE.ABOUT_JELLYSTAT"} />
|
||||
</h1>
|
||||
<Card className="about p-0">
|
||||
<Card.Body>
|
||||
<Row>
|
||||
<Col className="px-0">
|
||||
<Trans i18nKey={"ABOUT_PAGE.VERSION"} />:
|
||||
</Col>
|
||||
<Col>
|
||||
{data.current_version}
|
||||
</Col>
|
||||
<Col>{data.current_version}</Col>
|
||||
</Row>
|
||||
<Row style={{color:(data.update_available ? "#00A4DC": "White")}}>
|
||||
<Row style={{ color: data.update_available ? "#00A4DC" : "White" }}>
|
||||
<Col className="px-0">
|
||||
<Trans i18nKey={"ABOUT_PAGE.UPDATE_AVAILABLE"} />:
|
||||
</Col>
|
||||
<Col>
|
||||
{data.message}
|
||||
</Col>
|
||||
<Col>{data.message}</Col>
|
||||
</Row>
|
||||
<Row style={{height:'20px'}}></Row>
|
||||
<Row style={{ height: "20px" }}></Row>
|
||||
<Row>
|
||||
<Col className="px-0">
|
||||
<Trans i18nKey={"ABOUT_PAGE.GITHUB"} />:
|
||||
</Col>
|
||||
<Col>
|
||||
<a href="https://github.com/CyferShepard/Jellystat" target="_blank" rel="noreferrer" > https://github.com/CyferShepard/Jellystat</a>
|
||||
<a href="https://github.com/CyferShepard/Jellystat" target="_blank" rel="noreferrer">
|
||||
{" "}
|
||||
https://github.com/CyferShepard/Jellystat
|
||||
</a>
|
||||
</Col>
|
||||
</Row>
|
||||
</Card.Body>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useState, useEffect } from "react";
|
||||
|
||||
import axios from "axios";
|
||||
import axios from "../lib/axios_instance";
|
||||
|
||||
import "./css/activity.css";
|
||||
import Config from "../lib/config";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {useState} from "react";
|
||||
import axios from "axios";
|
||||
import axios from "../../../lib/axios_instance";
|
||||
import "../../css/library/library-card.css";
|
||||
|
||||
import { Form ,Card,Row,Col } from 'react-bootstrap';
|
||||
@@ -9,6 +9,7 @@ import FilmLineIcon from "remixicon-react/FilmLineIcon";
|
||||
import FileMusicLineIcon from "remixicon-react/FileMusicLineIcon";
|
||||
import CheckboxMultipleBlankLineIcon from "remixicon-react/CheckboxMultipleBlankLineIcon";
|
||||
import { Trans } from "react-i18next";
|
||||
import baseUrl from "../../../lib/baseurl";
|
||||
|
||||
function SelectionCard(props) {
|
||||
const [imageLoaded, setImageLoaded] = useState(true);
|
||||
@@ -54,7 +55,7 @@ function SelectionCard(props) {
|
||||
<Card.Img
|
||||
variant="top"
|
||||
className="library-card-banner default_library_image"
|
||||
src={"/proxy/Items/Images/Primary?id=" + props.data.Id + "&fillWidth=800&quality=50"}
|
||||
src={baseUrl+"/proxy/Items/Images/Primary?id=" + props.data.Id + "&fillWidth=800&quality=50"}
|
||||
onError={() =>setImageLoaded(false)}
|
||||
/>
|
||||
:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* eslint-disable react/prop-types */
|
||||
import React, { useEffect, useMemo } from "react";
|
||||
import axios from "axios";
|
||||
import axios from "../../../lib/axios_instance";
|
||||
import { enUS } from "@mui/material/locale";
|
||||
|
||||
import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";
|
||||
@@ -155,8 +155,7 @@ export default function ActivityTable(props) {
|
||||
row = row.original;
|
||||
if (
|
||||
isRemoteSession(row.RemoteEndPoint) &&
|
||||
(window.env.JS_GEOLITE_ACCOUNT_ID ?? import.meta.env.JS_GEOLITE_ACCOUNT_ID) &&
|
||||
(window.env.JS_GEOLITE_LICENSE_KEY ?? import.meta.env.JS_GEOLITE_LICENSE_KEY)
|
||||
(window.env.JS_GEOLITE_ACCOUNT_ID ?? import.meta.env.JS_GEOLITE_ACCOUNT_ID)
|
||||
) {
|
||||
return (
|
||||
<Link className="text-decoration-none" onClick={() => showIPDataModal(row.RemoteEndPoint)}>
|
||||
|
||||
@@ -6,6 +6,7 @@ import ArchiveDrawerFillIcon from "remixicon-react/ArchiveDrawerFillIcon";
|
||||
import "../../css/lastplayed.css";
|
||||
import { Trans } from "react-i18next";
|
||||
import i18next from "i18next";
|
||||
import baseUrl from "../../../lib/baseurl";
|
||||
|
||||
function formatTime(time) {
|
||||
const units = {
|
||||
@@ -42,7 +43,7 @@ function LastWatchedCard(props) {
|
||||
) : null}
|
||||
{!props.data.archived ? (
|
||||
<img
|
||||
src={`${"/proxy/Items/Images/Primary?id=" + props.data.Id + "&fillHeight=320&fillWidth=213&quality=50"}`}
|
||||
src={`${baseUrl+"/proxy/Items/Images/Primary?id=" + props.data.Id + "&fillHeight=320&fillWidth=213&quality=50"}`}
|
||||
alt=""
|
||||
onLoad={() => setLoaded(true)}
|
||||
style={loaded ? { display: "block" } : { display: "none" }}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import axios from "axios";
|
||||
import axios from "../../../lib/axios_instance";
|
||||
import Row from 'react-bootstrap/Row';
|
||||
import Col from 'react-bootstrap/Col';
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import TableCell from "@mui/material/TableCell";
|
||||
import TableContainer from "@mui/material/TableContainer";
|
||||
import TableHead from "@mui/material/TableHead";
|
||||
import TableRow from "@mui/material/TableRow";
|
||||
import axios from "axios";
|
||||
import axios from "../../lib/axios_instance";
|
||||
import { Trans } from "react-i18next";
|
||||
|
||||
export default function IpInfoModal(props) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* eslint-disable react/prop-types */
|
||||
import { useState, useEffect } from "react";
|
||||
import axios from "axios";
|
||||
import axios from "../../lib/axios_instance";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { Link } from "react-router-dom";
|
||||
import { Blurhash } from "react-blurhash";
|
||||
@@ -26,6 +26,7 @@ import TvLineIcon from "remixicon-react/TvLineIcon";
|
||||
import FilmLineIcon from "remixicon-react/FilmLineIcon";
|
||||
import FileMusicLineIcon from "remixicon-react/FileMusicLineIcon";
|
||||
import CheckboxMultipleBlankLineIcon from "remixicon-react/CheckboxMultipleBlankLineIcon";
|
||||
import baseUrl from "../../lib/baseurl";
|
||||
|
||||
function ItemInfo() {
|
||||
const { Id } = useParams();
|
||||
@@ -207,7 +208,7 @@ function ItemInfo() {
|
||||
<img
|
||||
className="item-image"
|
||||
src={
|
||||
"/proxy/Items/Images/Primary?id=" +
|
||||
baseUrl+"/proxy/Items/Images/Primary?id=" +
|
||||
(["Episode", "Season"].includes(data.Type) ? data.SeriesId : data.Id) +
|
||||
"&fillWidth=200&quality=90"
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import axios from "axios";
|
||||
import axios from "../../../lib/axios_instance";
|
||||
import "../../css/globalstats.css";
|
||||
|
||||
import WatchTimeStats from "./globalstats/watchtimestats";
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import axios from "axios";
|
||||
import axios from "../../../lib/axios_instance";
|
||||
import ActivityTable from "../activity/activity-table";
|
||||
import { Trans } from "react-i18next";
|
||||
import { Button, FormControl, FormSelect } from "react-bootstrap";
|
||||
import { FormControl, FormSelect } from "react-bootstrap";
|
||||
import i18next from "i18next";
|
||||
|
||||
function ItemActivity(props) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useState } from "react";
|
||||
import axios from "axios";
|
||||
import axios from "../../../lib/axios_instance";
|
||||
import "../../css/error.css";
|
||||
import { Button } from "react-bootstrap";
|
||||
import Loading from "../general/loading";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
import axios from "axios";
|
||||
import axios from "../../../lib/axios_instance";
|
||||
import i18next from "i18next";
|
||||
import { useState } from "react";
|
||||
import { Container, Row,Col, Modal } from "react-bootstrap";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import axios from "axios";
|
||||
import axios from "../../../lib/axios_instance";
|
||||
|
||||
import MoreItemCards from "./more-items/more-items-card";
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import TvLineIcon from "remixicon-react/TvLineIcon";
|
||||
import FilmLineIcon from "remixicon-react/FilmLineIcon";
|
||||
import FileMusicLineIcon from "remixicon-react/FileMusicLineIcon";
|
||||
import CheckboxMultipleBlankLineIcon from "remixicon-react/CheckboxMultipleBlankLineIcon";
|
||||
import baseUrl from "../../../../lib/baseurl";
|
||||
|
||||
function MoreItemCards(props) {
|
||||
const { Id } = useParams();
|
||||
@@ -88,7 +89,7 @@ function MoreItemCards(props) {
|
||||
</div>
|
||||
) : (
|
||||
<img
|
||||
src={`${"/proxy/Items/Images/Primary?id=" + Id + "&fillHeight=320&fillWidth=213&quality=50"}`}
|
||||
src={`${baseUrl+"/proxy/Items/Images/Primary?id=" + Id + "&fillHeight=320&fillWidth=213&quality=50"}`}
|
||||
alt=""
|
||||
onLoad={() => setLoaded(true)}
|
||||
style={loaded ? { display: "block" } : { display: "none" }}
|
||||
@@ -96,7 +97,7 @@ function MoreItemCards(props) {
|
||||
)
|
||||
) : (
|
||||
<img
|
||||
src={`${
|
||||
src={`${baseUrl+
|
||||
"/proxy/Items/Images/Primary?id=" +
|
||||
(props.data.Type === "Episode" ? props.data.EpisodeId : props.data.Id) +
|
||||
(props.data.Type === "Audio"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useParams } from "react-router-dom";
|
||||
import { useState, useEffect } from "react";
|
||||
import axios from "axios";
|
||||
import axios from "../../lib/axios_instance";
|
||||
import TvLineIcon from "remixicon-react/TvLineIcon";
|
||||
import FilmLineIcon from "remixicon-react/FilmLineIcon";
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import TvLineIcon from "remixicon-react/TvLineIcon";
|
||||
import FilmLineIcon from "remixicon-react/FilmLineIcon";
|
||||
import FileMusicLineIcon from "remixicon-react/FileMusicLineIcon";
|
||||
import CheckboxMultipleBlankLineIcon from "remixicon-react/CheckboxMultipleBlankLineIcon";
|
||||
import baseUrl from "../../../../lib/baseurl";
|
||||
|
||||
function RecentlyAddedCard(props) {
|
||||
const [loaded, setLoaded] = useState(false);
|
||||
@@ -73,7 +74,7 @@ function RecentlyAddedCard(props) {
|
||||
) : null}
|
||||
<img
|
||||
src={`${
|
||||
"/proxy/Items/Images/Primary?id=" +
|
||||
baseUrl+"/proxy/Items/Images/Primary?id=" +
|
||||
(props.data.Type === "Episode" ? props.data.SeriesId : props.data.Id) +
|
||||
"&fillHeight=320&fillWidth=213&quality=50"
|
||||
}`}
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import axios from "axios";
|
||||
|
||||
// import ItemCardInfo from "./LastWatched/last-watched-card";
|
||||
|
||||
import axios from "../../../lib/axios_instance";
|
||||
import LastWatchedCard from "../general/last-watched-card";
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import axios from "axios";
|
||||
import axios from "../../../lib/axios_instance";
|
||||
|
||||
import ActivityTable from "../activity/activity-table";
|
||||
import { Trans } from "react-i18next";
|
||||
|
||||
@@ -12,6 +12,7 @@ import FileMusicLineIcon from "remixicon-react/FileMusicLineIcon";
|
||||
import CheckboxMultipleBlankLineIcon from "remixicon-react/CheckboxMultipleBlankLineIcon";
|
||||
import { Trans } from "react-i18next";
|
||||
import i18next from "i18next";
|
||||
import baseUrl from "../../../lib/baseurl";
|
||||
|
||||
function LibraryCard(props) {
|
||||
const [imageLoaded, setImageLoaded] = useState(true);
|
||||
@@ -146,7 +147,7 @@ function LibraryCard(props) {
|
||||
<Card.Img
|
||||
variant="top"
|
||||
className="library-card-banner library-card-banner-hover"
|
||||
src={"/proxy/Items/Images/Primary?id=" + props.data.Id + "&fillWidth=800&quality=50"}
|
||||
src={baseUrl+"/proxy/Items/Images/Primary?id=" + props.data.Id + "&fillWidth=800&quality=50"}
|
||||
onError={() =>setImageLoaded(false)}
|
||||
/>
|
||||
:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import axios from "axios";
|
||||
import axios from "../../../lib/axios_instance";
|
||||
import { FormControl, FormSelect, Button } from "react-bootstrap";
|
||||
import SortAscIcon from "remixicon-react/SortAscIcon";
|
||||
import SortDescIcon from "remixicon-react/SortDescIcon";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import axios from "axios";
|
||||
import axios from "../../../lib/axios_instance";
|
||||
import i18next from "i18next";
|
||||
import { useState } from "react";
|
||||
import { Container, Row, Col, Modal } from "react-bootstrap";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import axios from "axios";
|
||||
import axios from "../../../lib/axios_instance";
|
||||
import "../../css/globalstats.css";
|
||||
|
||||
import WatchTimeStats from "./globalstats/watchtimestats";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import axios from "axios";
|
||||
import axios from "../../../lib/axios_instance";
|
||||
|
||||
import RecentlyAddedCard from "./RecentlyAdded/recently-added-card";
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import "../css/libraryOverview.css";
|
||||
import { useState, useEffect } from "react";
|
||||
import axios from "axios";
|
||||
import axios from "../../lib/axios_instance";
|
||||
import Loading from "./general/loading";
|
||||
|
||||
import LibraryStatComponent from "./libraryStatCard/library-stat-component";
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import axios from "axios";
|
||||
// import Config from "../lib/config";
|
||||
|
||||
// import AccountCircleFillIcon from "remixicon-react/AccountCircleFillIcon";
|
||||
import { useState, useEffect } from "react";
|
||||
import axios from "../../lib/axios_instance";
|
||||
|
||||
import "../css/users/users.css";
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import { clientData } from "../../../lib/devices";
|
||||
import Tooltip from "@mui/material/Tooltip";
|
||||
import IpInfoModal from "../ip-info";
|
||||
import { Trans } from "react-i18next";
|
||||
import baseUrl from "../../../lib/baseurl";
|
||||
|
||||
function ticksToTimeString(ticks) {
|
||||
// Convert ticks to seconds
|
||||
@@ -112,7 +113,7 @@ function SessionCard(props) {
|
||||
: "stat-card-image rounded-0 rounded-start"
|
||||
}
|
||||
src={
|
||||
"/proxy/Items/Images/Primary?id=" +
|
||||
baseUrl+"/proxy/Items/Images/Primary?id=" +
|
||||
(props.data.session.NowPlayingItem.SeriesId
|
||||
? props.data.session.NowPlayingItem.SeriesId
|
||||
: props.data.session.NowPlayingItem.Id) +
|
||||
@@ -164,8 +165,7 @@ function SessionCard(props) {
|
||||
<Row>
|
||||
<Col className="col-auto ellipse">
|
||||
{isRemoteSession(props.data.session.RemoteEndPoint) &&
|
||||
(window.env.JS_GEOLITE_ACCOUNT_ID ?? import.meta.env.JS_GEOLITE_ACCOUNT_ID) &&
|
||||
(window.env.JS_GEOLITE_LICENSE_KEY ?? import.meta.env.JS_GEOLITE_LICENSE_KEY) ? (
|
||||
(window.env.JS_GEOLITE_ACCOUNT_ID ?? import.meta.env.JS_GEOLITE_ACCOUNT_ID) ? (
|
||||
<Link
|
||||
className="text-decoration-none text-white"
|
||||
onClick={() => showIPDataModal(props.data.session.RemoteEndPoint)}
|
||||
@@ -185,7 +185,7 @@ function SessionCard(props) {
|
||||
<img
|
||||
className="card-device-image"
|
||||
src={
|
||||
"/proxy/web/assets/img/devices/?devicename=" +
|
||||
baseUrl+"/proxy/web/assets/img/devices/?devicename=" +
|
||||
(props.data.session.Client.toLowerCase() === "jellyfin mobile (ios)" &&
|
||||
props.data.session.DeviceName.toLowerCase() === "iphone"
|
||||
? "apple"
|
||||
@@ -246,7 +246,7 @@ function SessionCard(props) {
|
||||
{props.data.session.UserPrimaryImageTag !== undefined ? (
|
||||
<img
|
||||
className="session-card-user-image"
|
||||
src={"/proxy/Users/Images/Primary?id=" + props.data.session.UserId + "&quality=50"}
|
||||
src={baseUrl+"/proxy/Users/Images/Primary?id=" + props.data.session.UserId + "&quality=50"}
|
||||
alt=""
|
||||
/>
|
||||
) : (
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useState } from "react";
|
||||
import axios from "axios";
|
||||
import axios from "../../../lib/axios_instance";
|
||||
import Button from "react-bootstrap/Button";
|
||||
|
||||
import Table from "@mui/material/Table";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useState,useEffect } from "react";
|
||||
import axios from "axios";
|
||||
import axios from "../../../lib/axios_instance";
|
||||
import {Form, Row, Col,ButtonGroup, Button } from 'react-bootstrap';
|
||||
|
||||
import Table from '@mui/material/Table';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import axios from "axios";
|
||||
import axios from "../../../lib/axios_instance";
|
||||
|
||||
import "../../css/settings/backups.css";
|
||||
import { Trans } from "react-i18next";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import axios from "axios";
|
||||
import axios from "../../../lib/axios_instance";
|
||||
import { Form, DropdownButton, Dropdown, ButtonGroup, Button } from "react-bootstrap";
|
||||
|
||||
import Table from "@mui/material/Table";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useEffect } from "react";
|
||||
import axios from "axios";
|
||||
import axios from "../../../lib/axios_instance";
|
||||
import {ButtonGroup, Button } from 'react-bootstrap';
|
||||
|
||||
import Table from '@mui/material/Table';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import axios from "axios";
|
||||
import axios from "../../../lib/axios_instance";
|
||||
import Form from "react-bootstrap/Form";
|
||||
import Row from "react-bootstrap/Row";
|
||||
import Col from "react-bootstrap/Col";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import axios from "axios";
|
||||
import axios from "../../../lib/axios_instance";
|
||||
import Config from "../../../lib/config";
|
||||
import Loading from "../general/loading";
|
||||
import Form from "react-bootstrap/Form";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import axios from "axios";
|
||||
import axios from "../../../lib/axios_instance";
|
||||
import Config from "../../../lib/config";
|
||||
import ItemStatComponent from "./ItemStatComponent";
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import axios from "axios";
|
||||
import axios from "../../../lib/axios_instance";
|
||||
import ItemStatComponent from "./ItemStatComponent";
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import axios from "axios";
|
||||
import axios from "../../../lib/axios_instance";
|
||||
import Config from "../../../lib/config";
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useState, useEffect } from "react";
|
||||
|
||||
import axios from "axios";
|
||||
import axios from "../../../lib/axios_instance";
|
||||
import Config from "../../../lib/config";
|
||||
|
||||
import ItemStatComponent from "./ItemStatComponent";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import axios from "axios";
|
||||
import axios from "../../../lib/axios_instance";
|
||||
import Config from "../../../lib/config";
|
||||
import ItemStatComponent from "./ItemStatComponent";
|
||||
import { Trans } from "react-i18next";
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import axios from "axios";
|
||||
|
||||
import axios from "../../../lib/axios_instance";
|
||||
|
||||
import ItemStatComponent from "./ItemStatComponent";
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import axios from "axios";
|
||||
import axios from "../../../lib/axios_instance";
|
||||
|
||||
import Config from "../../../lib/config";
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import axios from "axios";
|
||||
import axios from "../../../lib/axios_instance";
|
||||
import Config from "../../../lib/config";
|
||||
import ItemStatComponent from "./ItemStatComponent";
|
||||
import { Trans } from "react-i18next";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import axios from "axios";
|
||||
import axios from "../../../lib/axios_instance";
|
||||
import Config from "../../../lib/config";
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import axios from "axios";
|
||||
import axios from "../../../lib/axios_instance";
|
||||
import Config from "../../../lib/config";
|
||||
|
||||
import ItemStatComponent from "./ItemStatComponent";
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import axios from "axios";
|
||||
|
||||
import axios from "../../../lib/axios_instance";
|
||||
import Chart from "./chart";
|
||||
|
||||
import "../../css/stats.css";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import axios from "axios";
|
||||
import axios from "../../../lib/axios_instance";
|
||||
import Chart from "./chart";
|
||||
|
||||
import "../../css/stats.css";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import axios from "axios";
|
||||
import axios from "../../../lib/axios_instance";
|
||||
import Chart from "./chart";
|
||||
import "../../css/stats.css";
|
||||
import { Trans } from "react-i18next";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import axios from "axios";
|
||||
import axios from "../../../lib/axios_instance";
|
||||
|
||||
import "../../css/stats.css";
|
||||
import { Trans } from "react-i18next";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useParams } from "react-router-dom";
|
||||
import { useState, useEffect } from "react";
|
||||
import axios from "axios";
|
||||
import axios from "../../lib/axios_instance";
|
||||
import AccountCircleFillIcon from "remixicon-react/AccountCircleFillIcon";
|
||||
import Config from "../../lib/config";
|
||||
import { Tabs, Tab, Button, ButtonGroup } from "react-bootstrap";
|
||||
@@ -10,6 +10,7 @@ import LastPlayed from "./user-info/lastplayed";
|
||||
import UserActivity from "./user-info/user-activity";
|
||||
import "../css/users/user-details.css";
|
||||
import { Trans } from "react-i18next";
|
||||
import baseUrl from "../../lib/baseurl";
|
||||
|
||||
function UserInfo() {
|
||||
const { UserId } = useParams();
|
||||
@@ -76,7 +77,7 @@ function UserInfo() {
|
||||
) : (
|
||||
<img
|
||||
className="user-image"
|
||||
src={"/proxy/Users/Images/Primary?id=" + UserId + "&quality=100"}
|
||||
src={baseUrl+"/proxy/Users/Images/Primary?id=" + UserId + "&quality=100"}
|
||||
onError={handleImageError}
|
||||
alt=""
|
||||
></img>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import axios from "axios";
|
||||
import { useState, useEffect } from "react";
|
||||
import axios from "../../../lib/axios_instance";
|
||||
import "../../css/globalstats.css";
|
||||
|
||||
import WatchTimeStats from "./globalstats/watchtimestats";
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import axios from "axios";
|
||||
|
||||
import axios from "../../../lib/axios_instance";
|
||||
import LastWatchedCard from "../general/last-watched-card";
|
||||
import ErrorBoundary from "../general/ErrorBoundary";
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import axios from "axios";
|
||||
import axios from "../../../lib/axios_instance";
|
||||
import ActivityTable from "../activity/activity-table";
|
||||
import { Trans } from "react-i18next";
|
||||
import { Button, FormControl, FormSelect, Modal } from "react-bootstrap";
|
||||
|
||||
@@ -14,6 +14,7 @@ import { clientData } from "../../lib/devices";
|
||||
import Tooltip from "@mui/material/Tooltip";
|
||||
import IpInfoModal from "../components/ip-info";
|
||||
import "./sessionCard.css";
|
||||
import baseUrl from "../../lib/baseurl";
|
||||
|
||||
function ticksToTimeString(ticks) {
|
||||
// Convert ticks to seconds
|
||||
@@ -117,7 +118,7 @@ function SessionCard(props) {
|
||||
: "stat-card-image rounded-0 rounded-start"
|
||||
}
|
||||
src={
|
||||
"/proxy/Items/Images/Primary?id=" +
|
||||
baseUrl+"/proxy/Items/Images/Primary?id=" +
|
||||
(props.data.session.NowPlayingItem.SeriesId
|
||||
? props.data.session.NowPlayingItem.SeriesId
|
||||
: props.data.session.NowPlayingItem.Id) +
|
||||
@@ -183,7 +184,7 @@ function SessionCard(props) {
|
||||
<img
|
||||
className="card-device-image"
|
||||
src={
|
||||
"/proxy/web/assets/img/devices/?devicename=" +
|
||||
baseUrl+"/proxy/web/assets/img/devices/?devicename=" +
|
||||
(props.data.session.Client.toLowerCase() === "jellyfin mobile (ios)" &&
|
||||
props.data.session.DeviceName.toLowerCase() === "iphone"
|
||||
? "apple"
|
||||
@@ -244,7 +245,7 @@ function SessionCard(props) {
|
||||
{props.data.session.UserPrimaryImageTag !== undefined ? (
|
||||
<img
|
||||
className="session-card-user-image"
|
||||
src={"/proxy/Users/Images/Primary?id=" + props.data.session.UserId + "&quality=50"}
|
||||
src={baseUrl+"/proxy/Users/Images/Primary?id=" + props.data.session.UserId + "&quality=50"}
|
||||
alt=""
|
||||
/>
|
||||
) : (
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import axios from "axios";
|
||||
import axios from "../lib/axios_instance";
|
||||
import Config from "../lib/config";
|
||||
|
||||
import "./css/library/libraries.css";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import axios from "axios";
|
||||
import axios from "../lib/axios_instance";
|
||||
import Config from "../lib/config";
|
||||
|
||||
import "./css/library/libraries.css";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import axios from "axios";
|
||||
import axios from "../lib/axios_instance";
|
||||
import Config from "../lib/config";
|
||||
import CryptoJS from "crypto-js";
|
||||
import "./css/setup.css";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import axios from "axios";
|
||||
import axios from "../lib/axios_instance";
|
||||
import Config from "../lib/config";
|
||||
import Form from "react-bootstrap/Form";
|
||||
import Button from "react-bootstrap/Button";
|
||||
@@ -12,7 +12,6 @@ import logo_dark from "./images/icon-b-512.png";
|
||||
import "./css/setup.css";
|
||||
import i18next from "i18next";
|
||||
import { Trans } from "react-i18next";
|
||||
const token = localStorage.getItem("token");
|
||||
|
||||
function Setup() {
|
||||
const [config, setConfig] = useState(null);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useState, useEffect } from "react";
|
||||
|
||||
import axios from "axios";
|
||||
import axios from "../lib/axios_instance";
|
||||
import Config from "../lib/config";
|
||||
import CryptoJS from "crypto-js";
|
||||
import "./css/setup.css";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import axios from "axios";
|
||||
import axios from "../lib/axios_instance";
|
||||
import Config from "../lib/config";
|
||||
import { Link } from "react-router-dom";
|
||||
import AccountCircleFillIcon from "remixicon-react/AccountCircleFillIcon";
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { io } from 'socket.io-client';
|
||||
import { io } from "socket.io-client";
|
||||
import baseUrl from "./lib/baseurl";
|
||||
|
||||
const socket = io();
|
||||
const socket = io({
|
||||
path: baseUrl + "/socket.io/",
|
||||
});
|
||||
|
||||
export default socket;
|
||||
@@ -4,6 +4,7 @@ import react from "@vitejs/plugin-react-swc";
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
envPrefix: "JS_",
|
||||
base: "",
|
||||
optimizeDeps: {
|
||||
include: ["react", "react-dom", "react-router-dom", "axios", "react-toastify"],
|
||||
esbuildOptions: {
|
||||
|
||||
Reference in New Issue
Block a user