From cc5a56bc33394362267e91c313d3981795e760f2 Mon Sep 17 00:00:00 2001 From: Wesley Luyten Date: Fri, 11 Oct 2024 16:14:00 -0500 Subject: [PATCH] fix: add setup config decoupled from Next --- .../default-provider/app/api/video/route.js | 3 +- examples/default-provider/next-video.mjs | 11 +++++ examples/default-provider/next.config.mjs | 12 +---- src/assets.ts | 2 +- src/config.ts | 22 +++++---- src/process.ts | 3 +- src/request-handler.ts | 46 +++++++++++++------ src/setup-next-video.ts | 20 ++++++++ src/with-next-video.ts | 8 +--- 9 files changed, 83 insertions(+), 44 deletions(-) create mode 100644 examples/default-provider/next-video.mjs create mode 100644 src/setup-next-video.ts diff --git a/examples/default-provider/app/api/video/route.js b/examples/default-provider/app/api/video/route.js index 3109cda..f1e895f 100644 --- a/examples/default-provider/app/api/video/route.js +++ b/examples/default-provider/app/api/video/route.js @@ -1 +1,2 @@ -export { GET } from 'next-video/request-handler'; +//export { GET } from 'next-video/request-handler'; +export { GET, POST } from '@/next-video'; diff --git a/examples/default-provider/next-video.mjs b/examples/default-provider/next-video.mjs new file mode 100644 index 0000000..6f52de2 --- /dev/null +++ b/examples/default-provider/next-video.mjs @@ -0,0 +1,11 @@ +import { NextVideo } from 'next-video/process'; +import { readFile } from 'fs/promises'; + +export const { GET, POST, handler, withNextVideo } = NextVideo({ + loadAsset: async function (assetPath) { + console.warn(99, assetPath); + const file = await readFile(assetPath); + const asset = JSON.parse(file.toString()); + return asset; + }, +}); diff --git a/examples/default-provider/next.config.mjs b/examples/default-provider/next.config.mjs index 77613a7..2c27711 100644 --- a/examples/default-provider/next.config.mjs +++ b/examples/default-provider/next.config.mjs @@ -1,5 +1,4 @@ -import { withNextVideo } from 'next-video/process'; -import { readFile } from 'node:fs/promises'; +import { withNextVideo } from './next-video.mjs'; /** @type {import('next').NextConfig} */ const nextConfig = (phase, { defaultConfig }) => { @@ -8,14 +7,7 @@ const nextConfig = (phase, { defaultConfig }) => { }; }; -export default withNextVideo(nextConfig, { - loadAsset: async function (assetPath) { - console.warn(99, assetPath); - const file = await readFile(assetPath); - const asset = JSON.parse(file.toString()); - return asset; - }, -}); +export default withNextVideo(nextConfig); // Amazon S3 example // export default withNextVideo(nextConfig, { diff --git a/src/assets.ts b/src/assets.ts index f62fc15..8a7e3d9 100644 --- a/src/assets.ts +++ b/src/assets.ts @@ -1,6 +1,6 @@ import * as path from 'node:path'; import { cwd } from 'node:process'; -import { stat, readFile, writeFile, mkdir } from 'node:fs/promises'; +import { stat } from 'node:fs/promises'; import { getVideoConfig } from './config.js'; import { deepMerge, camelCase, isRemote, toSafePath } from './utils/utils.js'; import * as transformers from './providers/transformers.js'; diff --git a/src/config.ts b/src/config.ts index 3f747f6..89197de 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,14 +1,10 @@ import { cwd } from 'node:process'; import path from 'node:path'; import { pathToFileURL } from 'node:url'; -import nextConfig from 'next/config.js'; import type { NextConfig } from 'next'; import { Asset } from './assets'; import { mkdir, readFile, writeFile } from 'node:fs/promises'; -// @ts-ignore -const getConfig = nextConfig.default; - /** * Video configurations */ @@ -98,25 +94,31 @@ export const videoConfigDefault: VideoConfigComplete = { } }; +let videoConfigComplete: VideoConfigComplete = videoConfigDefault; + +export function setVideoConfig(videoConfig?: VideoConfig): VideoConfigComplete { + videoConfigComplete = Object.assign({}, videoConfigDefault, videoConfig); + return videoConfigComplete; +} + /** * The video config is set in `next.config.js` and passed to the `withNextVideo` function. * The video config is then stored in `serverRuntimeConfig`. */ export async function getVideoConfig(): Promise { - let nextConfig: NextConfig | undefined = getConfig(); - if (!nextConfig?.serverRuntimeConfig?.nextVideo) { + let videoConfig: NextConfig | undefined = videoConfigComplete; + if (!videoConfig) { try { - nextConfig = await importConfig('next.config.js'); + await importConfig('next.config.js'); } catch (err) { try { - nextConfig = await importConfig('next.config.mjs'); + await importConfig('next.config.mjs'); } catch { console.error('Failed to load next-video config.'); } } } - - return nextConfig?.serverRuntimeConfig?.nextVideo; + return videoConfigComplete; } async function importConfig(file: string) { diff --git a/src/process.ts b/src/process.ts index b40eae6..c557cd5 100644 --- a/src/process.ts +++ b/src/process.ts @@ -2,6 +2,7 @@ import { videoHandler, callHandler } from './video-handler.js'; import { uploadLocalFile } from './handlers/local-upload.js'; import { uploadRequestedFile } from './handlers/api-request.js'; import log from './utils/logger.js'; +import { NextVideo } from './setup-next-video.js'; import { withNextVideo } from './with-next-video.js'; try { @@ -17,4 +18,4 @@ try { console.error(err); } -export { videoHandler, withNextVideo, callHandler }; +export { videoHandler, callHandler, NextVideo, withNextVideo }; diff --git a/src/request-handler.ts b/src/request-handler.ts index 94391ca..e9da36f 100644 --- a/src/request-handler.ts +++ b/src/request-handler.ts @@ -2,24 +2,31 @@ import type { NextApiRequest, NextApiResponse } from 'next'; import { callHandler } from './process.js'; import { createAsset, getAsset } from './assets.js'; import { getVideoConfig } from './config.js'; -import { isRemote } from './utils/utils.js'; // App Router export async function GET(request: Request) { const { searchParams } = new URL(request.url) const url = searchParams.get('url'); - const { status, data } = await handleRequest(url); + const { status, data } = await getRequest(url); + // @ts-ignore - Response.json() is only valid from TypeScript 5.2 + return Response.json(data, { status }); +} + +// App Router +export async function POST(request: Request) { + const { url } = await request.json(); + const { status, data } = await postRequest(url); // @ts-ignore - Response.json() is only valid from TypeScript 5.2 return Response.json(data, { status }); } // Pages Router export default async function handler(req: NextApiRequest, res: NextApiResponse) { - const { status, data } = await handleRequest(String(req.query.url)); + const { status, data } = await getRequest(String(req.query.url)); res.status(status).json(data); } -async function handleRequest(url?: string | null) { +async function getRequest(url?: string | null) { if (!url) { return { status: 400, @@ -27,28 +34,37 @@ async function handleRequest(url?: string | null) { }; } - if (!isRemote(url)) { - // todo: handle local files via string src + let asset; + try { + asset = await getAsset(url); + } catch { + return { status: 404, data: { error: 'asset not found' } }; + } + + return { status: 200, data: asset }; +} + +async function postRequest(url?: string | null) { + if (!url) { return { status: 400, - data: { error: 'local files should be imported as a module' } + data: { error: 'url parameter is required' } }; } let asset; try { - asset = await getAsset(url); - } catch { - // todo: does this require auth? asset = await createAsset(url); - if (asset) { - const videoConfig = await getVideoConfig(); - await callHandler('request.video.added', asset, videoConfig); + if (!asset) { + return { status: 500, data: { error: 'could not create asset' } }; } + const videoConfig = await getVideoConfig(); + await callHandler('request.video.added', asset, videoConfig); + return { status: 200, data: asset }; + } catch { + return { status: 500, data: { error: 'could not create asset' } }; } - - return { status: 200, data: asset }; } diff --git a/src/setup-next-video.ts b/src/setup-next-video.ts new file mode 100644 index 0000000..d87c352 --- /dev/null +++ b/src/setup-next-video.ts @@ -0,0 +1,20 @@ +import { setVideoConfig } from './config.js'; +import handler, { GET, POST } from './request-handler.js'; +import { withNextVideo as withNextVideoInternal } from './with-next-video.js'; +import type { VideoConfig } from './config.js'; +import type { NextConfig } from 'next'; + +export function NextVideo(config?: VideoConfig) { + setVideoConfig(config); + + const withNextVideo = (nextConfig: NextConfig) => { + return withNextVideoInternal(nextConfig, config); + }; + + return { + GET, + POST, + handler, + withNextVideo, + }; +} diff --git a/src/with-next-video.ts b/src/with-next-video.ts index 3210787..c641388 100644 --- a/src/with-next-video.ts +++ b/src/with-next-video.ts @@ -3,11 +3,11 @@ import { join, dirname } from 'node:path'; import fs from 'node:fs/promises'; import { env } from 'node:process'; import { fileURLToPath } from 'node:url'; -import { videoConfigDefault } from './config.js'; +import { setVideoConfig } from './config.js'; import type { VideoConfig } from './config.js'; export function withNextVideo(nextConfig: any, videoConfig?: VideoConfig) { - const videoConfigComplete = Object.assign({}, videoConfigDefault, videoConfig); + const videoConfigComplete = setVideoConfig(videoConfig); const { path, folder, provider } = videoConfigComplete; // env VARS have to be set before the async function return!! @@ -49,10 +49,6 @@ export function withNextVideo(nextConfig: any, videoConfig?: VideoConfig) { return Object.assign({}, nextConfig, { experimental, - serverRuntimeConfig: { - ...nextConfig.serverRuntimeConfig, - nextVideo: videoConfigComplete, - }, webpack(config: any, options: any) { if (!options.defaultLoaders) { throw new Error(