Added audio and video collections

This commit is contained in:
DrMint 2024-04-05 15:41:58 +02:00
parent f9517c4262
commit af7c5ee5a0
16 changed files with 572 additions and 87 deletions

View File

@ -0,0 +1,11 @@
import { User } from "payload/auth";
import { RecordersRoles } from "../../constants";
import { Recorder } from "../../types/collections";
import { isDefined, isUndefined } from "../../utils/asserts";
export const shownOnlyToAdmin = ({ user }: { user: User }): boolean => {
if (isUndefined(user)) return false;
const recorder = user as unknown as Recorder;
const isAdmin = isDefined(recorder.role) && recorder.role.includes(RecordersRoles.Admin);
return !isAdmin;
};

View File

@ -0,0 +1,71 @@
import { CollectionGroups, Collections } from "../../constants";
import { imageField } from "../../fields/imageField/imageField";
import { rowField } from "../../fields/rowField/rowField";
import { tagsField } from "../../fields/tagsField/tagsField";
import { translatedFields } from "../../fields/translatedFields/translatedFields";
import { buildCollectionConfig } from "../../utils/collectionConfig";
import { createEditor } from "../../utils/editor";
const fields = {
filename: "filename",
mimeType: "mimeType",
filesize: "filesize",
updatedAt: "updatedAt",
translations: "translations",
translationsTitle: "title",
translationsDescription: "description",
translationsSubfile: "subfile",
thumbnail: "thumbnail",
duration: "duration",
tags: "tags",
};
export const Audios = buildCollectionConfig({
slug: Collections.Audios,
labels: { singular: "Audio", plural: "Audios" },
defaultSort: fields.updatedAt,
admin: {
group: CollectionGroups.Media,
defaultColumns: [
fields.filename,
fields.mimeType,
fields.filesize,
fields.translations,
fields.updatedAt,
],
},
upload: {
mimeTypes: ["audio/*"],
disableLocalStorage: true,
},
fields: [
rowField([
{ name: fields.duration, type: "number", min: 0, required: true },
imageField({
name: fields.thumbnail,
relationTo: Collections.MediaThumbnails,
}),
]),
translatedFields({
name: fields.translations,
admin: { useAsTitle: fields.translationsTitle },
required: true,
minRows: 1,
fields: [
{ name: fields.translationsTitle, type: "text", required: true },
{
name: fields.translationsDescription,
type: "richText",
editor: createEditor({ inlines: true, lists: true, links: true }),
},
{
name: fields.translationsSubfile,
type: "upload",
relationTo: Collections.VideosSubtitles,
admin: { description: "The subtitle file needs to follow the WebVTT file format (.vtt)" },
},
],
}),
tagsField({ name: fields.tags }),
],
});

View File

@ -611,8 +611,12 @@ export const Collectibles = buildVersionedCollectionConfig({
{
name: fields.contentsContent,
type: "relationship",
// TODO: Add audio and video files
relationTo: [Collections.Pages, Collections.GenericContents],
relationTo: [
Collections.Pages,
Collections.GenericContents,
Collections.Audios,
Collections.Videos,
],
admin: {
allowCreate: false,
},

View File

@ -1,5 +1,6 @@
import { text } from "payload/dist/fields/validations";
import { mustBeAdmin } from "../../accesses/collections/mustBeAdmin";
import { shownOnlyToAdmin } from "../../accesses/collections/shownOnlyToAdmin";
import { CollectionGroups, Collections } from "../../constants";
import { afterOperationWebhook } from "../../hooks/afterOperationWebhook";
import { buildCollectionConfig } from "../../utils/collectionConfig";
@ -23,8 +24,9 @@ export const Currencies = buildCollectionConfig({
defaultColumns: [fields.id],
disableDuplicate: true,
group: CollectionGroups.Meta,
hidden: shownOnlyToAdmin,
},
access: { create: mustBeAdmin, update: mustBeAdmin },
access: { create: mustBeAdmin, update: mustBeAdmin, delete: mustBeAdmin },
hooks: {
afterOperation: [afterOperationWebhook],
},

View File

@ -99,7 +99,13 @@ export const Folders = buildCollectionConfig({
{
type: "relationship",
name: fields.files,
relationTo: [Collections.Collectibles, Collections.Pages],
relationTo: [
Collections.Collectibles,
Collections.Pages,
Collections.Videos,
Collections.Images,
Collections.Audios,
],
hasMany: true,
},
],

View File

@ -35,7 +35,7 @@ export const getBySlugEndpoint = createGetByEndpoint({
},
files:
files?.flatMap<EndpointFolder["files"][number]>(({ relationTo, value }) => {
if (!isPayloadType(value) || !isPublished(value)) {
if (!isPayloadType(value) || ("_status" in value && !isPublished(value))) {
return [];
}
@ -44,6 +44,13 @@ export const getBySlugEndpoint = createGetByEndpoint({
return [{ relationTo, value: convertCollectibleToPreview(value) }];
case "pages":
return [{ relationTo, value: convertPageToPreview(value) }];
// TODO: handle media type files
case "images":
return [];
case "audios":
return [];
case "videos":
return [];
}
}) ?? [],
parentPages: handleParentPages({ folders: parentFolders }),

View File

@ -1,4 +1,7 @@
import { Collections } from "../../constants";
import { tagsField } from "../../fields/tagsField/tagsField";
import { translatedFields } from "../../fields/translatedFields/translatedFields";
import { createEditor } from "../../utils/editor";
import { buildImageCollectionConfig } from "../../utils/imageCollectionConfig";
const fields = {
@ -7,6 +10,10 @@ const fields = {
filesize: "filesize",
posts: "posts",
updatedAt: "updatedAt",
translations: "translations",
translationsTitle: "title",
translationsDescription: "description",
tags: "tags",
} as const satisfies Record<string, string>;
export const Images = buildImageCollectionConfig({
@ -29,5 +36,19 @@ export const Images = buildImageCollectionConfig({
},
],
},
fields: [],
fields: [
translatedFields({
name: fields.translations,
admin: { useAsTitle: fields.translationsTitle },
fields: [
{ name: fields.translationsTitle, type: "text", required: true },
{
name: fields.translationsDescription,
type: "richText",
editor: createEditor({ inlines: true, lists: true, links: true }),
},
],
}),
tagsField({ name: fields.tags }),
],
});

View File

@ -1,6 +1,6 @@
import { text } from "payload/dist/fields/validations";
import { mustBeAdmin } from "../../accesses/collections/mustBeAdmin";
import { publicAccess } from "../../accesses/publicAccess";
import { shownOnlyToAdmin } from "../../accesses/collections/shownOnlyToAdmin";
import { CollectionGroups, Collections } from "../../constants";
import { afterOperationWebhook } from "../../hooks/afterOperationWebhook";
import { buildCollectionConfig } from "../../utils/collectionConfig";
@ -25,8 +25,9 @@ export const Languages = buildCollectionConfig({
disableDuplicate: true,
group: CollectionGroups.Meta,
pagination: { defaultLimit: 100 },
hidden: shownOnlyToAdmin,
},
access: { create: mustBeAdmin, update: mustBeAdmin, read: publicAccess },
access: { create: mustBeAdmin, update: mustBeAdmin, delete: mustBeAdmin },
hooks: {
afterOperation: [afterOperationWebhook],
},

View File

@ -1,3 +1,4 @@
import { shownOnlyToAdmin } from "../../accesses/collections/shownOnlyToAdmin";
import { Collections } from "../../constants";
import { buildImageCollectionConfig } from "../../utils/imageCollectionConfig";
@ -14,7 +15,10 @@ export const Scans = buildImageCollectionConfig({
singular: "Scan",
plural: "Scans",
},
admin: { defaultColumns: [fields.filename, fields.updatedAt], hidden: true },
admin: {
defaultColumns: [fields.filename, fields.updatedAt],
hidden: shownOnlyToAdmin,
},
upload: {
imageSizes: [
{

View File

@ -1,13 +1,118 @@
import { CollectionGroups, Collections } from "../../constants";
import { componentField } from "../../fields/componentField/componentField";
import { imageField } from "../../fields/imageField/imageField";
import { rowField } from "../../fields/rowField/rowField";
import { tagsField } from "../../fields/tagsField/tagsField";
import { translatedFields } from "../../fields/translatedFields/translatedFields";
import { buildCollectionConfig } from "../../utils/collectionConfig";
import { createEditor } from "../../utils/editor";
const fields = {
filename: "filename",
mimeType: "mimeType",
filesize: "filesize",
updatedAt: "updatedAt",
translations: "translations",
translationsTitle: "title",
translationsDescription: "description",
translationsSubfile: "subfile",
thumbnail: "thumbnail",
duration: "duration",
tags: "tags",
platform: "platform",
platformChannel: "channel",
platformViews: "views",
platformPublishedDate: "publishedDate",
platformUrl: "url",
platformLikes: "likes",
platformDislikes: "dislikes",
};
export const Videos = buildCollectionConfig({
slug: Collections.Videos,
labels: { singular: "Video", plural: "Videos" },
admin: { group: CollectionGroups.Media },
defaultSort: fields.updatedAt,
admin: {
group: CollectionGroups.Media,
defaultColumns: [
fields.filename,
fields.mimeType,
fields.filesize,
fields.translations,
fields.updatedAt,
],
},
upload: {
mimeTypes: ["video/*"],
disableLocalStorage: true,
},
fields: [],
fields: [
rowField([
{ name: fields.duration, type: "number", min: 0, required: true },
imageField({
name: fields.thumbnail,
relationTo: Collections.MediaThumbnails,
required: true,
}),
]),
translatedFields({
name: fields.translations,
admin: { useAsTitle: fields.translationsTitle },
required: true,
minRows: 1,
fields: [
{ name: fields.translationsTitle, type: "text", required: true },
{
name: fields.translationsDescription,
type: "richText",
editor: createEditor({ inlines: true, lists: true, links: true }),
},
{
name: fields.translationsSubfile,
type: "upload",
relationTo: Collections.VideosSubtitles,
admin: { description: "The subtitle file needs to follow the WebVTT file format (.vtt)" },
},
],
}),
tagsField({ name: fields.tags }),
componentField({
name: fields.platform,
admin: {
description:
"If the video comes from a platform (e.g: YouTube, NicoNico, Tumblr...),\
add additional informations here.",
},
fields: [
{
name: fields.platformChannel,
type: "relationship",
relationTo: Collections.VideosChannels,
required: true,
},
rowField([
{ name: fields.platformViews, type: "number", min: 0 },
{ name: fields.platformLikes, type: "number", min: 0 },
{ name: fields.platformDislikes, type: "number", min: 0 },
]),
{
name: fields.platformUrl,
type: "text",
required: true,
admin: {
description:
"If the video comes from a platform (e.g: YouTube, NicoNico, Tumblr...), paste the URL here.",
},
},
{
name: fields.platformPublishedDate,
required: true,
type: "date",
admin: {
date: { pickerAppearance: "dayOnly", displayFormat: "yyyy-MM-dd" },
},
},
],
}),
],
});

View File

@ -0,0 +1,41 @@
import { CollectionConfig } from "payload/types";
import { CollectionGroups, Collections } from "../../constants";
import { backPropagationField } from "../../fields/backPropagationField/backPropagationField";
import { rowField } from "../../fields/rowField/rowField";
import { buildCollectionConfig } from "../../utils/collectionConfig";
const fields = {
url: "url",
title: "title",
subscribers: "subscribers",
videos: "videos",
} as const satisfies Record<string, string>;
export const VideosChannels: CollectionConfig = buildCollectionConfig({
slug: Collections.VideosChannels,
labels: {
singular: "Videos Channel",
plural: "Videos Channels",
},
defaultSort: fields.title,
admin: {
useAsTitle: fields.title,
defaultColumns: [fields.url, fields.title, fields.subscribers, fields.videos],
group: CollectionGroups.Media,
disableDuplicate: true,
},
timestamps: false,
fields: [
{ name: fields.url, type: "text", required: true, unique: true },
rowField([
{ name: fields.title, type: "text", required: true },
{ name: fields.subscribers, type: "number" },
]),
backPropagationField({
name: fields.videos,
relationTo: Collections.Videos,
hasMany: true,
where: ({ id }) => ({ "platform.channel": { equals: id } }),
}),
],
});

View File

@ -0,0 +1,15 @@
import { shownOnlyToAdmin } from "../../accesses/collections/shownOnlyToAdmin";
import { CollectionGroups, Collections } from "../../constants";
import { buildCollectionConfig } from "../../utils/collectionConfig";
export const VideosSubtitles = buildCollectionConfig({
slug: Collections.VideosSubtitles,
labels: { singular: "Video Subtitle", plural: "Videos Subtitles" },
admin: { group: CollectionGroups.Media, disableDuplicate: true, hidden: shownOnlyToAdmin },
upload: {
staticDir: `../uploads/${Collections.VideosSubtitles}`,
mimeTypes: ["text/*"],
},
timestamps: false,
fields: [],
});

View File

@ -0,0 +1,33 @@
import { shownOnlyToAdmin } from "../../accesses/collections/shownOnlyToAdmin";
import { Collections } from "../../constants";
import { buildImageCollectionConfig } from "../../utils/imageCollectionConfig";
const fields = {
filename: "filename",
mimeType: "mimeType",
filesize: "filesize",
updatedAt: "updatedAt",
} as const satisfies Record<string, string>;
export const MediaThumbnails = buildImageCollectionConfig({
slug: Collections.MediaThumbnails,
labels: {
singular: "Media Thumbnail",
plural: "Media Thumbnails",
},
admin: { defaultColumns: [fields.filename, fields.updatedAt], hidden: shownOnlyToAdmin },
upload: {
imageSizes: [
{
name: "og",
height: 750,
width: 1125,
formatOptions: {
format: "jpg",
options: { progressive: true, mozjpeg: true, compressionLevel: 9, quality: 60 },
},
},
],
},
fields: [],
});

View File

@ -3,11 +3,11 @@ import type { BreakBlock, Image, SectionBlock, TranscriptBlock } from "./types/c
// END MOCKING SECTION
export enum Collections {
Audios = "audios",
ChronologyEvents = "chronology-events",
Currencies = "currencies",
Languages = "languages",
Pages = "pages",
PagesThumbnails = "pages-thumbnails",
Recorders = "recorders",
Folders = "folders",
Tags = "tags",
@ -18,6 +18,9 @@ export enum Collections {
GenericContents = "generic-contents",
HomeFolders = "home-folders",
Videos = "videos",
VideosSubtitles = "videos-subtitles",
VideosChannels = "videos-channels",
MediaThumbnails = "media-thumbnails",
Scans = "scans",
}

View File

@ -3,6 +3,7 @@ import { mongooseAdapter } from "@payloadcms/db-mongodb";
import { cloudStorage } from "@payloadcms/plugin-cloud-storage";
import path from "path";
import { buildConfig } from "payload/config";
import { Audios } from "./collections/Audios/Audios";
import { ChronologyEvents } from "./collections/ChronologyEvents/ChronologyEvents";
import { Collectibles } from "./collections/Collectibles/Collectibles";
import { Currencies } from "./collections/Currencies/Currencies";
@ -11,7 +12,6 @@ import { GenericContents } from "./collections/GenericContents/GenericContents";
import { HomeFolders } from "./collections/HomeFolders/HomeFolders";
import { Images } from "./collections/Images/Images";
import { Languages } from "./collections/Languages/Languages";
import { Notes } from "./collections/Notes/Notes";
import { Pages } from "./collections/Pages/Pages";
import { Recorders } from "./collections/Recorders/Recorders";
import { Scans } from "./collections/Scans/Scans";
@ -20,6 +20,7 @@ import { TagsGroups } from "./collections/TagsGroups/TagsGroups";
import { Videos } from "./collections/Videos/Videos";
import { VideosChannels } from "./collections/VideosChannels/VideosChannels";
import { VideosSubtitles } from "./collections/VideosSubtitles/VideosSubtitles";
import { MediaThumbnails } from "./collections/VideosThumbnails/VideosThumbnails";
import { Wordings } from "./collections/Wordings/Wordings";
import { Icon } from "./components/Icon";
import { Logo } from "./components/Logo";
@ -27,6 +28,14 @@ import { Collections } from "./constants";
import { ftpAdapter } from "./plugins/ftpAdapter";
import { createEditor } from "./utils/editor";
const configuredFtpAdapter = ftpAdapter({
host: process.env.FTP_HOST ?? "",
user: process.env.FTP_USER ?? "",
password: process.env.FTP_PASSWORD ?? "",
secure: false,
endpoint: process.env.FTP_BASE_URL ?? "",
});
export default buildConfig({
serverURL: process.env.PAYLOAD_URI,
admin: {
@ -48,6 +57,8 @@ export default buildConfig({
ChronologyEvents,
Images,
Audios,
MediaThumbnails,
Videos,
VideosSubtitles,
VideosChannels,
@ -76,13 +87,12 @@ export default buildConfig({
cloudStorage({
collections: {
[Collections.Videos]: {
adapter: ftpAdapter({
host: process.env.FTP_HOST ?? "",
user: process.env.FTP_USER ?? "",
password: process.env.FTP_PASSWORD ?? "",
secure: false,
endpoint: process.env.FTP_BASE_URL ?? "",
}),
adapter: configuredFtpAdapter,
disableLocalStorage: true,
disablePayloadAccessControl: true,
},
[Collections.Audios]: {
adapter: configuredFtpAdapter,
disableLocalStorage: true,
disablePayloadAccessControl: true,
},

View File

@ -22,9 +22,12 @@ export interface Config {
collectibles: Collectible;
folders: Folder;
"chronology-events": ChronologyEvent;
notes: Note;
images: Image;
audios: Audio;
"media-thumbnails": MediaThumbnail;
videos: Video;
"videos-subtitles": VideoSubtitle;
"videos-channels": VideosChannel;
scans: Scan;
tags: Tag;
"tags-groups": TagsGroup;
@ -37,7 +40,7 @@ export interface Config {
"payload-migrations": PayloadMigration;
};
globals: {
"home-folders": HomeFolder;
config: Config1;
};
}
/**
@ -106,6 +109,29 @@ export interface Page {
*/
export interface Image {
id: string;
translations?:
| {
language: string | Language;
title: string;
description?: {
root: {
type: string;
children: {
type: string;
version: number;
[k: string]: unknown;
}[];
direction: ("ltr" | "rtl") | null;
format: "left" | "start" | "center" | "right" | "end" | "justify" | "";
indent: number;
version: number;
};
[k: string]: unknown;
} | null;
id?: string | null;
}[]
| null;
tags?: (string | Tag)[] | null;
updatedAt: string;
createdAt: string;
url?: string | null;
@ -133,6 +159,14 @@ export interface Image {
};
};
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "languages".
*/
export interface Language {
id: string;
name: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "tags".
@ -150,14 +184,6 @@ export interface Tag {
updatedAt: string;
createdAt: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "languages".
*/
export interface Language {
id: string;
name: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "tags-groups".
@ -246,6 +272,18 @@ export interface Folder {
relationTo: "pages";
value: string | Page;
}
| {
relationTo: "videos";
value: string | Video;
}
| {
relationTo: "images";
value: string | Image;
}
| {
relationTo: "audios";
value: string | Audio;
}
)[]
| null;
updatedAt: string;
@ -383,6 +421,14 @@ export interface Collectible {
| {
relationTo: "generic-contents";
value: string | GenericContent;
}
| {
relationTo: "audios";
value: string | Audio;
}
| {
relationTo: "videos";
value: string | Video;
};
range?:
| (
@ -489,6 +535,150 @@ export interface GenericContent {
updatedAt: string;
createdAt: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "audios".
*/
export interface Audio {
id: string;
duration: number;
thumbnail?: string | MediaThumbnail | null;
translations: {
language: string | Language;
title: string;
description?: {
root: {
type: string;
children: {
type: string;
version: number;
[k: string]: unknown;
}[];
direction: ("ltr" | "rtl") | null;
format: "left" | "start" | "center" | "right" | "end" | "justify" | "";
indent: number;
version: number;
};
[k: string]: unknown;
} | null;
subfile?: string | VideoSubtitle | null;
id?: string | null;
}[];
tags?: (string | Tag)[] | null;
updatedAt: string;
createdAt: string;
url?: string | null;
filename?: string | null;
mimeType?: string | null;
filesize?: number | null;
width?: number | null;
height?: number | null;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "media-thumbnails".
*/
export interface MediaThumbnail {
id: string;
updatedAt: string;
createdAt: string;
url?: string | null;
filename?: string | null;
mimeType?: string | null;
filesize?: number | null;
width?: number | null;
height?: number | null;
sizes?: {
thumb?: {
url?: string | null;
width?: number | null;
height?: number | null;
mimeType?: string | null;
filesize?: number | null;
filename?: string | null;
};
og?: {
url?: string | null;
width?: number | null;
height?: number | null;
mimeType?: string | null;
filesize?: number | null;
filename?: string | null;
};
};
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "videos-subtitles".
*/
export interface VideoSubtitle {
id: string;
url?: string | null;
filename?: string | null;
mimeType?: string | null;
filesize?: number | null;
width?: number | null;
height?: number | null;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "videos".
*/
export interface Video {
id: string;
duration: number;
thumbnail: string | MediaThumbnail;
translations: {
language: string | Language;
title: string;
description?: {
root: {
type: string;
children: {
type: string;
version: number;
[k: string]: unknown;
}[];
direction: ("ltr" | "rtl") | null;
format: "left" | "start" | "center" | "right" | "end" | "justify" | "";
indent: number;
version: number;
};
[k: string]: unknown;
} | null;
subfile?: string | VideoSubtitle | null;
id?: string | null;
}[];
tags?: (string | Tag)[] | null;
platformEnabled?: boolean | null;
platform?: {
channel: string | VideosChannel;
views?: number | null;
likes?: number | null;
dislikes?: number | null;
url: string;
publishedDate: string;
};
updatedAt: string;
createdAt: string;
url?: string | null;
filename?: string | null;
mimeType?: string | null;
filesize?: number | null;
width?: number | null;
height?: number | null;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "videos-channels".
*/
export interface VideosChannel {
id: string;
url: string;
title: string;
subscribers?: number | null;
videos?: (string | Video)[] | null;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "chronology-events".
@ -605,45 +795,6 @@ export interface PageBlock {
blockName?: string | null;
blockType: "pageBlock";
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "notes".
*/
export interface Note {
id: string;
note: {
root: {
type: string;
children: {
type: string;
version: number;
[k: string]: unknown;
}[];
direction: ("ltr" | "rtl") | null;
format: "left" | "start" | "center" | "right" | "end" | "justify" | "";
indent: number;
version: number;
};
[k: string]: unknown;
};
updatedAt: string;
createdAt: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "videos".
*/
export interface Video {
id: string;
updatedAt: string;
createdAt: string;
url?: string | null;
filename?: string | null;
mimeType?: string | null;
filesize?: number | null;
width?: number | null;
height?: number | null;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "wordings".
@ -691,9 +842,9 @@ export interface PayloadMigration {
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "home-folders".
* via the `definition` "config".
*/
export interface HomeFolder {
export interface Config1 {
id: string;
folders?:
| {