From 44f2db7c25c5f1851bb75026e6f279421a2aaa7f Mon Sep 17 00:00:00 2001 From: Guusvanmeerveld Date: Fri, 25 Mar 2022 23:26:59 +0100 Subject: [PATCH] Added option to change invidious server #5 --- src/components/Video/index.tsx | 15 ++-- src/interfaces/api/instances.ts | 79 ++++++++++++++++++ src/interfaces/settings.ts | 1 + src/pages/settings.tsx | 141 +++++++++++++++++++++++++++++++- src/pages/trending.tsx | 5 +- src/pages/watch.tsx | 5 +- src/utils/hooks.ts | 3 +- 7 files changed, 238 insertions(+), 11 deletions(-) create mode 100644 src/interfaces/api/instances.ts diff --git a/src/components/Video/index.tsx b/src/components/Video/index.tsx index c55c1c7..8cff4dd 100644 --- a/src/components/Video/index.tsx +++ b/src/components/Video/index.tsx @@ -22,6 +22,8 @@ import { abbreviateNumber } from "@src/utils"; import { Video as VideoModel } from "@interfaces/video"; +import { useSettings } from "@utils/hooks"; + interface Channel { authorThumbnails: { url: string; width: number; height: number }[]; } @@ -34,7 +36,7 @@ const Video: FC = ({ views, published }) => { - const requestFields = ["authorThumbnails"]; + const [settings] = useSettings(); const { isLoading, error, data, refetch, isFetched } = useQuery< Channel, @@ -43,11 +45,14 @@ const Video: FC = ({ ["channelData", author.id], () => axios - .get(`https://invidious.privacy.gd/api/v1/channels/${author.id}`, { - params: { - fields: requestFields.join(",") + .get( + `https://${settings.invidiousServer}/api/v1/channels/${author.id}`, + { + params: { + fields: ["authorThumbnails"].join(",") + } } - }) + ) .then((res) => res.data), { enabled: false } ); diff --git a/src/interfaces/api/instances.ts b/src/interfaces/api/instances.ts new file mode 100644 index 0000000..7dc4b83 --- /dev/null +++ b/src/interfaces/api/instances.ts @@ -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" +} diff --git a/src/interfaces/settings.ts b/src/interfaces/settings.ts index 6ef85de..2842f84 100644 --- a/src/interfaces/settings.ts +++ b/src/interfaces/settings.ts @@ -2,6 +2,7 @@ interface Settings { theme?: "light" | "dark"; primaryColor: string; accentColor: string; + invidiousServer: string; } export default Settings; diff --git a/src/pages/settings.tsx b/src/pages/settings.tsx index 52e7078..ec77b02 100644 --- a/src/pages/settings.tsx +++ b/src/pages/settings.tsx @@ -1,12 +1,22 @@ +import axios from "axios"; + 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 Button from "@mui/material/Button"; import ButtonGroup from "@mui/material/ButtonGroup"; +import CircularProgress from "@mui/material/CircularProgress"; import Container from "@mui/material/Container"; 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 { blue, @@ -19,8 +29,17 @@ import { } from "@mui/material/colors/"; 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 { + ServerInstance, + ServerInstanceType, + Stats +} from "@interfaces/api/instances"; + import { useSettings } from "@utils/hooks"; import Layout from "@components/Layout"; @@ -28,12 +47,29 @@ import Layout from "@components/Layout"; const Settings: NextPage = () => { const [settings, setSettings] = useSettings(); - const theme = useTheme(); - const setSetting = (key: string, value?: string): void => { 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("invidiousInstance", () => + axios + .get(`https://${settings.invidiousServer}/api/v1/stats`) + .then((res) => res.data) + ); + const ColorSetting: FC<{ type: string }> = ({ type }) => { 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 ( + setModalState(false)} + aria-labelledby="modal-modal-title" + aria-describedby="modal-modal-description" + > + + + Stats for {settings.invidiousServer} + + + Version: {data.version}

+ Software name: {data.software.name}
+ Software version: {data.software.version}
+ Software branch: {data.software.branch}

+ Is accepting registrations: {data.openRegistrations + ? "Yes" + : "No"}{" "} +

+ Total users: {data.usage.users.total}
+ Active in the past half year: {data.usage.users.activeHalfyear} +
+ Active in the past month: {data.usage.users.activeMonth}
{" "} +
+ Stats updated at: {lastUpdated.toLocaleDateString()} -{" "} + {lastUpdated.toLocaleTimeString()} +
+
+
+ ); + }; + return ( @@ -114,6 +199,56 @@ const Settings: NextPage = () => { Miscellaneous + + + + Invidious Server + + + {invidiousServerResponse.data && + !invidiousServerResponse.isLoading && ( + <> + setModalState(true)} + sx={{ color: green[800], cursor: "pointer" }} + /> + + + )} + {invidiousServerResponse.error && ( + + )} + {invidiousServerResponse.isLoading && } + + + Server + + + ); diff --git a/src/pages/trending.tsx b/src/pages/trending.tsx index 6989e92..1e88da9 100644 --- a/src/pages/trending.tsx +++ b/src/pages/trending.tsx @@ -15,6 +15,7 @@ import { Error } from "@interfaces/api"; import TrendingModel from "@interfaces/api/trending"; import { trendingToVideo } from "@utils/conversions"; +import { useSettings } from "@utils/hooks"; import Layout from "@components/Layout"; import Loading from "@components/Loading"; @@ -23,12 +24,14 @@ import Grid from "@components/Video/Grid"; const Trending: NextPage = () => { const [selectedCategory, setCategory] = useState(); + const [settings] = useSettings(); + const { isLoading, error, data } = useQuery< TrendingModel[], AxiosError >("trendingData", () => axios - .get("https://invidious.privacy.gd/api/v1/trending", { + .get(`https://${settings.invidiousServer}/api/v1/trending`, { params: { fields: [ "title", diff --git a/src/pages/watch.tsx b/src/pages/watch.tsx index 9f29ddc..5fb957b 100644 --- a/src/pages/watch.tsx +++ b/src/pages/watch.tsx @@ -13,6 +13,7 @@ import { Error } from "@interfaces/api"; import Video from "@interfaces/api/video"; import { videoToVideo } from "@utils/conversions"; +import { useSettings } from "@utils/hooks"; import Layout from "@components/Layout"; import Loading from "@components/Loading"; @@ -22,6 +23,8 @@ const Watch: NextPage = () => { const videoId = router.query["v"]; + const [settings] = useSettings(); + const { isLoading, error, data, refetch } = useQuery< Video, AxiosError @@ -29,7 +32,7 @@ const Watch: NextPage = () => { ["videoData", videoId], () => axios - .get(`https://invidious.privacy.gd/api/v1/videos/${videoId}`, { + .get(`https://${settings.invidiousServer}/api/v1/videos/${videoId}`, { params: {} }) .then((res) => res.data), diff --git a/src/utils/hooks.ts b/src/utils/hooks.ts index b5dba8b..6bf9c70 100644 --- a/src/utils/hooks.ts +++ b/src/utils/hooks.ts @@ -8,7 +8,8 @@ import Settings from "@interfaces/settings"; const defaultSettings: Settings = { primaryColor: red[800], - accentColor: red[800] + accentColor: red[800], + invidiousServer: "vid.puffyan.us" }; export const useSettings = (): [