Fix to address #229 Base urls. Alot of hacky code here. hope it doesnt break

This commit is contained in:
CyferShepard
2024-09-08 00:31:13 +02:00
parent ed917f5be8
commit 29d6464397
70 changed files with 279 additions and 207 deletions

10
.vscode/launch.json vendored
View File

@@ -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",

View File

@@ -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;

View File

@@ -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);

View File

@@ -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 };

View File

@@ -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`.
-->

View File

@@ -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";

View File

@@ -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"],

View File

@@ -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
View 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;

View File

@@ -1,4 +1,4 @@
import axios from "axios";
import axios from "../lib/axios_instance";
class Config {
async fetchConfig() {

View File

@@ -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>
);
}

View File

@@ -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";

View File

@@ -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)}
/>
:

View File

@@ -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)}>

View File

@@ -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" }}

View File

@@ -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';

View File

@@ -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) {

View File

@@ -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"
}

View File

@@ -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";

View File

@@ -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) {

View File

@@ -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";

View File

@@ -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";

View File

@@ -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";

View File

@@ -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"

View File

@@ -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";

View File

@@ -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"
}`}

View File

@@ -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";

View File

@@ -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";

View File

@@ -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)}
/>
:

View File

@@ -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";

View File

@@ -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";

View File

@@ -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";

View File

@@ -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";

View File

@@ -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";

View File

@@ -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";

View File

@@ -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=""
/>
) : (

View File

@@ -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";

View File

@@ -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';

View File

@@ -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";

View File

@@ -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";

View File

@@ -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';

View File

@@ -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";

View File

@@ -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";

View File

@@ -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";

View File

@@ -1,5 +1,5 @@
import { useState, useEffect } from "react";
import axios from "axios";
import axios from "../../../lib/axios_instance";
import ItemStatComponent from "./ItemStatComponent";

View File

@@ -1,5 +1,5 @@
import { useState, useEffect } from "react";
import axios from "axios";
import axios from "../../../lib/axios_instance";
import Config from "../../../lib/config";

View File

@@ -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";

View File

@@ -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";

View File

@@ -1,6 +1,5 @@
import { useState, useEffect } from "react";
import axios from "axios";
import axios from "../../../lib/axios_instance";
import ItemStatComponent from "./ItemStatComponent";

View File

@@ -1,5 +1,5 @@
import { useState, useEffect } from "react";
import axios from "axios";
import axios from "../../../lib/axios_instance";
import Config from "../../../lib/config";

View File

@@ -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";

View File

@@ -1,5 +1,5 @@
import { useState, useEffect } from "react";
import axios from "axios";
import axios from "../../../lib/axios_instance";
import Config from "../../../lib/config";

View File

@@ -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";

View File

@@ -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";

View File

@@ -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";

View File

@@ -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";

View File

@@ -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";

View File

@@ -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>

View File

@@ -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";

View File

@@ -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";

View File

@@ -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";

View File

@@ -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=""
/>
) : (

View File

@@ -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";

View File

@@ -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";

View File

@@ -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";

View File

@@ -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);

View File

@@ -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";

View File

@@ -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";

View File

@@ -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;

View File

@@ -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: {