diff --git a/apps/website/blog/2019-05-28-first-blog-post.md b/apps/website/blog/2019-05-28-first-blog-post.md
deleted file mode 100644
index 02f3f81..0000000
--- a/apps/website/blog/2019-05-28-first-blog-post.md
+++ /dev/null
@@ -1,12 +0,0 @@
----
-slug: first-blog-post
-title: First Blog Post
-authors:
- name: Gao Wei
- title: Docusaurus Core Team
- url: https://github.com/wgao19
- image_url: https://github.com/wgao19.png
-tags: [hola, docusaurus]
----
-
-Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
diff --git a/apps/website/blog/2019-05-29-long-blog-post.md b/apps/website/blog/2019-05-29-long-blog-post.md
deleted file mode 100644
index 26ffb1b..0000000
--- a/apps/website/blog/2019-05-29-long-blog-post.md
+++ /dev/null
@@ -1,44 +0,0 @@
----
-slug: long-blog-post
-title: Long Blog Post
-authors: endi
-tags: [hello, docusaurus]
----
-
-This is the summary of a very long blog post,
-
-Use a `` comment to limit blog post size in the list view.
-
-
-
-Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
-
-Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
-
-Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
-
-Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
-
-Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
-
-Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
-
-Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
-
-Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
-
-Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
-
-Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
-
-Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
-
-Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
-
-Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
-
-Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
-
-Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
-
-Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
diff --git a/apps/website/blog/2021-08-01-mdx-blog-post.mdx b/apps/website/blog/2021-08-01-mdx-blog-post.mdx
deleted file mode 100644
index c04ebe3..0000000
--- a/apps/website/blog/2021-08-01-mdx-blog-post.mdx
+++ /dev/null
@@ -1,20 +0,0 @@
----
-slug: mdx-blog-post
-title: MDX Blog Post
-authors: [slorber]
-tags: [docusaurus]
----
-
-Blog posts support [Docusaurus Markdown features](https://docusaurus.io/docs/markdown-features), such as [MDX](https://mdxjs.com/).
-
-:::tip
-
-Use the power of React to create interactive blog posts.
-
-```js
-
-```
-
-
-
-:::
diff --git a/apps/website/blog/2021-08-26-welcome/docusaurus-plushie-banner.jpeg b/apps/website/blog/2021-08-26-welcome/docusaurus-plushie-banner.jpeg
deleted file mode 100644
index 11bda09..0000000
Binary files a/apps/website/blog/2021-08-26-welcome/docusaurus-plushie-banner.jpeg and /dev/null differ
diff --git a/apps/website/blog/2021-08-26-welcome/index.md b/apps/website/blog/2021-08-26-welcome/index.md
deleted file mode 100644
index 9455168..0000000
--- a/apps/website/blog/2021-08-26-welcome/index.md
+++ /dev/null
@@ -1,25 +0,0 @@
----
-slug: welcome
-title: Welcome
-authors: [slorber, yangshun]
-tags: [facebook, hello, docusaurus]
----
-
-[Docusaurus blogging features](https://docusaurus.io/docs/blog) are powered by the [blog plugin](https://docusaurus.io/docs/api/plugins/@docusaurus/plugin-content-blog).
-
-Simply add Markdown files (or folders) to the `blog` directory.
-
-Regular blog authors can be added to `authors.yml`.
-
-The blog post date can be extracted from filenames, such as:
-
-- `2019-05-30-welcome.md`
-- `2019-05-30-welcome/index.md`
-
-A blog post folder can be convenient to co-locate blog post images:
-
-
-
-The blog supports tags as well!
-
-**And if you don't want a blog**: just delete this directory, and use `blog: false` in your Docusaurus config.
diff --git a/apps/website/blog/2022-10-8-release-ffmpeg.wasm-0.12.0.mdx b/apps/website/blog/2022-10-8-release-ffmpeg.wasm-0.12.0.mdx
new file mode 100644
index 0000000..af492e4
--- /dev/null
+++ b/apps/website/blog/2022-10-8-release-ffmpeg.wasm-0.12.0.mdx
@@ -0,0 +1,10 @@
+---
+slug: release-ffmpeg.wasm-0.12.0
+title: Release ffmpeg.wasm 0.12.0
+authors: [jeromewu]
+tags: [ffmpeg.wasm]
+---
+
+See what are the differences.
+
+
diff --git a/apps/website/blog/authors.yml b/apps/website/blog/authors.yml
index bcb2991..3fca26a 100644
--- a/apps/website/blog/authors.yml
+++ b/apps/website/blog/authors.yml
@@ -1,17 +1,5 @@
-endi:
- name: Endilie Yacop Sucipto
- title: Maintainer of Docusaurus
- url: https://github.com/endiliey
- image_url: https://github.com/endiliey.png
-
-yangshun:
- name: Yangshun Tay
- title: Front End Engineer @ Facebook
- url: https://github.com/yangshun
- image_url: https://github.com/yangshun.png
-
-slorber:
- name: Sébastien Lorber
- title: Docusaurus maintainer
- url: https://sebastienlorber.com
- image_url: https://github.com/slorber.png
+jeromewu:
+ name: Jerome Wu
+ title: Maintainer of ffmpeg.wasm
+ url: https://github.com/jeromewu
+ image_url: https://github.com/jeromewu.png
diff --git a/apps/website/docs/contribution/core.md b/apps/website/docs/contribution/core.md
new file mode 100644
index 0000000..cb8a4ef
--- /dev/null
+++ b/apps/website/docs/contribution/core.md
@@ -0,0 +1 @@
+# @ffmpeg/core
diff --git a/apps/website/docs/contribution/ffmpeg.md b/apps/website/docs/contribution/ffmpeg.md
new file mode 100644
index 0000000..b859dee
--- /dev/null
+++ b/apps/website/docs/contribution/ffmpeg.md
@@ -0,0 +1 @@
+# @ffmpeg/ffmpeg
diff --git a/apps/website/docs/contribution/util.md b/apps/website/docs/contribution/util.md
new file mode 100644
index 0000000..543b383
--- /dev/null
+++ b/apps/website/docs/contribution/util.md
@@ -0,0 +1 @@
+# @ffmpeg/util
diff --git a/apps/website/docs/faq.md b/apps/website/docs/faq.md
new file mode 100644
index 0000000..4514b4c
--- /dev/null
+++ b/apps/website/docs/faq.md
@@ -0,0 +1 @@
+# FAQ
diff --git a/apps/website/docs/getting-started/configuration.md b/apps/website/docs/getting-started/configuration.md
new file mode 100644
index 0000000..a025a48
--- /dev/null
+++ b/apps/website/docs/getting-started/configuration.md
@@ -0,0 +1 @@
+# Configuration
diff --git a/apps/website/docs/getting-started/installation.md b/apps/website/docs/getting-started/installation.md
new file mode 100644
index 0000000..25267fe
--- /dev/null
+++ b/apps/website/docs/getting-started/installation.md
@@ -0,0 +1 @@
+# Installation
diff --git a/apps/website/docs/getting-started/lib-versions.md b/apps/website/docs/getting-started/lib-versions.md
new file mode 100644
index 0000000..b530bec
--- /dev/null
+++ b/apps/website/docs/getting-started/lib-versions.md
@@ -0,0 +1 @@
+# Library Versions
diff --git a/apps/website/docs/getting-started/multi-thread.md b/apps/website/docs/getting-started/multi-thread.md
new file mode 100644
index 0000000..235e353
--- /dev/null
+++ b/apps/website/docs/getting-started/multi-thread.md
@@ -0,0 +1 @@
+# Use Mutlithreading
diff --git a/apps/website/docs/intro.md b/apps/website/docs/intro.md
index 8a2e69d..ae9d07e 100644
--- a/apps/website/docs/intro.md
+++ b/apps/website/docs/intro.md
@@ -1,8 +1,4 @@
----
-sidebar_position: 1
----
-
-# Tutorial Intro
+# Introduction
Let's discover **Docusaurus in less than 5 minutes**.
diff --git a/apps/website/docs/migration.md b/apps/website/docs/migration.md
new file mode 100644
index 0000000..ed417b6
--- /dev/null
+++ b/apps/website/docs/migration.md
@@ -0,0 +1 @@
+# Migrating from 0.11.x to 0.12+
diff --git a/apps/website/docs/tutorial-basics/_category_.json b/apps/website/docs/tutorial-basics/_category_.json
deleted file mode 100644
index 2e6db55..0000000
--- a/apps/website/docs/tutorial-basics/_category_.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "label": "Tutorial - Basics",
- "position": 2,
- "link": {
- "type": "generated-index",
- "description": "5 minutes to learn the most important Docusaurus concepts."
- }
-}
diff --git a/apps/website/docs/tutorial-basics/congratulations.md b/apps/website/docs/tutorial-basics/congratulations.md
deleted file mode 100644
index 04771a0..0000000
--- a/apps/website/docs/tutorial-basics/congratulations.md
+++ /dev/null
@@ -1,23 +0,0 @@
----
-sidebar_position: 6
----
-
-# Congratulations!
-
-You have just learned the **basics of Docusaurus** and made some changes to the **initial template**.
-
-Docusaurus has **much more to offer**!
-
-Have **5 more minutes**? Take a look at **[versioning](../tutorial-extras/manage-docs-versions.md)** and **[i18n](../tutorial-extras/translate-your-site.md)**.
-
-Anything **unclear** or **buggy** in this tutorial? [Please report it!](https://github.com/facebook/docusaurus/discussions/4610)
-
-## What's next?
-
-- Read the [official documentation](https://docusaurus.io/)
-- Modify your site configuration with [`docusaurus.config.js`](https://docusaurus.io/docs/api/docusaurus-config)
-- Add navbar and footer items with [`themeConfig`](https://docusaurus.io/docs/api/themes/configuration)
-- Add a custom [Design and Layout](https://docusaurus.io/docs/styling-layout)
-- Add a [search bar](https://docusaurus.io/docs/search)
-- Find inspirations in the [Docusaurus showcase](https://docusaurus.io/showcase)
-- Get involved in the [Docusaurus Community](https://docusaurus.io/community/support)
diff --git a/apps/website/docs/tutorial-basics/create-a-blog-post.md b/apps/website/docs/tutorial-basics/create-a-blog-post.md
deleted file mode 100644
index ea472bb..0000000
--- a/apps/website/docs/tutorial-basics/create-a-blog-post.md
+++ /dev/null
@@ -1,34 +0,0 @@
----
-sidebar_position: 3
----
-
-# Create a Blog Post
-
-Docusaurus creates a **page for each blog post**, but also a **blog index page**, a **tag system**, an **RSS** feed...
-
-## Create your first Post
-
-Create a file at `blog/2021-02-28-greetings.md`:
-
-```md title="blog/2021-02-28-greetings.md"
----
-slug: greetings
-title: Greetings!
-authors:
- - name: Joel Marcey
- title: Co-creator of Docusaurus 1
- url: https://github.com/JoelMarcey
- image_url: https://github.com/JoelMarcey.png
- - name: Sébastien Lorber
- title: Docusaurus maintainer
- url: https://sebastienlorber.com
- image_url: https://github.com/slorber.png
-tags: [greetings]
----
-
-Congratulations, you have made your first post!
-
-Feel free to play around and edit this post as much you like.
-```
-
-A new blog post is now available at [http://localhost:3000/blog/greetings](http://localhost:3000/blog/greetings).
diff --git a/apps/website/docs/tutorial-basics/create-a-document.md b/apps/website/docs/tutorial-basics/create-a-document.md
deleted file mode 100644
index ffddfa8..0000000
--- a/apps/website/docs/tutorial-basics/create-a-document.md
+++ /dev/null
@@ -1,57 +0,0 @@
----
-sidebar_position: 2
----
-
-# Create a Document
-
-Documents are **groups of pages** connected through:
-
-- a **sidebar**
-- **previous/next navigation**
-- **versioning**
-
-## Create your first Doc
-
-Create a Markdown file at `docs/hello.md`:
-
-```md title="docs/hello.md"
-# Hello
-
-This is my **first Docusaurus document**!
-```
-
-A new document is now available at [http://localhost:3000/docs/hello](http://localhost:3000/docs/hello).
-
-## Configure the Sidebar
-
-Docusaurus automatically **creates a sidebar** from the `docs` folder.
-
-Add metadata to customize the sidebar label and position:
-
-```md title="docs/hello.md" {1-4}
----
-sidebar_label: 'Hi!'
-sidebar_position: 3
----
-
-# Hello
-
-This is my **first Docusaurus document**!
-```
-
-It is also possible to create your sidebar explicitly in `sidebars.js`:
-
-```js title="sidebars.js"
-module.exports = {
- tutorialSidebar: [
- 'intro',
- // highlight-next-line
- 'hello',
- {
- type: 'category',
- label: 'Tutorial',
- items: ['tutorial-basics/create-a-document'],
- },
- ],
-};
-```
diff --git a/apps/website/docs/tutorial-basics/create-a-page.md b/apps/website/docs/tutorial-basics/create-a-page.md
deleted file mode 100644
index 20e2ac3..0000000
--- a/apps/website/docs/tutorial-basics/create-a-page.md
+++ /dev/null
@@ -1,43 +0,0 @@
----
-sidebar_position: 1
----
-
-# Create a Page
-
-Add **Markdown or React** files to `src/pages` to create a **standalone page**:
-
-- `src/pages/index.js` → `localhost:3000/`
-- `src/pages/foo.md` → `localhost:3000/foo`
-- `src/pages/foo/bar.js` → `localhost:3000/foo/bar`
-
-## Create your first React Page
-
-Create a file at `src/pages/my-react-page.js`:
-
-```jsx title="src/pages/my-react-page.js"
-import React from 'react';
-import Layout from '@theme/Layout';
-
-export default function MyReactPage() {
- return (
-
-
My React page
-
This is a React page
-
- );
-}
-```
-
-A new page is now available at [http://localhost:3000/my-react-page](http://localhost:3000/my-react-page).
-
-## Create your first Markdown Page
-
-Create a file at `src/pages/my-markdown-page.md`:
-
-```mdx title="src/pages/my-markdown-page.md"
-# My Markdown page
-
-This is a Markdown page
-```
-
-A new page is now available at [http://localhost:3000/my-markdown-page](http://localhost:3000/my-markdown-page).
diff --git a/apps/website/docs/tutorial-basics/deploy-your-site.md b/apps/website/docs/tutorial-basics/deploy-your-site.md
deleted file mode 100644
index 1c50ee0..0000000
--- a/apps/website/docs/tutorial-basics/deploy-your-site.md
+++ /dev/null
@@ -1,31 +0,0 @@
----
-sidebar_position: 5
----
-
-# Deploy your site
-
-Docusaurus is a **static-site-generator** (also called **[Jamstack](https://jamstack.org/)**).
-
-It builds your site as simple **static HTML, JavaScript and CSS files**.
-
-## Build your site
-
-Build your site **for production**:
-
-```bash
-npm run build
-```
-
-The static files are generated in the `build` folder.
-
-## Deploy your site
-
-Test your production build locally:
-
-```bash
-npm run serve
-```
-
-The `build` folder is now served at [http://localhost:3000/](http://localhost:3000/).
-
-You can now deploy the `build` folder **almost anywhere** easily, **for free** or very small cost (read the **[Deployment Guide](https://docusaurus.io/docs/deployment)**).
diff --git a/apps/website/docs/tutorial-basics/markdown-features.mdx b/apps/website/docs/tutorial-basics/markdown-features.mdx
deleted file mode 100644
index c2ffcc4..0000000
--- a/apps/website/docs/tutorial-basics/markdown-features.mdx
+++ /dev/null
@@ -1,146 +0,0 @@
----
-sidebar_position: 4
----
-
-# Markdown Features
-
-Docusaurus supports **[Markdown](https://daringfireball.net/projects/markdown/syntax)** and a few **additional features**.
-
-## Front Matter
-
-Markdown documents have metadata at the top called [Front Matter](https://jekyllrb.com/docs/front-matter/):
-
-```text title="my-doc.md"
-// highlight-start
----
-id: my-doc-id
-title: My document title
-description: My document description
-slug: /my-custom-url
----
-// highlight-end
-
-## Markdown heading
-
-Markdown text with [links](./hello.md)
-```
-
-## Links
-
-Regular Markdown links are supported, using url paths or relative file paths.
-
-```md
-Let's see how to [Create a page](/create-a-page).
-```
-
-```md
-Let's see how to [Create a page](./create-a-page.md).
-```
-
-**Result:** Let's see how to [Create a page](./create-a-page.md).
-
-## Images
-
-Regular Markdown images are supported.
-
-
-
-
-
-
-
-
-
-You can reference images relative to the current file as well, as shown in [the extra guides](../tutorial-extras/manage-docs-versions.md).
-
-## Code Blocks
-
-Markdown code blocks are supported with Syntax highlighting.
-
- ```jsx title="src/components/HelloDocusaurus.js"
- function HelloDocusaurus() {
- return (
-
;
-}
-```
-
-## Admonitions
-
-Docusaurus has a special syntax to create admonitions and callouts:
-
- :::tip My tip
-
- Use this awesome feature option
-
- :::
-
- :::danger Take care
-
- This action is dangerous
-
- :::
-
-:::tip My tip
-
-Use this awesome feature option
-
-:::
-
-:::danger Take care
-
-This action is dangerous
-
-:::
-
-## MDX and React Components
-
-[MDX](https://mdxjs.com/) can make your documentation more **interactive** and allows using any **React components inside Markdown**:
-
-```jsx
-export const Highlight = ({children, color}) => (
- {
- alert(`You clicked the color ${color} with label ${children}`)
- }}>
- {children}
-
-);
-
-This is Docusaurus green !
-
-This is Facebook blue !
-```
-
-export const Highlight = ({children, color}) => (
- {
- alert(`You clicked the color ${color} with label ${children}`);
- }}>
- {children}
-
-);
-
-This is Docusaurus green !
-
-This is Facebook blue !
diff --git a/apps/website/docs/tutorial-extras/_category_.json b/apps/website/docs/tutorial-extras/_category_.json
deleted file mode 100644
index a8ffcc1..0000000
--- a/apps/website/docs/tutorial-extras/_category_.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "label": "Tutorial - Extras",
- "position": 3,
- "link": {
- "type": "generated-index"
- }
-}
diff --git a/apps/website/docs/tutorial-extras/img/docsVersionDropdown.png b/apps/website/docs/tutorial-extras/img/docsVersionDropdown.png
deleted file mode 100644
index 97e4164..0000000
Binary files a/apps/website/docs/tutorial-extras/img/docsVersionDropdown.png and /dev/null differ
diff --git a/apps/website/docs/tutorial-extras/img/localeDropdown.png b/apps/website/docs/tutorial-extras/img/localeDropdown.png
deleted file mode 100644
index e257edc..0000000
Binary files a/apps/website/docs/tutorial-extras/img/localeDropdown.png and /dev/null differ
diff --git a/apps/website/docs/tutorial-extras/manage-docs-versions.md b/apps/website/docs/tutorial-extras/manage-docs-versions.md
deleted file mode 100644
index e12c3f3..0000000
--- a/apps/website/docs/tutorial-extras/manage-docs-versions.md
+++ /dev/null
@@ -1,55 +0,0 @@
----
-sidebar_position: 1
----
-
-# Manage Docs Versions
-
-Docusaurus can manage multiple versions of your docs.
-
-## Create a docs version
-
-Release a version 1.0 of your project:
-
-```bash
-npm run docusaurus docs:version 1.0
-```
-
-The `docs` folder is copied into `versioned_docs/version-1.0` and `versions.json` is created.
-
-Your docs now have 2 versions:
-
-- `1.0` at `http://localhost:3000/docs/` for the version 1.0 docs
-- `current` at `http://localhost:3000/docs/next/` for the **upcoming, unreleased docs**
-
-## Add a Version Dropdown
-
-To navigate seamlessly across versions, add a version dropdown.
-
-Modify the `docusaurus.config.js` file:
-
-```js title="docusaurus.config.js"
-module.exports = {
- themeConfig: {
- navbar: {
- items: [
- // highlight-start
- {
- type: 'docsVersionDropdown',
- },
- // highlight-end
- ],
- },
- },
-};
-```
-
-The docs version dropdown appears in your navbar:
-
-
-
-## Update an existing version
-
-It is possible to edit versioned docs in their respective folder:
-
-- `versioned_docs/version-1.0/hello.md` updates `http://localhost:3000/docs/hello`
-- `docs/hello.md` updates `http://localhost:3000/docs/next/hello`
diff --git a/apps/website/docs/tutorial-extras/translate-your-site.md b/apps/website/docs/tutorial-extras/translate-your-site.md
deleted file mode 100644
index caeaffb..0000000
--- a/apps/website/docs/tutorial-extras/translate-your-site.md
+++ /dev/null
@@ -1,88 +0,0 @@
----
-sidebar_position: 2
----
-
-# Translate your site
-
-Let's translate `docs/intro.md` to French.
-
-## Configure i18n
-
-Modify `docusaurus.config.js` to add support for the `fr` locale:
-
-```js title="docusaurus.config.js"
-module.exports = {
- i18n: {
- defaultLocale: 'en',
- locales: ['en', 'fr'],
- },
-};
-```
-
-## Translate a doc
-
-Copy the `docs/intro.md` file to the `i18n/fr` folder:
-
-```bash
-mkdir -p i18n/fr/docusaurus-plugin-content-docs/current/
-
-cp docs/intro.md i18n/fr/docusaurus-plugin-content-docs/current/intro.md
-```
-
-Translate `i18n/fr/docusaurus-plugin-content-docs/current/intro.md` in French.
-
-## Start your localized site
-
-Start your site on the French locale:
-
-```bash
-npm run start -- --locale fr
-```
-
-Your localized site is accessible at [http://localhost:3000/fr/](http://localhost:3000/fr/) and the `Getting Started` page is translated.
-
-:::caution
-
-In development, you can only use one locale at a same time.
-
-:::
-
-## Add a Locale Dropdown
-
-To navigate seamlessly across languages, add a locale dropdown.
-
-Modify the `docusaurus.config.js` file:
-
-```js title="docusaurus.config.js"
-module.exports = {
- themeConfig: {
- navbar: {
- items: [
- // highlight-start
- {
- type: 'localeDropdown',
- },
- // highlight-end
- ],
- },
- },
-};
-```
-
-The locale dropdown now appears in your navbar:
-
-
-
-## Build your localized site
-
-Build your site for a specific locale:
-
-```bash
-npm run build -- --locale fr
-```
-
-Or build your site to include all the locales at once:
-
-```bash
-npm run build
-```
diff --git a/apps/website/docusaurus.config.js b/apps/website/docusaurus.config.js
index 9f6e6b7..95fe318 100644
--- a/apps/website/docusaurus.config.js
+++ b/apps/website/docusaurus.config.js
@@ -76,8 +76,8 @@ const config = {
position: "left",
label: "Docs",
},
- { to: "/blog", label: "Blog", position: "left" },
{ to: "/playground", label: "Playground", position: "left" },
+ { to: "/blog", label: "Blog", position: "left" },
{
href: "https://github.com/ffmpegwasm/ffmpeg.wasm",
label: "GitHub",
diff --git a/apps/website/sidebars.js b/apps/website/sidebars.js
index 6a96ed4..a43b295 100644
--- a/apps/website/sidebars.js
+++ b/apps/website/sidebars.js
@@ -19,11 +19,28 @@ const sidebars = {
// But you can create a sidebar manually
tutorialSidebar: [
"intro",
+ {
+ type: "category",
+ label: "Getting Started",
+ items: [
+ "getting-started/installation",
+ "getting-started/configuration",
+ "getting-started/multi-thread",
+ "getting-started/lib-versions",
+ ],
+ },
+ "migration",
+ "faq",
{
type: "category",
label: "API",
items: ["api/classes/FFmpeg"],
},
+ {
+ type: "category",
+ label: "Contribution",
+ items: ["contribution/core", "contribution/ffmpeg", "contribution/util"],
+ },
],
};
diff --git a/apps/website/src/components/Playground/CoreDownloader.tsx b/apps/website/src/components/Playground/CoreDownloader.tsx
index 838ea86..64ace3d 100644
--- a/apps/website/src/components/Playground/CoreDownloader.tsx
+++ b/apps/website/src/components/Playground/CoreDownloader.tsx
@@ -1,7 +1,7 @@
import * as React from "react";
import Typography from "@mui/material/Typography";
import Container from "@mui/material/Container";
-import LinearProgressWithLabel from "./LinearProgressWithLabel";
+import LinearProgressWithLabel from "@site/src/components/common/LinearProgressWithLabel";
import { CORE_SIZE } from "./const";
export default function CoreDownloader({ url, received }) {
diff --git a/apps/website/src/components/Playground/CoreSwitcher.tsx b/apps/website/src/components/Playground/CoreSwitcher.tsx
index 50596c4..84cb341 100644
--- a/apps/website/src/components/Playground/CoreSwitcher.tsx
+++ b/apps/website/src/components/Playground/CoreSwitcher.tsx
@@ -19,12 +19,18 @@ export default function CoreSwitcher({ checked, onChange }: CoreSwitcherProps) {
}
- label="Use Multi-thread"
+ label="Use Multithreading"
disabled={typeof SharedArrayBuffer !== "function"}
/>
-
+ {
+ location.href = "/docs/getting-started/multi-thread";
+ }}
+ >
diff --git a/apps/website/src/components/Playground/Editor.tsx b/apps/website/src/components/Playground/Editor.tsx
deleted file mode 100644
index 7fb059e..0000000
--- a/apps/website/src/components/Playground/Editor.tsx
+++ /dev/null
@@ -1,387 +0,0 @@
-///
-
-import * as React from "react";
-import Box from "@mui/material/Box";
-import Grid from "@mui/material/Grid";
-import Stack from "@mui/material/Stack";
-import Button from "@mui/material/Button";
-import Typography from "@mui/material/Typography";
-import FolderIcon from "@mui/icons-material/Folder";
-import List from "@mui/material/List";
-import ListItem from "@mui/material/ListItem";
-import ListItemIcon from "@mui/material/ListItemIcon";
-import ListItemText from "@mui/material/ListItemText";
-import IconButton from "@mui/material/IconButton";
-import RefreshIcon from "@mui/icons-material/Refresh";
-import UploadFileIcon from "@mui/icons-material/UploadFile";
-import UploadIcon from "@mui/icons-material/Upload";
-import InsertDriveFileIcon from "@mui/icons-material/InsertDriveFile";
-import CreateNewFolderIcon from "@mui/icons-material/CreateNewFolder";
-import Tooltip from "@mui/material/Tooltip";
-import Paper from "@mui/material/Paper";
-import { useColorMode } from "@docusaurus/theme-common";
-import { FFmpeg } from "@ffmpeg/ffmpeg";
-import { fetchFile } from "@ffmpeg/util";
-import { downloadFile } from "@site/src/util";
-import AceEditor from "react-ace";
-import { getFFmpeg } from "./ffmpeg";
-import { SAMPLE_FILES } from "./const";
-import LinearProgressWithLabel from "./LinearProgressWithLabel";
-import MoreButton from "./MoreButton";
-import "ace-builds/src-noconflict/mode-json";
-import "ace-builds/src-noconflict/mode-javascript";
-import "ace-builds/src-noconflict/mode-text";
-import "ace-builds/src-noconflict/theme-dracula";
-import "ace-builds/src-noconflict/theme-github";
-import ListItemButton from "@mui/material/ListItemButton";
-import Modal from "@mui/material/Modal";
-import TextField from "@mui/material/TextField";
-
-const defaultArgs = JSON.stringify(["-i", "video.avi", "video.mp4"], null, 2);
-const options = [
- { text: "Download", key: "download" },
- { text: "Download as Text File", key: "download-text" },
- { text: "Delete", key: "delete" },
-];
-
-const modalStyle = {
- position: "absolute" as "absolute",
- top: "50%",
- left: "50%",
- transform: "translate(-50%, -50%)",
- width: 400,
- bgcolor: "background.paper",
- p: 4,
-};
-
-const genFFmpegText = (args: string) => {
- let data: any = [];
- try {
- data = JSON.parse(args);
- } catch (e) {}
- return `// equivalent ffmpeg.wasm API call
-ffmpeg.exec(${JSON.stringify(data)});
-
-// equivalent ffmpeg command line
-ffmpeg ${data.join(" ")}`;
-};
-
-export default function Editor() {
- const { useState, useEffect } = React;
- const [args, setArgs] = useState(defaultArgs);
- const [logs, setLogs] = useState([]);
- const [output, setOutput] = useState();
- const [path, setPath] = useState("/");
- const [nodes, setNodes] = useState([]);
- const [progress, setProgress] = useState(0);
- const [open, setOpen] = useState(false);
- const [time, setTime] = useState(0);
- const [folderName, setFolderName] = useState("");
- const handleModalOpen = () => setOpen(true);
- const handleModalClose = () => setOpen(false);
- const { colorMode } = useColorMode();
-
- const theme = colorMode === "dark" ? "github" : "dracula";
-
- const scrollToEnd = () => {
- output && output.renderer.scrollToLine(Number.POSITIVE_INFINITY);
- };
-
- const refreshDir = async (curPath: string) => {
- const ffmpeg = getFFmpeg();
- if (ffmpeg.loaded) {
- setNodes(
- (await ffmpeg.listDir(curPath)).filter(({ name }) => name !== ".")
- );
- }
- };
-
- const loadSamples = async () => {
- const ffmpeg = getFFmpeg();
- for (const name of Object.keys(SAMPLE_FILES)) {
- await ffmpeg.writeFile(name, await fetchFile(SAMPLE_FILES[name]));
- }
- refreshDir(path);
- };
-
- const exec = async () => {
- const ffmpeg = getFFmpeg();
- setProgress(0);
- setTime(0);
- const logListener = ({ message }) => {
- setLogs((_logs) => [..._logs, message]);
- scrollToEnd();
- };
- const progListener = ({ progress: prog }) => {
- setProgress(prog * 100);
- };
- ffmpeg.on(FFmpeg.LOG, logListener);
- ffmpeg.on(FFmpeg.PROGRESS, progListener);
- const start = performance.now();
- await ffmpeg.exec(JSON.parse(args));
- setTime(performance.now() - start);
- ffmpeg.removeListener(FFmpeg.LOG, logListener);
- ffmpeg.removeListener(FFmpeg.PROGRESS, progListener);
- refreshDir(path);
- };
-
- const cd = (name: string) => async () => {
- let nextPath = path;
- if (path === "/") {
- if (name !== "..") nextPath = `/${name}`;
- } else if (name === "..") {
- const cols = path.split("/");
- cols.pop();
- nextPath = cols.length === 1 ? "/" : cols.join("/");
- } else {
- nextPath = `${path}/${name}`;
- }
- setPath(nextPath);
- refreshDir(nextPath);
- };
-
- const handleFileUpload =
- (isText: boolean = false) =>
- async ({ target: { files } }: React.ChangeEvent) => {
- const ffmpeg = getFFmpeg();
- for (let i = 0; i < files.length; i++) {
- const file = files[i];
- let data: Uint8Array | string = await fetchFile(file);
- if (isText) data = new TextDecoder().decode(data);
- await ffmpeg.writeFile(`${path}/${file.name}`, data);
- }
- refreshDir(path);
- };
-
- const handleItemClick = (name: string) => async (option: string) => {
- const ffmpeg = getFFmpeg();
- const fullPath = `${path}/${name}`;
- switch (option) {
- case "download":
- downloadFile(
- name,
- ((await ffmpeg.readFile(fullPath, "binary")) as Uint8Array).buffer
- );
- break;
- case "download-text":
- downloadFile(name, await ffmpeg.readFile(fullPath, "utf8"));
- break;
- case "delete":
- await ffmpeg.deleteFile(fullPath);
- refreshDir(path);
- break;
- default:
- break;
- }
- };
-
- const handleFolderCreate = async () => {
- if (folderName !== "") {
- await getFFmpeg().createDir(`${path}/${folderName}`);
- }
- refreshDir(path);
- handleModalClose();
- };
-
- useEffect(() => {
- refreshDir(path);
- }, []);
-
- return (
-
-
-
-
-
- <>
-
- File System:
-
-
-
-
-
-
-
-
- {}}
- aria-label="upload-text"
- component="label"
- size="small"
- >
-
-
-
-
-
- {
- setFolderName("");
- handleModalOpen();
- }}
- aria-label="create-a-new-folder"
- size="small"
- >
-
-
-
-
- refreshDir(path)}
- aria-label="fresh"
- size="small"
- >
-
-
-
-
-
- {`Path: ${path}`}
-
- {nodes.map(({ name, isDir }, index) =>
- isDir ? (
-
-
-
-
-
-
- ) : (
-
- }
- >
-
-
-
-
-
- )
- )}
-
- >
-
-
-
-
-
-
-
-
- Edit JSON below to update command:
- setArgs(value)}
- setOptions={{ tabSize: 2 }}
- />
-
-
- Console Output:
- setOutput(editor)}
- />
- Transcoding Progress:
-
-
-
- {time === 0
- ? ""
- : `Time Elapsed: ${(time / 1000).toFixed(2)} s`}
-
-
-
-
-
-
-
-
-
-
-
- Folder Name:
-
- setFolderName(event.target.value)}
- />
-
-
-
-
-
-
-
-
- );
-}
diff --git a/apps/website/src/components/Playground/Workspace/Editor.tsx b/apps/website/src/components/Playground/Workspace/Editor.tsx
new file mode 100644
index 0000000..6199135
--- /dev/null
+++ b/apps/website/src/components/Playground/Workspace/Editor.tsx
@@ -0,0 +1,121 @@
+///
+
+import React, { useEffect, useState } from "react";
+import AceEditor from "react-ace";
+import Paper from "@mui/material/Paper";
+import Stack from "@mui/material/Stack";
+import Typography from "@mui/material/Typography";
+import Button from "@mui/material/Button";
+import LinearProgressWithLabel from "@site/src/components/common/LinearProgressWithLabel";
+import { useColorMode } from "@docusaurus/theme-common";
+import "ace-builds/src-noconflict/mode-json";
+import "ace-builds/src-noconflict/mode-javascript";
+import "ace-builds/src-noconflict/mode-text";
+import "ace-builds/src-noconflict/theme-dracula";
+import "ace-builds/src-noconflict/theme-github";
+
+const genFFmpegText = (args: string) => {
+ let data: any = [];
+ try {
+ data = JSON.parse(args);
+ } catch (e) {}
+ return `// equivalent ffmpeg.wasm API call
+ffmpeg.exec(${JSON.stringify(data)});
+
+// equivalent ffmpeg command line
+ffmpeg ${data.join(" ")}`;
+};
+
+interface EditorProps {
+ args: string;
+ logs: string[];
+ progress: number;
+ time: number;
+ onArgsUpdate: (args: string) => void;
+ onExec: () => Promise;
+}
+
+export default function Editor({
+ args = "",
+ logs = [],
+ progress = 0,
+ time = 0,
+ onArgsUpdate,
+ onExec,
+}: EditorProps) {
+ const { colorMode } = useColorMode();
+ const [output, setOutput] = useState();
+
+ useEffect(() => {
+ // scroll logs to the end.
+ output && output.renderer.scrollToLine(Number.POSITIVE_INFINITY);
+ }, [logs]);
+
+ const theme = colorMode === "dark" ? "github" : "dracula";
+
+ return (
+
+
+
+ Editor:
+ Edit arguments below to update command:
+
+
+
+ Console Output:
+ setOutput(editor)}
+ />
+ Transcoding Progress:
+
+
+
+ {time === 0 ? "" : `Time Elapsed: ${(time / 1000).toFixed(2)} s`}
+
+
+
+
+
+ );
+}
diff --git a/apps/website/src/components/Playground/Workspace/FileSystemManager.tsx b/apps/website/src/components/Playground/Workspace/FileSystemManager.tsx
new file mode 100644
index 0000000..499fbcb
--- /dev/null
+++ b/apps/website/src/components/Playground/Workspace/FileSystemManager.tsx
@@ -0,0 +1,202 @@
+import React, { useState, ChangeEvent } from "react";
+import Box from "@mui/material/Box";
+import Button from "@mui/material/Button";
+import IconButton from "@mui/material/IconButton";
+import List from "@mui/material/List";
+import ListItem from "@mui/material/ListItem";
+import ListItemButton from "@mui/material/ListItemButton";
+import ListItemIcon from "@mui/material/ListItemIcon";
+import ListItemText from "@mui/material/ListItemText";
+import Modal from "@mui/material/Modal";
+import Paper from "@mui/material/Paper";
+import Stack from "@mui/material/Stack";
+import TextField from "@mui/material/TextField";
+import Tooltip from "@mui/material/Tooltip";
+import Typography from "@mui/material/Typography";
+import FolderIcon from "@mui/icons-material/Folder";
+import RefreshIcon from "@mui/icons-material/Refresh";
+import UploadFileIcon from "@mui/icons-material/UploadFile";
+import UploadIcon from "@mui/icons-material/Upload";
+import InsertDriveFileIcon from "@mui/icons-material/InsertDriveFile";
+import CreateNewFolderIcon from "@mui/icons-material/CreateNewFolder";
+import MoreButton from "./MoreButton";
+import { Node } from "./types";
+
+interface FileSystemManagerProps {
+ path: string;
+ nodes: Node[];
+ onFileUpload: (
+ isText: boolean
+ ) => (event: ChangeEvent) => Promise;
+ onDirClick: (name: string) => () => Promise;
+ onFileClick: (name: string) => (option: string) => Promise;
+ onDirCreate: (name: string) => () => Promise;
+ onRefresh: () => Promise;
+ onLoadSamples: () => Promise;
+}
+
+const modalStyle = {
+ position: "absolute" as "absolute",
+ top: "50%",
+ left: "50%",
+ transform: "translate(-50%, -50%)",
+ width: 400,
+ bgcolor: "background.paper",
+ p: 4,
+};
+
+export const options = [
+ { text: "Download", key: "download" },
+ { text: "Download as Text File", key: "download-text" },
+ { text: "Delete", key: "delete" },
+];
+
+export default function FileSystemManager({
+ path = "/",
+ nodes = [],
+ onFileUpload = () => () => Promise.resolve(),
+ onFileClick = () => () => Promise.resolve(),
+ onDirClick = () => () => Promise.resolve(),
+ onDirCreate = () => () => Promise.resolve(),
+ onRefresh = () => Promise.resolve(),
+ onLoadSamples = () => Promise.resolve(),
+}: FileSystemManagerProps) {
+ const [open, setOpen] = useState(false);
+ const [dirName, setDirName] = useState("");
+ const handleModalOpen = () => setOpen(true);
+ const handleModalClose = () => setOpen(false);
+
+ return (
+ <>
+
+
+ <>
+
+ File System:
+
+
+
+
+
+
+
+
+ {}}
+ aria-label="upload-text"
+ component="label"
+ size="small"
+ >
+
+
+
+
+
+ {
+ setDirName("");
+ handleModalOpen();
+ }}
+ aria-label="create-a-new-folder"
+ size="small"
+ >
+
+
+
+
+
+
+
+
+
+
+ {`Path: ${path}`}
+
+ {nodes.map(({ name, isDir }, index) =>
+ isDir ? (
+
+
+
+
+
+
+ ) : (
+
+ }
+ >
+
+
+
+
+
+ )
+ )}
+
+ >
+
+
+
+
+
+
+
+ Folder Name:
+
+ setDirName(event.target.value)}
+ />
+
+
+
+
+
+
+
+ >
+ );
+}
diff --git a/apps/website/src/components/Playground/MoreButton.tsx b/apps/website/src/components/Playground/Workspace/MoreButton.tsx
similarity index 100%
rename from apps/website/src/components/Playground/MoreButton.tsx
rename to apps/website/src/components/Playground/Workspace/MoreButton.tsx
diff --git a/apps/website/src/components/Playground/Workspace/index.tsx b/apps/website/src/components/Playground/Workspace/index.tsx
new file mode 100644
index 0000000..d13cf6f
--- /dev/null
+++ b/apps/website/src/components/Playground/Workspace/index.tsx
@@ -0,0 +1,154 @@
+import React, {
+ ChangeEvent,
+ useState,
+ useEffect,
+ MutableRefObject,
+} from "react";
+import Box from "@mui/material/Box";
+import Grid from "@mui/material/Grid";
+import { FFmpeg } from "@ffmpeg/ffmpeg";
+import { fetchFile } from "@ffmpeg/util";
+import { downloadFile } from "@site/src/util";
+import { Node } from "./types";
+import FileSystemManager from "./FileSystemManager";
+import { SAMPLE_FILES } from "../const";
+import Editor from "./Editor";
+
+const defaultArgs = JSON.stringify(["-i", "video.avi", "video.mp4"], null, 2);
+
+interface WorkspaceProps {
+ ffmpeg: MutableRefObject;
+}
+
+export default function Workspace({ ffmpeg: _ffmpeg }: WorkspaceProps) {
+ const [path, setPath] = useState("/");
+ const [nodes, setNodes] = useState([]);
+ const [args, setArgs] = useState(defaultArgs);
+ const [progress, setProgress] = useState(0);
+ const [time, setTime] = useState(0);
+ const [logs, setLogs] = useState([]);
+
+ const ffmpeg = _ffmpeg.current;
+
+ const refreshDir = async (curPath: string) => {
+ if (ffmpeg.loaded) {
+ setNodes(
+ (await ffmpeg.listDir(curPath)).filter(({ name }) => name !== ".")
+ );
+ }
+ };
+
+ const onFileUpload =
+ (isText: boolean) =>
+ async ({ target: { files } }: ChangeEvent) => {
+ for (let i = 0; i < files.length; i++) {
+ const file = files[i];
+ let data: Uint8Array | string = await fetchFile(file);
+ if (isText) data = new TextDecoder().decode(data);
+ await ffmpeg.writeFile(`${path}/${file.name}`, data);
+ }
+ refreshDir(path);
+ };
+
+ const onFileClick = (name: string) => async (option: string) => {
+ const fullPath = `${path}/${name}`;
+ switch (option) {
+ case "download":
+ downloadFile(
+ name,
+ ((await ffmpeg.readFile(fullPath, "binary")) as Uint8Array).buffer
+ );
+ break;
+ case "download-text":
+ downloadFile(name, await ffmpeg.readFile(fullPath, "utf8"));
+ break;
+ case "delete":
+ await ffmpeg.deleteFile(fullPath);
+ refreshDir(path);
+ break;
+ default:
+ break;
+ }
+ };
+
+ const onDirClick = (name: string) => async () => {
+ let nextPath = path;
+ if (path === "/") {
+ if (name !== "..") nextPath = `/${name}`;
+ } else if (name === "..") {
+ const cols = path.split("/");
+ cols.pop();
+ nextPath = cols.length === 1 ? "/" : cols.join("/");
+ } else {
+ nextPath = `${path}/${name}`;
+ }
+ setPath(nextPath);
+ refreshDir(nextPath);
+ };
+
+ const onDirCreate = (name: string) => async () => {
+ if (name !== "") {
+ await ffmpeg.createDir(`${path}/${name}`);
+ }
+ refreshDir(path);
+ };
+
+ const onLoadSamples = async () => {
+ for (const name of Object.keys(SAMPLE_FILES)) {
+ await ffmpeg.writeFile(name, await fetchFile(SAMPLE_FILES[name]));
+ }
+ refreshDir(path);
+ };
+
+ const onExec = async () => {
+ setProgress(0);
+ setTime(0);
+ const logListener = ({ message }) => {
+ setLogs((_logs) => [..._logs, message]);
+ };
+ const progListener = ({ progress: prog }) => {
+ setProgress(prog * 100);
+ };
+ ffmpeg.on(FFmpeg.LOG, logListener);
+ ffmpeg.on(FFmpeg.PROGRESS, progListener);
+ const start = performance.now();
+ await ffmpeg.exec(JSON.parse(args));
+ setTime(performance.now() - start);
+ ffmpeg.removeListener(FFmpeg.LOG, logListener);
+ ffmpeg.removeListener(FFmpeg.PROGRESS, progListener);
+ refreshDir(path);
+ };
+
+ useEffect(() => {
+ refreshDir(path);
+ }, []);
+
+ return (
+
+
+
+ refreshDir(path)}
+ />
+
+
+ setArgs(_args)}
+ onExec={onExec}
+ />
+
+
+
+ );
+}
diff --git a/apps/website/src/components/Playground/Workspace/types.tsx b/apps/website/src/components/Playground/Workspace/types.tsx
new file mode 100644
index 0000000..7490c2b
--- /dev/null
+++ b/apps/website/src/components/Playground/Workspace/types.tsx
@@ -0,0 +1,4 @@
+export interface Node {
+ name: string;
+ isDir: boolean;
+}
diff --git a/apps/website/src/components/Playground/ffmpeg.ts b/apps/website/src/components/Playground/ffmpeg.ts
deleted file mode 100644
index 99765c9..0000000
--- a/apps/website/src/components/Playground/ffmpeg.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-import { FFmpeg } from "@ffmpeg/ffmpeg";
-
-let ffmpeg = new FFmpeg();
-
-export const getFFmpeg = () => ffmpeg;
diff --git a/apps/website/src/components/Playground/index.tsx b/apps/website/src/components/Playground/index.tsx
index 82626c5..f94ccaa 100644
--- a/apps/website/src/components/Playground/index.tsx
+++ b/apps/website/src/components/Playground/index.tsx
@@ -1,10 +1,9 @@
-import * as React from "react";
+import React, { useState, useEffect, useRef } from "react";
import { FFmpeg } from "@ffmpeg/ffmpeg";
import Stack from "@mui/material/Stack";
-import MuiThemeProvider from "@site/src/components/MuiThemeProvider";
+import MuiThemeProvider from "@site/src/components/common/MuiThemeProvider";
import CoreDownloader from "./CoreDownloader";
-import Editor from "./Editor";
-import { getFFmpeg } from "./ffmpeg";
+import Workspace from "./Workspace";
import { CORE_URL, CORE_MT_URL } from "./const";
import CoreSwitcher from "./CoreSwitcher";
@@ -15,20 +14,20 @@ enum State {
}
export default function Playground() {
- const { useState, useEffect } = React;
const [state, setState] = useState(State.LOADED);
const [isCoreMT, setIsCoreMT] = useState(false);
const [url, setURL] = useState("");
const [received, setReceived] = useState(0);
+ const ffmpeg = useRef(new FFmpeg());
+
const load = async (mt: boolean = false) => {
setState(State.LOADING);
- const ffmpeg = getFFmpeg();
- ffmpeg.terminate();
- ffmpeg.on(FFmpeg.DOWNLOAD, ({ url: _url, received: _received }) => {
+ ffmpeg.current.terminate();
+ ffmpeg.current.on(FFmpeg.DOWNLOAD, ({ url: _url, received: _received }) => {
setURL(_url as string);
setReceived(_received);
});
- await ffmpeg.load({
+ await ffmpeg.current.load({
coreURL: mt ? CORE_MT_URL : CORE_URL,
thread: mt,
});
@@ -54,7 +53,7 @@ export default function Playground() {
case State.LOADING:
return ;
case State.LOADED:
- return ;
+ return ;
default:
return <>>;
}
diff --git a/apps/website/src/components/Playground/styles.module.css b/apps/website/src/components/Playground/styles.module.css
deleted file mode 100644
index e69de29..0000000
diff --git a/apps/website/src/components/Playground/LinearProgressWithLabel.tsx b/apps/website/src/components/common/LinearProgressWithLabel.tsx
similarity index 100%
rename from apps/website/src/components/Playground/LinearProgressWithLabel.tsx
rename to apps/website/src/components/common/LinearProgressWithLabel.tsx
diff --git a/apps/website/src/components/MuiThemeProvider/index.tsx b/apps/website/src/components/common/MuiThemeProvider/index.tsx
similarity index 100%
rename from apps/website/src/components/MuiThemeProvider/index.tsx
rename to apps/website/src/components/common/MuiThemeProvider/index.tsx
diff --git a/apps/website/src/components/common/ThemedButton/index.tsx b/apps/website/src/components/common/ThemedButton/index.tsx
new file mode 100644
index 0000000..1a71fe6
--- /dev/null
+++ b/apps/website/src/components/common/ThemedButton/index.tsx
@@ -0,0 +1,11 @@
+import React from "react";
+import MuiThemeProvider from "../MuiThemeProvider";
+import Button, { ButtonProps } from "@mui/material/Button";
+
+export default function ThemedButton(props: ButtonProps) {
+ return (
+
+
+
+ );
+}
diff --git a/apps/website/src/components/common/ThemedIconButton/index.tsx b/apps/website/src/components/common/ThemedIconButton/index.tsx
new file mode 100644
index 0000000..eef11b9
--- /dev/null
+++ b/apps/website/src/components/common/ThemedIconButton/index.tsx
@@ -0,0 +1,11 @@
+import React from "react";
+import MuiThemeProvider from "../MuiThemeProvider";
+import IconButton, { IconButtonProps } from "@mui/material/IconButton";
+
+export default function ThemedIconButton(props: IconButtonProps) {
+ return (
+
+
+
+ );
+}
diff --git a/apps/website/src/pages/index.tsx b/apps/website/src/pages/index.tsx
index ca626ad..6769c92 100644
--- a/apps/website/src/pages/index.tsx
+++ b/apps/website/src/pages/index.tsx
@@ -18,7 +18,7 @@ function HomepageHeader() {
Try it Now!
diff --git a/apps/website/src/pages/playground.md b/apps/website/src/pages/playground.md
index 92b8bf8..0c202fc 100644
--- a/apps/website/src/pages/playground.md
+++ b/apps/website/src/pages/playground.md
@@ -1,8 +1,104 @@
import Playground from "@site/src/components/Playground";
+import CoreSwitcher from "@site/src/components/Playground/CoreSwitcher";
+import FileSystemManager from
+"@site/src/components/Playground/Workspace/FileSystemManager";
+import Editor from
+"@site/src/components/Playground/Workspace/Editor";
+import MuiThemeProvider from "@site/src/components/common/MuiThemeProvider";
+import ThemedButton from "@site/src/components/common/ThemedButton";
+import ThemedIconButton from "@site/src/components/common/ThemedIconButton";
+import CreateNewFolderIcon from "@mui/icons-material/CreateNewFolder";
+import RefreshIcon from "@mui/icons-material/Refresh";
+import UploadFileIcon from "@mui/icons-material/UploadFile";
+import UploadIcon from "@mui/icons-material/Upload";
# Playground
-Hi! Welcome to ffmpeg.wasm playground! Here you can try and test ffmpeg.wasm
-with ease. :smile:
+Playground allows you to try ffmpeg.wasm without any installation and
+development!
+
+:::tip Quick Start
+
+1. Wait for assets (~32 MB) downloading.
+2. Press Load Sample Files to download & add sample files.
+3. Press Run to convert an AVI file to MP4 file.
+4. Download output files.
+
+:::
+
+
+
+## How to Use :rocket:
+
+> It is recommended to read [Introduction](/docs/intro) first to learn
+ffmpeg.wasm fundamentals.
+
+Demo Video:
+
+
+A typical flow to use ffmpeg.wasm is:
+
+#### Download and load JavaScript & WebAssembly assets
+
+The assets are downloaded automatically when you enter the Playground. You can
+choose to use multithreading version instead by click on the switch:
+
+
+
+
+
+#### Load files to in-memory File System
+
+When ffmpeg.wasm is loaded and ready, you can upload files to its in-memory File
+System to make sure these files can be consumed by the ffmpeg.wasm APIs:
+
+
+
+
+
+
+
+
+
+- : Upload a media file.
+- : Upload a text file.
+- : Create a new folder.
+- : Refresh File System.
+
+> Press Load Sample Files to load a set of samples
+files.
+
+#### Run a command
+
+With files are ready in the File System, you can update arguments in the Editor
+and hit Run afterward:
+
+
+
+
+
+
+
+
+
+#### Download output files
+
+Lastly you can download your files using File System panel and check the result.
+:tada:
diff --git a/apps/website/static/video/playground-how-to.webm b/apps/website/static/video/playground-how-to.webm
new file mode 100755
index 0000000..b209223
Binary files /dev/null and b/apps/website/static/video/playground-how-to.webm differ