diff --git a/frontend/src/i18n/locales/de.yaml b/frontend/src/i18n/locales/de.yaml
index f38ec9b0b..6891c5167 100644
--- a/frontend/src/i18n/locales/de.yaml
+++ b/frontend/src/i18n/locales/de.yaml
@@ -56,6 +56,7 @@ not-found:
page-not-found: Seite nicht gefunden
video-not-found: Video nicht gefunden
series-not-found: Serie nicht gefunden
+ playlist-not-found: Playlist nicht gefunden
page-explanation: >
Die von Ihnen gewünschte Seite existiert nicht. Sie wurde möglicherweise
gelöscht oder umbenannt.
@@ -65,6 +66,9 @@ not-found:
series-explanation: >
Die von Ihnen gewünschte Serie existiert nicht. Sie wurde möglicherweise
gelöscht oder verschoben.
+ playlist-explanation: >
+ Die von Ihnen gewünschte Playlist existiert nicht. Sie wurde möglicherweise
+ gelöscht oder verschoben.
url-typo: >
Falls Sie die Adresse/URL manuell eingegeben haben, prüfen Sie diese auf
Fehler.
@@ -176,8 +180,6 @@ series:
series: Serie
deleted: Gelöschte Serie
deleted-series-block: Die hier referenzierte Serie wurde gelöscht.
- no-events: Diese Serie enthält keine Videos oder Sie sind nicht berechtigt, diese zu sehen.
- upcoming-live-streams: "Anstehende Livestreams ({{count}})"
entry-of-series-thumbnail: "Vorschlaubild für Teil von „{{series}}“"
videos:
heading: Videos
@@ -186,9 +188,17 @@ series:
text: >
Die Daten der gewünschten Serie wurden leider noch nicht vollständig übertragen. Dies sollte
in Kürze automatisch passieren. Versuchen Sie es in wenigen Minuten noch einmal!
+
+videolist-block:
+ upcoming-live-streams: "Anstehende Livestreams ({{count}})"
+ missing-video: Video nicht gefunden
+ unauthorized: Fehlende Berechtigung
+ hidden-items_one: 'Ein Video wurde nicht gefunden oder Sie haben keinen Zugriff darauf.'
+ hidden-items_other: '{{count}} Videos wurden nicht gefunden oder Sie haben keinen Zugriff darauf.'
settings:
order: Reihenfolge
order-label: Video Reihenfolge auswählen
+ original: Wie Playlist
new-to-old: Neueste zuerst
old-to-new: Älteste zuerst
a-z: A bis Z
@@ -580,6 +590,7 @@ manage:
api-remote-errors:
view:
event: Sie sind nicht autorisiert, dieses Video zu sehen.
+ playlist: Sie sind nicht autorisiert, diese Playlist zu sehen.
upload:
not-logged-in: Sie müssen angemeldet sein, um Videos hochzuladen.
not-authorized: $t(upload.not-authorized)
diff --git a/frontend/src/i18n/locales/en.yaml b/frontend/src/i18n/locales/en.yaml
index 1385cf14b..497c55849 100644
--- a/frontend/src/i18n/locales/en.yaml
+++ b/frontend/src/i18n/locales/en.yaml
@@ -55,6 +55,7 @@ not-found:
page-not-found: Page not found
video-not-found: Video not found
series-not-found: Series not found
+ playlist-not-found: Playlist not found
page-explanation: >
The page you want to visit does not exist. It might have been removed or
renamed.
@@ -64,6 +65,9 @@ not-found:
series-explanation: >
The series you want to view does not exist. It might have been removed or
moved.
+ playlist-explanation: >
+ The playlist you want to view does not exist. It might have been removed or
+ moved.
url-typo: >
If you entered the address/URL manually, please double check it for
spelling mistakes.
@@ -173,8 +177,6 @@ series:
series: Series
deleted: Deleted series
deleted-series-block: The series referenced here was deleted.
- no-events: This series does not contain any events, or you might not be authorized to see them.
- upcoming-live-streams: "Upcoming live streams ({{count}})"
entry-of-series-thumbnail: "Thumbnail for entry of “{{series}}”"
videos:
heading: Videos
@@ -183,9 +185,18 @@ series:
text: >
The data of the requested series has not been fully transferred yet. This should
happen automatically soon. Try again in a few minutes.
+
+videolist-block:
+ upcoming-live-streams: "Upcoming live streams ({{count}})"
+ missing-video: Video not found
+ unauthorized: Missing permissions
+ hidden-items_one: 'One video is missing or requires additional permissions to view.'
+ hidden-items_other: '{{count}} videos are missing or require additional permissions to view.'
+ no-videos: No videos
settings:
order: Order
order-label: Choose video order
+ original: Playlist order
new-to-old: Newest first
old-to-new: Oldest first
a-z: A to Z
@@ -554,6 +565,7 @@ manage:
api-remote-errors:
view:
event: You are not authorized to view this video.
+ playlist: You are not authorized to view this playlist.
upload:
not-logged-in: You have to be logged in to upload videos.
not-authorized: $t(upload.not-authorized)
diff --git a/frontend/src/router.tsx b/frontend/src/router.tsx
index 28dd2e609..bb469d993 100644
--- a/frontend/src/router.tsx
+++ b/frontend/src/router.tsx
@@ -18,6 +18,7 @@ import { ManageVideoDetailsRoute } from "./routes/manage/Video/Details";
import { ManageVideoTechnicalDetailsRoute } from "./routes/manage/Video/TechnicalDetails";
import React from "react";
import { ManageVideoAccessRoute } from "./routes/manage/Video/Access";
+import { DirectPlaylistOCRoute, DirectPlaylistRoute } from "./routes/Playlist";
@@ -42,6 +43,8 @@ const {
DirectOpencastVideoRoute,
DirectSeriesRoute,
DirectSeriesOCRoute,
+ DirectPlaylistRoute,
+ DirectPlaylistOCRoute,
ManageRoute,
ManageVideosRoute,
ManageVideoAccessRoute,
diff --git a/frontend/src/routes/NotFound.tsx b/frontend/src/routes/NotFound.tsx
index 0711554f9..b1d199523 100644
--- a/frontend/src/routes/NotFound.tsx
+++ b/frontend/src/routes/NotFound.tsx
@@ -32,7 +32,7 @@ const query = graphql`
`;
type Props = {
- kind: "page" | "video" | "series";
+ kind: "page" | "video" | "series" | "playlist";
};
export const NotFound: React.FC = ({ kind }) => {
@@ -41,6 +41,7 @@ export const NotFound: React.FC = ({ kind }) => {
"page": () => t("not-found.page-not-found"),
"video": () => t("not-found.video-not-found"),
"series": () => t("not-found.series-not-found"),
+ "playlist": () => t("not-found.playlist-not-found"),
});
// Ideally our backend would respond with 404 here, but that's not
@@ -64,6 +65,7 @@ export const NotFound: React.FC = ({ kind }) => {
"page": () => t("not-found.page-explanation"),
"video": () => t("not-found.video-explanation"),
"series": () => t("not-found.series-explanation"),
+ "playlist": () => t("not-found.playlist-explanation"),
})}
{t("not-found.url-typo")}
span": {
+ display: "inline-block",
+ whiteSpace: "nowrap",
},
- // TODO: maybe find something better than `join`
- }}>{event.creators.join(", ")}}
- {/* `new Date` is well defined for our ISO Date strings */}
-
-
- {showDescription && }
+ }}>
+ {item.creators.length > 0 && {item.creators.join(", ")}}
+ {/* `new Date` is well defined for our ISO Date strings */}
+
+
+);
+
type ThumbnailOverlayProps = PropsWithChildren<{
backgroundColor: string;
}>;
diff --git a/frontend/src/util/index.ts b/frontend/src/util/index.ts
index b12fd2614..e700c9e0a 100644
--- a/frontend/src/util/index.ts
+++ b/frontend/src/util/index.ts
@@ -26,6 +26,9 @@ export const eventId = (key: string) => `ev${key}`;
/** Constructs series ID for graphQL by adding the "sr" prefix. */
export const seriesId = (key: string) => `sr${key}`;
+/** Constructs series ID for graphQL by adding the "sr" prefix. */
+export const playlistId = (key: string) => `pl${key}`;
+
/**
* Create a comparison function for `Array.prototype.sort` comparing whatever
* the given key function returns as numbers.