diff --git a/src/components/Admin/Channels/Delete.tsx b/src/components/Admin/Channels/Delete.tsx index 90171ad..21d66e6 100644 --- a/src/components/Admin/Channels/Delete.tsx +++ b/src/components/Admin/Channels/Delete.tsx @@ -24,8 +24,8 @@ const AdminChannelsDelete = ({ handleClose, channel }) => { queryClient.invalidateQueries(["admin-channels"]); setLoading(false); showNotification({ - title: "Kanal gelöscht", - message: "Der Kanal wurde erfolgreich gelöscht!", + title: "Channel Deleted", + message: "Channel has been deleted successfully", }); handleClose(); }) @@ -38,17 +38,17 @@ const AdminChannelsDelete = ({ handleClose, channel }) => { return (
- Bist du dir sicher, dass du den folgenden Kanal löschen möchtest? + Are you sure you want to delete the following channel?
- Kanal ID: {channel.id} + Channel ID: {channel.id}
- Kanal Name: {channel.name} + Channel Name: {channel.name}
- Diese Aktion löscht keine Dateien, sondern lediglich nur den Kanal! + This action does not delete any files.
@@ -57,7 +57,7 @@ const AdminChannelsDelete = ({ handleClose, channel }) => { color="red" loading={loading} > - Löschen + Delete
diff --git a/src/components/Admin/Channels/Drawer.tsx b/src/components/Admin/Channels/Drawer.tsx index 329580b..707ff82 100644 --- a/src/components/Admin/Channels/Drawer.tsx +++ b/src/components/Admin/Channels/Drawer.tsx @@ -54,8 +54,8 @@ const AdminChannelDrawer = ({ handleClose, channel, mode }) => { queryClient.invalidateQueries(["admin-channels"]); setLoading(false); showNotification({ - title: "Kanal aktualisiert", - message: "Der Kanal wurde erfolgreich aktualisiert!", + title: "Channel Updated", + message: "Channel has been updated successfully", }); handleClose(); }) @@ -84,8 +84,8 @@ const AdminChannelDrawer = ({ handleClose, channel, mode }) => { queryClient.invalidateQueries(["admin-channels"]); setLoading(false); showNotification({ - title: "Kanal erstellt", - message: "Der Kanal wurde erfolgreich erstellt!", + title: "Channel Created", + message: "Channel has been created successfully", }); handleClose(); }) @@ -111,8 +111,8 @@ const AdminChannelDrawer = ({ handleClose, channel, mode }) => { .then(() => { setUpdateImageLoading(false); showNotification({ - title: "Kanal aktualisiert", - message: "Kanalbild wurde erfolgreich aktualisiert", + title: "Channel Updated", + message: "Channel image has been updated successfully", }); handleClose(); }) @@ -163,8 +163,9 @@ const AdminChannelDrawer = ({ handleClose, channel, mode }) => { diff --git a/src/components/Admin/Channels/Table.tsx b/src/components/Admin/Channels/Table.tsx index f26feb6..b2191b4 100644 --- a/src/components/Admin/Channels/Table.tsx +++ b/src/components/Admin/Channels/Table.tsx @@ -100,7 +100,7 @@ const AdminChannelsTable = () => { setDeletedOpened(false); }; - if (error) return
Fehler beim Laden
; + if (error) return
failed to load
; if (isLoading) return ; return ( diff --git a/src/components/Admin/Vods/Delete.tsx b/src/components/Admin/Vods/Delete.tsx index 747f421..ed63af4 100644 --- a/src/components/Admin/Vods/Delete.tsx +++ b/src/components/Admin/Vods/Delete.tsx @@ -4,6 +4,7 @@ import { useMutation, useQueryClient } from "@tanstack/react-query"; import getConfig from "next/config"; import { useState } from "react"; import { useApi } from "../../../hooks/useApi"; +import { escapeURL } from "../../../util/util"; const AdminVodDelete = ({ handleClose, vod }) => { const { publicRuntimeConfig } = getConfig(); @@ -54,7 +55,9 @@ const AdminVodDelete = ({ handleClose, vod }) => {
diff --git a/src/components/Admin/Watched/Drawer.tsx b/src/components/Admin/Watched/Drawer.tsx index 5d8ff80..871c534 100644 --- a/src/components/Admin/Watched/Drawer.tsx +++ b/src/components/Admin/Watched/Drawer.tsx @@ -44,9 +44,9 @@ const AdminWatchedDrawer = ({ handleClose, watched, mode }) => { const qualityOptions = [ { label: "Best", value: "best" }, { label: "720p60", value: "720p60" }, - { label: "480p30", value: "480p30" }, - { label: "360p30", value: "360p30" }, - { label: "160p30", value: "160p30" }, + { label: "480p", value: "480p30" }, + { label: "360p", value: "360p30" }, + { label: "160p", value: "160p30" }, ]; useEffect(() => { diff --git a/src/components/Archive/VodPreview.tsx b/src/components/Archive/VodPreview.tsx index dd6e299..7da74d8 100644 --- a/src/components/Archive/VodPreview.tsx +++ b/src/components/Archive/VodPreview.tsx @@ -14,11 +14,11 @@ export const VodPreview = ({ vod }: TwitchVODResponse) => {
- Erstellt: + Created At: {vod.created_at}
- Dauer: + Duration: {vod.duration}
diff --git a/src/components/Authentication/Login.tsx b/src/components/Authentication/Login.tsx index f75e9dc..b7a52af 100644 --- a/src/components/Authentication/Login.tsx +++ b/src/components/Authentication/Login.tsx @@ -35,7 +35,7 @@ export function LoginForm(props: PaperProps) { validate: { password: (val) => val.length <= 8 - ? "Das Passwort sollte mindestens 8 Zeichen enthalten" + ? "Password should include at least 8 characters" : null, }, }); @@ -55,7 +55,7 @@ export function LoginForm(props: PaperProps) { return ( - Willkommen bei VodArchiv.net + Welcome to Ganymede {publicRuntimeConfig.SHOW_SSO_LOGIN_BUTTON != "false" ? ( @@ -100,7 +100,7 @@ export function LoginForm(props: PaperProps) { } error={ form.errors.password && - "Das Passwort sollte mindestens 8 Zeichen enthalten" + "Password should include at least 8 characters" } /> diff --git a/src/components/Authentication/Register.tsx b/src/components/Authentication/Register.tsx index 6d273de..becbd9a 100644 --- a/src/components/Authentication/Register.tsx +++ b/src/components/Authentication/Register.tsx @@ -33,7 +33,7 @@ export function RegisterForm(props: PaperProps) { validate: { password: (val) => val.length <= 7 - ? "Das Passwort sollte mindestens 8 Zeichen enthalten" + ? "Password should include at least 8 characters" : null, }, }); @@ -59,7 +59,7 @@ export function RegisterForm(props: PaperProps) { return ( - Willkommen bei VodArchiv.net, bitte melde dich an + Welcome to Ganymede, sign up below
@@ -87,7 +87,7 @@ export function RegisterForm(props: PaperProps) { } error={ form.errors.password && - "Das Passwort sollte mindestens 8 Zeichen enthalten" + "Password should include at least 8 characters" } /> diff --git a/src/components/Channel/NoVideosFound.tsx b/src/components/Channel/NoVideosFound.tsx index c9affe9..69e8b1b 100644 --- a/src/components/Channel/NoVideosFound.tsx +++ b/src/components/Channel/NoVideosFound.tsx @@ -5,7 +5,7 @@ const ChannelNoVideosFound = () => { return (
- Keine VODs gefunden + No videos found
diff --git a/src/components/Error/Unauthorized.tsx b/src/components/Error/Unauthorized.tsx index 8383c55..c21e1a5 100644 --- a/src/components/Error/Unauthorized.tsx +++ b/src/components/Error/Unauthorized.tsx @@ -61,12 +61,13 @@ export function Unauthorized() { align="center" className={classes.description} > - Entweder bist du nicht eingeloggt oder du hast keine Berechtigung diese Seite zu sehen. + Either you are not logged in or you do not have permission to view this + page. diff --git a/src/components/Landing/ContinueWatching.tsx b/src/components/Landing/ContinueWatching.tsx index 6c87ffe..d9ed8bc 100644 --- a/src/components/Landing/ContinueWatching.tsx +++ b/src/components/Landing/ContinueWatching.tsx @@ -57,7 +57,7 @@ const LandingContinueWatching = () => { }), }); - if (error) return
Fehler beim Laden
; + if (error) return
failed to load
; if (isLoading) return ; return ( diff --git a/src/components/Landing/Hero.tsx b/src/components/Landing/Hero.tsx index 8a47199..1a61925 100644 --- a/src/components/Landing/Hero.tsx +++ b/src/components/Landing/Hero.tsx @@ -98,19 +98,30 @@ export function LandingHero() {
+ Twitch{" "} <Text component="span" inherit variant="gradient" gradient={{ from: "orange", to: "purple" }} > - VodArchiv.net{" "} - </Text> + Live Stream{" "} + </Text>{" "} + and{" "} + <Text + component="span" + inherit + variant="gradient" + gradient={{ from: "purple", to: "orange" }} + > + VOD{" "} + </Text>{" "} + archiving platform - Nahtlose Archivierung von Livestreams und Videoclips. - Jedes Archiv enthält eine Chat-Wiederholung und einen gerenderten Chat. + Seamlessly archive live streams and vods. Each archive includes a + chat replay and rendered chat.
@@ -122,7 +133,7 @@ export function LandingHero() { className={classes.control} mt={40} > - Zu den Kanälen + Channels diff --git a/src/components/Landing/LoggedInHero.tsx b/src/components/Landing/LoggedInHero.tsx index d9bb673..a6ec110 100644 --- a/src/components/Landing/LoggedInHero.tsx +++ b/src/components/Landing/LoggedInHero.tsx @@ -37,7 +37,7 @@ export function LandingLoggedInHero() { return (
- VodArchiv + Ganymede
); diff --git a/src/components/Landing/Recent.tsx b/src/components/Landing/Recent.tsx index 2cf6a70..70ef111 100644 --- a/src/components/Landing/Recent.tsx +++ b/src/components/Landing/Recent.tsx @@ -39,7 +39,7 @@ const LandingRecentlyArchived = () => { }), }); - if (error) return
Fehler beim Laden
; + if (error) return
failed to load
; if (isLoading) return ; return ( diff --git a/src/components/Playlist/Header.tsx b/src/components/Playlist/Header.tsx index f5e0bc7..aca6be4 100644 --- a/src/components/Playlist/Header.tsx +++ b/src/components/Playlist/Header.tsx @@ -49,7 +49,7 @@ const PlaylistHeader = ({ playlist, handleOpen, handleDeleteOpen }: any) => { - Einstellungen + Settings handleOpen()}>Edit handleDeleteOpen()} color="red"> Delete diff --git a/src/components/Queue/ChatTimeline.tsx b/src/components/Queue/ChatTimeline.tsx index 6db910f..4daf344 100644 --- a/src/components/Queue/ChatTimeline.tsx +++ b/src/components/Queue/ChatTimeline.tsx @@ -59,13 +59,15 @@ const QueueChatTimeline = ({ queue }: Object) => { > logs - {" - "} - restartTask("chat_download")} - > - restart - + {!queue.live_archive && ( + {" - "} + restartTask("chat_download")} + > + restart + + )} diff --git a/src/components/Queue/Header.tsx b/src/components/Queue/Header.tsx index f0ff3cf..b72e8ab 100644 --- a/src/components/Queue/Header.tsx +++ b/src/components/Queue/Header.tsx @@ -1,6 +1,7 @@ import { Container, createStyles, Image, Text, Tooltip } from "@mantine/core"; import dayjs from "dayjs"; import getConfig from "next/config"; +import { escapeURL } from "../../util/util"; const useStyles = createStyles((theme) => ({ queueHeader: { @@ -20,12 +21,10 @@ const useStyles = createStyles((theme) => ({ }, }, queueHeaderTitle: { - fontFamily: "Inter", fontWeight: 600, fontSize: "24px", }, queueHeaderHoverText: { - fontFamily: "Inter", fontWeight: 600, fontSize: "18px", }, @@ -33,7 +32,6 @@ const useStyles = createStyles((theme) => ({ paddingLeft: "10px", }, liveArchive: { - fontFamily: "Inter", fontWeight: 600, fontSize: "18px", backgroundColor: theme.colors.red[8], @@ -43,7 +41,6 @@ const useStyles = createStyles((theme) => ({ marginRight: "7px", }, onHold: { - fontFamily: "Inter", fontWeight: 600, fontSize: "18px", backgroundColor: theme.colors.indigo[8], @@ -64,7 +61,8 @@ const QueueHeader = ({ queue }: Object) => {
@@ -101,7 +99,7 @@ const QueueHeader = ({ queue }: Object) => { title="Streamed At" className={classes.queueHeaderHoverText} > - {dayjs(queue.edges.vod.streamed_at).format("DD.MM.YYYY")} + {dayjs(queue.edges.vod.streamed_at).format("YYYY/MM/DD")}
diff --git a/src/components/Queue/Table.tsx b/src/components/Queue/Table.tsx index ae7bf72..3e920bd 100644 --- a/src/components/Queue/Table.tsx +++ b/src/components/Queue/Table.tsx @@ -107,7 +107,7 @@ const QueueTable = () => { return false; }; - if (error) return
Fehler beim Laden
; + if (error) return
Failed to load
; if (isLoading) return ; return ( @@ -139,14 +139,14 @@ const QueueTable = () => { )} {value.processing && !checkFailed(value) && !value.on_hold && (
- +
)} {value.processing && !checkFailed(value) && value.on_hold && (
- + @@ -159,21 +159,21 @@ const QueueTable = () => { { accessor: "live_archive", - title: "Live-Archiv", + title: "Live Archive", render: ({ live_archive }) => ( {live_archive ? "true" : "false"} ), }, { accessor: "created_at", - title: "Erstellt am", + title: "Created At", render: ({ created_at }) => ( {new Date(created_at).toLocaleString()} ), }, { accessor: "actions", - title: "Aktionen", + title: "Actions", render: ({ id, live_archive }) => (
{ > logs - {" - "} - restartTask("video_download")} - > - restart - + {!queue.live_archive && ( + {" - "} + restartTask("video_download")} + > + restart + + )} + diff --git a/src/components/Vod/Card.tsx b/src/components/Vod/Card.tsx index 0120a19..3b92592 100644 --- a/src/components/Vod/Card.tsx +++ b/src/components/Vod/Card.tsx @@ -27,6 +27,7 @@ import { IconCircleCheck, IconMenu2 } from "@tabler/icons"; import { ROLES } from "../../hooks/useJsxAuth"; import { VodMenu } from "./Menu"; import Link from "next/link"; +import { escapeURL } from "../../util/util"; dayjs.extend(duration); dayjs.extend(localizedFormat); @@ -176,14 +177,16 @@ const VideoCard = ({ - Das Bild konnte nicht geladen werden! + This image could not be loaded } /> @@ -240,13 +243,13 @@ const VideoCard = ({
- {dayjs(video.streamed_at).format("DD.MM.YYYY")}{" "} + {dayjs(video.streamed_at).format("YYYY/MM/DD")}{" "} {user.settings.moreUIDetails && ( {dayjs(video.streamed_at).format("LT")} )} diff --git a/src/components/Vod/ChatPlayer.tsx b/src/components/Vod/ChatPlayer.tsx index 8fd08e5..9f394e8 100644 --- a/src/components/Vod/ChatPlayer.tsx +++ b/src/components/Vod/ChatPlayer.tsx @@ -4,6 +4,7 @@ import { createStyles } from "@mantine/core"; import React, { useEffect, useRef } from "react"; import vodDataBus from "./EventBus"; import getConfig from "next/config"; +import { escapeURL } from "../../util/util"; const useStyles = createStyles((theme) => ({ chatPlayer: { @@ -75,7 +76,7 @@ export const VodChatPlayer = ({ vod }: any) => { title: vod.title, sources: [ { - src: `${publicRuntimeConfig.CDN_URL}${vod.chat_video_path}`, + src: `${publicRuntimeConfig.CDN_URL}${escapeURL(vod.chat_video_path)}`, type: "video/mp4", }, ], diff --git a/src/components/Vod/ExperimentalChatPlayer.tsx b/src/components/Vod/ExperimentalChatPlayer.tsx index fd2fe44..2f34255 100644 --- a/src/components/Vod/ExperimentalChatPlayer.tsx +++ b/src/components/Vod/ExperimentalChatPlayer.tsx @@ -65,7 +65,7 @@ const ExperimentalChatPlayer = ({ vod }: any) => { const data = await axios.get( `${publicRuntimeConfig.API_URL}/api/v1/vod/${vod.id}/chat/emotes` ); - console.log(`Es wurden ${data.data.emotes.length} emotes geladen`); + console.log(`Loaded ${data.data.emotes.length} emotes`); data.data.emotes.forEach((emote: GanymedeEmote) => { if (emote.name == null || emote.name == "") { // If Twitch emote, see the emote ID as the key @@ -130,9 +130,9 @@ const ExperimentalChatPlayer = ({ vod }: any) => { setReady(true); internalReady = true; - createCustomComment("Chat-Player bereit."); + createCustomComment("Chat player ready."); createCustomComment( - `Es wurden ${generalBadgeMap.size.toLocaleString()} badges, ${subscriptionBadgeMap.size.toLocaleString()} subscription badges, und ${emoteMap.size.toLocaleString()} emotes geladen.` + `Fetched ${generalBadgeMap.size.toLocaleString()} badges, ${subscriptionBadgeMap.size.toLocaleString()} subscription badges, and ${emoteMap.size.toLocaleString()} emotes.` ); }); }); @@ -369,7 +369,7 @@ const ExperimentalChatPlayer = ({ vod }: any) => { _id: randomId(), content_offset_seconds: 0, commenter: { - display_name: "Olaf der Weidengeist", + display_name: "Ganymede", }, message: { body: message, diff --git a/src/components/Vod/InfoModalContent.tsx b/src/components/Vod/InfoModalContent.tsx index d016a6d..31b013b 100644 --- a/src/components/Vod/InfoModalContent.tsx +++ b/src/components/Vod/InfoModalContent.tsx @@ -29,7 +29,7 @@ export const VodInfoModalContent = ({ vod }: any) => { Video FFprobe - {error &&
Fehler beim Laden
} + {error &&
failed to load
} {isLoading && } {data && {JSON.stringify(data.data, null, 2)}}
diff --git a/src/components/Vod/LoginRequred.tsx b/src/components/Vod/LoginRequred.tsx index 1ae845c..b11f880 100644 --- a/src/components/Vod/LoginRequred.tsx +++ b/src/components/Vod/LoginRequred.tsx @@ -3,6 +3,7 @@ import { IconLock } from "@tabler/icons"; import getConfig from "next/config"; import React from "react"; import { Video } from "../../ganymede-defs"; +import { escapeURL } from "../../util/util"; const useStyles = createStyles((theme) => ({ container: { @@ -46,7 +47,7 @@ const VodLoginRequired = (vod: Video) => { const getImageUrl = () => { if (vod.thumbnail_path) { - return `${publicRuntimeConfig.CDN_URL}${vod.thumbnail_path}`; + return `${publicRuntimeConfig.CDN_URL}${escapeURL(vod.thumbnail_path)}`; } else { return "/images/landing-hero.webp"; } @@ -63,7 +64,7 @@ const VodLoginRequired = (vod: Video) => {
- Du musst eingeloggt sein, um dieses Video anzuschauen! + You must be logged in to view this video
diff --git a/src/components/Vod/Menu.tsx b/src/components/Vod/Menu.tsx index 18d557b..c08b120 100644 --- a/src/components/Vod/Menu.tsx +++ b/src/components/Vod/Menu.tsx @@ -18,6 +18,7 @@ import { IconTrashX, IconLock, IconLockOpen, + IconShare, } from "@tabler/icons"; import { useMutation, useQueryClient } from "@tanstack/react-query"; import { useEffect, useRef, useState } from "react"; @@ -26,6 +27,7 @@ import { VodInfoModalContent } from "./InfoModalContent"; import { VodPlaylistModalContent } from "./PlaylistModalContent"; import { ROLES, useJsxAuth } from "../../hooks/useJsxAuth"; import AdminVodDelete from "../Admin/Vods/Delete"; +import vodDataBus from "./EventBus"; const useStyles = createStyles((theme) => ({ action: { @@ -74,8 +76,8 @@ export const VodMenu = ({ vod, style }: any) => { ).then(() => { queryClient.invalidateQueries(["playback-data"]); showNotification({ - title: "Als gesehen markiert", - message: "VOD wurde erfolgreich als angesehen markiert", + title: "Marked as Watched", + message: "VOD has been successfully marked as watched", }); }); }, @@ -94,8 +96,8 @@ export const VodMenu = ({ vod, style }: any) => { ).then(() => { queryClient.invalidateQueries(["vod", vod.id]); showNotification({ - title: "Video aktualisiert", - message: `Das Video wurde ${lock ? "gesperrt" : "entsperrt"}`, + title: "Video Updated", + message: `Video has been ${lock ? "locked" : "unlocked"}`, }); if (lock == true) { isLocked.current = true; @@ -120,8 +122,8 @@ export const VodMenu = ({ vod, style }: any) => { ).then(() => { queryClient.invalidateQueries(["playback-data"]); showNotification({ - title: "Als ungesehen markiert", - message: "VOD wurde erfolgreich als ungesehen markiert", + title: "Marked as Unwatched", + message: "VOD has been successfully marked as unwatched", }); }); }, @@ -135,6 +137,46 @@ export const VodMenu = ({ vod, style }: any) => { setDeletedOpened(false); }; + const shareVideo = () => { + let shareUrl: string = ""; + const url = window.location.origin; + + // check if we are on a vod page + if (window.location.pathname.includes("/vods/") && window.location.pathname.includes(vod.id)) { + // get the current time + const { time } = vodDataBus.getData(); + const roundedTime = Math.ceil(time); + // create the url + shareUrl = `${url}/vods/${vod.id}?t=${roundedTime}`; + } else { + // create the url + shareUrl = `${url}/vods/${vod.id}`; + } + + // copy the url to the clipboard + try { + navigator.clipboard.writeText(shareUrl); + + // show a notification + showNotification({ + title: "Copied to Clipboard", + message: "The video url has been copied to your clipboard", + }); + + } catch (err) { + console.error(err); + // show a notification + showNotification({ + title: "Error", + message: "The clipboard API requires HTTPS, falling back to a prompt", + color: "red", + }); + + // fallback to a prompt + prompt("Copy to clipboard: Ctrl+C, Enter", shareUrl); + } + } + return (
@@ -170,43 +212,49 @@ export const VodMenu = ({ vod, style }: any) => { onClick={() => markAsWatched.mutate()} icon={} > - Als gesehen markieren + Mark as Watched markAsUnWatched.mutate()} icon={} > - Als nicht angesehen markieren + Mark as Unwatched {isLocked.current ? ( lockVod.mutate(false)} icon={} > - Entsperren + Unlock ) : ( lockVod.mutate(true)} icon={} > - Sperren + Lock )} {useJsxAuth({ loggedIn: true, roles: [ROLES.ADMIN], }) && ( - { - openDeleteModal(); - }} - icon={} - > - Löschen - - )} + { + openDeleteModal(); + }} + icon={} + > + Delete + + )} + shareVideo()} + icon={} + > + Share + { opened={infoModalOpened} onClose={() => setInfoModalOpended(false)} size="xl" - title="VOD Informationen" + title="VOD Information" > setDeletedOpened(false)} - title="Video Löschen" + title="Delete Video" > diff --git a/src/components/Vod/PlaylistModalContent.tsx b/src/components/Vod/PlaylistModalContent.tsx index 5d0f910..f902792 100644 --- a/src/components/Vod/PlaylistModalContent.tsx +++ b/src/components/Vod/PlaylistModalContent.tsx @@ -81,7 +81,7 @@ export const VodPlaylistModalContent = ({ vod }: any) => { }, }); - if (error) return
Fehler beim Laden
; + if (error) return
failed to load
; if (isLoading) return (
@@ -108,7 +108,7 @@ export const VodPlaylistModalContent = ({ vod }: any) => { value={value} onChange={setValue} searchable - placeholder="Eine Wiedergabeliste auswählen" + placeholder="Select a Playlist" dropdownPosition="bottom" />
+
+ + Livestream + { setStreamlinkLiveArgs(event.currentTarget.value) } placeholder="--force-progress,--force,--twitch-low-latency,--twitch-disable-hosting" - label="Streamlink Args" - description="Streamlink arguments for live streams. Must be comma separated." + label="Streamlink Parameters" + description="For live streams. Must be comma separated." /> - + Proxies + + + Archive livestreams through a proxy to prevent ads. Your Twitch + token will not be sent to the proxy. + + - setPostVideoFFmpegArgs(event.currentTarget.value) + setProxyEnabled(event.currentTarget.checked) } - placeholder="-c:v copy -c:a copy" - label="Video Convert FFmpeg Args" - description="Post video download ffmpeg args." + /> + {proxyList.map((proxy, index) => ( +
+ { + const newProxyList = [...proxyList]; + newProxyList[index].url = event.currentTarget.value; + setProxyList(newProxyList); + }} + label="Proxy URL" + /> + { + const newProxyList = [...proxyList]; + newProxyList[index].header = event.currentTarget.value; + setProxyList(newProxyList); + }} + label="Header" + /> + { + const newProxyList = [...proxyList]; + newProxyList.splice(index, 1); + setProxyList(newProxyList); + }} + > + + +
+ ))} + + {/* proxy whitelist */} +
diff --git a/src/pages/archive/index.tsx b/src/pages/archive/index.tsx index b8db277..24152fe 100644 --- a/src/pages/archive/index.tsx +++ b/src/pages/archive/index.tsx @@ -66,14 +66,14 @@ const ArchivePage = () => { ); const [channelData, setChannelData] = useState([]); const [channelId, setChannelID] = useState(""); - useDocumentTitle("Archive - VodArchiv.net"); + useDocumentTitle("Archive - Ganymede"); const qualityOptions = [ { label: "Best", value: "best" }, { label: "720p60", value: "720p60" }, - { label: "480p30", value: "480p30" }, - { label: "360p30", value: "360p30" }, - { label: "160p30", value: "160p30" }, + { label: "480p", value: "480p30" }, + { label: "360p", value: "360p30" }, + { label: "160p", value: "160p30" }, ]; const archiveVodSubmit = useMutation({ @@ -225,7 +225,7 @@ const ArchivePage = () => {
- Gebe eine Video-ID ein oder wähle einen Kanal, um einen Livestream zu archivieren. + Enter a video ID or select a channel to archive a livestream
{ { value: "clip", label: "Clip" }, ]; - useDocumentTitle(`${props.channel.display_name} - VodArchiv.net`); + useDocumentTitle(`${props.channel.display_name} - Ganymede`); const queryClient = useQueryClient(); @@ -96,7 +96,7 @@ const ChannelPage = (props: any) => { setPage(1); }; - if (error) return
Fehler beim Laden
; + if (error) return
failed to load
; return (
diff --git a/src/pages/channels/index.tsx b/src/pages/channels/index.tsx index b2d6a4c..7225c8b 100644 --- a/src/pages/channels/index.tsx +++ b/src/pages/channels/index.tsx @@ -21,7 +21,7 @@ const ChannelsPage = () => { ), }); - if (error) return
Fehler beim Laden
; + if (error) return
failed to load
; if (isLoading) return ; return ( diff --git a/src/pages/index.tsx b/src/pages/index.tsx index afafcf9..a73a5cb 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -39,7 +39,7 @@ const useStyles = createStyles((theme) => ({ export default function Home() { const { classes } = useStyles(); - useDocumentTitle("VodArchiv"); + useDocumentTitle("Ganymede"); const user = useUserStore((state) => state); @@ -55,7 +55,7 @@ export default function Home() { )}
- Kürzlich archiviert + Recently Archived
diff --git a/src/pages/login.tsx b/src/pages/login.tsx index 0f21fff..35eadea 100644 --- a/src/pages/login.tsx +++ b/src/pages/login.tsx @@ -3,7 +3,7 @@ import { Container } from "@mantine/core"; import { useDocumentTitle } from "@mantine/hooks"; const LoginPage = () => { - useDocumentTitle("VodArchiv.net - Login"); + useDocumentTitle("Ganymede - Login"); return (
diff --git a/src/pages/profile.tsx b/src/pages/profile.tsx index 89573c6..4deab06 100644 --- a/src/pages/profile.tsx +++ b/src/pages/profile.tsx @@ -46,7 +46,7 @@ const ProfilePage = () => { const { classes, theme } = useStyles(); - useDocumentTitle("Profile - VodArchiv.net"); + useDocumentTitle("Profile - Ganymede"); useEffect(() => { setUseNewChatPlayer(user.settings.useNewChatPlayer); diff --git a/src/pages/register.tsx b/src/pages/register.tsx index e53ae5b..7556618 100644 --- a/src/pages/register.tsx +++ b/src/pages/register.tsx @@ -4,7 +4,7 @@ import { RegisterForm } from "../components/Authentication/Register"; import { useDocumentTitle } from "@mantine/hooks"; const RegisterPage = () => { - useDocumentTitle("VodArchiv.net - Registrierung"); + useDocumentTitle("Ganymede - Register"); return (
diff --git a/src/pages/search/index.tsx b/src/pages/search/index.tsx index 4250f61..4649e18 100644 --- a/src/pages/search/index.tsx +++ b/src/pages/search/index.tsx @@ -29,7 +29,7 @@ const SearchPage = (props: SearchPageProps) => { const [limit, setLimit] = useState(24); const handlers = useRef(); - useDocumentTitle("VodArchiv.net - Suche"); + useDocumentTitle("Ganymede - Search"); useEffect(() => { if (props.q && props.q.length > 0) { diff --git a/src/pages/vods/[vodId].tsx b/src/pages/vods/[vodId].tsx index 644be52..74c1bf5 100644 --- a/src/pages/vods/[vodId].tsx +++ b/src/pages/vods/[vodId].tsx @@ -55,7 +55,7 @@ const VodPage = (props: any) => { const { ref, toggle, fullscreen } = useFullscreen(); const isSmallDevice = useRef(false); - useDocumentTitle(`VodArchiv.net - VOD ${props.vodId}`); + useDocumentTitle(`Ganymede - VOD ${props.vodId}`); const { data } = useQuery({ queryKey: ["vod", props.vodId], @@ -107,7 +107,7 @@ const VodPage = (props: any) => { return (
- {data.title} - VodArchiv.net + {data.title} - Ganymede {checkLoginRequired() && } {!checkLoginRequired() && ( diff --git a/src/util/util.ts b/src/util/util.ts new file mode 100644 index 0000000..a76351c --- /dev/null +++ b/src/util/util.ts @@ -0,0 +1,3 @@ +export function escapeURL(str: string): string { + return str.replace(/#/g, "%23"); +}