diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index b45a645..38298bd 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -3,7 +3,7 @@ { "name": "Ganymede Frontend", // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile - "image": "mcr.microsoft.com/devcontainers/javascript-node:0-20", + "image": "mcr.microsoft.com/devcontainers/typescript-node:20", // Features to add to the dev container. More info: https://containers.dev/features. // "features": {}, @@ -16,7 +16,9 @@ "customizations": { "vscode": { "extensions": [ - "eamodio.gitlens" + "eamodio.gitlens", + "vunguyentuan.vscode-postcss", + "vunguyentuan.vscode-css-variables" ] } } diff --git a/.dockerignore b/.dockerignore index d862f96..22b7245 100644 --- a/.dockerignore +++ b/.dockerignore @@ -3,4 +3,6 @@ Dockerfile node_modules npm-debug.log README.md -.next \ No newline at end of file +.next +!.next/static +!.next/standalone diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index c55c158..44e7ed3 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -22,55 +22,44 @@ env: jobs: build: runs-on: ubuntu-latest - permissions: - contents: read - packages: write - # This is used to complete the identity challenge - # with sigstore/fulcio when running outside of PRs. - id-token: write - steps: - name: Checkout repository - uses: actions/checkout@v3.3.0 + uses: actions/checkout@v4 + # Set up QEMU for Arm64 - name: Set up QEMU - uses: docker/setup-qemu-action@v2.1.0 - with: - platforms: all + uses: docker/setup-qemu-action@v3 - # Workaround: https://github.com/docker/build-push-action/issues/461 - - name: Setup Docker buildx - uses: docker/setup-buildx-action@v2.2.1 + # Set up Docker Buildx + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 - # Login against a Docker registry except on PR - # https://github.com/docker/login-action + # Login into GitHub Container Registry except on PR - name: Log into registry ${{ env.REGISTRY }} if: github.event_name != 'pull_request' - uses: docker/login-action@v2.1.0 + uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} # Extract metadata (tags, labels) for Docker - # https://github.com/docker/metadata-action - - name: Extract Docker metadata + - name: Extract Docker metadata (release) id: meta - uses: docker/metadata-action@v4.3.0 + uses: docker/metadata-action@v5 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + flavor: | + latest=auto + tags: | + type=semver,pattern={{version}} + type=raw,value=dev - # Build and push Docker image with Buildx (don't push on PR) - # https://github.com/docker/build-push-action - - name: Build and push Docker image - id: build-and-push - uses: docker/build-push-action@v3.3.0 + - name: Build and push + uses: docker/build-push-action@v6 with: - context: . + platforms: linux/amd64,linux/arm64 push: ${{ github.event_name != 'pull_request' }} tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - platforms: linux/amd64,linux/arm64 - provenance: false - cache-from: type=gha,scope=${{ env.IMAGE_NAME }} - cache-to: type=gha,scope=${{ env.IMAGE_NAME }},mode=max + context: . + dockerfile: ./Dockerfile \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index e69de29..076b96e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -0,0 +1,9 @@ +{ + "cssVariables.lookupFiles": [ + "**/*.css", + "**/*.scss", + "**/*.sass", + "**/*.less", + "node_modules/@mantine/core/styles.css" + ] +} \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 7271388..cf6d9fe 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,3 +1,5 @@ +ARG GITHUB_SHA + # Install dependencies only when needed FROM node:18-alpine AS deps # Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed. @@ -8,6 +10,9 @@ RUN npm install # Rebuild the source code only when needed FROM node:18-alpine AS builder + +ENV NEXT_PUBLIC_GIT_COMMIT=$GITHUB_SHA + RUN apk add --no-cache git WORKDIR /app COPY --from=deps /app/node_modules ./node_modules @@ -20,7 +25,6 @@ RUN npm i sharp -y RUN npm run build - # Production image, copy all the files and run next FROM node:18-alpine AS runner WORKDIR /app diff --git a/src/components/Admin/Channels/Delete.tsx b/src/components/Admin/Channels/Delete.tsx index f4c7e25..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,7 +38,7 @@ const AdminChannelsDelete = ({ handleClose, channel }) => { return (
- Bist du sicher, dass du diesen Channel löschen möchtest? + Are you sure you want to delete the following channel?
Channel ID: {channel.id} @@ -48,7 +48,7 @@ const AdminChannelsDelete = ({ handleClose, channel }) => {
- Diese Aktion entfernt keine Dateien + 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 0c704c8..dd99e1c 100644 --- a/src/components/Admin/Channels/Drawer.tsx +++ b/src/components/Admin/Channels/Drawer.tsx @@ -55,8 +55,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(); }) @@ -86,8 +86,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(); }) @@ -113,8 +113,8 @@ const AdminChannelDrawer = ({ handleClose, channel, mode }) => { .then(() => { setUpdateImageLoading(false); showNotification({ - title: "Kanal Aktualisiert", - message: "Das Kanalbild wurde Aktualisiert.", + title: "Channel Updated", + message: "Channel image has been updated successfully", }); handleClose(); }) diff --git a/src/components/Admin/Channels/Table.tsx b/src/components/Admin/Channels/Table.tsx index 8412a66..cde279a 100644 --- a/src/components/Admin/Channels/Table.tsx +++ b/src/components/Admin/Channels/Table.tsx @@ -122,7 +122,7 @@ const AdminChannelsTable = () => { title: "Created At", sortable: true, render: ({ created_at }) => ( -
{dayjs(created_at).format("DD.MM.YYYY")}
+
{dayjs(created_at).format("YYYY/MM/DD")}
), }, { diff --git a/src/components/Archive/VodPreview.tsx b/src/components/Archive/VodPreview.tsx index 9e38f57..2ace9eb 100644 --- a/src/components/Archive/VodPreview.tsx +++ b/src/components/Archive/VodPreview.tsx @@ -15,11 +15,11 @@ export const VodPreview = ({ video }: { video: PlatformVideoInfo }) => {
- Veröffentlichungsdatum: + Created At: {video.created_at}
- Dauer: + Duration: {video.duration}
@@ -28,7 +28,7 @@ export const VodPreview = ({ video }: { video: PlatformVideoInfo }) => {
{video.chapters && (
- Kapitel: + Chapters: {video.chapters.length}
)} diff --git a/src/components/Authentication/Login.tsx b/src/components/Authentication/Login.tsx index 9198c34..d7c3517 100644 --- a/src/components/Authentication/Login.tsx +++ b/src/components/Authentication/Login.tsx @@ -47,7 +47,7 @@ export function LoginForm(props: PaperProps) { return ( - Willkommen bei Vodarchiv, bitte melde dich an + Welcome to Ganymede, login below {publicRuntimeConfig.SHOW_SSO_LOGIN_BUTTON != "false" && ( @@ -95,7 +95,7 @@ export function LoginForm(props: PaperProps) { - Du hast noch keinen Account? Dann Registriere dich + Don't have an account? Register diff --git a/src/components/Landing/ContinueWatching.tsx b/src/components/Landing/ContinueWatching.tsx index 08929be..d0b3a23 100644 --- a/src/components/Landing/ContinueWatching.tsx +++ b/src/components/Landing/ContinueWatching.tsx @@ -26,7 +26,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 286fb81..824b7f2 100644 --- a/src/components/Landing/Hero.tsx +++ b/src/components/Landing/Hero.tsx @@ -10,19 +10,19 @@ export function LandingHero() {
- VodArchiv + Ganymede - Deine Plattform, wenn es um Twitch VOD's geht! + Effortlessly preserve live streams and VODs. Every archive comes complete with a chat replay and a rendered chat experience.
diff --git a/src/components/Landing/LoggedInHero.tsx b/src/components/Landing/LoggedInHero.tsx index 6c3e033..43c4338 100644 --- a/src/components/Landing/LoggedInHero.tsx +++ b/src/components/Landing/LoggedInHero.tsx @@ -6,7 +6,7 @@ export function LandingLoggedInHero() { return (
- Vodarchiv + Ganymede
); diff --git a/src/components/Landing/Recent.tsx b/src/components/Landing/Recent.tsx index ddba4ba..5227a87 100644 --- a/src/components/Landing/Recent.tsx +++ b/src/components/Landing/Recent.tsx @@ -22,7 +22,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/Table.tsx b/src/components/Playlist/Table.tsx index fe38f0f..86024ce 100644 --- a/src/components/Playlist/Table.tsx +++ b/src/components/Playlist/Table.tsx @@ -26,7 +26,7 @@ const PlaylistTable = () => { ).then((res) => res?.data), }); - if (error) return
Fehler beim Laden
; + if (error) return
Failed to load
; if (isLoading) return ; return ( diff --git a/src/components/Video/Core/VideoCard.tsx b/src/components/Video/Core/VideoCard.tsx index 1675c34..048d509 100644 --- a/src/components/Video/Core/VideoCard.tsx +++ b/src/components/Video/Core/VideoCard.tsx @@ -166,13 +166,13 @@ const VideoCard = ({ video, showProgress = true, showMenu = true, showChannel = align="center" pt={2}> - {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/Video/ExperimentalChatPlayer.tsx b/src/components/Video/ExperimentalChatPlayer.tsx index 4ff2117..d99e31b 100644 --- a/src/components/Video/ExperimentalChatPlayer.tsx +++ b/src/components/Video/ExperimentalChatPlayer.tsx @@ -356,7 +356,7 @@ const ExperimentalChatPlayer = ({ vod }: any) => { _id: randomId(), content_offset_seconds: 0, commenter: { - display_name: "VODChat", + display_name: "Ganymede", }, message: { body: message, @@ -381,7 +381,7 @@ const ExperimentalChatPlayer = ({ vod }: any) => {
- Chat wird geladen + Loading Chat
)} diff --git a/src/components/Video/InfoModalContent.tsx b/src/components/Video/InfoModalContent.tsx index d016a6d..31b013b 100644 --- a/src/components/Video/InfoModalContent.tsx +++ b/src/components/Video/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/Video/LoginRequred.tsx b/src/components/Video/LoginRequred.tsx index f1d32ca..de43fcf 100644 --- a/src/components/Video/LoginRequred.tsx +++ b/src/components/Video/LoginRequred.tsx @@ -28,7 +28,7 @@ const VodLoginRequired = (vod: Video) => {
- Du musst eingeloggt sein, um dieses Video zu sehen + You must be logged in to view this video
diff --git a/src/components/Video/PlaylistModalContent.tsx b/src/components/Video/PlaylistModalContent.tsx index c6db903..b5dc898 100644 --- a/src/components/Video/PlaylistModalContent.tsx +++ b/src/components/Video/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 (
diff --git a/src/pages/channels/[channelName].tsx b/src/pages/channels/[channelName].tsx index 39bd461..9f8ceba 100644 --- a/src/pages/channels/[channelName].tsx +++ b/src/pages/channels/[channelName].tsx @@ -55,7 +55,7 @@ const ChannelPage = (props: any) => { { value: "clip", label: "Clip" }, ]; - useDocumentTitle(`${props.channel.display_name} - VodArchiv`); + useDocumentTitle(`${props.channel.display_name} - Ganymede`); const queryClient = useQueryClient(); diff --git a/src/pages/channels/index.tsx b/src/pages/channels/index.tsx index e3a3144..d0482b1 100644 --- a/src/pages/channels/index.tsx +++ b/src/pages/channels/index.tsx @@ -10,7 +10,7 @@ import { useDocumentTitle } from "@mantine/hooks"; const ChannelsPage = () => { const queryClient = useQueryClient(); - useDocumentTitle("Kanäle - VODArchiv"); + useDocumentTitle("Channels - Ganymede"); // React Query const { isLoading, error, data } = useQuery({ @@ -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 d6b6591..f1313fd 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -13,7 +13,7 @@ import { LandingLoggedInHero } from "../components/Landing/LoggedInHero"; export default function Home() { - useDocumentTitle("VODArchiv"); + useDocumentTitle("Ganymede"); const user = useUserStore((state) => state); @@ -23,7 +23,7 @@ export default function Home() { {user.isLoggedIn && (
-
Weiterschauen
+
Continue Watching
@@ -31,7 +31,7 @@ export default function Home() {
)}
-
Zuletzt Archiviert
+
Recently Archived
diff --git a/src/pages/login.tsx b/src/pages/login.tsx index af72d4c..ee92718 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 - Login"); + useDocumentTitle("Ganymede - Login"); return (
diff --git a/src/pages/playlists/[playlistId].tsx b/src/pages/playlists/[playlistId].tsx index 1b4f363..e50dfb7 100644 --- a/src/pages/playlists/[playlistId].tsx +++ b/src/pages/playlists/[playlistId].tsx @@ -16,7 +16,7 @@ const PlaylistPage = (props: any) => { const [deletePlaylistModalOpened, setDeletePlaylistModalOpened] = useState(false); - useDocumentTitle(`VODArchiv - Playlist ${props.playlistId}`); + useDocumentTitle(`Ganymede - Playlist ${props.playlistId}`); const { isLoading, error, data } = useQuery({ queryKey: ["playlist", props.playlistId], @@ -63,7 +63,7 @@ const PlaylistPage = (props: any) => { return (
- {data.name} - VODArchiv Playlist + {data.name} - Ganymede Playlist { const [showMoreUIDetails, setShowMoreUIDetails] = useState(false); - useDocumentTitle("Profile - VODArchiv"); + useDocumentTitle("Profile - Ganymede"); useEffect(() => { setUseNewChatPlayer(user.settings.useNewChatPlayer); diff --git a/src/pages/queue/index.tsx b/src/pages/queue/index.tsx index 631bb67..61ade4f 100644 --- a/src/pages/queue/index.tsx +++ b/src/pages/queue/index.tsx @@ -5,7 +5,7 @@ import { Authorization, ROLES } from "../../components/ProtectedRoute"; import QueueTable from "../../components/Queue/Table"; const QueuePage = () => { - useDocumentTitle("Queue - VODArchiv"); + useDocumentTitle("Queue - Ganymede"); return ( diff --git a/src/pages/register.tsx b/src/pages/register.tsx index dddc7a8..380f878 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 - Register"); + useDocumentTitle("Ganymede - Register"); return (
diff --git a/src/pages/search/index.tsx b/src/pages/search/index.tsx index fea4350..5a9bff8 100644 --- a/src/pages/search/index.tsx +++ b/src/pages/search/index.tsx @@ -30,7 +30,7 @@ const SearchPage = (props: SearchPageProps) => { const [limit, setLimit] = useState(24); const handlers = useRef(); - useDocumentTitle("VODArchiv - 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 9162179..b1f3571 100644 --- a/src/pages/vods/[vodId].tsx +++ b/src/pages/vods/[vodId].tsx @@ -48,7 +48,7 @@ const VodPage = (props: any) => { const isMobile = useMediaQuery(`(max-width: 1000px)`); - useDocumentTitle(`VODArchiv - VOD ${props.vodId}`); + useDocumentTitle(`Ganymede - VOD ${props.vodId}`); const { data } = useQuery({ queryKey: ["vod", props.vodId], @@ -89,7 +89,7 @@ const VodPage = (props: any) => { return (
- {data.title} - VODARchiv + {data.title} - Ganymede {checkLoginRequired() && } {!checkLoginRequired() && (