├── .gitignore
├── README.md
├── RoboFile.php
├── config-overrides.js
├── docs
├── CNAME
├── asset-manifest.json
├── favicon.ico
├── ffmpeg_all.js
├── ffmpeg_asm.js
├── index.html
├── locales
│ ├── en
│ │ └── translations.json
│ └── zh-cn
│ │ └── translations.json
├── manifest.json
├── precache-manifest.0afa75fe425fc5d069bd74dcfcc67115.js
├── service-worker.js
└── static
│ ├── css
│ ├── 2.167c390c.chunk.css
│ ├── 2.167c390c.chunk.css.map
│ ├── main.3913fafe.chunk.css
│ └── main.3913fafe.chunk.css.map
│ ├── js
│ ├── 2.cf004cbd.chunk.js
│ ├── 2.cf004cbd.chunk.js.map
│ ├── main.54787058.chunk.js
│ ├── main.54787058.chunk.js.map
│ ├── runtime~main.675b9bed.js
│ └── runtime~main.675b9bed.js.map
│ └── media
│ ├── icons-16.5fa1b8f2.ttf
│ ├── icons-16.6fdb722c.eot
│ ├── icons-16.cd1a2669.woff
│ ├── icons-20.14279fa7.eot
│ ├── icons-20.657723dc.woff
│ └── icons-20.90247cfd.ttf
├── package.json
├── public
├── favicon.ico
├── ffmpeg_all.js
├── ffmpeg_asm.js
├── index.html
├── locales
│ ├── en
│ │ └── translations.json
│ └── zh-cn
│ │ └── translations.json
└── manifest.json
├── src
├── App.css
├── App.js
├── App.test.js
├── _template
│ ├── component.js
│ └── screen.js
├── component
│ └── LangLink.js
├── i18n.js
├── index.css
├── index.js
├── index.scss
├── logo.svg
├── screen
│ ├── Login.js
│ ├── Test.js
│ └── Test2.js
├── serviceWorker.js
└── store
│ └── AppState.js
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.vscode
6 | /.pnp
7 | .pnp.js
8 |
9 | # testing
10 | /coverage
11 |
12 | # production
13 | /build
14 |
15 | # misc
16 | .DS_Store
17 | .env.local
18 | .env.development.local
19 | .env.test.local
20 | .env.production.local
21 |
22 | npm-debug.log*
23 | yarn-debug.log*
24 | yarn-error.log*
25 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # FFOnline
2 |
3 | video creating && merging just in browser ( use ffmpeg.js )
4 |
5 | ⚠️ heavily modifed from https://github.com/muaz-khan/Ffmpeg.js
6 |
7 | ## How to use
8 |
9 | - yarn
10 | - yarn start
11 |
12 | ## License
13 |
14 | - MIT
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/RoboFile.php:
--------------------------------------------------------------------------------
1 | copy_template( $name , 'screen' );
12 | }
13 |
14 | /**
15 | * 创建一个新的组件
16 | */
17 | public function newComponent( $name = null )
18 | {
19 | return $this->copy_template( $name , 'component' );
20 | }
21 |
22 | private function copy_template( $name , $type = 'component' )
23 | {
24 |
25 | $type = basename( $type );
26 | if( $type != 'component' ) $type = 'screen';
27 |
28 | if( $name === null ) $name =$this->ask("请输入组件名称");
29 | if( strlen( $name ) < 1 )
30 | {
31 | $this->say("错误组件的名称");
32 | return false;
33 | }
34 |
35 | $file_path = SRC . '/' . $type . '/'. ucfirst( $name ) . '.js';
36 |
37 | if( file_exists( $file_path ) )
38 | {
39 | $this->say("组件已存在");
40 | return false;
41 | }
42 |
43 | $file_tmp = SRC .'/_template/'. $type .'.js';
44 | if( !file_exists( $file_tmp ) )
45 | {
46 | $this->say("模板文件 $file_tmp 不存在");
47 | return false;
48 | }
49 |
50 | $content = file_get_contents( $file_tmp );
51 | $content = str_replace( 'ClassNamePlaceHolder' , ucfirst( $name ) , $content);
52 |
53 | file_put_contents( $file_path , $content );
54 |
55 | if( $type == 'component' ) $path = '..';
56 | else $path = '.';
57 | $this->_exec(" echo \"import " . $name . " from '" . $path . "/" . $type . "/" . $name . "'; \" | pbcopy");
58 |
59 | $this->say( "组件初始化完成,import 语句已经复制到剪贴板" );
60 |
61 | }
62 | }
--------------------------------------------------------------------------------
/config-overrides.js:
--------------------------------------------------------------------------------
1 | const {
2 | override,
3 | addDecoratorsLegacy,
4 | disableEsLint,
5 | overrideDevServer,
6 | watchAll
7 | } = require("customize-cra");
8 |
9 | module.exports = {
10 | webpack: override(
11 | addDecoratorsLegacy(),
12 | disableEsLint(),
13 | ),
14 | devServer: overrideDevServer(
15 | // dev server plugin
16 | watchAll()
17 | )
18 | };
--------------------------------------------------------------------------------
/docs/CNAME:
--------------------------------------------------------------------------------
1 | ff.ftqq.com
--------------------------------------------------------------------------------
/docs/asset-manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "files": {
3 | "main.css": "/static/css/main.3913fafe.chunk.css",
4 | "main.js": "/static/js/main.54787058.chunk.js",
5 | "main.js.map": "/static/js/main.54787058.chunk.js.map",
6 | "runtime~main.js": "/static/js/runtime~main.675b9bed.js",
7 | "runtime~main.js.map": "/static/js/runtime~main.675b9bed.js.map",
8 | "static/css/2.167c390c.chunk.css": "/static/css/2.167c390c.chunk.css",
9 | "static/js/2.cf004cbd.chunk.js": "/static/js/2.cf004cbd.chunk.js",
10 | "static/js/2.cf004cbd.chunk.js.map": "/static/js/2.cf004cbd.chunk.js.map",
11 | "index.html": "/index.html",
12 | "precache-manifest.0afa75fe425fc5d069bd74dcfcc67115.js": "/precache-manifest.0afa75fe425fc5d069bd74dcfcc67115.js",
13 | "service-worker.js": "/service-worker.js",
14 | "static/css/2.167c390c.chunk.css.map": "/static/css/2.167c390c.chunk.css.map",
15 | "static/css/main.3913fafe.chunk.css.map": "/static/css/main.3913fafe.chunk.css.map",
16 | "static/media/index.scss": "/static/media/icons-20.90247cfd.ttf"
17 | }
18 | }
--------------------------------------------------------------------------------
/docs/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/easychen/ffonline/831ecbf4eae93bb5456b4d5767d69238b32005c1/docs/favicon.ico
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 |
React App You need to enable JavaScript to run this app.
--------------------------------------------------------------------------------
/docs/locales/en/translations.json:
--------------------------------------------------------------------------------
1 | {
2 | "Welcome":"Welcome"
3 | }
--------------------------------------------------------------------------------
/docs/locales/zh-cn/translations.json:
--------------------------------------------------------------------------------
1 | {
2 | "Welcome":"欢迎"
3 | }
--------------------------------------------------------------------------------
/docs/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": ".",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/docs/precache-manifest.0afa75fe425fc5d069bd74dcfcc67115.js:
--------------------------------------------------------------------------------
1 | self.__precacheManifest = (self.__precacheManifest || []).concat([
2 | {
3 | "revision": "156431ce75468289481a2526365d7134",
4 | "url": "/index.html"
5 | },
6 | {
7 | "revision": "334de938eec1919706aa",
8 | "url": "/static/css/2.167c390c.chunk.css"
9 | },
10 | {
11 | "revision": "f442c368d2296ed38bff",
12 | "url": "/static/css/main.3913fafe.chunk.css"
13 | },
14 | {
15 | "revision": "334de938eec1919706aa",
16 | "url": "/static/js/2.cf004cbd.chunk.js"
17 | },
18 | {
19 | "revision": "f442c368d2296ed38bff",
20 | "url": "/static/js/main.54787058.chunk.js"
21 | },
22 | {
23 | "revision": "8eaaee04148a8164bbac",
24 | "url": "/static/js/runtime~main.675b9bed.js"
25 | },
26 | {
27 | "revision": "5fa1b8f25b4aa8787f70d5b6b7f0b90f",
28 | "url": "/static/media/icons-16.5fa1b8f2.ttf"
29 | },
30 | {
31 | "revision": "6fdb722c94434811f89a68eafc4531f8",
32 | "url": "/static/media/icons-16.6fdb722c.eot"
33 | },
34 | {
35 | "revision": "cd1a26696ebf17a89545a3f9067d7028",
36 | "url": "/static/media/icons-16.cd1a2669.woff"
37 | },
38 | {
39 | "revision": "14279fa7b78ac2230a015925b603a1f7",
40 | "url": "/static/media/icons-20.14279fa7.eot"
41 | },
42 | {
43 | "revision": "657723dc996cf0ae6b4c6110f5d4ddab",
44 | "url": "/static/media/icons-20.657723dc.woff"
45 | },
46 | {
47 | "revision": "90247cfdde1bd7b76d804a797d5dec56",
48 | "url": "/static/media/icons-20.90247cfd.ttf"
49 | }
50 | ]);
--------------------------------------------------------------------------------
/docs/service-worker.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Welcome to your Workbox-powered service worker!
3 | *
4 | * You'll need to register this file in your web app and you should
5 | * disable HTTP caching for this file too.
6 | * See https://goo.gl/nhQhGp
7 | *
8 | * The rest of the code is auto-generated. Please don't update this file
9 | * directly; instead, make changes to your Workbox build configuration
10 | * and re-run your build process.
11 | * See https://goo.gl/2aRDsh
12 | */
13 |
14 | importScripts("https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js");
15 |
16 | importScripts(
17 | "/precache-manifest.0afa75fe425fc5d069bd74dcfcc67115.js"
18 | );
19 |
20 | self.addEventListener('message', (event) => {
21 | if (event.data && event.data.type === 'SKIP_WAITING') {
22 | self.skipWaiting();
23 | }
24 | });
25 |
26 | workbox.core.clientsClaim();
27 |
28 | /**
29 | * The workboxSW.precacheAndRoute() method efficiently caches and responds to
30 | * requests for URLs in the manifest.
31 | * See https://goo.gl/S9QRab
32 | */
33 | self.__precacheManifest = [].concat(self.__precacheManifest || []);
34 | workbox.precaching.precacheAndRoute(self.__precacheManifest, {});
35 |
36 | workbox.routing.registerNavigationRoute(workbox.precaching.getCacheKeyForURL("/index.html"), {
37 |
38 | blacklist: [/^\/_/,/\/[^\/?]+\.[^\/]+$/],
39 | });
40 |
--------------------------------------------------------------------------------
/docs/static/css/main.3913fafe.chunk.css:
--------------------------------------------------------------------------------
1 | .row{display:flex;flex-direction:row}.row>*{flex:1 1}.center-800{max-width:800px;margin-left:auto;margin-right:auto}.bottom-100{margin-bottom:100px}.top-100{margin-top:100px}.top-20{margin-top:20px}.oneline{display:flex;flex-direction:row}.oneline>*{margin-right:10px}
2 | /*# sourceMappingURL=main.3913fafe.chunk.css.map */
--------------------------------------------------------------------------------
/docs/static/css/main.3913fafe.chunk.css.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["index.scss"],"names":[],"mappings":"AAMA,KAEI,YAAa,CACb,kBAAmB,CAHvB,OAMQ,QAAM,CAId,YAEI,eAAe,CACf,gBAAiB,CACjB,iBAAkB,CAGtB,YAEI,mBAAoB,CAGxB,SAEI,gBAAiB,CAGrB,QAEI,eAAgB,CAGpB,SAEI,YAAa,CACb,kBAAmB,CAHvB,WAMQ,iBAAiB","file":"main.3913fafe.chunk.css","sourcesContent":["@import \"~@blueprintjs/core/lib/css/blueprint.css\";\n@import \"~@blueprintjs/icons/lib/css/blueprint-icons.css\";\n@import \"~@blueprintjs/core/lib/scss/variables\";\n\n\n\n.row\n{\n display: flex;\n flex-direction: row;\n > *\n {\n flex:1;\n }\n}\n\n.center-800\n{\n max-width:800px;\n margin-left: auto;\n margin-right: auto;\n}\n\n.bottom-100\n{\n margin-bottom: 100px;\n}\n\n.top-100\n{\n margin-top: 100px;\n}\n\n.top-20\n{\n margin-top: 20px;\n}\n\n.oneline\n{\n display: flex;\n flex-direction: row;\n > *\n {\n margin-right:10px;\n }\n}\n\n"]}
--------------------------------------------------------------------------------
/docs/static/js/main.54787058.chunk.js:
--------------------------------------------------------------------------------
1 | (window.webpackJsonpmbapp=window.webpackJsonpmbapp||[]).push([[0],{116:function(e,t,a){},126:function(e,t,a){"use strict";a.r(t);var n,o,s,r,i,l,c,d,p,u,m,f,g=a(0),h=a.n(g),v=a(42),y=a.n(v),b=a(16),w=a(25),j=a(27),k=a(26),E=a(28),O=a(24),M=a(33),_=a(9),R=a(17),N=(n=Object(R.c)(),o=Object(_.b)("store"),Object(M.f)(s=n(s=o(s=Object(_.c)(s=function(e){function t(){return Object(b.a)(this,t),Object(j.a)(this,Object(k.a)(t).apply(this,arguments))}return Object(E.a)(t,e),Object(w.a)(t,[{key:"setLang",value:function(e){this.props.i18n.changeLanguage(e)}},{key:"render",value:function(){var e=this;return h.a.createElement("div",null,h.a.createElement("span",{className:"pointer",onClick:function(){return e.setLang("zh-cn")}},"\u4e2d\u6587")," \xb7 ",h.a.createElement("span",{className:"pointer",onClick:function(){return e.setLang("en")}},"English"))}}]),t}(g.Component))||s)||s)||s)||s),B=a(38),L=a.n(B),T=(r=Object(R.c)(),i=Object(_.b)("store"),Object(M.f)(l=r(l=i(l=Object(_.c)(l=function(e){function t(){return Object(b.a)(this,t),Object(j.a)(this,Object(k.a)(t).apply(this,arguments))}return Object(E.a)(t,e),Object(w.a)(t,[{key:"render",value:function(){var e=this.props.t,t=h.a.createElement("div",{className:"box"},h.a.createElement("div",{className:"title"},e("Welcome")),h.a.createElement("div",{className:"lang"}," ",h.a.createElement(N,null)));return h.a.createElement(L.a,{title:this.props.store.appname},t)}}]),t}(g.Component))||l)||l)||l)||l),A=a(40),P=a.n(A),S=a(129),C=a(128),U=a(45),W=a.n(U),x=(c=Object(R.c)(),d=Object(_.b)("store"),Object(M.f)(p=c(p=d(p=Object(_.c)(p=function(e){function t(){var e,a;Object(b.a)(this,t);for(var n=arguments.length,o=new Array(n),s=0;sffmpeg-asm.js file has been loaded.'),e.workerReady=!0,e.buffersReady?e.postMessage():console.log("buffer is not ready");else if("stdout"==a.type)F(a.data);else if("start"==a.type)e.setState({show_loading:!0}),F('ffmpeg-asm.js file received ffmpeg command.');else if("done"==a.type){console.log(JSON.stringify(a));var n=a.data[0];console.log(JSON.stringify(n));var o=new Blob([n.data],{type:"video/mp4"});e.PostBlob(o)}}}},{key:"postMessage",value:function(){console.log("in post message"),this.posted=!0,this.worker.postMessage({type:"command",arguments:["-loop","1","-i","cover.jpg","-i","audio.mp3","-q:v","1","-c:a","aac","-b:v","1450k","-b:a","96k","-bf","2","-g","90","-sc_threshold","0","-ar","32000","-strict","-2","-shortest","output.mp4"],files:[{data:new Uint8Array(this.cover_data),name:"cover.jpg",type:"image/jpeg"},{data:new Uint8Array(this.audio_data),name:"audio.mp3",type:"audio/mp3"}]})}},{key:"PostBlob",value:function(e){W()(e,"merged.mp4"),this.posted=!1,this.setState({show_loading:!1}),console.log("done")}},{key:"render",value:function(){var e=this,t=h.a.createElement("div",null,h.a.createElement("div",{className:" center-800 top-100"},h.a.createElement("h1",null,"jpg + mp3 \u5408\u6210 mp4"),h.a.createElement("div",{className:"oneline"},h.a.createElement("div",{className:"left"}," ",h.a.createElement(P.a,{fileTypes:[".jpg"],handleFiles:function(t){return e.cover_uploaded(t)}},h.a.createElement(S.a,{large:!0,icon:"media"},"\u9009\u62e9\u4e00\u5f20jpg\u56fe\u7247"))),h.a.createElement("div",{className:"middle"},h.a.createElement(P.a,{fileTypes:[".mp3"],handleFiles:function(t){return e.audio_uploaded(t)}},h.a.createElement(S.a,{large:!0,icon:"music"},"\u9009\u62e9\u4e00\u4e2amp3"))),h.a.createElement("div",{className:"right"},h.a.createElement(S.a,{large:!0,icon:"build",onClick:function(){return e.convert()}},"\u5f00\u59cb\u5408\u6210"))),h.a.createElement("div",{className:"row center-800 top-20"},this.state.show_loading&&h.a.createElement(C.a,null)),h.a.createElement("div",{className:"row center-800"},this.state.download_url&&h.a.createElement("a",{href:this.state.download_url},"Download Recorded Audio+Canvas file in MP4 container and play in VLC player!")),h.a.createElement("div",{className:"link top-20"},h.a.createElement(O.b,{to:"/t2"},"\u4e24\u4e2amp4\u5408\u6210\u4e00\u4e2a"))));return h.a.createElement(L.a,{title:this.props.store.appname},t)}}]),t}(g.Component))||p)||p)||p)||p);function F(e){console.log(e)}var J=(u=Object(R.c)(),m=Object(_.b)("store"),Object(M.f)(f=u(f=m(f=Object(_.c)(f=function(e){function t(){var e,a;Object(b.a)(this,t);for(var n=arguments.length,o=new Array(n),s=0;sffmpeg-asm.js file has been loaded.'),e.workerReady=!0,e.buffersReady?e.postMessage():console.log("buffer is not ready");else if("stdout"==a.type);else if("start"==a.type)e.setState({show_loading:!0}),D('ffmpeg-asm.js file received ffmpeg command.');else if("done"==a.type){console.log(JSON.stringify(a));var n=a.data[0];console.log(JSON.stringify(n));var o=new Blob([n.data],{type:"video/mp4"});console.log(JSON.stringify(o)),e.PostBlob(o)}}}},{key:"postMessage",value:function(){console.log("in post message"),this.posted=!0,this.worker.postMessage({type:"command",arguments:["-safe","0","-f","concat","-i","list.txt","-c:v","libx264","-vf","pad=ceil(iw/2)*2:ceil(ih/2)*2","-strict","-2","output.mp4"],files:[{data:new Uint8Array(this.ts1_data),name:"ts1.mp4"},{data:new Uint8Array(this.ts2_data),name:"ts2.mp4"},{data:(new TextEncoder).encode("file ts1.mp4\r\nfile ts2.mp4"),name:"list.txt"}]})}},{key:"PostBlob",value:function(e){W()(e,"all.mp4"),this.setState({show_loading:!1}),console.log("done")}},{key:"render",value:function(){var e=this,t=h.a.createElement("div",null,h.a.createElement("div",{className:" center-800 top-100"},h.a.createElement("h1",null,"\u4e24\u4e2a mp4 \u5408\u6210\u4e00\u4e2a"),h.a.createElement("div",{className:"oneline"},h.a.createElement("div",{className:"left"}," ",h.a.createElement(P.a,{fileTypes:[".mp4"],handleFiles:function(t){return e.ts1_uploaded(t)}},h.a.createElement(S.a,{large:!0,icon:"video"},"\u9009\u62e9\u4e00\u4e2aMP4"))),h.a.createElement("div",{className:"middle"},h.a.createElement(P.a,{fileTypes:[".mp4"],handleFiles:function(t){return e.ts2_uploaded(t)}},h.a.createElement(S.a,{large:!0,icon:"video"},"\u518d\u9009\u62e9\u4e00\u4e2a\u540c\u6837\u5206\u8fa8\u7387\u7684MP4"))),h.a.createElement("div",{className:"right"},h.a.createElement(S.a,{large:!0,icon:"build",onClick:function(){return e.convert()}},"\u5f00\u59cb\u5408\u6210"))),h.a.createElement("div",{className:"row center-800 top-20"},this.state.show_loading&&h.a.createElement(C.a,null)),h.a.createElement("div",{className:"row center-800"},this.state.download_url&&h.a.createElement("a",{href:this.state.download_url},"Download Recorded Audio+Canvas file in MP4 container and play in VLC player!")),h.a.createElement("div",{className:"link top-20"},h.a.createElement(O.b,{to:"/"},"jpg + mp3\u5408\u6210 mp4"))));return h.a.createElement(L.a,{title:this.props.store.appname},t)}}]),t}(g.Component))||f)||f)||f)||f);function D(e){console.log(e)}var Y=function(e){function t(){return Object(b.a)(this,t),Object(j.a)(this,Object(k.a)(t).apply(this,arguments))}return Object(E.a)(t,e),Object(w.a)(t,[{key:"render",value:function(){return h.a.createElement(O.a,null,h.a.createElement(M.c,null,h.a.createElement(M.a,{path:"/t2",component:J}),h.a.createElement(M.a,{path:"/",component:x}),h.a.createElement(M.a,{path:"/Login",component:T})))}}]),t}(g.Component);Boolean("localhost"===window.location.hostname||"[::1]"===window.location.hostname||window.location.hostname.match(/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/));var I,z,V=a(67),q=a(68),H=(a(99),a(2)),K=a(50),$=a.n(K),G=new(I=function e(){Object(b.a)(this,e),Object(V.a)(this,"appname",z,this)},z=Object(q.a)(I.prototype,"appname",[H.k],{configurable:!0,enumerable:!0,writable:!0,initializer:function(){return"EasyStarter"}}),I),Q=(a(116),a(53)),X=a(69),Z=a(70),ee=a.n(Z);Q.a.use(X.a).use(ee.a).use(R.b).init({fallbackLng:"zh-cn",ns:["translations"],defaultNS:"translations",debug:!1,saveMissing:!1,missingKeyHandler:function(e,t,a){var n=new URLSearchParams;n.append("lng",JSON.stringify(e)),n.append("ns",t),n.append("key",a);$.a.post("http://localhost:8088/misswords",n).data},interpolation:{escapeValue:!1},react:{wait:!0}});var te=Q.a;y.a.render(h.a.createElement(_.a,{store:G},h.a.createElement(R.a,{i18n:te},h.a.createElement(Y,null))),document.getElementById("root")),"serviceWorker"in navigator&&navigator.serviceWorker.ready.then((function(e){e.unregister()}))},77:function(e,t,a){e.exports=a(126)}},[[77,1,2]]]);
2 | //# sourceMappingURL=main.54787058.chunk.js.map
--------------------------------------------------------------------------------
/docs/static/js/main.54787058.chunk.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["component/LangLink.js","screen/Login.js","screen/Test.js","screen/Test2.js","App.js","serviceWorker.js","store/AppState.js","i18n.js","index.js"],"names":["LangLink","translate","inject","withRouter","observer","lang","this","props","i18n","changeLanguage","className","onClick","setLang","Component","Login","t","main","title","store","appname","Test","state","workerPath","location","href","replace","split","pop","buffersReady","workerReady","posted","worker","blob","URL","createObjectURL","Blob","type","Worker","revokeObjectURL","files","file","coverBlob","console","log","audioBlob","setState","fileReader1","FileReader","onload","e","cover_data","target","result","audio_data","postMessage","fileReader2","readAsArrayBuffer","processInWebWorker","onmessage","event","message","data","JSON","stringify","PostBlob","arguments","Uint8Array","name","FileSaver","fileTypes","handleFiles","cover_uploaded","large","icon","audio_uploaded","convert","show_loading","download_url","to","info","Test2","ts1Blob","ts2Blob","ts1_data","ts2_data","TextEncoder","encode","ts1_uploaded","ts2_uploaded","App","path","component","Boolean","window","hostname","match","observable","use","Backend","LanguageDetector","reactI18nextModule","init","fallbackLng","ns","defaultNS","debug","saveMissing","missingKeyHandler","lng","key","params","URLSearchParams","append","axios","post","dev_api","interpolation","escapeValue","react","wait","ReactDOM","render","AppState","document","getElementById","navigator","serviceWorker","ready","then","registration","unregister"],"mappings":"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","file":"static/js/main.54787058.chunk.js","sourcesContent":["import React, { Component } from 'react';\nimport { observer , inject } from 'mobx-react';\nimport { withRouter } from 'react-router-dom';\nimport { translate } from 'react-i18next';\n\n@withRouter\n@translate()\n@inject(\"store\")\n@observer\nexport default class LangLink extends Component\n{\n setLang( lang )\n {\n this.props.i18n.changeLanguage( lang );\n }\n render()\n {\n return \n this.setLang('zh-cn')}>中文 · this.setLang('en')}>English \n
;\n }\n}","import React, { Component } from 'react';\nimport { observer , inject } from 'mobx-react';\nimport { withRouter,Link } from 'react-router-dom';\nimport { translate } from 'react-i18next';\nimport LangLink from '../component/LangLink'; \nimport DocumentTitle from 'react-document-title';\n\n@withRouter\n@translate()\n@inject(\"store\")\n@observer\nexport default class Login extends Component\n{\n render()\n {\n const { t } = this.props;\n const main = ;\n return {main} ;\n }\n}","import React, { Component } from 'react';\nimport { observer , inject } from 'mobx-react';\nimport { Link } from \"react-router-dom\";\nimport { withRouter } from 'react-router-dom';\nimport { translate } from 'react-i18next';\n\nimport DocumentTitle from 'react-document-title';\nimport ReactFileReader from 'react-file-reader';\n\nimport { Button, ProgressBar } from \"@blueprintjs/core\";\nimport FileSaver from 'file-saver';\n\n@withRouter\n@translate()\n@inject(\"store\")\n@observer\nexport default class Test extends Component\n{\n state = {\"download_url\":false,\"show_loading\":false};\n \n componentDidMount()\n {\n this.workerPath = location.href.replace(location.href.split('/').pop(), '') + 'ffmpeg_all.js';\n this.buffersReady = false ;\n this.workerReady = false ;\n this.posted = false;\n this.worker = false;\n }\n\n processInWebWorker() \n {\n var blob = URL.createObjectURL(new Blob([`\n importScripts(\"${this.workerPath}\");\n var now = Date.now;\n function print(text) \n {\n postMessage({\"type\" : \"stdout\",\"data\" : text});};\n onmessage = function(event)\n {\n var message = event.data;\n if (message.type === \"command\") \n {\n var Module = {print: print,printErr: print,files: message.files || [],arguments: message.arguments || [],TOTAL_MEMORY: 943718400};\n \n postMessage({\"type\" : \"start\",\"data\" : Module.arguments.join(\" \")});\n \n postMessage({\"type\" : \"stdout\",\"data\" : \"Received command: \" +Module.arguments.join(\" \") +((Module.TOTAL_MEMORY) ? \". Processing with \" + Module.TOTAL_MEMORY + \" bits.\" : \"\")});var time = now();var result = ffmpeg_run(Module);var totalTime = now() - time;postMessage({\"type\" : \"stdout\",\"data\" : \"Finished processing (took \" + totalTime + \"ms)\"});postMessage({\"type\" : \"done\",\"data\" : result,\"time\" : totalTime});}};postMessage({\"type\" : \"ready\"});`], {\n type: 'application/javascript'\n }));\n \n var worker = new Worker(blob);\n URL.revokeObjectURL(blob);\n return worker;\n }\n \n cover_uploaded( files )\n {\n const file = files[0];\n this.coverBlob = new Blob([file],{type:'image/jpeg'}); \n\n console.log( file );\n }\n\n audio_uploaded( files )\n {\n const file = files[0];\n this.audioBlob = new Blob([file],{type:'audio/mp3'}); \n console.log( file );\n }\n\n convert()\n {\n this.setState({\"show_loading\":true});\n \n var fileReader1 = new FileReader();\n fileReader1.onload = (e)=> {\n console.log( e );\n \n this.cover_data = e.target.result;\n if( this.audio_data ) this.buffersReady = true;\n if( this.buffersReady && this.workerReady && !this.posted )\n this.postMessage();\n };\n\n var fileReader2 = new FileReader();\n fileReader2.onload = (e)=> {\n this.audio_data = e.target.result;\n if( this.cover_data ) this.buffersReady = true;\n if( this.buffersReady && this.workerReady && !this.posted )\n this.postMessage();\n };\n\n // console.log( typeof this.coverBlob )\n\n fileReader1.readAsArrayBuffer(this.coverBlob);\n fileReader2.readAsArrayBuffer(this.audioBlob);\n //console.log( \"convert\" );\n if( !this.worker )\n {\n this.worker = this.processInWebWorker();\n }\n\n this.worker.onmessage = (event)=> \n {\n var message = event.data;\n if (message.type == \"ready\") \n {\n log('ffmpeg-asm.js file has been loaded.');\n this.workerReady = true;\n\n if (this.buffersReady) \n this.postMessage();\n else\n console.log( \"buffer is not ready\" );\n \n } \n else if (message.type == \"stdout\") \n {\n log(message.data);\n }\n else if (message.type == \"start\") \n {\n this.setState({\"show_loading\":true});\n log('ffmpeg-asm.js file received ffmpeg command.');\n }\n else if (message.type == \"done\") \n {\n console.log(JSON.stringify(message));\n \n var result = message.data[0];\n console.log(JSON.stringify(result));\n \n var blob = new Blob([result.data], {\n type: 'video/mp4'\n });\n \n //console.log(JSON.stringify(blob));\n \n this.PostBlob(blob);\n }\n };\n }\n\n postMessage() \n {\n console.log( \"in post message\" );\n this.posted = true;\n\t\t\n\t\t/*\n\t\t\t[\n '-i', 'video.webm',\n '-i', 'audio.wav',\n\t\t\t\t'-s', '1280x720',\n '-c:v', 'mpeg4',\n '-c:a', 'aac',\n '-b:v', '1450k',\n '-b:a', '96k',\n\t\t\t\t'-bf', '2',\n\t\t\t\t'-g', '90',\n\t\t\t\t'-sc_threshold', '0',\n\t\t\t\t'-ar', '32000',\n '-strict', 'experimental', 'output.mp4'\n ]\n\t\t*/\n\t\t\n\t\tthis.worker.postMessage({\n type: 'command',\n arguments: [\n '-loop', '1',\n '-i', 'cover.jpg',\n '-i', 'audio.mp3',\n '-q:v', '1',\n // '-c:a', 'copy', // or aac\n '-c:a', 'aac',\n '-b:v', '1450k',\n '-b:a', '96k',\n\t\t\t\t'-bf', '2',\n\t\t\t\t'-g', '90',\n\t\t\t\t'-sc_threshold', '0',\n '-ar', '32000',\n // aac\n '-strict','-2',\n '-shortest',\n 'output.mp4'\n ],\n files: [\n {\n data: new Uint8Array(this.cover_data),\n name: 'cover.jpg',\n type:'image/jpeg'\n },\n {\n data: new Uint8Array(this.audio_data),\n name: 'audio.mp3',\n type:'audio/mp3'\n }\n ]\n });\n };\n\n PostBlob(blob)\n {\n \n FileSaver( blob , 'merged.mp4' );\n this.posted = false;\n this.setState({\"show_loading\":false});\n console.log( \"done\" );\n \n ///this.setState( {\"download_url\":URL.createObjectURL(blob)} )\n }\n \n render()\n {\n const main = \n \n
\n
jpg + mp3 合成 mp4 \n
\n
this.cover_uploaded(e)}>\n 选择一张jpg图片 \n
\n
\n this.audio_uploaded(e)}>\n 选择一个mp3 \n \n
\n
\n this.convert()}>开始合成 \n
\n
\n\n
\n {this.state.show_loading &&
}\n
\n\n
\n\n
两个mp4合成一个
\n\n
\n\n \n\n
;\n \n \n return {main} ;\n }\n}\n\nfunction log( info )\n{\n console.log( info );\n}","import React, { Component } from 'react';\nimport { observer , inject } from 'mobx-react';\nimport { Link } from \"react-router-dom\";\nimport { withRouter } from 'react-router-dom';\nimport { translate } from 'react-i18next';\n\nimport DocumentTitle from 'react-document-title';\nimport ReactFileReader from 'react-file-reader';\n\nimport { Button, ProgressBar } from \"@blueprintjs/core\";\nimport FileSaver from 'file-saver';\n\n@withRouter\n@translate()\n@inject(\"store\")\n@observer\nexport default class Test2 extends Component\n{\n state = {\"download_url\":false,\"show_loading\":false};\n \n componentDidMount()\n {\n this.workerPath = location.href.replace(location.href.split('/').pop(), '') + 'ffmpeg_all.js';\n this.buffersReady = false ;\n this.workerReady = false ;\n this.posted = false;\n this.worker = false;\n }\n\n processInWebWorker() \n {\n var blob = URL.createObjectURL(new Blob([`\n importScripts(\"${this.workerPath}\");\n var now = Date.now;\n function print(text) \n {\n postMessage({\"type\" : \"stdout\",\"data\" : text});};\n onmessage = function(event)\n {\n var message = event.data;\n if (message.type === \"command\") \n {\n var Module = {print: print,printErr: print,files: message.files || [],arguments: message.arguments || [],TOTAL_MEMORY: 943718400};\n \n postMessage({\"type\" : \"start\",\"data\" : Module.arguments.join(\" \")});\n \n postMessage({\"type\" : \"stdout\",\"data\" : \"Received command: \" +Module.arguments.join(\" \") +((Module.TOTAL_MEMORY) ? \". Processing with \" + Module.TOTAL_MEMORY + \" bits.\" : \"\")});var time = now();var result = ffmpeg_run(Module);var totalTime = now() - time;postMessage({\"type\" : \"stdout\",\"data\" : \"Finished processing (took \" + totalTime + \"ms)\"});postMessage({\"type\" : \"done\",\"data\" : result,\"time\" : totalTime});}};postMessage({\"type\" : \"ready\"});`], {\n type: 'application/javascript'\n }));\n \n var worker = new Worker(blob);\n URL.revokeObjectURL(blob);\n return worker;\n }\n \n ts1_uploaded( files )\n {\n const file = files[0];\n this.ts1Blob = new Blob([file],{type:'video/mp2t'}); \n\n console.log( file );\n }\n\n ts2_uploaded( files )\n {\n const file = files[0];\n this.ts2Blob = new Blob([file],{type:'video/mp2t'});\n console.log( file );\n }\n\n convert()\n {\n this.setState({\"show_loading\":true});\n \n var fileReader1 = new FileReader();\n fileReader1.onload = (e)=> {\n console.log( e );\n \n this.ts1_data = e.target.result;\n if( this.ts2_data ) this.buffersReady = true;\n if( this.buffersReady && this.workerReady && !this.posted )\n this.postMessage();\n };\n\n var fileReader2 = new FileReader();\n fileReader2.onload = (e)=> {\n this.ts2_data = e.target.result;\n if( this.ts1_data ) this.buffersReady = true;\n if( this.buffersReady && this.workerReady && !this.posted )\n this.postMessage();\n };\n\n // console.log( typeof this.ts1Blob )\n\n fileReader1.readAsArrayBuffer(this.ts1Blob);\n fileReader2.readAsArrayBuffer(this.ts2Blob);\n //console.log( \"convert\" );\n if( !this.worker )\n {\n this.worker = this.processInWebWorker();\n }\n\n this.worker.onmessage = (event)=> \n {\n var message = event.data;\n if (message.type == \"ready\") \n {\n log('ffmpeg-asm.js file has been loaded.');\n this.workerReady = true;\n\n if (this.buffersReady) \n this.postMessage();\n else\n console.log( \"buffer is not ready\" );\n \n } \n else if (message.type == \"stdout\") \n {\n // log(message.data);\n }\n else if (message.type == \"start\") \n {\n this.setState({\"show_loading\":true});\n log('ffmpeg-asm.js file received ffmpeg command.');\n }\n else if (message.type == \"done\") \n {\n console.log(JSON.stringify(message));\n \n var result = message.data[0];\n console.log(JSON.stringify(result));\n \n var blob = new Blob([result.data], {\n type: 'video/mp4'\n });\n \n console.log(JSON.stringify(blob));\n \n this.PostBlob(blob);\n }\n };\n }\n\n postMessage() \n {\n console.log( \"in post message\" );\n this.posted = true;\n\t\t\n\t\t/*\n\t\t\t[\n '-i', 'video.webm',\n '-i', 'ts2.wav',\n\t\t\t\t'-s', '1280x720',\n '-c:v', 'mpeg4',\n '-c:a', 'aac',\n '-b:v', '1450k',\n '-b:a', '96k',\n\t\t\t\t'-bf', '2',\n\t\t\t\t'-g', '90',\n\t\t\t\t'-sc_threshold', '0',\n\t\t\t\t'-ar', '32000',\n '-strict', 'experimental', 'output.mp4'\n ]\n\t\t*/\n\t\t\n\t\tthis.worker.postMessage({\n type: 'command',\n arguments: [\n '-safe', '0',\n '-f', 'concat',\n '-i', 'list.txt',\n '-c:v', 'libx264',\n '-vf','pad=ceil(iw/2)*2:ceil(ih/2)*2',\n '-strict','-2',\n // '-c:a', 'aac',\n // '-b:v', '1450k',\n // '-b:a', '96k',\n\t\t\t\t// '-bf', '2',\n\t\t\t\t// '-g', '90',\n\t\t\t\t// '-sc_threshold', '0',\n // '-ar', '32000',\n //'-s','TOTAL_MEMORY=943718400', // 900M\n // TOTAL_MEMORY=X with X higher than the current value 268435456\n // Cannot enlarge memory arrays. Either (1) compile with -s TOTAL_MEMORY=X with X higher than the current value 268435456, (2) compile with ALLOW_MEMORY_GROWTH which adjusts the size at runtime but prevents some optimizations, or (3) set Module.TOTAL_MEMORY before the program runs.\n 'output.mp4'\n ],\n files: [\n {\n data: new Uint8Array(this.ts1_data),\n name: 'ts1.mp4'\n },\n {\n data: new Uint8Array(this.ts2_data),\n name: 'ts2.mp4'\n },\n {\n //data: new Uint8Array(new Blob([\"file ts1.ts\"+\"\\r\\n\"+\"file ts2.ts\"],{\"type\":\"text/plain\"})),\n data: new TextEncoder().encode( \"file ts1.mp4\"+\"\\r\\n\"+\"file ts2.mp4\" ),\n name: 'list.txt' \n }\n ]\n });\n };\n\n PostBlob(blob)\n {\n FileSaver( blob , 'all.mp4' );\n this.setState({\"show_loading\":false});\n console.log( \"done\" );\n ///this.setState( {\"download_url\":URL.createObjectURL(blob)} )\n }\n \n render()\n {\n const main = \n\n
\n
两个 mp4 合成一个 \n
\n
this.ts1_uploaded(e)}>\n 选择一个MP4 \n
\n
\n this.ts2_uploaded(e)}>\n 再选择一个同样分辨率的MP4 \n \n
\n
\n this.convert()}>开始合成 \n
\n
\n\n
\n {this.state.show_loading &&
}\n
\n\n
\n\n
jpg + mp3合成 mp4
\n\n
\n \n \n
;\n \n \n return {main} ;\n }\n}\n\nfunction log( info )\n{\n console.log( info );\n}","import React, { Component } from 'react';\nimport { BrowserRouter as Router, Route, Switch } from 'react-router-dom';\n\nimport Login from './screen/Login';\nimport Test from './screen/Test'; \nimport Test2 from './screen/Test2'; \n\n\nclass App extends Component {\n render() {\n return (\n \n \n \n \n \n \n \n );\n }\n}\n\nexport default App;\n","// This optional code is used to register a service worker.\n// register() is not called by default.\n\n// This lets the app load faster on subsequent visits in production, and gives\n// it offline capabilities. However, it also means that developers (and users)\n// will only see deployed updates on subsequent visits to a page, after all the\n// existing tabs open on the page have been closed, since previously cached\n// resources are updated in the background.\n\n// To learn more about the benefits of this model and instructions on how to\n// opt-in, read http://bit.ly/CRA-PWA\n\nconst isLocalhost = Boolean(\n window.location.hostname === 'localhost' ||\n // [::1] is the IPv6 localhost address.\n window.location.hostname === '[::1]' ||\n // 127.0.0.1/8 is considered localhost for IPv4.\n window.location.hostname.match(\n /^127(?:\\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/\n )\n);\n\nexport function register(config) {\n if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {\n // The URL constructor is available in all browsers that support SW.\n const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);\n if (publicUrl.origin !== window.location.origin) {\n // Our service worker won't work if PUBLIC_URL is on a different origin\n // from what our page is served on. This might happen if a CDN is used to\n // serve assets; see https://github.com/facebook/create-react-app/issues/2374\n return;\n }\n\n window.addEventListener('load', () => {\n const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;\n\n if (isLocalhost) {\n // This is running on localhost. Let's check if a service worker still exists or not.\n checkValidServiceWorker(swUrl, config);\n\n // Add some additional logging to localhost, pointing developers to the\n // service worker/PWA documentation.\n navigator.serviceWorker.ready.then(() => {\n console.log(\n 'This web app is being served cache-first by a service ' +\n 'worker. To learn more, visit http://bit.ly/CRA-PWA'\n );\n });\n } else {\n // Is not localhost. Just register service worker\n registerValidSW(swUrl, config);\n }\n });\n }\n}\n\nfunction registerValidSW(swUrl, config) {\n navigator.serviceWorker\n .register(swUrl)\n .then(registration => {\n registration.onupdatefound = () => {\n const installingWorker = registration.installing;\n if (installingWorker == null) {\n return;\n }\n installingWorker.onstatechange = () => {\n if (installingWorker.state === 'installed') {\n if (navigator.serviceWorker.controller) {\n // At this point, the updated precached content has been fetched,\n // but the previous service worker will still serve the older\n // content until all client tabs are closed.\n console.log(\n 'New content is available and will be used when all ' +\n 'tabs for this page are closed. See http://bit.ly/CRA-PWA.'\n );\n\n // Execute callback\n if (config && config.onUpdate) {\n config.onUpdate(registration);\n }\n } else {\n // At this point, everything has been precached.\n // It's the perfect time to display a\n // \"Content is cached for offline use.\" message.\n console.log('Content is cached for offline use.');\n\n // Execute callback\n if (config && config.onSuccess) {\n config.onSuccess(registration);\n }\n }\n }\n };\n };\n })\n .catch(error => {\n console.error('Error during service worker registration:', error);\n });\n}\n\nfunction checkValidServiceWorker(swUrl, config) {\n // Check if the service worker can be found. If it can't reload the page.\n fetch(swUrl)\n .then(response => {\n // Ensure service worker exists, and that we really are getting a JS file.\n const contentType = response.headers.get('content-type');\n if (\n response.status === 404 ||\n (contentType != null && contentType.indexOf('javascript') === -1)\n ) {\n // No service worker found. Probably a different app. Reload the page.\n navigator.serviceWorker.ready.then(registration => {\n registration.unregister().then(() => {\n window.location.reload();\n });\n });\n } else {\n // Service worker found. Proceed as normal.\n registerValidSW(swUrl, config);\n }\n })\n .catch(() => {\n console.log(\n 'No internet connection found. App is running in offline mode.'\n );\n });\n}\n\nexport function unregister() {\n if ('serviceWorker' in navigator) {\n navigator.serviceWorker.ready.then(registration => {\n registration.unregister();\n });\n }\n}\n","import { observable, action } from \"mobx\";\nimport axios from 'axios';\n\nclass AppState\n{\n @observable appname = \"EasyStarter\"; \n}\n\nexport default new AppState();","import i18n from 'i18next';\nimport Backend from 'i18next-xhr-backend';\nimport LanguageDetector from 'i18next-browser-languagedetector';\nimport { reactI18nextModule } from 'react-i18next';\nimport axios from 'axios';\n\n\nconst dev_api = 'http://localhost:8088/';\n\ni18n\n .use(Backend)\n .use(LanguageDetector)\n .use(reactI18nextModule)\n .init({\n fallbackLng: 'zh-cn',\n\n // have a common namespace used around the full app\n ns: ['translations'],\n defaultNS: 'translations',\n\n debug: false,\n saveMissing:false,\n missingKeyHandler: function(lng, ns, key) {\n var params = new URLSearchParams();\n params.append(\"lng\" , JSON.stringify( lng ));\n params.append(\"ns\" , ns );\n params.append(\"key\" , key );\n const { data } = axios.post( dev_api + 'misswords' , params );\n },\n interpolation: {\n escapeValue: false, // not needed for react!!\n },\n\n react: {\n wait: true\n }\n });\n\n\nexport default i18n;","import React from 'react';\nimport ReactDOM from 'react-dom';\n\nimport App from './App';\nimport * as serviceWorker from './serviceWorker';\n\nimport { Provider } from \"mobx-react\";\nimport AppState from './store/AppState';\n\nimport './index.scss';\n\nimport { I18nextProvider } from 'react-i18next';\nimport i18n from './i18n'; \n\nReactDOM.render(\n \n \n \n , document.getElementById('root'));\nserviceWorker.unregister();\n\n"],"sourceRoot":""}
--------------------------------------------------------------------------------
/docs/static/js/runtime~main.675b9bed.js:
--------------------------------------------------------------------------------
1 | !function(e){function r(r){for(var n,p,l=r[0],a=r[1],f=r[2],c=0,s=[];c0.2%",
34 | "not dead",
35 | "not ie <= 11",
36 | "not op_mini all"
37 | ],
38 | "devDependencies": {
39 | "@babel/plugin-proposal-decorators": "^7.4.4",
40 | "customize-cra": "^0.2.11",
41 | "node-sass": "^7.0.0",
42 | "react-app-rewired": "^2.1.0"
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/easychen/ffonline/831ecbf4eae93bb5456b4d5767d69238b32005c1/public/favicon.ico
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
15 |
16 |
25 | React App
26 |
27 |
28 | You need to enable JavaScript to run this app.
29 |
30 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/public/locales/en/translations.json:
--------------------------------------------------------------------------------
1 | {
2 | "Welcome":"Welcome"
3 | }
--------------------------------------------------------------------------------
/public/locales/zh-cn/translations.json:
--------------------------------------------------------------------------------
1 | {
2 | "Welcome":"欢迎"
3 | }
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": ".",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/src/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | text-align: center;
3 | }
4 |
5 | .App-logo {
6 | animation: App-logo-spin infinite 20s linear;
7 | height: 40vmin;
8 | pointer-events: none;
9 | }
10 |
11 | .App-header {
12 | background-color: #282c34;
13 | min-height: 100vh;
14 | display: flex;
15 | flex-direction: column;
16 | align-items: center;
17 | justify-content: center;
18 | font-size: calc(10px + 2vmin);
19 | color: white;
20 | }
21 |
22 | .App-link {
23 | color: #61dafb;
24 | }
25 |
26 | @keyframes App-logo-spin {
27 | from {
28 | transform: rotate(0deg);
29 | }
30 | to {
31 | transform: rotate(360deg);
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
3 |
4 | import Login from './screen/Login';
5 | import Test from './screen/Test';
6 | import Test2 from './screen/Test2';
7 |
8 |
9 | class App extends Component {
10 | render() {
11 | return (
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | );
20 | }
21 | }
22 |
23 | export default App;
24 |
--------------------------------------------------------------------------------
/src/App.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | it('renders without crashing', () => {
6 | const div = document.createElement('div');
7 | ReactDOM.render( , div);
8 | ReactDOM.unmountComponentAtNode(div);
9 | });
10 |
--------------------------------------------------------------------------------
/src/_template/component.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { observer , inject } from 'mobx-react';
3 | import { withRouter } from 'react-router-dom';
4 | import { translate } from 'react-i18next';
5 |
6 | @withRouter
7 | @translate()
8 | @inject("store")
9 | @observer
10 | export default class ClassNamePlaceHolder extends Component
11 | {
12 | render()
13 | {
14 | return ClassNamePlaceHolder
;
15 | }
16 | }
--------------------------------------------------------------------------------
/src/_template/screen.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { observer , inject } from 'mobx-react';
3 | import { Link } from "react-router-dom";
4 | import { withRouter } from 'react-router-dom';
5 | import { translate } from 'react-i18next';
6 |
7 | import DocumentTitle from 'react-document-title';
8 |
9 | @withRouter
10 | @translate()
11 | @inject("store")
12 | @observer
13 | export default class ClassNamePlaceHolder extends Component
14 | {
15 | render()
16 | {
17 | const main = ClassNamePlaceHolder
;
18 | return {main} ;
19 | }
20 | }
--------------------------------------------------------------------------------
/src/component/LangLink.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { observer , inject } from 'mobx-react';
3 | import { withRouter } from 'react-router-dom';
4 | import { translate } from 'react-i18next';
5 |
6 | @withRouter
7 | @translate()
8 | @inject("store")
9 | @observer
10 | export default class LangLink extends Component
11 | {
12 | setLang( lang )
13 | {
14 | this.props.i18n.changeLanguage( lang );
15 | }
16 | render()
17 | {
18 | return
19 | this.setLang('zh-cn')}>中文 · this.setLang('en')}>English
20 |
;
21 | }
22 | }
--------------------------------------------------------------------------------
/src/i18n.js:
--------------------------------------------------------------------------------
1 | import i18n from 'i18next';
2 | import Backend from 'i18next-xhr-backend';
3 | import LanguageDetector from 'i18next-browser-languagedetector';
4 | import { reactI18nextModule } from 'react-i18next';
5 | import axios from 'axios';
6 |
7 |
8 | const dev_api = 'http://localhost:8088/';
9 |
10 | i18n
11 | .use(Backend)
12 | .use(LanguageDetector)
13 | .use(reactI18nextModule)
14 | .init({
15 | fallbackLng: 'zh-cn',
16 |
17 | // have a common namespace used around the full app
18 | ns: ['translations'],
19 | defaultNS: 'translations',
20 |
21 | debug: false,
22 | saveMissing:false,
23 | missingKeyHandler: function(lng, ns, key) {
24 | var params = new URLSearchParams();
25 | params.append("lng" , JSON.stringify( lng ));
26 | params.append("ns" , ns );
27 | params.append("key" , key );
28 | const { data } = axios.post( dev_api + 'misswords' , params );
29 | },
30 | interpolation: {
31 | escapeValue: false, // not needed for react!!
32 | },
33 |
34 | react: {
35 | wait: true
36 | }
37 | });
38 |
39 |
40 | export default i18n;
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 0;
4 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
5 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
6 | sans-serif;
7 | -webkit-font-smoothing: antialiased;
8 | -moz-osx-font-smoothing: grayscale;
9 | }
10 |
11 | code {
12 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
13 | monospace;
14 | }
15 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 |
4 | import App from './App';
5 | import * as serviceWorker from './serviceWorker';
6 |
7 | import { Provider } from "mobx-react";
8 | import AppState from './store/AppState';
9 |
10 | import './index.scss';
11 |
12 | import { I18nextProvider } from 'react-i18next';
13 | import i18n from './i18n';
14 |
15 | ReactDOM.render(
16 |
17 |
18 |
19 | , document.getElementById('root'));
20 | serviceWorker.unregister();
21 |
22 |
--------------------------------------------------------------------------------
/src/index.scss:
--------------------------------------------------------------------------------
1 | @import "~@blueprintjs/core/lib/css/blueprint.css";
2 | @import "~@blueprintjs/icons/lib/css/blueprint-icons.css";
3 | @import "~@blueprintjs/core/lib/scss/variables";
4 |
5 |
6 |
7 | .row
8 | {
9 | display: flex;
10 | flex-direction: row;
11 | > *
12 | {
13 | flex:1;
14 | }
15 | }
16 |
17 | .center-800
18 | {
19 | max-width:800px;
20 | margin-left: auto;
21 | margin-right: auto;
22 | }
23 |
24 | .bottom-100
25 | {
26 | margin-bottom: 100px;
27 | }
28 |
29 | .top-100
30 | {
31 | margin-top: 100px;
32 | }
33 |
34 | .top-20
35 | {
36 | margin-top: 20px;
37 | }
38 |
39 | .oneline
40 | {
41 | display: flex;
42 | flex-direction: row;
43 | > *
44 | {
45 | margin-right:10px;
46 | }
47 | }
48 |
49 |
--------------------------------------------------------------------------------
/src/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/screen/Login.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { observer , inject } from 'mobx-react';
3 | import { withRouter,Link } from 'react-router-dom';
4 | import { translate } from 'react-i18next';
5 | import LangLink from '../component/LangLink';
6 | import DocumentTitle from 'react-document-title';
7 |
8 | @withRouter
9 | @translate()
10 | @inject("store")
11 | @observer
12 | export default class Login extends Component
13 | {
14 | render()
15 | {
16 | const { t } = this.props;
17 | const main =
18 |
{t("Welcome")}
19 |
20 |
;
21 | return {main} ;
22 | }
23 | }
--------------------------------------------------------------------------------
/src/screen/Test.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { observer , inject } from 'mobx-react';
3 | import { Link } from "react-router-dom";
4 | import { withRouter } from 'react-router-dom';
5 | import { translate } from 'react-i18next';
6 |
7 | import DocumentTitle from 'react-document-title';
8 | import ReactFileReader from 'react-file-reader';
9 |
10 | import { Button, ProgressBar } from "@blueprintjs/core";
11 | import FileSaver from 'file-saver';
12 |
13 | @withRouter
14 | @translate()
15 | @inject("store")
16 | @observer
17 | export default class Test extends Component
18 | {
19 | state = {"download_url":false,"show_loading":false};
20 |
21 | componentDidMount()
22 | {
23 | this.workerPath = location.href.replace(location.href.split('/').pop(), '') + 'ffmpeg_all.js';
24 | this.buffersReady = false ;
25 | this.workerReady = false ;
26 | this.posted = false;
27 | this.worker = false;
28 | }
29 |
30 | processInWebWorker()
31 | {
32 | var blob = URL.createObjectURL(new Blob([`
33 | importScripts("${this.workerPath}");
34 | var now = Date.now;
35 | function print(text)
36 | {
37 | postMessage({"type" : "stdout","data" : text});};
38 | onmessage = function(event)
39 | {
40 | var message = event.data;
41 | if (message.type === "command")
42 | {
43 | var Module = {print: print,printErr: print,files: message.files || [],arguments: message.arguments || [],TOTAL_MEMORY: 943718400};
44 |
45 | postMessage({"type" : "start","data" : Module.arguments.join(" ")});
46 |
47 | postMessage({"type" : "stdout","data" : "Received command: " +Module.arguments.join(" ") +((Module.TOTAL_MEMORY) ? ". Processing with " + Module.TOTAL_MEMORY + " bits." : "")});var time = now();var result = ffmpeg_run(Module);var totalTime = now() - time;postMessage({"type" : "stdout","data" : "Finished processing (took " + totalTime + "ms)"});postMessage({"type" : "done","data" : result,"time" : totalTime});}};postMessage({"type" : "ready"});`], {
48 | type: 'application/javascript'
49 | }));
50 |
51 | var worker = new Worker(blob);
52 | URL.revokeObjectURL(blob);
53 | return worker;
54 | }
55 |
56 | cover_uploaded( files )
57 | {
58 | const file = files[0];
59 | this.coverBlob = new Blob([file],{type:'image/jpeg'});
60 |
61 | console.log( file );
62 | }
63 |
64 | audio_uploaded( files )
65 | {
66 | const file = files[0];
67 | this.audioBlob = new Blob([file],{type:'audio/mp3'});
68 | console.log( file );
69 | }
70 |
71 | convert()
72 | {
73 | this.setState({"show_loading":true});
74 |
75 | var fileReader1 = new FileReader();
76 | fileReader1.onload = (e)=> {
77 | console.log( e );
78 |
79 | this.cover_data = e.target.result;
80 | if( this.audio_data ) this.buffersReady = true;
81 | if( this.buffersReady && this.workerReady && !this.posted )
82 | this.postMessage();
83 | };
84 |
85 | var fileReader2 = new FileReader();
86 | fileReader2.onload = (e)=> {
87 | this.audio_data = e.target.result;
88 | if( this.cover_data ) this.buffersReady = true;
89 | if( this.buffersReady && this.workerReady && !this.posted )
90 | this.postMessage();
91 | };
92 |
93 | // console.log( typeof this.coverBlob )
94 |
95 | fileReader1.readAsArrayBuffer(this.coverBlob);
96 | fileReader2.readAsArrayBuffer(this.audioBlob);
97 | //console.log( "convert" );
98 | if( !this.worker )
99 | {
100 | this.worker = this.processInWebWorker();
101 | }
102 |
103 | this.worker.onmessage = (event)=>
104 | {
105 | var message = event.data;
106 | if (message.type == "ready")
107 | {
108 | log('ffmpeg-asm.js file has been loaded.');
109 | this.workerReady = true;
110 |
111 | if (this.buffersReady)
112 | this.postMessage();
113 | else
114 | console.log( "buffer is not ready" );
115 |
116 | }
117 | else if (message.type == "stdout")
118 | {
119 | log(message.data);
120 | }
121 | else if (message.type == "start")
122 | {
123 | this.setState({"show_loading":true});
124 | log('ffmpeg-asm.js file received ffmpeg command.');
125 | }
126 | else if (message.type == "done")
127 | {
128 | console.log(JSON.stringify(message));
129 |
130 | var result = message.data[0];
131 | console.log(JSON.stringify(result));
132 |
133 | var blob = new Blob([result.data], {
134 | type: 'video/mp4'
135 | });
136 |
137 | //console.log(JSON.stringify(blob));
138 |
139 | this.PostBlob(blob);
140 | }
141 | };
142 | }
143 |
144 | postMessage()
145 | {
146 | console.log( "in post message" );
147 | this.posted = true;
148 |
149 | /*
150 | [
151 | '-i', 'video.webm',
152 | '-i', 'audio.wav',
153 | '-s', '1280x720',
154 | '-c:v', 'mpeg4',
155 | '-c:a', 'aac',
156 | '-b:v', '1450k',
157 | '-b:a', '96k',
158 | '-bf', '2',
159 | '-g', '90',
160 | '-sc_threshold', '0',
161 | '-ar', '32000',
162 | '-strict', 'experimental', 'output.mp4'
163 | ]
164 | */
165 |
166 | this.worker.postMessage({
167 | type: 'command',
168 | arguments: [
169 | '-loop', '1',
170 | '-i', 'cover.jpg',
171 | '-i', 'audio.mp3',
172 | '-q:v', '1',
173 | // '-c:a', 'copy', // or aac
174 | '-c:a', 'aac',
175 | '-b:v', '1450k',
176 | '-b:a', '96k',
177 | '-bf', '2',
178 | '-g', '90',
179 | '-sc_threshold', '0',
180 | '-ar', '32000',
181 | // aac
182 | '-strict','-2',
183 | '-shortest',
184 | 'output.mp4'
185 | ],
186 | files: [
187 | {
188 | data: new Uint8Array(this.cover_data),
189 | name: 'cover.jpg',
190 | type:'image/jpeg'
191 | },
192 | {
193 | data: new Uint8Array(this.audio_data),
194 | name: 'audio.mp3',
195 | type:'audio/mp3'
196 | }
197 | ]
198 | });
199 | };
200 |
201 | PostBlob(blob)
202 | {
203 |
204 | FileSaver( blob , 'merged.mp4' );
205 | this.posted = false;
206 | this.setState({"show_loading":false});
207 | console.log( "done" );
208 |
209 | ///this.setState( {"download_url":URL.createObjectURL(blob)} )
210 | }
211 |
212 | render()
213 | {
214 | const main =
215 |
216 |
217 |
jpg + mp3 合成 mp4
218 |
219 |
this.cover_uploaded(e)}>
220 | 选择一张jpg图片
221 |
222 |
223 | this.audio_uploaded(e)}>
224 | 选择一个mp3
225 |
226 |
227 |
228 | this.convert()}>开始合成
229 |
230 |
231 |
232 |
233 | {this.state.show_loading &&
}
234 |
235 |
236 |
239 |
240 |
两个mp4合成一个
241 |
242 |
243 |
244 |
245 |
246 |
;
247 |
248 |
249 | return {main} ;
250 | }
251 | }
252 |
253 | function log( info )
254 | {
255 | console.log( info );
256 | }
--------------------------------------------------------------------------------
/src/screen/Test2.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { observer , inject } from 'mobx-react';
3 | import { Link } from "react-router-dom";
4 | import { withRouter } from 'react-router-dom';
5 | import { translate } from 'react-i18next';
6 |
7 | import DocumentTitle from 'react-document-title';
8 | import ReactFileReader from 'react-file-reader';
9 |
10 | import { Button, ProgressBar } from "@blueprintjs/core";
11 | import FileSaver from 'file-saver';
12 |
13 | @withRouter
14 | @translate()
15 | @inject("store")
16 | @observer
17 | export default class Test2 extends Component
18 | {
19 | state = {"download_url":false,"show_loading":false};
20 |
21 | componentDidMount()
22 | {
23 | this.workerPath = location.href.replace(location.href.split('/').pop(), '') + 'ffmpeg_all.js';
24 | this.buffersReady = false ;
25 | this.workerReady = false ;
26 | this.posted = false;
27 | this.worker = false;
28 | }
29 |
30 | processInWebWorker()
31 | {
32 | var blob = URL.createObjectURL(new Blob([`
33 | importScripts("${this.workerPath}");
34 | var now = Date.now;
35 | function print(text)
36 | {
37 | postMessage({"type" : "stdout","data" : text});};
38 | onmessage = function(event)
39 | {
40 | var message = event.data;
41 | if (message.type === "command")
42 | {
43 | var Module = {print: print,printErr: print,files: message.files || [],arguments: message.arguments || [],TOTAL_MEMORY: 943718400};
44 |
45 | postMessage({"type" : "start","data" : Module.arguments.join(" ")});
46 |
47 | postMessage({"type" : "stdout","data" : "Received command: " +Module.arguments.join(" ") +((Module.TOTAL_MEMORY) ? ". Processing with " + Module.TOTAL_MEMORY + " bits." : "")});var time = now();var result = ffmpeg_run(Module);var totalTime = now() - time;postMessage({"type" : "stdout","data" : "Finished processing (took " + totalTime + "ms)"});postMessage({"type" : "done","data" : result,"time" : totalTime});}};postMessage({"type" : "ready"});`], {
48 | type: 'application/javascript'
49 | }));
50 |
51 | var worker = new Worker(blob);
52 | URL.revokeObjectURL(blob);
53 | return worker;
54 | }
55 |
56 | ts1_uploaded( files )
57 | {
58 | const file = files[0];
59 | this.ts1Blob = new Blob([file],{type:'video/mp2t'});
60 |
61 | console.log( file );
62 | }
63 |
64 | ts2_uploaded( files )
65 | {
66 | const file = files[0];
67 | this.ts2Blob = new Blob([file],{type:'video/mp2t'});
68 | console.log( file );
69 | }
70 |
71 | convert()
72 | {
73 | this.setState({"show_loading":true});
74 |
75 | var fileReader1 = new FileReader();
76 | fileReader1.onload = (e)=> {
77 | console.log( e );
78 |
79 | this.ts1_data = e.target.result;
80 | if( this.ts2_data ) this.buffersReady = true;
81 | if( this.buffersReady && this.workerReady && !this.posted )
82 | this.postMessage();
83 | };
84 |
85 | var fileReader2 = new FileReader();
86 | fileReader2.onload = (e)=> {
87 | this.ts2_data = e.target.result;
88 | if( this.ts1_data ) this.buffersReady = true;
89 | if( this.buffersReady && this.workerReady && !this.posted )
90 | this.postMessage();
91 | };
92 |
93 | // console.log( typeof this.ts1Blob )
94 |
95 | fileReader1.readAsArrayBuffer(this.ts1Blob);
96 | fileReader2.readAsArrayBuffer(this.ts2Blob);
97 | //console.log( "convert" );
98 | if( !this.worker )
99 | {
100 | this.worker = this.processInWebWorker();
101 | }
102 |
103 | this.worker.onmessage = (event)=>
104 | {
105 | var message = event.data;
106 | if (message.type == "ready")
107 | {
108 | log('ffmpeg-asm.js file has been loaded.');
109 | this.workerReady = true;
110 |
111 | if (this.buffersReady)
112 | this.postMessage();
113 | else
114 | console.log( "buffer is not ready" );
115 |
116 | }
117 | else if (message.type == "stdout")
118 | {
119 | // log(message.data);
120 | }
121 | else if (message.type == "start")
122 | {
123 | this.setState({"show_loading":true});
124 | log('ffmpeg-asm.js file received ffmpeg command.');
125 | }
126 | else if (message.type == "done")
127 | {
128 | console.log(JSON.stringify(message));
129 |
130 | var result = message.data[0];
131 | console.log(JSON.stringify(result));
132 |
133 | var blob = new Blob([result.data], {
134 | type: 'video/mp4'
135 | });
136 |
137 | console.log(JSON.stringify(blob));
138 |
139 | this.PostBlob(blob);
140 | }
141 | };
142 | }
143 |
144 | postMessage()
145 | {
146 | console.log( "in post message" );
147 | this.posted = true;
148 |
149 | /*
150 | [
151 | '-i', 'video.webm',
152 | '-i', 'ts2.wav',
153 | '-s', '1280x720',
154 | '-c:v', 'mpeg4',
155 | '-c:a', 'aac',
156 | '-b:v', '1450k',
157 | '-b:a', '96k',
158 | '-bf', '2',
159 | '-g', '90',
160 | '-sc_threshold', '0',
161 | '-ar', '32000',
162 | '-strict', 'experimental', 'output.mp4'
163 | ]
164 | */
165 |
166 | this.worker.postMessage({
167 | type: 'command',
168 | arguments: [
169 | '-safe', '0',
170 | '-f', 'concat',
171 | '-i', 'list.txt',
172 | '-c:v', 'libx264',
173 | '-vf','pad=ceil(iw/2)*2:ceil(ih/2)*2',
174 | '-strict','-2',
175 | // '-c:a', 'aac',
176 | // '-b:v', '1450k',
177 | // '-b:a', '96k',
178 | // '-bf', '2',
179 | // '-g', '90',
180 | // '-sc_threshold', '0',
181 | // '-ar', '32000',
182 | //'-s','TOTAL_MEMORY=943718400', // 900M
183 | // TOTAL_MEMORY=X with X higher than the current value 268435456
184 | // Cannot enlarge memory arrays. Either (1) compile with -s TOTAL_MEMORY=X with X higher than the current value 268435456, (2) compile with ALLOW_MEMORY_GROWTH which adjusts the size at runtime but prevents some optimizations, or (3) set Module.TOTAL_MEMORY before the program runs.
185 | 'output.mp4'
186 | ],
187 | files: [
188 | {
189 | data: new Uint8Array(this.ts1_data),
190 | name: 'ts1.mp4'
191 | },
192 | {
193 | data: new Uint8Array(this.ts2_data),
194 | name: 'ts2.mp4'
195 | },
196 | {
197 | //data: new Uint8Array(new Blob(["file ts1.ts"+"\r\n"+"file ts2.ts"],{"type":"text/plain"})),
198 | data: new TextEncoder().encode( "file ts1.mp4"+"\r\n"+"file ts2.mp4" ),
199 | name: 'list.txt'
200 | }
201 | ]
202 | });
203 | };
204 |
205 | PostBlob(blob)
206 | {
207 | FileSaver( blob , 'all.mp4' );
208 | this.setState({"show_loading":false});
209 | console.log( "done" );
210 | ///this.setState( {"download_url":URL.createObjectURL(blob)} )
211 | }
212 |
213 | render()
214 | {
215 | const main =
216 |
217 |
218 |
两个 mp4 合成一个
219 |
220 |
this.ts1_uploaded(e)}>
221 | 选择一个MP4
222 |
223 |
224 | this.ts2_uploaded(e)}>
225 | 再选择一个同样分辨率的MP4
226 |
227 |
228 |
229 | this.convert()}>开始合成
230 |
231 |
232 |
233 |
234 | {this.state.show_loading &&
}
235 |
236 |
237 |
240 |
241 |
jpg + mp3合成 mp4
242 |
243 |
244 |
245 |
246 |
;
247 |
248 |
249 | return {main} ;
250 | }
251 | }
252 |
253 | function log( info )
254 | {
255 | console.log( info );
256 | }
--------------------------------------------------------------------------------
/src/serviceWorker.js:
--------------------------------------------------------------------------------
1 | // This optional code is used to register a service worker.
2 | // register() is not called by default.
3 |
4 | // This lets the app load faster on subsequent visits in production, and gives
5 | // it offline capabilities. However, it also means that developers (and users)
6 | // will only see deployed updates on subsequent visits to a page, after all the
7 | // existing tabs open on the page have been closed, since previously cached
8 | // resources are updated in the background.
9 |
10 | // To learn more about the benefits of this model and instructions on how to
11 | // opt-in, read http://bit.ly/CRA-PWA
12 |
13 | const isLocalhost = Boolean(
14 | window.location.hostname === 'localhost' ||
15 | // [::1] is the IPv6 localhost address.
16 | window.location.hostname === '[::1]' ||
17 | // 127.0.0.1/8 is considered localhost for IPv4.
18 | window.location.hostname.match(
19 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
20 | )
21 | );
22 |
23 | export function register(config) {
24 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
25 | // The URL constructor is available in all browsers that support SW.
26 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
27 | if (publicUrl.origin !== window.location.origin) {
28 | // Our service worker won't work if PUBLIC_URL is on a different origin
29 | // from what our page is served on. This might happen if a CDN is used to
30 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374
31 | return;
32 | }
33 |
34 | window.addEventListener('load', () => {
35 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
36 |
37 | if (isLocalhost) {
38 | // This is running on localhost. Let's check if a service worker still exists or not.
39 | checkValidServiceWorker(swUrl, config);
40 |
41 | // Add some additional logging to localhost, pointing developers to the
42 | // service worker/PWA documentation.
43 | navigator.serviceWorker.ready.then(() => {
44 | console.log(
45 | 'This web app is being served cache-first by a service ' +
46 | 'worker. To learn more, visit http://bit.ly/CRA-PWA'
47 | );
48 | });
49 | } else {
50 | // Is not localhost. Just register service worker
51 | registerValidSW(swUrl, config);
52 | }
53 | });
54 | }
55 | }
56 |
57 | function registerValidSW(swUrl, config) {
58 | navigator.serviceWorker
59 | .register(swUrl)
60 | .then(registration => {
61 | registration.onupdatefound = () => {
62 | const installingWorker = registration.installing;
63 | if (installingWorker == null) {
64 | return;
65 | }
66 | installingWorker.onstatechange = () => {
67 | if (installingWorker.state === 'installed') {
68 | if (navigator.serviceWorker.controller) {
69 | // At this point, the updated precached content has been fetched,
70 | // but the previous service worker will still serve the older
71 | // content until all client tabs are closed.
72 | console.log(
73 | 'New content is available and will be used when all ' +
74 | 'tabs for this page are closed. See http://bit.ly/CRA-PWA.'
75 | );
76 |
77 | // Execute callback
78 | if (config && config.onUpdate) {
79 | config.onUpdate(registration);
80 | }
81 | } else {
82 | // At this point, everything has been precached.
83 | // It's the perfect time to display a
84 | // "Content is cached for offline use." message.
85 | console.log('Content is cached for offline use.');
86 |
87 | // Execute callback
88 | if (config && config.onSuccess) {
89 | config.onSuccess(registration);
90 | }
91 | }
92 | }
93 | };
94 | };
95 | })
96 | .catch(error => {
97 | console.error('Error during service worker registration:', error);
98 | });
99 | }
100 |
101 | function checkValidServiceWorker(swUrl, config) {
102 | // Check if the service worker can be found. If it can't reload the page.
103 | fetch(swUrl)
104 | .then(response => {
105 | // Ensure service worker exists, and that we really are getting a JS file.
106 | const contentType = response.headers.get('content-type');
107 | if (
108 | response.status === 404 ||
109 | (contentType != null && contentType.indexOf('javascript') === -1)
110 | ) {
111 | // No service worker found. Probably a different app. Reload the page.
112 | navigator.serviceWorker.ready.then(registration => {
113 | registration.unregister().then(() => {
114 | window.location.reload();
115 | });
116 | });
117 | } else {
118 | // Service worker found. Proceed as normal.
119 | registerValidSW(swUrl, config);
120 | }
121 | })
122 | .catch(() => {
123 | console.log(
124 | 'No internet connection found. App is running in offline mode.'
125 | );
126 | });
127 | }
128 |
129 | export function unregister() {
130 | if ('serviceWorker' in navigator) {
131 | navigator.serviceWorker.ready.then(registration => {
132 | registration.unregister();
133 | });
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/src/store/AppState.js:
--------------------------------------------------------------------------------
1 | import { observable, action } from "mobx";
2 | import axios from 'axios';
3 |
4 | class AppState
5 | {
6 | @observable appname = "EasyStarter";
7 | }
8 |
9 | export default new AppState();
--------------------------------------------------------------------------------