diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 4c2c5724..603cf1d6 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -31,6 +31,7 @@ jobs: with: fetch-depth: 0 token: ${{secrets.PUBLISH_TOKEN}} + submodules: true - uses: actions/setup-node@v3 with: node-version: 18 diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..a8c87a9e --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "packages/create/examples"] + path = packages/create/examples + url = https://github.com/redotvideo/examples diff --git a/packages/create/examples b/packages/create/examples new file mode 160000 index 00000000..ca265692 --- /dev/null +++ b/packages/create/examples @@ -0,0 +1 @@ +Subproject commit ca265692c230b64705c88a999b2361c90e569021 diff --git a/packages/create/index.js b/packages/create/index.js index df3cd5ce..febf6f10 100755 --- a/packages/create/index.js +++ b/packages/create/index.js @@ -7,53 +7,21 @@ import {fileURLToPath} from 'node:url'; import path from 'path'; import prompts from 'prompts'; -const FILES_TO_MODIFY = { - gitignore: '.gitignore', - '.gitkeep': false, -}; - -const MANIFEST = JSON.parse( - fs.readFileSync( - path.resolve(fileURLToPath(import.meta.url), '../package.json'), - 'utf-8', - ), -); - -const PLUGINS = { - core: { - package: '@revideo/vite-plugin', - variable: 'motionCanvas', - options: response => - response.language === 'js' ? `{project: './src/project.js'}` : '', - }, -}; - -(async () => { +const templates = [ + 'default', + 'avatar-with-background', + 'google-cloud-run', + 'google-cloud-run-parallelized', + 'stitching-videos', + 'youtube-shorts', +]; + +async function run() { const options = minimist(process.argv.slice(2)); - if (options.plugins !== undefined) { - if (typeof options.plugins === 'string') { - options.plugins = options.plugins.split(','); - } - - if (!Array.isArray(options.plugins)) { - options.plugins = [options.plugins]; - } - - const plugins = ['core']; - for (const plugin of options.plugins) { - if (plugin === 'core') continue; - if (!(plugin in PLUGINS)) { - console.log(kleur.yellow(`! Unknown plugin "${plugin}".\n`)); - continue; - } - plugins.push(plugin); - } - - options.plugins = plugins; - } prompts.override(options); const response = await prompts([ + // Prompt for project name { type: 'text', name: 'name', @@ -64,6 +32,7 @@ const PLUGINS = { ? true : 'Project name must be a valid npm package name.', }, + // Prompt for project path { type: 'text', name: 'path', @@ -90,42 +59,48 @@ const PLUGINS = { }, format: value => path.resolve(value), }, + // Prompt for which example to scaffold + { + type: 'select', + name: 'starter', + message: 'Choose a starter template', + + choices: [ + ...templates.map(template => ({title: template, value: template})), + ], + }, ]); + // Abort if the user didn't provide a project name if (!response.path) { console.log(kleur.red('× Scaffolding aborted by the user.\n')); return; } - const plugins = [PLUGINS.core]; + // Clone files const templateDir = path.resolve( fileURLToPath(import.meta.url), '..', - `template-2d-ts`, + `examples/${response.starter}`, ); copyDirectory(templateDir, response.path); - createConfig(response, plugins); + createConfig(response); - const manifest = JSON.parse( - fs.readFileSync(path.join(templateDir, `package.json`), 'utf-8'), - ); - manifest.name = response.name; - manifest.dependencies ??= {}; - for (const data of plugins) { - if (data.version) { - manifest.dependencies[data.package] = data.version; - } - } - cloneVersions(manifest.dependencies); - - if (manifest.devDependencies) { - cloneVersions(manifest.devDependencies); + // Read package.json and modify name + try { + const manifest = JSON.parse( + fs.readFileSync(path.join(templateDir, `package.json`), 'utf-8'), + ); + manifest.name = response.name; + fs.writeFileSync( + path.join(response.path, 'package.json'), + JSON.stringify(manifest, undefined, 2), + ); + } catch (e) { + // Example doesn't have a package.json file } - fs.writeFileSync( - path.join(response.path, 'package.json'), - JSON.stringify(manifest, undefined, 2), - ); + // Tell user that the process is complete const manager = getPackageManager(); console.log(kleur.green('\n√ Scaffolding complete. You can now run:')); if (response.path !== process.cwd()) { @@ -142,7 +117,7 @@ const PLUGINS = { console.log(` ${boldManager} start`); } console.log(); -})(); +} function isValidPackageName(projectName) { return /^(?:@[a-z0-9-*~][a-z0-9-*._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/.test( @@ -153,13 +128,8 @@ function isValidPackageName(projectName) { function copyDirectory(src, dest) { fs.mkdirSync(dest, {recursive: true}); for (const file of fs.readdirSync(src)) { - let target = file; - if (file in FILES_TO_MODIFY) { - if (FILES_TO_MODIFY[file] === false) continue; - target = FILES_TO_MODIFY[file]; - } const srcFile = path.resolve(src, file); - const destFile = path.resolve(dest, target); + const destFile = path.resolve(dest, file); copy(srcFile, destFile); } } @@ -173,27 +143,17 @@ function copy(src, dest) { } } -function createConfig(response, selectedPlugins) { - const imports = []; - const plugins = []; - for (const data of selectedPlugins) { - imports.push(`import ${data.variable} from '${data.package}';\n`); - plugins.push(`${data.variable}(${data.options?.(response) ?? ''}),`); - } - +function createConfig(response) { const configFile = path.resolve(response.path, `vite.config.ts`); fs.writeFileSync( configFile, - `import {defineConfig} from 'vite'; -${imports.join('')} - -export default defineConfig({ - plugins: [ - ${plugins.join('\n ')} - ], -}); -`, + `import motionCanvas from '@revideo/vite-plugin'; + import {defineConfig} from 'vite'; + + export default defineConfig({ + plugins: [motionCanvas()], + });`, ); } @@ -202,13 +162,4 @@ function getPackageManager() { return ua?.split(' ')[0].split('/')[0] ?? 'npm'; } -function cloneVersions(versions) { - for (const dependency in versions) { - if ( - dependency.startsWith('@revideo') && - MANIFEST.devDependencies[dependency] - ) { - versions[dependency] = MANIFEST.devDependencies[dependency]; - } - } -} +void run(); diff --git a/packages/create/package.json b/packages/create/package.json index 4d9cb736..7c3ad084 100644 --- a/packages/create/package.json +++ b/packages/create/package.json @@ -13,7 +13,7 @@ }, "files": [ "index.js", - "template-*" + "examples" ], "repository": { "type": "git", diff --git a/packages/create/template-2d-ts/package.json b/packages/create/template-2d-ts/package.json deleted file mode 100644 index 3a91a731..00000000 --- a/packages/create/template-2d-ts/package.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "name": "2d-starter", - "private": true, - "version": "0.0.0", - "scripts": { - "start": "vite", - "serve": "vite", - "build": "tsc && vite build", - "render": "tsc && node dist/render.js" - }, - "dependencies": { - "@revideo/core": "^0.3.4", - "@revideo/2d": "^0.3.4", - "@revideo/renderer": "^0.3.4", - "@revideo/vite-plugin": "^0.3.4", - "@revideo/ffmpeg": "^0.3.4" - }, - "devDependencies": { - "@revideo/ui": "^0.3.4", - "@revideo/cli": "^0.3.4", - "typescript": "^5.2.2", - "vite": "^4.5" - } -} diff --git a/packages/create/template-2d-ts/public/.gitkeep b/packages/create/template-2d-ts/public/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/packages/create/template-2d-ts/src/project.meta b/packages/create/template-2d-ts/src/project.meta deleted file mode 100644 index 18e0a42e..00000000 --- a/packages/create/template-2d-ts/src/project.meta +++ /dev/null @@ -1,31 +0,0 @@ -{ - "version": 0, - "shared": { - "background": null, - "range": [ - 0, - null - ], - "size": { - "x": 1920, - "y": 1080 - }, - "audioOffset": 0 - }, - "preview": { - "fps": 30, - "resolutionScale": 1 - }, - "rendering": { - "fps": 30, - "resolutionScale": 1, - "colorSpace": "srgb", - "exporter": { - "name": "@revideo/ffmpeg", - "options": { - "fastStart": true, - "includeAudio": true - } - } - } -} \ No newline at end of file diff --git a/packages/create/template-2d-ts/src/project.ts b/packages/create/template-2d-ts/src/project.ts deleted file mode 100644 index ab05fd7e..00000000 --- a/packages/create/template-2d-ts/src/project.ts +++ /dev/null @@ -1,7 +0,0 @@ -import {makeProject} from '@revideo/core'; - -import example from './scenes/example?scene'; - -export default makeProject({ - scenes: [example], -}); diff --git a/packages/create/template-2d-ts/src/render.ts b/packages/create/template-2d-ts/src/render.ts deleted file mode 100644 index 2769fab8..00000000 --- a/packages/create/template-2d-ts/src/render.ts +++ /dev/null @@ -1,17 +0,0 @@ -import {renderVideo} from '@revideo/renderer'; - -async function render() { - console.log('Rendering video...'); - - // This is the main function that renders the video - const file = await renderVideo( - './vite.config.ts', - {fill: 'orange'}, - () => {}, - {logProgress: true}, - ); - - console.log(`Rendered video to ${file}`); -} - -render(); diff --git a/packages/create/template-2d-ts/src/revideo.d.ts b/packages/create/template-2d-ts/src/revideo.d.ts deleted file mode 100644 index 7ec2e111..00000000 --- a/packages/create/template-2d-ts/src/revideo.d.ts +++ /dev/null @@ -1 +0,0 @@ -/// diff --git a/packages/create/template-2d-ts/src/scenes/example.tsx b/packages/create/template-2d-ts/src/scenes/example.tsx deleted file mode 100644 index 95f3c71e..00000000 --- a/packages/create/template-2d-ts/src/scenes/example.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import {Audio, Img, Video, makeScene2D} from '@revideo/2d'; -import {all, chain, createRef, waitFor} from '@revideo/core'; - -export default makeScene2D(function* (view) { - const logoRef = createRef(); - - yield view.add( - <> -