├── .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
-------------------------------------------------------------------------------- /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
{t(\"Welcome\")}
\n
\n
;\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 \n
\n
\n this.audio_uploaded(e)}>\n \n \n
\n
\n \n
\n
\n\n
\n {this.state.show_loading && }\n
\n\n
\n { this.state.download_url && Download Recorded Audio+Canvas file in MP4 container and play in VLC player! }\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 \n
\n
\n this.ts2_uploaded(e)}>\n \n \n
\n
\n \n
\n
\n\n
\n {this.state.show_loading && }\n
\n\n
\n { this.state.download_url && Download Recorded Audio+Canvas file in MP4 container and play in VLC player! }\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 | 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 | 221 |
222 |
223 | this.audio_uploaded(e)}> 224 | 225 | 226 |
227 |
228 | 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 | 222 |
223 |
224 | this.ts2_uploaded(e)}> 225 | 226 | 227 |
228 |
229 | 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(); --------------------------------------------------------------------------------