SanHQin 6 mēneši atpakaļ
vecāks
revīzija
a812f81bf2

+ 147 - 118
build/webpack.prod.conf.js

@@ -14,132 +14,161 @@ const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
 const env = require('../config/prod.env')
 
 const webpackConfig = merge(baseWebpackConfig, {
-  module: {
-    rules: utils.styleLoaders({
-      sourceMap: config.build.productionSourceMap,
-      extract: true,
-      usePostCSS: true
-    })
-  },
-  devtool: config.build.productionSourceMap ? config.build.devtool : false,
-  output: {
-    path: config.build.assetsRoot,
-    filename: utils.assetsPath('js/[name].[chunkhash].js'),
-    chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
-  },
-  plugins: [
-    // http://vuejs.github.io/vue-loader/en/workflow/production.html
-    new webpack.DefinePlugin({
-      'process.env': env
-    }),
-    new UglifyJsPlugin({
-      uglifyOptions: {
-        compress: {
-          warnings: false
-        }
-      },
-      sourceMap: config.build.productionSourceMap,
-      parallel: true
-    }),
-    // extract css into its own file
-    new ExtractTextPlugin({
-      filename: utils.assetsPath('css/[name].[contenthash].css'),
-      // Setting the following option to `false` will not extract CSS from codesplit chunks.
-      // Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack.
-      // It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`, 
-      // increasing file size: https://github.com/vuejs-templates/webpack/issues/1110
-      allChunks: true,
-    }),
-    // Compress extracted CSS. We are using this plugin so that possible
-    // duplicated CSS from different components can be deduped.
-    new OptimizeCSSPlugin({
-      cssProcessorOptions: config.build.productionSourceMap
-        ? { safe: true, map: { inline: false } }
-        : { safe: true }
-    }),
-    // generate dist index.html with correct asset hash for caching.
-    // you can customize output by editing /index.html
-    // see https://github.com/ampedandwired/html-webpack-plugin
-    new HtmlWebpackPlugin({
-      filename: config.build.index,
-      template: 'index.html',
-      inject: true,
-      minify: {
-        removeComments: true,
-        collapseWhitespace: true,
-        removeAttributeQuotes: true
-        // more options:
-        // https://github.com/kangax/html-minifier#options-quick-reference
-      },
-      // necessary to consistently work with multiple chunks via CommonsChunkPlugin
-      chunksSortMode: 'dependency'
-    }),
-    // keep module.id stable when vendor modules does not change
-    new webpack.HashedModuleIdsPlugin(),
-    // enable scope hoisting
-    new webpack.optimize.ModuleConcatenationPlugin(),
-    // split vendor js into its own file
-    new webpack.optimize.CommonsChunkPlugin({
-      name: 'vendor',
-      minChunks (module) {
-        // any required modules inside node_modules are extracted to vendor
-        return (
-          module.resource &&
-          /\.js$/.test(module.resource) &&
-          module.resource.indexOf(
-            path.join(__dirname, '../node_modules')
-          ) === 0
-        )
-      }
-    }),
-    // extract webpack runtime and module manifest to its own file in order to
-    // prevent vendor hash from being updated whenever app bundle is updated
-    new webpack.optimize.CommonsChunkPlugin({
-      name: 'manifest',
-      minChunks: Infinity
-    }),
-    // This instance extracts shared chunks from code splitted chunks and bundles them
-    // in a separate chunk, similar to the vendor chunk
-    // see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk
-    new webpack.optimize.CommonsChunkPlugin({
-      name: 'app',
-      async: 'vendor-async',
-      children: true,
-      minChunks: 3
-    }),
+	module: {
+		rules: utils.styleLoaders({
+			sourceMap: config.build.productionSourceMap,
+			extract: true,
+			usePostCSS: true
+		})
+	},
+	devtool: config.build.productionSourceMap ? config.build.devtool : false,
+	output: {
+		path: config.build.assetsRoot,
+		filename: utils.assetsPath('js/[name].[chunkhash].js'),
+		chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
+	},
+	plugins: [
+		// http://vuejs.github.io/vue-loader/en/workflow/production.html
+		new webpack.DefinePlugin({
+			'process.env': env
+		}),
+		new UglifyJsPlugin({
+			uglifyOptions: {
+				compress: {
+					warnings: false
+				}
+			},
+			sourceMap: config.build.productionSourceMap,
+			parallel: true
+		}),
+		// extract css into its own file
+		new ExtractTextPlugin({
+			filename: utils.assetsPath('css/[name].[contenthash].css'),
+			// Setting the following option to `false` will not extract CSS from codesplit chunks.
+			// Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack.
+			// It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`, 
+			// increasing file size: https://github.com/vuejs-templates/webpack/issues/1110
+			allChunks: true,
+		}),
+		// Compress extracted CSS. We are using this plugin so that possible
+		// duplicated CSS from different components can be deduped.
+		new OptimizeCSSPlugin({
+			cssProcessorOptions: config.build.productionSourceMap
+				? { safe: true, map: { inline: false } }
+				: { safe: true }
+		}),
+		// generate dist index.html with correct asset hash for caching.
+		// you can customize output by editing /index.html
+		// see https://github.com/ampedandwired/html-webpack-plugin
+		// new HtmlWebpackPlugin({
+		//   filename: config.build.index,
+		//   template: 'index.html',
+		//   inject: true,
+		//   minify: {
+		//     removeComments: true,
+		//     collapseWhitespace: true,
+		//     removeAttributeQuotes: true
+		//     // more options:
+		//     // https://github.com/kangax/html-minifier#options-quick-reference
+		//   },
+		//   // necessary to consistently work with multiple chunks via CommonsChunkPlugin
+		//   chunksSortMode: 'dependency'
+		// }),
 
-    // copy custom static assets
-    new CopyWebpackPlugin([
-      {
-        from: path.resolve(__dirname, '../static'),
-        to: config.build.assetsSubDirectory,
-        ignore: ['.*']
-      }
-    ])
-  ]
+		// 多页面配置
+		new HtmlWebpackPlugin({
+			entry: "../src/main.js",
+			filename: 'index.html',
+			template: 'index.html',
+			inject: true,
+			chunks: ['index'],  // 对应入口文件
+			minify: {
+				removeComments: true,
+				collapseWhitespace: true,
+				removeAttributeQuotes: true
+			},
+			chunksSortMode: 'dependency'
+		}),
+
+		new HtmlWebpackPlugin({
+			entry: "../src/pages/ffmpeg/main.js",
+			filename: 'ffmpeg.html',
+			template: 'index.html',
+			inject: true,
+			chunks: ['ffmpeg'],  // 对应另一个入口文件
+			minify: {
+				removeComments: true,
+				collapseWhitespace: true,
+				removeAttributeQuotes: true
+			},
+			chunksSortMode: 'dependency'
+		}),
+		// keep module.id stable when vendor modules does not change
+		new webpack.HashedModuleIdsPlugin(),
+		// enable scope hoisting
+		new webpack.optimize.ModuleConcatenationPlugin(),
+		// split vendor js into its own file
+		new webpack.optimize.CommonsChunkPlugin({
+			name: 'vendor',
+			minChunks(module) {
+				// any required modules inside node_modules are extracted to vendor
+				return (
+					module.resource &&
+					/\.js$/.test(module.resource) &&
+					module.resource.indexOf(
+						path.join(__dirname, '../node_modules')
+					) === 0
+				)
+			}
+		}),
+		// extract webpack runtime and module manifest to its own file in order to
+		// prevent vendor hash from being updated whenever app bundle is updated
+		new webpack.optimize.CommonsChunkPlugin({
+			name: 'manifest',
+			minChunks: Infinity
+		}),
+		// This instance extracts shared chunks from code splitted chunks and bundles them
+		// in a separate chunk, similar to the vendor chunk
+		// see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk
+		new webpack.optimize.CommonsChunkPlugin({
+			name: 'app',
+			async: 'vendor-async',
+			children: true,
+			minChunks: 3
+		}),
+
+		// copy custom static assets
+		new CopyWebpackPlugin([
+			{
+				from: path.resolve(__dirname, '../static'),
+				to: config.build.assetsSubDirectory,
+				ignore: ['.*']
+			}
+		])
+	]
 })
 
 if (config.build.productionGzip) {
-  const CompressionWebpackPlugin = require('compression-webpack-plugin')
+	const CompressionWebpackPlugin = require('compression-webpack-plugin')
 
-  webpackConfig.plugins.push(
-    new CompressionWebpackPlugin({
-      asset: '[path].gz[query]',
-      algorithm: 'gzip',
-      test: new RegExp(
-        '\\.(' +
-        config.build.productionGzipExtensions.join('|') +
-        ')$'
-      ),
-      threshold: 10240,
-      minRatio: 0.8
-    })
-  )
+	webpackConfig.plugins.push(
+		new CompressionWebpackPlugin({
+			asset: '[path].gz[query]',
+			algorithm: 'gzip',
+			test: new RegExp(
+				'\\.(' +
+				config.build.productionGzipExtensions.join('|') +
+				')$'
+			),
+			threshold: 10240,
+			minRatio: 0.8
+		})
+	)
 }
 
 if (config.build.bundleAnalyzerReport) {
-  const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
-  webpackConfig.plugins.push(new BundleAnalyzerPlugin())
+	const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
+	webpackConfig.plugins.push(new BundleAnalyzerPlugin())
 }
 
 module.exports = webpackConfig

+ 63 - 0
dist/ffmpeg.html

@@ -0,0 +1,63 @@
+<!DOCTYPE html><html><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><title>PBL教师端</title><link rel="shortcut icon" type=image/x-icon href=static/logo.ico><style>@charset "utf-8";
+    /* @font-face {
+        font-family: 'Source Han Sans SC';
+        src: url('./static/SourceHanSans-Regular.otf') format('truetype');
+        font-weight: normal;
+        font-style: normal;
+      } */
+
+    div::-webkit-scrollbar {
+      /*滚动条整体样式*/
+      width: 6px;
+      /*高宽分别对应横竖滚动条的尺寸*/
+      height: 6px;
+    }
+
+    /*定义滚动条轨道 内阴影+圆角*/
+    div::-webkit-scrollbar-track {
+      border-radius: 10px;
+      background-color: rgba(0, 0, 0, 0.1);
+    }
+
+    /*定义滑块 内阴影+圆角*/
+    div::-webkit-scrollbar-thumb {
+      border-radius: 10px;
+      -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, .3);
+      background-color: rgba(0, 0, 0, 0.1);
+    }
+
+    html,
+    body {
+      height: 100%;
+      width: 100%;
+      background: #e6eaf0;
+      font-family: '黑体';
+    }</style></head><body><div id=app></div></body></html><script>function stopSafari() {
+    //阻止safari浏览器双击放大功能
+    let lastTouchEnd = 0  //更新手指弹起的时间
+    document.documentElement.addEventListener("touchstart", function (event) {
+      //多根手指同时按下屏幕,禁止默认行为
+      if (event.touches.length > 1) {
+        event.preventDefault();
+      }
+    });
+    document.documentElement.addEventListener("touchend", function (event) {
+      let now = (new Date()).getTime();
+      if (now - lastTouchEnd <= 300) {
+        //当两次手指弹起的时间小于300毫秒,认为双击屏幕行为
+        event.preventDefault();
+      } else { // 否则重新手指弹起的时间
+        lastTouchEnd = now;
+      }
+    }, false);
+    //阻止双指放大页面
+    document.documentElement.addEventListener("gesturestart", function (event) {
+      event.preventDefault();
+    });
+  }
+
+  window.onload = () => {
+    stopSafari();
+  }
+  
+  document.domain = document.domain.split(".").slice(-2).join(".");</script>

+ 1 - 1
dist/index.html

@@ -32,7 +32,7 @@
       width: 100%;
       background: #e6eaf0;
       font-family: '黑体';
-    }</style><link href=./static/css/app.5789dfc46182c96cda4a4d9744d96f90.css rel=stylesheet></head><body><div id=app></div><script type=text/javascript src=./static/js/manifest.3eade43afd174522bb8a.js></script><script type=text/javascript src=./static/js/vendor.6bffbf9058a42e825dbc.js></script><script type=text/javascript src=./static/js/app.9cc5a346959b1874f35c.js></script></body></html><script>function stopSafari() {
+    }</style></head><body><div id=app></div></body></html><script>function stopSafari() {
     //阻止safari浏览器双击放大功能
     let lastTouchEnd = 0  //更新手指弹起的时间
     document.documentElement.addEventListener("touchstart", function (event) {

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 0 - 0
dist/static/js/0.4f3b05586c3acc102a54.js


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 0 - 0
dist/static/js/0.4f3b05586c3acc102a54.js.map


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 0 - 0
dist/static/js/0.7da288988865a9378b49.js.map


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 0 - 0
dist/static/js/app.9cc5a346959b1874f35c.js.map


+ 2 - 2
dist/static/js/manifest.3eade43afd174522bb8a.js → dist/static/js/manifest.161e82026ac2ae03ab6f.js

@@ -1,2 +1,2 @@
-!function(e){var n=window.webpackJsonp;window.webpackJsonp=function(r,a,c){for(var i,u,f,s=0,l=[];s<r.length;s++)u=r[s],t[u]&&l.push(t[u][0]),t[u]=0;for(i in a)Object.prototype.hasOwnProperty.call(a,i)&&(e[i]=a[i]);for(n&&n(r,a,c);l.length;)l.shift()();if(c)for(s=0;s<c.length;s++)f=o(o.s=c[s]);return f};var r={},t={6:0};function o(n){if(r[n])return r[n].exports;var t=r[n]={i:n,l:!1,exports:{}};return e[n].call(t.exports,t,t.exports,o),t.l=!0,t.exports}o.e=function(e){var n=t[e];if(0===n)return new Promise(function(e){e()});if(n)return n[2];var r=new Promise(function(r,o){n=t[e]=[r,o]});n[2]=r;var a=document.getElementsByTagName("head")[0],c=document.createElement("script");c.type="text/javascript",c.charset="utf-8",c.async=!0,c.timeout=12e4,o.nc&&c.setAttribute("nonce",o.nc),c.src=o.p+"static/js/"+e+"."+{0:"7da288988865a9378b49",1:"14e8e8c7e44fc858e4a6",2:"94e1427bfc7ef0b4c685",3:"3a9f53a78da16650e6b8"}[e]+".js";var i=setTimeout(u,12e4);function u(){c.onerror=c.onload=null,clearTimeout(i);var n=t[e];0!==n&&(n&&n[1](new Error("Loading chunk "+e+" failed.")),t[e]=void 0)}return c.onerror=c.onload=u,a.appendChild(c),r},o.m=e,o.c=r,o.d=function(e,n,r){o.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:r})},o.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return o.d(n,"a",n),n},o.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},o.p="./",o.oe=function(e){throw console.error(e),e}}([]);
-//# sourceMappingURL=manifest.3eade43afd174522bb8a.js.map
+!function(e){var n=window.webpackJsonp;window.webpackJsonp=function(r,c,a){for(var i,u,f,s=0,l=[];s<r.length;s++)u=r[s],t[u]&&l.push(t[u][0]),t[u]=0;for(i in c)Object.prototype.hasOwnProperty.call(c,i)&&(e[i]=c[i]);for(n&&n(r,c,a);l.length;)l.shift()();if(a)for(s=0;s<a.length;s++)f=o(o.s=a[s]);return f};var r={},t={6:0};function o(n){if(r[n])return r[n].exports;var t=r[n]={i:n,l:!1,exports:{}};return e[n].call(t.exports,t,t.exports,o),t.l=!0,t.exports}o.e=function(e){var n=t[e];if(0===n)return new Promise(function(e){e()});if(n)return n[2];var r=new Promise(function(r,o){n=t[e]=[r,o]});n[2]=r;var c=document.getElementsByTagName("head")[0],a=document.createElement("script");a.type="text/javascript",a.charset="utf-8",a.async=!0,a.timeout=12e4,o.nc&&a.setAttribute("nonce",o.nc),a.src=o.p+"static/js/"+e+"."+{0:"4f3b05586c3acc102a54",1:"14e8e8c7e44fc858e4a6",2:"94e1427bfc7ef0b4c685",3:"3a9f53a78da16650e6b8"}[e]+".js";var i=setTimeout(u,12e4);function u(){a.onerror=a.onload=null,clearTimeout(i);var n=t[e];0!==n&&(n&&n[1](new Error("Loading chunk "+e+" failed.")),t[e]=void 0)}return a.onerror=a.onload=u,c.appendChild(a),r},o.m=e,o.c=r,o.d=function(e,n,r){o.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:r})},o.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return o.d(n,"a",n),n},o.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},o.p="./",o.oe=function(e){throw console.error(e),e}}([]);
+//# sourceMappingURL=manifest.161e82026ac2ae03ab6f.js.map

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 0 - 0
dist/static/js/manifest.161e82026ac2ae03ab6f.js.map


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 0 - 0
dist/static/js/vendor.de15001ce66f032e9274.js


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 0 - 0
dist/static/js/vendor.de15001ce66f032e9274.js.map


+ 97 - 0
src/pages/ffmpeg/App.vue

@@ -0,0 +1,97 @@
+<template>
+  <div>
+		<div class="ffmpeg">
+		<input type="file" @change="onFileChange" accept="video/mp4" />
+		<button @click="extractAudio()" :disabled="!videoFile">提取音频</button>
+		<audio v-if="audioUrl" :src="audioUrl" controls></audio>
+	</div>
+  </div>
+</template>
+
+<script>
+import FFmpeg from "@ffmpeg/ffmpeg";
+const { createFFmpeg, fetchFile } = FFmpeg;
+
+const ffmpeg = createFFmpeg({
+	corePath: './static/ffmpeg/ffmpeg-core.js',
+	log: true,
+});
+export default {
+  name: "App",
+  data() {
+    return {
+			videoFile: null,
+			audioUrl: null
+    };
+  },
+  methods: {
+		async onFileChange(event){
+			this.videoFile = event.target.files[0];
+		},
+		async extractAudio(file) {//提取音频
+			return new Promise(async(resolve) => {
+				try {
+					file = file || this.videoFile;
+					if (!file){
+						window.parent.postMessage({code:1,msg:'请选择视频文件'},'*');
+						resolve({code:1,msg:'请选择视频文件'});
+						return;
+					};
+
+					// 加载FFmpeg
+					if (!ffmpeg.isLoaded()) {
+						await ffmpeg.load();
+					}
+
+					// 将视频文件加载到FFmpeg
+					ffmpeg.FS("writeFile", "input.mp4", await fetchFile(file));
+
+					// 提取音频
+					await ffmpeg.run(
+						"-i",
+							"input.mp4",
+							"-q:a",
+							"0",
+							"-map",
+							"a",
+							"output.mp3"
+					);
+
+					// 从FFmpeg文件系统中读取音频文件
+					const data = ffmpeg.FS("readFile", "output.mp3");
+
+					// 创建音频URL
+					let audioBlob = new Blob([data.buffer], { type: "audio/mp3" });
+					audioBlob.name = "output.mp3"
+					if(this.videoFile){
+						this.audioUrl = URL.createObjectURL(audioBlob);
+					}
+					let resultFile = new File([audioBlob], "output.mp3", { type: "audio/mp3" });
+					window.parent.postMessage({code:0,msg:'提取音频成功',file:resultFile},'*');
+					resolve({file:audioBlob});
+				} catch (error) {
+					console.log(error);
+					window.parent.postMessage({code:1,msg:'提取音频失败',error:error},'*');
+					resolve({code:1,msg:'提取音频失败',error:error});
+				}
+			})
+		},
+  },
+  mounted(){
+		window.addEventListener('message',(e)=>{
+			let _data = e.data;
+			if(_data.type === 'extractAudio'){
+				this.extractAudio(_data.file);
+			}
+		})
+	}
+};
+</script>
+
+<style>
+* {
+  margin: 0;
+  padding: 0;
+}
+
+</style>

+ 23 - 0
src/pages/ffmpeg/main.js

@@ -0,0 +1,23 @@
+// The Vue build version to load with the `import` command
+// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
+import Vue from 'vue'
+import App from './App'
+
+
+
+Vue.config.productionTip = false
+
+/* eslint-disable no-new */
+new Vue({
+    el: '#app',
+    components: { App },
+    template: '<App/>'
+})
+
+
+// new Vue({
+//   render: h => h(App),
+// 	// router,
+// 	// store
+// }).$mount('#app')
+

Daži faili netika attēloti, jo izmaiņu fails ir pārāk liels