Skip to content
This repository has been archived by the owner on Feb 19, 2022. It is now read-only.

Commit

Permalink
gif動画をwebmに置き換え
Browse files Browse the repository at this point in the history
  • Loading branch information
sapphi-red committed Dec 31, 2021
1 parent 2cd670a commit 7cbbfbf
Show file tree
Hide file tree
Showing 23 changed files with 45 additions and 69 deletions.
2 changes: 0 additions & 2 deletions client/package.json
Expand Up @@ -13,9 +13,7 @@
"classnames": "2.3.1",
"core-js": "3.19.1",
"fast-average-color": "7.0.1",
"gifler": "github:themadcreator/gifler#v0.3.0",
"normalize.css": "8.0.1",
"omggif": "1.0.10",
"react": "17.0.2",
"react-dom": "17.0.2",
"react-router-dom": "6.0.2",
Expand Down
52 changes: 7 additions & 45 deletions client/src/components/foundation/PausableMovie/PausableMovie.jsx
@@ -1,10 +1,6 @@
import classNames from 'classnames';
import { Animator, Decoder } from 'gifler';
import { GifReader } from 'omggif';
import React from 'react';

import { useFetch } from '../../../hooks/use_fetch';
import { fetchBinary } from '../../../utils/fetchers';
import { AspectRatioBox } from '../AspectRatioBox';
import { FontAwesomeIcon } from '../FontAwesomeIcon';

Expand All @@ -18,61 +14,27 @@ import { FontAwesomeIcon } from '../FontAwesomeIcon';
* @type {React.VFC<Props>}
*/
const PausableMovie = ({ src }) => {
const { data, isLoading } = useFetch(src, fetchBinary);
/** @type {React.RefObject<HTMLVideoElement>} */
const videoRef = React.useRef(null);

/** @type {React.RefObject<import('gifler').Animator>} */
const animatorRef = React.useRef(null);
/** @type {React.RefCallback<HTMLCanvasElement>} */
const canvasCallbackRef = React.useCallback(
(el) => {
animatorRef.current?.stop();
const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;

if (el === null || data === null) {
return;
}

// GIF を解析する
const reader = new GifReader(new Uint8Array(data));
const frames = Decoder.decodeFramesSync(reader);
const animator = new Animator(reader, frames);

animator.animateInCanvas(el);
animator.onFrame(frames[0]);

// 視覚効果 off のとき GIF を自動再生しない
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
setIsPlaying(false);
animator.stop();
} else {
setIsPlaying(true);
animator.start();
}

animatorRef.current = animator;
},
[data],
);

const [isPlaying, setIsPlaying] = React.useState(true);
const [isPlaying, setIsPlaying] = React.useState(!prefersReducedMotion);
const handleClick = React.useCallback(() => {
setIsPlaying((isPlaying) => {
if (isPlaying) {
animatorRef.current?.stop();
videoRef.current?.pause();
} else {
animatorRef.current?.start();
videoRef.current?.play();
}
return !isPlaying;
});
}, []);

if (isLoading || data === null) {
return null;
}

return (
<AspectRatioBox aspectHeight={1} aspectWidth={1}>
<button className="group relative block w-full h-full" onClick={handleClick} type="button">
<canvas ref={canvasCallbackRef} className="w-full" />
<video src={src} ref={videoRef} className="w-full" muted autoPlay={!prefersReducedMotion} loop />
<div
className={classNames(
'absolute left-1/2 top-1/2 flex items-center justify-center w-16 h-16 text-white text-3xl bg-black bg-opacity-50 rounded-full transform -translate-x-1/2 -translate-y-1/2',
Expand Down
2 changes: 1 addition & 1 deletion client/src/utils/get_path.js
Expand Up @@ -11,7 +11,7 @@ function getImagePath(imageId) {
* @returns {string}
*/
function getMoviePath(movieId) {
return `/movies/${movieId}.gif`;
return `/movies/${movieId}.webm`;
}

/**
Expand Down
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
32 changes: 32 additions & 0 deletions server/convert_gif2webm.js
@@ -0,0 +1,32 @@
import fs from 'fs/promises'
import path from 'path'
import { fetchFile } from '@ffmpeg/ffmpeg'
import { PUBLIC_PATH } from './src/paths.js'
import { ffmpeg } from './src/ffmpeg.js'

const doneFiles = []

;(async () => {
const dir = path.resolve(PUBLIC_PATH, './movies')

console.log('Loading ffmpeg')
await ffmpeg.load();
console.log('Loaded ffmpeg')

const files = await fs.readdir(dir)
console.log('Start convert files', files)
for (const file of files) {
if (path.extname(file) !== '.gif') continue
const name = path.basename(file, '.gif')
if (doneFiles.includes(name)) continue

const newFile = `${name}.webm`

const buf = await fetchFile(path.resolve(dir, file))
ffmpeg.FS('writeFile', 'file', buf)
await ffmpeg.run('-i', 'file', '-movflags', 'faststart', '-pix_fmt', 'yuv420p', '-row-mt', '1', newFile)
const exported = ffmpeg.FS('readFile', newFile)
await fs.writeFile(path.resolve(dir, `./${newFile}`), exported, 'binary')
console.log(`outputed: ${newFile}`)
}
})()
3 changes: 2 additions & 1 deletion server/package.json
Expand Up @@ -9,7 +9,8 @@
"prestart": "npm-run-all prestart:clean prestart:init",
"start": "babel-node --experimental-wasm-threads src/index.js",
"prestart:clean": "rimraf ../upload",
"prestart:init": "mkdirp ../upload/images ../upload/movies ../upload/sounds"
"prestart:init": "mkdirp ../upload/images ../upload/movies ../upload/sounds",
"convert:movie": "babel-node --experimental-wasm-threads convert_gif2webm.js"
},
"dependencies": {
"@babel/core": "7.16.0",
Expand Down
4 changes: 2 additions & 2 deletions server/src/converters/convert_movie.js
Expand Up @@ -13,15 +13,15 @@ async function convertMovie(buffer, options) {
.filter(Boolean)
.join(',');

const exportFile = `export.${options.extension ?? 'gif'}`;
const exportFile = `export.${options.extension ?? 'webm'}`;

if (ffmpeg.isLoaded() === false) {
await ffmpeg.load();
}

ffmpeg.FS('writeFile', 'file', new Uint8Array(buffer));

await ffmpeg.run(...['-i', 'file', '-t', '5', '-r', '10', '-vf', `crop=${cropOptions}`, '-an', exportFile]);
await ffmpeg.run(...['-i', 'file', '-movflags', 'faststart', '-t', '5', '-r', '10', '-vf', `crop=${cropOptions}`, '-an', exportFile]);

return ffmpeg.FS('readFile', exportFile);
}
Expand Down
2 changes: 1 addition & 1 deletion server/src/routes/api/movie.js
Expand Up @@ -9,7 +9,7 @@ import { convertMovie } from '../../converters/convert_movie';
import { UPLOAD_PATH } from '../../paths';

// 変換した動画の拡張子
const EXTENSION = 'gif';
const EXTENSION = 'webm';

const router = Router();

Expand Down
17 changes: 0 additions & 17 deletions yarn.lock
Expand Up @@ -1731,11 +1731,6 @@ block-stream@*:
dependencies:
inherits "~2.0.0"

bluebird@3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.0.2.tgz#c2be172246d7ae84bcf74338ff6a99cc50df9f9d"
integrity sha1-wr4XIkbXroS890M4/2qZzFDfn50=

body-parser@1.19.0:
version "1.19.0"
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a"
Expand Down Expand Up @@ -3078,13 +3073,6 @@ getpass@^0.1.1:
dependencies:
assert-plus "^1.0.0"

"gifler@github:themadcreator/gifler#v0.3.0":
version "0.3.0"
resolved "https://codeload.github.com/themadcreator/gifler/tar.gz/89484cb3db174c584a3138e89664f0167a7760c1"
dependencies:
bluebird "3.0.2"
omggif "1.0.x"

github-from-package@0.0.0:
version "0.0.0"
resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce"
Expand Down Expand Up @@ -4545,11 +4533,6 @@ obuf@^1.0.0, obuf@^1.1.2:
resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e"
integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==

omggif@1.0.10, omggif@1.0.x:
version "1.0.10"
resolved "https://registry.yarnpkg.com/omggif/-/omggif-1.0.10.tgz#ddaaf90d4a42f532e9e7cb3a95ecdd47f17c7b19"
integrity sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw==

on-finished@~2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947"
Expand Down

0 comments on commit 7cbbfbf

Please sign in to comment.