Added option to change invidious server #5

main
Guus van Meerveld 3 years ago
parent 0edd0afb2a
commit 44f2db7c25
Signed by: Guusvanmeerveld
GPG Key ID: 2BA7D7912771966E

@ -22,6 +22,8 @@ import { abbreviateNumber } from "@src/utils";
import { Video as VideoModel } from "@interfaces/video"; import { Video as VideoModel } from "@interfaces/video";
import { useSettings } from "@utils/hooks";
interface Channel { interface Channel {
authorThumbnails: { url: string; width: number; height: number }[]; authorThumbnails: { url: string; width: number; height: number }[];
} }
@ -34,7 +36,7 @@ const Video: FC<VideoModel> = ({
views, views,
published published
}) => { }) => {
const requestFields = ["authorThumbnails"]; const [settings] = useSettings();
const { isLoading, error, data, refetch, isFetched } = useQuery< const { isLoading, error, data, refetch, isFetched } = useQuery<
Channel, Channel,
@ -43,11 +45,14 @@ const Video: FC<VideoModel> = ({
["channelData", author.id], ["channelData", author.id],
() => () =>
axios axios
.get(`https://invidious.privacy.gd/api/v1/channels/${author.id}`, { .get(
params: { `https://${settings.invidiousServer}/api/v1/channels/${author.id}`,
fields: requestFields.join(",") {
params: {
fields: ["authorThumbnails"].join(",")
}
} }
}) )
.then((res) => res.data), .then((res) => res.data),
{ enabled: false } { enabled: false }
); );

@ -0,0 +1,79 @@
export interface ServerInstance {
flag?: string;
region?: string;
stats?: Stats;
cors?: boolean;
api?: boolean;
type: ServerInstanceType;
uri: string;
monitor?: Monitor;
}
interface Monitor {
monitorId: number;
createdAt: number;
statusClass: StatusClass;
name: string;
url: null;
type: MonitorType;
dailyRatios: Ratio[];
"90dRatio": Ratio;
"30dRatio": Ratio;
}
interface Ratio {
ratio: string;
label: StatusClass;
}
export enum StatusClass {
Black = "black",
Success = "success",
Warning = "warning"
}
export enum MonitorType {
HTTPS = "HTTP(s)"
}
export interface Stats {
version: string;
software: Software;
openRegistrations: boolean;
usage: Usage;
metadata: Metadata;
}
interface Metadata {
updatedAt: number;
lastChannelRefreshedAt: number;
}
interface Software {
name: Name;
version: string;
branch: Branch;
}
export enum Branch {
Master = "master"
}
export enum Name {
Invidious = "invidious"
}
interface Usage {
users: Users;
}
interface Users {
total: number;
activeHalfyear: number;
activeMonth: number;
}
export enum ServerInstanceType {
HTTPS = "https",
Onion = "onion"
}

@ -2,6 +2,7 @@ interface Settings {
theme?: "light" | "dark"; theme?: "light" | "dark";
primaryColor: string; primaryColor: string;
accentColor: string; accentColor: string;
invidiousServer: string;
} }
export default Settings; export default Settings;

@ -1,12 +1,22 @@
import axios from "axios";
import { NextPage } from "next"; import { NextPage } from "next";
import { FC } from "react"; import { FC, useState } from "react";
import { useQuery } from "react-query";
import Box from "@mui/material/Box"; import Box from "@mui/material/Box";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
import ButtonGroup from "@mui/material/ButtonGroup"; import ButtonGroup from "@mui/material/ButtonGroup";
import CircularProgress from "@mui/material/CircularProgress";
import Container from "@mui/material/Container"; import Container from "@mui/material/Container";
import Divider from "@mui/material/Divider"; import Divider from "@mui/material/Divider";
import FormControl from "@mui/material/FormControl";
import InputLabel from "@mui/material/InputLabel";
import MenuItem from "@mui/material/MenuItem";
import Modal from "@mui/material/Modal";
import Select from "@mui/material/Select";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import { import {
blue, blue,
@ -19,8 +29,17 @@ import {
} from "@mui/material/colors/"; } from "@mui/material/colors/";
import { useTheme } from "@mui/material/styles/"; import { useTheme } from "@mui/material/styles/";
import Done from "@mui/icons-material/Done";
import Error from "@mui/icons-material/Error";
import { toCamelCase } from "@src/utils"; import { toCamelCase } from "@src/utils";
import {
ServerInstance,
ServerInstanceType,
Stats
} from "@interfaces/api/instances";
import { useSettings } from "@utils/hooks"; import { useSettings } from "@utils/hooks";
import Layout from "@components/Layout"; import Layout from "@components/Layout";
@ -28,12 +47,29 @@ import Layout from "@components/Layout";
const Settings: NextPage = () => { const Settings: NextPage = () => {
const [settings, setSettings] = useSettings(); const [settings, setSettings] = useSettings();
const theme = useTheme();
const setSetting = (key: string, value?: string): void => { const setSetting = (key: string, value?: string): void => {
setSettings({ ...settings, [key]: value }); setSettings({ ...settings, [key]: value });
}; };
const [modalIsOpen, setModalState] = useState(false);
const theme = useTheme();
const instances = useQuery<[string, ServerInstance][]>(
"invidiousInstances",
() =>
axios
.get("https://api.invidious.io/instances.json")
.then((res) => res.data),
{ retry: false }
);
const invidiousServerResponse = useQuery<Stats>("invidiousInstance", () =>
axios
.get(`https://${settings.invidiousServer}/api/v1/stats`)
.then((res) => res.data)
);
const ColorSetting: FC<{ type: string }> = ({ type }) => { const ColorSetting: FC<{ type: string }> = ({ type }) => {
const camelCase = toCamelCase(type) as "primaryColor" | "accentColor"; const camelCase = toCamelCase(type) as "primaryColor" | "accentColor";
@ -77,6 +113,55 @@ const Settings: NextPage = () => {
); );
}; };
const InfoModal: FC = () => {
const data = invidiousServerResponse.data!;
const lastUpdated = new Date(data.metadata.updatedAt * 1000);
return (
<Modal
open={modalIsOpen}
onClose={() => setModalState(false)}
aria-labelledby="modal-modal-title"
aria-describedby="modal-modal-description"
>
<Box
sx={{
p: 4,
position: "absolute",
left: "50%",
top: "50%",
transform: "translate(-50%, -50%)",
backgroundColor: theme.palette.background.paper,
borderRadius: 1,
outline: "none"
}}
>
<Typography id="modal-modal-title" variant="h4">
Stats for {settings.invidiousServer}
</Typography>
<Typography id="modal-modal-description" sx={{ mt: 2 }}>
Version: {data.version} <br /> <br />
Software name: {data.software.name} <br />
Software version: {data.software.version} <br />
Software branch: {data.software.branch} <br /> <br />
Is accepting registrations: {data.openRegistrations
? "Yes"
: "No"}{" "}
<br /> <br />
Total users: {data.usage.users.total} <br />
Active in the past half year: {data.usage.users.activeHalfyear}
<br />
Active in the past month: {data.usage.users.activeMonth} <br />{" "}
<br />
Stats updated at: {lastUpdated.toLocaleDateString()} -{" "}
{lastUpdated.toLocaleTimeString()}
</Typography>
</Box>
</Modal>
);
};
return ( return (
<Layout> <Layout>
<Container> <Container>
@ -114,6 +199,56 @@ const Settings: NextPage = () => {
<Divider sx={{ my: 4 }} /> <Divider sx={{ my: 4 }} />
<Typography variant="h4">Miscellaneous</Typography> <Typography variant="h4">Miscellaneous</Typography>
<Box sx={{ my: 3, display: "flex", alignItems: "center" }}>
<Typography variant="h5" sx={{ flexGrow: 1 }}>
Invidious Server
</Typography>
<Box sx={{ mr: 2 }}>
{invidiousServerResponse.data &&
!invidiousServerResponse.isLoading && (
<>
<Done
onClick={() => setModalState(true)}
sx={{ color: green[800], cursor: "pointer" }}
/>
<InfoModal />
</>
)}
{invidiousServerResponse.error && (
<Error sx={{ color: red[800] }} />
)}
{invidiousServerResponse.isLoading && <CircularProgress />}
</Box>
<FormControl sx={{ minWidth: 200 }}>
<InputLabel id="server-select-label">Server</InputLabel>
<Select
labelId="server-select-label"
id="server-select"
value={settings.invidiousServer}
label="Server"
onChange={(e) => {
const server = e.target.value;
setSetting("invidiousServer", server);
}}
MenuProps={{ sx: { maxHeight: 300 } }}
>
{instances.data &&
instances.data
.filter(
([, server]) =>
server.type != ServerInstanceType.Onion &&
server.api == true
)
.map(([uri, server]) => (
<MenuItem key={uri} value={uri}>
{server.flag} {uri}
</MenuItem>
))}
</Select>
</FormControl>
</Box>
</Container> </Container>
</Layout> </Layout>
); );

@ -15,6 +15,7 @@ import { Error } from "@interfaces/api";
import TrendingModel from "@interfaces/api/trending"; import TrendingModel from "@interfaces/api/trending";
import { trendingToVideo } from "@utils/conversions"; import { trendingToVideo } from "@utils/conversions";
import { useSettings } from "@utils/hooks";
import Layout from "@components/Layout"; import Layout from "@components/Layout";
import Loading from "@components/Loading"; import Loading from "@components/Loading";
@ -23,12 +24,14 @@ import Grid from "@components/Video/Grid";
const Trending: NextPage = () => { const Trending: NextPage = () => {
const [selectedCategory, setCategory] = useState<string | undefined>(); const [selectedCategory, setCategory] = useState<string | undefined>();
const [settings] = useSettings();
const { isLoading, error, data } = useQuery< const { isLoading, error, data } = useQuery<
TrendingModel[], TrendingModel[],
AxiosError<Error> AxiosError<Error>
>("trendingData", () => >("trendingData", () =>
axios axios
.get("https://invidious.privacy.gd/api/v1/trending", { .get(`https://${settings.invidiousServer}/api/v1/trending`, {
params: { params: {
fields: [ fields: [
"title", "title",

@ -13,6 +13,7 @@ import { Error } from "@interfaces/api";
import Video from "@interfaces/api/video"; import Video from "@interfaces/api/video";
import { videoToVideo } from "@utils/conversions"; import { videoToVideo } from "@utils/conversions";
import { useSettings } from "@utils/hooks";
import Layout from "@components/Layout"; import Layout from "@components/Layout";
import Loading from "@components/Loading"; import Loading from "@components/Loading";
@ -22,6 +23,8 @@ const Watch: NextPage = () => {
const videoId = router.query["v"]; const videoId = router.query["v"];
const [settings] = useSettings();
const { isLoading, error, data, refetch } = useQuery< const { isLoading, error, data, refetch } = useQuery<
Video, Video,
AxiosError<Error> AxiosError<Error>
@ -29,7 +32,7 @@ const Watch: NextPage = () => {
["videoData", videoId], ["videoData", videoId],
() => () =>
axios axios
.get(`https://invidious.privacy.gd/api/v1/videos/${videoId}`, { .get(`https://${settings.invidiousServer}/api/v1/videos/${videoId}`, {
params: {} params: {}
}) })
.then((res) => res.data), .then((res) => res.data),

@ -8,7 +8,8 @@ import Settings from "@interfaces/settings";
const defaultSettings: Settings = { const defaultSettings: Settings = {
primaryColor: red[800], primaryColor: red[800],
accentColor: red[800] accentColor: red[800],
invidiousServer: "vid.puffyan.us"
}; };
export const useSettings = (): [ export const useSettings = (): [

Loading…
Cancel
Save