diff --git a/.vscode/launch.json b/.vscode/launch.json
index b8f7b47..7312014 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -9,7 +9,7 @@
"type": "chrome",
"request": "launch",
"name": "Launch Chrome against localhost",
- "url": "http://10.0.0.99:3000",
+ "url": "http://10.0.0.20:3000",
"webRoot": "${workspaceFolder}"
}
]
diff --git a/backend/api.js b/backend/api.js
index 364292a..a63d78f 100644
--- a/backend/api.js
+++ b/backend/api.js
@@ -10,17 +10,35 @@ router.get('/test', async (req, res) => {
});
router.get('/getconfig', async (req, res) => {
- const { rows } = await db.query('SELECT * FROM app_config');
+ const { rows } = await db.query('SELECT * FROM app_config where "ID"=1');
console.log(`ENDPOINT CALLED: /getconfig: `+rows);
+ // console.log(`ENDPOINT CALLED: /setconfig: `+rows.length);
res.send(rows);
+
});
router.post('/setconfig', async (req, res) => {
const { JF_HOST, JF_API_KEY } = req.body;
- console.log(req.body);
- const { rows } = await db.query('UPDATE app_config SET "JF_HOST"=$1, "JF_API_KEY"=$2', [JF_HOST, JF_API_KEY]);
- console.log(`ENDPOINT CALLED: /setconfig: `+rows);
- res.send(rows);
+ const { rows } = await db.query('UPDATE app_config SET "JF_HOST"=$1, "JF_API_KEY"=$2 where "ID"=1', [JF_HOST, JF_API_KEY]);
+ res.send(rows);
+ // const { existing } = await db.query('SELECT * FROM app_config where "ID"=1');
+ // console.log("Lenght: "+existing.rows[0].length );
+ // if(existing != undefined && existing.length != 0)
+ // {
+ // console.log("Update Config");
+ // const { rows } = await db.query('UPDATE app_config SET "JF_HOST"=$1, "JF_API_KEY"=$2 where "ID"=1', [JF_HOST, JF_API_KEY]);
+ // res.send(rows);
+ // }
+ // else{
+ // console.log("Insert Config");
+ // const { rows } = await db.query('INSERT into app_config VALUES ( $1, $2, null, null)', [JF_HOST, JF_API_KEY]);
+ // res.send(rows);
+ // }
+
+
+
+ console.log(`ENDPOINT CALLED: /setconfig: `);
+
});
module.exports = router;
diff --git a/src/App.js b/src/App.js
index c7cb29c..c164d6e 100644
--- a/src/App.js
+++ b/src/App.js
@@ -1,10 +1,17 @@
// import logo from './logo.svg';
import './App.css';
+import React, { useState, useEffect } from 'react';
import {
Routes,
Route,
} from "react-router-dom";
+import Config from './lib/config';
+
+import Loading from './pages/components/loading';
+
+import Setup from './pages/setup';
+
import SideNav from './pages/components/sidenav';
import Home from './pages/home';
@@ -13,9 +20,46 @@ import Activity from './pages/activity';
import UserActivity from './pages/useractivity';
import Libraries from './pages/libraries';
+import RecentlyPlayed from './pages/components/recentlyplayed';
+
import UserData from './pages/userdata';
function App() {
+
+ const [config, setConfig] = useState(null);
+ const [loading, setLoading] = useState(true);
+
+ useEffect(() => {
+
+
+ const fetchConfig = async () => {
+ try {
+ const newConfig = await Config();
+ setConfig(newConfig);
+ setLoading(false);
+ } catch (error) {
+ if (error.code === 'ERR_NETWORK') {
+ console.log(error);
+ }
+ }
+ };
+
+ if (!config) {
+ fetchConfig();
+ }
+
+}, [config]);
+
+if (loading) {
+ return ;
+}
+
+if (!config || config.apiKey ==null) {
+ return ;
+}
+
+
+
return (
@@ -28,6 +72,7 @@ function App() {
} />
} />
} />
+ } />
diff --git a/src/classes/jellyfin-api.js b/src/classes/jellyfin-api.js
new file mode 100644
index 0000000..28e6528
--- /dev/null
+++ b/src/classes/jellyfin-api.js
@@ -0,0 +1,126 @@
+import { Component } from 'react';
+import axios from 'axios';
+import Config from '../lib/config';
+
+class API extends Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ data: [],
+ };
+ }
+
+
+ async getSessions() {
+ try {
+ const config = await Config();
+ const url = `${config.hostUrl}/Sessions`;
+ const response = await axios.get(url, {
+ headers: {
+ 'X-MediaBrowser-Token': config.apiKey,
+ },
+ });
+ return response.data;
+ } catch (error) {
+ console.log(error);
+ return [];
+ }
+ }
+
+ async getActivityData(limit) {
+ if(limit===undefined || limit<1)
+ {
+ return[];
+ }
+ try {
+ const config = await Config();
+ const url = `${config.hostUrl}/System/ActivityLog/Entries?limit=${limit}`;
+ const response = await axios.get(url, {
+ headers: {
+ 'X-MediaBrowser-Token': config.apiKey,
+ },
+ });
+ return response.data;
+ } catch (error) {
+ console.log(error);
+ return [];
+ }
+ }
+
+ async getAdminUser() {
+ try {
+ const config = await Config();
+ const url = `${config.hostUrl}/Users`;
+ const response = await axios.get(url, {
+ headers: {
+ 'X-MediaBrowser-Token': config.apiKey,
+ },
+ });
+ const adminUser = response.data.filter(user => user.Policy.IsAdministrator === true);
+ return adminUser || null;
+ } catch (error) {
+ console.log(error);
+ return [];
+ }
+ }
+
+ async getLibraries() {
+
+ try {
+ const config = await Config();
+ const admins=await this.getAdminUser()
+ const userid=admins[0].Id;
+ const url = `${config.hostUrl}/users/${userid}/Items`;
+ const response = await axios.get(url, {
+ headers: {
+ 'X-MediaBrowser-Token': config.apiKey,
+ },
+ });
+ const mediafolders = response.data.Items.filter(type => ['tvshows','movies'].includes(type.CollectionType));
+ return mediafolders || null;
+ } catch (error) {
+ console.log(error);
+ return [];
+ }
+ }
+
+ async getItem(itemID) {
+
+ try {
+ const config = await Config();
+ const admins=await this.getAdminUser()
+ const userid=admins[0].Id;
+ const url = `${config.hostUrl}/users/${userid}/Items?ParentID=${itemID}`;
+ const response = await axios.get(url, {
+ headers: {
+ 'X-MediaBrowser-Token': config.apiKey,
+ },
+ });
+ return response.data.Items;
+ } catch (error) {
+ console.log(error);
+ return [];
+ }
+ }
+
+ async getRecentlyPlayed(userid,limit) {
+
+ try {
+ const config = await Config();
+ const url = `${config.hostUrl}/users/${userid}/Items/Resume?limit=${limit}`;
+ const response = await axios.get(url, {
+ headers: {
+ 'X-MediaBrowser-Token': config.apiKey,
+ },
+ });
+ return response.data.Items;
+ } catch (error) {
+ console.log(error);
+ return [];
+ }
+ }
+
+
+}
+
+export default API;
diff --git a/src/classes/sync.js b/src/classes/sync.js
index ffe49be..a83a33c 100644
--- a/src/classes/sync.js
+++ b/src/classes/sync.js
@@ -2,7 +2,7 @@ import { Component } from 'react';
import axios from 'axios';
import Config from '../lib/config';
-class GetSeries extends Component {
+class sync extends Component {
constructor(props) {
super(props);
this.state = {
@@ -11,10 +11,50 @@ class GetSeries extends Component {
}
- async getData() {
+ async getAdminUser() {
try {
const config = await Config();
- const url = `${config.hostUrl}/users/5f63950a2339462196eb8cead70cae7e/Items?ParentID=9d7ad6afe9afa2dab1a2f6e00ad28fa6`;
+ const url = `${config.hostUrl}/Users`;
+ const response = await axios.get(url, {
+ headers: {
+ 'X-MediaBrowser-Token': config.apiKey,
+ },
+ });
+ const adminUser = response.data.filter(user => user.Policy.IsAdministrator === true);
+ return adminUser || null;
+ } catch (error) {
+ console.log(error);
+ return [];
+ }
+ }
+
+ async getLibraries() {
+
+ try {
+ const config = await Config();
+ const admins=await this.getAdminUser()
+ const userid=admins[0].Id;
+ const url = `${config.hostUrl}/users/${userid}/Items`;
+ const response = await axios.get(url, {
+ headers: {
+ 'X-MediaBrowser-Token': config.apiKey,
+ },
+ });
+ const mediafolders = response.data.Items.filter(type => ['tvshows','movies'].includes(type.CollectionType));
+ return mediafolders || null;
+ } catch (error) {
+ console.log(error);
+ return [];
+ }
+ }
+
+ async getItem(itemID) {
+
+ try {
+ const config = await Config();
+ const admins=await this.getAdminUser()
+ const userid=admins[0].Id;
+ const url = `${config.hostUrl}/users/${userid}/Items?ParentID=${itemID}`;
const response = await axios.get(url, {
headers: {
'X-MediaBrowser-Token': config.apiKey,
@@ -26,6 +66,8 @@ class GetSeries extends Component {
return [];
}
}
+
+
}
-export default GetSeries;
+export default sync;
diff --git a/src/lib/config.js b/src/lib/config.js
index 538639a..52ec053 100644
--- a/src/lib/config.js
+++ b/src/lib/config.js
@@ -2,7 +2,7 @@ import axios from 'axios';
async function Config() {
try {
- const response = await axios.get('http://10.0.0.99:3003/api/getconfig');
+ const response = await axios.get('http://10.0.0.20:3003/api/getconfig');
const { JF_HOST, JF_API_KEY, APP_USER, APP_PASSWORD } = response.data[0];
return { hostUrl: JF_HOST, apiKey: JF_API_KEY, username: APP_USER, password: APP_PASSWORD };
} catch (error) {
diff --git a/src/lib/navdata.js b/src/lib/navdata.js
index b60cf41..a34330c 100644
--- a/src/lib/navdata.js
+++ b/src/lib/navdata.js
@@ -5,6 +5,9 @@ import FileListFillIcon from 'remixicon-react/FileListFillIcon';
import BarChartFillIcon from 'remixicon-react/BarChartFillIcon';
import SettingsFillIcon from 'remixicon-react/SettingsFillIcon';
import GalleryFillIcon from 'remixicon-react/GalleryFillIcon';
+import UserFillIcon from 'remixicon-react/UserFillIcon';
+
+import ReactjsFillIcon from 'remixicon-react/ReactjsFillIcon';
export const navData = [
{
@@ -24,23 +27,30 @@ export const navData = [
icon: ,
text: "Libraries",
link: "libraries"
- },
+ } ,
{
id: 3,
+ icon: ,
+ text: "Recently Played",
+ link: "recent"
+ },
+ {
+ id: 4,
icon: ,
text: "User Activity",
link: "usersactivity"
},
{
- id: 4,
- icon: ,
- text: "User Data",
+ id: 5,
+ icon: ,
+ text: "Library Data",
link: "userdata"
},
{
- id: 5,
+ id: 6,
icon: ,
text: "Settings",
link: "settings"
}
+
]
\ No newline at end of file
diff --git a/src/models/libraryItem.js b/src/models/libraryItem.js
new file mode 100644
index 0000000..63c9962
--- /dev/null
+++ b/src/models/libraryItem.js
@@ -0,0 +1,7 @@
+class libraryItem {
+ constructor(id, name, email) {
+ this.id = id;
+ this.name = name;
+ this.email = email;
+ }
+ }
\ No newline at end of file
diff --git a/src/pages/activity.js b/src/pages/activity.js
index 179d545..f1ac6f5 100644
--- a/src/pages/activity.js
+++ b/src/pages/activity.js
@@ -1,6 +1,5 @@
import React, { useState, useEffect } from 'react';
-import axios from 'axios';
-import Config from '../lib/config';
+import API from '../classes/jellyfin-api';
import '../App.css'
@@ -10,55 +9,30 @@ import Loading from './components/loading';
function Activity() {
const [data, setData] = useState([]);
- const [config, setConfig] = useState(null);
useEffect(() => {
-
-
- const fetchConfig = async () => {
- try {
- const newConfig = await Config();
- setConfig(newConfig);
- } catch (error) {
- if (error.code === 'ERR_NETWORK') {
- console.log(error);
- }
- }
- };
+ let _api= new API()
const fetchData = () => {
- if (config) {
- const url = `${config.hostUrl}/System/ActivityLog/Entries?limit=30`;
- const apiKey = config.apiKey;
-
- axios.get(url, {
- headers: {
- 'X-MediaBrowser-Token': apiKey,
- },
- })
- .then(newData => {
- if (data && data.length > 0) {
- const newDataOnly = newData.data.Items.filter(item => {
- return !data.some(existingItem => existingItem.Id === item.Id);
- });
- setData([...newDataOnly, ...data.slice(0, data.length - newDataOnly.length)]);
- } else {
- setData(newData.data.Items);
- }
- })
- .catch(error => {
- console.log(error);
+ _api.getActivityData(30).then((ActivityData) => {
+ if (data && data.length > 0)
+ {
+ const newDataOnly = ActivityData.Items.filter(item => {
+ return !data.some(existingItem => existingItem.Id === item.Id);
});
- }
+ setData([...newDataOnly, ...data.slice(0, data.length - newDataOnly.length)]);
+ } else
+ {
+ setData(ActivityData.Items);
+ }
+ });
};
- if (!config) {
- fetchConfig();
- }
- const intervalId = setInterval(fetchData, 2000);
+ const intervalId = setInterval(fetchData, 1000);
return () => clearInterval(intervalId);
- }, [data,config]);
+ }, [data]);
+
@@ -70,7 +44,7 @@ function Activity() {
hour: 'numeric',
minute: 'numeric',
second: 'numeric',
- hour12: true
+ hour12: false
};
if (!data || data.length === 0) {
diff --git a/src/pages/components/recent-card.js b/src/pages/components/recent-card.js
new file mode 100644
index 0000000..447ec21
--- /dev/null
+++ b/src/pages/components/recent-card.js
@@ -0,0 +1,69 @@
+import React from 'react';
+
+function getLastPlayedTimeString(datetime) {
+ const now = new Date();
+ const lastPlayed = new Date(datetime);
+
+ const timeDifference = Math.abs(now.getTime() - lastPlayed.getTime());
+ const yearsDifference = Math.floor(timeDifference / (1000 * 3600 * 24 * 365));
+ const weeksDifference = Math.floor(
+ (timeDifference % (1000 * 3600 * 24 * 365)) / (1000 * 3600 * 24 * 7)
+ );
+ const daysDifference = Math.floor(
+ (timeDifference % (1000 * 3600 * 24 * 7)) / (1000 * 3600 * 24)
+ );
+ const hoursDifference = Math.floor(
+ (timeDifference % (1000 * 3600 * 24)) / (1000 * 3600)
+ );
+ const minutesDifference = Math.floor(
+ (timeDifference % (1000 * 3600)) / (1000 * 60)
+ );
+
+ const timeUnits = [
+ { label: "year", pluralLabel: "years", value: yearsDifference },
+ { label: "week", pluralLabel: "weeks", value: weeksDifference },
+ { label: "day", pluralLabel: "days", value: daysDifference },
+ { label: "hour", pluralLabel: "hours", value: hoursDifference },
+ { label: "minute", pluralLabel: "minutes", value: minutesDifference },
+ ];
+
+ const timeString = timeUnits
+ .filter((unit) => unit.value > 0)
+ .map((unit, index, array) => {
+ const label = unit.value === 1 ? unit.label : unit.pluralLabel;
+ if (index === array.length - 1 && array.length > 1) {
+ // Special case for last time unit
+ return `and ${unit.value} ${label}`;
+ } else {
+ return `${unit.value} ${label}`;
+ }
+ })
+ .join(" ");
+
+ return `Watched ${timeString} ago`;
+ }
+
+
+
+function RecentCard(props) {
+
+
+ return (
+
+
+
+
+
+
+
+
{props.data.recent.Name}
+
{getLastPlayedTimeString(props.data.recent.UserData.LastPlayedDate)}
+
+
+
+ );
+}
+
+export default RecentCard;
\ No newline at end of file
diff --git a/src/pages/components/recentlyplayed.js b/src/pages/components/recentlyplayed.js
new file mode 100644
index 0000000..f851e65
--- /dev/null
+++ b/src/pages/components/recentlyplayed.js
@@ -0,0 +1,68 @@
+import React, { useState, useEffect } from 'react';
+// import axios from 'axios';
+import Config from '../../lib/config';
+import API from '../../classes/jellyfin-api';
+
+
+import "../css/recent.css"
+// import "../../App.css"
+
+
+import RecentCard from './recent-card';
+
+
+import Loading from './loading';
+
+
+
+
+
+function RecentlyPlayed() {
+ const [data, setData] = useState([]);
+ const [base_url, setURL] = useState('');
+ // const [errorHandler, seterrorHandler] = useState({ error_count: 0, error_message: '' })
+
+
+ useEffect(() => {
+ const _api = new API();
+ const fetchData = () => {
+ _api.getRecentlyPlayed('5f63950a2339462196eb8cead70cae7e',10).then((recentData) => {
+ setData(recentData);
+ });
+ };
+
+
+ Config().then(config => {
+ setURL(config.hostUrl);
+ }).catch(error => {
+ console.log(error);
+ }
+ );
+
+ const intervalId = setInterval(fetchData, 1000);
+ return () => clearInterval(intervalId);
+ }, []);
+
+
+
+ if (!data || data.length === 0) {
+ return ;
+ }
+
+
+ return (
+
+
+ {data &&
+ data.sort((a, b) => b.UserData.LastPlayedDate.localeCompare(a.UserData.LastPlayedDate)).map(recent => (
+
+
+
+ ))}
+
+
+ );
+}
+
+export default RecentlyPlayed;
+
diff --git a/src/pages/components/sessions.js b/src/pages/components/sessions.js
index 30af2e0..4c740be 100644
--- a/src/pages/components/sessions.js
+++ b/src/pages/components/sessions.js
@@ -1,6 +1,7 @@
import React, { useState, useEffect } from 'react';
-import axios from 'axios';
+// import axios from 'axios';
import Config from '../../lib/config';
+import API from '../../classes/jellyfin-api';
import "../css/sessions.css"
// import "../../App.css"
@@ -22,45 +23,23 @@ function Sessions() {
useEffect(() => {
+ const _api = new API();
+ const fetchData = () => {
+ _api.getSessions().then((SessionData) => {
+ setData(SessionData);
+ });
+ };
+
+
Config().then(config => {
- console.log('hit api');
- // let error_counter=0;
setURL(config.hostUrl);
- const url = `${config.hostUrl}/sessions`;
- const fetchData = () => {
- axios.get(url, {
- headers: {
- 'X-MediaBrowser-Token': config.apiKey,
- },
- })
- .then(response => setData(response.data))
- .catch(error => {
- console.log(error);
- // error_counter++;
- // console.log(error_counter);
- // if(error_counter>9)
- // {
- // console.log('Terminating');
- // return () => clearInterval(intervalId);
- // }
- }
- );
- };
-
- // fetchData();
- const intervalId = setInterval(fetchData, 1000);
- return () => clearInterval(intervalId);
}).catch(error => {
- // if (error.code === 'ERR_NETWORK') {
- // console.log(errorHandler.error_count);
- // let _error_count = 1;
- // let _error_message = '';
- // seterrorHandler({ error_count: _error_count, error_message: _error_message });
- // }
-
console.log(error);
}
);
+
+ const intervalId = setInterval(fetchData, 1000);
+ return () => clearInterval(intervalId);
}, []);
@@ -76,8 +55,8 @@ function Sessions() {
{data &&
data.sort((a, b) => a.Id.padStart(12, '0').localeCompare(b.Id.padStart(12, '0'))).map(session => (
-
-
+
+
))}
diff --git a/src/pages/css/recent.css b/src/pages/css/recent.css
new file mode 100644
index 0000000..af16c7e
--- /dev/null
+++ b/src/pages/css/recent.css
@@ -0,0 +1,75 @@
+.recent {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(185px, 200px));
+ grid-auto-rows: 340px;/* max-width+offset so 215 + 20*/
+ background-color: rgba(0,0,0,0.5);
+ padding: 20px;
+ border-radius: 4px;
+ margin-right: 20px;
+ color: white;
+
+
+}
+
+
+
+.recent-card
+{
+ display: flex;
+ flex-direction: column;
+
+ /* background-color: grey; */
+ box-shadow: 0 0 20px rgba(255, 255, 255, 0.05);
+
+ height: 320px;
+ width: 185px;
+ border-radius: 4px;
+
+
+ /* Add a third row that takes up remaining space */
+}
+
+
+.card-banner {
+ width: 100%;
+ height: 70%;
+ background-size: cover;
+ background-repeat: no-repeat;
+ background-position: center top;
+ border-radius: 4px 4px 0px 0px;
+
+}
+
+.recent-card-details {
+
+
+ width: 100%;
+ height: 30%;
+ position: relative;
+ /* margin: 4px; */
+
+ /* background-color: #f71b1b; */
+}
+
+.recent-card-item-name {
+ width: 185px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ position: absolute;
+ margin: 0;
+
+
+/*
+
+ position: absolute; */
+}
+
+.recent-card-last-played{
+ width: 185px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ position: absolute;
+ bottom: 0;
+ margin: 0;
+}
+
diff --git a/src/pages/css/sessions.css b/src/pages/css/sessions.css
index b23b30e..da342cc 100644
--- a/src/pages/css/sessions.css
+++ b/src/pages/css/sessions.css
@@ -15,6 +15,7 @@
color: white;
background-color: grey;
+ box-shadow: 0 0 20px rgba(255, 255, 255, 0.05);
max-height: 215px;
max-width: 500px;
diff --git a/src/pages/css/setup.css b/src/pages/css/setup.css
new file mode 100644
index 0000000..f2255d5
--- /dev/null
+++ b/src/pages/css/setup.css
@@ -0,0 +1,117 @@
+@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@500&display=swap');
+/* *{
+ margin: 0;
+ padding: 0;
+ font-family: 'poppins',sans-serif;
+} */
+
+
+section{
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ min-height: 100vh;
+ width: 100%;
+
+ /* background: url('background6.jpg')no-repeat; */
+ background-position: center;
+ background-size: cover;
+}
+.form-box{
+ position: relative;
+ width: 400px;
+ height: 450px;
+ background: transparent;
+ border: 2px solid rgba(255,255,255,0.5);
+ border-radius: 20px;
+ backdrop-filter: blur(15px);
+ display: flex;
+ justify-content: center;
+ align-items: center;
+
+}
+h2{
+ font-size: 2em;
+ color: #fff;
+ text-align: center;
+}
+.inputbox{
+ position: relative;
+ margin: 30px 0;
+ width: 310px;
+ border-bottom: 2px solid #fff;
+}
+.inputbox label{
+ position: absolute;
+ top: 50%;
+ left: 5px;
+ transform: translateY(-50%);
+ color: #fff;
+ font-size: 1em;
+ pointer-events: none;
+ transition: .2s;
+}
+input:focus ~ label,
+input:valid ~ label{
+top: -15px;
+}
+.inputbox input {
+ width: 100%;
+ height: 50px;
+ background: transparent;
+ border: none;
+ outline: none;
+ font-size: 1em;
+ color: #fff;
+}
+.inputbox ion-icon{
+ position: absolute;
+ right: 8px;
+ color: #fff;
+ font-size: 1.2em;
+ top: 20px;
+}
+.forget{
+ margin: -15px 0 15px ;
+ font-size: .9em;
+ color: #fff;
+ display: flex;
+ justify-content: space-between;
+}
+
+.forget label input{
+ margin-right: 3px;
+
+}
+.forget label a{
+ color: #fff;
+ text-decoration: none;
+}
+.forget label a:hover{
+ text-decoration: underline;
+}
+.setup-button{
+ width: 100%;
+ height: 40px;
+ border-radius: 40px;
+ background: #fff;
+ border: none;
+ outline: none;
+ cursor: pointer;
+ font-size: 1em;
+ font-weight: 600;
+}
+.register{
+ font-size: .9em;
+ color: #fff;
+ text-align: center;
+ margin: 25px 0 10px;
+}
+.register p a{
+ text-decoration: none;
+ color: #fff;
+ font-weight: 600;
+}
+.register p a:hover{
+ text-decoration: underline;
+}
\ No newline at end of file
diff --git a/src/pages/setup.js b/src/pages/setup.js
new file mode 100644
index 0000000..3bd3df6
--- /dev/null
+++ b/src/pages/setup.js
@@ -0,0 +1,160 @@
+import React, { useState, useEffect } from 'react';
+import axios from 'axios';
+import Config from '../lib/config';
+
+import './css/setup.css'
+
+// import Loading from './components/loading';
+
+function Setup() {
+ const [config, setConfig] = useState(null);
+ const [formValues, setFormValues] = useState({});
+ const [processing, setProcessing] = useState(false);
+ const [submitButtonText, setsubmitButtonText] = useState('Save');
+
+ function handleFormChange(event) {
+
+ setFormValues({ ...formValues, [event.target.name]: event.target.value });
+ }
+
+ async function validateSettings(_url, _apikey) {
+ // Send a GET request to /system/configuration to test copnnection
+ let isValid = false;
+ let errorMessage = '';
+ await axios.get(_url + '/system/configuration', {
+ headers: {
+ 'X-MediaBrowser-Token': _apikey,
+ },
+ })
+ .then(response => {
+ // console.log('HTTP status code:', response.status); // logs the HTTP status code
+ //console.log('Response data:', response.data); // logs the response data
+
+ if (response.status === 200) {
+ isValid = true;
+ }
+
+
+ })
+ .catch(error => {
+ // console.log(error.code);
+ if (error.code === 'ERR_NETWORK') {
+ isValid = false;
+ errorMessage = `Unable to connect to Jellyfin Server`;
+ } else
+ if (error.response.status === 401) {
+ isValid = false;
+ errorMessage = `Error ${error.response.status} Not Authorized`;
+ } else
+ if (error.response.status === 404) {
+ isValid = false;
+ errorMessage = `Error ${error.response.status}: The requested URL was not found.`;
+ } else {
+ isValid = false;
+ errorMessage = `Error : ${error.response.status}`;
+ }
+
+ });
+
+ return ({ isValid: isValid, errorMessage: errorMessage });
+ }
+
+ async function handleFormSubmit(event) {
+ setProcessing(true);
+ event.preventDefault();
+
+
+ // if(formValues.JF_HOST=='' || formValues.JF_API_KEY=='')
+ // {
+ // setsubmitButtonText('Plea');
+ // return;
+ // }
+
+
+ let validation = await validateSettings(formValues.JF_HOST, formValues.JF_API_KEY);
+
+
+ if (!validation.isValid) {
+
+ setsubmitButtonText(validation.errorMessage);
+ setProcessing(false);
+ return;
+
+ }
+
+
+ // Send a POST request to /api/setconfig/ with the updated configuration
+ axios.post('http://localhost:3003/api/setconfig/', formValues, {
+ headers: {
+ 'Content-Type': 'application/json'
+ }
+ })
+ .then(response => {
+ setsubmitButtonText('Settings Saved');
+ setProcessing(false);
+ setTimeout(() => {
+ window.location.href = '/';
+ }, 1000);
+
+ return;
+ })
+ .catch(error => {
+ setsubmitButtonText('Error Saving Settings');
+ setProcessing(false);
+
+ });
+
+
+
+ }
+
+
+ useEffect(() => {
+
+
+ const fetchConfig = async () => {
+ try {
+ const newConfig = await Config();
+ setConfig(newConfig);
+ } catch (error) {
+ if (error.code === 'ERR_NETWORK') {
+ console.log(error);
+ }
+ }
+ };
+
+ if (!config) {
+ fetchConfig();
+ }
+
+ }, [config]);
+
+
+
+ return (
+
+ );
+}
+
+export default Setup;
diff --git a/src/pages/userdata.js b/src/pages/userdata.js
index 4864a59..64c4109 100644
--- a/src/pages/userdata.js
+++ b/src/pages/userdata.js
@@ -1,16 +1,22 @@
import React, { useState, useEffect } from 'react';
-import GetSeries from '../classes/sync';
+// import sync from '../classes/sync';
import './css/libraries.css';
import Loading from './components/loading';
+import API from '../classes/jellyfin-api';
+
function UserData() {
const [data, setData] = useState([]);
useEffect(() => {
- const seriesInstance = new GetSeries(); // create an instance of the GetSeries class
- seriesInstance.getData().then((seriesData) => {
+ const seriesInstance = new API(); // create an instance of the GetSeries class
+ seriesInstance.getLibraries().then((seriesData) => {
setData(seriesData);
});
+
+
+
+
}, []); // run this effect only once, when the component mounts
if (!data || data.length === 0) {