├── .editorconfig ├── .erb ├── configs │ ├── .eslintrc │ ├── webpack.config.base.ts │ ├── webpack.config.eslint.ts │ ├── webpack.config.main.prod.ts │ ├── webpack.config.renderer.dev.dll.ts │ ├── webpack.config.renderer.dev.ts │ ├── webpack.config.renderer.prod.ts │ └── webpack.paths.ts ├── img │ ├── erb-banner.svg │ └── erb-logo.png ├── mocks │ └── fileMock.js └── scripts │ ├── .eslintrc │ ├── check-build-exists.ts │ ├── check-native-dep.js │ ├── check-node-env.js │ ├── check-port-in-use.js │ ├── clean.js │ ├── delete-source-maps.js │ ├── electron-rebuild.js │ ├── link-modules.ts │ └── notarize.js ├── .eslintignore ├── .eslintrc.js ├── .gitattributes ├── .github └── workflows │ └── build.yml ├── .gitignore ├── .husky └── pre-commit ├── CHANGELOG.md ├── LICENSE ├── README.md ├── README_DEV.md ├── assets ├── assets.d.ts ├── entitlements.mac.plist ├── fonts │ └── lolita.ttf ├── icon.icns ├── icon.ico ├── icon.png ├── icon.svg └── icons │ ├── 1024x1024.png │ ├── 128x128.png │ ├── 16x16.png │ ├── 24x24.png │ ├── 256x256.png │ ├── 32x32.png │ ├── 48x48.png │ ├── 512x512.png │ ├── 64x64.png │ └── 96x96.png ├── extraResources └── fontlist │ ├── getSystemFonts.js │ ├── index.d.ts │ ├── index.js │ └── libs │ ├── darwin │ ├── fontlist │ ├── fontlist.m │ └── index.js │ ├── linux │ └── index.js │ └── win32 │ ├── fonts.vbs │ ├── getByPowerShell.js │ ├── getByVBS.js │ └── index.js ├── package-lock.json ├── package.json ├── release └── app │ ├── package-lock.json │ ├── package.json │ └── yarn.lock ├── screenshot └── session.png ├── src ├── __tests__ │ └── App.test.tsx ├── main │ ├── main.ts │ ├── menu.ts │ ├── preload.js │ ├── stateKeeper.ts │ └── util.ts └── renderer │ ├── App.css │ ├── App.tsx │ ├── api │ └── index.ts │ ├── app.global.scss │ ├── assets │ ├── css │ │ ├── main.scss │ │ ├── rc_dropdown.scss │ │ ├── rc_menu.scss │ │ ├── rc_notification.scss │ │ └── rc_toolTip.scss │ └── fonts │ │ └── lolita.ttf │ ├── bilive │ └── @types │ │ ├── LICENSE │ │ ├── danmaku.d.ts │ │ └── danmakuFormatted.d.ts │ ├── components │ ├── Base │ │ ├── DragSlider.tsx │ │ ├── Slider.tsx │ │ └── Switch.tsx │ └── Danmaku │ │ ├── DanmakuControl │ │ ├── CustomStyledPanel.tsx │ │ ├── DanmakuControl.tsx │ │ ├── LanguagePanel.tsx │ │ └── UserInfoConfigPanel.tsx │ │ ├── DanmakuGiftList │ │ └── DanmakuGiftList.tsx │ │ ├── DanmakuList │ │ └── DanmakuList.tsx │ │ ├── GiftBubble │ │ ├── Container.tsx │ │ ├── GiftBubble.tsx │ │ ├── GiftBubbleEntity.tsx │ │ ├── GiftBubbleItem.tsx │ │ └── Provider.tsx │ │ ├── LiveRoomLists │ │ └── LiveRoomLists.tsx │ │ ├── MsgEntity │ │ ├── MsgConnectSuccess.tsx │ │ ├── MsgConnecting.tsx │ │ ├── MsgDanmu.tsx │ │ ├── MsgDisconnected.tsx │ │ ├── MsgEntity.tsx │ │ ├── MsgGuardBuy.tsx │ │ ├── MsgGuardBuySystem.tsx │ │ ├── MsgInterActWord.tsx │ │ ├── MsgLive.tsx │ │ ├── MsgRoomBlock.tsx │ │ ├── MsgSendGift.tsx │ │ ├── MsgSuperChatCard.tsx │ │ ├── MsgUserAvatar.tsx │ │ ├── MsgVip.tsx │ │ ├── MsgWelcome.tsx │ │ └── MsgWelcomeGuard.tsx │ │ ├── MsgModel.ts │ │ ├── RankMessageLists │ │ └── RankMessageLists.tsx │ │ ├── SuperChatPanel │ │ ├── Container.tsx │ │ ├── Provider.tsx │ │ ├── SuperChatEntity.tsx │ │ ├── SuperChatItem.tsx │ │ └── SuperChatPanel.tsx │ │ ├── base │ │ └── Socket.ts │ │ ├── common │ │ ├── msg-struct.ts │ │ └── ws-url.ts │ │ └── index.tsx │ ├── config.ts │ ├── dao │ ├── ConfigDao.ts │ ├── LiveRoomDao.ts │ ├── StyledDao.ts │ ├── UesrInfoDao.ts │ └── UserAvatarDao.ts │ ├── i18n │ ├── index.ts │ └── locales │ │ ├── ja.json │ │ ├── lang.json │ │ └── zh-cn.json │ ├── index.ejs │ ├── index.tsx │ ├── reducers │ └── types.ts │ ├── store │ ├── features │ │ ├── configSlice.ts │ │ ├── counterSlice.ts │ │ └── danmakuSlice.ts │ ├── hooks.ts │ └── index.ts │ └── utils │ ├── .gitkeep │ ├── brotli.ts │ ├── common.ts │ ├── convert.ts │ ├── json-parser.ts │ ├── packet.ts │ ├── safeEval.ts │ ├── translation.ts │ ├── ttk.ts │ └── vioce.ts └── tsconfig.json /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /.erb/configs/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "no-console": "off", 4 | "no-shadow": "off", 5 | "camelcase": "off", 6 | "global-require": "off", 7 | "import/no-dynamic-require": "off", 8 | "prefer-destructuring":"off", 9 | "no-use-before-define": "off" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /.erb/configs/webpack.config.base.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Base webpack config used across other specific configs 3 | */ 4 | 5 | import webpack from 'webpack'; 6 | import webpackPaths from './webpack.paths'; 7 | import { dependencies as externals } from '../../release/app/package.json'; 8 | 9 | const configuration: webpack.Configuration = { 10 | externals: [...Object.keys(externals || {})], 11 | 12 | stats: 'errors-only', 13 | 14 | module: { 15 | rules: [ 16 | { 17 | test: /\.[jt]sx?$/, 18 | exclude: /node_modules/, 19 | use: { 20 | loader: 'ts-loader', 21 | options: { 22 | // Remove this line to enable type checking in webpack builds 23 | transpileOnly: true, 24 | }, 25 | }, 26 | }, 27 | ], 28 | }, 29 | 30 | output: { 31 | path: webpackPaths.srcPath, 32 | // https://github.com/webpack/webpack/issues/1114 33 | library: { 34 | type: 'commonjs2', 35 | }, 36 | }, 37 | 38 | /** 39 | * Determine the array of extensions that should be used to resolve modules. 40 | */ 41 | resolve: { 42 | extensions: ['.js', '.jsx', '.json', '.ts', '.tsx'], 43 | modules: [webpackPaths.srcPath, 'node_modules'], 44 | }, 45 | 46 | plugins: [ 47 | new webpack.EnvironmentPlugin({ 48 | NODE_ENV: 'production', 49 | }), 50 | ], 51 | }; 52 | 53 | export default configuration; 54 | -------------------------------------------------------------------------------- /.erb/configs/webpack.config.eslint.ts: -------------------------------------------------------------------------------- 1 | /* eslint import/no-unresolved: off, import/no-self-import: off */ 2 | 3 | module.exports = require('./webpack.config.renderer.dev').default; 4 | -------------------------------------------------------------------------------- /.erb/configs/webpack.config.main.prod.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Webpack config for production electron main process 3 | */ 4 | 5 | import path from 'path'; 6 | import webpack from 'webpack'; 7 | import { merge } from 'webpack-merge'; 8 | import TerserPlugin from 'terser-webpack-plugin'; 9 | import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer'; 10 | import baseConfig from './webpack.config.base'; 11 | import webpackPaths from './webpack.paths'; 12 | import checkNodeEnv from '../scripts/check-node-env'; 13 | import deleteSourceMaps from '../scripts/delete-source-maps'; 14 | 15 | checkNodeEnv('production'); 16 | deleteSourceMaps(); 17 | 18 | const devtoolsConfig = 19 | process.env.DEBUG_PROD === 'true' 20 | ? { 21 | devtool: 'source-map', 22 | } 23 | : {}; 24 | 25 | const configuration: webpack.Configuration = { 26 | ...devtoolsConfig, 27 | 28 | mode: 'production', 29 | 30 | target: 'electron-main', 31 | 32 | entry: { 33 | main: path.join(webpackPaths.srcMainPath, 'main.ts'), 34 | // preload: path.join(webpackPaths.srcMainPath, 'preload.js'), 35 | }, 36 | 37 | output: { 38 | path: webpackPaths.distMainPath, 39 | filename: '[name].js', 40 | }, 41 | 42 | optimization: { 43 | minimizer: [ 44 | new TerserPlugin({ 45 | parallel: true, 46 | }), 47 | ], 48 | }, 49 | 50 | plugins: [ 51 | new BundleAnalyzerPlugin({ 52 | analyzerMode: process.env.ANALYZE === 'true' ? 'server' : 'disabled', 53 | }), 54 | 55 | /** 56 | * Create global constants which can be configured at compile time. 57 | * 58 | * Useful for allowing different behaviour between development builds and 59 | * release builds 60 | * 61 | * NODE_ENV should be production so that modules do not perform certain 62 | * development checks 63 | */ 64 | new webpack.EnvironmentPlugin({ 65 | NODE_ENV: 'production', 66 | DEBUG_PROD: false, 67 | START_MINIMIZED: false, 68 | }), 69 | ], 70 | 71 | /** 72 | * Disables webpack processing of __dirname and __filename. 73 | * If you run the bundle in node.js it falls back to these values of node.js. 74 | * https://github.com/webpack/webpack/issues/2010 75 | */ 76 | node: { 77 | __dirname: false, 78 | __filename: false, 79 | }, 80 | }; 81 | 82 | export default merge(baseConfig, configuration); 83 | -------------------------------------------------------------------------------- /.erb/configs/webpack.config.renderer.dev.dll.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Builds the DLL for development electron renderer process 3 | */ 4 | 5 | import webpack from 'webpack'; 6 | import path from 'path'; 7 | import { merge } from 'webpack-merge'; 8 | import baseConfig from './webpack.config.base'; 9 | import webpackPaths from './webpack.paths'; 10 | import { dependencies } from '../../package.json'; 11 | import checkNodeEnv from '../scripts/check-node-env'; 12 | 13 | checkNodeEnv('development'); 14 | 15 | const dist = webpackPaths.dllPath; 16 | 17 | const configuration: webpack.Configuration = { 18 | context: webpackPaths.rootPath, 19 | 20 | devtool: 'eval', 21 | 22 | mode: 'development', 23 | 24 | target: 'electron-renderer', 25 | 26 | externals: ['fsevents', 'crypto-browserify'], 27 | 28 | /** 29 | * Use `module` from `webpack.config.renderer.dev.js` 30 | */ 31 | module: require('./webpack.config.renderer.dev').default.module, 32 | 33 | entry: { 34 | renderer: Object.keys(dependencies || {}), 35 | }, 36 | 37 | output: { 38 | path: dist, 39 | filename: '[name].dev.dll.js', 40 | library: { 41 | name: 'renderer', 42 | type: 'var', 43 | }, 44 | }, 45 | 46 | plugins: [ 47 | new webpack.DllPlugin({ 48 | path: path.join(dist, '[name].json'), 49 | name: '[name]', 50 | }), 51 | 52 | /** 53 | * Create global constants which can be configured at compile time. 54 | * 55 | * Useful for allowing different behaviour between development builds and 56 | * release builds 57 | * 58 | * NODE_ENV should be production so that modules do not perform certain 59 | * development checks 60 | */ 61 | new webpack.EnvironmentPlugin({ 62 | NODE_ENV: 'development', 63 | }), 64 | 65 | new webpack.LoaderOptionsPlugin({ 66 | debug: true, 67 | options: { 68 | context: webpackPaths.srcPath, 69 | output: { 70 | path: webpackPaths.dllPath, 71 | }, 72 | }, 73 | }), 74 | ], 75 | }; 76 | 77 | export default merge(baseConfig, configuration); 78 | -------------------------------------------------------------------------------- /.erb/configs/webpack.config.renderer.dev.ts: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import fs from 'fs'; 3 | import webpack from 'webpack'; 4 | import HtmlWebpackPlugin from 'html-webpack-plugin'; 5 | import chalk from 'chalk'; 6 | import { merge } from 'webpack-merge'; 7 | import { spawn, execSync } from 'child_process'; 8 | import baseConfig from './webpack.config.base'; 9 | import webpackPaths from './webpack.paths'; 10 | import checkNodeEnv from '../scripts/check-node-env'; 11 | import ReactRefreshWebpackPlugin from '@pmmmwh/react-refresh-webpack-plugin'; 12 | 13 | // When an ESLint server is running, we can't set the NODE_ENV so we'll check if it's 14 | // at the dev webpack config is not accidentally run in a production environment 15 | if (process.env.NODE_ENV === 'production') { 16 | checkNodeEnv('development'); 17 | } 18 | 19 | const port = process.env.PORT || 1212; 20 | const manifest = path.resolve(webpackPaths.dllPath, 'renderer.json'); 21 | const requiredByDLLConfig = module.parent!.filename.includes( 22 | 'webpack.config.renderer.dev.dll' 23 | ); 24 | 25 | /** 26 | * Warn if the DLL is not built 27 | */ 28 | if ( 29 | !requiredByDLLConfig && 30 | !(fs.existsSync(webpackPaths.dllPath) && fs.existsSync(manifest)) 31 | ) { 32 | console.log( 33 | chalk.black.bgYellow.bold( 34 | 'The DLL files are missing. Sit back while we build them for you with "npm run build-dll"' 35 | ) 36 | ); 37 | execSync('npm run postinstall'); 38 | } 39 | 40 | const configuration: webpack.Configuration = { 41 | devtool: 'inline-source-map', 42 | 43 | mode: 'development', 44 | 45 | target: 'electron-renderer', 46 | 47 | entry: [ 48 | `webpack-dev-server/client?http://localhost:${port}/dist`, 49 | 'webpack/hot/only-dev-server', 50 | path.join(webpackPaths.srcRendererPath, 'index.tsx'), 51 | ], 52 | 53 | output: { 54 | path: webpackPaths.distRendererPath, 55 | publicPath: '/', 56 | filename: 'renderer.dev.js', 57 | }, 58 | 59 | module: { 60 | rules: [ 61 | { 62 | test: /\.s?css$/, 63 | use: [ 64 | 'style-loader', 65 | { 66 | loader: 'css-loader', 67 | options: { 68 | modules: true, 69 | sourceMap: true, 70 | importLoaders: 1, 71 | }, 72 | }, 73 | 'sass-loader', 74 | ], 75 | include: /\.module\.s?(c|a)ss$/, 76 | }, 77 | { 78 | test: /\.s?css$/, 79 | use: ['style-loader', 'css-loader', 'sass-loader'], 80 | exclude: /\.module\.s?(c|a)ss$/, 81 | }, 82 | // Fonts 83 | { 84 | test: /\.(woff|woff2|eot|ttf|otf)$/i, 85 | type: 'asset/resource', 86 | }, 87 | // Images 88 | { 89 | test: /\.(png|svg|jpg|jpeg|gif)$/i, 90 | type: 'asset/resource', 91 | }, 92 | ], 93 | }, 94 | plugins: [ 95 | ...(requiredByDLLConfig 96 | ? [] 97 | : [ 98 | new webpack.DllReferencePlugin({ 99 | context: webpackPaths.dllPath, 100 | manifest: require(manifest), 101 | sourceType: 'var', 102 | }), 103 | ]), 104 | 105 | new webpack.NoEmitOnErrorsPlugin(), 106 | 107 | /** 108 | * Create global constants which can be configured at compile time. 109 | * 110 | * Useful for allowing different behaviour between development builds and 111 | * release builds 112 | * 113 | * NODE_ENV should be production so that modules do not perform certain 114 | * development checks 115 | * 116 | * By default, use 'development' as NODE_ENV. This can be overriden with 117 | * 'staging', for example, by changing the ENV variables in the npm scripts 118 | */ 119 | new webpack.EnvironmentPlugin({ 120 | NODE_ENV: 'development', 121 | }), 122 | 123 | new webpack.LoaderOptionsPlugin({ 124 | debug: true, 125 | }), 126 | 127 | new ReactRefreshWebpackPlugin(), 128 | 129 | new HtmlWebpackPlugin({ 130 | filename: path.join('index.html'), 131 | template: path.join(webpackPaths.srcRendererPath, 'index.ejs'), 132 | minify: { 133 | collapseWhitespace: true, 134 | removeAttributeQuotes: true, 135 | removeComments: true, 136 | }, 137 | isBrowser: false, 138 | env: process.env.NODE_ENV, 139 | isDevelopment: process.env.NODE_ENV !== 'production', 140 | nodeModules: webpackPaths.appNodeModulesPath, 141 | }), 142 | ], 143 | 144 | node: { 145 | __dirname: false, 146 | __filename: false, 147 | }, 148 | 149 | // @ts-ignore 150 | devServer: { 151 | port, 152 | compress: true, 153 | hot: true, 154 | headers: { 'Access-Control-Allow-Origin': '*' }, 155 | static: { 156 | publicPath: '/', 157 | }, 158 | historyApiFallback: { 159 | verbose: true, 160 | }, 161 | onBeforeSetupMiddleware() { 162 | console.log('Starting Main Process...'); 163 | spawn('npm', ['run', 'start:main'], { 164 | shell: true, 165 | env: process.env, 166 | stdio: 'inherit', 167 | }) 168 | .on('close', (code: number) => process.exit(code!)) 169 | .on('error', (spawnError) => console.error(spawnError)); 170 | }, 171 | }, 172 | }; 173 | 174 | export default merge(baseConfig, configuration); 175 | -------------------------------------------------------------------------------- /.erb/configs/webpack.config.renderer.prod.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Build config for electron renderer process 3 | */ 4 | 5 | import path from 'path'; 6 | import webpack from 'webpack'; 7 | import HtmlWebpackPlugin from 'html-webpack-plugin'; 8 | import MiniCssExtractPlugin from 'mini-css-extract-plugin'; 9 | import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer'; 10 | import CssMinimizerPlugin from 'css-minimizer-webpack-plugin'; 11 | import { merge } from 'webpack-merge'; 12 | import TerserPlugin from 'terser-webpack-plugin'; 13 | import baseConfig from './webpack.config.base'; 14 | import webpackPaths from './webpack.paths'; 15 | import checkNodeEnv from '../scripts/check-node-env'; 16 | import deleteSourceMaps from '../scripts/delete-source-maps'; 17 | 18 | checkNodeEnv('production'); 19 | deleteSourceMaps(); 20 | 21 | const devtoolsConfig = 22 | process.env.DEBUG_PROD === 'true' 23 | ? { 24 | devtool: 'source-map', 25 | } 26 | : {}; 27 | 28 | const configuration: webpack.Configuration = { 29 | ...devtoolsConfig, 30 | 31 | mode: 'production', 32 | 33 | target: 'electron-renderer', 34 | 35 | entry: [path.join(webpackPaths.srcRendererPath, 'index.tsx')], 36 | 37 | output: { 38 | path: webpackPaths.distRendererPath, 39 | publicPath: './', 40 | filename: 'renderer.js', 41 | }, 42 | 43 | module: { 44 | rules: [ 45 | { 46 | test: /\.s?(a|c)ss$/, 47 | use: [ 48 | MiniCssExtractPlugin.loader, 49 | { 50 | loader: 'css-loader', 51 | options: { 52 | modules: true, 53 | sourceMap: true, 54 | importLoaders: 1, 55 | }, 56 | }, 57 | 'sass-loader', 58 | ], 59 | include: /\.module\.s?(c|a)ss$/, 60 | }, 61 | { 62 | test: /\.s?(a|c)ss$/, 63 | use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader'], 64 | exclude: /\.module\.s?(c|a)ss$/, 65 | }, 66 | // Fonts 67 | { 68 | test: /\.(woff|woff2|eot|ttf|otf)$/i, 69 | type: 'asset/resource', 70 | }, 71 | // Images 72 | { 73 | test: /\.(png|svg|jpg|jpeg|gif)$/i, 74 | type: 'asset/resource', 75 | }, 76 | ], 77 | }, 78 | 79 | optimization: { 80 | minimize: true, 81 | minimizer: [ 82 | new TerserPlugin({ 83 | parallel: true, 84 | }), 85 | new CssMinimizerPlugin(), 86 | ], 87 | }, 88 | 89 | plugins: [ 90 | /** 91 | * Create global constants which can be configured at compile time. 92 | * 93 | * Useful for allowing different behaviour between development builds and 94 | * release builds 95 | * 96 | * NODE_ENV should be production so that modules do not perform certain 97 | * development checks 98 | */ 99 | new webpack.EnvironmentPlugin({ 100 | NODE_ENV: 'production', 101 | DEBUG_PROD: false, 102 | }), 103 | 104 | new MiniCssExtractPlugin({ 105 | filename: 'style.css', 106 | }), 107 | 108 | new BundleAnalyzerPlugin({ 109 | analyzerMode: process.env.ANALYZE === 'true' ? 'server' : 'disabled', 110 | }), 111 | 112 | new HtmlWebpackPlugin({ 113 | filename: 'index.html', 114 | template: path.join(webpackPaths.srcRendererPath, 'index.ejs'), 115 | minify: { 116 | collapseWhitespace: true, 117 | removeAttributeQuotes: true, 118 | removeComments: true, 119 | }, 120 | isBrowser: false, 121 | isDevelopment: process.env.NODE_ENV !== 'production', 122 | }), 123 | ], 124 | }; 125 | 126 | export default merge(baseConfig, configuration); 127 | -------------------------------------------------------------------------------- /.erb/configs/webpack.paths.ts: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | const rootPath = path.join(__dirname, '../..'); 4 | 5 | const dllPath = path.join(__dirname, '../dll'); 6 | 7 | const srcPath = path.join(rootPath, 'src'); 8 | const srcMainPath = path.join(srcPath, 'main'); 9 | const srcRendererPath = path.join(srcPath, 'renderer'); 10 | 11 | const releasePath = path.join(rootPath, 'release'); 12 | const appPath = path.join(releasePath, 'app'); 13 | const appPackagePath = path.join(appPath, 'package.json'); 14 | const appNodeModulesPath = path.join(appPath, 'node_modules'); 15 | const srcNodeModulesPath = path.join(srcPath, 'node_modules'); 16 | 17 | const distPath = path.join(appPath, 'dist'); 18 | const distMainPath = path.join(distPath, 'main'); 19 | const distRendererPath = path.join(distPath, 'renderer'); 20 | 21 | const buildPath = path.join(releasePath, 'build'); 22 | 23 | export default { 24 | rootPath, 25 | dllPath, 26 | srcPath, 27 | srcMainPath, 28 | srcRendererPath, 29 | releasePath, 30 | appPath, 31 | appPackagePath, 32 | appNodeModulesPath, 33 | srcNodeModulesPath, 34 | distPath, 35 | distMainPath, 36 | distRendererPath, 37 | buildPath, 38 | }; 39 | -------------------------------------------------------------------------------- /.erb/img/erb-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beats0/bilive-danmaku/590e9f122929bc29659eeb8b44ff45a0890894c0/.erb/img/erb-logo.png -------------------------------------------------------------------------------- /.erb/mocks/fileMock.js: -------------------------------------------------------------------------------- 1 | export default 'test-file-stub'; 2 | -------------------------------------------------------------------------------- /.erb/scripts/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "no-console": "off", 4 | "global-require": "off", 5 | "import/no-dynamic-require": "off", 6 | "import/no-extraneous-dependencies": "off", 7 | "no-use-before-define": "off" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /.erb/scripts/check-build-exists.ts: -------------------------------------------------------------------------------- 1 | // Check if the renderer and main bundles are built 2 | import path from 'path'; 3 | import chalk from 'chalk'; 4 | import fs from 'fs'; 5 | import webpackPaths from '../configs/webpack.paths'; 6 | 7 | const mainPath = path.join(webpackPaths.distMainPath, 'main.js'); 8 | const rendererPath = path.join(webpackPaths.distRendererPath, 'renderer.js'); 9 | 10 | if (!fs.existsSync(mainPath)) { 11 | throw new Error( 12 | chalk.whiteBright.bgRed.bold( 13 | 'The main process is not built yet. Build it by running "npm run build:main"' 14 | ) 15 | ); 16 | } 17 | 18 | if (!fs.existsSync(rendererPath)) { 19 | throw new Error( 20 | chalk.whiteBright.bgRed.bold( 21 | 'The renderer process is not built yet. Build it by running "npm run build:renderer"' 22 | ) 23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /.erb/scripts/check-native-dep.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import chalk from 'chalk'; 3 | import { execSync } from 'child_process'; 4 | import { dependencies } from '../../package.json'; 5 | 6 | if (dependencies) { 7 | const dependenciesKeys = Object.keys(dependencies); 8 | const nativeDeps = fs 9 | .readdirSync('node_modules') 10 | .filter((folder) => fs.existsSync(`node_modules/${folder}/binding.gyp`)); 11 | if (nativeDeps.length === 0) { 12 | process.exit(0); 13 | } 14 | try { 15 | // Find the reason for why the dependency is installed. If it is installed 16 | // because of a devDependency then that is okay. Warn when it is installed 17 | // because of a dependency 18 | const { dependencies: dependenciesObject } = JSON.parse( 19 | execSync(`npm ls ${nativeDeps.join(' ')} --json`).toString() 20 | ); 21 | const rootDependencies = Object.keys(dependenciesObject); 22 | const filteredRootDependencies = rootDependencies.filter((rootDependency) => 23 | dependenciesKeys.includes(rootDependency) 24 | ); 25 | if (filteredRootDependencies.length > 0) { 26 | const plural = filteredRootDependencies.length > 1; 27 | console.log(` 28 | ${chalk.whiteBright.bgYellow.bold( 29 | 'Webpack does not work with native dependencies.' 30 | )} 31 | ${chalk.bold(filteredRootDependencies.join(', '))} ${ 32 | plural ? 'are native dependencies' : 'is a native dependency' 33 | } and should be installed inside of the "./release/app" folder. 34 | First, uninstall the packages from "./package.json": 35 | ${chalk.whiteBright.bgGreen.bold('npm uninstall your-package')} 36 | ${chalk.bold( 37 | 'Then, instead of installing the package to the root "./package.json":' 38 | )} 39 | ${chalk.whiteBright.bgRed.bold('npm install your-package')} 40 | ${chalk.bold('Install the package to "./release/app/package.json"')} 41 | ${chalk.whiteBright.bgGreen.bold('cd ./release/app && npm install your-package')} 42 | Read more about native dependencies at: 43 | ${chalk.bold( 44 | 'https://electron-react-boilerplate.js.org/docs/adding-dependencies/#module-structure' 45 | )} 46 | `); 47 | process.exit(1); 48 | } 49 | } catch (e) { 50 | console.log('Native dependencies could not be checked'); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /.erb/scripts/check-node-env.js: -------------------------------------------------------------------------------- 1 | import chalk from 'chalk'; 2 | 3 | export default function checkNodeEnv(expectedEnv) { 4 | if (!expectedEnv) { 5 | throw new Error('"expectedEnv" not set'); 6 | } 7 | 8 | if (process.env.NODE_ENV !== expectedEnv) { 9 | console.log( 10 | chalk.whiteBright.bgRed.bold( 11 | `"process.env.NODE_ENV" must be "${expectedEnv}" to use this webpack config` 12 | ) 13 | ); 14 | process.exit(2); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /.erb/scripts/check-port-in-use.js: -------------------------------------------------------------------------------- 1 | import chalk from 'chalk'; 2 | import detectPort from 'detect-port'; 3 | 4 | const port = process.env.PORT || '1212'; 5 | 6 | detectPort(port, (err, availablePort) => { 7 | if (port !== String(availablePort)) { 8 | throw new Error( 9 | chalk.whiteBright.bgRed.bold( 10 | `Port "${port}" on "localhost" is already in use. Please use another port. ex: PORT=4343 npm start` 11 | ) 12 | ); 13 | } else { 14 | process.exit(0); 15 | } 16 | }); 17 | -------------------------------------------------------------------------------- /.erb/scripts/clean.js: -------------------------------------------------------------------------------- 1 | import rimraf from 'rimraf'; 2 | import webpackPaths from '../configs/webpack.paths.ts'; 3 | import process from 'process'; 4 | 5 | const args = process.argv.slice(2); 6 | const commandMap = { 7 | dist: webpackPaths.distPath, 8 | release: webpackPaths.releasePath, 9 | dll: webpackPaths.dllPath, 10 | }; 11 | 12 | args.forEach((x) => { 13 | const pathToRemove = commandMap[x]; 14 | if (pathToRemove !== undefined) { 15 | rimraf.sync(pathToRemove); 16 | } 17 | }); 18 | -------------------------------------------------------------------------------- /.erb/scripts/delete-source-maps.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import rimraf from 'rimraf'; 3 | import webpackPaths from '../configs/webpack.paths'; 4 | 5 | export default function deleteSourceMaps() { 6 | rimraf.sync(path.join(webpackPaths.distMainPath, '*.js.map')); 7 | rimraf.sync(path.join(webpackPaths.distRendererPath, '*.js.map')); 8 | } 9 | -------------------------------------------------------------------------------- /.erb/scripts/electron-rebuild.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import { execSync } from 'child_process'; 3 | import fs from 'fs'; 4 | import { dependencies } from '../../release/app/package.json'; 5 | import webpackPaths from '../configs/webpack.paths'; 6 | 7 | if ( 8 | Object.keys(dependencies || {}).length > 0 && 9 | fs.existsSync(webpackPaths.appNodeModulesPath) 10 | ) { 11 | const electronRebuildCmd = 12 | '../../node_modules/.bin/electron-rebuild --parallel --force --types prod,dev,optional --module-dir .'; 13 | const cmd = 14 | process.platform === 'win32' 15 | ? electronRebuildCmd.replace(/\//g, '\\') 16 | : electronRebuildCmd; 17 | execSync(cmd, { 18 | cwd: webpackPaths.appPath, 19 | stdio: 'inherit', 20 | }); 21 | } 22 | -------------------------------------------------------------------------------- /.erb/scripts/link-modules.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import webpackPaths from '../configs/webpack.paths'; 3 | 4 | const srcNodeModulesPath = webpackPaths.srcNodeModulesPath; 5 | const appNodeModulesPath = webpackPaths.appNodeModulesPath 6 | 7 | if (!fs.existsSync(srcNodeModulesPath) && fs.existsSync(appNodeModulesPath)) { 8 | fs.symlinkSync(appNodeModulesPath, srcNodeModulesPath, 'junction'); 9 | } 10 | -------------------------------------------------------------------------------- /.erb/scripts/notarize.js: -------------------------------------------------------------------------------- 1 | const { notarize } = require('electron-notarize'); 2 | const { build } = require('../../package.json'); 3 | 4 | exports.default = async function notarizeMacos(context) { 5 | const { electronPlatformName, appOutDir } = context; 6 | if (electronPlatformName !== 'darwin') { 7 | return; 8 | } 9 | 10 | if (process.env.CI !== "true") { 11 | console.warn('Skipping notarizing step. Packaging is not running in CI'); 12 | return; 13 | } 14 | 15 | if (!('APPLE_ID' in process.env && 'APPLE_ID_PASS' in process.env)) { 16 | console.warn('Skipping notarizing step. APPLE_ID and APPLE_ID_PASS env variables must be set'); 17 | return; 18 | } 19 | 20 | const appName = context.packager.appInfo.productFilename; 21 | 22 | await notarize({ 23 | appBundleId: build.appId, 24 | appPath: `${appOutDir}/${appName}.app`, 25 | appleId: process.env.APPLE_ID, 26 | appleIdPassword: process.env.APPLE_ID_PASS, 27 | }); 28 | }; 29 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Coverage directory used by tools like istanbul 11 | coverage 12 | .eslintcache 13 | 14 | # Dependency directory 15 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 16 | node_modules 17 | 18 | # OSX 19 | .DS_Store 20 | 21 | release/app/dist 22 | release/build 23 | .erb/dll 24 | src 25 | .idea 26 | npm-debug.log.* 27 | package-lock.json 28 | *.css.d.ts 29 | *.sass.d.ts 30 | *.scss.d.ts 31 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: 'erb', 3 | rules: { 4 | // A temporary hack related to IDE not resolving correct package.json 5 | 'import/no-extraneous-dependencies': 'off', 6 | 'import/no-unresolved': 'error', 7 | // Since React 17 and typescript 4.1 you can safely disable the rule 8 | 'react/react-in-jsx-scope': 'off', 9 | 'no-use-before-define': 'off', 10 | }, 11 | parserOptions: { 12 | ecmaVersion: 2020, 13 | sourceType: 'module', 14 | project: './tsconfig.json', 15 | tsconfigRootDir: __dirname, 16 | createDefaultProgram: true, 17 | }, 18 | settings: { 19 | 'import/resolver': { 20 | // See https://github.com/benmosher/eslint-plugin-import/issues/1396#issuecomment-575727774 for line below 21 | node: {}, 22 | webpack: { 23 | config: require.resolve('./.erb/configs/webpack.config.eslint.ts'), 24 | }, 25 | typescript: {}, 26 | }, 27 | 'import/parsers': { 28 | '@typescript-eslint/parser': ['.ts', '.tsx'], 29 | }, 30 | }, 31 | }; 32 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text eol=lf 2 | *.exe binary 3 | *.png binary 4 | *.jpg binary 5 | *.jpeg binary 6 | *.ico binary 7 | *.icns binary 8 | *.eot binary 9 | *.otf binary 10 | *.ttf binary 11 | *.woff binary 12 | *.woff2 binary 13 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*.*.*' 7 | 8 | # Workflow's jobs 9 | jobs: 10 | # job's id 11 | release: 12 | # job's name 13 | name: build and release electron app 14 | 15 | # the type of machine to run the job on 16 | runs-on: ${{ matrix.os }} 17 | 18 | # create a build matrix for jobs 19 | strategy: 20 | fail-fast: false 21 | matrix: 22 | os: [windows-latest, macos-latest] 23 | 24 | # create steps 25 | steps: 26 | # step1: check out repository 27 | - name: Checkout git repo 28 | uses: actions/checkout@v2 29 | 30 | # step2: setup node env 31 | - name: Install Node and NPM 32 | uses: actions/setup-node@v3 33 | with: 34 | node-version: 18 35 | cache: npm 36 | 37 | # step3: npm install and package 38 | - name: Install and build 39 | run: | 40 | npm install 41 | npm run postinstall 42 | npm run package 43 | 44 | # step4: cleanup artifacts in release 45 | - name: Cleanup artifacts for windows 46 | if: matrix.os == 'windows-latest' 47 | run: | 48 | npx rimraf "release/build/!(*.exe)" 49 | 50 | - name: Cleanup artifacts for macos 51 | if: matrix.os == 'macos-latest' 52 | run: | 53 | npx rimraf "release/build/!(*.dmg)" 54 | 55 | # step5: upload artifacts 56 | - name: Upload artifacts 57 | uses: actions/upload-artifact@v2 58 | with: 59 | name: ${{ matrix.os }} 60 | path: release/build 61 | 62 | # step6: create release 63 | - name: Create release 64 | uses: softprops/action-gh-release@v1 65 | if: startsWith(github.ref, 'refs/tags/') 66 | with: 67 | files: 'release/build/**' 68 | env: 69 | GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} 70 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Coverage directory used by tools like istanbul 11 | coverage 12 | .eslintcache 13 | 14 | # Dependency directory 15 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 16 | node_modules 17 | 18 | # OSX 19 | .DS_Store 20 | 21 | release/app/dist 22 | release/build 23 | .erb/dll 24 | 25 | .idea 26 | .vscode 27 | npm-debug.log.* 28 | *.css.d.ts 29 | *.sass.d.ts 30 | *.scss.d.ts 31 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx lint-staged 5 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## v1.0.9 2 | 3 | - electron 版本更新 4 | - websocket 认证 5 | - 优化头像存储功能 6 | - 添加用户 session 配置 7 | 8 | ## v1.0.8 9 | 10 | - 添加鼠标穿透功能 11 | - 优化头像存储功能 12 | 13 | ## v1.0.7 14 | 15 | - 添加打开时记住上次窗口大小位置 16 | - 添加警告类弹幕消息提示 17 | - 添加查看历史留言功能 18 | - 添加可屏蔽舰长消息文字特效功能 19 | 20 | ## v1.0.6 21 | 22 | - electron 版本更新,使用 reduxjs/toolkit 状态管理,功能优化 23 | - 移除礼物弹幕过渡动画 24 | - 添加自定义字体功能 25 | - 优化置顶功能 26 | - 优化顶部栏拖动功能 27 | - 优化点击弹幕朗读队列 28 | - 替换版本号 api 29 | 30 | ## v1.0.5 31 | 32 | - 弹幕翻译已被废弃 33 | - 修复百度语音 TTS 返回失败 bug 34 | 35 | ## v1.0.4 36 | 37 | - 优化新版粉丝勋章样式 38 | - 替换版本号 api,防止获取版本号失败 39 | - 修复礼物弹幕图片不显示 bug 40 | 41 | ## v1.0.3 42 | 43 | - 弹幕列表与礼物列表分开 44 | - 礼物列表拖动定位 45 | - 优化获取用户头像 api 访问受限 46 | - 修复 windows 下字体文件缺失 bug 47 | 48 | ## v1.0.2 49 | 50 | - 优化 config 类型 51 | - 添加 ref 类型 52 | - 移除 less,使用 scss 53 | - 优化 socket parseData 54 | - 添加单元测试 55 | - 修复 maxMessageCount 不更新问题 56 | - 修复 mac 无法复制粘贴 57 | 58 | ## v1.0.1 59 | 60 | - 优化版本号检查 61 | - win 和 mac 顶部窗口菜单栏保持一致 62 | - 修复客户端版本号错误 bug 63 | - 文档优化 64 | 65 | ## v1.0.1-beta.0 66 | 67 | - 完成初版功能 68 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Beats0 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # bilive-danmaku 2 | 3 |
119 | {i.uname} 120 |
121 |122 | {i.title} 123 |
124 |openLink(`https://space.bilibili.com/${i.uid}`)}>{i.uname}
90 |{dateFormat(new Date(i.send_time * 1000), 'HH:MM:SS')}
91 |