├── .eslintrc.js ├── .gitignore ├── .npmignore ├── .npmrc ├── .vscode └── settings.json ├── LICENSE ├── README.md ├── dist ├── vod-js-sdk-v6.js └── vod-js-sdk-v6.js.map ├── docs ├── import-demo │ ├── README.md │ ├── bundle.js │ ├── bundle.js.map │ ├── index.html │ ├── main.js │ └── package.json └── index.html ├── lib ├── package.json └── src │ ├── tc_vod.d.ts │ ├── tc_vod.js │ ├── tc_vod.js.map │ ├── uploader.d.ts │ ├── uploader.js │ ├── uploader.js.map │ ├── util.d.ts │ ├── util.js │ ├── util.js.map │ ├── vod_reporter.d.ts │ ├── vod_reporter.js │ └── vod_reporter.js.map ├── package.json ├── src ├── tc_vod.ts ├── types.d.ts ├── uploader.ts ├── util.ts └── vod_reporter.ts ├── test ├── env.ts ├── tc_vod.test.ts ├── uploader.test.ts └── vod_reporter.test.ts ├── tsconfig.json ├── webpack.config.js └── webpack.dev.config.js /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parser: "@typescript-eslint/parser", // Specifies the ESLint parser 3 | extends: [ 4 | "plugin:@typescript-eslint/recommended", // Uses the recommended rules from the @typescript-eslint/eslint-plugin 5 | "prettier/@typescript-eslint", // Uses eslint-config-prettier to disable ESLint rules from @typescript-eslint/eslint-plugin that would conflict with prettier 6 | "plugin:prettier/recommended" // Enables eslint-plugin-prettier and eslint-config-prettier. This will display prettier errors as ESLint errors. Make sure this is always the last configuration in the extends array. 7 | ], 8 | parserOptions: { 9 | ecmaVersion: 2018, // Allows for the parsing of modern ECMAScript features 10 | sourceType: "module" // Allows for the use of imports 11 | }, 12 | rules: { 13 | // Place to specify ESLint rules. Can be used to overwrite rules specified from the extended configs 14 | // e.g. "@typescript-eslint/explicit-function-return-type": "off", 15 | "@typescript-eslint/explicit-member-accessibility": "off", 16 | "@typescript-eslint/no-var-requires": "off", 17 | "@typescript-eslint/camelcase": "off", 18 | "@typescript-eslint/interface-name-prefix": "off", 19 | "@typescript-eslint/no-explicit-any": "off", 20 | "@typescript-eslint/explicit-function-return-type": "off" 21 | } 22 | }; -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | docs/import-demo/node_modules 38 | jspm_packages/ 39 | 40 | # TypeScript v1 declaration files 41 | typings/ 42 | 43 | # Optional npm cache directory 44 | .npm 45 | 46 | # Optional eslint cache 47 | .eslintcache 48 | 49 | # Optional REPL history 50 | .node_repl_history 51 | 52 | # Output of 'npm pack' 53 | *.tgz 54 | 55 | # Yarn Integrity file 56 | .yarn-integrity 57 | 58 | # dotenv environment variables file 59 | .env 60 | 61 | # next.js build output 62 | .next 63 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | docs 2 | src 3 | test 4 | .vscode -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "eslint.autoFixOnSave": true, 3 | "eslint.validate": [ 4 | "javascript", 5 | "javascriptreact", 6 | { "language": "typescript", "autoFix": true }, 7 | { "language": "typescriptreact", "autoFix": true } 8 | ], 9 | "editor.codeActionsOnSave": { 10 | "source.fixAll.eslint": true 11 | } 12 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 腾讯云 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vod-js-sdk-v6 2 | 3 | Web SDK for Tencent Cloud Video Service 4 | 5 | Document: https://cloud.tencent.com/document/product/266 6 | 7 | ## Install 8 | 9 | `npm install vod-js-sdk-v6` 10 | 11 | For browser, there is a umd bundle: `dist/vod-js-sdk-v6.js` 12 | 13 | We use `Promise` in the source code. You should imoprt Promise polyfill when target legacy browsers. 14 | 15 | ## Demo 16 | 17 | demo https://tencentyun.github.io/vod-js-sdk-v6/ 18 | 19 | demo source code https://github.com/tencentyun/vod-js-sdk-v6/blob/master/docs/index.html 20 | 21 | `import` usage demo: https://github.com/tencentyun/vod-js-sdk-v6/tree/master/docs/import-demo 22 | 23 | troubleshooting https://github.com/tencentyun/vod-js-sdk-v6/wiki/%E5%B8%B8%E8%A7%81%E6%95%85%E9%9A%9C%E6%8E%92%E6%9F%A5 24 | 25 | ## Usage 26 | 27 | * NOTICE: Document may be outdated, but demo and source code are always latest* 28 | 29 | See: https://cloud.tencent.com/document/product/266/9239 30 | 31 | ## Contributing 32 | 33 | 1. `git clone` this project 34 | 2. `npm install` 35 | 3. `npm run test` 36 | 4. add more task cases -------------------------------------------------------------------------------- /docs/import-demo/README.md: -------------------------------------------------------------------------------- 1 | ## desc 2 | 3 | This demo show how to install `vod-js-sdk-v6` by npm and use it in a webpack environment. 4 | 5 | ## demo url 6 | 7 | https://tencentyun.github.io/vod-js-sdk-v6/import-demo/index.html 8 | 9 | ## How to build 10 | 11 | 1. run `npm install` in this directory 12 | 2. run `npx webpack --entry ./main.js --output ./bundle.js` to build bundle.js 13 | 3. launch a http server in this directory, and open `index.html` -------------------------------------------------------------------------------- /docs/import-demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | QCloud VIDEO UGC UPLOAD SDK 10 | 11 | 39 | 40 | 41 | 42 |
43 |
44 |

UGC-Uploader

45 |
46 |
47 |
48 |
49 |

50 | 1.示例中的签名是直接从demo后台获取签名。该 demo 对应的点播账号已开启防盗链功能,此视频分发 URL 仅限 2 个独立 IP 进行访问,有效期为 10 分钟。
51 | 2.示例1点击“直接上传视频”按钮即可上传视频。
52 | 3.示例2点击“添加视频”添加视频文件,点击“添加封面”添加封面文件,然后点击“上传视频和封面”按钮即可上传视频和封面。
53 | 4.取消上传为取消上传中的视频,上传成功的视频不能取消上传。 54 |

55 |
56 | 57 |
58 | 59 |
60 |
61 |

示例1:直接上传视频

62 | 直接上传视频 63 |
64 | 65 |
66 | 67 | 68 |
69 |
70 |

示例2:上传视频和封面

71 | {{vcExampleVideoName || '添加视频'}} 72 | {{vcExampleCoverName || '添加封面'}} 73 | 上传视频和封面 74 |
75 | 76 |
77 | 78 |
79 | 89 |
90 | 91 |
92 |
93 | 视频名称:{{uploaderInfo.videoInfo.name + '.' + uploaderInfo.videoInfo.type}}; 94 | 上传进度:{{Math.floor(uploaderInfo.progress * 100) + '%'}}; 95 | fileId:{{uploaderInfo.fileId}}; 96 | 上传结果:{{uploaderInfo.isVideoUploadCancel ? '已取消' : uploaderInfo.isVideoUploadSuccess ? '上传成功' : '上传中'}}; 97 |
98 | 地址:{{uploaderInfo.videoUrl}}; 99 | 取消上传
100 |
101 | 102 |
103 | 封面名称:{{uploaderInfo.coverInfo.name}}; 104 | 上传进度:{{Math.floor(uploaderInfo.coverProgress * 100) + '%'}}; 105 | 上传结果:{{uploaderInfo.isCoverUploadSuccess ? '上传成功' : '上传中'}}; 106 |
107 | 地址:{{uploaderInfo.coverUrl}}; 108 |
109 |
110 |
111 |
112 | 113 |
114 | 115 | 116 | 117 | 125 | 126 | 127 | 128 | -------------------------------------------------------------------------------- /docs/import-demo/main.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue/dist/vue.js"; 2 | import axios from "axios"; 3 | import TcVod from "vod-js-sdk-v6"; 4 | 5 | /** 6 | * 计算签名。 7 | **/ 8 | function getSignature() { 9 | return axios 10 | .post( 11 | "https://demo.vod2.myqcloud.com/ugc-upload/", 12 | JSON.stringify({ 13 | Action: "GetUgcUploadSign" 14 | }) 15 | ) 16 | .then(function(response) { 17 | return response.data.data.sign; 18 | }); 19 | } 20 | 21 | /* 22 | 防盗链地址获取。这是腾讯云官网demo的特殊逻辑,用户可忽略此处。 23 | */ 24 | function getAntiLeechUrl(videoUrl, callback) { 25 | return axios 26 | .post( 27 | "https://demo.vod2.myqcloud.com/ugc-upload/", 28 | JSON.stringify({ 29 | Action: "GetAntiLeechUrl", 30 | Url: videoUrl 31 | }) 32 | ) 33 | .then(function(response) { 34 | return response.data.data.url; 35 | }); 36 | } 37 | 38 | var app = new Vue({ 39 | el: "#main-area", 40 | data: { 41 | uploaderInfos: [], 42 | 43 | vcExampleVideoName: "", 44 | vcExampleCoverName: "", 45 | 46 | cExampleFileId: "" 47 | }, 48 | created: function() { 49 | this.tcVod = new TcVod({ 50 | getSignature: getSignature 51 | }); 52 | }, 53 | methods: { 54 | /** 55 | * vExample示例。添加视频 56 | **/ 57 | vExampleAdd: function() { 58 | this.$refs.vExampleFile.click(); 59 | }, 60 | /** 61 | * vExample示例。上传视频过程。 62 | **/ 63 | vExampleUpload: function() { 64 | var self = this; 65 | var mediaFile = this.$refs.vExampleFile.files[0]; 66 | 67 | var uploader = this.tcVod.upload({ 68 | mediaFile: mediaFile 69 | }); 70 | uploader.on("media_progress", function(info) { 71 | uploaderInfo.progress = info.percent; 72 | }); 73 | uploader.on("media_upload", function(info) { 74 | uploaderInfo.isVideoUploadSuccess = true; 75 | }); 76 | 77 | console.log(uploader, "uploader"); 78 | 79 | var uploaderInfo = { 80 | videoInfo: uploader.videoInfo, 81 | isVideoUploadSuccess: false, 82 | isVideoUploadCancel: false, 83 | progress: 0, 84 | fileId: "", 85 | videoUrl: "", 86 | cancel: function() { 87 | uploaderInfo.isVideoUploadCancel = true; 88 | uploader.cancel(); 89 | } 90 | }; 91 | 92 | this.uploaderInfos.push(uploaderInfo); 93 | 94 | uploader 95 | .done() 96 | .then(function(doneResult) { 97 | console.log("doneResult", doneResult); 98 | 99 | uploaderInfo.fileId = doneResult.fileId; 100 | 101 | return getAntiLeechUrl(doneResult.video.url); 102 | }) 103 | .then(function(videoUrl) { 104 | uploaderInfo.videoUrl = videoUrl; 105 | self.$refs.vExample.reset(); 106 | }); 107 | }, 108 | 109 | setVcExampleVideoName: function() { 110 | this.vcExampleVideoName = this.$refs.vcExampleVideo.files[0].name; 111 | }, 112 | setVcExampleCoverName: function() { 113 | this.vcExampleCoverName = this.$refs.vcExampleCover.files[0].name; 114 | }, 115 | /* 116 | vcExample添加视频 117 | */ 118 | vcExampleAddVideo: function() { 119 | this.$refs.vcExampleVideo.click(); 120 | }, 121 | /* 122 | vcExample添加封面 123 | */ 124 | vcExampleAddCover: function() { 125 | this.$refs.vcExampleCover.click(); 126 | }, 127 | /* 128 | vcExample上传过程 129 | */ 130 | vcExampleAddUpload: function() { 131 | var self = this; 132 | 133 | var mediaFile = this.$refs.vcExampleVideo.files[0]; 134 | var coverFile = this.$refs.vcExampleCover.files[0]; 135 | 136 | var uploader = this.tcVod.upload({ 137 | mediaFile: mediaFile, 138 | coverFile: coverFile 139 | }); 140 | uploader.on("media_progress", function(info) { 141 | uploaderInfo.progress = info.percent; 142 | }); 143 | uploader.on("media_upload", function(info) { 144 | uploaderInfo.isVideoUploadSuccess = true; 145 | }); 146 | uploader.on("cover_progress", function(info) { 147 | uploaderInfo.coverProgress = info.percent; 148 | }); 149 | uploader.on("cover_upload", function(info) { 150 | uploaderInfo.isCoverUploadSuccess = true; 151 | }); 152 | console.log(uploader, "uploader"); 153 | 154 | var uploaderInfo = { 155 | videoInfo: uploader.videoInfo, 156 | coverInfo: uploader.coverInfo, 157 | isVideoUploadSuccess: false, 158 | isVideoUploadCancel: false, 159 | isCoverUploadSuccess: false, 160 | progress: 0, 161 | coverProgress: 0, 162 | fileId: "", 163 | videoUrl: "", 164 | coverUrl: "", 165 | cancel: function() { 166 | uploaderInfo.isVideoUploadCancel = true; 167 | uploader.cancel(); 168 | } 169 | }; 170 | 171 | this.uploaderInfos.push(uploaderInfo); 172 | 173 | uploader 174 | .done() 175 | .then(function(doneResult) { 176 | console.log("doneResult", doneResult); 177 | 178 | uploaderInfo.fileId = doneResult.fileId; 179 | 180 | uploaderInfo.coverUrl = doneResult.cover.url; 181 | return getAntiLeechUrl(doneResult.video.url); 182 | }) 183 | .then(function(videoUrl) { 184 | uploaderInfo.videoUrl = videoUrl; 185 | self.$refs.vcExample.reset(); 186 | self.vcExampleVideoName = ""; 187 | self.vcExampleCoverName = ""; 188 | }); 189 | }, 190 | 191 | // cExample 添加封面 192 | cExampleAddCover: function() { 193 | this.$refs.cExampleCover.click(); 194 | }, 195 | // cExample 上传过程 196 | cExampleUpload: function() { 197 | var self = this; 198 | 199 | var coverFile = this.$refs.cExampleCover.files[0]; 200 | 201 | var uploader = this.tcVod.upload({ 202 | fileId: this.cExampleFileId, 203 | coverFile: coverFile 204 | }); 205 | uploader.on("cover_progress", function(info) { 206 | uploaderInfo.coverProgress = info.percent; 207 | }); 208 | uploader.on("cover_upload", function(info) { 209 | uploaderInfo.isCoverUploadSuccess = true; 210 | }); 211 | console.log(uploader, "uploader"); 212 | 213 | var uploaderInfo = { 214 | coverInfo: uploader.coverInfo, 215 | isCoverUploadSuccess: false, 216 | coverProgress: 0, 217 | coverUrl: "", 218 | cancel: function() { 219 | uploader.cancel(); 220 | } 221 | }; 222 | 223 | this.uploaderInfos.push(uploaderInfo); 224 | 225 | uploader.done().then(function(doneResult) { 226 | console.log("doneResult", doneResult); 227 | 228 | uploaderInfo.coverUrl = doneResult.cover.url; 229 | 230 | self.$refs.cExample.reset(); 231 | }); 232 | } 233 | } 234 | }); 235 | -------------------------------------------------------------------------------- /docs/import-demo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "import-demo", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "main.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "MIT", 11 | "dependencies": { 12 | "axios": "^0.18.0", 13 | "vod-js-sdk-v6": "^1.4.8", 14 | "vue": "^2.6.10" 15 | }, 16 | "devDependencies": { 17 | "webpack": "^4.30.0", 18 | "webpack-cli": "^3.3.1" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | QCloud VIDEO UGC UPLOAD SDK 10 | 11 | 39 | 40 | 41 | 42 |
43 |
44 |

UGC-Uploader

45 |
46 |
47 |
48 |
49 |

50 | 1.示例中的签名是直接从demo后台获取签名。该 demo 对应的点播账号已开启防盗链功能,此视频分发 URL 仅限 2 个独立 IP 进行访问,有效期为 10 分钟。
51 | 2.示例1点击“直接上传视频”按钮即可上传视频。
52 | 3.示例2点击“添加视频”添加视频文件,点击“添加封面”添加封面文件,然后点击“上传视频和封面”按钮即可上传视频和封面。
53 | 4.取消上传为取消上传中的视频,上传成功的视频不能取消上传。 54 |

55 |
56 | 57 |
58 | 59 |
60 |
61 |

示例1:直接上传视频

62 | 直接上传视频 63 |
64 | 65 |
66 | 67 | 68 |
69 |
70 |

示例2:上传视频和封面

71 | {{vcExampleVideoName || '添加视频'}} 72 | {{vcExampleCoverName || '添加封面'}} 73 | 上传视频和封面 74 |
75 | 76 |
77 | 78 |
79 | 89 |
90 | 91 |
92 |
93 | 视频名称:{{uploaderInfo.videoInfo.name + '.' + uploaderInfo.videoInfo.type}}; 94 | 上传进度:{{Math.floor(uploaderInfo.progress * 100) + '%'}}; 95 | fileId:{{uploaderInfo.fileId}}; 96 | 上传结果:{{uploaderInfo.isVideoUploadCancel ? '已取消' : uploaderInfo.isVideoUploadSuccess ? '上传成功' : '上传中'}}; 97 |
98 | 地址:{{uploaderInfo.videoUrl}}; 99 | 取消上传
100 |
101 | 102 |
103 | 封面名称:{{uploaderInfo.coverInfo.name}}; 104 | 上传进度:{{Math.floor(uploaderInfo.coverProgress * 100) + '%'}}; 105 | 上传结果:{{uploaderInfo.isCoverUploadSuccess ? '上传成功' : '上传中'}}; 106 |
107 | 地址:{{uploaderInfo.coverUrl}}; 108 |
109 |
110 |
111 |
112 | 113 |
114 | 115 | 116 | 117 | 118 | 119 | 341 | 342 | 343 | 351 | 352 | 353 | 354 | -------------------------------------------------------------------------------- /lib/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vod-js-sdk-v6", 3 | "version": "1.4.12", 4 | "description": "tencent cloud vod js sdk v6", 5 | "main": "lib/src/tc_vod.js", 6 | "unpkg": "dist/vod-js-sdk-v6.js", 7 | "typings": "lib/src/tc_vod.d.ts", 8 | "scripts": { 9 | "test": "cross-env NODE_ENV=test mocha -r espower-typescript/guess -r jsdom-global/register -r test/env test/**/*.test.ts", 10 | "cover": "cross-env NODE_ENV=test nyc mocha -r espower-typescript/guess -r jsdom-global/register -r test/env test/**/*.test.ts", 11 | "dev": "webpack --config webpack.dev.config.js --watch", 12 | "dist": "webpack --config webpack.config.js", 13 | "build": "npm run test && npm run dist && npm run compile", 14 | "compile": "tsc -p tsconfig.json", 15 | "prepublish": "npm run build", 16 | "lint": "tsc --noEmit && eslint 'src/**/*.{js,ts,tsx}' --quiet --fix" 17 | }, 18 | "repository": { 19 | "type": "git", 20 | "url": "git+https://github.com/tencentyun/vod-js-sdk-v6.git" 21 | }, 22 | "keywords": [ 23 | "tencentcloud", 24 | "sdk", 25 | "vod" 26 | ], 27 | "author": "alsotang ", 28 | "license": "MIT", 29 | "bugs": { 30 | "url": "https://github.com/tencentyun/vod-js-sdk-v6/issues" 31 | }, 32 | "homepage": "https://github.com/tencentyun/vod-js-sdk-v6#readme", 33 | "dependencies": { 34 | "axios": "^0.21.1", 35 | "cos-js-sdk-v5": "0.5.27", 36 | "eventemitter3": "^4.0.7", 37 | "js-sha1": "^0.6.0", 38 | "uuid": "^3.3.2" 39 | }, 40 | "devDependencies": { 41 | "@types/mocha": "^5.2.5", 42 | "@types/semver": "^6.0.0", 43 | "@types/sha1": "^1.1.1", 44 | "@types/uuid": "^3.4.4", 45 | "@typescript-eslint/eslint-plugin": "^1.9.0", 46 | "@typescript-eslint/parser": "^1.9.0", 47 | "cross-env": "^6.0.3", 48 | "eslint": "^5.16.0", 49 | "eslint-config-prettier": "^4.3.0", 50 | "eslint-plugin-prettier": "^3.1.0", 51 | "espower-typescript": "^9.0.1", 52 | "jsdom": "^13.1.0", 53 | "jsdom-global": "^3.0.2", 54 | "mm": "^2.4.1", 55 | "mocha": "^5.2.0", 56 | "nyc": "^13.1.0", 57 | "power-assert": "^1.6.1", 58 | "prettier": "^1.17.1", 59 | "semver": "^6.1.1", 60 | "ts-loader": "^5.3.3", 61 | "typescript": "^3.5.3", 62 | "webpack": "^4.28.1", 63 | "webpack-cli": "^3.2.1" 64 | }, 65 | "nyc": { 66 | "extension": [ 67 | ".ts", 68 | ".tsx" 69 | ], 70 | "include": [ 71 | "src" 72 | ], 73 | "reporter": [ 74 | "html" 75 | ], 76 | "all": true 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /lib/src/tc_vod.d.ts: -------------------------------------------------------------------------------- 1 | import Uploader, { IGetSignature, UploaderOptions } from "./uploader"; 2 | interface TcVodParams { 3 | getSignature: IGetSignature; 4 | allowReport?: boolean; 5 | appId?: number; 6 | reportId?: string; 7 | enableResume?: boolean; 8 | } 9 | declare class TcVod { 10 | getSignature: IGetSignature; 11 | allowReport: boolean; 12 | appId: number; 13 | reportId: string; 14 | enableResume: boolean; 15 | constructor(params: TcVodParams); 16 | upload(params: Omit): Uploader; 17 | initReporter(uploader: Uploader): void; 18 | } 19 | export default TcVod; 20 | -------------------------------------------------------------------------------- /lib/src/tc_vod.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __assign = (this && this.__assign) || function () { 3 | __assign = Object.assign || function(t) { 4 | for (var s, i = 1, n = arguments.length; i < n; i++) { 5 | s = arguments[i]; 6 | for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) 7 | t[p] = s[p]; 8 | } 9 | return t; 10 | }; 11 | return __assign.apply(this, arguments); 12 | }; 13 | Object.defineProperty(exports, "__esModule", { value: true }); 14 | var uploader_1 = require("./uploader"); 15 | var vod_reporter_1 = require("./vod_reporter"); 16 | var TcVod = /** @class */ (function () { 17 | function TcVod(params) { 18 | this.allowReport = true; // report sdk status to tencent cloud 19 | this.enableResume = true; // resume uploading from break point 20 | this.getSignature = params.getSignature; 21 | if (params.allowReport !== void 0) { 22 | this.allowReport = params.allowReport; 23 | } 24 | if (params.enableResume !== void 0) { 25 | this.enableResume = params.enableResume; 26 | } 27 | this.appId = params.appId; 28 | this.reportId = params.reportId; 29 | } 30 | TcVod.prototype.upload = function (params) { 31 | var uploaderParams = __assign({ getSignature: this.getSignature, appId: this.appId, reportId: this.reportId, enableResume: this.enableResume }, params); 32 | var uploader = new uploader_1.default(uploaderParams); 33 | if (this.allowReport) { 34 | this.initReporter(uploader); 35 | } 36 | uploader.start(); 37 | return uploader; 38 | }; 39 | // report to official report system 40 | TcVod.prototype.initReporter = function (uploader) { 41 | new vod_reporter_1.VodReporter(uploader); 42 | }; 43 | return TcVod; 44 | }()); 45 | exports.default = TcVod; 46 | //# sourceMappingURL=tc_vod.js.map -------------------------------------------------------------------------------- /lib/src/tc_vod.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"tc_vod.js","sourceRoot":"","sources":["../../src/tc_vod.ts"],"names":[],"mappings":";;;;;;;;;;;;;AAAA,uCAAsE;AACtE,+CAA6C;AAS7C;IAME,eAAY,MAAmB;QAJ/B,gBAAW,GAAG,IAAI,CAAC,CAAC,qCAAqC;QAGzD,iBAAY,GAAG,IAAI,CAAC,CAAC,oCAAoC;QAEvD,IAAI,CAAC,YAAY,GAAG,MAAM,CAAC,YAAY,CAAC;QACxC,IAAI,MAAM,CAAC,WAAW,KAAK,KAAK,CAAC,EAAE;YACjC,IAAI,CAAC,WAAW,GAAG,MAAM,CAAC,WAAW,CAAC;SACvC;QACD,IAAI,MAAM,CAAC,YAAY,KAAK,KAAK,CAAC,EAAE;YAClC,IAAI,CAAC,YAAY,GAAG,MAAM,CAAC,YAAY,CAAC;SACzC;QACD,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC;QAC1B,IAAI,CAAC,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;IAClC,CAAC;IAED,sBAAM,GAAN,UAAO,MAA6C;QAClD,IAAM,cAAc,cAClB,YAAY,EAAE,IAAI,CAAC,YAAY,EAC/B,KAAK,EAAE,IAAI,CAAC,KAAK,EACjB,QAAQ,EAAE,IAAI,CAAC,QAAQ,EACvB,YAAY,EAAE,IAAI,CAAC,YAAY,IAC5B,MAAM,CACV,CAAC;QACF,IAAM,QAAQ,GAAG,IAAI,kBAAQ,CAAC,cAAc,CAAC,CAAC;QAC9C,IAAI,IAAI,CAAC,WAAW,EAAE;YACpB,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;SAC7B;QACD,QAAQ,CAAC,KAAK,EAAE,CAAC;QACjB,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,mCAAmC;IACnC,4BAAY,GAAZ,UAAa,QAAkB;QAC7B,IAAI,0BAAW,CAAC,QAAQ,CAAC,CAAC;IAC5B,CAAC;IACH,YAAC;AAAD,CAAC,AAtCD,IAsCC;AAED,kBAAe,KAAK,CAAC"} -------------------------------------------------------------------------------- /lib/src/uploader.d.ts: -------------------------------------------------------------------------------- 1 | import { EventEmitter } from "eventemitter3"; 2 | import { HOST } from "./util"; 3 | export declare const vodAxios: import("axios").AxiosInstance; 4 | export declare type IGetSignature = () => Promise; 5 | export interface TcVodFileInfo { 6 | name: string; 7 | type: string; 8 | size: number; 9 | } 10 | export declare enum UploaderEvent { 11 | video_progress = "video_progress", 12 | media_progress = "media_progress", 13 | video_upload = "video_upload", 14 | media_upload = "media_upload", 15 | cover_progress = "cover_progress", 16 | cover_upload = "cover_upload" 17 | } 18 | interface IApplyData { 19 | video: { 20 | storageSignature: string; 21 | storagePath: string; 22 | }; 23 | cover?: { 24 | storageSignature: string; 25 | storagePath: string; 26 | }; 27 | storageAppId: number; 28 | storageBucket: string; 29 | storageRegion: string; 30 | storageRegionV5: string; 31 | domain: string; 32 | vodSessionKey: string; 33 | tempCertificate: { 34 | secretId: string; 35 | secretKey: string; 36 | token: string; 37 | expiredTime: number; 38 | }; 39 | appId: number; 40 | timestamp: number; 41 | StorageRegionV5: string; 42 | } 43 | export interface UploaderOptions { 44 | getSignature: IGetSignature; 45 | videoFile?: File; 46 | mediaFile?: File; 47 | coverFile?: File; 48 | videoName?: string; 49 | mediaName?: string; 50 | fileId?: string; 51 | appId?: number; 52 | reportId?: string; 53 | applyRequestTimeout?: number; 54 | commitRequestTimeout?: number; 55 | retryDelay?: number; 56 | enableResume?: boolean; 57 | } 58 | declare class Uploader extends EventEmitter implements UploaderOptions { 59 | getSignature: IGetSignature; 60 | videoFile: File; 61 | videoInfo: TcVodFileInfo; 62 | coverFile: File; 63 | coverInfo: TcVodFileInfo; 64 | cos: any; 65 | taskId: string; 66 | cosAuthTime: number; 67 | videoName: string; 68 | sessionName: string; 69 | vodSessionKey: string; 70 | appId: number; 71 | fileId: string; 72 | reqKey: string; 73 | reportId: string; 74 | enableResume: boolean; 75 | donePromise: Promise; 76 | applyRequestTimeout: number; 77 | applyRequestRetryCount: number; 78 | commitRequestTimeout: number; 79 | commitRequestRetryCount: number; 80 | retryDelay: number; 81 | static host: HOST; 82 | constructor(params: UploaderOptions); 83 | setStorage(name: string, value: string): void; 84 | getStorage(name: string): string; 85 | delStorage(name: string): void; 86 | validateInitParams(params: UploaderOptions): void; 87 | genFileInfo(): void; 88 | applyUploadUGC(retryCount?: number): Promise; 89 | uploadToCos(applyData: IApplyData): Promise; 90 | commitUploadUGC(retryCount?: number): Promise; 91 | start(): void; 92 | _start(): Promise; 93 | done(): Promise; 94 | cancel(): void; 95 | } 96 | export default Uploader; 97 | -------------------------------------------------------------------------------- /lib/src/uploader.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __extends = (this && this.__extends) || (function () { 3 | var extendStatics = function (d, b) { 4 | extendStatics = Object.setPrototypeOf || 5 | ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || 6 | function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; 7 | return extendStatics(d, b); 8 | }; 9 | return function (d, b) { 10 | extendStatics(d, b); 11 | function __() { this.constructor = d; } 12 | d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); 13 | }; 14 | })(); 15 | var __assign = (this && this.__assign) || function () { 16 | __assign = Object.assign || function(t) { 17 | for (var s, i = 1, n = arguments.length; i < n; i++) { 18 | s = arguments[i]; 19 | for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) 20 | t[p] = s[p]; 21 | } 22 | return t; 23 | }; 24 | return __assign.apply(this, arguments); 25 | }; 26 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 27 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 28 | return new (P || (P = Promise))(function (resolve, reject) { 29 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 30 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 31 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 32 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 33 | }); 34 | }; 35 | var __generator = (this && this.__generator) || function (thisArg, body) { 36 | var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; 37 | return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; 38 | function verb(n) { return function (v) { return step([n, v]); }; } 39 | function step(op) { 40 | if (f) throw new TypeError("Generator is already executing."); 41 | while (_) try { 42 | if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; 43 | if (y = 0, t) op = [op[0] & 2, t.value]; 44 | switch (op[0]) { 45 | case 0: case 1: t = op; break; 46 | case 4: _.label++; return { value: op[1], done: false }; 47 | case 5: _.label++; y = op[1]; op = [0]; continue; 48 | case 7: op = _.ops.pop(); _.trys.pop(); continue; 49 | default: 50 | if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } 51 | if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } 52 | if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } 53 | if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } 54 | if (t[2]) _.ops.pop(); 55 | _.trys.pop(); continue; 56 | } 57 | op = body.call(thisArg, _); 58 | } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } 59 | if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; 60 | } 61 | }; 62 | Object.defineProperty(exports, "__esModule", { value: true }); 63 | exports.UploaderEvent = exports.vodAxios = void 0; 64 | var sha1 = require("js-sha1"); 65 | var COS = require("cos-js-sdk-v5"); 66 | var eventemitter3_1 = require("eventemitter3"); 67 | var axios_1 = require("axios"); 68 | var util_1 = require("./util"); 69 | var vod_reporter_1 = require("./vod_reporter"); 70 | var uuidv4 = require("uuid/v4"); 71 | exports.vodAxios = axios_1.default.create(); 72 | exports.vodAxios.interceptors.response.use(function (response) { 73 | return response; 74 | }, function (error) { 75 | if (isNaN(error.code)) { 76 | error.code = 500; 77 | } 78 | return Promise.reject(error); 79 | }); 80 | var UploaderEvent; 81 | (function (UploaderEvent) { 82 | UploaderEvent["video_progress"] = "video_progress"; 83 | UploaderEvent["media_progress"] = "media_progress"; 84 | UploaderEvent["video_upload"] = "video_upload"; 85 | UploaderEvent["media_upload"] = "media_upload"; 86 | UploaderEvent["cover_progress"] = "cover_progress"; 87 | UploaderEvent["cover_upload"] = "cover_upload"; 88 | })(UploaderEvent = exports.UploaderEvent || (exports.UploaderEvent = {})); 89 | var Uploader = /** @class */ (function (_super) { 90 | __extends(Uploader, _super); 91 | function Uploader(params) { 92 | var _this = _super.call(this) || this; 93 | _this.sessionName = ""; 94 | _this.vodSessionKey = ""; 95 | _this.appId = 0; 96 | _this.reqKey = uuidv4(); 97 | _this.reportId = ""; 98 | // resume from break point 99 | _this.enableResume = true; 100 | // apply 请求的超时时间 101 | _this.applyRequestTimeout = 5000; 102 | _this.applyRequestRetryCount = 3; 103 | // commit 请求的超时时间 104 | _this.commitRequestTimeout = 5000; 105 | _this.commitRequestRetryCount = 3; 106 | // 重试请求的等待时间 107 | _this.retryDelay = 1000; 108 | _this.validateInitParams(params); 109 | _this.videoFile = params.mediaFile || params.videoFile; 110 | _this.getSignature = params.getSignature; 111 | _this.enableResume = params.enableResume; 112 | _this.videoName = params.mediaName || params.videoName; 113 | _this.coverFile = params.coverFile; 114 | _this.fileId = params.fileId; 115 | _this.applyRequestTimeout = 116 | params.applyRequestTimeout || _this.applyRequestTimeout; 117 | _this.commitRequestTimeout = 118 | params.commitRequestTimeout || _this.commitRequestTimeout; 119 | _this.retryDelay = params.retryDelay || _this.retryDelay; 120 | // custom report metrics 121 | _this.appId = params.appId || _this.appId; 122 | _this.reportId = params.reportId || _this.reportId; 123 | _this.cosAuthTime = 0; 124 | _this.genFileInfo(); 125 | return _this; 126 | } 127 | // set storage 128 | Uploader.prototype.setStorage = function (name, value) { 129 | if (!name) { 130 | return; 131 | } 132 | var cname = "webugc_" + sha1(name); 133 | try { 134 | localStorage.setItem(cname, value); 135 | } 136 | catch (e) { } 137 | }; 138 | // get storage 139 | Uploader.prototype.getStorage = function (name) { 140 | if (!name) { 141 | return; 142 | } 143 | var cname = "webugc_" + sha1(name); 144 | var result = null; 145 | try { 146 | result = localStorage.getItem(cname); 147 | } 148 | catch (e) { } 149 | return result; 150 | }; 151 | // delete storage 152 | Uploader.prototype.delStorage = function (name) { 153 | if (!name) { 154 | return; 155 | } 156 | var cname = "webugc_" + sha1(name); 157 | try { 158 | localStorage.removeItem(cname); 159 | } 160 | catch (e) { } 161 | }; 162 | // validate init params 163 | Uploader.prototype.validateInitParams = function (params) { 164 | if (!util_1.default.isFunction(params.getSignature)) { 165 | throw new Error("getSignature must be a function"); 166 | } 167 | if (params.videoFile && !util_1.default.isFile(params.videoFile)) { 168 | throw new Error("videoFile must be a File"); 169 | } 170 | }; 171 | Uploader.prototype.genFileInfo = function () { 172 | // video file info 173 | var videoFile = this.videoFile; 174 | if (videoFile) { 175 | var lastDotIndex = videoFile.name.lastIndexOf("."); 176 | var videoName = ""; 177 | // if specified, use it. 178 | if (this.videoName) { 179 | if (!util_1.default.isString(this.videoName)) { 180 | throw new Error("mediaName must be a string"); 181 | } 182 | else if (/[:*?<>\"\\/|]/g.test(this.videoName)) { 183 | throw new Error('Cant use these chars in filename: \\ / : * ? " < > |'); 184 | } 185 | else { 186 | videoName = this.videoName; 187 | } 188 | } 189 | else { 190 | // else use the meta info of file 191 | videoName = videoFile.name.substring(0, lastDotIndex); 192 | } 193 | this.videoInfo = { 194 | name: videoName, 195 | type: videoFile.name.substring(lastDotIndex + 1).toLowerCase(), 196 | size: videoFile.size 197 | }; 198 | this.sessionName += videoFile.name + "_" + videoFile.size + ";"; 199 | } 200 | // cover file info 201 | var coverFile = this.coverFile; 202 | if (coverFile) { 203 | var coverName = coverFile.name; 204 | var coverLastDotIndex = coverName.lastIndexOf("."); 205 | this.coverInfo = { 206 | name: coverName.substring(0, coverLastDotIndex), 207 | type: coverName.substring(coverLastDotIndex + 1).toLowerCase(), 208 | size: coverFile.size 209 | }; 210 | this.sessionName += coverFile.name + "_" + coverFile.size + ";"; 211 | } 212 | }; 213 | Uploader.prototype.applyUploadUGC = function (retryCount) { 214 | if (retryCount === void 0) { retryCount = 0; } 215 | return __awaiter(this, void 0, void 0, function () { 216 | function whenError(err) { 217 | return __awaiter(this, void 0, void 0, function () { 218 | return __generator(this, function (_a) { 219 | switch (_a.label) { 220 | case 0: 221 | if (err.code === 500) { 222 | Uploader.host = Uploader.host === util_1.HOST.MAIN ? util_1.HOST.BACKUP : util_1.HOST.MAIN; 223 | } 224 | self.emit(vod_reporter_1.VodReportEvent.report_apply, { 225 | err: err, 226 | requestStartTime: requestStartTime 227 | }); 228 | self.delStorage(self.sessionName); 229 | if (self.applyRequestRetryCount == retryCount) { 230 | if (err) { 231 | throw err; 232 | } 233 | throw new Error("apply upload failed"); 234 | } 235 | return [4 /*yield*/, util_1.default.delay(self.retryDelay)]; 236 | case 1: 237 | _a.sent(); 238 | return [2 /*return*/, self.applyUploadUGC(retryCount + 1)]; 239 | } 240 | }); 241 | }); 242 | } 243 | var self, signature, sendParams, videoInfo, coverInfo, vodSessionKey, requestStartTime, response, e_1, applyResult, applyData, vodSessionKey_1, err; 244 | return __generator(this, function (_a) { 245 | switch (_a.label) { 246 | case 0: 247 | self = this; 248 | return [4 /*yield*/, this.getSignature()]; 249 | case 1: 250 | signature = _a.sent(); 251 | videoInfo = this.videoInfo; 252 | coverInfo = this.coverInfo; 253 | vodSessionKey = this.vodSessionKey || 254 | (this.enableResume && this.getStorage(this.sessionName)); 255 | // resume from break point 256 | if (vodSessionKey) { 257 | sendParams = { 258 | signature: signature, 259 | vodSessionKey: vodSessionKey 260 | }; 261 | } 262 | else if (videoInfo) { 263 | sendParams = { 264 | signature: signature, 265 | videoName: videoInfo.name, 266 | videoType: videoInfo.type, 267 | videoSize: videoInfo.size 268 | }; 269 | if (coverInfo) { 270 | // upload video together with cover 271 | sendParams.coverName = coverInfo.name; 272 | sendParams.coverType = coverInfo.type; 273 | sendParams.coverSize = coverInfo.size; 274 | } 275 | } 276 | else if (this.fileId && coverInfo) { 277 | // alter cover 278 | sendParams = { 279 | signature: signature, 280 | fileId: this.fileId, 281 | coverName: coverInfo.name, 282 | coverType: coverInfo.type, 283 | coverSize: coverInfo.size 284 | }; 285 | } 286 | else { 287 | throw "Wrong params, please check and try again"; 288 | } 289 | requestStartTime = new Date(); 290 | _a.label = 2; 291 | case 2: 292 | _a.trys.push([2, 4, , 5]); 293 | return [4 /*yield*/, exports.vodAxios.post("https://" + Uploader.host + "/v3/index.php?Action=ApplyUploadUGC", sendParams, { 294 | timeout: this.applyRequestTimeout, 295 | withCredentials: false 296 | })]; 297 | case 3: 298 | response = _a.sent(); 299 | return [3 /*break*/, 5]; 300 | case 4: 301 | e_1 = _a.sent(); 302 | return [2 /*return*/, whenError(e_1)]; 303 | case 5: 304 | applyResult = response.data; 305 | // all err code https://user-images.githubusercontent.com/1147375/51222454-bf6ef280-1978-11e9-8e33-1b0fdb2fe200.png 306 | if (applyResult.code == 0) { 307 | applyData = applyResult.data; 308 | vodSessionKey_1 = applyData.vodSessionKey; 309 | this.setStorage(this.sessionName, vodSessionKey_1); 310 | this.vodSessionKey = vodSessionKey_1; 311 | this.appId = applyData.appId; 312 | this.emit(vod_reporter_1.VodReportEvent.report_apply, { 313 | data: applyData, 314 | requestStartTime: requestStartTime 315 | }); 316 | return [2 /*return*/, applyData]; 317 | } 318 | else { 319 | err = new Error(applyResult.message); 320 | err.code = applyResult.code; 321 | return [2 /*return*/, whenError(err)]; 322 | } 323 | return [2 /*return*/]; 324 | } 325 | }); 326 | }); 327 | }; 328 | Uploader.prototype.uploadToCos = function (applyData) { 329 | return __awaiter(this, void 0, void 0, function () { 330 | var self, cosParam, cos, uploadCosParams, cosVideoParam, cosCoverParam, requestStartTime, uploadPromises; 331 | return __generator(this, function (_a) { 332 | switch (_a.label) { 333 | case 0: 334 | self = this; 335 | cosParam = { 336 | bucket: applyData.storageBucket + "-" + applyData.storageAppId, 337 | region: applyData.storageRegionV5 338 | }; 339 | cos = new COS({ 340 | getAuthorization: function (options, callback) { 341 | return __awaiter(this, void 0, void 0, function () { 342 | var currentTimeStamp, safeExpireTime; 343 | return __generator(this, function (_a) { 344 | switch (_a.label) { 345 | case 0: 346 | currentTimeStamp = util_1.default.getUnix(); 347 | safeExpireTime = (applyData.tempCertificate.expiredTime - applyData.timestamp) * 0.9; 348 | if (!(self.cosAuthTime === 0)) return [3 /*break*/, 1]; 349 | self.cosAuthTime = currentTimeStamp; 350 | return [3 /*break*/, 3]; 351 | case 1: 352 | if (!(self.cosAuthTime && 353 | currentTimeStamp - self.cosAuthTime >= safeExpireTime)) return [3 /*break*/, 3]; 354 | return [4 /*yield*/, self.applyUploadUGC()]; 355 | case 2: 356 | applyData = _a.sent(); 357 | self.cosAuthTime = util_1.default.getUnix(); 358 | _a.label = 3; 359 | case 3: 360 | callback({ 361 | TmpSecretId: applyData.tempCertificate.secretId, 362 | TmpSecretKey: applyData.tempCertificate.secretKey, 363 | XCosSecurityToken: applyData.tempCertificate.token, 364 | StartTime: applyData.timestamp, 365 | ExpiredTime: applyData.tempCertificate.expiredTime 366 | }); 367 | return [2 /*return*/]; 368 | } 369 | }); 370 | }); 371 | } 372 | }); 373 | this.cos = cos; 374 | uploadCosParams = []; 375 | if (this.videoFile) { 376 | cosVideoParam = __assign(__assign({}, cosParam), { file: this.videoFile, key: applyData.video.storagePath, onProgress: function (data) { 377 | self.emit(UploaderEvent.video_progress, data); 378 | self.emit(UploaderEvent.media_progress, data); 379 | }, onUpload: function (data) { 380 | self.emit(UploaderEvent.video_upload, data); 381 | self.emit(UploaderEvent.media_upload, data); 382 | }, onTaskReady: function (taskId) { 383 | self.taskId = taskId; 384 | } }); 385 | uploadCosParams.push(cosVideoParam); 386 | } 387 | if (this.coverFile) { 388 | cosCoverParam = __assign(__assign({}, cosParam), { file: this.coverFile, key: applyData.cover.storagePath, onProgress: function (data) { 389 | self.emit(UploaderEvent.cover_progress, data); 390 | }, onUpload: function (data) { 391 | self.emit(UploaderEvent.cover_upload, data); 392 | }, onTaskReady: util_1.default.noop }); 393 | uploadCosParams.push(cosCoverParam); 394 | } 395 | requestStartTime = new Date(); 396 | uploadPromises = uploadCosParams.map(function (uploadCosParam) { 397 | return new Promise(function (resolve, reject) { 398 | cos.sliceUploadFile({ 399 | Bucket: uploadCosParam.bucket, 400 | Region: uploadCosParam.region, 401 | Key: uploadCosParam.key, 402 | Body: uploadCosParam.file, 403 | onTaskReady: uploadCosParam.onTaskReady, 404 | onProgress: uploadCosParam.onProgress 405 | }, function (err, data) { 406 | // only report video file 407 | if (uploadCosParam.file === self.videoFile) { 408 | self.emit(vod_reporter_1.VodReportEvent.report_cos_upload, { 409 | err: err, 410 | requestStartTime: requestStartTime 411 | }); 412 | } 413 | if (!err) { 414 | uploadCosParam.onUpload(data); 415 | return resolve(); 416 | } 417 | self.delStorage(self.sessionName); 418 | if (JSON.stringify(err) === '{"error":"error","headers":{}}') { 419 | return reject(new Error("cors error")); 420 | } 421 | reject(err); 422 | }); 423 | }); 424 | }); 425 | return [4 /*yield*/, Promise.all(uploadPromises)]; 426 | case 1: return [2 /*return*/, _a.sent()]; 427 | } 428 | }); 429 | }); 430 | }; 431 | Uploader.prototype.commitUploadUGC = function (retryCount) { 432 | if (retryCount === void 0) { retryCount = 0; } 433 | return __awaiter(this, void 0, void 0, function () { 434 | function whenError(err) { 435 | return __awaiter(this, void 0, void 0, function () { 436 | return __generator(this, function (_a) { 437 | switch (_a.label) { 438 | case 0: 439 | if (err.code === 500) { 440 | Uploader.host = Uploader.host === util_1.HOST.MAIN ? util_1.HOST.BACKUP : util_1.HOST.MAIN; 441 | } 442 | self.emit(vod_reporter_1.VodReportEvent.report_commit, { 443 | err: err, 444 | requestStartTime: requestStartTime 445 | }); 446 | if (self.commitRequestRetryCount == retryCount) { 447 | if (err) { 448 | throw err; 449 | } 450 | throw new Error("commit upload failed"); 451 | } 452 | return [4 /*yield*/, util_1.default.delay(self.retryDelay)]; 453 | case 1: 454 | _a.sent(); 455 | return [2 /*return*/, self.commitUploadUGC(retryCount + 1)]; 456 | } 457 | }); 458 | }); 459 | } 460 | var self, signature, vodSessionKey, requestStartTime, response, e_2, commitResult, err; 461 | return __generator(this, function (_a) { 462 | switch (_a.label) { 463 | case 0: 464 | self = this; 465 | return [4 /*yield*/, this.getSignature()]; 466 | case 1: 467 | signature = _a.sent(); 468 | this.delStorage(this.sessionName); 469 | vodSessionKey = this.vodSessionKey; 470 | requestStartTime = new Date(); 471 | _a.label = 2; 472 | case 2: 473 | _a.trys.push([2, 4, , 5]); 474 | return [4 /*yield*/, exports.vodAxios.post("https://" + Uploader.host + "/v3/index.php?Action=CommitUploadUGC", { 475 | signature: signature, 476 | vodSessionKey: vodSessionKey 477 | }, { 478 | timeout: this.commitRequestTimeout, 479 | withCredentials: false 480 | })]; 481 | case 3: 482 | response = _a.sent(); 483 | return [3 /*break*/, 5]; 484 | case 4: 485 | e_2 = _a.sent(); 486 | return [2 /*return*/, whenError(e_2)]; 487 | case 5: 488 | commitResult = response.data; 489 | if (commitResult.code == 0) { 490 | this.emit(vod_reporter_1.VodReportEvent.report_commit, { 491 | data: commitResult.data, 492 | requestStartTime: requestStartTime 493 | }); 494 | return [2 /*return*/, commitResult.data]; 495 | } 496 | else { 497 | err = new Error(commitResult.message); 498 | err.code = commitResult.code; 499 | return [2 /*return*/, whenError(err)]; 500 | } 501 | return [2 /*return*/]; 502 | } 503 | }); 504 | }); 505 | }; 506 | Uploader.prototype.start = function () { 507 | var _this = this; 508 | var requestStartTime = new Date(); 509 | this.donePromise = this._start() 510 | .then(function (doneResult) { 511 | _this.emit(vod_reporter_1.VodReportEvent.report_done, { 512 | err: { code: 0 }, 513 | requestStartTime: requestStartTime 514 | }); 515 | return doneResult; 516 | }) 517 | .catch(function (err) { 518 | _this.emit(vod_reporter_1.VodReportEvent.report_done, { 519 | err: { 520 | code: (err && err.code) || util_1.default.CLIENT_ERROR_CODE.UPLOAD_FAIL 521 | }, 522 | requestStartTime: requestStartTime 523 | }); 524 | throw err; 525 | }); 526 | }; 527 | Uploader.prototype._start = function () { 528 | return __awaiter(this, void 0, void 0, function () { 529 | var applyData; 530 | return __generator(this, function (_a) { 531 | switch (_a.label) { 532 | case 0: return [4 /*yield*/, this.applyUploadUGC()]; 533 | case 1: 534 | applyData = _a.sent(); 535 | return [4 /*yield*/, this.uploadToCos(applyData)]; 536 | case 2: 537 | _a.sent(); 538 | return [4 /*yield*/, this.commitUploadUGC()]; 539 | case 3: return [2 /*return*/, _a.sent()]; 540 | } 541 | }); 542 | }); 543 | }; 544 | Uploader.prototype.done = function () { 545 | return this.donePromise; 546 | }; 547 | Uploader.prototype.cancel = function () { 548 | this.cos.cancelTask(this.taskId); 549 | }; 550 | // domain 551 | Uploader.host = util_1.HOST.MAIN; 552 | return Uploader; 553 | }(eventemitter3_1.EventEmitter)); 554 | exports.default = Uploader; 555 | //# sourceMappingURL=uploader.js.map -------------------------------------------------------------------------------- /lib/src/uploader.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"uploader.js","sourceRoot":"","sources":["../../src/uploader.ts"],"names":[],"mappings":"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"} -------------------------------------------------------------------------------- /lib/src/util.d.ts: -------------------------------------------------------------------------------- 1 | declare function isFile(v: any): boolean; 2 | declare function isFunction(v: any): boolean; 3 | declare function isString(v: any): boolean; 4 | declare function noop(): void; 5 | declare function delay(ms: number): Promise; 6 | declare function getUnix(): number; 7 | declare enum CLIENT_ERROR_CODE { 8 | UPLOAD_FAIL = 1 9 | } 10 | export declare enum HOST { 11 | MAIN = "vod2.qcloud.com", 12 | BACKUP = "vod2.dnsv1.com" 13 | } 14 | declare const _default: { 15 | isFile: typeof isFile; 16 | isFunction: typeof isFunction; 17 | isString: typeof isString; 18 | noop: typeof noop; 19 | delay: typeof delay; 20 | getUnix: typeof getUnix; 21 | isTest: boolean; 22 | isDev: boolean; 23 | CLIENT_ERROR_CODE: typeof CLIENT_ERROR_CODE; 24 | }; 25 | export default _default; 26 | -------------------------------------------------------------------------------- /lib/src/util.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.HOST = void 0; 4 | function isFile(v) { 5 | return Object.prototype.toString.call(v) == "[object File]"; 6 | } 7 | function isFunction(v) { 8 | return typeof v === "function"; 9 | } 10 | function isString(v) { 11 | return typeof v === "string"; 12 | } 13 | // eslint-disable-next-line @typescript-eslint/explicit-function-return-type 14 | function noop() { } 15 | function delay(ms) { 16 | return new Promise(function (resolve) { 17 | setTimeout(function () { 18 | resolve(); 19 | }, ms); 20 | }); 21 | } 22 | function getUnix() { 23 | return Math.floor(Date.now() / 1000); 24 | } 25 | var CLIENT_ERROR_CODE; 26 | (function (CLIENT_ERROR_CODE) { 27 | CLIENT_ERROR_CODE[CLIENT_ERROR_CODE["UPLOAD_FAIL"] = 1] = "UPLOAD_FAIL"; 28 | })(CLIENT_ERROR_CODE || (CLIENT_ERROR_CODE = {})); 29 | var HOST; 30 | (function (HOST) { 31 | HOST["MAIN"] = "vod2.qcloud.com"; 32 | HOST["BACKUP"] = "vod2.dnsv1.com"; 33 | })(HOST = exports.HOST || (exports.HOST = {})); 34 | exports.default = { 35 | isFile: isFile, 36 | isFunction: isFunction, 37 | isString: isString, 38 | noop: noop, 39 | delay: delay, 40 | getUnix: getUnix, 41 | isTest: process.env.NODE_ENV === "test", 42 | isDev: process.env.NODE_ENV === "development", 43 | CLIENT_ERROR_CODE: CLIENT_ERROR_CODE 44 | }; 45 | //# sourceMappingURL=util.js.map -------------------------------------------------------------------------------- /lib/src/util.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"util.js","sourceRoot":"","sources":["../../src/util.ts"],"names":[],"mappings":";;;AAAA,SAAS,MAAM,CAAC,CAAM;IACpB,OAAO,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,eAAe,CAAC;AAC9D,CAAC;AAED,SAAS,UAAU,CAAC,CAAM;IACxB,OAAO,OAAO,CAAC,KAAK,UAAU,CAAC;AACjC,CAAC;AAED,SAAS,QAAQ,CAAC,CAAM;IACtB,OAAO,OAAO,CAAC,KAAK,QAAQ,CAAC;AAC/B,CAAC;AAED,4EAA4E;AAC5E,SAAS,IAAI,KAAI,CAAC;AAElB,SAAS,KAAK,CAAC,EAAU;IACvB,OAAO,IAAI,OAAO,CAAC,UAAA,OAAO;QACxB,UAAU,CAAC;YACT,OAAO,EAAE,CAAC;QACZ,CAAC,EAAE,EAAE,CAAC,CAAC;IACT,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,OAAO;IACd,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;AACvC,CAAC;AAED,IAAK,iBAEJ;AAFD,WAAK,iBAAiB;IACpB,uEAAe,CAAA;AACjB,CAAC,EAFI,iBAAiB,KAAjB,iBAAiB,QAErB;AAED,IAAY,IAGX;AAHD,WAAY,IAAI;IACd,gCAAwB,CAAA;IACxB,iCAAyB,CAAA;AAC3B,CAAC,EAHW,IAAI,GAAJ,YAAI,KAAJ,YAAI,QAGf;AAED,kBAAe;IACb,MAAM,QAAA;IACN,UAAU,YAAA;IACV,QAAQ,UAAA;IACR,IAAI,MAAA;IACJ,KAAK,OAAA;IACL,OAAO,SAAA;IACP,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,MAAM;IACvC,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,aAAa;IAC7C,iBAAiB,mBAAA;CAClB,CAAC"} -------------------------------------------------------------------------------- /lib/src/vod_reporter.d.ts: -------------------------------------------------------------------------------- 1 | import Uploader from "./uploader"; 2 | interface IVodReporter { 3 | } 4 | export declare enum VodReportEvent { 5 | report_apply = "report_apply", 6 | report_cos_upload = "report_cos_upload", 7 | report_commit = "report_commit", 8 | report_done = "report_done" 9 | } 10 | interface ReportObj { 11 | err: any; 12 | requestStartTime: Date; 13 | data: any; 14 | } 15 | export declare class VodReporter { 16 | uploader: Uploader; 17 | options: IVodReporter; 18 | baseReportData: any; 19 | reportUrl: string; 20 | constructor(uploader: Uploader, options?: IVodReporter); 21 | init(): void; 22 | onApply(reportObj: ReportObj): void; 23 | onCosUpload(reportObj: ReportObj): void; 24 | onCommit(reportObj: ReportObj): void; 25 | onDone(reportObj: ReportObj): void; 26 | report(reportData: any): void; 27 | send(reportData: any): void; 28 | } 29 | export {}; 30 | -------------------------------------------------------------------------------- /lib/src/vod_reporter.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __assign = (this && this.__assign) || function () { 3 | __assign = Object.assign || function(t) { 4 | for (var s, i = 1, n = arguments.length; i < n; i++) { 5 | s = arguments[i]; 6 | for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) 7 | t[p] = s[p]; 8 | } 9 | return t; 10 | }; 11 | return __assign.apply(this, arguments); 12 | }; 13 | Object.defineProperty(exports, "__esModule", { value: true }); 14 | exports.VodReporter = exports.VodReportEvent = void 0; 15 | var uploader_1 = require("./uploader"); 16 | var pkg = require("../package.json"); 17 | var util_1 = require("./util"); 18 | var VodReportEvent; 19 | (function (VodReportEvent) { 20 | VodReportEvent["report_apply"] = "report_apply"; 21 | VodReportEvent["report_cos_upload"] = "report_cos_upload"; 22 | VodReportEvent["report_commit"] = "report_commit"; 23 | VodReportEvent["report_done"] = "report_done"; 24 | })(VodReportEvent = exports.VodReportEvent || (exports.VodReportEvent = {})); 25 | var ReqType; 26 | (function (ReqType) { 27 | ReqType[ReqType["apply"] = 10001] = "apply"; 28 | ReqType[ReqType["cos_upload"] = 20001] = "cos_upload"; 29 | ReqType[ReqType["commit"] = 10002] = "commit"; 30 | ReqType[ReqType["done"] = 40001] = "done"; 31 | })(ReqType || (ReqType = {})); 32 | var VodReporter = /** @class */ (function () { 33 | function VodReporter(uploader, options) { 34 | // only partial data when created 35 | this.baseReportData = { 36 | version: pkg.version, 37 | platform: 3000, 38 | device: navigator.userAgent 39 | }; 40 | this.reportUrl = "https://vodreport.qcloud.com/ugcupload_new"; 41 | this.uploader = uploader; 42 | this.options = options; 43 | this.init(); 44 | } 45 | VodReporter.prototype.init = function () { 46 | this.uploader.on(VodReportEvent.report_apply, this.onApply.bind(this)); 47 | this.uploader.on(VodReportEvent.report_cos_upload, this.onCosUpload.bind(this)); 48 | this.uploader.on(VodReportEvent.report_commit, this.onCommit.bind(this)); 49 | this.uploader.on(VodReportEvent.report_done, this.onDone.bind(this)); 50 | }; 51 | // ApplyUploadUGC 52 | VodReporter.prototype.onApply = function (reportObj) { 53 | try { 54 | var uploader = this.uploader; 55 | if (!uploader.videoFile) { 56 | return; 57 | } 58 | Object.assign(this.baseReportData, { 59 | appId: uploader.appId, 60 | fileSize: uploader.videoFile.size, 61 | fileName: uploader.videoFile.name, 62 | fileType: uploader.videoFile.type, 63 | vodSessionKey: uploader.vodSessionKey, 64 | reqKey: uploader.reqKey, 65 | reportId: uploader.reportId 66 | }); 67 | var customReportData = { 68 | reqType: ReqType.apply, 69 | errCode: 0, 70 | vodErrCode: 0, 71 | errMsg: "", 72 | reqTimeCost: Number(new Date()) - Number(reportObj.requestStartTime), 73 | reqTime: Number(reportObj.requestStartTime) 74 | }; 75 | if (reportObj.err) { 76 | customReportData.errCode = 1; 77 | customReportData.vodErrCode = reportObj.err.code; 78 | customReportData.errMsg = reportObj.err.message; 79 | } 80 | if (reportObj.data) { 81 | this.baseReportData.cosRegion = reportObj.data.storageRegionV5; 82 | } 83 | this.report(customReportData); 84 | } 85 | catch (e) { 86 | console.error("onApply", e); 87 | if (util_1.default.isTest) { 88 | throw e; 89 | } 90 | } 91 | }; 92 | // upload to cos 93 | VodReporter.prototype.onCosUpload = function (reportObj) { 94 | try { 95 | var customReportData = { 96 | reqType: ReqType.cos_upload, 97 | errCode: 0, 98 | cosErrCode: "", 99 | errMsg: "", 100 | reqTimeCost: Number(new Date()) - Number(reportObj.requestStartTime), 101 | reqTime: Number(reportObj.requestStartTime) 102 | }; 103 | if (reportObj.err) { 104 | customReportData.errCode = 1; 105 | customReportData.cosErrCode = reportObj.err.error 106 | ? reportObj.err.error.Code 107 | : reportObj.err; 108 | if (reportObj.err && reportObj.err.error === "error") { 109 | customReportData.cosErrCode = "cors error"; 110 | } 111 | customReportData.errMsg = JSON.stringify(reportObj.err); 112 | } 113 | this.report(customReportData); 114 | } 115 | catch (e) { 116 | console.error("onCosUpload", e); 117 | if (util_1.default.isTest) { 118 | throw e; 119 | } 120 | } 121 | }; 122 | // CommitUploadUGC 123 | VodReporter.prototype.onCommit = function (reportObj) { 124 | try { 125 | var customReportData = { 126 | reqType: ReqType.commit, 127 | errCode: 0, 128 | vodErrCode: 0, 129 | errMsg: "", 130 | reqTimeCost: Number(new Date()) - Number(reportObj.requestStartTime), 131 | reqTime: Number(reportObj.requestStartTime) 132 | }; 133 | if (reportObj.err) { 134 | customReportData.errCode = 1; 135 | customReportData.vodErrCode = reportObj.err.code; 136 | customReportData.errMsg = reportObj.err.message; 137 | } 138 | if (reportObj.data) { 139 | this.baseReportData.fileId = reportObj.data.fileId; 140 | } 141 | this.report(customReportData); 142 | } 143 | catch (e) { 144 | console.error("onCommit", e); 145 | if (util_1.default.isTest) { 146 | throw e; 147 | } 148 | } 149 | }; 150 | VodReporter.prototype.onDone = function (reportObj) { 151 | try { 152 | var customReportData = { 153 | reqType: ReqType.done, 154 | errCode: reportObj.err && reportObj.err.code, 155 | reqTimeCost: Number(new Date()) - Number(reportObj.requestStartTime), 156 | reqTime: Number(reportObj.requestStartTime) 157 | }; 158 | this.report(customReportData); 159 | } 160 | catch (e) { 161 | console.error("onDone", e); 162 | if (util_1.default.isTest) { 163 | throw e; 164 | } 165 | } 166 | }; 167 | VodReporter.prototype.report = function (reportData) { 168 | reportData = __assign(__assign({}, this.baseReportData), reportData); 169 | this.send(reportData); 170 | }; 171 | VodReporter.prototype.send = function (reportData) { 172 | if (util_1.default.isDev || util_1.default.isTest) { 173 | console.log("send reportData", reportData); 174 | return; 175 | } 176 | uploader_1.vodAxios.post(this.reportUrl, reportData); 177 | }; 178 | return VodReporter; 179 | }()); 180 | exports.VodReporter = VodReporter; 181 | //# sourceMappingURL=vod_reporter.js.map -------------------------------------------------------------------------------- /lib/src/vod_reporter.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"vod_reporter.js","sourceRoot":"","sources":["../../src/vod_reporter.ts"],"names":[],"mappings":";;;;;;;;;;;;;;AAAA,uCAAgD;AAChD,qCAAuC;AACvC,+BAA0B;AAK1B,IAAY,cAKX;AALD,WAAY,cAAc;IACxB,+CAA6B,CAAA;IAC7B,yDAAuC,CAAA;IACvC,iDAA+B,CAAA;IAC/B,6CAA2B,CAAA;AAC7B,CAAC,EALW,cAAc,GAAd,sBAAc,KAAd,sBAAc,QAKzB;AAED,IAAK,OAKJ;AALD,WAAK,OAAO;IACV,2CAAa,CAAA;IACb,qDAAkB,CAAA;IAClB,6CAAc,CAAA;IACd,yCAAY,CAAA;AACd,CAAC,EALI,OAAO,KAAP,OAAO,QAKX;AAQD;IAaE,qBAAY,QAAkB,EAAE,OAAsB;QATtD,iCAAiC;QACjC,mBAAc,GAAQ;YACpB,OAAO,EAAE,GAAG,CAAC,OAAO;YACpB,QAAQ,EAAE,IAAI;YACd,MAAM,EAAE,SAAS,CAAC,SAAS;SAC5B,CAAC;QAEF,cAAS,GAAG,4CAA4C,CAAC;QAGvD,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QAEvB,IAAI,CAAC,IAAI,EAAE,CAAC;IACd,CAAC;IAED,0BAAI,GAAJ;QACE,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,cAAc,CAAC,YAAY,EAAE,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QACvE,IAAI,CAAC,QAAQ,CAAC,EAAE,CACd,cAAc,CAAC,iBAAiB,EAChC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAC5B,CAAC;QACF,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,cAAc,CAAC,aAAa,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QACzE,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,cAAc,CAAC,WAAW,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IACvE,CAAC;IAED,iBAAiB;IACjB,6BAAO,GAAP,UAAQ,SAAoB;QAC1B,IAAI;YACF,IAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC;YAC/B,IAAI,CAAC,QAAQ,CAAC,SAAS,EAAE;gBACvB,OAAO;aACR;YACD,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,cAAc,EAAE;gBACjC,KAAK,EAAE,QAAQ,CAAC,KAAK;gBACrB,QAAQ,EAAE,QAAQ,CAAC,SAAS,CAAC,IAAI;gBACjC,QAAQ,EAAE,QAAQ,CAAC,SAAS,CAAC,IAAI;gBACjC,QAAQ,EAAE,QAAQ,CAAC,SAAS,CAAC,IAAI;gBACjC,aAAa,EAAE,QAAQ,CAAC,aAAa;gBACrC,MAAM,EAAE,QAAQ,CAAC,MAAM;gBACvB,QAAQ,EAAE,QAAQ,CAAC,QAAQ;aAC5B,CAAC,CAAC;YAEH,IAAM,gBAAgB,GAAG;gBACvB,OAAO,EAAE,OAAO,CAAC,KAAK;gBACtB,OAAO,EAAE,CAAC;gBACV,UAAU,EAAE,CAAC;gBACb,MAAM,EAAE,EAAE;gBACV,WAAW,EAAE,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC,GAAG,MAAM,CAAC,SAAS,CAAC,gBAAgB,CAAC;gBACpE,OAAO,EAAE,MAAM,CAAC,SAAS,CAAC,gBAAgB,CAAC;aAC5C,CAAC;YACF,IAAI,SAAS,CAAC,GAAG,EAAE;gBACjB,gBAAgB,CAAC,OAAO,GAAG,CAAC,CAAC;gBAC7B,gBAAgB,CAAC,UAAU,GAAG,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC;gBACjD,gBAAgB,CAAC,MAAM,GAAG,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC;aACjD;YACD,IAAI,SAAS,CAAC,IAAI,EAAE;gBAClB,IAAI,CAAC,cAAc,CAAC,SAAS,GAAG,SAAS,CAAC,IAAI,CAAC,eAAe,CAAC;aAChE;YACD,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC;SAC/B;QAAC,OAAO,CAAC,EAAE;YACV,OAAO,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;YAC5B,IAAI,cAAI,CAAC,MAAM,EAAE;gBACf,MAAM,CAAC,CAAC;aACT;SACF;IACH,CAAC;IAED,gBAAgB;IAChB,iCAAW,GAAX,UAAY,SAAoB;QAC9B,IAAI;YACF,IAAM,gBAAgB,GAAG;gBACvB,OAAO,EAAE,OAAO,CAAC,UAAU;gBAC3B,OAAO,EAAE,CAAC;gBACV,UAAU,EAAE,EAAE;gBACd,MAAM,EAAE,EAAE;gBACV,WAAW,EAAE,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC,GAAG,MAAM,CAAC,SAAS,CAAC,gBAAgB,CAAC;gBACpE,OAAO,EAAE,MAAM,CAAC,SAAS,CAAC,gBAAgB,CAAC;aAC5C,CAAC;YACF,IAAI,SAAS,CAAC,GAAG,EAAE;gBACjB,gBAAgB,CAAC,OAAO,GAAG,CAAC,CAAC;gBAC7B,gBAAgB,CAAC,UAAU,GAAG,SAAS,CAAC,GAAG,CAAC,KAAK;oBAC/C,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI;oBAC1B,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC;gBAClB,IAAI,SAAS,CAAC,GAAG,IAAI,SAAS,CAAC,GAAG,CAAC,KAAK,KAAK,OAAO,EAAE;oBACpD,gBAAgB,CAAC,UAAU,GAAG,YAAY,CAAC;iBAC5C;gBACD,gBAAgB,CAAC,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;aACzD;YACD,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC;SAC/B;QAAC,OAAO,CAAC,EAAE;YACV,OAAO,CAAC,KAAK,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC;YAChC,IAAI,cAAI,CAAC,MAAM,EAAE;gBACf,MAAM,CAAC,CAAC;aACT;SACF;IACH,CAAC;IAED,kBAAkB;IAClB,8BAAQ,GAAR,UAAS,SAAoB;QAC3B,IAAI;YACF,IAAM,gBAAgB,GAAG;gBACvB,OAAO,EAAE,OAAO,CAAC,MAAM;gBACvB,OAAO,EAAE,CAAC;gBACV,UAAU,EAAE,CAAC;gBACb,MAAM,EAAE,EAAE;gBACV,WAAW,EAAE,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC,GAAG,MAAM,CAAC,SAAS,CAAC,gBAAgB,CAAC;gBACpE,OAAO,EAAE,MAAM,CAAC,SAAS,CAAC,gBAAgB,CAAC;aAC5C,CAAC;YACF,IAAI,SAAS,CAAC,GAAG,EAAE;gBACjB,gBAAgB,CAAC,OAAO,GAAG,CAAC,CAAC;gBAC7B,gBAAgB,CAAC,UAAU,GAAG,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC;gBACjD,gBAAgB,CAAC,MAAM,GAAG,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC;aACjD;YACD,IAAI,SAAS,CAAC,IAAI,EAAE;gBAClB,IAAI,CAAC,cAAc,CAAC,MAAM,GAAG,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC;aACpD;YACD,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC;SAC/B;QAAC,OAAO,CAAC,EAAE;YACV,OAAO,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC;YAC7B,IAAI,cAAI,CAAC,MAAM,EAAE;gBACf,MAAM,CAAC,CAAC;aACT;SACF;IACH,CAAC;IAED,4BAAM,GAAN,UAAO,SAAoB;QACzB,IAAI;YACF,IAAM,gBAAgB,GAAG;gBACvB,OAAO,EAAE,OAAO,CAAC,IAAI;gBACrB,OAAO,EAAE,SAAS,CAAC,GAAG,IAAI,SAAS,CAAC,GAAG,CAAC,IAAI;gBAC5C,WAAW,EAAE,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC,GAAG,MAAM,CAAC,SAAS,CAAC,gBAAgB,CAAC;gBACpE,OAAO,EAAE,MAAM,CAAC,SAAS,CAAC,gBAAgB,CAAC;aAC5C,CAAC;YACF,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC;SAC/B;QAAC,OAAO,CAAC,EAAE;YACV,OAAO,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;YAC3B,IAAI,cAAI,CAAC,MAAM,EAAE;gBACf,MAAM,CAAC,CAAC;aACT;SACF;IACH,CAAC;IAED,4BAAM,GAAN,UAAO,UAAe;QACpB,UAAU,yBAAQ,IAAI,CAAC,cAAc,GAAK,UAAU,CAAE,CAAC;QACvD,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACxB,CAAC;IAED,0BAAI,GAAJ,UAAK,UAAe;QAClB,IAAI,cAAI,CAAC,KAAK,IAAI,cAAI,CAAC,MAAM,EAAE;YAC7B,OAAO,CAAC,GAAG,CAAC,iBAAiB,EAAE,UAAU,CAAC,CAAC;YAC3C,OAAO;SACR;QACD,mBAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;IAC5C,CAAC;IACH,kBAAC;AAAD,CAAC,AA/JD,IA+JC;AA/JY,kCAAW"} -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vod-js-sdk-v6", 3 | "version": "1.4.12", 4 | "description": "tencent cloud vod js sdk v6", 5 | "main": "lib/src/tc_vod.js", 6 | "unpkg": "dist/vod-js-sdk-v6.js", 7 | "typings": "lib/src/tc_vod.d.ts", 8 | "scripts": { 9 | "test": "cross-env NODE_ENV=test mocha -r espower-typescript/guess -r jsdom-global/register -r test/env test/**/*.test.ts", 10 | "cover": "cross-env NODE_ENV=test nyc mocha -r espower-typescript/guess -r jsdom-global/register -r test/env test/**/*.test.ts", 11 | "dev": "webpack --config webpack.dev.config.js --watch", 12 | "dist": "webpack --config webpack.config.js", 13 | "build": "npm run test && npm run dist && npm run compile", 14 | "compile": "tsc -p tsconfig.json", 15 | "prepublish": "npm run build", 16 | "lint": "tsc --noEmit && eslint 'src/**/*.{js,ts,tsx}' --quiet --fix" 17 | }, 18 | "repository": { 19 | "type": "git", 20 | "url": "git+https://github.com/tencentyun/vod-js-sdk-v6.git" 21 | }, 22 | "keywords": [ 23 | "tencentcloud", 24 | "sdk", 25 | "vod" 26 | ], 27 | "author": "alsotang ", 28 | "license": "MIT", 29 | "bugs": { 30 | "url": "https://github.com/tencentyun/vod-js-sdk-v6/issues" 31 | }, 32 | "homepage": "https://github.com/tencentyun/vod-js-sdk-v6#readme", 33 | "dependencies": { 34 | "axios": "^0.21.1", 35 | "cos-js-sdk-v5": "^1.3.7", 36 | "eventemitter3": "^4.0.7", 37 | "js-sha1": "^0.6.0", 38 | "uuid": "^3.3.2" 39 | }, 40 | "devDependencies": { 41 | "@types/mocha": "^5.2.5", 42 | "@types/semver": "^6.0.0", 43 | "@types/sha1": "^1.1.1", 44 | "@types/uuid": "^3.4.4", 45 | "@typescript-eslint/eslint-plugin": "^1.9.0", 46 | "@typescript-eslint/parser": "^1.9.0", 47 | "cross-env": "^6.0.3", 48 | "eslint": "^5.16.0", 49 | "eslint-config-prettier": "^4.3.0", 50 | "eslint-plugin-prettier": "^3.1.0", 51 | "espower-typescript": "^9.0.1", 52 | "jsdom": "^13.1.0", 53 | "jsdom-global": "^3.0.2", 54 | "mm": "^2.4.1", 55 | "mocha": "^5.2.0", 56 | "nyc": "^13.1.0", 57 | "power-assert": "^1.6.1", 58 | "prettier": "^1.17.1", 59 | "semver": "^6.1.1", 60 | "ts-loader": "^5.3.3", 61 | "typescript": "^3.5.3", 62 | "webpack": "^4.28.1", 63 | "webpack-cli": "^3.2.1" 64 | }, 65 | "nyc": { 66 | "extension": [ 67 | ".ts", 68 | ".tsx" 69 | ], 70 | "include": [ 71 | "src" 72 | ], 73 | "reporter": [ 74 | "html" 75 | ], 76 | "all": true 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/tc_vod.ts: -------------------------------------------------------------------------------- 1 | import Uploader, { IGetSignature, UploaderOptions } from "./uploader"; 2 | import { VodReporter } from "./vod_reporter"; 3 | 4 | interface TcVodParams { 5 | getSignature: IGetSignature; 6 | allowReport?: boolean; 7 | appId?: number; 8 | reportId?: string; 9 | enableResume?: boolean; 10 | } 11 | class TcVod { 12 | getSignature: IGetSignature; 13 | allowReport = true; // report sdk status to tencent cloud 14 | appId: number; 15 | reportId: string; 16 | enableResume = true; // resume uploading from break point 17 | constructor(params: TcVodParams) { 18 | this.getSignature = params.getSignature; 19 | if (params.allowReport !== void 0) { 20 | this.allowReport = params.allowReport; 21 | } 22 | if (params.enableResume !== void 0) { 23 | this.enableResume = params.enableResume; 24 | } 25 | this.appId = params.appId; 26 | this.reportId = params.reportId; 27 | } 28 | 29 | upload(params: Omit): Uploader { 30 | const uploaderParams = { 31 | getSignature: this.getSignature, 32 | appId: this.appId, 33 | reportId: this.reportId, 34 | enableResume: this.enableResume, 35 | ...params 36 | }; 37 | const uploader = new Uploader(uploaderParams); 38 | if (this.allowReport) { 39 | this.initReporter(uploader); 40 | } 41 | uploader.start(); 42 | return uploader; 43 | } 44 | 45 | // report to official report system 46 | initReporter(uploader: Uploader): void { 47 | new VodReporter(uploader); 48 | } 49 | } 50 | 51 | export default TcVod; 52 | -------------------------------------------------------------------------------- /src/types.d.ts: -------------------------------------------------------------------------------- 1 | export type vodError = Error & { 2 | code?: number; 3 | }; 4 | -------------------------------------------------------------------------------- /src/uploader.ts: -------------------------------------------------------------------------------- 1 | const sha1 = require("js-sha1"); 2 | const COS = require("cos-js-sdk-v5"); 3 | 4 | import { EventEmitter } from "eventemitter3"; 5 | import axios from "axios"; 6 | import util, { HOST } from "./util"; 7 | import { vodError } from "./types"; 8 | import { VodReportEvent } from "./vod_reporter"; 9 | import * as uuidv4 from "uuid/v4"; 10 | 11 | export const vodAxios = axios.create(); 12 | 13 | vodAxios.interceptors.response.use( 14 | function(response) { 15 | return response; 16 | }, 17 | function(error) { 18 | if (isNaN(error.code)) { 19 | error.code = 500; 20 | } 21 | return Promise.reject(error); 22 | } 23 | ); 24 | 25 | export type IGetSignature = () => Promise; 26 | export interface TcVodFileInfo { 27 | name: string; 28 | type: string; 29 | size: number; 30 | } 31 | 32 | export enum UploaderEvent { 33 | video_progress = "video_progress", 34 | media_progress = "media_progress", 35 | 36 | video_upload = "video_upload", 37 | media_upload = "media_upload", 38 | 39 | cover_progress = "cover_progress", 40 | cover_upload = "cover_upload" 41 | } 42 | 43 | interface IApplyUpload { 44 | signature: string; 45 | vodSessionKey?: string; 46 | 47 | videoName?: string; 48 | videoType?: string; 49 | videoSize?: number; 50 | 51 | coverName?: string; 52 | coverType?: string; 53 | coverSize?: number; 54 | 55 | fileId?: string; 56 | } 57 | 58 | interface IApplyData { 59 | video: { 60 | storageSignature: string; 61 | storagePath: string; 62 | }; 63 | cover?: { 64 | storageSignature: string; 65 | storagePath: string; 66 | }; 67 | storageAppId: number; 68 | storageBucket: string; 69 | storageRegion: string; 70 | storageRegionV5: string; 71 | domain: string; 72 | vodSessionKey: string; 73 | tempCertificate: { 74 | secretId: string; 75 | secretKey: string; 76 | token: string; 77 | expiredTime: number; 78 | }; 79 | appId: number; 80 | timestamp: number; 81 | StorageRegionV5: string; 82 | } 83 | 84 | export interface UploaderOptions { 85 | getSignature: IGetSignature; 86 | 87 | videoFile?: File; 88 | mediaFile?: File; 89 | coverFile?: File; 90 | 91 | videoName?: string; 92 | mediaName?: string; 93 | fileId?: string; 94 | 95 | appId?: number; 96 | reportId?: string; 97 | 98 | applyRequestTimeout?: number; 99 | commitRequestTimeout?: number; 100 | retryDelay?: number; 101 | 102 | enableResume?: boolean; 103 | } 104 | 105 | class Uploader extends EventEmitter implements UploaderOptions { 106 | getSignature: IGetSignature; 107 | videoFile: File; 108 | videoInfo: TcVodFileInfo; 109 | coverFile: File; 110 | coverInfo: TcVodFileInfo; 111 | 112 | cos: any; 113 | taskId: string; 114 | cosAuthTime: number; 115 | 116 | videoName: string; 117 | sessionName: string = ""; 118 | vodSessionKey: string = ""; 119 | appId: number = 0; 120 | fileId: string; 121 | 122 | reqKey: string = uuidv4(); 123 | reportId: string = ""; 124 | 125 | // resume from break point 126 | enableResume: boolean = true; 127 | 128 | donePromise: Promise; 129 | 130 | // apply 请求的超时时间 131 | applyRequestTimeout = 5000; 132 | applyRequestRetryCount = 3; 133 | 134 | // commit 请求的超时时间 135 | commitRequestTimeout = 5000; 136 | commitRequestRetryCount = 3; 137 | 138 | // 重试请求的等待时间 139 | retryDelay = 1000; 140 | 141 | // domain 142 | static host = HOST.MAIN; 143 | 144 | constructor(params: UploaderOptions) { 145 | super(); 146 | this.validateInitParams(params); 147 | 148 | this.videoFile = params.mediaFile || params.videoFile; 149 | this.getSignature = params.getSignature; 150 | 151 | this.enableResume = params.enableResume; 152 | this.videoName = params.mediaName || params.videoName; 153 | this.coverFile = params.coverFile; 154 | this.fileId = params.fileId; 155 | 156 | this.applyRequestTimeout = 157 | params.applyRequestTimeout || this.applyRequestTimeout; 158 | this.commitRequestTimeout = 159 | params.commitRequestTimeout || this.commitRequestTimeout; 160 | this.retryDelay = params.retryDelay || this.retryDelay; 161 | 162 | // custom report metrics 163 | this.appId = params.appId || this.appId; 164 | this.reportId = params.reportId || this.reportId; 165 | 166 | this.cosAuthTime = 0; 167 | this.genFileInfo(); 168 | } 169 | 170 | // set storage 171 | setStorage(name: string, value: string): void { 172 | if (!name) { 173 | return; 174 | } 175 | 176 | const cname = "webugc_" + sha1(name); 177 | try { 178 | localStorage.setItem(cname, value); 179 | } catch (e) {} 180 | } 181 | 182 | // get storage 183 | getStorage(name: string): string { 184 | if (!name) { 185 | return; 186 | } 187 | const cname = "webugc_" + sha1(name); 188 | let result = null; 189 | try { 190 | result = localStorage.getItem(cname); 191 | } catch (e) {} 192 | 193 | return result; 194 | } 195 | 196 | // delete storage 197 | delStorage(name: string): void { 198 | if (!name) { 199 | return; 200 | } 201 | const cname = "webugc_" + sha1(name); 202 | try { 203 | localStorage.removeItem(cname); 204 | } catch (e) {} 205 | } 206 | 207 | // validate init params 208 | validateInitParams(params: UploaderOptions): void { 209 | if (!util.isFunction(params.getSignature)) { 210 | throw new Error("getSignature must be a function"); 211 | } 212 | if (params.videoFile && !util.isFile(params.videoFile)) { 213 | throw new Error("videoFile must be a File"); 214 | } 215 | } 216 | 217 | genFileInfo(): void { 218 | // video file info 219 | const videoFile = this.videoFile; 220 | if (videoFile) { 221 | const lastDotIndex = videoFile.name.lastIndexOf("."); 222 | let videoName = ""; 223 | // if specified, use it. 224 | if (this.videoName) { 225 | if (!util.isString(this.videoName)) { 226 | throw new Error("mediaName must be a string"); 227 | } else if (/[:*?<>\"\\/|]/g.test(this.videoName)) { 228 | throw new Error( 229 | 'Cant use these chars in filename: \\ / : * ? " < > |' 230 | ); 231 | } else { 232 | videoName = this.videoName; 233 | } 234 | } else { 235 | // else use the meta info of file 236 | videoName = videoFile.name.substring(0, lastDotIndex); 237 | } 238 | this.videoInfo = { 239 | name: videoName, 240 | type: videoFile.name.substring(lastDotIndex + 1).toLowerCase(), 241 | size: videoFile.size 242 | }; 243 | this.sessionName += `${videoFile.name}_${videoFile.size};`; 244 | } 245 | 246 | // cover file info 247 | const coverFile = this.coverFile; 248 | if (coverFile) { 249 | const coverName = coverFile.name; 250 | const coverLastDotIndex = coverName.lastIndexOf("."); 251 | this.coverInfo = { 252 | name: coverName.substring(0, coverLastDotIndex), 253 | type: coverName.substring(coverLastDotIndex + 1).toLowerCase(), 254 | size: coverFile.size 255 | }; 256 | this.sessionName += `${coverFile.name}_${coverFile.size};`; 257 | } 258 | } 259 | 260 | async applyUploadUGC(retryCount: number = 0): Promise { 261 | const self = this; 262 | 263 | const signature = await this.getSignature(); 264 | 265 | let sendParams: IApplyUpload; 266 | const videoInfo = this.videoInfo; 267 | const coverInfo = this.coverInfo; 268 | const vodSessionKey = 269 | this.vodSessionKey || 270 | (this.enableResume && this.getStorage(this.sessionName)); 271 | 272 | // resume from break point 273 | if (vodSessionKey) { 274 | sendParams = { 275 | signature: signature, 276 | vodSessionKey: vodSessionKey 277 | }; 278 | } else if (videoInfo) { 279 | sendParams = { 280 | signature: signature, 281 | videoName: videoInfo.name, 282 | videoType: videoInfo.type, 283 | videoSize: videoInfo.size 284 | }; 285 | if (coverInfo) { 286 | // upload video together with cover 287 | sendParams.coverName = coverInfo.name; 288 | sendParams.coverType = coverInfo.type; 289 | sendParams.coverSize = coverInfo.size; 290 | } 291 | } else if (this.fileId && coverInfo) { 292 | // alter cover 293 | sendParams = { 294 | signature: signature, 295 | fileId: this.fileId, 296 | coverName: coverInfo.name, 297 | coverType: coverInfo.type, 298 | coverSize: coverInfo.size 299 | }; 300 | } else { 301 | throw "Wrong params, please check and try again"; 302 | } 303 | const requestStartTime = new Date(); 304 | 305 | async function whenError(err?: vodError): Promise { 306 | if (err.code === 500) { 307 | Uploader.host = Uploader.host === HOST.MAIN ? HOST.BACKUP : HOST.MAIN; 308 | } 309 | self.emit(VodReportEvent.report_apply, { 310 | err: err, 311 | requestStartTime: requestStartTime 312 | }); 313 | self.delStorage(self.sessionName); 314 | if (self.applyRequestRetryCount == retryCount) { 315 | if (err) { 316 | throw err; 317 | } 318 | throw new Error(`apply upload failed`); 319 | } 320 | await util.delay(self.retryDelay); 321 | return self.applyUploadUGC(retryCount + 1); 322 | } 323 | 324 | let response; 325 | try { 326 | response = await vodAxios.post( 327 | `https://${Uploader.host}/v3/index.php?Action=ApplyUploadUGC`, 328 | sendParams, 329 | { 330 | timeout: this.applyRequestTimeout, 331 | withCredentials: false 332 | } 333 | ); 334 | } catch (e) { 335 | return whenError(e); 336 | } 337 | 338 | const applyResult = response.data; 339 | 340 | // all err code https://user-images.githubusercontent.com/1147375/51222454-bf6ef280-1978-11e9-8e33-1b0fdb2fe200.png 341 | if (applyResult.code == 0) { 342 | const applyData = applyResult.data as IApplyData; 343 | const vodSessionKey = applyData.vodSessionKey; 344 | this.setStorage(this.sessionName, vodSessionKey); 345 | this.vodSessionKey = vodSessionKey; 346 | this.appId = applyData.appId; 347 | 348 | this.emit(VodReportEvent.report_apply, { 349 | data: applyData, 350 | requestStartTime: requestStartTime 351 | }); 352 | return applyData; 353 | } else { 354 | // return the apply result error info 355 | const err: vodError = new Error(applyResult.message); 356 | err.code = applyResult.code; 357 | 358 | return whenError(err); 359 | } 360 | } 361 | 362 | async uploadToCos(applyData: IApplyData) { 363 | const self = this; 364 | 365 | const cosParam = { 366 | bucket: applyData.storageBucket + "-" + applyData.storageAppId, 367 | region: applyData.storageRegionV5 368 | }; 369 | 370 | const cos = new COS({ 371 | getAuthorization: async function(options: object, callback: Function) { 372 | const currentTimeStamp = util.getUnix(); 373 | const safeExpireTime = 374 | (applyData.tempCertificate.expiredTime - applyData.timestamp) * 0.9; 375 | if (self.cosAuthTime === 0) { 376 | self.cosAuthTime = currentTimeStamp; 377 | } else if ( 378 | self.cosAuthTime && 379 | currentTimeStamp - self.cosAuthTime >= safeExpireTime 380 | ) { 381 | applyData = await self.applyUploadUGC(); 382 | self.cosAuthTime = util.getUnix(); 383 | } 384 | 385 | callback({ 386 | TmpSecretId: applyData.tempCertificate.secretId, 387 | TmpSecretKey: applyData.tempCertificate.secretKey, 388 | XCosSecurityToken: applyData.tempCertificate.token, 389 | StartTime: applyData.timestamp, 390 | ExpiredTime: applyData.tempCertificate.expiredTime 391 | }); 392 | } 393 | }); 394 | this.cos = cos; 395 | 396 | const uploadCosParams = []; 397 | 398 | if (this.videoFile) { 399 | const cosVideoParam = { 400 | ...cosParam, 401 | file: this.videoFile, 402 | key: applyData.video.storagePath, 403 | onProgress: function(data: any) { 404 | self.emit(UploaderEvent.video_progress, data); 405 | self.emit(UploaderEvent.media_progress, data); 406 | }, 407 | onUpload: function(data: any) { 408 | self.emit(UploaderEvent.video_upload, data); 409 | self.emit(UploaderEvent.media_upload, data); 410 | }, 411 | onTaskReady: function(taskId: string) { 412 | self.taskId = taskId; 413 | } 414 | }; 415 | uploadCosParams.push(cosVideoParam); 416 | } 417 | 418 | if (this.coverFile) { 419 | const cosCoverParam = { 420 | ...cosParam, 421 | file: this.coverFile, 422 | key: applyData.cover.storagePath, 423 | onProgress: function(data: any) { 424 | self.emit(UploaderEvent.cover_progress, data); 425 | }, 426 | onUpload: function(data: any) { 427 | self.emit(UploaderEvent.cover_upload, data); 428 | }, 429 | onTaskReady: util.noop 430 | }; 431 | uploadCosParams.push(cosCoverParam); 432 | } 433 | const requestStartTime = new Date(); 434 | const uploadPromises = uploadCosParams.map(uploadCosParam => { 435 | return new Promise(function(resolve, reject) { 436 | cos.sliceUploadFile( 437 | { 438 | Bucket: uploadCosParam.bucket, 439 | Region: uploadCosParam.region, 440 | Key: uploadCosParam.key, 441 | Body: uploadCosParam.file, 442 | onTaskReady: uploadCosParam.onTaskReady, 443 | onProgress: uploadCosParam.onProgress 444 | }, 445 | function(err: any, data: any) { 446 | // only report video file 447 | if (uploadCosParam.file === self.videoFile) { 448 | self.emit(VodReportEvent.report_cos_upload, { 449 | err: err, 450 | requestStartTime: requestStartTime 451 | }); 452 | } 453 | if (!err) { 454 | uploadCosParam.onUpload(data); 455 | return resolve(); 456 | } 457 | self.delStorage(self.sessionName); 458 | if (JSON.stringify(err) === '{"error":"error","headers":{}}') { 459 | return reject(new Error("cors error")); 460 | } 461 | reject(err); 462 | } 463 | ); 464 | }); 465 | }); 466 | 467 | return await Promise.all(uploadPromises); 468 | } 469 | 470 | async commitUploadUGC(retryCount: number = 0) { 471 | const self = this; 472 | 473 | const signature = await this.getSignature(); 474 | this.delStorage(this.sessionName); 475 | const vodSessionKey = this.vodSessionKey; 476 | 477 | const requestStartTime = new Date(); 478 | async function whenError(err?: vodError): Promise { 479 | if (err.code === 500) { 480 | Uploader.host = Uploader.host === HOST.MAIN ? HOST.BACKUP : HOST.MAIN; 481 | } 482 | self.emit(VodReportEvent.report_commit, { 483 | err: err, 484 | requestStartTime: requestStartTime 485 | }); 486 | if (self.commitRequestRetryCount == retryCount) { 487 | if (err) { 488 | throw err; 489 | } 490 | throw new Error("commit upload failed"); 491 | } 492 | await util.delay(self.retryDelay); 493 | return self.commitUploadUGC(retryCount + 1); 494 | } 495 | 496 | let response; 497 | try { 498 | response = await vodAxios.post( 499 | `https://${Uploader.host}/v3/index.php?Action=CommitUploadUGC`, 500 | { 501 | signature: signature, 502 | vodSessionKey: vodSessionKey 503 | }, 504 | { 505 | timeout: this.commitRequestTimeout, 506 | withCredentials: false 507 | } 508 | ); 509 | } catch (e) { 510 | return whenError(e); 511 | } 512 | 513 | const commitResult = response.data; 514 | 515 | if (commitResult.code == 0) { 516 | this.emit(VodReportEvent.report_commit, { 517 | data: commitResult.data, 518 | requestStartTime: requestStartTime 519 | }); 520 | return commitResult.data; 521 | } else { 522 | const err: vodError = new Error(commitResult.message); 523 | err.code = commitResult.code; 524 | return whenError(err); 525 | } 526 | } 527 | 528 | start() { 529 | const requestStartTime = new Date(); 530 | 531 | this.donePromise = this._start() 532 | .then(doneResult => { 533 | this.emit(VodReportEvent.report_done, { 534 | err: { code: 0 }, 535 | requestStartTime: requestStartTime 536 | }); 537 | return doneResult; 538 | }) 539 | .catch(err => { 540 | this.emit(VodReportEvent.report_done, { 541 | err: { 542 | code: (err && err.code) || util.CLIENT_ERROR_CODE.UPLOAD_FAIL 543 | }, 544 | requestStartTime: requestStartTime 545 | }); 546 | throw err; 547 | }); 548 | } 549 | 550 | async _start() { 551 | const applyData = await this.applyUploadUGC(); 552 | 553 | await this.uploadToCos(applyData); 554 | 555 | return await this.commitUploadUGC(); 556 | } 557 | 558 | done() { 559 | return this.donePromise; 560 | } 561 | 562 | cancel() { 563 | this.cos.cancelTask(this.taskId); 564 | } 565 | } 566 | 567 | export default Uploader; 568 | -------------------------------------------------------------------------------- /src/util.ts: -------------------------------------------------------------------------------- 1 | function isFile(v: any): boolean { 2 | return Object.prototype.toString.call(v) == "[object File]"; 3 | } 4 | 5 | function isFunction(v: any): boolean { 6 | return typeof v === "function"; 7 | } 8 | 9 | function isString(v: any): boolean { 10 | return typeof v === "string"; 11 | } 12 | 13 | // eslint-disable-next-line @typescript-eslint/explicit-function-return-type 14 | function noop() {} 15 | 16 | function delay(ms: number) { 17 | return new Promise(resolve => { 18 | setTimeout(() => { 19 | resolve(); 20 | }, ms); 21 | }); 22 | } 23 | 24 | function getUnix() { 25 | return Math.floor(Date.now() / 1000); 26 | } 27 | 28 | enum CLIENT_ERROR_CODE { 29 | UPLOAD_FAIL = 1 30 | } 31 | 32 | export enum HOST { 33 | MAIN = "vod2.qcloud.com", 34 | BACKUP = "vod2.dnsv1.com" 35 | } 36 | 37 | export default { 38 | isFile, 39 | isFunction, 40 | isString, 41 | noop, 42 | delay, 43 | getUnix, 44 | isTest: process.env.NODE_ENV === "test", 45 | isDev: process.env.NODE_ENV === "development", 46 | CLIENT_ERROR_CODE 47 | }; 48 | -------------------------------------------------------------------------------- /src/vod_reporter.ts: -------------------------------------------------------------------------------- 1 | import Uploader, { vodAxios } from "./uploader"; 2 | import * as pkg from "../package.json"; 3 | import util from "./util"; 4 | 5 | // eslint-disable-next-line @typescript-eslint/no-empty-interface 6 | interface IVodReporter {} 7 | 8 | export enum VodReportEvent { 9 | report_apply = "report_apply", 10 | report_cos_upload = "report_cos_upload", 11 | report_commit = "report_commit", 12 | report_done = "report_done" 13 | } 14 | 15 | enum ReqType { 16 | apply = 10001, 17 | cos_upload = 20001, 18 | commit = 10002, 19 | done = 40001 20 | } 21 | 22 | interface ReportObj { 23 | err: any; 24 | requestStartTime: Date; 25 | data: any; 26 | } 27 | 28 | export class VodReporter { 29 | uploader: Uploader; 30 | options: IVodReporter; 31 | 32 | // only partial data when created 33 | baseReportData: any = { 34 | version: pkg.version, 35 | platform: 3000, 36 | device: navigator.userAgent 37 | }; 38 | 39 | reportUrl = "https://vodreport.qcloud.com/ugcupload_new"; 40 | 41 | constructor(uploader: Uploader, options?: IVodReporter) { 42 | this.uploader = uploader; 43 | this.options = options; 44 | 45 | this.init(); 46 | } 47 | 48 | init() { 49 | this.uploader.on(VodReportEvent.report_apply, this.onApply.bind(this)); 50 | this.uploader.on( 51 | VodReportEvent.report_cos_upload, 52 | this.onCosUpload.bind(this) 53 | ); 54 | this.uploader.on(VodReportEvent.report_commit, this.onCommit.bind(this)); 55 | this.uploader.on(VodReportEvent.report_done, this.onDone.bind(this)); 56 | } 57 | 58 | // ApplyUploadUGC 59 | onApply(reportObj: ReportObj) { 60 | try { 61 | const uploader = this.uploader; 62 | if (!uploader.videoFile) { 63 | return; 64 | } 65 | Object.assign(this.baseReportData, { 66 | appId: uploader.appId, 67 | fileSize: uploader.videoFile.size, 68 | fileName: uploader.videoFile.name, 69 | fileType: uploader.videoFile.type, 70 | vodSessionKey: uploader.vodSessionKey, 71 | reqKey: uploader.reqKey, 72 | reportId: uploader.reportId 73 | }); 74 | 75 | const customReportData = { 76 | reqType: ReqType.apply, 77 | errCode: 0, 78 | vodErrCode: 0, 79 | errMsg: "", 80 | reqTimeCost: Number(new Date()) - Number(reportObj.requestStartTime), 81 | reqTime: Number(reportObj.requestStartTime) 82 | }; 83 | if (reportObj.err) { 84 | customReportData.errCode = 1; 85 | customReportData.vodErrCode = reportObj.err.code; 86 | customReportData.errMsg = reportObj.err.message; 87 | } 88 | if (reportObj.data) { 89 | this.baseReportData.cosRegion = reportObj.data.storageRegionV5; 90 | } 91 | this.report(customReportData); 92 | } catch (e) { 93 | console.error(`onApply`, e); 94 | if (util.isTest) { 95 | throw e; 96 | } 97 | } 98 | } 99 | 100 | // upload to cos 101 | onCosUpload(reportObj: ReportObj) { 102 | try { 103 | const customReportData = { 104 | reqType: ReqType.cos_upload, 105 | errCode: 0, 106 | cosErrCode: "", 107 | errMsg: "", 108 | reqTimeCost: Number(new Date()) - Number(reportObj.requestStartTime), 109 | reqTime: Number(reportObj.requestStartTime) 110 | }; 111 | if (reportObj.err) { 112 | customReportData.errCode = 1; 113 | customReportData.cosErrCode = reportObj.err.error 114 | ? reportObj.err.error.Code 115 | : reportObj.err; 116 | if (reportObj.err && reportObj.err.error === "error") { 117 | customReportData.cosErrCode = "cors error"; 118 | } 119 | customReportData.errMsg = JSON.stringify(reportObj.err); 120 | } 121 | this.report(customReportData); 122 | } catch (e) { 123 | console.error(`onCosUpload`, e); 124 | if (util.isTest) { 125 | throw e; 126 | } 127 | } 128 | } 129 | 130 | // CommitUploadUGC 131 | onCommit(reportObj: ReportObj) { 132 | try { 133 | const customReportData = { 134 | reqType: ReqType.commit, 135 | errCode: 0, 136 | vodErrCode: 0, 137 | errMsg: "", 138 | reqTimeCost: Number(new Date()) - Number(reportObj.requestStartTime), 139 | reqTime: Number(reportObj.requestStartTime) 140 | }; 141 | if (reportObj.err) { 142 | customReportData.errCode = 1; 143 | customReportData.vodErrCode = reportObj.err.code; 144 | customReportData.errMsg = reportObj.err.message; 145 | } 146 | if (reportObj.data) { 147 | this.baseReportData.fileId = reportObj.data.fileId; 148 | } 149 | this.report(customReportData); 150 | } catch (e) { 151 | console.error(`onCommit`, e); 152 | if (util.isTest) { 153 | throw e; 154 | } 155 | } 156 | } 157 | 158 | onDone(reportObj: ReportObj) { 159 | try { 160 | const customReportData = { 161 | reqType: ReqType.done, 162 | errCode: reportObj.err && reportObj.err.code, 163 | reqTimeCost: Number(new Date()) - Number(reportObj.requestStartTime), 164 | reqTime: Number(reportObj.requestStartTime) 165 | }; 166 | this.report(customReportData); 167 | } catch (e) { 168 | console.error(`onDone`, e); 169 | if (util.isTest) { 170 | throw e; 171 | } 172 | } 173 | } 174 | 175 | report(reportData: any) { 176 | reportData = { ...this.baseReportData, ...reportData }; 177 | this.send(reportData); 178 | } 179 | 180 | send(reportData: any) { 181 | if (util.isDev || util.isTest) { 182 | console.log(`send reportData`, reportData); 183 | return; 184 | } 185 | vodAxios.post(this.reportUrl, reportData); 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /test/env.ts: -------------------------------------------------------------------------------- 1 | import util from "../src/util"; 2 | 3 | // though implement `toString`, `Object.prototype.toString.call` would not use it. 4 | util.isFile = function(v: any) { 5 | return String(v) === "[object File]"; 6 | }; 7 | -------------------------------------------------------------------------------- /test/tc_vod.test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "assert"; 2 | import TcVod from "../src/tc_vod"; 3 | import Uploader from "../src/uploader"; 4 | import * as mm from "mm"; 5 | 6 | describe("tc_vod.test.ts", () => { 7 | afterEach(function() { 8 | mm.restore(); 9 | }); 10 | 11 | describe("#upload", () => { 12 | it("should return a Promise", async () => { 13 | const tcVod = new TcVod({ 14 | getSignature: (() => {}) as any 15 | }); 16 | mm(Uploader.prototype, "_start", () => { 17 | return Promise.resolve(1); 18 | }); 19 | const uploader = tcVod.upload({} as any); 20 | const doneResult = await uploader.done(); 21 | assert(doneResult === 1); 22 | }); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /test/uploader.test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "assert"; 2 | import Uploader, { UploaderEvent } from "../src/uploader"; 3 | import util from "../src/util"; 4 | import * as mm from "mm"; 5 | import { vodAxios as axios } from "../src/uploader"; 6 | const COS = require("cos-js-sdk-v5"); 7 | 8 | const fakeGetSignature = async () => { 9 | return "fakeGetSignature"; 10 | }; 11 | const fakeMediaFile: File = ({ 12 | lastModified: null, 13 | name: "vv.dd.mp4", 14 | size: 100, 15 | type: "video/mp4", 16 | slice: null, 17 | toString() { 18 | return "[object File]"; 19 | } 20 | } as any) as File; 21 | describe("uploader.test.ts", () => { 22 | afterEach(function() { 23 | mm.restore(); 24 | }); 25 | describe("#new", function() { 26 | it("should accept getSignature and mediaFile", () => { 27 | const uploader = new Uploader({ 28 | getSignature: fakeGetSignature, 29 | mediaFile: fakeMediaFile 30 | }); 31 | }); 32 | 33 | it("should fail when init params wrong", () => { 34 | // without signature 35 | assert.throws(() => { 36 | const uploader = new Uploader({ 37 | getSignature: null 38 | }); 39 | }, /getSignature must be a function/); 40 | }); 41 | }); 42 | 43 | describe("#genFileInfo", () => { 44 | it("should gen file info", () => { 45 | const uploader = new Uploader({ 46 | getSignature: fakeGetSignature, 47 | mediaFile: fakeMediaFile 48 | }); 49 | assert(uploader.videoInfo.name == "vv.dd"); 50 | assert(uploader.videoInfo.type == "mp4"); 51 | assert(uploader.videoInfo.size == 100); 52 | }); 53 | 54 | it("should use `mediaName` param", () => { 55 | const uploader = new Uploader({ 56 | getSignature: fakeGetSignature, 57 | mediaFile: fakeMediaFile, 58 | mediaName: "custom_video_name" 59 | }); 60 | assert(uploader.videoName == "custom_video_name"); 61 | }); 62 | 63 | it("should throw when invalid `mediaName`", () => { 64 | assert.throws(() => { 65 | const uploader = new Uploader({ 66 | getSignature: fakeGetSignature, 67 | mediaFile: fakeMediaFile, 68 | mediaName: 11 as any 69 | }); 70 | }, /mediaName must be a string/); 71 | 72 | assert.throws(() => { 73 | const uploader = new Uploader({ 74 | getSignature: fakeGetSignature, 75 | mediaFile: fakeMediaFile, 76 | mediaName: "*" 77 | }); 78 | }, /Cant use these chars in filename/); 79 | }); 80 | }); 81 | 82 | describe("#start", () => { 83 | it("should start", async function() { 84 | // 各函数需要被调用的次数 85 | const shouldCalled: any = { 86 | getSignature: 2, 87 | applyUploadUGC: 1, 88 | cosSuccess: 1, 89 | cosCoverSuccess: 1 90 | }; 91 | 92 | const uploader = new Uploader({ 93 | getSignature: () => { 94 | shouldCalled.getSignature--; 95 | return fakeGetSignature(); 96 | }, 97 | mediaFile: fakeMediaFile, 98 | coverFile: fakeMediaFile 99 | }); 100 | uploader.on(UploaderEvent.video_upload, () => { 101 | shouldCalled.cosSuccess--; 102 | }); 103 | uploader.on(UploaderEvent.cover_upload, () => { 104 | shouldCalled.cosCoverSuccess--; 105 | }); 106 | 107 | // 拦截 applyUploadUGC 中的post请求 108 | const old_applyUploadUGC = uploader.applyUploadUGC; 109 | mm(uploader, "applyUploadUGC", function() { 110 | shouldCalled.applyUploadUGC--; 111 | mm(axios, "post", () => { 112 | return { 113 | data: { 114 | code: 0, 115 | message: "成功", 116 | data: { 117 | video: { 118 | storageSignature: 119 | "bWcRMHMhwcaYQykMVNx0izvERAdhPTEwMDIyODUzJmI9ZmFlZWQ0NTZ2b2RjcTE0MDAxNzAwMzQmaz1BS0lESVdlN0F0STEwUFFrbThSRURsNFVPN0k2bXluNk5ERjcmZT0xNTQ3ODkyMTc1JnQ9MTU0NzcxOTM3NSZyPTI2NzAxNTYxODAmZj0vMTAwMjI4NTMvZmFlZWQ0NTZ2b2RjcTE0MDAxNzAwMzQvZmFlZWQ0NTZ2b2RjcTE0MDAxNzAwMzQvN2Y1MWQ5NmI1Mjg1ODkwNzg0NDE4NjA1MTIzL3gyZVdGWFBmdE1FQS5tcDQ=", 120 | storagePath: 121 | "/faeed456vodcq1400170034/7f51d96b5285890784418605123/x2eWFXPftMEA.mp4" 122 | }, 123 | cover: { 124 | storageSignature: 125 | "VgnnV+kwG8p7LvME+Thc2aaiIO5hPTEwMDIyODUzJmI9ZmFlZWQ0NTZ2b2RjcTE0MDAxNzAwMzQmaz1BS0lESVdlN0F0STEwUFFrbThSRURsNFVPN0k2bXluNk5ERjcmZT0xNTQ3ODkyMTc1JnQ9MTU0NzcxOTM3NSZyPTE0NzAzNDkzNjkmZj0vMTAwMjI4NTMvZmFlZWQ0NTZ2b2RjcTE0MDAxNzAwMzQvZmFlZWQ0NTZ2b2RjcTE0MDAxNzAwMzQvN2Y1MWQ5NmI1Mjg1ODkwNzg0NDE4NjA1MTIzLzUyODU4OTA3ODQ0MTg2MDUxMjQucG5n", 126 | storagePath: 127 | "/faeed456vodcq1400170034/7f51d96b5285890784418605123/5285890784418605124.png" 128 | }, 129 | storageAppId: 10022853, 130 | storageBucket: "faeed456vodcq1400170034", 131 | storageRegion: "cq", 132 | storageRegionV5: "ap-chongqing", 133 | domain: "vod2.qcloud.com", 134 | vodSessionKey: 135 | "3FEmq9DWHl1xF819mM6j0fyDQDSON8VtwN/RTrl/9m49AEEWV9jV5txk3AckBj1lkl3EkTMVx1ah/wOuKPElzHG4gHwRVPpge+UZ7sdmqvmQJ9bhLhiAZDTDDMVlSW6j2a4bIIUDI7erahcpKs/oh0koMLcoErCFBr+nHhFKT2qfIyc8b+izX5GBFIkXuLzDGMjTnYuQAomqeHYanuP5dns0+7CGkK26M4d9n0VU7DEQ4GEweORsIQCqsQT6+0DLnZ9qFO4mBeVOb6Ro5qGIi3uhq2AFmMQmrMXZfdSvcdnoLYPmDxvM8inrhri0CHBIq6UyJqsKmsYU0ufHkc2S3feaVRHxM9ahqRK2iNXtOche9wH2dFJ+eX7OQ/Vek75rKlpCUE68peHv9KeNf5/7y9buUXeguBYC74pU+oJ2ffu/INgkFkP9tdw4EgkkRyqsQPIWbuFrXXy5fyPzCoMgDnH0QgzUsHGgKTkk4UjvLdXZR21soD6pxcdXj1n/VHyljouY8uMz5nvg3KTAL9ze73+MJvuD4LUIMahagY5MhiHbMCGHXOVG6BDq6WQr9PjkOxH3PteeekyWvxdaxc/icVvQO+2ytIX2/P/V91eBs9qmxpYvD2Mnf1HkeANPCRBIpvLhBRHRGPKN9tVg1CFcBRr6Ruxr5rs2E0eDPhy6s/5RwRcf2CcfnOTKC+jamY/qhgc1Kl3sLh7JkhM3Onwxi+i5OIdaOvdS2NYst6Xn0tQFDT+vOK3n45k4HTYK6LdaJZAqjmtBHNBeohp/JM/7ZiAcNoCsR4FPaTH9Y1GAGYZHC0nw/E932tYrg26QsyFeAOWhW6xkdk+TwMtV92bQu8DmvxojyQZlDJgWjJPa+1B9B9C7OtbYeUZ5+SBC+Z23YYplFkKyBEWC4j+x52R+g463In9DFsZdRb2NCmBGBC8CARgQypTZ4Q==", 136 | // 测试参数tempCertificate需要从demo中的ApplyUploadUGC接口获取 137 | // demo地址https://tencentyun.github.io/vod-js-sdk-v6/ 138 | tempCertificate: { 139 | secretId: "", 140 | secretKey: "", 141 | token: "", 142 | expiredTime: 1547719375, 143 | }, 144 | appId: 1400170034, 145 | timestamp: 1547719375, 146 | StorageRegionV5: "ap-chongqing" 147 | } 148 | } 149 | }; 150 | }); 151 | return old_applyUploadUGC.apply(uploader, arguments); 152 | }); 153 | 154 | // 拦截 uploadToCos 中的上传请求 155 | const old_uploadToCos = uploader.uploadToCos; 156 | mm(uploader, "uploadToCos", function() { 157 | mm(COS.prototype, "sliceUploadFile", function( 158 | sliceParams: any, 159 | callback: Function 160 | ) { 161 | // { 162 | // Bucket: uploadCosParam.bucket, 163 | // Region: uploadCosParam.region, 164 | // Key: uploadCosParam.key, 165 | // Body: uploadCosParam.file, 166 | // onTaskReady: uploadCosParam.onTaskReady, 167 | // onProgress: uploadCosParam.onProgress, 168 | // } 169 | assert(util.isString(sliceParams.Bucket)); 170 | assert(util.isString(sliceParams.Region)); 171 | assert(util.isString(sliceParams.Key)); 172 | assert(util.isFile(sliceParams.Body)); 173 | assert(util.isFunction(sliceParams.onTaskReady)); 174 | assert(util.isFunction(sliceParams.onProgress)); 175 | callback(); 176 | }); 177 | return old_uploadToCos.apply(uploader, arguments); 178 | }); 179 | 180 | // 拦截 commitUploadUGC 中的post请求 181 | const old_commitUploadUGC = uploader.commitUploadUGC; 182 | mm(uploader, "commitUploadUGC", function() { 183 | mm(axios, "post", () => { 184 | return { 185 | data: { 186 | code: 0, 187 | message: "成功", 188 | data: { 189 | video: { 190 | url: 191 | "http://1400170034.vod2.myqcloud.com/faeed456vodcq1400170034/7f51d96b5285890784418605123/x2eWFXPftMEA.mp4", 192 | verify_content: 193 | "EmqHd4LLop2dL692wAoVl5FshQVFeHBUaW1lPTE1NDc3MjI5ODYmRmlsZUlkPTUyODU4OTA3ODQ0MTg2MDUxMjM=" 194 | }, 195 | cover: { 196 | url: 197 | "http://1400170034.vod2.myqcloud.com/faeed456vodcq1400170034/7f51d96b5285890784418605123/5285890784418605124.png", 198 | verify_content: 199 | "WIqkMGzIaOl8try34aPB+JklA4xFeHBUaW1lPTE1NDc3MjI5ODYmRmlsZUlkPTUyODU4OTA3ODQ0MTg2MDUxMjQ=" 200 | }, 201 | fileId: "5285890784418605123" 202 | } 203 | } 204 | }; 205 | }); 206 | return old_commitUploadUGC.call(uploader, arguments); 207 | }); 208 | 209 | uploader.start(); 210 | const doneResult = await uploader.done(); 211 | 212 | // 开始校验各部分的工作情况 213 | assert(util.isString(doneResult.video.url)); 214 | 215 | for (const key in shouldCalled) { 216 | assert(shouldCalled[key] == 0); 217 | } 218 | }); 219 | }); 220 | 221 | describe("#applyUploadUGC", () => { 222 | it("should retry", async () => { 223 | const uploader = new Uploader({ 224 | getSignature: fakeGetSignature, 225 | mediaFile: fakeMediaFile 226 | }); 227 | uploader.retryDelay = 100; // dont wait too long 228 | 229 | let applyUploadUGCCalled = 0; 230 | 231 | mm(axios, "post", () => { 232 | applyUploadUGCCalled++; 233 | throw new Error("fake post error"); 234 | }); 235 | await assert.rejects(async () => { 236 | await uploader.applyUploadUGC(); 237 | }, /fake post error/); 238 | assert(applyUploadUGCCalled == 4); 239 | }); 240 | }); 241 | }); 242 | -------------------------------------------------------------------------------- /test/vod_reporter.test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "assert"; 2 | import { VodReporter, VodReportEvent } from "../src/vod_reporter"; 3 | import Uploader from "../src/uploader"; 4 | import * as semver from "semver"; 5 | 6 | const device = window.navigator.userAgent; 7 | 8 | function genUploader() { 9 | const fakeGetSignature = async () => { 10 | return "fakeGetSignature"; 11 | }; 12 | const fakeMediaFile: File = ({ 13 | lastModified: null, 14 | name: "vv.dd.mp4", 15 | size: 100, 16 | type: "video/mp4", 17 | slice: null, 18 | toString() { 19 | return "[object File]"; 20 | } 21 | } as any) as File; 22 | const uploader = new Uploader({ 23 | getSignature: fakeGetSignature, 24 | mediaFile: fakeMediaFile, 25 | reportId: "12345" 26 | }); 27 | return uploader; 28 | } 29 | 30 | function checkAndDelCommonProperty(reportData: any, requestStartTime: Date) { 31 | assert.strictEqual(reportData.reqTime, Number(requestStartTime)); 32 | delete reportData.reqTime; 33 | assert.ok(reportData.reqTimeCost < 600); 34 | delete reportData.reqTimeCost; 35 | assert.ok(semver.valid(reportData.version)); 36 | delete reportData.version; 37 | 38 | assert.ok(/\w+-\w+-\w+-\w+-\w+/.test(reportData.reqKey)); 39 | delete reportData.reqKey; 40 | } 41 | 42 | describe("vod_reporter.test.ts", () => { 43 | const uploader = genUploader(); 44 | const vodReporter = new VodReporter(uploader); 45 | describe("apply", () => { 46 | it("should report apply", () => { 47 | const requestStartTime = new Date(Number(new Date()) - 500); 48 | 49 | vodReporter.send = reportData => { 50 | checkAndDelCommonProperty(reportData, requestStartTime); 51 | 52 | assert.deepStrictEqual(reportData, { 53 | platform: 3000, 54 | reportId: "12345", 55 | device: 56 | device || 57 | "Mozilla/5.0 (darwin) AppleWebKit/537.36 (KHTML, like Gecko) jsdom/13.2.0", 58 | errCode: 0, 59 | appId: 0, 60 | fileSize: 100, 61 | fileName: "vv.dd.mp4", 62 | fileType: "video/mp4", 63 | vodSessionKey: "", 64 | cosRegion: "ap-chongqing", 65 | reqType: 10001, 66 | vodErrCode: 0, 67 | errMsg: "" 68 | }); 69 | }; 70 | uploader.emit(VodReportEvent.report_apply, { 71 | data: { 72 | storageRegionV5: "ap-chongqing" 73 | }, 74 | requestStartTime: requestStartTime 75 | }); 76 | }); 77 | 78 | it("should report apply error", () => { 79 | const uploader = genUploader(); 80 | const vodReporter = new VodReporter(uploader); 81 | const requestStartTime = new Date(Number(new Date()) - 500); 82 | 83 | vodReporter.send = reportData => { 84 | checkAndDelCommonProperty(reportData, requestStartTime); 85 | 86 | assert.deepStrictEqual(reportData, { 87 | platform: 3000, 88 | reportId: "12345", 89 | device: 90 | device || 91 | "Mozilla/5.0 (darwin) AppleWebKit/537.36 (KHTML, like Gecko) jsdom/13.2.0", 92 | errCode: 1, 93 | appId: 0, 94 | fileSize: 100, 95 | fileName: "vv.dd.mp4", 96 | fileType: "video/mp4", 97 | vodSessionKey: "", 98 | reqType: 10001, 99 | vodErrCode: 10004, 100 | errMsg: "ugc upload | invalid signature" 101 | }); 102 | }; 103 | uploader.emit(VodReportEvent.report_apply, { 104 | err: { code: 10004, message: "ugc upload | invalid signature" }, 105 | requestStartTime: requestStartTime 106 | }); 107 | }); 108 | }); 109 | 110 | describe("cos upload", () => { 111 | it("should report cos upload", () => { 112 | const requestStartTime = new Date(Number(new Date()) - 500); 113 | 114 | vodReporter.send = reportData => { 115 | checkAndDelCommonProperty(reportData, requestStartTime); 116 | 117 | assert.deepStrictEqual(reportData, { 118 | platform: 3000, 119 | reportId: "12345", 120 | device: 121 | device || 122 | "Mozilla/5.0 (darwin) AppleWebKit/537.36 (KHTML, like Gecko) jsdom/13.2.0", 123 | errCode: 0, 124 | appId: 0, 125 | fileSize: 100, 126 | fileName: "vv.dd.mp4", 127 | fileType: "video/mp4", 128 | vodSessionKey: "", 129 | cosRegion: "ap-chongqing", 130 | reqType: 20001, 131 | cosErrCode: "", 132 | errMsg: "" 133 | }); 134 | }; 135 | uploader.emit(VodReportEvent.report_cos_upload, { 136 | err: null, 137 | requestStartTime: requestStartTime 138 | }); 139 | }); 140 | }); 141 | 142 | describe("commit", () => { 143 | it("should report commit", () => { 144 | const requestStartTime = new Date(Number(new Date()) - 500); 145 | 146 | vodReporter.send = reportData => { 147 | checkAndDelCommonProperty(reportData, requestStartTime); 148 | 149 | assert.deepStrictEqual(reportData, { 150 | platform: 3000, 151 | reportId: "12345", 152 | device: 153 | device || 154 | "Mozilla/5.0 (darwin) AppleWebKit/537.36 (KHTML, like Gecko) jsdom/13.2.0", 155 | errCode: 0, 156 | appId: 0, 157 | fileSize: 100, 158 | fileName: "vv.dd.mp4", 159 | fileType: "video/mp4", 160 | vodSessionKey: "", 161 | cosRegion: "ap-chongqing", 162 | reqType: 10002, 163 | vodErrCode: 0, 164 | errMsg: "", 165 | fileId: "20190606" 166 | }); 167 | }; 168 | uploader.emit(VodReportEvent.report_commit, { 169 | data: { 170 | fileId: "20190606" 171 | }, 172 | requestStartTime: requestStartTime 173 | }); 174 | }); 175 | 176 | it("should report commit error", () => { 177 | const requestStartTime = new Date(Number(new Date()) - 500); 178 | 179 | vodReporter.send = reportData => { 180 | checkAndDelCommonProperty(reportData, requestStartTime); 181 | 182 | assert.deepStrictEqual(reportData, { 183 | platform: 3000, 184 | reportId: "12345", 185 | device: 186 | device || 187 | "Mozilla/5.0 (darwin) AppleWebKit/537.36 (KHTML, like Gecko) jsdom/13.2.0", 188 | errCode: 1, 189 | appId: 0, 190 | fileSize: 100, 191 | fileName: "vv.dd.mp4", 192 | fileType: "video/mp4", 193 | vodSessionKey: "", 194 | reqType: 10002, 195 | vodErrCode: 10004, 196 | errMsg: "ugc upload | invalid signature", 197 | fileId: "20190606", 198 | cosRegion: "ap-chongqing" 199 | }); 200 | }; 201 | uploader.emit(VodReportEvent.report_commit, { 202 | err: { code: 10004, message: "ugc upload | invalid signature" }, 203 | requestStartTime: requestStartTime 204 | }); 205 | }); 206 | }); 207 | }); 208 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "lib", 4 | "noImplicitAny": true, 5 | "noImplicitThis": true, 6 | "target": "es5", 7 | "sourceMap": true, 8 | "declaration": true, 9 | "resolveJsonModule": true, 10 | "lib": [ 11 | "dom", 12 | "es2015", 13 | "scripthost", 14 | "es2015.promise" 15 | ] 16 | }, 17 | "include": ["src"] 18 | } -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | 3 | module.exports = { 4 | mode: "production", 5 | devtool: "source-map", 6 | entry: "./src/tc_vod.ts", 7 | module: { 8 | rules: [ 9 | { 10 | test: /\.ts$/, 11 | use: "ts-loader", 12 | exclude: /node_modules/ 13 | } 14 | ] 15 | }, 16 | resolve: { 17 | extensions: [".ts", ".js"] 18 | }, 19 | optimization: { 20 | // 这个选项开启后会影响 ts sourcemap 的生成,调试了半天也不懂是为什么。 21 | occurrenceOrder: false 22 | }, 23 | output: { 24 | filename: "vod-js-sdk-v6.js", 25 | path: path.resolve(__dirname, "dist"), 26 | library: "TcVod", 27 | libraryTarget: "umd" 28 | }, 29 | plugins: [] 30 | }; 31 | -------------------------------------------------------------------------------- /webpack.dev.config.js: -------------------------------------------------------------------------------- 1 | const webpackProdConfig = require("./webpack.config"); 2 | 3 | const webpackDevConfig = { ...webpackProdConfig, mode: "development" }; 4 | 5 | module.exports = webpackDevConfig; 6 | --------------------------------------------------------------------------------