├── .gitattributes ├── packages ├── client │ ├── .eslintignore │ ├── src │ │ ├── icons │ │ │ ├── icon16.png │ │ │ ├── icon48.png │ │ │ └── icon128.png │ │ ├── manifest.json │ │ ├── danmu │ │ │ ├── dev │ │ │ │ └── index.js │ │ │ ├── index.js │ │ │ └── index.js.map │ │ ├── share │ │ │ ├── constant.js │ │ │ └── index.js │ │ ├── popup │ │ │ ├── index.css │ │ │ ├── index.html │ │ │ ├── index.css.map │ │ │ └── dev │ │ │ │ ├── index.scss │ │ │ │ └── index.js │ │ ├── active │ │ │ ├── index.css │ │ │ ├── dev │ │ │ │ ├── index.scss │ │ │ │ ├── index.js │ │ │ │ └── DanmakuWebSocket.js │ │ │ └── index.css.map │ │ └── background │ │ │ └── dev │ │ │ └── index.js │ ├── dist │ │ ├── bilibili-live-hime-client.crx │ │ ├── bilibili-live-hime-client.zip │ │ ├── bilibili-live-hime-client │ │ │ ├── icons │ │ │ │ ├── donate.png │ │ │ │ ├── icon128.png │ │ │ │ ├── icon16.png │ │ │ │ └── icon48.png │ │ │ ├── manifest.json │ │ │ ├── danmu │ │ │ │ └── index.js │ │ │ ├── injected │ │ │ │ └── index.js │ │ │ ├── popup │ │ │ │ ├── index.css │ │ │ │ ├── index.html │ │ │ │ └── index.js │ │ │ ├── content │ │ │ │ ├── index.css │ │ │ │ └── index.js │ │ │ └── active │ │ │ │ ├── index.css │ │ │ │ └── index.js │ │ └── bilibili-live-hime-client.pem │ ├── archive.js │ ├── .eslintrc │ ├── package.json │ └── rollup.config.js └── server │ ├── package.json │ ├── FFmpeg.js │ └── index.js ├── images ├── .DS_Store ├── logo.png ├── qqgroup.png ├── tryitnow.png ├── screenshot.png └── wechatpay.jpg ├── .prettierrc ├── .editorconfig ├── LICENSE ├── .gitignore └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | *.js text eol=lf -------------------------------------------------------------------------------- /packages/client/.eslintignore: -------------------------------------------------------------------------------- 1 | /dist 2 | /node_modules 3 | /rollup-config.js 4 | /archive.js -------------------------------------------------------------------------------- /images/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhw2590582/bilibili-live-hime/HEAD/images/.DS_Store -------------------------------------------------------------------------------- /images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhw2590582/bilibili-live-hime/HEAD/images/logo.png -------------------------------------------------------------------------------- /images/qqgroup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhw2590582/bilibili-live-hime/HEAD/images/qqgroup.png -------------------------------------------------------------------------------- /images/tryitnow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhw2590582/bilibili-live-hime/HEAD/images/tryitnow.png -------------------------------------------------------------------------------- /images/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhw2590582/bilibili-live-hime/HEAD/images/screenshot.png -------------------------------------------------------------------------------- /images/wechatpay.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhw2590582/bilibili-live-hime/HEAD/images/wechatpay.jpg -------------------------------------------------------------------------------- /packages/client/src/icons/icon16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhw2590582/bilibili-live-hime/HEAD/packages/client/src/icons/icon16.png -------------------------------------------------------------------------------- /packages/client/src/icons/icon48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhw2590582/bilibili-live-hime/HEAD/packages/client/src/icons/icon48.png -------------------------------------------------------------------------------- /packages/client/src/icons/icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhw2590582/bilibili-live-hime/HEAD/packages/client/src/icons/icon128.png -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "useTabs": false, 3 | "tabWidth": 4, 4 | "singleQuote": true, 5 | "trailingComma": "all", 6 | "printWidth": 120 7 | } 8 | -------------------------------------------------------------------------------- /packages/client/dist/bilibili-live-hime-client.crx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhw2590582/bilibili-live-hime/HEAD/packages/client/dist/bilibili-live-hime-client.crx -------------------------------------------------------------------------------- /packages/client/dist/bilibili-live-hime-client.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhw2590582/bilibili-live-hime/HEAD/packages/client/dist/bilibili-live-hime-client.zip -------------------------------------------------------------------------------- /packages/client/dist/bilibili-live-hime-client/icons/donate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhw2590582/bilibili-live-hime/HEAD/packages/client/dist/bilibili-live-hime-client/icons/donate.png -------------------------------------------------------------------------------- /packages/client/dist/bilibili-live-hime-client/icons/icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhw2590582/bilibili-live-hime/HEAD/packages/client/dist/bilibili-live-hime-client/icons/icon128.png -------------------------------------------------------------------------------- /packages/client/dist/bilibili-live-hime-client/icons/icon16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhw2590582/bilibili-live-hime/HEAD/packages/client/dist/bilibili-live-hime-client/icons/icon16.png -------------------------------------------------------------------------------- /packages/client/dist/bilibili-live-hime-client/icons/icon48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhw2590582/bilibili-live-hime/HEAD/packages/client/dist/bilibili-live-hime-client/icons/icon48.png -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # See editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | end_of_line = lf 7 | indent_size = 4 8 | indent_style = space 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true -------------------------------------------------------------------------------- /packages/server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bilibili-live-hime-server", 3 | "version": "1.1.1", 4 | "description": "哔哩哔哩直播姬 Chrome 扩展服务端", 5 | "main": "index.js", 6 | "homepage": "https://github.com/zhw2590582/bilibili-live-hime", 7 | "scripts": { 8 | "start": "npx nodemon ./index.js" 9 | }, 10 | "dependencies": { 11 | "@ffmpeg-installer/ffmpeg": "^1.0.20", 12 | "nodemon": "^2.0.2", 13 | "socket.io": "^2.3.0" 14 | } 15 | } -------------------------------------------------------------------------------- /packages/client/archive.js: -------------------------------------------------------------------------------- 1 | const crx = require('puppeteer-crx'); 2 | const path = require('path'); 3 | const { name } = require('./package.json'); 4 | 5 | crx(path.resolve(__dirname, `./dist/${name}`), { 6 | zip: true, 7 | delay: 1000, 8 | }).then(result => { 9 | Object.keys(result) 10 | .filter(key => result[key]) 11 | .forEach(key => { 12 | console.log(`Packaged ${key} successfully in ${result[key]}`); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /packages/client/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "extends": [ 4 | "airbnb-base", 5 | "prettier" 6 | ], 7 | "env": { 8 | "browser": true, 9 | "es6": true 10 | }, 11 | "globals": { 12 | "chrome": true 13 | }, 14 | "rules": { 15 | "camelcase": 0, 16 | "no-console": 0, 17 | "no-param-reassign": 0, 18 | "prefer-destructuring": 0, 19 | "class-methods-use-this": 0 20 | }, 21 | "parserOptions": { 22 | "sourceType": "module" 23 | } 24 | } -------------------------------------------------------------------------------- /packages/client/dist/bilibili-live-hime-client/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Bilibili 直播姬", 3 | "version": "1.0.3", 4 | "description": "把浏览器页面直播到 Bilibili 直播间", 5 | "manifest_version": 2, 6 | "background": { 7 | "scripts": ["background/index.js"] 8 | }, 9 | "icons": { 10 | "16": "icons/icon16.png", 11 | "48": "icons/icon48.png", 12 | "128": "icons/icon128.png" 13 | }, 14 | "browser_action": { 15 | "default_popup": "popup/index.html" 16 | }, 17 | "permissions": ["tabs", "storage", "activeTab", "tabCapture", "*://*.bilibili.com/*"], 18 | "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'" 19 | } 20 | -------------------------------------------------------------------------------- /packages/client/src/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Bilibili 直播姬", 3 | "version": "1.0.3", 4 | "description": "把浏览器页面直播到 Bilibili 直播间", 5 | "manifest_version": 2, 6 | "background": { 7 | "scripts": [ 8 | "background/index.js" 9 | ] 10 | }, 11 | "icons": { 12 | "16": "icons/icon16.png", 13 | "48": "icons/icon48.png", 14 | "128": "icons/icon128.png" 15 | }, 16 | "browser_action": { 17 | "default_popup": "popup/index.html" 18 | }, 19 | "permissions": [ 20 | "tabs", 21 | "storage", 22 | "activeTab", 23 | "tabCapture", 24 | "*://*.bilibili.com/*" 25 | ], 26 | "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'" 27 | } -------------------------------------------------------------------------------- /packages/client/dist/bilibili-live-hime-client/danmu/index.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * bilibili-live-hime v1.0.3 3 | * Github: https://github.com/zhw2590582/bilibili-live-hime 4 | * (c) 2018-2019 Harvey Zack 5 | * Released under the MIT License. 6 | */ 7 | 8 | var BilibiliLiveHimeDanmu=function(){"use strict";var e=function(e,n){if(!(e instanceof n))throw new TypeError("Cannot call a class as a function")},n=/^function\s*([\w$]*)\s*\(([\w\s,$]*)\)\s*\{([\w\W\s\S]*)\}$/;function t(){var e=Date.now();!function n(){if(window.DanmakuWebSocket){var t=window.DanmakuWebSocket;window.DanmakuWebSocket=function(e){var n=new t(e);return window.postMessage({type:"danmu_option",data:Object.keys(e).reduce((function(n,t){return"function"!=typeof e[t]&&(n[t]=e[t]),n}),{})}),n}}else Date.now()-e>=6e4?window.postMessage({type:"danmu_error"}):setTimeout(n,10)}()}return new function o(){e(this,o);var a=document.createElement("script");a.type="text/javascript",a.text=t.toString().match(n)[3],document.documentElement.appendChild(a),a.onload=document.documentElement.removeChild(a),window.addEventListener("message",(function(e){chrome.runtime.sendMessage(e.data)}))}}(); 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Harvey Zack 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 | -------------------------------------------------------------------------------- /.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 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # next.js build output 61 | .next 62 | 63 | .DS_Store 64 | package-lock.json -------------------------------------------------------------------------------- /packages/client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bilibili-live-hime-client", 3 | "version": "1.0.3", 4 | "description": "哔哩哔哩直播姬 Chrome 扩展客户端", 5 | "homepage": "https://github.com/zhw2590582/bilibili-live-hime", 6 | "scripts": { 7 | "dev": "cross-env NODE_ENV=development rollup -c -m -w", 8 | "build": "cross-env NODE_ENV=production rollup -c && node ./archive.js", 9 | "test": "eslint src/**/dev/*.js" 10 | }, 11 | "devDependencies": { 12 | "@babel/core": "^7.16.0", 13 | "@babel/plugin-external-helpers": "^7.16.0", 14 | "@babel/plugin-transform-runtime": "^7.16.0", 15 | "@babel/preset-env": "^7.16.0", 16 | "@babel/runtime": "^7.16.3", 17 | "autoprefixer": "^10.4.0", 18 | "babel-eslint": "^10.1.0", 19 | "cross-env": "^7.0.3", 20 | "cssnano": "^5.0.10", 21 | "eslint": "^8.2.0", 22 | "eslint-config-airbnb-base": "^15.0.0", 23 | "eslint-config-prettier": "^8.3.0", 24 | "eslint-plugin-import": "^2.25.3", 25 | "node-sass": "^6.0.1", 26 | "rollup": "^2.60.0", 27 | "rollup-plugin-babel": "^4.4.0", 28 | "rollup-plugin-commonjs": "^10.1.0", 29 | "rollup-plugin-copy": "^3.4.0", 30 | "rollup-plugin-eslint": "^7.0.0", 31 | "rollup-plugin-node-resolve": "^5.2.0", 32 | "rollup-plugin-postcss": "^4.0.1", 33 | "rollup-plugin-replace": "^2.2.0", 34 | "rollup-plugin-terser": "^7.0.2" 35 | }, 36 | "dependencies": { 37 | "crx-hotreload": "^1.0.6", 38 | "obj-to-string": "^1.0.1", 39 | "puppeteer-crx": "^1.0.6", 40 | "socket.io-client": "^4.3.2" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/client/src/danmu/dev/index.js: -------------------------------------------------------------------------------- 1 | import { REG_FUNCTION } from '../../share/constant'; 2 | 3 | function getDanmuOption() { 4 | const initTime = Date.now(); 5 | (function loop() { 6 | if (window.DanmakuWebSocket) { 7 | const DWS = window.DanmakuWebSocket; 8 | window.DanmakuWebSocket = function f(option) { 9 | const dws = new DWS(option); 10 | window.postMessage({ 11 | type: 'danmu_option', 12 | data: Object.keys(option).reduce((obj, key) => { 13 | if (typeof option[key] !== 'function') { 14 | obj[key] = option[key]; 15 | } 16 | return obj; 17 | }, {}), 18 | }); 19 | return dws; 20 | }; 21 | } else if (Date.now() - initTime >= 60000) { 22 | window.postMessage({ 23 | type: 'danmu_error', 24 | }); 25 | } else { 26 | setTimeout(loop, 10); 27 | } 28 | })(); 29 | } 30 | 31 | class Danmu { 32 | constructor() { 33 | const $script = document.createElement('script'); 34 | $script.type = 'text/javascript'; 35 | $script.text = getDanmuOption.toString().match(REG_FUNCTION)[3]; 36 | document.documentElement.appendChild($script); 37 | $script.onload = document.documentElement.removeChild($script); 38 | 39 | window.addEventListener('message', event => { 40 | chrome.runtime.sendMessage(event.data); 41 | }); 42 | } 43 | } 44 | 45 | export default new Danmu(); 46 | -------------------------------------------------------------------------------- /packages/server/FFmpeg.js: -------------------------------------------------------------------------------- 1 | const ffmpegPath = require('@ffmpeg-installer/ffmpeg').path; 2 | const spawn = require('child_process').spawn; 3 | 4 | class FFmpeg { 5 | constructor(rtmp, socket) { 6 | this.socket = socket; 7 | this.ff = this.create(rtmp); 8 | this.ff.stdout.on('data', data => console.log(String(data))); 9 | this.ff.stderr.on('data', data => console.log(String(data))); 10 | FFmpeg.instances.set(socket, this); 11 | } 12 | 13 | static getInstance(socket) { 14 | return this.instances.get(socket); 15 | } 16 | 17 | static destroy() { 18 | [...this.instances.values()].forEach(item => item.destroy()); 19 | } 20 | 21 | create(rtmp) { 22 | return spawn(ffmpegPath, [ 23 | '-re', 24 | '-i', 25 | '-', 26 | '-vcodec', 27 | 'copy', 28 | '-acodec', 29 | 'aac', 30 | '-b:a', 31 | '192k', 32 | '-f', 33 | 'flv', 34 | rtmp, 35 | ]); 36 | } 37 | 38 | write(data) { 39 | if (this.ff) { 40 | this.ff.stdin.write(data); 41 | } 42 | } 43 | 44 | onClose(callback) { 45 | if (this.ff) { 46 | this.ff.on('close', callback); 47 | } 48 | } 49 | 50 | destroy() { 51 | if (this.ff) { 52 | try { 53 | this.ff.stdin.end(); 54 | this.ff.kill('SIGINT'); 55 | } catch (error) {} 56 | this.ff = null; 57 | } 58 | FFmpeg.instances.delete(this.socket); 59 | } 60 | } 61 | 62 | Object.defineProperty(FFmpeg, 'instances', { 63 | value: new Map(), 64 | }); 65 | 66 | module.exports = FFmpeg; 67 | -------------------------------------------------------------------------------- /packages/client/dist/bilibili-live-hime-client.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDHDFu2tjXLyGLq 3 | zd/+66QYL1JKVmajn0LbWq8j4gA8OxteY1KkRGOUewp51HJPZEQ/FesBps+yKAP/ 4 | 4yCHcVTEcL7d+1NSaBWLa4Y9qwr/jXQh2Da6tFIG9p6fpMnn0M3OBwFGwmpWN0w1 5 | sm7L3U6e+cuWm8MeXL4rcSV4tKKJ8KD92lYT3ypL/QBqou2gEWxiSf+MjTdPtdU9 6 | RNmJQac1G0sFAP5JQndBMkFKi046OqGfGAphQbXjiyICbAHgTsvcmp3uBHtJUmDs 7 | dGhglA44HQUxqDgPmn3QYBZ7Dn8DvmMbsOXLh2pYFr97Ic+0pcSEUSfeAq/Hatqa 8 | 9aCbbNW5AgMBAAECggEAB/tS4izEU3uhm9DNnqdMWTvsjNaSHxWP1MP6KfIyGDg2 9 | jL5m3fdwFtAoR2JhgxV+Nxll5xcNVRONtYEiGMxOneH3hWdCnBdYZdLV+zsUodLG 10 | SWUmLK52a5kQG068WfNhjVVyk6fQI8zr3WteTgZ91Jons85jMKB/b7aFziVQLz/I 11 | 0n/GpZOyZBPDpCo46ROugx8fQJcD8GNR+eRdQOZAPFegqMtcSWHs4uzxy+m9sL92 12 | 88lmtX9gIm6jM5Utd7JZWhpVzuvNP8E/8/CIBWkUoZwoln2T068kJpgYOE4Su7ug 13 | 2boiw7ME+lDpnCrSi6qK95scNXQAFp1Z2Yv8Wk520QKBgQDvMPt2ofZBx/3ueHKF 14 | 3CFdqtgkCFWALbKQnjhSVkBOY2HsicvRy/J2oBGAV8gfIoiwfZDyKAGbQY6hWy3g 15 | Z4tXb//95f6Gjqhg+ai2X4QNGAzshV/Vb9LR9Polxlr0HWxfMAf/rl79cMaOFDI7 16 | O/jRa0ma2q9hYgfDxIU3Oac8XwKBgQDVCTVDdAT3WCVIgQJSZySzQ/L9ca7ljy/Y 17 | 9bZ/rDwS0bzHQYmfXSVPQXxAR8BM2mhTyQrxXayoSguKLaKO4NFhSZDkfMLra7Yb 18 | f2mB41eAR1cwLv6EAtoAPfSq62spT695dnlQTz+9CpZWUEtF6MnqtK/lzF1Jdyu2 19 | s5Tai/gk5wKBgHu0E3lJTji7G+MHPANq037D6sDH5qBCAgW1Ll+2588iqvRQV68L 20 | ETy0kBiWTZN8osDUT/BNLHeFMiTVKSbTpkb0T6qaFka05PQyakTXpU24UIbKe8PA 21 | 74FSd0SuD0ANL40TJC/hmJLujmHQpCHqZEa6e/iwfN+aeXWP322uTYV1AoGAYA13 22 | y4MLHi7LGKa6UkcZIA66B5XzYqOt0ifvch9JMr8IW9rr30PTJ6oCCMWRKdWsBmL2 23 | IR+4UmSYUWQEEGHDZhcSUkMCa70z7bAD9UF42WjRIfN+ZC0Y99T3dP0SYxz/pEdL 24 | qW8h8O1OHXq0hb/REpcHXBhJ02788bKEoglMB+8CgYAtWa+B7sLWQc02YknntuwN 25 | 9XCwx5srbE8F8SRyg/PrfueofI3vCGoGZIVx1U7zVQGQYwytcBrqdMP2YxvYkN37 26 | tCHsXS/kfZwCQjUYU3lLKHkej8IGXiFw1Kz/KSQzJ84PXosZupfeBf9Jt2flu15c 27 | uIWZT6fjO4UNA8DYhkb+gg== 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /packages/client/src/danmu/index.js: -------------------------------------------------------------------------------- 1 | var BilibiliLiveHimeDanmu = (function () { 2 | 'use strict'; 3 | 4 | function _classCallCheck(instance, Constructor) { 5 | if (!(instance instanceof Constructor)) { 6 | throw new TypeError("Cannot call a class as a function"); 7 | } 8 | } 9 | 10 | var classCallCheck = _classCallCheck; 11 | 12 | var REG_FUNCTION = /^function\s*([\w$]*)\s*\(([\w\s,$]*)\)\s*\{([\w\W\s\S]*)\}$/; 13 | 14 | function getDanmuOption() { 15 | var initTime = Date.now(); 16 | 17 | (function loop() { 18 | if (window.DanmakuWebSocket) { 19 | var DWS = window.DanmakuWebSocket; 20 | 21 | window.DanmakuWebSocket = function f(option) { 22 | var dws = new DWS(option); 23 | window.postMessage({ 24 | type: 'danmu_option', 25 | data: Object.keys(option).reduce(function (obj, key) { 26 | if (typeof option[key] !== 'function') { 27 | obj[key] = option[key]; 28 | } 29 | 30 | return obj; 31 | }, {}) 32 | }); 33 | return dws; 34 | }; 35 | } else if (Date.now() - initTime >= 60000) { 36 | window.postMessage({ 37 | type: 'danmu_error' 38 | }); 39 | } else { 40 | setTimeout(loop, 10); 41 | } 42 | })(); 43 | } 44 | 45 | var Danmu = function Danmu() { 46 | classCallCheck(this, Danmu); 47 | 48 | var $script = document.createElement('script'); 49 | $script.type = 'text/javascript'; 50 | $script.text = getDanmuOption.toString().match(REG_FUNCTION)[3]; 51 | document.documentElement.appendChild($script); 52 | $script.onload = document.documentElement.removeChild($script); 53 | window.addEventListener('message', function (event) { 54 | chrome.runtime.sendMessage(event.data); 55 | }); 56 | }; 57 | 58 | var index = new Danmu(); 59 | 60 | return index; 61 | 62 | }()); 63 | //# sourceMappingURL=index.js.map 64 | -------------------------------------------------------------------------------- /packages/server/index.js: -------------------------------------------------------------------------------- 1 | const port = Number(process.argv[2]) || 8080; 2 | const io = require('socket.io')(port); 3 | const FFmpeg = require('./FFmpeg'); 4 | 5 | console.log(`中转地址:http://localhost:${port}`); 6 | 7 | io.on('connection', socket => { 8 | // 来自浏览器:开启进程 9 | socket.on('rtmp', rtmp => { 10 | // 销毁旧的重复进程 11 | const ffmpeg_old = FFmpeg.getInstance(socket); 12 | if (ffmpeg_old) { 13 | ffmpeg_old.destroy(); 14 | 15 | // 告知浏览器:打印 16 | socket.emit('log', `销毁旧的重复FFmpeg进程: ${FFmpeg.instances.size}`); 17 | } 18 | 19 | // 创建新进程 20 | const ffmpeg = new FFmpeg(rtmp, socket); 21 | 22 | // 进程关闭时 23 | ffmpeg.onClose(code => { 24 | const msg = 'FFmpeg进程退出码:' + code; 25 | 26 | // 告知浏览器:重连 27 | socket.emit('reconnect', msg); 28 | 29 | // 告知浏览器:不重连直接终止 30 | // socket.emit('fail', msg); 31 | 32 | console.log(msg); 33 | 34 | // 每次关闭都销毁进程 35 | ffmpeg.destroy(); 36 | }); 37 | 38 | // 告知浏览器:打印 39 | socket.emit('log', `创建FFmpeg进程成功: ${FFmpeg.instances.size}`); 40 | }); 41 | 42 | // 来自浏览器:推流 43 | socket.on('binary_stream', data => { 44 | const ffmpeg = FFmpeg.getInstance(socket); 45 | if (ffmpeg) { 46 | ffmpeg.write(data); 47 | } 48 | }); 49 | 50 | // 来自浏览器:终止 51 | socket.on('stream_disconnect', () => { 52 | console.log('来自浏览器:终止'); 53 | const ffmpeg = FFmpeg.getInstance(socket); 54 | if (ffmpeg) { 55 | ffmpeg.destroy(); 56 | } 57 | }); 58 | 59 | socket.on('error', error => { 60 | console.log(error); 61 | const ffmpeg = FFmpeg.getInstance(socket); 62 | if (ffmpeg) { 63 | ffmpeg.destroy(); 64 | } 65 | }); 66 | }); 67 | 68 | io.on('error', error => { 69 | FFmpeg.destroy(); 70 | console.log(error); 71 | }); 72 | 73 | process.on('uncaughtException', error => { 74 | FFmpeg.destroy(); 75 | console.log(error); 76 | }); 77 | -------------------------------------------------------------------------------- /packages/client/src/share/constant.js: -------------------------------------------------------------------------------- 1 | export const START = 'start'; 2 | export const STOP = 'stop'; 3 | export const CONFIG = 'config'; 4 | export const RECORDING = 'recording'; 5 | export const DEBUG = 'debug'; 6 | export const LOG = 'log'; 7 | export const ERROR = 'error'; 8 | export const FAIL = 'fail'; 9 | export const RTMP = 'rtmp'; 10 | export const RECONNECT = 'reconnect'; 11 | export const STREAM_DISCONNECT = 'stream_disconnect'; 12 | export const BINARY_STREAM = 'binary_stream'; 13 | export const DANMU_OPTION = 'danmu_option'; 14 | export const DANMU_ERROR = 'danmu_error'; 15 | 16 | export const SOCKET_SUCCESS = '建立socket连接成功'; 17 | export const SOCKET_FAIL = '建立socket连接失败'; 18 | export const TAB_VIDEO_STREAM_SUCCESS = '获取标签视频流成功'; 19 | export const TAB_VIDEO_STREAM_FAIL = '无法获取标签视频流,请重试!'; 20 | export const RECORDER_SUCCESS = '录制器启动成功'; 21 | export const RECORDER_FAIL = '无法录制标签的视频流,请重试!'; 22 | export const PUSH_STREAM_ING = '正在推流中...'; 23 | export const CAN_NOT_FIND_TAB = '未获取到当前激活的标签'; 24 | export const RTMP_ERROR = '请输入正确的rtmp推流地址'; 25 | export const STREAM_NAME_ERROR = '请输入正确的直播码'; 26 | export const SOCKET_ERROR = '请输入正确的中转地址'; 27 | export const LIVE_ROOM_ERROR = '不是有效的 Bilibili 直播间地址'; 28 | export const OPEN_SUCCESS = '打开直播间页面成功'; 29 | export const DANMU_ING = '正在获取弹幕接口...'; 30 | export const DANMU_SUCCESS = '获取弹幕接口成功'; 31 | export const DANMU_FAIL = '获取弹幕接口失败'; 32 | export const CURRENT_PAGE = '当前页面'; 33 | export const PUSH_STREAM_END = '已停止推流...'; 34 | export const RECONNECT_INFO = '自动重连中:'; 35 | export const INJECTED_SUCCESS = '注入文件:'; 36 | 37 | export const DEFAULT_SOCKET = 'http://localhost:8080'; 38 | export const DEFAULT_RESOLUTION = 720; 39 | export const DEFAULT_VIDEO_BITSPER = 2500000; 40 | export const DEFAULT_AUDIO_BITSPER = 128000; 41 | export const MIME_TYPE = 'video/webm; codecs="h264, opus"'; 42 | export const RECONNECT_TIME = 100; 43 | export const MAX_DANMU = 50; 44 | 45 | export const REG_RTMP = /^rtmp:\/\/.+/i; 46 | export const REG_HTTP = /^https?:\/\/.+/i; 47 | export const REG_LIVE = /^https?:\/\/live\.bilibili\.com/i; 48 | export const REG_FUNCTION = /^function\s*([\w$]*)\s*\(([\w\s,$]*)\)\s*\{([\w\W\s\S]*)\}$/; 49 | -------------------------------------------------------------------------------- /packages/client/dist/bilibili-live-hime-client/injected/index.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * bilibili-live-hime v1.0.1 3 | * Github: https://github.com/zhw2590582/bilibili-live-hime 4 | * (c) 2018-2019 Harvey Zack 5 | * Released under the MIT License. 6 | */ 7 | 8 | var BilibiliLiveHimeInjected=function(){"use strict";var t=function(t,n){if(!(t instanceof n))throw new TypeError("Cannot call a class as a function")};function n(t,n){for(var e=0;e1&&void 0!==arguments[1]?arguments[1]:document).querySelector(t)}return new(function(){function n(){t(this,n),window.location.href.includes(i)&&this.init()}return e(n,[{key:"init",value:function(){this.getChatHistoryList().then((function(t){new MutationObserver((function(t){t.forEach((function(t){Array.from(t.addedNodes||[]).forEach((function(t){if(t.classList.contains("danmaku-item"))try{window.postMessage({type:"danmu",data:{uid:Number(t.dataset.uid),uname:t.dataset.uname.trim(),text:t.dataset.danmaku.trim()}})}catch(t){}if(t.classList.contains("gift-item"))try{window.postMessage({type:"gift",data:{uid:null,uname:r(".username",t).innerText.trim(),action:r(".action",t).innerText.trim(),gift:r(".gift-name",t).innerText.trim(),num:r(".gift-num",t).innerText.trim(),count:r(".gift-count",t).innerText.trim()}})}catch(t){}if(t.classList.contains("guard-buy"))try{window.postMessage({type:"guard",data:{uid:null,uname:r(".username",t).innerText.trim(),action:"购买",gift:"舰长",num:"",count:r(".count",t).innerText.trim()}})}catch(t){}}))}))})).observe(t,{childList:!0})})),this.getPenuryGiftMsg().then((function(t){new MutationObserver((function(t){t.forEach((function(t){Array.from(t.addedNodes||[]).forEach((function(t){try{window.postMessage({type:"gift",data:{uid:null,uname:r(".username",t).innerText.trim(),action:r(".action",t).innerText.trim(),gift:r(".gift-name",t).innerText.trim(),num:"",count:r(".count",t).innerText.trim()}})}catch(t){}}))}))})).observe(t,{childList:!0})}))}},{key:"getChatHistoryList",value:function(){return new Promise((function(t){!function n(){var e=r("#chat-history-list");e?t(e):setTimeout(n,1e3)}()}))}},{key:"getPenuryGiftMsg",value:function(){return new Promise((function(t){!function n(){var e=r("#penury-gift-msg");e?t(e):setTimeout(n,1e3)}()}))}}]),n}())}(); 9 | -------------------------------------------------------------------------------- /packages/client/dist/bilibili-live-hime-client/popup/index.css: -------------------------------------------------------------------------------- 1 | *,:after,:before{box-sizing:border-box}body{margin:0;padding:0;font-size:12px;color:#666;line-height:1}.container{width:300px;-webkit-box-orient:vertical;-webkit-box-direction:normal;flex-direction:column}.container,.container .header{display:-webkit-box;display:flex}.container .header{-webkit-box-align:center;align-items:center;-webkit-box-pack:justify;justify-content:space-between;height:30px;padding:0 10px;background:#f7f7f7;border-bottom:1px solid #e6e6e6}.container .header .feedback,.container .header .name{cursor:pointer}.container .config{display:-webkit-box;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;flex-direction:column;position:relative;padding:10px 10px 0}.container .config .item{margin-bottom:10px}.container .config .item .key{margin-bottom:5px}.container .config .item .key .setting{color:#4fc1e9;cursor:pointer}.container .config .item .value input,.container .config .item .value select{width:100%;height:25px;padding:0 5px;outline:none}.container .config .item .debug{display:-webkit-box;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;flex-direction:column;padding:5px;height:100px;overflow:auto;line-height:1.5;background:#000}.container .config .item .debug p{position:relative;margin:0 0 0 10px}.container .config .item .debug p.log{color:#19d420}.container .config .item .debug p.log:before{content:"";position:absolute;top:5px;left:-8px;border-left:4px solid #19d420;border-top:4px solid transparent;border-bottom:4px solid transparent}.container .config .item .debug p.log:after{content:"";position:absolute;left:-8px;top:6px;border-left:3px solid #000;border-top:3px solid transparent;border-bottom:3px solid transparent}.container .config .item .debug p.error{color:red}.container .config .item .debug p.error:before{content:"";position:absolute;top:5px;left:-8px;border-left:4px solid red;border-top:4px solid transparent;border-bottom:4px solid transparent}.container .config .item .debug p.error:after{content:"";position:absolute;left:-8px;top:6px;border-left:3px solid #000;border-top:3px solid transparent;border-bottom:3px solid transparent}.container .footer{height:30px;cursor:pointer;color:#fff}.container .footer .start{display:-webkit-box;display:flex;background:#4fc1e9}.container .footer .start,.container .footer .stop{-webkit-box-align:center;align-items:center;-webkit-box-pack:center;justify-content:center;width:100%;height:100%}.container .footer .stop{display:none;background:#f6628f}.container.recording .start{display:none}.container.recording .stop{display:-webkit-box;display:flex} -------------------------------------------------------------------------------- /packages/client/src/popup/index.css: -------------------------------------------------------------------------------- 1 | *,:after,:before{box-sizing:border-box}body{margin:0;padding:0;font-size:12px;color:#666;line-height:1}.container{width:300px;-webkit-box-orient:vertical;-webkit-box-direction:normal;flex-direction:column}.container,.container .header{display:-webkit-box;display:flex}.container .header{-webkit-box-align:center;align-items:center;-webkit-box-pack:justify;justify-content:space-between;height:30px;padding:0 10px;background:#f7f7f7;border-bottom:1px solid #e6e6e6}.container .header .feedback,.container .header .name{cursor:pointer}.container .config{display:-webkit-box;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;flex-direction:column;position:relative;padding:10px 10px 0}.container .config .item{margin-bottom:10px}.container .config .item .key{margin-bottom:5px}.container .config .item .key .setting{color:#4fc1e9;cursor:pointer}.container .config .item .value input,.container .config .item .value select{width:100%;height:25px;padding:0 5px;outline:none}.container .config .item .debug{display:-webkit-box;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;flex-direction:column;padding:5px;height:100px;overflow:auto;line-height:1.5;background:#000}.container .config .item .debug p{position:relative;margin:0 0 0 10px}.container .config .item .debug p.log{color:#19d420}.container .config .item .debug p.log:before{content:"";position:absolute;top:5px;left:-8px;border-left:4px solid #19d420;border-top:4px solid transparent;border-bottom:4px solid transparent}.container .config .item .debug p.log:after{content:"";position:absolute;left:-8px;top:6px;border-left:3px solid #000;border-top:3px solid transparent;border-bottom:3px solid transparent}.container .config .item .debug p.error{color:red}.container .config .item .debug p.error:before{content:"";position:absolute;top:5px;left:-8px;border-left:4px solid red;border-top:4px solid transparent;border-bottom:4px solid transparent}.container .config .item .debug p.error:after{content:"";position:absolute;left:-8px;top:6px;border-left:3px solid #000;border-top:3px solid transparent;border-bottom:3px solid transparent}.container .footer{height:30px;cursor:pointer;color:#fff}.container .footer .start{display:-webkit-box;display:flex;background:#4fc1e9}.container .footer .start,.container .footer .stop{-webkit-box-align:center;align-items:center;-webkit-box-pack:center;justify-content:center;width:100%;height:100%}.container .footer .stop{display:none;background:#f6628f}.container.recording .start{display:none}.container.recording .stop{display:-webkit-box;display:flex} 2 | /*# sourceMappingURL=index.css.map */ -------------------------------------------------------------------------------- /packages/client/dist/bilibili-live-hime-client/content/index.css: -------------------------------------------------------------------------------- 1 | .blh-danmuku{position:fixed;z-index:9999;top:10px;right:10px;width:280px;font-size:13px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;font-family:Hiragino Sans GB,Microsoft YaHei,\5fae\8f6f\96c5\9ed1,tahoma,arial,simsun,\5b8b\4f53;-webkit-text-size-adjust:100%!important;-moz-text-size-adjust:100%!important;-ms-text-size-adjust:100%!important;text-size-adjust:100%!important;text-rendering:optimizeLegibility!important;-webkit-font-smoothing:antialiased!important;line-height:1;color:#fff;background-color:rgba(0,0,0,.6)}.blh-danmuku *,.blh-danmuku :after,.blh-danmuku :before{box-sizing:border-box}.blh-danmuku ::-webkit-scrollbar{display:block!important;width:5px!important}.blh-danmuku ::-webkit-scrollbar-thumb{background-color:#ccc!important}.blh-danmuku ::-webkit-scrollbar-thumb:hover{background-color:#eee!important}.blh-danmuku .blh-header{display:-webkit-box;display:flex;height:25px;border-bottom:1px solid rgba(0,0,0,.2);color:hsla(0,0%,100%,.8)}.blh-danmuku .blh-header .blh-header-l{display:-webkit-box;display:flex;-webkit-box-align:center;align-items:center;-webkit-box-flex:1;flex:1;cursor:move;padding:0 10px;font-size:13px}.blh-danmuku .blh-header .blh-header-r{display:-webkit-box;display:flex;-webkit-box-pack:center;justify-content:center;-webkit-box-align:center;align-items:center;width:25px;font-size:18px;cursor:pointer}.blh-danmuku .blh-danmu{padding:10px;min-height:100px;height:500px;overflow-y:auto;scroll-behavior:smooth;border-bottom:1px solid rgba(0,0,0,.2)}.blh-danmuku .blh-danmu .blh-danmu-inner .blh-danmu-item{text-align:left;line-height:1.4;margin-bottom:10px;text-shadow:0 1px 0 rgba(0,0,0,.1)}.blh-danmuku .blh-danmu .blh-danmu-inner .blh-danmu-item .blh-danmu-uname{color:#cae8ff}.blh-danmuku .blh-danmu .blh-danmu-inner .blh-danmu-item .blh-danmu-text{white-space:normal;word-break:break-all}.blh-danmuku .blh-gift{padding:10px;height:100px;overflow-y:auto;scroll-behavior:smooth}.blh-danmuku .blh-gift .blh-gift-inner .blh-gift-item{text-align:left;line-height:1.4;margin-bottom:10px;text-shadow:0 1px 0 rgba(0,0,0,.1)}.blh-danmuku .blh-gift .blh-gift-inner .blh-gift-item .blh-gift-uname{color:#cae8ff}.blh-danmuku .blh-gift .blh-gift-inner .blh-gift-item .blh-gift-text{white-space:normal;word-break:break-all}.blh-danmuku .blh-footer{height:10px;cursor:ns-resize}.blh-danmuku-icon{display:none;position:fixed;z-index:9999;top:10px;right:10px;width:25px;height:25px;text-align:center;line-height:25px;font-size:13px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;font-family:Hiragino Sans GB,Microsoft YaHei,\5fae\8f6f\96c5\9ed1,tahoma,arial,simsun,\5b8b\4f53;-webkit-text-size-adjust:100%!important;-moz-text-size-adjust:100%!important;-ms-text-size-adjust:100%!important;text-size-adjust:100%!important;text-rendering:optimizeLegibility!important;-webkit-font-smoothing:antialiased!important;cursor:pointer;opacity:.2;color:#fff;background-color:#000} -------------------------------------------------------------------------------- /packages/client/dist/bilibili-live-hime-client/active/index.css: -------------------------------------------------------------------------------- 1 | .blh-danmuku{position:fixed;z-index:9999;top:10px;right:10px;width:280px;font-size:13px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;font-family:Hiragino Sans GB,Microsoft YaHei,\5fae\8f6f\96c5\9ed1,tahoma,arial,simsun,\5b8b\4f53;-webkit-text-size-adjust:100%!important;-moz-text-size-adjust:100%!important;-ms-text-size-adjust:100%!important;text-size-adjust:100%!important;text-rendering:optimizeLegibility!important;-webkit-font-smoothing:antialiased!important;line-height:1;color:#fff;background-color:rgba(0,0,0,.6)}.blh-danmuku *,.blh-danmuku :after,.blh-danmuku :before{box-sizing:border-box}.blh-danmuku ::-webkit-scrollbar{display:block!important;width:5px!important}.blh-danmuku ::-webkit-scrollbar-thumb{background-color:#ccc!important}.blh-danmuku ::-webkit-scrollbar-thumb:hover{background-color:#eee!important}@supports ((-webkit-backdrop-filter:initial) or (backdrop-filter:initial)){.blh-danmuku{-webkit-backdrop-filter:saturate(180%) blur(10px);backdrop-filter:saturate(180%) blur(10px)}}.blh-danmuku .blh-header{display:-webkit-box;display:flex;height:25px;border-bottom:1px solid rgba(0,0,0,.2);color:hsla(0,0%,100%,.8)}.blh-danmuku .blh-header .blh-header-l{display:-webkit-box;display:flex;-webkit-box-align:center;align-items:center;-webkit-box-flex:1;flex:1;cursor:move;padding:0 10px;font-size:13px}.blh-danmuku .blh-header .blh-header-r{-webkit-box-pack:center;justify-content:center;width:25px;font-size:18px;cursor:pointer}.blh-danmuku .blh-header .blh-header-r,.blh-danmuku .blh-info{display:-webkit-box;display:flex;-webkit-box-align:center;align-items:center}.blh-danmuku .blh-info{height:25px;padding:0 10px;border-bottom:1px solid rgba(0,0,0,.2);color:hsla(0,0%,100%,.8)}.blh-danmuku .blh-info .blh-popular{margin-right:15px}.blh-danmuku .blh-danmu{padding:10px;min-height:100px;height:500px;overflow-y:auto;scroll-behavior:smooth;border-bottom:1px solid rgba(0,0,0,.2)}.blh-danmuku .blh-danmu .blh-danmu-inner .blh-danmu-item{text-align:left;line-height:1.4;margin-bottom:10px;text-shadow:0 1px 0 rgba(0,0,0,.1)}.blh-danmuku .blh-danmu .blh-danmu-inner .blh-danmu-item .blh-danmu-uname{color:#cae8ff}.blh-danmuku .blh-danmu .blh-danmu-inner .blh-danmu-item .blh-danmu-text{white-space:normal;word-break:break-all}.blh-danmuku .blh-gift{padding:10px;height:100px;overflow-y:auto;scroll-behavior:smooth}.blh-danmuku .blh-gift .blh-gift-inner .blh-gift-item{text-align:left;line-height:1.4;margin-bottom:10px;text-shadow:0 1px 0 rgba(0,0,0,.1)}.blh-danmuku .blh-gift .blh-gift-inner .blh-gift-item .blh-gift-uname{color:#cae8ff}.blh-danmuku .blh-gift .blh-gift-inner .blh-gift-item .blh-gift-text{white-space:normal;word-break:break-all}.blh-danmuku .blh-footer{height:10px;cursor:ns-resize}.blh-danmuku-icon{display:none;position:fixed;z-index:9999;top:10px;right:10px;width:25px;height:25px;text-align:center;line-height:25px;font-size:13px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;font-family:Hiragino Sans GB,Microsoft YaHei,\5fae\8f6f\96c5\9ed1,tahoma,arial,simsun,\5b8b\4f53;-webkit-text-size-adjust:100%!important;-moz-text-size-adjust:100%!important;-ms-text-size-adjust:100%!important;text-size-adjust:100%!important;text-rendering:optimizeLegibility!important;-webkit-font-smoothing:antialiased!important;cursor:pointer;opacity:.2;color:#fff;background-color:#000} -------------------------------------------------------------------------------- /packages/client/src/active/index.css: -------------------------------------------------------------------------------- 1 | .blh-danmuku{position:fixed;z-index:9999;top:10px;right:10px;width:280px;font-size:13px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;font-family:Hiragino Sans GB,Microsoft YaHei,\5fae\8f6f\96c5\9ed1,tahoma,arial,simsun,\5b8b\4f53;-webkit-text-size-adjust:100%!important;-moz-text-size-adjust:100%!important;-ms-text-size-adjust:100%!important;text-size-adjust:100%!important;text-rendering:optimizeLegibility!important;-webkit-font-smoothing:antialiased!important;line-height:1;color:#fff;background-color:rgba(0,0,0,.6)}.blh-danmuku *,.blh-danmuku :after,.blh-danmuku :before{box-sizing:border-box}.blh-danmuku ::-webkit-scrollbar{display:block!important;width:5px!important}.blh-danmuku ::-webkit-scrollbar-thumb{background-color:#ccc!important}.blh-danmuku ::-webkit-scrollbar-thumb:hover{background-color:#eee!important}@supports ((-webkit-backdrop-filter:initial) or (backdrop-filter:initial)){.blh-danmuku{-webkit-backdrop-filter:saturate(180%) blur(10px);backdrop-filter:saturate(180%) blur(10px)}}.blh-danmuku .blh-header{display:-webkit-box;display:flex;height:25px;border-bottom:1px solid rgba(0,0,0,.2);color:hsla(0,0%,100%,.8)}.blh-danmuku .blh-header .blh-header-l{display:-webkit-box;display:flex;-webkit-box-align:center;align-items:center;-webkit-box-flex:1;flex:1;cursor:move;padding:0 10px;font-size:13px}.blh-danmuku .blh-header .blh-header-r{-webkit-box-pack:center;justify-content:center;width:25px;font-size:18px;cursor:pointer}.blh-danmuku .blh-header .blh-header-r,.blh-danmuku .blh-info{display:-webkit-box;display:flex;-webkit-box-align:center;align-items:center}.blh-danmuku .blh-info{height:25px;padding:0 10px;border-bottom:1px solid rgba(0,0,0,.2);color:hsla(0,0%,100%,.8)}.blh-danmuku .blh-info .blh-popular{margin-right:15px}.blh-danmuku .blh-danmu{padding:10px;min-height:100px;height:500px;overflow-y:auto;scroll-behavior:smooth;border-bottom:1px solid rgba(0,0,0,.2)}.blh-danmuku .blh-danmu .blh-danmu-inner .blh-danmu-item{text-align:left;line-height:1.4;margin-bottom:10px;text-shadow:0 1px 0 rgba(0,0,0,.1)}.blh-danmuku .blh-danmu .blh-danmu-inner .blh-danmu-item .blh-danmu-uname{color:#cae8ff}.blh-danmuku .blh-danmu .blh-danmu-inner .blh-danmu-item .blh-danmu-text{white-space:normal;word-break:break-all}.blh-danmuku .blh-gift{padding:10px;height:100px;overflow-y:auto;scroll-behavior:smooth}.blh-danmuku .blh-gift .blh-gift-inner .blh-gift-item{text-align:left;line-height:1.4;margin-bottom:10px;text-shadow:0 1px 0 rgba(0,0,0,.1)}.blh-danmuku .blh-gift .blh-gift-inner .blh-gift-item .blh-gift-uname{color:#cae8ff}.blh-danmuku .blh-gift .blh-gift-inner .blh-gift-item .blh-gift-text{white-space:normal;word-break:break-all}.blh-danmuku .blh-footer{height:10px;cursor:ns-resize}.blh-danmuku-icon{display:none;position:fixed;z-index:9999;top:10px;right:10px;width:25px;height:25px;text-align:center;line-height:25px;font-size:13px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;font-family:Hiragino Sans GB,Microsoft YaHei,\5fae\8f6f\96c5\9ed1,tahoma,arial,simsun,\5b8b\4f53;-webkit-text-size-adjust:100%!important;-moz-text-size-adjust:100%!important;-ms-text-size-adjust:100%!important;text-size-adjust:100%!important;text-rendering:optimizeLegibility!important;-webkit-font-smoothing:antialiased!important;cursor:pointer;opacity:.2;color:#fff;background-color:#000} 2 | /*# sourceMappingURL=index.css.map */ -------------------------------------------------------------------------------- /packages/client/rollup.config.js: -------------------------------------------------------------------------------- 1 | const babel = require('rollup-plugin-babel'); 2 | const commonjs = require('rollup-plugin-commonjs'); 3 | const nodeResolve = require('rollup-plugin-node-resolve'); 4 | const { eslint } = require('rollup-plugin-eslint'); 5 | const { terser } = require('rollup-plugin-terser'); 6 | const replace = require('rollup-plugin-replace'); 7 | const copy = require('rollup-plugin-copy'); 8 | const postcss = require('rollup-plugin-postcss'); 9 | const autoprefixer = require('autoprefixer'); 10 | const cssnano = require('cssnano'); 11 | const { name, version, homepage } = require('./package.json'); 12 | 13 | const isProd = process.env.NODE_ENV === 'production'; 14 | module.exports = ['background', 'popup', 'active', 'danmu'].map(item => { 15 | return { 16 | input: `src/${item}/dev/index.js`, 17 | output: { 18 | name: `BilibiliLiveHime${item[0].toUpperCase()}${item.slice(1)}`, 19 | file: isProd ? `dist/${name}/${item}/index.js` : `src/${item}/index.js`, 20 | format: 'iife', 21 | sourcemap: !isProd, 22 | }, 23 | plugins: [ 24 | eslint({ 25 | exclude: ['node_modules/**', 'src/**/dev/*.scss'], 26 | }), 27 | nodeResolve(), 28 | commonjs(), 29 | babel({ 30 | runtimeHelpers: true, 31 | exclude: 'node_modules/**', 32 | presets: [ 33 | [ 34 | '@babel/env', 35 | { 36 | modules: false, 37 | }, 38 | ], 39 | ], 40 | plugins: ['@babel/plugin-external-helpers', '@babel/plugin-transform-runtime'], 41 | }), 42 | replace({ 43 | __ENV__: JSON.stringify(process.env.NODE_ENV), 44 | }), 45 | postcss({ 46 | plugins: [ 47 | autoprefixer(), 48 | cssnano({ 49 | preset: 'default', 50 | }), 51 | ], 52 | sourceMap: !isProd, 53 | extract: isProd ? `dist/${name}/${item}/index.css` : `src/${item}/index.css`, 54 | }), 55 | isProd && 56 | terser({ 57 | output: { 58 | preamble: 59 | '/*!\n' + 60 | ` * bilibili-live-hime v${version}\n` + 61 | ` * Github: ${homepage}\n` + 62 | ` * (c) 2018-${new Date().getFullYear()} Harvey Zack\n` + 63 | ' * Released under the MIT License.\n' + 64 | ' */\n', 65 | comments: () => false, 66 | }, 67 | }), 68 | isProd && 69 | copy({ 70 | targets: [ 71 | { 72 | src: [`src/${item}/*.html`], 73 | dest: `dist/${name}/${item}/`, 74 | }, 75 | { 76 | src: [`src/icons`, `src/manifest.json`], 77 | dest: `dist/${name}/`, 78 | }, 79 | ], 80 | hook: 'writeBundle', 81 | }), 82 | ], 83 | }; 84 | }); 85 | -------------------------------------------------------------------------------- /packages/client/src/popup/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Bilibili 直播姬 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 |
15 |
16 |
17 | 18 |
19 |
20 |
21 |
22 |
RTMP地址: 获取
23 |
24 | 25 |
26 |
27 |
28 |
直播码:
29 |
30 | 31 |
32 |
33 |
34 |
直播间地址:
35 |
36 | 37 |
38 |
39 |
40 |
中转地址: 获取
41 |
42 | 43 |
44 |
45 |
46 |
分辨率:
47 |
48 | 55 |
56 |
57 |
58 |
比特率:
59 |
60 | 70 |
71 |
72 |
73 |
Debug日志:
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 | 开始推流 82 |
83 |
84 | 停止推流 85 |
86 |
87 |
88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /packages/client/dist/bilibili-live-hime-client/popup/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Bilibili 直播姬 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 |
15 |
16 |
17 | 18 |
19 |
20 |
21 |
22 |
RTMP地址: 获取
23 |
24 | 25 |
26 |
27 |
28 |
直播码:
29 |
30 | 31 |
32 |
33 |
34 |
直播间地址:
35 |
36 | 37 |
38 |
39 |
40 |
中转地址: 获取
41 |
42 | 43 |
44 |
45 |
46 |
分辨率:
47 |
48 | 55 |
56 |
57 |
58 |
比特率:
59 |
60 | 70 |
71 |
72 |
73 |
Debug日志
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 | 开始推流 82 |
83 |
84 | 停止推流 85 |
86 |
87 |
88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # :strawberry: Chrome 扩展 - Bilibili 直播姬 2 | 3 | > 把浏览器页面直播到 Bilibili 直播间 4 | 5 | ## 说明 6 | 7 | - 无需安装`OBS`这类直播软件,就可以把浏览器任何页面直播到`B站`、`斗鱼`或者`虎牙`的直播间里去,假如你有自己的直播间并且也安装了`Node.js`的话,那么可以安装这个`Chrome`扩展玩下,适合做无人值守`音乐轮播`或者`电影轮播`什么的。 8 | 9 | - 配合`弹幕`接口功能,可以做出更自定义更丰富的`弹幕互动`直播场景,例如`弹幕点歌`、`弹幕点电影`和`语音朗读弹幕`这些小玩意,甚至还可以`弹幕写文章`或者`弹幕玩页游`等等,全都能在你的网页用`js`实现。 10 | 11 | - 为什么还需要`Node.js`呢,因为找了一圈也没找到浏览器直接编码和推流的好方案,所以还是需要运行一个简单的`Node.js`程序来做中转服务,不过这个程序只有几十行代码,可以本地运行也可以部署到服务器上。 12 | 13 | ## 演示 14 | 15 | 我的直播间,不定时在线:[https://live.bilibili.com/4092892](https://live.bilibili.com/4092892) 16 | 17 | ## 安装 18 | 19 | #### 客户端 20 | 21 | [![https://chrome.google.com/webstore/detail/jfgjlmafdjaofbkjpaoojooghnocjcag](./images/tryitnow.png)](https://chrome.google.com/webstore/detail/jfgjlmafdjaofbkjpaoojooghnocjcag) 22 | 23 | - [代码目录 packages/client](./packages/client) 24 | 25 | #### 服务端 26 | 27 | - [代码目录 packages/server](./packages/server) 28 | 29 | 也可以通过`npm`获取服务端代码: 30 | 31 | ```bash 32 | npm i bilibili-live-hime-server 33 | ``` 34 | 35 | 进入代码目录,假如不是通过`npm`获取的话,还需要安装依赖: 36 | 37 | ```bash 38 | npm install 39 | ``` 40 | 41 | 运行服务端,默认端口`8080`,默认中转地址就是`http://localhost:8080`: 42 | 43 | ```bash 44 | npm start 45 | ``` 46 | 47 | 假如是部署到服务器的话,中转地址就是`http://[公网IP]:8080`,或者也可以使用自定义端口: 48 | 49 | ```bash 50 | npm start 8081 51 | ``` 52 | 53 | ## 使用 54 | 55 | 安装好`Chrome`扩展,假如服务端已经在运行情况下,就可以打开你想直播的浏览器页面,然后在扩展填好基本的直播信息(如下图)就可以开播了。 56 | 57 | ## 截图 58 | 59 | 60 | 61 | ## 支持 62 | 63 | 只列出常见的平台,请注意有的平台是`rtmp地址`和`直播码`是写在一起的,而且要注意之间的斜杆`/`不能漏掉,你要自己把它拆开填写如: 64 | 65 | ```bash 66 | rtmp://***/{直播码} 67 | ``` 68 | 69 | | 直播平台 | 获取直播码 | 70 | | ------------ | ---------------------------------------------------------------------------------------------------------------------------------- | 71 | | B 站直播 | [https://link.bilibili.com/p/center/index#/my-room/start-live](https://link.bilibili.com/p/center/index#/my-room/start-live) | 72 | | 斗鱼直播 | [https://mp.douyu.com/live/main](https://mp.douyu.com/live/main) | 73 | | 虎牙直播 | [https://i.huya.com/index.php?m=ProfileSetting#ktylts](https://i.huya.com/index.php?m=ProfileSetting#ktylts) | 74 | | 战旗直播 | [https://www.zhanqi.tv/user/anchor/flowSetting](https://www.zhanqi.tv/user/anchor/flowSetting) | 75 | | Twitch 直播 | [https://stream.twitch.tv/ingests/](https://stream.twitch.tv/ingests/) | 76 | | Twitch 直播 | [https://dashboard.twitch.tv/settings/channel#stream-preferences](https://dashboard.twitch.tv/settings/channel#stream-preferences) | 77 | | YouTube 直播 | [https://www.youtube.com/live_dashboard](https://www.youtube.com/live_dashboard) | 78 | 79 | ## 弹幕 80 | 81 | 假如你有填写`直播间地址`的话,默认就会生成一个动态简单的弹幕姬,你也可以自定义弹幕行为,只要你的`被直播页面`有类似下面的代码就能接收到弹幕: 82 | 83 | ```js 84 | window.addEventListener('message', event => { 85 | const { cmd, info, data } = event.data; 86 | switch (cmd) { 87 | case 'DANMU_MSG': 88 | console.log('弹幕', info); 89 | break; 90 | case 'SEND_GIFT': 91 | console.log('礼物', data); 92 | break; 93 | case 'GUARD_BUY': 94 | console.log('上船', data); 95 | break; 96 | case 'ROOM_REAL_TIME_MESSAGE_UPDATE': 97 | console.log('粉丝', data.fans); 98 | console.log('房间', data.roomid); 99 | break; 100 | case 'POPULAR': 101 | console.log('人气', data); 102 | break; 103 | default: 104 | console.log('其他', event.data); 105 | break; 106 | } 107 | }); 108 | ``` 109 | 110 | ## 问题 111 | 112 | - 一个中转地址可以支持同时支持多个直播推流吗? 113 | 114 | 可以是可以,但目前服务端的代码还不是很完善,还是推荐不同直播推流使用不同端口的做法。 115 | 116 | - 怎么为页面快速地注入自定义的`js`和`css`文件? 117 | 118 | 例如怎么为网易云音乐注入你的`js`和`css`文件,通常只要打开`开发者工具`的控制台,然后把代码粘贴进去就可以。但是这个扩展支持只要把`js`和`css`直接拖放到`直播姬`的控制台就可以完成注入。 119 | 120 | - 可以录制直播的视频吗? 121 | 122 | 可以的,只要在服务端的`binary_stream`事件接收到视频字节流那里把数据存起来,最后保存成`webm`视频格式就行。 123 | 124 | ## 捐助 125 | 126 | ![捐助](./images/wechatpay.jpg) 127 | 128 | ## 交流 129 | 130 | ![QQ 群](./images/qqgroup.png) 131 | 132 | ## License 133 | 134 | MIT © [Harvey Zack](https://sleepy.im/) 135 | -------------------------------------------------------------------------------- /packages/client/src/active/dev/index.scss: -------------------------------------------------------------------------------- 1 | .blh-danmuku { 2 | *, 3 | *::before, 4 | *::after { 5 | box-sizing: border-box; 6 | } 7 | 8 | ::-webkit-scrollbar { 9 | display: block !important; 10 | width: 5px !important; 11 | } 12 | 13 | ::-webkit-scrollbar-thumb { 14 | background-color: #ccc !important; 15 | } 16 | 17 | ::-webkit-scrollbar-thumb:hover { 18 | background-color: #eee !important; 19 | } 20 | 21 | position: fixed; 22 | z-index: 9999; 23 | top: 10px; 24 | right: 10px; 25 | width: 280px; 26 | font-size: 13px; 27 | user-select: none; 28 | font-family: Hiragino Sans GB, Microsoft YaHei, \5fae\8f6f\96c5\9ed1, tahoma, arial, simsun, \5b8b\4f53; 29 | text-size-adjust: 100% !important; 30 | text-rendering: optimizeLegibility !important; 31 | -webkit-font-smoothing: antialiased !important; 32 | line-height: 1; 33 | color: #fff; 34 | background-color: rgba(0, 0, 0, 0.6); 35 | 36 | @supports ((-webkit-backdrop-filter: initial) or (backdrop-filter: initial)) { 37 | -webkit-backdrop-filter: saturate(180%) blur(10px); 38 | backdrop-filter: saturate(180%) blur(10px); 39 | } 40 | 41 | .blh-header { 42 | display: flex; 43 | height: 25px; 44 | border-bottom: 1px solid rgba(0, 0, 0, 0.2); 45 | color: rgba(255, 255, 255, 0.8); 46 | 47 | .blh-header-l { 48 | display: flex; 49 | align-items: center; 50 | flex: 1; 51 | cursor: move; 52 | padding: 0 10px; 53 | font-size: 13px; 54 | } 55 | 56 | .blh-header-r { 57 | display: flex; 58 | justify-content: center; 59 | align-items: center; 60 | width: 25px; 61 | font-size: 18px; 62 | cursor: pointer; 63 | } 64 | } 65 | 66 | .blh-info { 67 | display: flex; 68 | align-items: center; 69 | height: 25px; 70 | padding: 0 10px; 71 | border-bottom: 1px solid rgba(0, 0, 0, 0.2); 72 | color: rgba(255, 255, 255, 0.8); 73 | .blh-popular { 74 | margin-right: 15px; 75 | } 76 | } 77 | 78 | .blh-danmu { 79 | padding: 10px; 80 | min-height: 100px; 81 | height: 500px; 82 | overflow-y: auto; 83 | scroll-behavior: smooth; 84 | border-bottom: 1px solid rgba(0, 0, 0, 0.2); 85 | 86 | .blh-danmu-inner { 87 | .blh-danmu-item { 88 | text-align: left; 89 | line-height: 1.4; 90 | margin-bottom: 10px; 91 | text-shadow: 0 1px 0 rgba(0, 0, 0, 0.1); 92 | .blh-danmu-uname { 93 | color: #cae8ff; 94 | } 95 | .blh-danmu-text { 96 | white-space: normal; 97 | word-break: break-all; 98 | } 99 | } 100 | } 101 | } 102 | 103 | .blh-gift { 104 | padding: 10px; 105 | height: 100px; 106 | overflow-y: auto; 107 | scroll-behavior: smooth; 108 | 109 | .blh-gift-inner { 110 | .blh-gift-item { 111 | text-align: left; 112 | line-height: 1.4; 113 | margin-bottom: 10px; 114 | text-shadow: 0 1px 0 rgba(0, 0, 0, 0.1); 115 | .blh-gift-uname { 116 | color: #cae8ff; 117 | } 118 | .blh-gift-text { 119 | white-space: normal; 120 | word-break: break-all; 121 | } 122 | } 123 | } 124 | } 125 | 126 | .blh-footer { 127 | height: 10px; 128 | cursor: ns-resize; 129 | } 130 | } 131 | 132 | .blh-danmuku-icon { 133 | display: none; 134 | position: fixed; 135 | z-index: 9999; 136 | top: 10px; 137 | right: 10px; 138 | width: 25px; 139 | height: 25px; 140 | text-align: center; 141 | line-height: 25px; 142 | font-size: 13px; 143 | user-select: none; 144 | font-family: Hiragino Sans GB, Microsoft YaHei, \5fae\8f6f\96c5\9ed1, tahoma, arial, simsun, \5b8b\4f53; 145 | text-size-adjust: 100% !important; 146 | text-rendering: optimizeLegibility !important; 147 | -webkit-font-smoothing: antialiased !important; 148 | cursor: pointer; 149 | opacity: 0.2; 150 | color: #fff; 151 | background-color: #000; 152 | } 153 | -------------------------------------------------------------------------------- /packages/client/src/popup/index.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["index.scss"],"names":[],"mappings":"AAAA,iBAGE,qBAAwB,CAE1B,KACE,QAAS,CACT,SAAU,CACV,cAAe,CACf,UAAW,CACX,aAAgB,CAElB,WACE,WAAY,CAEZ,2BAAsB,CAAtB,4BAAsB,CAAtB,qBAAwB,CACxB,8BAFA,mBAAa,CAAb,YASoC,CAPpC,mBAEE,wBAAmB,CAAnB,kBAAmB,CACnB,wBAA8B,CAA9B,6BAA8B,CAC9B,WAAY,CACZ,cAAe,CACf,kBAAmB,CACnB,+BAAkC,CAClC,sDAEE,cAAiB,CACrB,mBACE,mBAAa,CAAb,YAAa,CACb,2BAAsB,CAAtB,4BAAsB,CAAtB,qBAAsB,CACtB,iBAAkB,CAClB,mBAAsB,CACtB,yBACE,kBAAqB,CACrB,8BACE,iBAAoB,CACpB,uCACE,aAAc,CACd,cAAiB,CACrB,6EAEE,UAAW,CACX,WAAY,CACZ,aAAc,CACd,YAAe,CACjB,gCACE,mBAAa,CAAb,YAAa,CACb,2BAAsB,CAAtB,4BAAsB,CAAtB,qBAAsB,CACtB,WAAY,CACZ,YAAa,CACb,aAAc,CACd,eAAgB,CAChB,eAAkB,CAClB,kCACE,iBAAkB,CAClB,iBAAoB,CACpB,sCACE,aAAgB,CAChB,6CACE,UAAW,CACX,iBAAkB,CAClB,OAAQ,CACR,SAAU,CACV,6BAA8B,CAC9B,gCAAiC,CACjC,mCAAsC,CACxC,4CACE,UAAW,CACX,iBAAkB,CAClB,SAAU,CACV,OAAQ,CACR,0BAA2B,CAC3B,gCAAiC,CACjC,mCAAsC,CAC1C,wCACE,SAAY,CACZ,+CACE,UAAW,CACX,iBAAkB,CAClB,OAAQ,CACR,SAAU,CACV,yBAA0B,CAC1B,gCAAiC,CACjC,mCAAsC,CACxC,8CACE,UAAW,CACX,iBAAkB,CAClB,SAAU,CACV,OAAQ,CACR,0BAA2B,CAC3B,gCAAiC,CACjC,mCAAsC,CAClD,mBACE,WAAY,CACZ,cAAe,CACf,UAAa,CACb,0BACE,mBAAa,CAAb,YAAa,CAKb,kBAAqB,CACvB,mDALE,wBAAmB,CAAnB,kBAAmB,CACnB,uBAAuB,CAAvB,sBAAuB,CACvB,UAAW,CACX,WAQqB,CANvB,yBACE,YAAa,CAKb,kBAAqB,CACzB,4BACE,YAAe,CACjB,2BACE,mBAAa,CAAb,YAAe","file":"index.css","sourcesContent":["*,\n*::before,\n*::after {\n box-sizing: border-box; }\n\nbody {\n margin: 0;\n padding: 0;\n font-size: 12px;\n color: #666;\n line-height: 1; }\n\n.container {\n width: 300px;\n display: flex;\n flex-direction: column; }\n .container .header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n height: 30px;\n padding: 0 10px;\n background: #f7f7f7;\n border-bottom: 1px solid #e6e6e6; }\n .container .header .name,\n .container .header .feedback {\n cursor: pointer; }\n .container .config {\n display: flex;\n flex-direction: column;\n position: relative;\n padding: 10px 10px 0; }\n .container .config .item {\n margin-bottom: 10px; }\n .container .config .item .key {\n margin-bottom: 5px; }\n .container .config .item .key .setting {\n color: #4fc1e9;\n cursor: pointer; }\n .container .config .item .value input,\n .container .config .item .value select {\n width: 100%;\n height: 25px;\n padding: 0 5px;\n outline: none; }\n .container .config .item .debug {\n display: flex;\n flex-direction: column;\n padding: 5px;\n height: 100px;\n overflow: auto;\n line-height: 1.5;\n background: #000; }\n .container .config .item .debug p {\n position: relative;\n margin: 0 0 0 10px; }\n .container .config .item .debug p.log {\n color: #19d420; }\n .container .config .item .debug p.log::before {\n content: '';\n position: absolute;\n top: 5px;\n left: -8px;\n border-left: 4px solid #19d420;\n border-top: 4px solid transparent;\n border-bottom: 4px solid transparent; }\n .container .config .item .debug p.log::after {\n content: '';\n position: absolute;\n left: -8px;\n top: 6px;\n border-left: 3px solid #000;\n border-top: 3px solid transparent;\n border-bottom: 3px solid transparent; }\n .container .config .item .debug p.error {\n color: red; }\n .container .config .item .debug p.error::before {\n content: '';\n position: absolute;\n top: 5px;\n left: -8px;\n border-left: 4px solid red;\n border-top: 4px solid transparent;\n border-bottom: 4px solid transparent; }\n .container .config .item .debug p.error::after {\n content: '';\n position: absolute;\n left: -8px;\n top: 6px;\n border-left: 3px solid #000;\n border-top: 3px solid transparent;\n border-bottom: 3px solid transparent; }\n .container .footer {\n height: 30px;\n cursor: pointer;\n color: #fff; }\n .container .footer .start {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n height: 100%;\n background: #4fc1e9; }\n .container .footer .stop {\n display: none;\n align-items: center;\n justify-content: center;\n width: 100%;\n height: 100%;\n background: #f6628f; }\n .container.recording .start {\n display: none; }\n .container.recording .stop {\n display: flex; }\n"]} -------------------------------------------------------------------------------- /packages/client/dist/bilibili-live-hime-client/content/index.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * bilibili-live-hime v1.0.1 3 | * Github: https://github.com/zhw2590582/bilibili-live-hime 4 | * (c) 2018-2019 Harvey Zack 5 | * Released under the MIT License. 6 | */ 7 | 8 | var BilibiliLiveHimeContent=function(){"use strict";var n=function(n,e){if(!(n instanceof e))throw new TypeError("Cannot call a class as a function")};function e(n,e){for(var t=0;t1&&void 0!==arguments[1]?arguments[1]:document).querySelector(n)}return new(function(){function e(){n(this,e);var t=i(".blh-danmuku");t?(i(".blh-danmu-inner",t).innerHTML="",i(".blh-gift-inner",t).innerHTML=""):(this.createUI(),this.eventBind(),this.receiveDanmu())}return t(e,[{key:"createUI",value:function(){this.manifest=chrome.runtime.getManifest(),this.$danmuku=document.createElement("div"),this.$danmuku.classList.add("blh-danmuku"),this.$danmuku.innerHTML='\n
\n
\n
×
\n
\n
\n
\n
\n
\n
\n
\n \n ',this.$headL=i(".blh-header-l",this.$danmuku),this.$headR=i(".blh-header-r",this.$danmuku),this.$danmu=i(".blh-danmu",this.$danmuku),this.$danmuInner=i(".blh-danmu-inner",this.$danmuku),this.$gift=i(".blh-gift",this.$danmuku),this.$giftInner=i(".blh-gift-inner",this.$danmuku),this.$footer=i(".blh-footer",this.$danmuku),this.$headL.textContent="".concat(this.manifest.name," ").concat(this.manifest.version),document.body.appendChild(this.$danmuku),this.$icon=document.createElement("div"),this.$icon.classList.add("blh-danmuku-icon"),this.$icon.textContent="弹",document.body.appendChild(this.$icon)}},{key:"eventBind",value:function(){var n=this,e=!1,t=!1,i=0,a=0,s=0,d=0,u=0;this.$headL.addEventListener("mousedown",(function(t){e=!0,i=t.pageX,a=t.pageY,s=n.$danmuku.offsetLeft,d=n.$danmuku.offsetTop})),this.$footer.addEventListener("mousedown",(function(e){t=!0,a=e.pageY,u=n.$danmu.clientHeight})),document.addEventListener("mousemove",(function(s){if(e){var d=s.pageX-i,c=s.pageY-a;n.$danmuku.style.transform="translate(".concat(d,"px, ").concat(c,"px)")}if(t){var o=u+s.pageY-a;o>=100?n.$danmu.style.height="".concat(o,"px"):t=!1}s.composedPath().indexOf(n.$danmuku)>-1?n.isHover=!0:n.isHover=!1})),document.addEventListener("mouseup",(function(u){if(e){e=!1,n.$danmuku.style.transform="translate(0, 0)";var c=s+u.pageX-i,o=d+u.pageY-a;n.$danmuku.style.left="".concat(c,"px"),n.$danmuku.style.top="".concat(o,"px")}t&&(t=!1)})),this.$headR.addEventListener("click",(function(){n.$danmuku.style.display="none",n.$icon.style.display="block"})),this.$icon.addEventListener("click",(function(){n.$danmuku.style.display="block",n.$icon.style.display="none"}))}},{key:"addDanmu",value:function(n){var e=this,t=this.$danmuInner.children;if(t.length>50){var a=t[0];i(".blh-danmu-uname",a).innerText="".concat(n.uname,":"),i(".blh-danmu-text",a).innerText=n.text,this.$danmuInner.appendChild(a)}else this.$danmuInner.insertAdjacentHTML("beforeend",'\n
\n '.concat(n.uname,':\n ').concat(n.text,"\n
\n "));this.isHover||(clearTimeout(this.danmuTimer),this.danmuTimer=setTimeout((function(){e.$danmu.scrollTo(0,e.$danmu.scrollHeight)}),100))}},{key:"addGift",value:function(n){var e=this,t=this.$giftInner.children;if(t.length>50){var a=t[0];i(".blh-gift-uname",a).innerText="".concat(n.uname,":"),i(".blh-gift-text",a).innerText="".concat(n.action," ").concat(n.gift," ").concat(n.num," ").concat(n.count),this.$giftInner.appendChild(a)}else this.$giftInner.insertAdjacentHTML("beforeend",'\n
\n '.concat(n.uname,':\n ').concat(n.action," ").concat(n.gift," ").concat(n.num," ").concat(n.count,"\n
\n "));this.isHover||(clearTimeout(this.giftTimer),this.giftTimer=setTimeout((function(){e.$gift.scrollTo(0,e.$gift.scrollHeight)}),100))}},{key:"receiveDanmu",value:function(){var n=this;chrome&&chrome.runtime.onMessage.addListener((function(e){var t=e.type,i=e.data;switch(window.postMessage(e),t){case"danmu":n.addDanmu(i);break;case"gift":case"guard":n.addGift(i)}}))}}]),e}())}(); 9 | -------------------------------------------------------------------------------- /packages/client/src/popup/dev/index.scss: -------------------------------------------------------------------------------- 1 | *, 2 | *::before, 3 | *::after { 4 | box-sizing: border-box; 5 | } 6 | 7 | body { 8 | margin: 0; 9 | padding: 0; 10 | font-size: 12px; 11 | color: #666; 12 | line-height: 1; 13 | } 14 | 15 | .container { 16 | width: 300px; 17 | display: flex; 18 | flex-direction: column; 19 | 20 | .header { 21 | display: flex; 22 | align-items: center; 23 | justify-content: space-between; 24 | height: 30px; 25 | padding: 0 10px; 26 | background: #f7f7f7; 27 | border-bottom: 1px solid #e6e6e6; 28 | 29 | .name, 30 | .feedback { 31 | cursor: pointer; 32 | } 33 | } 34 | 35 | .config { 36 | display: flex; 37 | flex-direction: column; 38 | position: relative; 39 | padding: 10px 10px 0; 40 | .item { 41 | margin-bottom: 10px; 42 | .key { 43 | margin-bottom: 5px; 44 | 45 | .setting { 46 | color: #4fc1e9; 47 | cursor: pointer; 48 | } 49 | } 50 | .value { 51 | input, 52 | select { 53 | width: 100%; 54 | height: 25px; 55 | padding: 0 5px; 56 | outline: none; 57 | } 58 | } 59 | .debug { 60 | display: flex; 61 | flex-direction: column; 62 | padding: 5px; 63 | height: 100px; 64 | overflow: auto; 65 | line-height: 1.5; 66 | background: #000; 67 | 68 | p { 69 | position: relative; 70 | margin: 0 0 0 10px; 71 | 72 | &.log { 73 | color: #19d420; 74 | &::before { 75 | content: ''; 76 | position: absolute; 77 | top: 5px; 78 | left: -8px; 79 | border-left: 4px solid #19d420; 80 | border-top: 4px solid transparent; 81 | border-bottom: 4px solid transparent; 82 | } 83 | 84 | &::after { 85 | content: ''; 86 | position: absolute; 87 | left: -8px; 88 | top: 6px; 89 | border-left: 3px solid #000; 90 | border-top: 3px solid transparent; 91 | border-bottom: 3px solid transparent; 92 | } 93 | } 94 | 95 | &.error { 96 | color: red; 97 | &::before { 98 | content: ''; 99 | position: absolute; 100 | top: 5px; 101 | left: -8px; 102 | border-left: 4px solid red; 103 | border-top: 4px solid transparent; 104 | border-bottom: 4px solid transparent; 105 | } 106 | 107 | &::after { 108 | content: ''; 109 | position: absolute; 110 | left: -8px; 111 | top: 6px; 112 | border-left: 3px solid #000; 113 | border-top: 3px solid transparent; 114 | border-bottom: 3px solid transparent; 115 | } 116 | } 117 | } 118 | } 119 | } 120 | } 121 | 122 | .footer { 123 | height: 30px; 124 | cursor: pointer; 125 | color: #fff; 126 | 127 | .start { 128 | display: flex; 129 | align-items: center; 130 | justify-content: center; 131 | width: 100%; 132 | height: 100%; 133 | background: #4fc1e9; 134 | } 135 | 136 | .stop { 137 | display: none; 138 | align-items: center; 139 | justify-content: center; 140 | width: 100%; 141 | height: 100%; 142 | background: #f6628f; 143 | } 144 | } 145 | 146 | &.recording { 147 | .start { 148 | display: none; 149 | } 150 | 151 | .stop { 152 | display: flex; 153 | } 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /packages/client/src/active/index.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["index.scss"],"names":[],"mappings":"AAAA,aACE,cAAe,CACf,YAAa,CACb,QAAS,CACT,UAAW,CACX,WAAY,CACZ,cAAe,CACf,wBAAiB,CAAjB,qBAAiB,CAAjB,oBAAiB,CAAjB,gBAAiB,CACjB,gGAAuG,CACvG,uCAAiC,CAAjC,oCAAiC,CAAjC,mCAAiC,CAAjC,+BAAiC,CACjC,2CAA6C,CAC7C,4CAA8C,CAC9C,aAAc,CACd,UAAW,CACX,+BAAsC,CACtC,wDAGE,qBAAwB,CAC1B,iCACE,uBAAyB,CACzB,mBAAuB,CACzB,uCACE,+BAAmC,CACrC,6CACE,+BAAmC,CACrC,2EACE,aACE,iDAAkD,CAClD,yCAA4C,CAAE,CAClD,yBACE,mBAAa,CAAb,YAAa,CACb,WAAY,CACZ,sCAA2C,CAC3C,wBAAiC,CACjC,uCACE,mBAAa,CAAb,YAAa,CACb,wBAAmB,CAAnB,kBAAmB,CACnB,kBAAO,CAAP,MAAO,CACP,WAAY,CACZ,cAAe,CACf,cAAiB,CACnB,uCAEE,uBAAuB,CAAvB,sBAAuB,CAEvB,UAAW,CACX,cAAe,CACf,cAAiB,CACrB,8DANI,mBAAa,CAAb,YAAa,CAEb,wBAAmB,CAAnB,kBAU+B,CANnC,uBAGE,WAAY,CACZ,cAAe,CACf,sCAA2C,CAC3C,wBAAiC,CACjC,oCACE,iBAAoB,CACxB,wBACE,YAAa,CACb,gBAAiB,CACjB,YAAa,CACb,eAAgB,CAChB,sBAAuB,CACvB,sCAA6C,CAC7C,yDACE,eAAgB,CAChB,eAAgB,CAChB,kBAAmB,CACnB,kCAAyC,CACzC,0EACE,aAAgB,CAClB,yEACE,kBAAmB,CACnB,oBAAuB,CAC7B,uBACE,YAAa,CACb,YAAa,CACb,eAAgB,CAChB,sBAAyB,CACzB,sDACE,eAAgB,CAChB,eAAgB,CAChB,kBAAmB,CACnB,kCAAyC,CACzC,sEACE,aAAgB,CAClB,qEACE,kBAAmB,CACnB,oBAAuB,CAC7B,yBACE,WAAY,CACZ,gBAAmB,CAEvB,kBACE,YAAa,CACb,cAAe,CACf,YAAa,CACb,QAAS,CACT,UAAW,CACX,UAAW,CACX,WAAY,CACZ,iBAAkB,CAClB,gBAAiB,CACjB,cAAe,CACf,wBAAiB,CAAjB,qBAAiB,CAAjB,oBAAiB,CAAjB,gBAAiB,CACjB,gGAAuG,CACvG,uCAAiC,CAAjC,oCAAiC,CAAjC,mCAAiC,CAAjC,+BAAiC,CACjC,2CAA6C,CAC7C,4CAA8C,CAC9C,cAAe,CACf,UAAY,CACZ,UAAW,CACX,qBAAwB","file":"index.css","sourcesContent":[".blh-danmuku {\n position: fixed;\n z-index: 9999;\n top: 10px;\n right: 10px;\n width: 280px;\n font-size: 13px;\n user-select: none;\n font-family: Hiragino Sans GB, Microsoft YaHei, \\5fae\\8f6f\\96c5\\9ed1, tahoma, arial, simsun, \\5b8b\\4f53;\n text-size-adjust: 100% !important;\n text-rendering: optimizeLegibility !important;\n -webkit-font-smoothing: antialiased !important;\n line-height: 1;\n color: #fff;\n background-color: rgba(0, 0, 0, 0.6); }\n .blh-danmuku *,\n .blh-danmuku *::before,\n .blh-danmuku *::after {\n box-sizing: border-box; }\n .blh-danmuku ::-webkit-scrollbar {\n display: block !important;\n width: 5px !important; }\n .blh-danmuku ::-webkit-scrollbar-thumb {\n background-color: #ccc !important; }\n .blh-danmuku ::-webkit-scrollbar-thumb:hover {\n background-color: #eee !important; }\n @supports (-webkit-backdrop-filter: initial) or (backdrop-filter: initial) {\n .blh-danmuku {\n -webkit-backdrop-filter: saturate(180%) blur(10px);\n backdrop-filter: saturate(180%) blur(10px); } }\n .blh-danmuku .blh-header {\n display: flex;\n height: 25px;\n border-bottom: 1px solid rgba(0, 0, 0, 0.2);\n color: rgba(255, 255, 255, 0.8); }\n .blh-danmuku .blh-header .blh-header-l {\n display: flex;\n align-items: center;\n flex: 1;\n cursor: move;\n padding: 0 10px;\n font-size: 13px; }\n .blh-danmuku .blh-header .blh-header-r {\n display: flex;\n justify-content: center;\n align-items: center;\n width: 25px;\n font-size: 18px;\n cursor: pointer; }\n .blh-danmuku .blh-info {\n display: flex;\n align-items: center;\n height: 25px;\n padding: 0 10px;\n border-bottom: 1px solid rgba(0, 0, 0, 0.2);\n color: rgba(255, 255, 255, 0.8); }\n .blh-danmuku .blh-info .blh-popular {\n margin-right: 15px; }\n .blh-danmuku .blh-danmu {\n padding: 10px;\n min-height: 100px;\n height: 500px;\n overflow-y: auto;\n scroll-behavior: smooth;\n border-bottom: 1px solid rgba(0, 0, 0, 0.2); }\n .blh-danmuku .blh-danmu .blh-danmu-inner .blh-danmu-item {\n text-align: left;\n line-height: 1.4;\n margin-bottom: 10px;\n text-shadow: 0 1px 0 rgba(0, 0, 0, 0.1); }\n .blh-danmuku .blh-danmu .blh-danmu-inner .blh-danmu-item .blh-danmu-uname {\n color: #cae8ff; }\n .blh-danmuku .blh-danmu .blh-danmu-inner .blh-danmu-item .blh-danmu-text {\n white-space: normal;\n word-break: break-all; }\n .blh-danmuku .blh-gift {\n padding: 10px;\n height: 100px;\n overflow-y: auto;\n scroll-behavior: smooth; }\n .blh-danmuku .blh-gift .blh-gift-inner .blh-gift-item {\n text-align: left;\n line-height: 1.4;\n margin-bottom: 10px;\n text-shadow: 0 1px 0 rgba(0, 0, 0, 0.1); }\n .blh-danmuku .blh-gift .blh-gift-inner .blh-gift-item .blh-gift-uname {\n color: #cae8ff; }\n .blh-danmuku .blh-gift .blh-gift-inner .blh-gift-item .blh-gift-text {\n white-space: normal;\n word-break: break-all; }\n .blh-danmuku .blh-footer {\n height: 10px;\n cursor: ns-resize; }\n\n.blh-danmuku-icon {\n display: none;\n position: fixed;\n z-index: 9999;\n top: 10px;\n right: 10px;\n width: 25px;\n height: 25px;\n text-align: center;\n line-height: 25px;\n font-size: 13px;\n user-select: none;\n font-family: Hiragino Sans GB, Microsoft YaHei, \\5fae\\8f6f\\96c5\\9ed1, tahoma, arial, simsun, \\5b8b\\4f53;\n text-size-adjust: 100% !important;\n text-rendering: optimizeLegibility !important;\n -webkit-font-smoothing: antialiased !important;\n cursor: pointer;\n opacity: 0.2;\n color: #fff;\n background-color: #000; }\n"]} -------------------------------------------------------------------------------- /packages/client/src/danmu/index.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"index.js","sources":["../../node_modules/@babel/runtime/helpers/classCallCheck.js","../share/constant.js","dev/index.js"],"sourcesContent":["function _classCallCheck(instance, Constructor) {\n if (!(instance instanceof Constructor)) {\n throw new TypeError(\"Cannot call a class as a function\");\n }\n}\n\nmodule.exports = _classCallCheck;","export const START = 'start';\nexport const STOP = 'stop';\nexport const CONFIG = 'config';\nexport const RECORDING = 'recording';\nexport const DEBUG = 'debug';\nexport const LOG = 'log';\nexport const ERROR = 'error';\nexport const FAIL = 'fail';\nexport const RTMP = 'rtmp';\nexport const RECONNECT = 'reconnect';\nexport const STREAM_DISCONNECT = 'stream_disconnect';\nexport const BINARY_STREAM = 'binary_stream';\nexport const DANMU_OPTION = 'danmu_option';\nexport const DANMU_ERROR = 'danmu_error';\n\nexport const SOCKET_SUCCESS = '建立socket连接成功';\nexport const SOCKET_FAIL = '建立socket连接失败';\nexport const TAB_VIDEO_STREAM_SUCCESS = '获取标签视频流成功';\nexport const TAB_VIDEO_STREAM_FAIL = '无法获取标签视频流,请重试!';\nexport const RECORDER_SUCCESS = '录制器启动成功';\nexport const RECORDER_FAIL = '无法录制标签的视频流,请重试!';\nexport const PUSH_STREAM_ING = '正在推流中...';\nexport const CAN_NOT_FIND_TAB = '未获取到当前激活的标签';\nexport const RTMP_ERROR = '请输入正确的rtmp推流地址';\nexport const STREAM_NAME_ERROR = '请输入正确的直播码';\nexport const SOCKET_ERROR = '请输入正确的中转地址';\nexport const LIVE_ROOM_ERROR = '不是有效的 Bilibili 直播间地址';\nexport const OPEN_SUCCESS = '打开直播间页面成功';\nexport const DANMU_ING = '正在获取弹幕接口...';\nexport const DANMU_SUCCESS = '获取弹幕接口成功';\nexport const DANMU_FAIL = '获取弹幕接口失败';\nexport const CURRENT_PAGE = '当前页面';\nexport const PUSH_STREAM_END = '已停止推流...';\nexport const RECONNECT_INFO = '自动重连中:';\nexport const INJECTED_SUCCESS = '注入文件:';\n\nexport const DEFAULT_SOCKET = 'http://localhost:8080';\nexport const DEFAULT_RESOLUTION = 720;\nexport const DEFAULT_VIDEO_BITSPER = 2500000;\nexport const DEFAULT_AUDIO_BITSPER = 128000;\nexport const MIME_TYPE = 'video/webm; codecs=\"h264, opus\"';\nexport const RECONNECT_TIME = 100;\nexport const MAX_DANMU = 50;\n\nexport const REG_RTMP = /^rtmp:\\/\\/.+/i;\nexport const REG_HTTP = /^https?:\\/\\/.+/i;\nexport const REG_LIVE = /^https?:\\/\\/live\\.bilibili\\.com/i;\nexport const REG_FUNCTION = /^function\\s*([\\w$]*)\\s*\\(([\\w\\s,$]*)\\)\\s*\\{([\\w\\W\\s\\S]*)\\}$/;\n","import { REG_FUNCTION } from '../../share/constant';\n\nfunction getDanmuOption() {\n const initTime = Date.now();\n (function loop() {\n if (window.DanmakuWebSocket) {\n const DWS = window.DanmakuWebSocket;\n window.DanmakuWebSocket = function f(option) {\n const dws = new DWS(option);\n window.postMessage({\n type: 'danmu_option',\n data: Object.keys(option).reduce((obj, key) => {\n if (typeof option[key] !== 'function') {\n obj[key] = option[key];\n }\n return obj;\n }, {}),\n });\n return dws;\n };\n } else if (Date.now() - initTime >= 60000) {\n window.postMessage({\n type: 'danmu_error',\n });\n } else {\n setTimeout(loop, 10);\n }\n })();\n}\n\nclass Danmu {\n constructor() {\n const $script = document.createElement('script');\n $script.type = 'text/javascript';\n $script.text = getDanmuOption.toString().match(REG_FUNCTION)[3];\n document.documentElement.appendChild($script);\n $script.onload = document.documentElement.removeChild($script);\n\n window.addEventListener('message', event => {\n chrome.runtime.sendMessage(event.data);\n });\n }\n}\n\nexport default new Danmu();\n"],"names":["REG_FUNCTION","getDanmuOption","initTime","Date","now","loop","window","DanmakuWebSocket","DWS","f","option","dws","postMessage","type","data","Object","keys","reduce","obj","key","setTimeout","Danmu","$script","document","createElement","text","toString","match","documentElement","appendChild","onload","removeChild","addEventListener","event","chrome","runtime","sendMessage"],"mappings":";;;EAAA,SAAS,eAAe,CAAC,QAAQ,EAAE,WAAW,EAAE;IAC9C,IAAI,EAAE,QAAQ,YAAY,WAAW,CAAC,EAAE;MACtC,MAAM,IAAI,SAAS,CAAC,mCAAmC,CAAC,CAAC;KAC1D;GACF;;EAED,kBAAc,GAAG,eAAe;;ECyCzB,IAAMA,YAAY,GAAG,6DAArB;;EC7CP,SAASC,cAAT,GAA0B;EACtB,MAAMC,QAAQ,GAAGC,IAAI,CAACC,GAAL,EAAjB;;EACA,GAAC,SAASC,IAAT,GAAgB;EACb,QAAIC,MAAM,CAACC,gBAAX,EAA6B;EACzB,UAAMC,GAAG,GAAGF,MAAM,CAACC,gBAAnB;;EACAD,MAAAA,MAAM,CAACC,gBAAP,GAA0B,SAASE,CAAT,CAAWC,MAAX,EAAmB;EACzC,YAAMC,GAAG,GAAG,IAAIH,GAAJ,CAAQE,MAAR,CAAZ;EACAJ,QAAAA,MAAM,CAACM,WAAP,CAAmB;EACfC,UAAAA,IAAI,EAAE,cADS;EAEfC,UAAAA,IAAI,EAAEC,MAAM,CAACC,IAAP,CAAYN,MAAZ,EAAoBO,MAApB,CAA2B,UAACC,GAAD,EAAMC,GAAN,EAAc;EAC3C,gBAAI,OAAOT,MAAM,CAACS,GAAD,CAAb,KAAuB,UAA3B,EAAuC;EACnCD,cAAAA,GAAG,CAACC,GAAD,CAAH,GAAWT,MAAM,CAACS,GAAD,CAAjB;EACH;;EACD,mBAAOD,GAAP;EACH,WALK,EAKH,EALG;EAFS,SAAnB;EASA,eAAOP,GAAP;EACH,OAZD;EAaH,KAfD,MAeO,IAAIR,IAAI,CAACC,GAAL,KAAaF,QAAb,IAAyB,KAA7B,EAAoC;EACvCI,MAAAA,MAAM,CAACM,WAAP,CAAmB;EACfC,QAAAA,IAAI,EAAE;EADS,OAAnB;EAGH,KAJM,MAIA;EACHO,MAAAA,UAAU,CAACf,IAAD,EAAO,EAAP,CAAV;EACH;EACJ,GAvBD;EAwBH;;MAEKgB,QACF,iBAAc;EAAA;;EACV,MAAMC,OAAO,GAAGC,QAAQ,CAACC,aAAT,CAAuB,QAAvB,CAAhB;EACAF,EAAAA,OAAO,CAACT,IAAR,GAAe,iBAAf;EACAS,EAAAA,OAAO,CAACG,IAAR,GAAexB,cAAc,CAACyB,QAAf,GAA0BC,KAA1B,CAAgC3B,YAAhC,EAA8C,CAA9C,CAAf;EACAuB,EAAAA,QAAQ,CAACK,eAAT,CAAyBC,WAAzB,CAAqCP,OAArC;EACAA,EAAAA,OAAO,CAACQ,MAAR,GAAiBP,QAAQ,CAACK,eAAT,CAAyBG,WAAzB,CAAqCT,OAArC,CAAjB;EAEAhB,EAAAA,MAAM,CAAC0B,gBAAP,CAAwB,SAAxB,EAAmC,UAAAC,KAAK,EAAI;EACxCC,IAAAA,MAAM,CAACC,OAAP,CAAeC,WAAf,CAA2BH,KAAK,CAACnB,IAAjC;EACH,GAFD;EAGH;;AAGL,cAAe,IAAIO,KAAJ,EAAf;;;;;;;;"} -------------------------------------------------------------------------------- /packages/client/src/share/index.js: -------------------------------------------------------------------------------- 1 | import ots from 'obj-to-string'; 2 | import { DEBUG, LOG, ERROR } from './constant'; 3 | 4 | export function sleep(ms = 0) { 5 | return new Promise(resolve => setTimeout(resolve, ms)); 6 | } 7 | 8 | export function query(el, doc = document) { 9 | return doc.querySelector(el); 10 | } 11 | 12 | export function getActiveTab() { 13 | return new Promise(resolve => { 14 | chrome.tabs.query( 15 | { 16 | active: true, 17 | currentWindow: true, 18 | }, 19 | tabs => { 20 | resolve(tabs[0]); 21 | }, 22 | ); 23 | }); 24 | } 25 | 26 | export function has(result, key) { 27 | return Object.prototype.hasOwnProperty.call(result, key); 28 | } 29 | 30 | export function setStorage(key, value) { 31 | return new Promise(resolve => { 32 | chrome.storage.local.set( 33 | { 34 | [key]: value, 35 | }, 36 | () => { 37 | resolve(value); 38 | }, 39 | ); 40 | }); 41 | } 42 | 43 | export function getStorage(key, defaultValue) { 44 | return new Promise(resolve => { 45 | chrome.storage.local.get([key], result => { 46 | if (has(result, key)) { 47 | resolve(result[key]); 48 | } else if (defaultValue) { 49 | setStorage(key, defaultValue).then(value => { 50 | resolve(value); 51 | }); 52 | } else { 53 | resolve(); 54 | } 55 | }); 56 | }); 57 | } 58 | 59 | export function storageChange(callback) { 60 | chrome.storage.onChanged.addListener(callback); 61 | } 62 | 63 | export function openTab(url, active = true) { 64 | return new Promise(resolve => { 65 | chrome.tabs.create({ url, active }, tab => { 66 | resolve(tab); 67 | }); 68 | }); 69 | } 70 | 71 | export function setActiveTab(tabId) { 72 | return new Promise(resolve => { 73 | chrome.tabs.update( 74 | tabId, 75 | { 76 | active: true, 77 | }, 78 | tab => { 79 | resolve(tab); 80 | }, 81 | ); 82 | }); 83 | } 84 | 85 | export function getCapturedTab() { 86 | return new Promise(resolve => { 87 | chrome.tabCapture.getCapturedTabs(tabs => { 88 | resolve(tabs[0]); 89 | }); 90 | }); 91 | } 92 | 93 | export function findTabById(id) { 94 | return new Promise(resolve => { 95 | chrome.tabs.get(id, tab => { 96 | resolve(tab); 97 | }); 98 | }); 99 | } 100 | 101 | export function removeTab(id) { 102 | return new Promise(resolve => { 103 | chrome.tabs.remove(id, () => { 104 | resolve(); 105 | }); 106 | }); 107 | } 108 | 109 | export const debug = { 110 | async log(msg) { 111 | const logs = (await getStorage(DEBUG)) || []; 112 | logs.push({ 113 | type: LOG, 114 | data: ots(msg), 115 | }); 116 | await setStorage(DEBUG, logs); 117 | }, 118 | async err(msg) { 119 | const logs = (await getStorage(DEBUG)) || []; 120 | logs.push({ 121 | type: ERROR, 122 | data: ots(msg), 123 | }); 124 | await setStorage(DEBUG, logs); 125 | }, 126 | async clean() { 127 | await setStorage(DEBUG, []); 128 | }, 129 | }; 130 | 131 | export function sendMessage(data) { 132 | chrome.runtime.sendMessage(data); 133 | } 134 | 135 | export function onMessage(callback) { 136 | chrome.runtime.onMessage.addListener(callback); 137 | } 138 | 139 | export function sendMessageToTab(tabId, data) { 140 | chrome.tabs.sendMessage(tabId, data); 141 | } 142 | 143 | export function setBadge(text = '', color = 'red') { 144 | return new Promise(resolve => { 145 | chrome.browserAction.setBadgeBackgroundColor( 146 | { 147 | color, 148 | }, 149 | () => { 150 | chrome.browserAction.setBadgeText( 151 | { 152 | text, 153 | }, 154 | () => { 155 | resolve(); 156 | }, 157 | ); 158 | }, 159 | ); 160 | }); 161 | } 162 | 163 | export function injectedScript(tabId, file) { 164 | return new Promise(resolve => { 165 | chrome.tabs.executeScript( 166 | tabId, 167 | { 168 | file, 169 | runAt: 'document_start', 170 | }, 171 | () => { 172 | resolve(); 173 | }, 174 | ); 175 | }); 176 | } 177 | 178 | export function runScript(code) { 179 | return new Promise(resolve => { 180 | chrome.tabs.executeScript( 181 | { 182 | code, 183 | }, 184 | () => { 185 | resolve(); 186 | }, 187 | ); 188 | }); 189 | } 190 | 191 | export function insertCSS(tabId, file) { 192 | return new Promise(resolve => { 193 | chrome.tabs.insertCSS( 194 | tabId, 195 | { 196 | file, 197 | }, 198 | () => { 199 | resolve(); 200 | }, 201 | ); 202 | }); 203 | } 204 | 205 | export function runCss(code) { 206 | return new Promise(resolve => { 207 | chrome.tabs.insertCSS( 208 | { 209 | code, 210 | }, 211 | () => { 212 | resolve(); 213 | }, 214 | ); 215 | }); 216 | } 217 | -------------------------------------------------------------------------------- /packages/client/src/active/dev/index.js: -------------------------------------------------------------------------------- 1 | import './index.scss'; 2 | import './DanmakuWebSocket'; 3 | import { DANMU_OPTION, MAX_DANMU } from '../../share/constant'; 4 | 5 | function query(el, doc = document) { 6 | return doc.querySelector(el); 7 | } 8 | 9 | class Content { 10 | constructor() { 11 | chrome.runtime.onMessage.addListener(request => { 12 | if (query('.blh-danmuku')) return; 13 | const { type, data } = request; 14 | switch (type) { 15 | case DANMU_OPTION: { 16 | this.dws = new window.DanmakuWebSocket({ 17 | ...data, 18 | onInitialized: () => { 19 | if (!query('.blh-danmuku')) { 20 | this.createUI(); 21 | this.eventBind(); 22 | } 23 | }, 24 | onReceivedMessage: msg => { 25 | window.postMessage(msg); 26 | this.receivedMessage(msg); 27 | }, 28 | onHeartBeatReply: msg => { 29 | window.postMessage({ 30 | cmd: 'POPULAR', 31 | data: msg.count, 32 | }); 33 | this.$popular.innerText = `人气:${msg.count || '--'}`; 34 | }, 35 | }); 36 | break; 37 | } 38 | default: 39 | break; 40 | } 41 | }); 42 | } 43 | 44 | receivedMessage(msg) { 45 | const { cmd, info, data } = msg; 46 | switch (cmd) { 47 | case 'DANMU_MSG': 48 | this.addDanmu({ 49 | uname: info[2][1], 50 | text: info[1], 51 | }); 52 | break; 53 | case 'SEND_GIFT': 54 | this.addGift({ 55 | uname: data.uname, 56 | action: data.action, 57 | gift: data.giftName, 58 | num: data.num, 59 | }); 60 | break; 61 | case 'GUARD_BUY': 62 | this.addGift({ 63 | uname: data.username, 64 | action: '购买', 65 | gift: data.gift_name, 66 | num: data.num, 67 | }); 68 | break; 69 | case 'ROOM_REAL_TIME_MESSAGE_UPDATE': 70 | this.$headL.innerText = `房间:${data.roomid || '--'}`; 71 | this.$fans.innerText = `粉丝:${data.fans || '--'}`; 72 | break; 73 | default: 74 | break; 75 | } 76 | } 77 | 78 | createUI() { 79 | this.manifest = chrome.runtime.getManifest(); 80 | this.$danmuku = document.createElement('div'); 81 | this.$danmuku.classList.add('blh-danmuku'); 82 | this.$danmuku.innerHTML = ` 83 |
84 |
85 |
×
86 |
87 |
88 | 人气:-- 89 | 粉丝:-- 90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 | 98 | `; 99 | this.$headL = query('.blh-header-l', this.$danmuku); 100 | this.$popular = query('.blh-popular', this.$danmuku); 101 | this.$fans = query('.blh-fans', this.$danmuku); 102 | this.$headR = query('.blh-header-r', this.$danmuku); 103 | this.$danmu = query('.blh-danmu', this.$danmuku); 104 | this.$danmuInner = query('.blh-danmu-inner', this.$danmuku); 105 | this.$gift = query('.blh-gift', this.$danmuku); 106 | this.$giftInner = query('.blh-gift-inner', this.$danmuku); 107 | this.$footer = query('.blh-footer', this.$danmuku); 108 | this.$headL.textContent = `${this.manifest.name} ${this.manifest.version}`; 109 | document.body.appendChild(this.$danmuku); 110 | 111 | this.$icon = document.createElement('div'); 112 | this.$icon.classList.add('blh-danmuku-icon'); 113 | this.$icon.textContent = '弹'; 114 | document.body.appendChild(this.$icon); 115 | } 116 | 117 | eventBind() { 118 | let isHeadDroging = false; 119 | let isFootDroging = false; 120 | let lastPageX = 0; 121 | let lastPageY = 0; 122 | let lastLeft = 0; 123 | let lastTop = 0; 124 | let lastHeight = 0; 125 | 126 | this.$headL.addEventListener('mousedown', event => { 127 | isHeadDroging = true; 128 | lastPageX = event.pageX; 129 | lastPageY = event.pageY; 130 | lastLeft = this.$danmuku.offsetLeft; 131 | lastTop = this.$danmuku.offsetTop; 132 | }); 133 | 134 | this.$footer.addEventListener('mousedown', event => { 135 | isFootDroging = true; 136 | lastPageY = event.pageY; 137 | lastHeight = this.$danmu.clientHeight; 138 | }); 139 | 140 | document.addEventListener('mousemove', event => { 141 | if (isHeadDroging) { 142 | const x = event.pageX - lastPageX; 143 | const y = event.pageY - lastPageY; 144 | this.$danmuku.style.transform = `translate(${x}px, ${y}px)`; 145 | } 146 | 147 | if (isFootDroging) { 148 | const height = lastHeight + event.pageY - lastPageY; 149 | if (height >= 100) { 150 | this.$danmu.style.height = `${height}px`; 151 | } else { 152 | isFootDroging = false; 153 | } 154 | } 155 | 156 | if (event.composedPath().indexOf(this.$danmuku) > -1) { 157 | this.isHover = true; 158 | } else { 159 | this.isHover = false; 160 | } 161 | }); 162 | 163 | document.addEventListener('mouseup', event => { 164 | if (isHeadDroging) { 165 | isHeadDroging = false; 166 | this.$danmuku.style.transform = 'translate(0, 0)'; 167 | const x = lastLeft + event.pageX - lastPageX; 168 | const y = lastTop + event.pageY - lastPageY; 169 | this.$danmuku.style.left = `${x}px`; 170 | this.$danmuku.style.top = `${y}px`; 171 | } 172 | 173 | if (isFootDroging) { 174 | isFootDroging = false; 175 | } 176 | }); 177 | 178 | this.$headR.addEventListener('click', () => { 179 | this.$danmuku.style.display = 'none'; 180 | this.$icon.style.display = 'block'; 181 | }); 182 | 183 | this.$icon.addEventListener('click', () => { 184 | this.$danmuku.style.display = 'block'; 185 | this.$icon.style.display = 'none'; 186 | }); 187 | } 188 | 189 | addDanmu(danmu) { 190 | const { children } = this.$danmuInner; 191 | if (children.length > MAX_DANMU) { 192 | const child = children[0]; 193 | query('.blh-danmu-uname', child).innerText = `${danmu.uname}:`; 194 | query('.blh-danmu-text', child).innerText = danmu.text; 195 | this.$danmuInner.appendChild(child); 196 | } else { 197 | this.$danmuInner.insertAdjacentHTML( 198 | 'beforeend', 199 | ` 200 |
201 | ${danmu.uname}: 202 | ${danmu.text} 203 |
204 | `, 205 | ); 206 | } 207 | if (!this.isHover) { 208 | clearTimeout(this.danmuTimer); 209 | this.danmuTimer = setTimeout(() => { 210 | this.$danmu.scrollTo(0, this.$danmu.scrollHeight); 211 | }, 100); 212 | } 213 | } 214 | 215 | addGift(gift) { 216 | const { children } = this.$giftInner; 217 | if (children.length > MAX_DANMU) { 218 | const child = children[0]; 219 | query('.blh-gift-uname', child).innerText = `${gift.uname}:`; 220 | query('.blh-gift-text', child).innerText = `${gift.action} ${gift.gift} X ${gift.num}`; 221 | this.$giftInner.appendChild(child); 222 | } else { 223 | this.$giftInner.insertAdjacentHTML( 224 | 'beforeend', 225 | ` 226 |
227 | ${gift.uname}: 228 | ${gift.action} ${gift.gift} X ${gift.num} 229 |
230 | `, 231 | ); 232 | } 233 | if (!this.isHover) { 234 | clearTimeout(this.giftTimer); 235 | this.giftTimer = setTimeout(() => { 236 | this.$gift.scrollTo(0, this.$gift.scrollHeight); 237 | }, 100); 238 | } 239 | } 240 | } 241 | 242 | export default new Content(); 243 | -------------------------------------------------------------------------------- /packages/client/src/popup/dev/index.js: -------------------------------------------------------------------------------- 1 | import './index.scss'; 2 | import { 3 | debug, 4 | query, 5 | runCss, 6 | openTab, 7 | runScript, 8 | insertCSS, 9 | getStorage, 10 | setStorage, 11 | sendMessage, 12 | getActiveTab, 13 | storageChange, 14 | injectedScript, 15 | getCapturedTab, 16 | sendMessageToTab, 17 | } from '../../share'; 18 | import { 19 | STOP, 20 | START, 21 | DEBUG, 22 | CONFIG, 23 | REG_RTMP, 24 | REG_HTTP, 25 | REG_LIVE, 26 | RECORDING, 27 | RTMP_ERROR, 28 | DANMU_OPTION, 29 | SOCKET_ERROR, 30 | OPEN_SUCCESS, 31 | CURRENT_PAGE, 32 | DEFAULT_SOCKET, 33 | PUSH_STREAM_END, 34 | LIVE_ROOM_ERROR, 35 | INJECTED_SUCCESS, 36 | CAN_NOT_FIND_TAB, 37 | STREAM_NAME_ERROR, 38 | DEFAULT_RESOLUTION, 39 | DEFAULT_VIDEO_BITSPER, 40 | } from '../../share/constant'; 41 | 42 | class Popup { 43 | constructor() { 44 | this.manifest = chrome.runtime.getManifest(); 45 | this.$container = query('.container'); 46 | this.$name = query('.name'); 47 | this.$feedback = query('.feedback'); 48 | this.$liveSetting = query('.liveSetting'); 49 | this.$socketSetting = query('.socketSetting'); 50 | this.$name.textContent = `${this.manifest.name} ${this.manifest.version}`; 51 | 52 | this.$rtmp = query('.rtmp'); 53 | this.$streamname = query('.streamname'); 54 | this.$socket = query('.socket'); 55 | this.$live = query('.live'); 56 | this.$resolution = query('.resolution'); 57 | this.$videoBitsPerSecond = query('.videoBitsPerSecond'); 58 | this.$debug = query('.debug'); 59 | this.$start = query('.start'); 60 | this.$stop = query('.stop'); 61 | 62 | this.init(); 63 | this.bindEvent(); 64 | this.updateDebug(); 65 | this.updateRecording(); 66 | storageChange(changes => { 67 | if (changes[DEBUG]) { 68 | this.updateDebug(); 69 | } 70 | if (changes[RECORDING]) { 71 | this.updateRecording(); 72 | } 73 | }); 74 | } 75 | 76 | async bindEvent() { 77 | this.$name.addEventListener('click', () => { 78 | openTab(`https://chrome.google.com/webstore/detail/${chrome.runtime.id}`); 79 | }); 80 | 81 | this.$feedback.addEventListener('click', () => { 82 | openTab('https://github.com/zhw2590582/bilibili-live-hime'); 83 | }); 84 | 85 | this.$liveSetting.addEventListener('click', () => { 86 | openTab('https://link.bilibili.com/p/center/index#/my-room/start-live'); 87 | }); 88 | 89 | this.$socketSetting.addEventListener('click', () => { 90 | openTab('https://github.com/zhw2590582/bilibili-live-hime#%E6%9C%8D%E5%8A%A1%E7%AB%AF'); 91 | }); 92 | 93 | this.$rtmp.addEventListener('input', () => { 94 | this.saveInput('rtmp'); 95 | }); 96 | 97 | this.$streamname.addEventListener('input', () => { 98 | this.saveInput('streamname'); 99 | }); 100 | 101 | this.$socket.addEventListener('input', () => { 102 | this.saveInput('socket'); 103 | }); 104 | 105 | this.$live.addEventListener('input', () => { 106 | this.saveInput('live'); 107 | }); 108 | 109 | this.$resolution.addEventListener('change', () => { 110 | this.saveInput('resolution'); 111 | }); 112 | 113 | this.$videoBitsPerSecond.addEventListener('change', () => { 114 | this.saveInput('videoBitsPerSecond'); 115 | }); 116 | 117 | this.$start.addEventListener('click', () => { 118 | this.start(); 119 | }); 120 | 121 | this.$stop.addEventListener('click', () => { 122 | this.stop(); 123 | }); 124 | 125 | this.$container.addEventListener('dragover', event => { 126 | event.preventDefault(); 127 | }); 128 | 129 | this.$container.addEventListener('drop', async event => { 130 | this.inject(event); 131 | }); 132 | } 133 | 134 | async inject(event) { 135 | event.preventDefault(); 136 | const files = Array.from(event.dataTransfer.files); 137 | await debug.log(INJECTED_SUCCESS + files.map(f => f.name).join(',')); 138 | files.forEach(file => { 139 | const reader = new FileReader(); 140 | reader.addEventListener('load', () => { 141 | const code = reader.result; 142 | switch (file.type) { 143 | case 'text/javascript': 144 | runScript(code); 145 | break; 146 | case 'text/css': 147 | runCss(code); 148 | break; 149 | default: 150 | break; 151 | } 152 | }); 153 | reader.readAsText(file); 154 | }); 155 | } 156 | 157 | async saveInput(name) { 158 | const config = (await getStorage(CONFIG)) || {}; 159 | config[name] = this[`$${name}`].value.trim(); 160 | await setStorage(CONFIG, config); 161 | } 162 | 163 | async init() { 164 | const recording = await getStorage(RECORDING); 165 | const config = (await getStorage(CONFIG)) || {}; 166 | const danmu_option = await getStorage(DANMU_OPTION); 167 | const capturedTab = await getCapturedTab(); 168 | 169 | if (config) { 170 | this.$rtmp.value = config.rtmp || ''; 171 | this.$live.value = config.live || ''; 172 | this.$streamname.value = config.streamname || ''; 173 | this.$socket.value = config.socket || DEFAULT_SOCKET; 174 | this.$resolution.value = config.resolution || DEFAULT_RESOLUTION; 175 | this.$videoBitsPerSecond.value = config.videoBitsPerSecond || DEFAULT_VIDEO_BITSPER; 176 | } 177 | 178 | if (recording && capturedTab) { 179 | if (danmu_option && config.activeTab) { 180 | await injectedScript(config.activeTab, 'active/index.js'); 181 | await insertCSS(config.activeTab, 'active/index.css'); 182 | sendMessageToTab(config.activeTab, danmu_option); 183 | } 184 | } else { 185 | debug.clean(); 186 | sendMessage({ 187 | type: STOP, 188 | }); 189 | } 190 | } 191 | 192 | async updateDebug() { 193 | const logs = (await getStorage(DEBUG)) || []; 194 | this.$debug.innerHTML = logs.map(item => `

${item.data}

`).join(''); 195 | this.$debug.scrollTo(0, this.$debug.scrollHeight); 196 | } 197 | 198 | async updateRecording() { 199 | const recording = await getStorage(RECORDING); 200 | const capturedTab = await getCapturedTab(); 201 | if (recording && capturedTab) { 202 | this.$container.classList.add(RECORDING); 203 | this.$rtmp.disabled = true; 204 | this.$streamname.disabled = true; 205 | this.$socket.disabled = true; 206 | this.$live.disabled = true; 207 | this.$resolution.disabled = true; 208 | this.$videoBitsPerSecond.disabled = true; 209 | } else { 210 | this.$container.classList.remove(RECORDING); 211 | this.$rtmp.disabled = false; 212 | this.$streamname.disabled = false; 213 | this.$socket.disabled = false; 214 | this.$live.disabled = false; 215 | this.$resolution.disabled = false; 216 | this.$videoBitsPerSecond.disabled = false; 217 | } 218 | } 219 | 220 | async start() { 221 | const activeTab = await getActiveTab(); 222 | 223 | if (!activeTab) { 224 | await debug.err(CAN_NOT_FIND_TAB); 225 | return; 226 | } 227 | 228 | const config = { 229 | activeTab: activeTab.id, 230 | rtmp: this.$rtmp.value.trim(), 231 | streamname: this.$streamname.value.trim(), 232 | socket: this.$socket.value.trim(), 233 | live: this.$live.value.trim(), 234 | resolution: Number(this.$resolution.value), 235 | videoBitsPerSecond: Number(this.$videoBitsPerSecond.value), 236 | }; 237 | 238 | if (!config.rtmp || !REG_RTMP.test(config.rtmp)) { 239 | await debug.err(RTMP_ERROR); 240 | return; 241 | } 242 | 243 | if (!config.streamname) { 244 | await debug.err(STREAM_NAME_ERROR); 245 | return; 246 | } 247 | 248 | if (!config.socket || !REG_HTTP.test(config.socket)) { 249 | await debug.err(SOCKET_ERROR); 250 | return; 251 | } 252 | 253 | if (config.live) { 254 | if (REG_LIVE.test(config.live)) { 255 | await injectedScript(config.activeTab, 'active/index.js'); 256 | await insertCSS(config.activeTab, 'active/index.css'); 257 | const liveTab = await openTab(config.live, false); 258 | config.liveTab = liveTab.id; 259 | await debug.log(OPEN_SUCCESS); 260 | } else { 261 | await debug.err(LIVE_ROOM_ERROR); 262 | return; 263 | } 264 | } 265 | 266 | await debug.log(`${CURRENT_PAGE}:${activeTab.title}`); 267 | await setStorage(CONFIG, config); 268 | sendMessage({ 269 | type: START, 270 | data: config, 271 | }); 272 | } 273 | 274 | async stop() { 275 | await debug.log(PUSH_STREAM_END); 276 | sendMessage({ 277 | type: STOP, 278 | }); 279 | } 280 | } 281 | 282 | export default new Popup(); 283 | -------------------------------------------------------------------------------- /packages/client/src/background/dev/index.js: -------------------------------------------------------------------------------- 1 | import 'crx-hotreload'; 2 | import io from 'socket.io-client/dist/socket.io'; 3 | import { 4 | sleep, 5 | debug, 6 | setBadge, 7 | removeTab, 8 | onMessage, 9 | setStorage, 10 | getStorage, 11 | storageChange, 12 | injectedScript, 13 | sendMessageToTab, 14 | } from '../../share'; 15 | import { 16 | LOG, 17 | FAIL, 18 | RTMP, 19 | STOP, 20 | START, 21 | DANMU_ING, 22 | MIME_TYPE, 23 | RECORDING, 24 | RECONNECT, 25 | DANMU_FAIL, 26 | DANMU_ERROR, 27 | SOCKET_FAIL, 28 | DANMU_OPTION, 29 | DANMU_SUCCESS, 30 | BINARY_STREAM, 31 | RECORDER_FAIL, 32 | RECONNECT_TIME, 33 | SOCKET_SUCCESS, 34 | RECONNECT_INFO, 35 | PUSH_STREAM_ING, 36 | RECORDER_SUCCESS, 37 | STREAM_DISCONNECT, 38 | DEFAULT_RESOLUTION, 39 | TAB_VIDEO_STREAM_FAIL, 40 | DEFAULT_VIDEO_BITSPER, 41 | DEFAULT_AUDIO_BITSPER, 42 | TAB_VIDEO_STREAM_SUCCESS, 43 | } from '../../share/constant'; 44 | 45 | class Background { 46 | constructor() { 47 | this.stream = null; 48 | this.socket = null; 49 | this.reconnect = 0; 50 | this.mediaRecorder = null; 51 | this.config = Background.Config; 52 | 53 | onMessage(async (request, sender) => { 54 | const { type, data } = request; 55 | switch (type) { 56 | case START: 57 | this.config = { 58 | ...Background.Config, 59 | ...data, 60 | }; 61 | await this.start(); 62 | break; 63 | case STOP: 64 | await this.stop(); 65 | break; 66 | case DANMU_ERROR: { 67 | if (this.config && this.config.liveTab) { 68 | await removeTab(this.config.liveTab); 69 | await debug.err(DANMU_FAIL); 70 | } 71 | break; 72 | } 73 | case DANMU_OPTION: 74 | if (this.config && sender) { 75 | const { activeTab, liveTab } = this.config; 76 | const { tab } = sender; 77 | if (activeTab && liveTab && tab && liveTab === tab.id) { 78 | await setStorage(DANMU_OPTION, request); 79 | sendMessageToTab(activeTab, request); 80 | await removeTab(liveTab); 81 | await debug.log(DANMU_SUCCESS); 82 | } 83 | } 84 | break; 85 | default: 86 | break; 87 | } 88 | }); 89 | 90 | storageChange(async changes => { 91 | if (changes[RECORDING]) { 92 | if (changes[RECORDING].newValue) { 93 | await setBadge('ON'); 94 | } else { 95 | await setBadge(''); 96 | } 97 | } 98 | }); 99 | } 100 | 101 | static get Config() { 102 | return { 103 | rtmp: '', 104 | socket: '', 105 | liveTab: null, 106 | streamname: '', 107 | activeTab: null, 108 | resolution: DEFAULT_RESOLUTION, 109 | videoBitsPerSecond: DEFAULT_VIDEO_BITSPER, 110 | }; 111 | } 112 | 113 | static get CaptureOptions() { 114 | return { 115 | audio: true, 116 | video: true, 117 | videoConstraints: { 118 | mandatory: { 119 | chromeMediaSource: 'tab', 120 | maxWidth: 1280, 121 | minWidth: 1280, 122 | maxHeight: 720, 123 | minHeight: 720, 124 | }, 125 | }, 126 | audioConstraints: { 127 | mandatory: { 128 | echoCancellation: true, 129 | }, 130 | }, 131 | }; 132 | } 133 | 134 | static get RecorderOptions() { 135 | return { 136 | audioBitsPerSecond: DEFAULT_AUDIO_BITSPER, 137 | videoBitsPerSecond: DEFAULT_VIDEO_BITSPER, 138 | mimeType: MIME_TYPE, 139 | }; 140 | } 141 | 142 | static get Resolution() { 143 | return { 144 | 1920: { 145 | width: 1920, 146 | height: 1080, 147 | }, 148 | 720: { 149 | width: 1280, 150 | height: 720, 151 | }, 152 | 480: { 153 | width: 640, 154 | height: 480, 155 | }, 156 | 360: { 157 | width: 640, 158 | height: 360, 159 | }, 160 | 240: { 161 | width: 320, 162 | height: 240, 163 | }, 164 | }; 165 | } 166 | 167 | connectSocket(url) { 168 | return new Promise((revolve, reject) => { 169 | const socket = io(url); 170 | 171 | socket.on('connect_error', error => { 172 | reject(error); 173 | }); 174 | 175 | socket.on('connect', () => { 176 | revolve(socket); 177 | }); 178 | }); 179 | } 180 | 181 | tabCapture(res) { 182 | return new Promise((revolve, reject) => { 183 | const captureOptions = Background.CaptureOptions; 184 | const resolution = Background.Resolution[res]; 185 | captureOptions.videoConstraints.mandatory.maxWidth = resolution.width; 186 | captureOptions.videoConstraints.mandatory.minWidth = resolution.width; 187 | captureOptions.videoConstraints.mandatory.maxHeight = resolution.height; 188 | captureOptions.videoConstraints.mandatory.minHeight = resolution.height; 189 | chrome.tabCapture.capture(captureOptions, stream => { 190 | if (stream) { 191 | revolve(stream); 192 | } else { 193 | reject(); 194 | } 195 | }); 196 | }); 197 | } 198 | 199 | recorder(stream, videoBitsPerSecond) { 200 | return new Promise((revolve, reject) => { 201 | const recorderOptions = Background.RecorderOptions; 202 | recorderOptions.videoBitsPerSecond = videoBitsPerSecond; 203 | if (MediaRecorder && MediaRecorder.isTypeSupported(recorderOptions.mimeType)) { 204 | const mediaRecorder = new MediaRecorder(stream, recorderOptions); 205 | revolve(mediaRecorder); 206 | } else { 207 | reject(); 208 | } 209 | }); 210 | } 211 | 212 | async start() { 213 | const { socket, rtmp, liveTab, streamname, resolution, videoBitsPerSecond } = this.config; 214 | 215 | if (liveTab) { 216 | await debug.log(DANMU_ING); 217 | await injectedScript(liveTab, 'danmu/index.js'); 218 | } 219 | 220 | try { 221 | const rtmpFullUrl = rtmp + streamname; 222 | this.socket = await this.connectSocket(socket); 223 | await debug.log(SOCKET_SUCCESS); 224 | // 告知服务器命令:开启ffmpeg进程 225 | this.socket.emit(RTMP, rtmpFullUrl); 226 | // 来自服务器命令:打印 227 | this.socket.on(LOG, async info => { 228 | await debug.log(info); 229 | }); 230 | // 来自服务器命令:重连 231 | this.socket.on(RECONNECT, async info => { 232 | await debug.err(info); 233 | const recording1 = await getStorage(RECORDING); 234 | if (this.reconnect >= RECONNECT_TIME || !recording1) { 235 | await this.stop(); 236 | } else { 237 | this.reconnect += 1; 238 | await debug.log(RECONNECT_INFO + this.reconnect); 239 | await sleep(3000); 240 | const recording2 = await getStorage(RECORDING); 241 | if (recording2) { 242 | this.socket.emit(RTMP, rtmpFullUrl); 243 | } else { 244 | await this.stop(); 245 | } 246 | } 247 | }); 248 | // 来自服务器命令:终止 249 | this.socket.on(FAIL, async info => { 250 | await debug.err(info); 251 | await this.stop(); 252 | }); 253 | } catch (error) { 254 | await debug.err(`${SOCKET_FAIL}: ${error.message.trim()}`); 255 | await this.stop(); 256 | return; 257 | } 258 | 259 | try { 260 | this.stream = await this.tabCapture(resolution); 261 | await debug.log(TAB_VIDEO_STREAM_SUCCESS); 262 | } catch (error) { 263 | await debug.err(TAB_VIDEO_STREAM_FAIL); 264 | await this.stop(); 265 | return; 266 | } 267 | 268 | try { 269 | this.mediaRecorder = await this.recorder(this.stream, videoBitsPerSecond); 270 | await debug.log(RECORDER_SUCCESS); 271 | this.mediaRecorder.ondataavailable = event => { 272 | if (event.data && event.data.size > 0) { 273 | // 告知服务器命令:推流 274 | this.socket.emit(BINARY_STREAM, event.data, rtmp); 275 | } 276 | }; 277 | this.mediaRecorder.start(1000); 278 | } catch (error) { 279 | await debug.err(RECORDER_FAIL); 280 | await this.stop(); 281 | } 282 | 283 | await debug.log(PUSH_STREAM_ING); 284 | await setStorage(RECORDING, true); 285 | } 286 | 287 | async stop() { 288 | this.reconnect = 0; 289 | this.config = Background.Config; 290 | await setStorage(RECORDING, false); 291 | await setStorage(DANMU_OPTION, false); 292 | 293 | if (this.stream) { 294 | this.stream.getTracks().forEach(track => track.stop()); 295 | } 296 | 297 | if (this.socket) { 298 | this.socket.emit(STREAM_DISCONNECT); 299 | this.socket.close(); 300 | } 301 | 302 | if (this.mediaRecorder && this.mediaRecorder.state === 'recording') { 303 | this.mediaRecorder.stop(); 304 | } 305 | } 306 | } 307 | 308 | export default new Background(); 309 | -------------------------------------------------------------------------------- /packages/client/dist/bilibili-live-hime-client/popup/index.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * bilibili-live-hime v1.0.3 3 | * Github: https://github.com/zhw2590582/bilibili-live-hime 4 | * (c) 2018-2019 Harvey Zack 5 | * Released under the MIT License. 6 | */ 7 | 8 | var BilibiliLiveHimePopup=function(){"use strict";var t=function(t,e){return t(e={exports:{}},e.exports),e.exports}((function(t){var e=function(t){var e,n=Object.prototype,r=n.hasOwnProperty,i="function"==typeof Symbol?Symbol:{},a=i.iterator||"@@iterator",o=i.asyncIterator||"@@asyncIterator",c=i.toStringTag||"@@toStringTag";function s(t,e,n,r){var i=e&&e.prototype instanceof v?e:v,a=Object.create(i.prototype),o=new P(r||[]);return a._invoke=function(t,e,n){var r=h;return function(i,a){if(r===f)throw new Error("Generator is already running");if(r===p){if("throw"===i)throw a;return T()}for(n.method=i,n.arg=a;;){var o=n.delegate;if(o){var c=L(o,n);if(c){if(c===d)continue;return c}}if("next"===n.method)n.sent=n._sent=n.arg;else if("throw"===n.method){if(r===h)throw r=p,n.arg;n.dispatchException(n.arg)}else"return"===n.method&&n.abrupt("return",n.arg);r=f;var s=u(t,e,n);if("normal"===s.type){if(r=n.done?p:l,s.arg===d)continue;return{value:s.arg,done:n.done}}"throw"===s.type&&(r=p,n.method="throw",n.arg=s.arg)}}}(t,n,o),a}function u(t,e,n){try{return{type:"normal",arg:t.call(e,n)}}catch(t){return{type:"throw",arg:t}}}t.wrap=s;var h="suspendedStart",l="suspendedYield",f="executing",p="completed",d={};function v(){}function m(){}function y(){}var b={};b[a]=function(){return this};var w=Object.getPrototypeOf,g=w&&w(w(j([])));g&&g!==n&&r.call(g,a)&&(b=g);var x=y.prototype=v.prototype=Object.create(b);function k(t){["next","throw","return"].forEach((function(e){t[e]=function(t){return this._invoke(e,t)}}))}function $(t){var e;this._invoke=function(n,i){function a(){return new Promise((function(e,a){!function e(n,i,a,o){var c=u(t[n],t,i);if("throw"!==c.type){var s=c.arg,h=s.value;return h&&"object"==typeof h&&r.call(h,"__await")?Promise.resolve(h.__await).then((function(t){e("next",t,a,o)}),(function(t){e("throw",t,a,o)})):Promise.resolve(h).then((function(t){s.value=t,a(s)}),(function(t){return e("throw",t,a,o)}))}o(c.arg)}(n,i,e,a)}))}return e=e?e.then(a,a):a()}}function L(t,n){var r=t.iterator[n.method];if(r===e){if(n.delegate=null,"throw"===n.method){if(t.iterator.return&&(n.method="return",n.arg=e,L(t,n),"throw"===n.method))return d;n.method="throw",n.arg=new TypeError("The iterator does not provide a 'throw' method")}return d}var i=u(r,t.iterator,n.arg);if("throw"===i.type)return n.method="throw",n.arg=i.arg,n.delegate=null,d;var a=i.arg;return a?a.done?(n[t.resultName]=a.value,n.next=t.nextLoc,"return"!==n.method&&(n.method="next",n.arg=e),n.delegate=null,d):a:(n.method="throw",n.arg=new TypeError("iterator result is not an object"),n.delegate=null,d)}function E(t){var e={tryLoc:t[0]};1 in t&&(e.catchLoc=t[1]),2 in t&&(e.finallyLoc=t[2],e.afterLoc=t[3]),this.tryEntries.push(e)}function S(t){var e=t.completion||{};e.type="normal",delete e.arg,t.completion=e}function P(t){this.tryEntries=[{tryLoc:"root"}],t.forEach(E,this),this.reset(!0)}function j(t){if(t){var n=t[a];if(n)return n.call(t);if("function"==typeof t.next)return t;if(!isNaN(t.length)){var i=-1,o=function n(){for(;++i=0;--a){var o=this.tryEntries[a],c=o.completion;if("root"===o.tryLoc)return i("end");if(o.tryLoc<=this.prev){var s=r.call(o,"catchLoc"),u=r.call(o,"finallyLoc");if(s&&u){if(this.prev=0;--n){var i=this.tryEntries[n];if(i.tryLoc<=this.prev&&r.call(i,"finallyLoc")&&this.prev=0;--e){var n=this.tryEntries[e];if(n.finallyLoc===t)return this.complete(n.completion,n.afterLoc),S(n),d}},catch:function(t){for(var e=this.tryEntries.length-1;e>=0;--e){var n=this.tryEntries[e];if(n.tryLoc===t){var r=n.completion;if("throw"===r.type){var i=r.arg;S(n)}return i}}throw new Error("illegal catch attempt")},delegateYield:function(t,n,r){return this.delegate={iterator:j(t),resultName:n,nextLoc:r},"next"===this.method&&(this.arg=e),d}},t}(t.exports);try{regeneratorRuntime=e}catch(t){Function("r","regeneratorRuntime = r")(e)}}));var e=function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")};function n(t,e){for(var n=0;nt(e)).join(", ")+"]";case"object":return"{ "+Object.keys(e).map(n=>n+": "+t(e[n])).join(", ")+" }";default:try{return e.toString()}catch(t){return"[Unknown type: "+n+"]"}}default:return e.toString()}},o="recording",c="debug",s=/^rtmp:\/\/.+/i,u=/^https?:\/\/.+/i,h=/^https?:\/\/live\.bilibili\.com/i;function l(t){return(arguments.length>1&&void 0!==arguments[1]?arguments[1]:document).querySelector(t)}function f(t,e){return new Promise((function(n){chrome.storage.local.set(i({},t,e),(function(){n(e)}))}))}function p(t,e){return new Promise((function(n){chrome.storage.local.get([t],(function(r){!function(t,e){return Object.prototype.hasOwnProperty.call(t,e)}(r,t)?e?f(t,e).then((function(t){n(t)})):n():n(r[t])}))}))}function d(t){var e=!(arguments.length>1&&void 0!==arguments[1])||arguments[1];return new Promise((function(n){chrome.tabs.create({url:t,active:e},(function(t){n(t)}))}))}function v(){return new Promise((function(t){chrome.tabCapture.getCapturedTabs((function(e){t(e[0])}))}))}var m=function(e){var n;return t.async((function(r){for(;;)switch(r.prev=r.next){case 0:return r.next=2,t.awrap(p(c));case 2:if(r.t0=r.sent,r.t0){r.next=5;break}r.t0=[];case 5:return(n=r.t0).push({type:"log",data:a(e)}),r.next=9,t.awrap(f(c,n));case 9:case"end":return r.stop()}}))},y=function(e){var n;return t.async((function(r){for(;;)switch(r.prev=r.next){case 0:return r.next=2,t.awrap(p(c));case 2:if(r.t0=r.sent,r.t0){r.next=5;break}r.t0=[];case 5:return(n=r.t0).push({type:"error",data:a(e)}),r.next=9,t.awrap(f(c,n));case 9:case"end":return r.stop()}}))},b=function(){return t.async((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,t.awrap(f(c,[]));case 2:case"end":return e.stop()}}))};function w(t){chrome.runtime.sendMessage(t)}function g(t,e){return new Promise((function(n){chrome.tabs.executeScript(t,{file:e,runAt:"document_start"},(function(){n()}))}))}function x(t){return new Promise((function(e){chrome.tabs.executeScript({code:t},(function(){e()}))}))}function k(t,e){return new Promise((function(n){chrome.tabs.insertCSS(t,{file:e},(function(){n()}))}))}function $(t){return new Promise((function(e){chrome.tabs.insertCSS({code:t},(function(){e()}))}))}return new(function(){function n(){var t,r=this;e(this,n),this.manifest=chrome.runtime.getManifest(),this.$container=l(".container"),this.$name=l(".name"),this.$feedback=l(".feedback"),this.$liveSetting=l(".liveSetting"),this.$socketSetting=l(".socketSetting"),this.$name.textContent="".concat(this.manifest.name," ").concat(this.manifest.version),this.$rtmp=l(".rtmp"),this.$streamname=l(".streamname"),this.$socket=l(".socket"),this.$live=l(".live"),this.$resolution=l(".resolution"),this.$videoBitsPerSecond=l(".videoBitsPerSecond"),this.$debug=l(".debug"),this.$start=l(".start"),this.$stop=l(".stop"),this.init(),this.bindEvent(),this.updateDebug(),this.updateRecording(),t=function(t){t[c]&&r.updateDebug(),t[o]&&r.updateRecording()},chrome.storage.onChanged.addListener(t)}return r(n,[{key:"bindEvent",value:function(){var e=this;return t.async((function(n){for(;;)switch(n.prev=n.next){case 0:this.$name.addEventListener("click",(function(){d("https://chrome.google.com/webstore/detail/".concat(chrome.runtime.id))})),this.$feedback.addEventListener("click",(function(){d("https://github.com/zhw2590582/bilibili-live-hime")})),this.$liveSetting.addEventListener("click",(function(){d("https://link.bilibili.com/p/center/index#/my-room/start-live")})),this.$socketSetting.addEventListener("click",(function(){d("https://github.com/zhw2590582/bilibili-live-hime#%E6%9C%8D%E5%8A%A1%E7%AB%AF")})),this.$rtmp.addEventListener("input",(function(){e.saveInput("rtmp")})),this.$streamname.addEventListener("input",(function(){e.saveInput("streamname")})),this.$socket.addEventListener("input",(function(){e.saveInput("socket")})),this.$live.addEventListener("input",(function(){e.saveInput("live")})),this.$resolution.addEventListener("change",(function(){e.saveInput("resolution")})),this.$videoBitsPerSecond.addEventListener("change",(function(){e.saveInput("videoBitsPerSecond")})),this.$start.addEventListener("click",(function(){e.start()})),this.$stop.addEventListener("click",(function(){e.stop()})),this.$container.addEventListener("dragover",(function(t){t.preventDefault()})),this.$container.addEventListener("drop",(function(n){return t.async((function(t){for(;;)switch(t.prev=t.next){case 0:e.inject(n);case 1:case"end":return t.stop()}}))}));case 14:case"end":return n.stop()}}),null,this)}},{key:"inject",value:function(e){var n;return t.async((function(r){for(;;)switch(r.prev=r.next){case 0:return e.preventDefault(),n=Array.from(e.dataTransfer.files),r.next=4,t.awrap(m("注入文件:"+n.map((function(t){return t.name})).join(",")));case 4:n.forEach((function(t){var e=new FileReader;e.addEventListener("load",(function(){var n=e.result;switch(t.type){case"text/javascript":x(n);break;case"text/css":$(n)}})),e.readAsText(t)}));case 5:case"end":return r.stop()}}))}},{key:"saveInput",value:function(e){var n;return t.async((function(r){for(;;)switch(r.prev=r.next){case 0:return r.next=2,t.awrap(p("config"));case 2:if(r.t0=r.sent,r.t0){r.next=5;break}r.t0={};case 5:return(n=r.t0)[e]=this["$".concat(e)].value.trim(),r.next=9,t.awrap(f("config",n));case 9:case"end":return r.stop()}}),null,this)}},{key:"init",value:function(){var e,n,r,i;return t.async((function(a){for(;;)switch(a.prev=a.next){case 0:return a.next=2,t.awrap(p(o));case 2:return e=a.sent,a.next=5,t.awrap(p("config"));case 5:if(a.t0=a.sent,a.t0){a.next=8;break}a.t0={};case 8:return n=a.t0,a.next=11,t.awrap(p("danmu_option"));case 11:return r=a.sent,a.next=14,t.awrap(v());case 14:if(i=a.sent,n&&(this.$rtmp.value=n.rtmp||"",this.$live.value=n.live||"",this.$streamname.value=n.streamname||"",this.$socket.value=n.socket||"http://localhost:8080",this.$resolution.value=n.resolution||720,this.$videoBitsPerSecond.value=n.videoBitsPerSecond||25e5),!e||!i){a.next=25;break}if(!r||!n.activeTab){a.next=23;break}return a.next=20,t.awrap(g(n.activeTab,"active/index.js"));case 20:return a.next=22,t.awrap(k(n.activeTab,"active/index.css"));case 22:c=n.activeTab,s=r,chrome.tabs.sendMessage(c,s);case 23:a.next=27;break;case 25:b(),w({type:"stop"});case 27:case"end":return a.stop()}var c,s}),null,this)}},{key:"updateDebug",value:function(){var e;return t.async((function(n){for(;;)switch(n.prev=n.next){case 0:return n.next=2,t.awrap(p(c));case 2:if(n.t0=n.sent,n.t0){n.next=5;break}n.t0=[];case 5:e=n.t0,this.$debug.innerHTML=e.map((function(t){return'

').concat(t.data,"

")})).join(""),this.$debug.scrollTo(0,this.$debug.scrollHeight);case 8:case"end":return n.stop()}}),null,this)}},{key:"updateRecording",value:function(){var e,n;return t.async((function(r){for(;;)switch(r.prev=r.next){case 0:return r.next=2,t.awrap(p(o));case 2:return e=r.sent,r.next=5,t.awrap(v());case 5:n=r.sent,e&&n?(this.$container.classList.add(o),this.$rtmp.disabled=!0,this.$streamname.disabled=!0,this.$socket.disabled=!0,this.$live.disabled=!0,this.$resolution.disabled=!0,this.$videoBitsPerSecond.disabled=!0):(this.$container.classList.remove(o),this.$rtmp.disabled=!1,this.$streamname.disabled=!1,this.$socket.disabled=!1,this.$live.disabled=!1,this.$resolution.disabled=!1,this.$videoBitsPerSecond.disabled=!1);case 7:case"end":return r.stop()}}),null,this)}},{key:"start",value:function(){var e,n,r;return t.async((function(i){for(;;)switch(i.prev=i.next){case 0:return i.next=2,t.awrap(new Promise((function(t){chrome.tabs.query({active:!0,currentWindow:!0},(function(e){t(e[0])}))})));case 2:if(e=i.sent){i.next=7;break}return i.next=6,t.awrap(y("未获取到当前激活的标签"));case 6:return i.abrupt("return");case 7:if((n={activeTab:e.id,rtmp:this.$rtmp.value.trim(),streamname:this.$streamname.value.trim(),socket:this.$socket.value.trim(),live:this.$live.value.trim(),resolution:Number(this.$resolution.value),videoBitsPerSecond:Number(this.$videoBitsPerSecond.value)}).rtmp&&s.test(n.rtmp)){i.next=12;break}return i.next=11,t.awrap(y("请输入正确的rtmp推流地址"));case 11:return i.abrupt("return");case 12:if(n.streamname){i.next=16;break}return i.next=15,t.awrap(y("请输入正确的直播码"));case 15:return i.abrupt("return");case 16:if(n.socket&&u.test(n.socket)){i.next=20;break}return i.next=19,t.awrap(y("请输入正确的中转地址"));case 19:return i.abrupt("return");case 20:if(!n.live){i.next=37;break}if(!h.test(n.live)){i.next=34;break}return i.next=24,t.awrap(g(n.activeTab,"active/index.js"));case 24:return i.next=26,t.awrap(k(n.activeTab,"active/index.css"));case 26:return i.next=28,t.awrap(d(n.live,!1));case 28:return r=i.sent,n.liveTab=r.id,i.next=32,t.awrap(m("打开直播间页面成功"));case 32:i.next=37;break;case 34:return i.next=36,t.awrap(y("不是有效的 Bilibili 直播间地址"));case 36:return i.abrupt("return");case 37:return i.next=39,t.awrap(m("".concat("当前页面",":").concat(e.title)));case 39:return i.next=41,t.awrap(f("config",n));case 41:w({type:"start",data:n});case 42:case"end":return i.stop()}}),null,this)}},{key:"stop",value:function(){return t.async((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,t.awrap(m("已停止推流..."));case 2:w({type:"stop"});case 3:case"end":return e.stop()}}))}}]),n}())}(); 9 | -------------------------------------------------------------------------------- /packages/client/src/active/dev/DanmakuWebSocket.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bilibili HTML5 Live Player v1.9.2 (304-54f7b3b9) 3 | * 4 | * Copyright 2016 - 2019 bilibili, Inc. 5 | * Released in Tue Dec 10 2019 20:56:25 GMT+0800 (GMT+08:00) 6 | */ 7 | 8 | /* eslint-disable */ 9 | !function(e){var t={};function n(i){if(t[i])return t[i].exports;var r=t[i]={i:i,l:!1,exports:{}};return e[i].call(r.exports,r,r.exports,n),r.l=!0,r.exports}n.m=e,n.c=t,n.d=function(e,t,i){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:i})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var i=Object.create(null);if(n.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)n.d(i,r,function(t){return e[t]}.bind(null,r));return i},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=22)}({22:function(e,t,n){window.DanmakuWebSocket=n(25)},25:function(e,t){var n,i;n="undefined"!=typeof self?self:this,i=function(){return function(e){function t(i){if(n[i])return n[i].exports;var r=n[i]={i:i,l:!1,exports:{}};return e[i].call(r.exports,r,r.exports,t),r.l=!0,r.exports}var n={};return t.m=e,t.c=n,t.d=function(e,n,i){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:i})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="",t(t.s=1)}([function(e,t,n){"use strict";t.a={WS_OP_HEARTBEAT:2,WS_OP_HEARTBEAT_REPLY:3,WS_OP_MESSAGE:5,WS_OP_USER_AUTHENTICATION:7,WS_OP_CONNECT_SUCCESS:8,WS_PACKAGE_HEADER_TOTAL_LENGTH:16,WS_PACKAGE_OFFSET:0,WS_HEADER_OFFSET:4,WS_VERSION_OFFSET:6,WS_OPERATION_OFFSET:8,WS_SEQUENCE_OFFSET:12,WS_BODY_PROTOCOL_VERSION_NORMAL:0,WS_BODY_PROTOCOL_VERSION_DEFLATE:2,WS_HEADER_DEFAULT_VERSION:1,WS_HEADER_DEFAULT_OPERATION:1,WS_HEADER_DEFAULT_SEQUENCE:1,WS_AUTH_OK:0,WS_AUTH_TOKEN_ERROR:-101}},function(e,t,n){var i=n(2).default;e.exports=i},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=n(3),r=function(){function e(e,t){for(var n=0;n0?this.options.urlList[0]:this.options.url)}}return e.prototype.initialize=function(e){var t="MozWebSocket"in window?window.MozWebSocket:window.WebSocket,n=this.options;try{this.ws=new t(e),this.ws.binaryType="arraybuffer",this.ws.onopen=this.onOpen.bind(this),this.ws.onmessage=this.onMessage.bind(this),this.ws.onclose=this.onClose.bind(this),this.ws.onerror=this.onError.bind(this),o.a.callFunction(this.callbackQueueList.onInitializedQueue),this.callbackQueueList.onInitializedQueue=[]}catch(e){"function"==typeof n.fallback&&n.fallback()}return this},e.prototype.onOpen=function(){return o.a.callFunction(this.callbackQueueList.onOpenQueue),this.userAuthentication(),this},e.prototype.userAuthentication=function(){var e,t=this,n=this.options,r={uid:parseInt(n.uid,10),roomid:parseInt(n.rid,10),protover:parseInt(n.protover,10)||i.a.WS_BODY_PROTOCOL_VERSION_NORMAL};n.aid&&(r.aid=parseInt(n.aid,10)),n.from>0&&(r.from=parseInt(n.from,10)||7);for(var o=0;oe.options.retryThreadCount?setTimeout(function(){e.initialize(e.options.url)},1e3*e.options.retryRoundInterval):0!==t&&e.state.index>t-1?(e.state.index=0,e.state.listConnectFinishedCount+=1,1===e.state.listConnectFinishedCount&&o.a.callFunction(e.callbackQueueList.onListConnectErrorQueue),setTimeout(function(){e.initialize(e.options.urlList[e.state.index])},1e3*e.options.retryRoundInterval)):e.initialize(e.options.urlList[e.state.index])},1e3*this.options.retryInterval):(console.error("Danmaku Websocket Retry Failed."),o.a.callFunction(this.callbackQueueList.onRetryFallbackQueue)),this):this},e.prototype.onError=function(e){return console.error("Danmaku Websocket On Error.",e),o.a.callFunction(this.callbackQueueList.onErrorQueue,e),this},e.prototype.destroy=function(){clearTimeout(this.HEART_BEAT_INTERVAL),this.options.retry=!1,this.ws&&this.ws.close(),this.ws=null},e.prototype.convertToArrayBuffer=function(e,t){this.encoder||(this.encoder=o.a.getEncoder());var n=new ArrayBuffer(i.a.WS_PACKAGE_HEADER_TOTAL_LENGTH),r=new DataView(n,i.a.WS_PACKAGE_OFFSET),a=this.encoder.encode(e);return r.setInt32(i.a.WS_PACKAGE_OFFSET,i.a.WS_PACKAGE_HEADER_TOTAL_LENGTH+a.byteLength),this.wsBinaryHeaderList[2].value=t,this.wsBinaryHeaderList.forEach(function(e){4===e.bytes?r.setInt32(e.offset,e.value):2===e.bytes&&r.setInt16(e.offset,e.value)}),o.a.mergeArrayBuffer(n,a)},e.prototype.convertToObject=function(e){var t=new DataView(e),n={body:[]};if(n.packetLen=t.getInt32(i.a.WS_PACKAGE_OFFSET),this.wsBinaryHeaderList.forEach(function(e){4===e.bytes?n[e.key]=t.getInt32(e.offset):2===e.bytes&&(n[e.key]=t.getInt16(e.offset))}),n.packetLen1?t-1:0),i=1;i>>6:(t[a++]=n<65536?224|n>>>12:(t[a++]=240|n>>>18,128|n>>>12&63),128|n>>>6&63),128|63&n);return t},n.buf2binstring=function(e){return i(e,e.length)},n.binstring2buf=function(e){for(var t=new r.Buf8(e.length),n=0,i=t.length;n>10&1023,56320|1023&o)}return i(c,r)},n.utf8border=function(e,t){var n;for((t=t||e.length)>e.length&&(t=e.length),n=t-1;0<=n&&128==(192&e[n]);)n--;return n<0?t:0===n?t:n+s[e[n]]>t?n:t}},{"./common":1}],3:[function(e,t,n){"use strict";t.exports=function(e,t,n,i){for(var r=65535&e|0,o=e>>>16&65535|0,a=0;0!==n;){for(n-=a=2e3>>1:e>>>1;t[n]=e}return t}();t.exports=function(e,t,n,r){var o=i,a=r+n;e^=-1;for(var s=r;s>>8^o[255&(e^t[s])];return-1^e}},{}],6:[function(e,t,n){"use strict";t.exports=function(){this.text=0,this.time=0,this.xflags=0,this.os=0,this.extra=null,this.extra_len=0,this.name="",this.comment="",this.hcrc=0,this.done=!1}},{}],7:[function(e,t,n){"use strict";t.exports=function(e,t){var n,i,r,o,a,s,u,c,l,f,d,h,b,_,p,y,w,k,m,g,E,v,A,S,O;n=e.state,i=e.next_in,S=e.input,r=i+(e.avail_in-5),o=e.next_out,O=e.output,a=o-(t-e.avail_out),s=o+(e.avail_out-257),u=n.dmax,c=n.wsize,l=n.whave,f=n.wnext,d=n.window,h=n.hold,b=n.bits,_=n.lencode,p=n.distcode,y=(1<>>=m=k>>>24,b-=m,0==(m=k>>>16&255))O[o++]=65535&k;else{if(!(16&m)){if(0==(64&m)){k=_[(65535&k)+(h&(1<>>=m,b-=m),b<15&&(h+=S[i++]<>>=m=k>>>24,b-=m,!(16&(m=k>>>16&255))){if(0==(64&m)){k=p[(65535&k)+(h&(1<>>=m,b-=m,(m=o-a)>3,h&=(1<<(b-=g<<3))-1,e.next_in=i,e.next_out=o,e.avail_in=i>>24&255)+(e>>>8&65280)+((65280&e)<<8)+((255&e)<<24)}function r(e){var t;return e&&e.state?(t=e.state,e.total_in=e.total_out=t.total=0,e.msg="",t.wrap&&(e.adler=1&t.wrap),t.mode=g,t.last=0,t.havedict=0,t.dmax=32768,t.head=null,t.hold=0,t.bits=0,t.lencode=t.lendyn=new d.Buf32(E),t.distcode=t.distdyn=new d.Buf32(v),t.sane=1,t.back=-1,k):m}function o(e){var t;return e&&e.state?((t=e.state).wsize=0,t.whave=0,t.wnext=0,r(e)):m}function a(e,t){var n,i;return e&&e.state?(i=e.state,t<0?(n=0,t=-t):(n=1+(t>>4),t<48&&(t&=15)),t&&(t<8||15=o.wsize?(d.arraySet(o.window,t,n-o.wsize,o.wsize,0),o.wnext=0,o.whave=o.wsize):(i<(r=o.wsize-o.wnext)&&(r=i),d.arraySet(o.window,t,n-i,r,o.wnext),(i-=r)?(d.arraySet(o.window,t,n-i,i,0),o.wnext=i,o.whave=o.wsize):(o.wnext+=r,o.wnext===o.wsize&&(o.wnext=0),o.whave>>8&255,n.check=b(n.check,U,2,0),v=E=0,n.mode=2;break}if(n.flags=0,n.head&&(n.head.done=!1),!(1&n.wrap)||(((255&E)<<8)+(E>>8))%31){e.msg="incorrect header check",n.mode=30;break}if(8!=(15&E)){e.msg="unknown compression method",n.mode=30;break}if(v-=4,N=8+(15&(E>>>=4)),0===n.wbits)n.wbits=N;else if(N>n.wbits){e.msg="invalid window size",n.mode=30;break}n.dmax=1<>8&1),512&n.flags&&(U[0]=255&E,U[1]=E>>>8&255,n.check=b(n.check,U,2,0)),v=E=0,n.mode=3;case 3:for(;v<32;){if(0===l)break e;l--,E+=r[a++]<>>8&255,U[2]=E>>>16&255,U[3]=E>>>24&255,n.check=b(n.check,U,4,0)),v=E=0,n.mode=4;case 4:for(;v<16;){if(0===l)break e;l--,E+=r[a++]<>8),512&n.flags&&(U[0]=255&E,U[1]=E>>>8&255,n.check=b(n.check,U,2,0)),v=E=0,n.mode=5;case 5:if(1024&n.flags){for(;v<16;){if(0===l)break e;l--,E+=r[a++]<>>8&255,n.check=b(n.check,U,2,0)),v=E=0}else n.head&&(n.head.extra=null);n.mode=6;case 6:if(1024&n.flags&&(l<(O=n.length)&&(O=l),O&&(n.head&&(N=n.head.extra_len-n.length,n.head.extra||(n.head.extra=new Array(n.head.extra_len)),d.arraySet(n.head.extra,r,a,O,N)),512&n.flags&&(n.check=b(n.check,r,O,a)),l-=O,a+=O,n.length-=O),n.length))break e;n.length=0,n.mode=7;case 7:if(2048&n.flags){if(0===l)break e;for(O=0;N=r[a+O++],n.head&&N&&n.length<65536&&(n.head.name+=String.fromCharCode(N)),N&&O>9&1,n.head.done=!0),e.adler=n.check=0,n.mode=12;break;case 10:for(;v<32;){if(0===l)break e;l--,E+=r[a++]<>>=7&v,v-=7&v,n.mode=27;break}for(;v<3;){if(0===l)break e;l--,E+=r[a++]<>>=1)){case 0:n.mode=14;break;case 1:if(u(n),n.mode=20,6!==t)break;E>>>=2,v-=2;break e;case 2:n.mode=17;break;case 3:e.msg="invalid block type",n.mode=30}E>>>=2,v-=2;break;case 14:for(E>>>=7&v,v-=7&v;v<32;){if(0===l)break e;l--,E+=r[a++]<>>16^65535)){e.msg="invalid stored block lengths",n.mode=30;break}if(n.length=65535&E,v=E=0,n.mode=15,6===t)break e;case 15:n.mode=16;case 16:if(O=n.length){if(l>>=5,v-=5,n.ndist=1+(31&E),E>>>=5,v-=5,n.ncode=4+(15&E),E>>>=4,v-=4,286>>=3,v-=3}for(;n.have<19;)n.lens[z[n.have++]]=0;if(n.lencode=n.lendyn,n.lenbits=7,D={bits:n.lenbits},H=p(0,n.lens,0,19,n.lencode,0,n.work,D),n.lenbits=D.bits,H){e.msg="invalid code lengths set",n.mode=30;break}n.have=0,n.mode=19;case 19:for(;n.have>>16&255,I=65535&P,!((C=P>>>24)<=v);){if(0===l)break e;l--,E+=r[a++]<>>=C,v-=C,n.lens[n.have++]=I;else{if(16===I){for(W=C+2;v>>=C,v-=C,0===n.have){e.msg="invalid bit length repeat",n.mode=30;break}N=n.lens[n.have-1],O=3+(3&E),E>>>=2,v-=2}else if(17===I){for(W=C+3;v>>=C)),E>>>=3,v-=3}else{for(W=C+7;v>>=C)),E>>>=7,v-=7}if(n.have+O>n.nlen+n.ndist){e.msg="invalid bit length repeat",n.mode=30;break}for(;O--;)n.lens[n.have++]=N}}if(30===n.mode)break;if(0===n.lens[256]){e.msg="invalid code -- missing end-of-block",n.mode=30;break}if(n.lenbits=9,D={bits:n.lenbits},H=p(y,n.lens,0,n.nlen,n.lencode,0,n.work,D),n.lenbits=D.bits,H){e.msg="invalid literal/lengths set",n.mode=30;break}if(n.distbits=6,n.distcode=n.distdyn,D={bits:n.distbits},H=p(w,n.lens,n.nlen,n.ndist,n.distcode,0,n.work,D),n.distbits=D.bits,H){e.msg="invalid distances set",n.mode=30;break}if(n.mode=20,6===t)break e;case 20:n.mode=21;case 21:if(6<=l&&258<=f){e.next_out=s,e.avail_out=f,e.next_in=a,e.avail_in=l,n.hold=E,n.bits=v,_(e,S),s=e.next_out,o=e.output,f=e.avail_out,a=e.next_in,r=e.input,l=e.avail_in,E=n.hold,v=n.bits,12===n.mode&&(n.back=-1);break}for(n.back=0;x=(P=n.lencode[E&(1<>>16&255,I=65535&P,!((C=P>>>24)<=v);){if(0===l)break e;l--,E+=r[a++]<>L)])>>>16&255,I=65535&P,!(L+(C=P>>>24)<=v);){if(0===l)break e;l--,E+=r[a++]<>>=L,v-=L,n.back+=L}if(E>>>=C,v-=C,n.back+=C,n.length=I,0===x){n.mode=26;break}if(32&x){n.back=-1,n.mode=12;break}if(64&x){e.msg="invalid literal/length code",n.mode=30;break}n.extra=15&x,n.mode=22;case 22:if(n.extra){for(W=n.extra;v>>=n.extra,v-=n.extra,n.back+=n.extra}n.was=n.length,n.mode=23;case 23:for(;x=(P=n.distcode[E&(1<>>16&255,I=65535&P,!((C=P>>>24)<=v);){if(0===l)break e;l--,E+=r[a++]<>L)])>>>16&255,I=65535&P,!(L+(C=P>>>24)<=v);){if(0===l)break e;l--,E+=r[a++]<>>=L,v-=L,n.back+=L}if(E>>>=C,v-=C,n.back+=C,64&x){e.msg="invalid distance code",n.mode=30;break}n.offset=I,n.extra=15&x,n.mode=24;case 24:if(n.extra){for(W=n.extra;v>>=n.extra,v-=n.extra,n.back+=n.extra}if(n.offset>n.dmax){e.msg="invalid distance too far back",n.mode=30;break}n.mode=25;case 25:if(0===f)break e;if(O=S-f,n.offset>O){if((O=n.offset-O)>n.whave&&n.sane){e.msg="invalid distance too far back",n.mode=30;break}T=O>n.wnext?(O-=n.wnext,n.wsize-O):n.wnext-O,O>n.length&&(O=n.length),R=n.window}else R=o,T=s-n.offset,O=n.length;for(fw?(m=D[W+f[A]],B[F+f[A]]):(m=96,0),h=1<>C)+(b-=h)]=k<<24|m<<16|g|0,0!==b;);for(h=1<>=1;if(0!==h?(L&=h-1,L+=h):L=0,A++,0==--N[v]){if(v===O)break;v=t[n+f[A]]}if(T0?this.options.urlList[0]:this.options.url)}}return e.prototype.initialize=function(e){var t="MozWebSocket"in window?window.MozWebSocket:window.WebSocket,n=this.options;try{this.ws=new t(e),this.ws.binaryType="arraybuffer",this.ws.onopen=this.onOpen.bind(this),this.ws.onmessage=this.onMessage.bind(this),this.ws.onclose=this.onClose.bind(this),this.ws.onerror=this.onError.bind(this),r.a.callFunction(this.callbackQueueList.onInitializedQueue),this.callbackQueueList.onInitializedQueue=[]}catch(e){"function"==typeof n.fallback&&n.fallback()}return this},e.prototype.onOpen=function(){return r.a.callFunction(this.callbackQueueList.onOpenQueue),this.userAuthentication(),this},e.prototype.userAuthentication=function(){var e,t=this,n=this.options,o={uid:parseInt(n.uid,10),roomid:parseInt(n.rid,10),protover:parseInt(n.protover,10)||i.a.WS_BODY_PROTOCOL_VERSION_NORMAL};n.aid&&(o.aid=parseInt(n.aid,10)),n.from>0&&(o.from=parseInt(n.from,10)||7);for(var r=0;re.options.retryThreadCount?setTimeout((function(){e.initialize(e.options.url)}),1e3*e.options.retryRoundInterval):0!==t&&e.state.index>t-1?(e.state.index=0,e.state.listConnectFinishedCount+=1,1===e.state.listConnectFinishedCount&&r.a.callFunction(e.callbackQueueList.onListConnectErrorQueue),setTimeout((function(){e.initialize(e.options.urlList[e.state.index])}),1e3*e.options.retryRoundInterval)):e.initialize(e.options.urlList[e.state.index])}),1e3*this.options.retryInterval):(console.error("Danmaku Websocket Retry Failed."),r.a.callFunction(this.callbackQueueList.onRetryFallbackQueue)),this):this},e.prototype.onError=function(e){return console.error("Danmaku Websocket On Error.",e),r.a.callFunction(this.callbackQueueList.onErrorQueue,e),this},e.prototype.destroy=function(){clearTimeout(this.HEART_BEAT_INTERVAL),this.options.retry=!1,this.ws&&this.ws.close(),this.ws=null},e.prototype.convertToArrayBuffer=function(e,t){this.encoder||(this.encoder=r.a.getEncoder());var n=new ArrayBuffer(i.a.WS_PACKAGE_HEADER_TOTAL_LENGTH),o=new DataView(n,i.a.WS_PACKAGE_OFFSET),a=this.encoder.encode(e);return o.setInt32(i.a.WS_PACKAGE_OFFSET,i.a.WS_PACKAGE_HEADER_TOTAL_LENGTH+a.byteLength),this.wsBinaryHeaderList[2].value=t,this.wsBinaryHeaderList.forEach((function(e){4===e.bytes?o.setInt32(e.offset,e.value):2===e.bytes&&o.setInt16(e.offset,e.value)})),r.a.mergeArrayBuffer(n,a)},e.prototype.convertToObject=function(e){var t=new DataView(e),n={body:[]};if(n.packetLen=t.getInt32(i.a.WS_PACKAGE_OFFSET),this.wsBinaryHeaderList.forEach((function(e){4===e.bytes?n[e.key]=t.getInt32(e.offset):2===e.bytes&&(n[e.key]=t.getInt16(e.offset))})),n.packetLen1?t-1:0),i=1;i>>6:(t[a++]=n<65536?224|n>>>12:(t[a++]=240|n>>>18,128|n>>>12&63),128|n>>>6&63),128|63&n);return t},n.buf2binstring=function(e){return i(e,e.length)},n.binstring2buf=function(e){for(var t=new o.Buf8(e.length),n=0,i=t.length;n>10&1023,56320|1023&r)}return i(c,o)},n.utf8border=function(e,t){var n;for((t=t||e.length)>e.length&&(t=e.length),n=t-1;0<=n&&128==(192&e[n]);)n--;return n<0?t:0===n?t:n+s[e[n]]>t?n:t}},{"./common":1}],3:[function(e,t,n){t.exports=function(e,t,n,i){for(var o=65535&e|0,r=e>>>16&65535|0,a=0;0!==n;){for(n-=a=2e3>>1:e>>>1;t[n]=e}return t}();t.exports=function(e,t,n,o){var r=i,a=o+n;e^=-1;for(var s=o;s>>8^r[255&(e^t[s])];return-1^e}},{}],6:[function(e,t,n){t.exports=function(){this.text=0,this.time=0,this.xflags=0,this.os=0,this.extra=null,this.extra_len=0,this.name="",this.comment="",this.hcrc=0,this.done=!1}},{}],7:[function(e,t,n){t.exports=function(e,t){var n,i,o,r,a,s,u,c,l,f,d,h,b,p,m,y,_,v,k,g,w,E,S,A,O;n=e.state,i=e.next_in,A=e.input,o=i+(e.avail_in-5),r=e.next_out,O=e.output,a=r-(t-e.avail_out),s=r+(e.avail_out-257),u=n.dmax,c=n.wsize,l=n.whave,f=n.wnext,d=n.window,h=n.hold,b=n.bits,p=n.lencode,m=n.distcode,y=(1<>>=k=v>>>24,b-=k,0==(k=v>>>16&255))O[r++]=65535&v;else{if(!(16&k)){if(0==(64&k)){v=p[(65535&v)+(h&(1<>>=k,b-=k),b<15&&(h+=A[i++]<>>=k=v>>>24,b-=k,!(16&(k=v>>>16&255))){if(0==(64&k)){v=m[(65535&v)+(h&(1<>>=k,b-=k,(k=r-a)>3,h&=(1<<(b-=g<<3))-1,e.next_in=i,e.next_out=r,e.avail_in=i>>24&255)+(e>>>8&65280)+((65280&e)<<8)+((255&e)<<24)}function o(e){var t;return e&&e.state?(t=e.state,e.total_in=e.total_out=t.total=0,e.msg="",t.wrap&&(e.adler=1&t.wrap),t.mode=g,t.last=0,t.havedict=0,t.dmax=32768,t.head=null,t.hold=0,t.bits=0,t.lencode=t.lendyn=new d.Buf32(w),t.distcode=t.distdyn=new d.Buf32(E),t.sane=1,t.back=-1,v):k}function r(e){var t;return e&&e.state?((t=e.state).wsize=0,t.whave=0,t.wnext=0,o(e)):k}function a(e,t){var n,i;return e&&e.state?(i=e.state,t<0?(n=0,t=-t):(n=1+(t>>4),t<48&&(t&=15)),t&&(t<8||15=r.wsize?(d.arraySet(r.window,t,n-r.wsize,r.wsize,0),r.wnext=0,r.whave=r.wsize):(i<(o=r.wsize-r.wnext)&&(o=i),d.arraySet(r.window,t,n-i,o,r.wnext),(i-=o)?(d.arraySet(r.window,t,n-i,i,0),r.wnext=i,r.whave=r.wsize):(r.wnext+=o,r.wnext===r.wsize&&(r.wnext=0),r.whave>>8&255,n.check=b(n.check,W,2,0),E=w=0,n.mode=2;break}if(n.flags=0,n.head&&(n.head.done=!1),!(1&n.wrap)||(((255&w)<<8)+(w>>8))%31){e.msg="incorrect header check",n.mode=30;break}if(8!=(15&w)){e.msg="unknown compression method",n.mode=30;break}if(E-=4,N=8+(15&(w>>>=4)),0===n.wbits)n.wbits=N;else if(N>n.wbits){e.msg="invalid window size",n.mode=30;break}n.dmax=1<>8&1),512&n.flags&&(W[0]=255&w,W[1]=w>>>8&255,n.check=b(n.check,W,2,0)),E=w=0,n.mode=3;case 3:for(;E<32;){if(0===l)break e;l--,w+=o[a++]<>>8&255,W[2]=w>>>16&255,W[3]=w>>>24&255,n.check=b(n.check,W,4,0)),E=w=0,n.mode=4;case 4:for(;E<16;){if(0===l)break e;l--,w+=o[a++]<>8),512&n.flags&&(W[0]=255&w,W[1]=w>>>8&255,n.check=b(n.check,W,2,0)),E=w=0,n.mode=5;case 5:if(1024&n.flags){for(;E<16;){if(0===l)break e;l--,w+=o[a++]<>>8&255,n.check=b(n.check,W,2,0)),E=w=0}else n.head&&(n.head.extra=null);n.mode=6;case 6:if(1024&n.flags&&(l<(O=n.length)&&(O=l),O&&(n.head&&(N=n.head.extra_len-n.length,n.head.extra||(n.head.extra=new Array(n.head.extra_len)),d.arraySet(n.head.extra,o,a,O,N)),512&n.flags&&(n.check=b(n.check,o,O,a)),l-=O,a+=O,n.length-=O),n.length))break e;n.length=0,n.mode=7;case 7:if(2048&n.flags){if(0===l)break e;for(O=0;N=o[a+O++],n.head&&N&&n.length<65536&&(n.head.name+=String.fromCharCode(N)),N&&O>9&1,n.head.done=!0),e.adler=n.check=0,n.mode=12;break;case 10:for(;E<32;){if(0===l)break e;l--,w+=o[a++]<>>=7&E,E-=7&E,n.mode=27;break}for(;E<3;){if(0===l)break e;l--,w+=o[a++]<>>=1)){case 0:n.mode=14;break;case 1:if(u(n),n.mode=20,6!==t)break;w>>>=2,E-=2;break e;case 2:n.mode=17;break;case 3:e.msg="invalid block type",n.mode=30}w>>>=2,E-=2;break;case 14:for(w>>>=7&E,E-=7&E;E<32;){if(0===l)break e;l--,w+=o[a++]<>>16^65535)){e.msg="invalid stored block lengths",n.mode=30;break}if(n.length=65535&w,E=w=0,n.mode=15,6===t)break e;case 15:n.mode=16;case 16:if(O=n.length){if(l>>=5,E-=5,n.ndist=1+(31&w),w>>>=5,E-=5,n.ncode=4+(15&w),w>>>=4,E-=4,286>>=3,E-=3}for(;n.have<19;)n.lens[M[n.have++]]=0;if(n.lencode=n.lendyn,n.lenbits=7,D={bits:n.lenbits},H=m(0,n.lens,0,19,n.lencode,0,n.work,D),n.lenbits=D.bits,H){e.msg="invalid code lengths set",n.mode=30;break}n.have=0,n.mode=19;case 19:for(;n.have>>16&255,L=65535&U,!((R=U>>>24)<=E);){if(0===l)break e;l--,w+=o[a++]<>>=R,E-=R,n.lens[n.have++]=L;else{if(16===L){for(P=R+2;E>>=R,E-=R,0===n.have){e.msg="invalid bit length repeat",n.mode=30;break}N=n.lens[n.have-1],O=3+(3&w),w>>>=2,E-=2}else if(17===L){for(P=R+3;E>>=R)),w>>>=3,E-=3}else{for(P=R+7;E>>=R)),w>>>=7,E-=7}if(n.have+O>n.nlen+n.ndist){e.msg="invalid bit length repeat",n.mode=30;break}for(;O--;)n.lens[n.have++]=N}}if(30===n.mode)break;if(0===n.lens[256]){e.msg="invalid code -- missing end-of-block",n.mode=30;break}if(n.lenbits=9,D={bits:n.lenbits},H=m(y,n.lens,0,n.nlen,n.lencode,0,n.work,D),n.lenbits=D.bits,H){e.msg="invalid literal/lengths set",n.mode=30;break}if(n.distbits=6,n.distcode=n.distdyn,D={bits:n.distbits},H=m(_,n.lens,n.nlen,n.ndist,n.distcode,0,n.work,D),n.distbits=D.bits,H){e.msg="invalid distances set",n.mode=30;break}if(n.mode=20,6===t)break e;case 20:n.mode=21;case 21:if(6<=l&&258<=f){e.next_out=s,e.avail_out=f,e.next_in=a,e.avail_in=l,n.hold=w,n.bits=E,p(e,A),s=e.next_out,r=e.output,f=e.avail_out,a=e.next_in,o=e.input,l=e.avail_in,w=n.hold,E=n.bits,12===n.mode&&(n.back=-1);break}for(n.back=0;C=(U=n.lencode[w&(1<>>16&255,L=65535&U,!((R=U>>>24)<=E);){if(0===l)break e;l--,w+=o[a++]<>I)])>>>16&255,L=65535&U,!(I+(R=U>>>24)<=E);){if(0===l)break e;l--,w+=o[a++]<>>=I,E-=I,n.back+=I}if(w>>>=R,E-=R,n.back+=R,n.length=L,0===C){n.mode=26;break}if(32&C){n.back=-1,n.mode=12;break}if(64&C){e.msg="invalid literal/length code",n.mode=30;break}n.extra=15&C,n.mode=22;case 22:if(n.extra){for(P=n.extra;E>>=n.extra,E-=n.extra,n.back+=n.extra}n.was=n.length,n.mode=23;case 23:for(;C=(U=n.distcode[w&(1<>>16&255,L=65535&U,!((R=U>>>24)<=E);){if(0===l)break e;l--,w+=o[a++]<>I)])>>>16&255,L=65535&U,!(I+(R=U>>>24)<=E);){if(0===l)break e;l--,w+=o[a++]<>>=I,E-=I,n.back+=I}if(w>>>=R,E-=R,n.back+=R,64&C){e.msg="invalid distance code",n.mode=30;break}n.offset=L,n.extra=15&C,n.mode=24;case 24:if(n.extra){for(P=n.extra;E>>=n.extra,E-=n.extra,n.back+=n.extra}if(n.offset>n.dmax){e.msg="invalid distance too far back",n.mode=30;break}n.mode=25;case 25:if(0===f)break e;if(O=A-f,n.offset>O){if((O=n.offset-O)>n.whave&&n.sane){e.msg="invalid distance too far back",n.mode=30;break}T=O>n.wnext?(O-=n.wnext,n.wsize-O):n.wnext-O,O>n.length&&(O=n.length),x=n.window}else x=r,T=s-n.offset,O=n.length;for(f_?(k=D[P+f[S]],B[F+f[S]]):(k=96,0),h=1<>R)+(b-=h)]=v<<24|k<<16|g|0,0!==b;);for(h=1<>=1;if(0!==h?(I&=h-1,I+=h):I=0,S++,0==--N[E]){if(E===O)break;E=t[n+f[S]]}if(T1&&void 0!==arguments[1]?arguments[1]:document).querySelector(e)}return new(function(){function n(){var i=this;t(this,n),chrome.runtime.onMessage.addListener((function(t){if(!s(".blh-danmuku")){var n=t.type,o=t.data;switch(n){case r:i.dws=new window.DanmakuWebSocket(function(t){for(var n=1;n\n
\n
×
\n \n
\n 人气:--\n 粉丝:--\n
\n
\n
\n
\n
\n
\n
\n \n ',this.$headL=s(".blh-header-l",this.$danmuku),this.$popular=s(".blh-popular",this.$danmuku),this.$fans=s(".blh-fans",this.$danmuku),this.$headR=s(".blh-header-r",this.$danmuku),this.$danmu=s(".blh-danmu",this.$danmuku),this.$danmuInner=s(".blh-danmu-inner",this.$danmuku),this.$gift=s(".blh-gift",this.$danmuku),this.$giftInner=s(".blh-gift-inner",this.$danmuku),this.$footer=s(".blh-footer",this.$danmuku),this.$headL.textContent="".concat(this.manifest.name," ").concat(this.manifest.version),document.body.appendChild(this.$danmuku),this.$icon=document.createElement("div"),this.$icon.classList.add("blh-danmuku-icon"),this.$icon.textContent="弹",document.body.appendChild(this.$icon)}},{key:"eventBind",value:function(){var e=this,t=!1,n=!1,i=0,o=0,r=0,a=0,s=0;this.$headL.addEventListener("mousedown",(function(n){t=!0,i=n.pageX,o=n.pageY,r=e.$danmuku.offsetLeft,a=e.$danmuku.offsetTop})),this.$footer.addEventListener("mousedown",(function(t){n=!0,o=t.pageY,s=e.$danmu.clientHeight})),document.addEventListener("mousemove",(function(r){if(t){var a=r.pageX-i,u=r.pageY-o;e.$danmuku.style.transform="translate(".concat(a,"px, ").concat(u,"px)")}if(n){var c=s+r.pageY-o;c>=100?e.$danmu.style.height="".concat(c,"px"):n=!1}r.composedPath().indexOf(e.$danmuku)>-1?e.isHover=!0:e.isHover=!1})),document.addEventListener("mouseup",(function(s){if(t){t=!1,e.$danmuku.style.transform="translate(0, 0)";var u=r+s.pageX-i,c=a+s.pageY-o;e.$danmuku.style.left="".concat(u,"px"),e.$danmuku.style.top="".concat(c,"px")}n&&(n=!1)})),this.$headR.addEventListener("click",(function(){e.$danmuku.style.display="none",e.$icon.style.display="block"})),this.$icon.addEventListener("click",(function(){e.$danmuku.style.display="block",e.$icon.style.display="none"}))}},{key:"addDanmu",value:function(e){var t=this,n=this.$danmuInner.children;if(n.length>50){var i=n[0];s(".blh-danmu-uname",i).innerText="".concat(e.uname,":"),s(".blh-danmu-text",i).innerText=e.text,this.$danmuInner.appendChild(i)}else this.$danmuInner.insertAdjacentHTML("beforeend",'\n
\n '.concat(e.uname,':\n ').concat(e.text,"\n
\n "));this.isHover||(clearTimeout(this.danmuTimer),this.danmuTimer=setTimeout((function(){t.$danmu.scrollTo(0,t.$danmu.scrollHeight)}),100))}},{key:"addGift",value:function(e){var t=this,n=this.$giftInner.children;if(n.length>50){var i=n[0];s(".blh-gift-uname",i).innerText="".concat(e.uname,":"),s(".blh-gift-text",i).innerText="".concat(e.action," ").concat(e.gift," X ").concat(e.num),this.$giftInner.appendChild(i)}else this.$giftInner.insertAdjacentHTML("beforeend",'\n
\n '.concat(e.uname,':\n ').concat(e.action," ").concat(e.gift," X ").concat(e.num,"\n
\n "));this.isHover||(clearTimeout(this.giftTimer),this.giftTimer=setTimeout((function(){t.$gift.scrollTo(0,t.$gift.scrollHeight)}),100))}}]),n}())}(); 9 | --------------------------------------------------------------------------------