├── .editorconfig ├── .eslintrc.js ├── .github └── workflows │ ├── close-issue.yml │ ├── create-pr.yml │ ├── preview-deploy.yml │ └── process-submit.yml ├── .gitignore ├── .gitmodules ├── .prettierrc ├── README.md ├── ci ├── closeIssue.js └── submit.js ├── docs ├── .vuepress │ ├── config.js │ └── public │ │ ├── favicon.ico │ │ ├── logo.png │ │ └── netlify.toml ├── README.md ├── Ruminoid │ ├── Develop │ │ ├── README.md │ │ └── Reference │ │ │ ├── PluginInterface │ │ │ ├── IInterfacePlugin.md │ │ │ ├── ITarget.md │ │ │ ├── ITransform.md │ │ │ └── README.md │ │ │ ├── Project │ │ │ ├── ColumnAndTarget.md │ │ │ ├── README.md │ │ │ └── Styles.md │ │ │ └── README.md │ ├── Guide │ │ ├── README.md │ │ └── ToolboxReference │ │ │ ├── Compatibility.md │ │ │ ├── Debug.md │ │ │ ├── Errors.md │ │ │ ├── Faq.md │ │ │ └── README.md │ ├── README.md │ └── Version │ │ ├── History.md │ │ ├── README.md │ │ ├── Thre.md │ │ └── ToolboxDownload.md ├── SimpleBox │ ├── Guide │ │ ├── History.md │ │ └── README.md │ └── README.md ├── VBox │ ├── Contribute.md │ ├── Edit.md │ ├── Intro.md │ ├── README.md │ └── Theme │ │ ├── Editor.md │ │ ├── Layout.md │ │ └── README.md └── VNerve │ ├── Components │ └── BiLive.md │ ├── GitHub.md │ ├── Guide │ └── README.md │ ├── Inside │ ├── Processor │ │ ├── CNS │ │ │ └── README.md │ │ ├── Effector │ │ │ └── README.md │ │ ├── README.md │ │ ├── Supervisor │ │ │ ├── Communication.md │ │ │ ├── Income.md │ │ │ ├── LoadBalance.md │ │ │ └── README.md │ │ └── Worker │ │ │ └── README.md │ ├── README.md │ └── Spider │ │ └── README.md │ └── README.md ├── jsconfig.json ├── package-lock.json ├── package.json └── tsconfig.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | browser: true, 5 | node: true, 6 | es2020: true 7 | }, 8 | parser: 'vue-eslint-parser', 9 | parserOptions: { 10 | sourceType: 'module', 11 | parser: 'babel-eslint', 12 | allowImportExportEverywhere: true 13 | }, 14 | extends: ['eslint:recommended'], 15 | plugins: ['prettier'], 16 | rules: { 17 | 'no-unused-vars': 1 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /.github/workflows/close-issue.yml: -------------------------------------------------------------------------------- 1 | name: 'Close Issue' 2 | 3 | on: 4 | pull_request: 5 | types: 6 | - closed 7 | 8 | jobs: 9 | close: 10 | name: Close Issue 11 | if: github.event_name == 'pull_request' && !github.event.pull_request.merged 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | 16 | - name: Use Node.js 14 17 | uses: actions/setup-node@v1 18 | with: 19 | node-version: '14' 20 | 21 | - name: Install Packages 22 | run: npm install 23 | 24 | - name: Close Issue 25 | run: node ci/closeIssue.js 26 | env: 27 | VBOX_DEPLOY_TOKEN: ${{ secrets.VBOX_DEPLOY_TOKEN }} 28 | NUMBER: ${{ github.event.number }} 29 | -------------------------------------------------------------------------------- /.github/workflows/create-pr.yml: -------------------------------------------------------------------------------- 1 | name: Create Pull Request 2 | on: 3 | push: 4 | branches: 5 | - 'submit/*' 6 | 7 | jobs: 8 | pr: 9 | name: Create Pull Request 10 | if: github.event_name == 'push' 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout 14 | uses: actions/checkout@v2 15 | - name: Create Pull Request 16 | uses: repo-sync/pull-request@v2 17 | with: 18 | github_token: ${{ secrets.VBOX_DEPLOY_TOKEN }} 19 | pr_label: 'bot' 20 | -------------------------------------------------------------------------------- /.github/workflows/preview-deploy.yml: -------------------------------------------------------------------------------- 1 | name: 'Build Docs' 2 | 3 | on: push 4 | 5 | jobs: 6 | test: 7 | name: Test 8 | runs-on: ubuntu-latest 9 | strategy: 10 | matrix: 11 | node-version: [14.x] 12 | steps: 13 | - name: Checkout 14 | uses: actions/checkout@v2 15 | with: 16 | submodules: recursive 17 | fetch-depth: 0 18 | - name: Use Node.js ${{ matrix.node-version }} 19 | uses: actions/setup-node@v1 20 | with: 21 | node-version: ${{ matrix.node-version }} 22 | - name: NPM Install 23 | run: | 24 | npm i 25 | npm ci 26 | env: 27 | CI: true 28 | - name: Build 29 | run: | 30 | npm run build 31 | 32 | deploy: 33 | name: Deploy 34 | needs: [test] 35 | if: github.ref == 'refs/heads/master' 36 | runs-on: ubuntu-latest 37 | strategy: 38 | matrix: 39 | node-version: [14.x] 40 | steps: 41 | - name: Checkout 42 | uses: actions/checkout@v2 43 | with: 44 | submodules: recursive 45 | fetch-depth: 0 46 | - name: Use Node.js ${{ matrix.node-version }} 47 | uses: actions/setup-node@v1 48 | with: 49 | node-version: ${{ matrix.node-version }} 50 | - name: NPM Install 51 | run: | 52 | npm i 53 | npm ci 54 | env: 55 | CI: true 56 | - name: Build 57 | run: | 58 | npm run build 59 | - name: Deploy 60 | env: 61 | VBOX_DEPLOY_TOKEN: ${{ secrets.VBOX_DEPLOY_TOKEN }} 62 | run: | 63 | cd docs/.vuepress/dist 64 | git config --global user.email "vboxbot@outlook.com" 65 | git config --global user.name "VBox Deploy Bot" 66 | git init 67 | rm -f Icons/.git 68 | git add -A 69 | git commit -m 'chore':' 🤖 GitHub Pages Deploy in GitHub Actions' 70 | git push -f https://${VBOX_DEPLOY_TOKEN}@github.com/Afanyiyu/VBox-Deploy.git master 71 | -------------------------------------------------------------------------------- /.github/workflows/process-submit.yml: -------------------------------------------------------------------------------- 1 | name: Process Submit 2 | on: 3 | issues: 4 | types: [opened, reopened] 5 | 6 | jobs: 7 | publish: 8 | name: Process Submit 9 | if: github.event_name == 'issues' 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v1 13 | 14 | - name: Use Node.js 14 15 | uses: actions/setup-node@v1 16 | with: 17 | node-version: '14' 18 | 19 | - name: npm install 20 | run: npm i 21 | 22 | - name: Commit Branch 23 | run: node ci/submit.js 24 | env: 25 | ISSUE_NUMBER: ${{ github.event.issue.number }} 26 | ISSUE_BODY: ${{ github.event.issue.body }} 27 | VBOX_DEPLOY_TOKEN: ${{ secrets.VBOX_DEPLOY_TOKEN }} 28 | 29 | - name: Push changes 30 | uses: ad-m/github-push-action@master 31 | with: 32 | github_token: ${{ secrets.VBOX_DEPLOY_TOKEN }} 33 | branch: ${{ format('submit/{0}', github.event.issue.number) }} 34 | force: true 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Node template 3 | # Logs 4 | /logs 5 | *.log 6 | npm-debug.log* 7 | yarn-debug.log* 8 | yarn-error.log* 9 | 10 | # Runtime data 11 | pids 12 | *.pid 13 | *.seed 14 | *.pid.lock 15 | 16 | # Directory for instrumented libs generated by jscoverage/JSCover 17 | lib-cov 18 | 19 | # Coverage directory used by tools like istanbul 20 | coverage 21 | 22 | # nyc test coverage 23 | .nyc_output 24 | 25 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 26 | .grunt 27 | 28 | # Bower dependency directory (https://bower.io/) 29 | bower_components 30 | 31 | # node-waf configuration 32 | .lock-wscript 33 | 34 | # Compiled binary addons (https://nodejs.org/api/addons.html) 35 | build/Release 36 | 37 | # Dependency directories 38 | node_modules/ 39 | jspm_packages/ 40 | 41 | # TypeScript v1 declaration files 42 | typings/ 43 | 44 | # Optional npm cache directory 45 | .npm 46 | 47 | # Optional eslint cache 48 | .eslintcache 49 | 50 | # Optional REPL history 51 | .node_repl_history 52 | 53 | # Output of 'npm pack' 54 | *.tgz 55 | 56 | # Yarn Integrity file 57 | .yarn-integrity 58 | 59 | # dotenv environment variables file 60 | .env 61 | 62 | # parcel-bundler cache (https://parceljs.org/) 63 | .cache 64 | 65 | # Serverless directories 66 | .serverless 67 | 68 | # IDE / Editor 69 | .idea 70 | 71 | # Service worker 72 | sw.* 73 | 74 | # macOS 75 | .DS_Store 76 | 77 | # Vim swap files 78 | *.swp 79 | 80 | docs/.vuepress/dist 81 | dist/ 82 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "docs/.vuepress/public/Icons"] 2 | path = docs/.vuepress/public/Icons 3 | url = https://github.com/vbox-moe/Icons.git 4 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "arrowParens": "always", 4 | "singleQuote": true 5 | } 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 |

VBox

6 | 7 |

Universal document website that All People Can Contribute.

8 |

人人都可参与贡献的统一文档平台。

9 | 10 |

For more information, please visit https://vbox.moe.

11 | 12 |
13 | -------------------------------------------------------------------------------- /ci/closeIssue.js: -------------------------------------------------------------------------------- 1 | const axios = require('axios') 2 | 3 | const { NUMBER, VBOX_DEPLOY_TOKEN } = process.env 4 | 5 | const log = (msg) => { 6 | console.log(msg) 7 | return msg 8 | } 9 | 10 | axios 11 | .get(log(`https://api.github.com/repos/vbox-moe/VBox/pulls/${NUMBER}`)) 12 | .then(async (response) => { 13 | const { 14 | head: { label } 15 | } = response.data 16 | if (label.startsWith('vbox-moe:submit/')) { 17 | const issueNumber = Number(label.replace('vbox-moe:submit/', '')) 18 | await axios.post( 19 | log( 20 | `https://api.github.com/repos/vbox-moe/VBox/issues/${issueNumber}/comments` 21 | ), 22 | { 23 | body: `Closing since #${NUMBER} is closed.` 24 | }, 25 | { 26 | headers: { 27 | Authorization: `token ${VBOX_DEPLOY_TOKEN}` 28 | } 29 | } 30 | ) 31 | await axios.patch( 32 | log(`https://api.github.com/repos/vbox-moe/VBox/issues/${issueNumber}`), 33 | { 34 | state: 'closed' 35 | }, 36 | { 37 | headers: { 38 | Authorization: `token ${VBOX_DEPLOY_TOKEN}` 39 | } 40 | } 41 | ) 42 | } else { 43 | console.log('Not bot. Skipped.') 44 | } 45 | }) 46 | -------------------------------------------------------------------------------- /ci/submit.js: -------------------------------------------------------------------------------- 1 | const { writeFile } = require('fs').promises 2 | const { join } = require('path') 3 | const { GitProcess } = require('dugite') 4 | 5 | const { ISSUE_NUMBER, ISSUE_BODY, VBOX_DEPLOY_TOKEN } = process.env 6 | const branchName = `submit/${ISSUE_NUMBER}` 7 | const remote = `https://${VBOX_DEPLOY_TOKEN}@github.com/vbox-moe/VBox.git` 8 | 9 | const decodeBase64 = (base64) => String(Buffer.from(base64, 'base64')) 10 | 11 | const gitExec = async (params) => { 12 | const { stdout, stderr } = await GitProcess.exec(params, process.cwd(), { 13 | env: { 14 | GIT_AUTHOR_NAME: 'VBox Bot', 15 | GIT_AUTHOR_EMAIL: 'vboxbot@outlook.com', 16 | GIT_COMMITTER_NAME: 'VBox Bot', 17 | GIT_COMMITTER_EMAIL: 'vboxbot@outlook.com' 18 | } 19 | }) 20 | console.log(stdout + ' (stdout)') 21 | console.log(stderr + ' (stderr)') 22 | } 23 | 24 | ;(async () => { 25 | await gitExec(['branch', branchName]) 26 | await gitExec(['checkout', branchName]) 27 | const block = ISSUE_BODY.split('-----END SUBMIT BLOCK-----')[0].split( 28 | '-----BEGIN SUBMIT BLOCK-----' 29 | )[1] 30 | if (block) { 31 | console.log( 32 | `Submit block finded. Start processing with issue#${ISSUE_NUMBER}` 33 | ) 34 | console.log('BLOCK:' + block) 35 | const blockData = decodeBase64(block) 36 | .split(':') 37 | .map((x) => decodeBase64(x)) 38 | console.log('PATH: ' + blockData[0]) 39 | const path = join('docs', blockData[0]) 40 | await writeFile(path, blockData[1]) 41 | await gitExec(['push', '--set-upstream', remote, branchName]) 42 | await gitExec(['add', 'docs']) 43 | await gitExec([ 44 | 'commit', 45 | '-m', 46 | 'update', 47 | '-m', 48 | ISSUE_BODY, 49 | '-m', 50 | `close #${ISSUE_NUMBER}` 51 | ]) 52 | } else console.log('Skipped with no submit block.') 53 | })() 54 | -------------------------------------------------------------------------------- /docs/.vuepress/config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | title: 'VBox', 3 | description: '', 4 | theme: 'vuepress-theme-vbox', 5 | themeConfig: { 6 | oauth: { 7 | prod: { 8 | id: '310cf13f466eff204be5', 9 | secret: '5fdfe06de0150e3b49283ff7857e01cd5f2b8063' 10 | }, 11 | dev: { 12 | id: '7f2011f57623382ebea6', 13 | secret: '36867f1788921671d595a5ab0166661ca92c98cd' 14 | } 15 | }, 16 | publicToken: 'd72d52658cfd3244fba4fb17e47505db6aa5c013', 17 | host: 'https://vbox.moe', 18 | repo: 'vbox-moe/VBox', 19 | repoLabel: '查看文档', 20 | docsDir: 'docs', 21 | editLinks: true, 22 | editLinkText: '编辑' 23 | }, 24 | head: [['link', { rel: 'icon', href: '/logo.png' }]], 25 | evergreen: true 26 | } 27 | -------------------------------------------------------------------------------- /docs/.vuepress/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vbox-moe/VBox/5cb51e376cfc3316168753f7cac8d0af63d5c0f5/docs/.vuepress/public/favicon.ico -------------------------------------------------------------------------------- /docs/.vuepress/public/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vbox-moe/VBox/5cb51e376cfc3316168753f7cac8d0af63d5c0f5/docs/.vuepress/public/logo.png -------------------------------------------------------------------------------- /docs/.vuepress/public/netlify.toml: -------------------------------------------------------------------------------- 1 | [[redirects]] 2 | from = "/*" 3 | to = "/index.html" 4 | status = 200 5 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | metaTitle: VBox 3 | footer: MIT Licensed | © 2020 VBox / Il Harper 4 | --- 5 | 6 | # 欢迎回来! 7 | 8 | 向下滑动以寻找需要的文档。祝您度过美好的一天。 9 | -------------------------------------------------------------------------------- /docs/Ruminoid/Develop/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | pageIndex: 2 3 | title: 面向开发者的 Ruminoid 4 | --- 5 | 6 | 这里是「面向开发者的 Ruminoid」部分。 7 | 8 | 若要查看数据结构和文件格式等的参考,请转到「[参考](./Reference/)」。 9 | -------------------------------------------------------------------------------- /docs/Ruminoid/Develop/Reference/PluginInterface/IInterfacePlugin.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: IInterfacePlugin 3 | pageIndex: 3 4 | --- 5 | 6 | IInterfacePlugin 是 Ruminoid Studio 中用于实现用户界面的接口,窗口、菜单等等都需要使用这个接口实现。 7 | 8 | IInterfacePlugin 中的成员如下。 9 | 10 | ## Name 11 | 12 | 用户界面插件的名称。 13 | 14 | ```cs 15 | public string Name { get; set; } 16 | ``` 17 | -------------------------------------------------------------------------------- /docs/Ruminoid/Develop/Reference/PluginInterface/ITarget.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: ITarget 3 | pageIndex: 2 4 | --- 5 | 6 | ITarget 是 Ruminoid Studio 中用于实现「目标(Target)」的接口实现。 7 | 8 | ITarget 中只有 Render() 这一主要方法。这一方法用于接受一个 `RumiItem` 对象,将该对象中提供的 `Text` 值渲染为一个 `OpenGL.GL.RGBA` 指针。 9 | 10 | ```cs 11 | public unsafe IntPtr Render(RumiItem item); 12 | ``` 13 | 14 | 你可以通过获取 `item.Text` 属性来访问值,也可以通过 `item.Parent` 属性来访问其所在的 `RumiRow`。 15 | 16 | 渲染必须在这个方法内同步完成。同时,你也可以使用不安全的代码。 17 | -------------------------------------------------------------------------------- /docs/Ruminoid/Develop/Reference/PluginInterface/ITransform.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: ITransform 3 | pageIndex: 1 4 | --- 5 | 6 | ITransform 是 Ruminoid Studio 中用于实现「转换(Transform)」的接口实现。 7 | 8 | ITransform 中只有 Convert() 这一主要方法。 9 | 10 | ```cs 11 | public string Convert(RumiItem item); 12 | ``` 13 | 14 | 你可以通过获取 `item.Text` 属性来访问值,也可以通过 `item.Parent` 属性来访问其所在的 `RumiRow`。 15 | 16 | 转换必须在这个方法内同步完成。你可以通过使用多线程来进行处理,但是若要使用 `Task`,请确保你等待了结果的返回。 17 | -------------------------------------------------------------------------------- /docs/Ruminoid/Develop/Reference/PluginInterface/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 插件接口列表 3 | pageIndex: 2 4 | --- 5 | 6 | 下面列举了 Ruminoid Studio 所提供的插件接口列表。 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /docs/Ruminoid/Develop/Reference/Project/ColumnAndTarget.md: -------------------------------------------------------------------------------- 1 | --- 2 | pageIndex: 2 3 | title: 列和目标 4 | --- 5 | 6 | ## 列(Column) 7 | 8 | 「列(Column)」是 Ruminoid Project 文件中存储「目标(Target)」相关信息的部分,这些信息会在字幕进行渲染的时候用到。一个 Ruminoid Project 文件中可以有多个列,它们作为一个 JSON 数组位于 `columns` 节点下。 9 | 10 | ### `id` 11 | 12 | 该列的唯一 ID,使用 GUID 标识。 13 | 14 | ### `title` 15 | 16 | 该列的标题。 17 | 18 | ### `transform` 19 | 20 | 参见「转换(Transform)」。 21 | 22 | ### `target` 23 | 24 | 参见「目标(Target)」。 25 | 26 | ## 转换(Transform) 27 | 28 | 转换(Transform)是一个可为空的对象,它存储了该列上要进行的值转换的操作。 29 | 30 | **Ruminoid Studio 在进行「渲染(Render)」或「生成(Build)」时,若该列的转换存在,则会将行(Row)中的 `items` 字段中的 `text` 字段使用「转换(Transform)」对值进行修改,之后再交给「目标(Target)」进行字幕的渲染。** 31 | 32 | 转换由「Ruminoid 插件(RMX)」通过实现 `ITransform` 接口提供,如 `JavaScriptTransform` 等。 33 | 34 | 大多数情况下,我们无需使用转换。 35 | 36 | ## 目标(Target) 37 | 38 | 目标(Target)存储了该列在渲染字幕时所使用的生成器。目标负责将输入的字段转换成 `OpenGL.GL.RGBA` 格式的图像,并交由 Ruminoid Studio、Ruminoid Exporter 或 R/Connect 进行显示。 39 | 40 | 目标由「Ruminoid 插件(RMX)」通过实现 `ITarget` 接口提供,如 `LibAssTarget`、`VsFilterTarget` 等。 41 | 42 | ## 项目设置中的 `textColumn` 和 `effectColumn` 43 | 44 | Ruminoid Studio 只会同时渲染一列的内容。`textColumn` 指示了 Ruminoid Studio 应该渲染哪一列的内容。 45 | 46 | `effectColumn` 则与 ASS 中的 Effect 列兼容,指示该行的效果类型。 47 | 48 | ## 行(Row)中的 `items` 49 | 50 | 既然一个 Ruminoid Project 文件中可以有多个列,那么每一行中就需要存储每一列的信息了。这些数据存储在行(Row)中的 `items` 字段。 51 | 52 | `items` 字段是一个 JSON Array,里面包含了含有信息的各列的信息。 53 | 54 | ```ts 55 | declare interface Row { 56 | items: Array 57 | } 58 | 59 | declare interface RumiItem { 60 | id: string // ID 61 | column: string // 列 ID 62 | text: string // 文本 63 | } 64 | ``` 65 | -------------------------------------------------------------------------------- /docs/Ruminoid/Develop/Reference/Project/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | pageIndex: 1 3 | title: Ruminoid Project(.rmproj) 4 | --- 5 | 6 | Ruminoid Project (.rmproj)文件是 Ruminoid Studio 的项目文件,使用 JSON 格式存储。一个 Ruminoid Project 文件存储了该 Ruminoid 项目中所包含的全部**内部资源**。(关于什么是外部资源,请参见「外部资源」)。Ruminoid Project 由以下几个部分组成。 7 | 8 | ## 项目设置(Project Settings) 9 | 10 | 「项目设置(Project Settings)」中包含了一个 Ruminoid 项目中需要具有的全部的配置属性,位于 `settings` 节点。它类似于 ASS 文件中的 `[Script Info]` 节点。 11 | 12 | ### `title` 13 | 14 | 项目的标题。 15 | 16 | ### `width` 17 | 18 | int 类型,指示字幕画布的宽度。这个值需要能够被 2 整除。 19 | 20 | ### `height` 21 | 22 | int 类型,指示字幕画布的高度。这个值需要能够被 2 整除。 23 | 24 | ### `textColumn` 和 `effectColumn` 25 | 26 | 有关这些字段的详细内容,请参见「[列和目标](./ColumnAndTarget.html)」。 27 | 28 | ::: tip 提示 29 | 30 | 诸如 `remoteUrl`、`commit` 和 `author` 等等的配置不在 Ruminoid Project 文件中。它们位于 Ruminoid PackageSpec(`rmnd-package.json`)文件中。该文件中同时包含了一个 `main` 字段用于指示 Ruminoid Project 文件在项目目录中的位置。 31 | 32 | ::: 33 | 34 | ## 样式(Styles) 35 | 36 | 有关样式的详细内容,请参见「[样式](./Styles.html)」。 37 | 38 | ## 行(Row) 39 | 40 | 「行(Row)」是 Ruminoid Project 文件中存储字幕文本和其他信息的部分,是项目文件的核心。一个 Ruminoid Project 文件中可以有多个行,一行包含一个时间段内的字幕信息。它们作为一个 JSON 数组位于 `rows` 节点下。 41 | 42 | ### `id` 43 | 44 | 该行的唯一 ID,使用 GUID 标识。 45 | 46 | ### `type` 47 | 48 | string 类型。 49 | 50 | ```ts 51 | declare interface Row { 52 | type: 'Comment' | 'Dialogue' 53 | } 54 | ``` 55 | 56 | ### `layer` 57 | 58 | ### `style` 59 | 60 | ### `start` 61 | 62 | ### `end` 63 | 64 | ### `items` 65 | 66 | 有关 `items` 字段的详细内容,请参见「[列和目标](./ColumnAndTarget.html)」。 67 | 68 | ## 列(Column) 69 | 70 | 「列(Column)」是 Ruminoid Project 文件中存储「目标(Target)」相关信息的部分,这些信息会在字幕进行渲染的时候用到。 71 | 72 | 有关列和目标的详细内容,请参见「[列和目标](./ColumnAndTarget.html)」。 73 | 74 | ## 元数据(Metadata) 75 | 76 | 「元数据(Metadata)」中包含了 Ruminoid Project 文件本身的一些特征,例如 Ruminoid Project 的架构版本和一些其他的信息。它们位于 `meta` 节点。 77 | 78 | ### `version` 79 | 80 | int 类型,指示该项目文件的架构版本。 81 | 82 | Ruminoid Studio 会去解析它们所支持的版本,或是版本 0。若某个版本不被 Ruminoid Studio 所支持,Ruminoid Studio 将会询问你是否尝试解析。 83 | 84 | 所有版本的 Ruminoid Studio 都会尝试解析版本 0 的 Ruminoid Project,但版本 0 只应该在测试项目文件或兼容性的时候使用。 85 | 86 | 目前,所有位于 stable 和 newrelease 上的 Ruminoid Studio 版本都只支持版本 1。 87 | -------------------------------------------------------------------------------- /docs/Ruminoid/Develop/Reference/Project/Styles.md: -------------------------------------------------------------------------------- 1 | --- 2 | pageIndex: 3 3 | title: 样式(Styles) 4 | --- 5 | 6 | 「样式(Styles)」是描述 Ruminoid 中字幕样式信息的部分,它们作为一个 JSON 数组位于 `styles` 节点下。 7 | -------------------------------------------------------------------------------- /docs/Ruminoid/Develop/Reference/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | pageIndex: 90 3 | title: 参考 4 | --- 5 | 6 | 这里是「参考」部分。这里列出了一些与 Ruminoid 相关的文件格式、架构和数据结构等的文档。轻敲以选择您想要查看的内容。 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /docs/Ruminoid/Guide/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | pageIndex: 1 3 | title: 快速上手 4 | --- 5 | 6 | 快速上手 7 | -------------------------------------------------------------------------------- /docs/Ruminoid/Guide/ToolboxReference/Compatibility.md: -------------------------------------------------------------------------------- 1 | --- 2 | pageIndex: 20 3 | title: Toolbox 兼容性疑难解答 4 | --- 5 | 6 | 欢迎来到 Ruminoid Toolbox 兼容性疑难解答。这里详细描述了 Toolbox 在各个平台上的缺陷、缺失的工具和行为。 7 | 8 | :::tip 提示 9 | 10 | 下方的工具文件名称统一不带 `.exe`,命令统一在 `tools` 目录下执行。 11 | 12 | ::: 13 | 14 | ## 我应该如何补充缺失的工具? 15 | 16 | 你可以从下面的两种方式中任选其一,进行缺失工具的补充。 17 | 18 | ### 下载并覆盖 19 | 20 | 搜索工具的官网,并下载二进制文件。将下载的二进制文件放置在 `tools` 目录下。 21 | 22 | ### 创建软连接(高级) 23 | 24 | 通过创建软连接的方式,你可以指示 Toolbox 使用安装在本地计算机上的工具实例运行操作。 25 | 26 | 在 Windows 上,你可以使用: 27 | 28 | ```sh 29 | mklink <目标> <源> 30 | ``` 31 | 32 | 如: 33 | 34 | ```sh 35 | mklink node "C:\Program Files\nodejs\node.exe" 36 | ``` 37 | 38 | 在 MacOS/Linux 上,你可以使用: 39 | 40 | ```sh 41 | ln -s <源> <目标> 42 | ``` 43 | 44 | 如: 45 | 46 | ```sh 47 | ln -s /opt/node/bin/node ./node 48 | ``` 49 | 50 | ## FFmpeg 51 | 52 | `ffserver` 仅在 MacOS 上可用。 53 | 54 | ## x264 55 | 56 | 该工具没有兼容性问题。 57 | 58 | ## PowerShell 59 | 60 | 该工具没有兼容性问题。 61 | 62 | ## Node.js 63 | 64 | 在 MacOS 上,你需要自己安装 `npm`。 65 | 66 | 在 Linux 上,你需要自己解压 `node.tar.xz`。 67 | 68 | ## Python 69 | 70 | 在 MacOS 上,你需要自己编译 `Python`。由于编译 `Python` 较为困难,我们推荐使用软连接。 71 | 72 | 在 Linux 上,你需要自己编译 `Python`。由于编译 `Python` 较为困难,我们推荐使用软连接。 73 | 74 | ## Lua/Json.lua 75 | 76 | 该工具没有兼容性问题。 77 | 78 | ## 7za 79 | 80 | 该工具没有兼容性问题。 81 | 82 | ## VapourSynth 83 | 84 | 在 MacOS 上,你需要自己安装 `VapourSynth`。`VapourSynth` 官网给出了详细的安装教程。 85 | 86 | 在 Linux 上,你需要自己安装 `VapourSynth`。`VapourSynth` 官网给出了详细的安装教程。 87 | 88 | ## QAAC 89 | 90 | 在 MacOS 上,你需要使用软连接的方式加入 `QAAC` 支持。 91 | 92 | 在 Linux 上,你需要自行搜索适用于 Linux 的 `QAAC` 二进制文件。 93 | 94 | ## HwEnc 95 | 96 | 在 MacOS 上,你需要自己编译 HwEnc。 97 | 98 | 在 Linux 上,你需要自己编译 HwEnc。 99 | -------------------------------------------------------------------------------- /docs/Ruminoid/Guide/ToolboxReference/Debug.md: -------------------------------------------------------------------------------- 1 | --- 2 | pageIndex: 8 3 | title: Toolbox 除错指南 4 | --- 5 | 6 | 欢迎来到 Ruminoid Toolbox 除错指南。按照这个文章的步骤进行操作,你将能够自己解决使用 Rmbox 过程中遇到的大部分错误。 7 | 8 | 那么,让我们开始吧。不过在开始之前…… 9 | 10 | ## 开始之前…… 11 | 12 | Toolbox FAQ 负责解决你 **在使用 Rmbox 的过程中遇到的报错**。 13 | 14 | 如果你是 **在使用 Rmbox 之前想要了解一些问题**,请参考 [Toolbox FAQ](./Faq.html)。 15 | 16 | ## 第一步:如果你能够确定…… 17 | 18 | 如果你能够确定你的报错是由 **缺少插件或工具** 引起的,那么你可以参考 [Toolbox 兼容性疑难解答](./Compatibility.html)。 19 | 20 | 如果你能够确定你的报错是由 **路径或文件名错误** 引起的,请将输入输出目录均放置于 **不含空格、只含英文的目录-文件名** 下。 21 | 22 | ## 第二步:查阅「常见报错一览」 23 | 24 | 首先,你应该找到报错的 **关键信息**,它一般位于 `处理命令时出现错误。` 的上一行或上两行左右,但也可能在较上部。 25 | 26 | 比如: 27 | 28 | ``` 29 | 21-04-04 02:49:05 info: ProcessRunner [null]Node.js is only supported on Windows 8.1, Windows Server 2012 R2, or higher. 30 | 21-04-04 02:49:05 info: ProcessRunner [null]Setting the NODE_SKIP_PLATFORM_CHECK environment variable to 1 skips this 31 | 21-04-04 02:49:05 info: ProcessRunner [null]check, but Node.js might not execute correctly. Any issues encountered on 32 | 21-04-04 02:49:05 info: ProcessRunner [null]unsupported platforms will not be fixed. 33 | 21-04-04 02:49:05 crit: ProcessRunner 命令运行 出现错误,退出码为 216。 34 | 21-04-04 02:49:05 crit: Processor 处理命令时出现错误。 35 | Ruminoid.Toolbox.Core.ProcessRunnerException: 命令运行 出现错误,退出码为 216。 36 | at Ruminoid.Toolbox.Core.ProcessRunner.Run(String target, String args, String formatter) in D:\a\Toolbox\Toolbox\src\rmbox\Core\ProcessRunner.cs:line 181 37 | at Ruminoid.Toolbox.Core.ProcessRunner.Run(TaskCommand command) in D:\a\Toolbox\Toolbox\src\rmbox\Core\ProcessRunner.cs:line 192 38 | at System.Collections.Generic.List`1.ForEach(Action`1 action) 39 | at Ruminoid.Toolbox.Core.ProcessRunner.Run(List`1 commands) in D:\a\Toolbox\Toolbox\src\rmbox\Core\ProcessRunner.cs:line 202 40 | at Ruminoid.Toolbox.Core.Processor..ctor(CommandLineHelper commandLineHelper, ProjectParser projectParser, ProcessRunner processRunner, ILogger`1 logger) in D:\a\Toolbox\Toolbox\src\rmbox\Core\Processor.cs:line 25 41 | ``` 42 | 43 | 这里的 44 | 45 | ``` 46 | Node.js is only supported on Windows 8.1, Windows Server 2012 R2, or higher. 47 | Setting the NODE_SKIP_PLATFORM_CHECK environment variable to 1 skips this 48 | check, but Node.js might not execute correctly. Any issues encountered on 49 | unsupported platforms will not be fixed. 50 | ``` 51 | 52 | 就是关键信息。机翻可得到,这是由于 Node.js 的版本不兼容导致的。(这个 bug 在最新版本已经被修复。) 53 | 54 | 得到关键信息之后,你可以查阅 [Toolbox 常见报错一览](./Errors.html)。 55 | 56 | ## 第三步:尝试理解报错 57 | 58 | Rmbox 及其插件的报错都是有意义的。 59 | 60 | 如果你在上一步中拿到的报错是中文的,请尝试理解它的意思,并按照它所说的去做。如果不是中文的,请机翻后尝试理解。 61 | 62 | ## 第四步:参考 Toolbox FAQ 63 | 64 | 参考 [Toolbox FAQ](./Faq.html),你或许可以从这里找到答案。 65 | 66 | ## 我依旧无法解决我的问题。 67 | 68 | 请加入 Ruminoid 交流群:470394928。仔细阅读群规,然后提交报错。 69 | -------------------------------------------------------------------------------- /docs/Ruminoid/Guide/ToolboxReference/Errors.md: -------------------------------------------------------------------------------- 1 | --- 2 | pageIndex: 15 3 | title: Toolbox 常见报错一览 4 | --- 5 | 6 | 这里收集了 Rmbox 的常见报错。请善用 `Ctrl+F` 进行页面内搜索。 7 | 8 | ## `mov,mp4,m4a,3gp,3g2,mj2` 9 | 10 | 常见报错: 11 | 12 | ``` 13 | info: ProcessRunner [ffmpeg] libpostproc 55. 7.100 / 55. 7.100 14 | info: ProcessRunner [ffmpeg]Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'test.mp4': 15 | info: ProcessRunner [ffmpeg] Stream #0:1 -> #0:0 (copy) 16 | info: ProcessRunner [ffmpeg] Last message repeated 1 times 17 | crit: ProcessRunner ffmpeg 出现错误,退出码为 1。 18 | crit: Processor 处理命令时出现错误。 19 | Ruminoid.Toolbox.Core.ProcessRunnerException: ffmpeg 出现错误,退出码为 1。 20 | ``` 21 | 22 | 该报错是因为输出指定了不兼容的格式。按照下列情况检查: 23 | 24 | - 如果你在抽取音频,请将输出格式设定为 `m4a`。如果你需要 `mp3` 音频请使用「音频压制」操作。 25 | 26 | ## `Node.js is only supported on Windows 8.1, Windows Server 2012 R2, or higher.` 27 | 28 | 常见报错: 29 | 30 | ``` 31 | info: ProcessRunner [null]Node.js is only supported on Windows 8.1, Windows Server 2012 R2, or higher. 32 | info: ProcessRunner [null]Setting the NODE_SKIP_PLATFORM_CHECK environment variable to 1 skips this 33 | info: ProcessRunner [null]check, but Node.js might not execute correctly. Any issues encountered on 34 | info: ProcessRunner [null]unsupported platforms will not be fixed. 35 | crit: ProcessRunner 命令运行 出现错误,退出码为 216。 36 | crit: Processor 处理命令时出现错误。 37 | Ruminoid.Toolbox.Core.ProcessRunnerException: 命令运行 出现错误,退出码为 216。 38 | ``` 39 | 40 | 该报错是因为 Node.js 版本不兼容。 41 | 42 | Rmbox 的最新版本已经解决了这个问题,请更新 Rmbox 至最新版本。 43 | -------------------------------------------------------------------------------- /docs/Ruminoid/Guide/ToolboxReference/Faq.md: -------------------------------------------------------------------------------- 1 | --- 2 | pageIndex: 10 3 | title: Toolbox FAQ 4 | --- 5 | 6 | 欢迎来到 Ruminoid Toolbox FAQ。这里收集了许多 Toolbox 相关的常见问题,希望它们可以帮到你。 7 | 8 | 如果你有什么问题和答案想要添加的话,你可以点击下方的「编辑」按钮。 9 | 10 | ## 开始之前…… 11 | 12 | Toolbox FAQ 负责解决你在 **使用 Rmbox 之前想要了解的一些问题**。 13 | 14 | 如 `我是否可以在 Windows XP 上使用 Rmbox?` `我能否无损添加字幕?` 这样的问题,你可以参考 FAQ 得到解决。 15 | 16 | 如果你是 **在使用 Rmbox 的过程中遇到了报错**,请参考 [Toolbox 除错指南](./Debug.html)。 17 | 18 | ## Q1:Does Rmbox have a plan to support more languages? 19 | 20 | Currently no. Sorry about that. 21 | 22 | ## Q2:我如何在 Windows XP 上使用 Rmbox? 23 | 24 | 你无法在 Windows XP 上使用 Rmbox。 25 | 26 | ## Q3:我如何在 32 位系统上使用 Rmbox? 27 | 28 | 你无法在 32 位系统上使用 Rmbox。 29 | 30 | ## Q4:我是否可以在基于 Apple M1 的电脑上使用 Rmbox? 31 | 32 | 目前尚未收到使用基于 Apple M1 的电脑的用户反馈的兼容性报告。如果 Rmbox 在你的电脑上无法启动,则表明 Rmbox 无法在基于 Apple M1 的电脑上使用。 33 | 34 | ## Q5:我如何在 Rmbox 中使用 AviSynth? 35 | 36 | 你无法在 Rmbox 中使用 AviSynth。请使用 VapourSynth。 37 | 38 | ## Q6:我的杀毒软件报告了一个病毒或威胁。 39 | 40 | Rmbox 是开源软件,它的本体不会对你的电脑造成威胁。 41 | 42 | 如果你不信任上述描述的话,你可以从 [此处](https://github.com/Ruminoid/Toolbox) 获取 Rmbox 的源代码并自行检视和编译。我们不会对此提供支持。 43 | 44 | 如果 Rmbox 携带的工具报告了病毒或威胁,请咨询该工具的开发者。 45 | 46 | ## Q7:我的视频在处理之后画面反了。 47 | 48 | 你的视频的画面方向本来就是反的。你可以使用「画面方向纠正」操作纠正。 49 | 50 | ## Q8:我使用 2Pass 压制,将码率设定得比原视频还要高。我的视频画质会提升吗? 51 | 52 | 不会。 53 | 54 | ## Q9:如何给视频添加水印? 55 | 56 | 请学习 Premiere、VapourSynth等可以完成这类需求的软件。 57 | 58 | ## Q10:Rmbox 有 MacOS/Linux 版本吗? 59 | 60 | 有。 61 | 62 | ## Q11:什么是 VFR? 63 | 64 | VFR 视频指可变帧率视频,遇到该类视频源请先参照 Q10的方法处理。 65 | 66 | ## Q12:如何处理 VFR 视频? 67 | 68 | 首先使用「FFmpeg压制」操作处理一遍视频,之后再进行常规的处理。 69 | 70 | ## Q13:使用「小丸(CPU)压制」压制之后,视频音画不同步怎么办? 71 | 72 | 将分离器修改为 `lavf` 重试。如果还不能解决,请参考 Q10 重试。 73 | 74 | ## Q14:如何提高我的操作的运行速度? 75 | 76 | 这里给出几条建议: 77 | 78 | - 如果你在使用「小丸(CPU)压制」,你可以更换 `x264` 核心为 `7mod` 版来提升速度。 79 | 80 | - 升级你的设备。 81 | 82 | - 检查系统的电源方案和性能配置。 83 | 84 | ## Q15:如何无损添加字幕? 85 | 86 | 如果你只是希望封装字幕而不改变视频的话,请使用「内封字幕」操作。 87 | 88 | 无损添加硬字幕不存在。硬字幕要想渲染到画面上必须经过重编码。 89 | 90 | 你可以使用压制操作中的「无损」预设,这样压制出来的视频画面是近似无损的。 91 | 92 | ## Q16:为什么压制后的视频体积反而更大? 93 | 94 | 这里引用视频教程中我说过的话: 95 | 96 | 视频的「压制」并非指视频的「压缩」而是指「重编码」,因此压制后的视频比原视频体积大是正常的,并且是非常常见的现象。 97 | 98 | 如果你想让视频体积更小的话,你需要提高CRF/CQP数值,或降低2Pass码率。 99 | 100 | ## Q17:抖音/快手/哔哩哔哩相关 101 | 102 | 此处不会有相关问题的解答,并且,群内禁止讨论相关问题,违者会被警告。 103 | 104 | 这里只留一条建议:「请将高码率视频直接投喂平台」。 105 | 106 | ## Q18:程序出现了bug/程序停止工作。 107 | 108 | 请加入群聊并将问题反馈给我。 109 | 110 | ## Q19:Rmbox 可以处理 qlv/qsv/blv/exe 等格式的文件吗? 111 | 112 | 不可以,且今后也不会加入相关支持。 113 | 114 | 如果有第三方插件开发者加入了对相关格式的支持,请咨询第三方开发者。在群内提出相关问题会被警告。 115 | 116 | ## Q20:操作的输入、输出文件名能否相同? 117 | 118 | 不行。 119 | 120 | 如果你由于误操作将源文件覆盖了,请不要在群里提问。源文件无法找回。 121 | 122 | ## Q21:Rmbox 的显示字体是宋体。 123 | 124 | ## Q22:Rmbox 的显示很奇怪。 125 | 126 | 你的系统安装出现了问题,或者可能是盗版。尝试解决你系统中存在的问题。 127 | 128 | 如果你是 Linux 用户,你可以通过尝试安装 `ttf-mscorefonts-installer`、`ubuntu-restricted-extra` 或其他同类型的软件包来尝试解决问题。 129 | 130 | ## Q23:HEVC、H265、AV1 是什么? 131 | 132 | 如果你正在处理你的文件并且不知道他们是什么,请不要使用它们。保留操作的默认配置,不要更改。 133 | 134 | 如果你希望了解关于这些专有名词的更多信息,你可以使用搜索引擎搜索它们。 135 | 136 | ## Q24:压制或重编码,理论上有可能做到无损吗? 137 | 138 | 理论是可行的,但已经没有人这样做了。目前虽然仍然存在针对视频的无损压缩算法,但并没有任何人正在使用,也就自然没有这种算法的实现。 139 | 140 | ## Q25:为什么想要保留画质需要使用 CPU 压制?显卡压制对画质的影响很大吗? 141 | 142 | 对于压制组等专业压制的人来说,显卡压制的画质水平属于「根本没法看」的程度。但大部分人毕竟没有太高的需求,所以如果你不是专业的视频爱好者或研究者的话,使用显卡压制当然是更划算的选择。 143 | 144 | ## 我没有找到我的问题。 145 | 146 | 请加入 Ruminoid 交流群:470394928。仔细阅读群规,然后提出你的问题。 147 | -------------------------------------------------------------------------------- /docs/Ruminoid/Guide/ToolboxReference/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | pageIndex: 100 3 | title: Toolbox 参考 4 | --- 5 | 6 | 这里是 Ruminoid Toolbox 的参考。侧栏中包含了所有的参考文章。 7 | -------------------------------------------------------------------------------- /docs/Ruminoid/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | home: true 3 | heroImage: /Icons/Icons/Ruminoid/Ruminoid.Colored.Transparent.png 4 | heroText: Ruminoid 5 | tagline: 高效、现代的视觉工作解决方案。 6 | actionText: 立即下载 → 7 | actionLink: /Ruminoid/#下载 8 | pageIndex: 10 9 | footer: MIT Licensed | Copyright © 2020 Il Harper 10 | --- 11 | 12 | ## 下载 13 | 14 | 如果你要下载 SubLight、Trimmer 或 LIVE 等产品,请选择下方的「下载 Ruminoid(v0.2.x)」。如果你要下载 Ruminoid Toolbox,请选择下方的「下载 Ruminoid Toolbox」。 15 | 16 | ### 下载 Ruminoid(v0.2.x) 17 | 18 | 轻敲下面的按钮即可下载 Ruminoid 的最新版本。 19 | 20 | 21 | 22 | 有关 Ruminoid(v0.2.x)或 Ruminoid Studio(v0.3.x)的更新历史,请参见 [更新历史](./Version/History.html)。 23 | 24 | ### 下载 Ruminoid Toolbox 25 | 26 | 前往 Ruminoid Toolbox 下载中心: 27 | 28 | 29 | 30 | 若要更新 Ruminoid Toolbox,下载最新版本后解压并覆盖原来的展开即可。 31 | 32 | 有关在 Ruminoid Toolbox 内排除错误,请参加 [Toolbox 除错指南](./Guide/ToolboxReference/Debug.html)。 33 | 34 | 有关 Ruminoid Toolbox 的常见问题,请参见 [Toolbox FAQ](./Guide/ToolboxReference/Faq.html)。 35 | 36 | ## 指南 37 | 38 | 下面是 Ruminoid 的指南和教程部分。 39 | 40 | 41 | 42 | 43 | 44 | ## 交流和讨论 45 | 46 | Ruminoid 相关交流和字幕相关交流可以加入群聊 470394928。 47 | -------------------------------------------------------------------------------- /docs/Ruminoid/Version/History.md: -------------------------------------------------------------------------------- 1 | --- 2 | pageIndex: 2 3 | title: 更新历史 4 | --- 5 | 6 | 这里是 Ruminoid 的更新历史。 7 | 8 | 此处包含的产品有: 9 | 10 | - Ruminoid(v0.2.x) 11 | 12 | - Ruminoid Studio(v0.3.x) 13 | 14 | - Ruminoid Dashboard、R/Connect 等 v0.2.x 以上的 Ruminoid 公共组件 15 | 16 | 此处不包含的产品有: 17 | 18 | - Ruminoid World 19 | 20 | - Ruminoid Toolbox 21 | 22 | 特定于通道的更新会在版本的右上角显示 Badge。标为“已弃用”的版本不推荐下载,因为这些版本的自动更新服务已经失效,或在更新到新版本之后旧的偏好设置会被清除。 23 | 24 | ## v0.2.8 25 | 26 | Build 11 - 2021-05-01 发布 27 | 28 | ### Ruminoid SubLight Classic 29 | 30 | 改进: 31 | 32 | - 现在,SubLight 会自动识别字幕文件的文件编码了。 33 | 34 | ## v0.2.7 35 | 36 | Build 10 - 2021-04-27 发布 37 | 38 | ### Ruminoid SubLight Classic 39 | 40 | 警告:因为增加了新的参数,更新这个版本的 SubLight 将会导致你的工程文件无法打开。在升级 SubLight 之前请务必移除旧版 SubLight 效果,保存工程,之后再进行升级。 41 | 42 | 如果你错误地进行了升级,你可以回退旧版并重新打开你的工程进行处理。 43 | 44 | 改进: 45 | 46 | - 新增了「叠加比率」参数。通过控制此参数,你可以控制 **字幕/原图层** 与下方图层进行叠加的比率。 47 | 48 | 比率为 0(默认)则使用字幕与下方叠加,比率为 65535(最大)则使用原图层与下方叠加。 49 | 50 | - 现在,在 SubLight 渲染过程中,After Effects 会显示表示渲染进度的进度条。 51 | 52 | ## v0.2.6 53 | 54 | Build 9 - 2021-04-24 发布 55 | 56 | ### Ruminoid SubLight Classic 57 | 58 | 修复: 59 | 60 | - 现在,Ruminoid SubLight Classic 的闪屏问题已经得到了修复。 61 | 62 | ### Ruminoid Tapper Timer 63 | 64 | Ruminoid Tapper Timer 现已发布!使用 Tapper Timer 可以一边看直播一边打轴,快来尝试吧。 65 | 66 | ### Ruminoid 67 | 68 | - 添加了新的 KeyTap 支持库。 69 | 70 | ## v0.2.5 71 | 72 | Build 8 - 2020-08-21 发布 73 | 74 | ### Ruminoid SubLight Classic 75 | 76 | Ruminoid SubLight Classic 现已发布!快来使用 SubLight 在 After Effects 和 Premiere 中导入、预览和渲染你的特效字幕吧。 77 | 78 | ### Ruminoid Plugin Manager 79 | 80 | Plugin Manager 现已发布!您可以使用它安装、升级和管理您的 Ruminoid 插件。 81 | 82 | ### Ruminoid Dashboard 83 | 84 | 改进: 85 | 86 | - 现在,Dashboard 中的设置将会应用到所有 Ruminoid 产品。 87 | 88 | ### Ruminoid 89 | 90 | 改进: 91 | 92 | - 添加了高性能的下载器组件。 93 | 94 | - 添加了部分公共窗口组件。 95 | 96 | ## v0.2.4 97 | 98 | Build 7 - 2020-08-15 发布 99 | 100 | ### Ruminoid LIVE 101 | 102 | 改进: 103 | 104 | - 重构了预渲染核心,预渲染性能更上一层楼。 105 | 106 | - 加入了内存和预渲染指示器,使内存和预渲染情况可视化。 107 | 108 | - 更新了默认参数配置,使其更加适应大多数设备。 109 | 110 | - 优化了垃圾回收机制,使得在释放字幕和音频时能够强制清理内存。 111 | 112 | - 移除了部分无用的代码。 113 | 114 | 修复: 115 | 116 | - 修复了某些在低配设备上会出现的预渲染逻辑问题。 117 | 118 | - 修复了若干界面上的问题。 119 | 120 | - 修复了一个问题,该问题会导致 LIVE 在多显示器设备上界面显示不正常。 121 | 122 | ## v0.2.3 123 | 124 | Build 6 - 2020-08-07 发布 125 | 126 | ### Ruminoid LIVE 127 | 128 | Ruminoid LIVE 现已发布!快来使用 LIVE 在 OBS 中展示特效字幕吧。 129 | 130 | ### Ruminoid Dashboard 131 | 132 | 改进: 133 | 134 | - 优化了应用启动逻辑。 135 | 136 | ### Ruminoid Trimmer 137 | 138 | 改进: 139 | 140 | - 优化了应用启动逻辑。 141 | 142 | - 为控件按钮应用了新的样式。 143 | 144 | - 更新了控件按钮的本地化字串。 145 | 146 | - 移除了部分无用代码。 147 | 148 | ### Ruminoid 149 | 150 | 改进: 151 | 152 | - 从这个版本开始,Ruminoid 安装程序将会显示 Ruminoid 图标。 153 | 154 | ## v0.2.2 155 | 156 | Build 5 - 2020-07-22 发布 157 | 158 | ### Ruminoid Dashboard 159 | 160 | 改进: 161 | 162 | - 优化了多个本地化字符串。 163 | 164 | 修复: 165 | 166 | - 修复了一个问题,该问题可能导致 Dashboard 在关闭之后弹出错误。 167 | 168 | - 修复了一个问题,该问题可能导致 Dashboard 中的更新状态显示不正确。 169 | 170 | ### Ruminoid Trimmer 171 | 172 | 改进: 173 | 174 | - 改进了界面布局,使得 Trimmer 更加现代化。 175 | 176 | - 移除了「帮助」菜单。请通过 Ruminoid World 获取帮助和支持。 177 | 178 | - 移除了一些并无实际作用的代码。 179 | 180 | - 欢迎提示现在可以默认关闭了。 181 | 182 | - 改善了应用退出逻辑。 183 | 184 | - 改进了列表样式,现在列表中的行不会有边框聚焦提示。 185 | 186 | 修复: 187 | 188 | - 修复了一个问题,该问题可能导致「加载媒体」快捷键无法使用。 189 | 190 | - 修复了一个问题,该问题可能导致导出 ASS 字幕文件时丢失扩展名。 191 | 192 | - 修复了部分对话框中的错误提示内容。 193 | 194 | 注意: 195 | 196 | - 原「保存」命令现已更名「导出ASS」,且快捷键修改为「Ctrl+Shift+S」。 197 | 198 | ### Ruminoid 199 | 200 | 改进: 201 | 202 | - 为多种控件优化了样式。 203 | 204 | ## v0.2.1 205 | 206 | Build 4 - 2020-07-18 发布 207 | 208 | ### Ruminoid Dashboard 209 | 210 | 改进: 211 | 212 | - 修复了一些小的问题并提升了应用的稳定性。 213 | 214 | ### Ruminoid Trimmer 215 | 216 | 修复: 217 | 218 | - Trimmer 无法启动的问题现已修复。 219 | 220 | ## v0.2.0 221 | 222 | 请注意,由于该版本的安装程序存在问题,我们已经不推荐下载这个版本的 Ruminoid。 223 | 224 | Build 3 - 2020-07-17 发布 225 | 226 | ### Ruminoid Dashboard 227 | 228 | Ruminoid Dashboard 发布!您现在可以在「开始」屏幕中直接打开 Ruminoid,并使用 Dashboard 启动 Ruminoid 产品。 229 | 230 | 改进: 231 | 232 | - Ruminoid Dashboard 中更新了全新的软件更新系统,在 Ruminoid 系列进行更新的时候,您可以继续使用 Ruminoid 的应用和产品。 233 | 234 | ### Ruminoid Trimmer 235 | 236 | 改进: 237 | 238 | - 音视频回放内核全新升级,新的内核在回放时时间码显示更加精准,且对系统性能的占用也大大降低。 239 | 240 | - 大大精简了安装包的体积。 241 | 242 | ### Ruminoid 243 | 244 | 改进: 245 | 246 | - 重构了所有框架,现在,Ruminoid 的大多数子产品都将使用 Ruminoid Common 框架来进行字幕的处理和渲染等通用操作。 247 | 248 | - 大大精简了安装包的体积。 249 | 250 | 注意: 251 | 252 | - 请注意,由于这个版本的 Ruminoid 重构了所有的通用框架和动态链接库,旧版本的偏好设置在升级之后将会清除。 253 | 254 | - 请注意,由于 Ruminoid 已经更换了新的更新服务,旧版本的更新服务将会失效。 255 | 256 | ## v0.1.1 257 | 258 | Build 2 - 2020-04-17 发布 259 | 260 | ### Ruminoid Trimmer 261 | 262 | 修复了一些小的问题并提升了应用的稳定性。 263 | 264 | ## v0.1.0 265 | 266 | Build 1 - 2020-04-16 发布 267 | 268 | ### Ruminoid Trimmer 269 | 270 | Ruminoid Trimmer 发布!快来使用 Trimmer 制作卡拉 OK 字幕时间轴吧。 271 | -------------------------------------------------------------------------------- /docs/Ruminoid/Version/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | pageIndex: 1000 3 | title: 更新与版本 4 | --- 5 | 6 | 有关 Ruminoid(v0.2.x)或 Ruminoid Studio(v0.3.x)的更新历史,请参见 [更新历史](./History.html)。 7 | 8 | 有关 Ruminoid Toolbox 的更新历史,请参见 [Toolbox 更新历史](./ToolboxHistory.html)。 9 | -------------------------------------------------------------------------------- /docs/Ruminoid/Version/Thre.md: -------------------------------------------------------------------------------- 1 | --- 2 | pageIndex: 30 3 | title: 公示 4 | --- 5 | 6 | 本页面对违反了 Ruminoid 内相关许可规定的个人或团体进行公示。 7 | 8 | 这个页面暂时没有内容。如果你发现了违规行为,请告知开发者。 9 | -------------------------------------------------------------------------------- /docs/Ruminoid/Version/ToolboxDownload.md: -------------------------------------------------------------------------------- 1 | --- 2 | pageIndex: 8 3 | title: 下载 Ruminoid Toolbox 4 | --- 5 | 6 | ## 标准下载 7 | 8 | 直接点击下面的按钮下载 Ruminoid Toolbox。对于安装包和 App 映像,直接双击即可使用。对于便携包,待下载完成后解压即可使用。如果要执行的是升级操作,在解压新版本的压缩包时直接选择「覆盖」即可。 9 | 10 | 最新版本:v0.5.6 11 | 12 | 2021-09-19 发布 13 | 14 | 类型|版本|下载 15 | -|-|- 16 | Windows 便携包|v0.5.6|[v0.5.6/rmbox-win.zip](https://vbox-down-a.obs.cn-east-3.myhuaweicloud.com/rmbox/v0.5.6/rmbox-win.zip) 17 | Windows 安装程序|v0.5.6|[v0.5.6/rmbox-install.exe](https://vbox-down-a.obs.cn-east-3.myhuaweicloud.com/rmbox/v0.5.6/rmbox-install.exe) 18 | macOS 便携包|v0.5.6|[v0.5.6/rmbox-osx.zip](https://vbox-down-a.obs.cn-east-3.myhuaweicloud.com/rmbox/v0.5.6/rmbox-osx.zip) 19 | macOS 安装映像|v0.5.6|[v0.5.6/rmbox-app.dmg](https://vbox-down-a.obs.cn-east-3.myhuaweicloud.com/rmbox/v0.5.6/rmbox-app.dmg) 20 | Linux 便携包|v0.5.6|[v0.5.6/rmbox-linux.zip](https://vbox-down-a.obs.cn-east-3.myhuaweicloud.com/rmbox/v0.5.6/rmbox-linux.zip) 21 | 22 | ## 其他可用的下载项 23 | 24 | ### Beta 25 | 26 | 当前没有可用的 Beta 版本。 27 | 28 | ### LTS 29 | 30 | 如果你选择下载 LTS 版本的 Toolbox,你可以获得长期的技术支持,但你将无法使用任何缺失的功能。 31 | 32 | #### v0.5.5 33 | 34 | 2021-09-01 发布 35 | 36 | 类型|版本|下载 37 | -|-|- 38 | Windows 便携包|v0.5.5|[v0.5.5/rmbox-win.zip](https://vbox-down-a.obs.cn-east-3.myhuaweicloud.com/rmbox/v0.5.5/rmbox-win.zip) 39 | Windows 安装程序|v0.5.5|[v0.5.5/rmbox-install.exe](https://vbox-down-a.obs.cn-east-3.myhuaweicloud.com/rmbox/v0.5.5/rmbox-install.exe) 40 | macOS 便携包|v0.5.5|[v0.5.5/rmbox-osx.zip](https://vbox-down-a.obs.cn-east-3.myhuaweicloud.com/rmbox/v0.5.5/rmbox-osx.zip) 41 | macOS 安装映像|v0.5.5|[v0.5.5/rmbox-app.dmg](https://vbox-down-a.obs.cn-east-3.myhuaweicloud.com/rmbox/v0.5.5/rmbox-app.dmg) 42 | Linux 便携包|v0.5.5|[v0.5.5/rmbox-linux.zip](https://vbox-down-a.obs.cn-east-3.myhuaweicloud.com/rmbox/v0.5.5/rmbox-linux.zip) 43 | 44 | #### v0.5.4 45 | 46 | 2021-07-04 发布 47 | 48 | 类型|版本|下载 49 | -|-|- 50 | Windows 便携包|v0.5.4|[v0.5.4/rmbox-win.zip](https://vbox-down-a.obs.cn-east-3.myhuaweicloud.com/rmbox/v0.5.4/rmbox-win.zip) 51 | Windows 安装程序|v0.5.4|[v0.5.4/rmbox-install.exe](https://vbox-down-a.obs.cn-east-3.myhuaweicloud.com/rmbox/v0.5.4/rmbox-install.exe) 52 | macOS 便携包|v0.5.4|[v0.5.4/rmbox-osx.zip](https://vbox-down-a.obs.cn-east-3.myhuaweicloud.com/rmbox/v0.5.4/rmbox-osx.zip) 53 | macOS 安装映像|v0.5.4|[v0.5.4/rmbox-app.dmg](https://vbox-down-a.obs.cn-east-3.myhuaweicloud.com/rmbox/v0.5.4/rmbox-app.dmg) 54 | Linux 便携包|v0.5.4|[v0.5.4/rmbox-linux.zip](https://vbox-down-a.obs.cn-east-3.myhuaweicloud.com/rmbox/v0.5.4/rmbox-linux.zip) 55 | 56 | #### v0.4.0 57 | 58 | 2021-04-18 发布 59 | 60 | 类型|版本|下载 61 | -|-|- 62 | Windows 便携包|v0.4.0|[v0.4.0/rmbox-win.zip](https://vbox-down-a.obs.cn-east-3.myhuaweicloud.com/rmbox/v0.4.0/rmbox-win.zip) 63 | Windows 安装程序|v0.4.0|[v0.4.0/rmbox-install.exe](https://vbox-down-a.obs.cn-east-3.myhuaweicloud.com/rmbox/v0.4.0/rmbox-install.exe) 64 | macOS 便携包|v0.4.0|[v0.4.0/rmbox-osx.zip](https://vbox-down-a.obs.cn-east-3.myhuaweicloud.com/rmbox/v0.4.0/rmbox-osx.zip) 65 | macOS 安装映像|v0.4.0|[v0.4.0/rmbox-app.dmg](https://vbox-down-a.obs.cn-east-3.myhuaweicloud.com/rmbox/v0.4.0/rmbox-app.dmg) 66 | Linux 便携包|v0.4.0|[v0.4.0/rmbox-linux.zip](https://vbox-down-a.obs.cn-east-3.myhuaweicloud.com/rmbox/v0.4.0/rmbox-linux.zip) 67 | 68 | #### v0.3.3 69 | 70 | 2021-04-07 发布 71 | 72 | 类型|版本|下载 73 | -|-|- 74 | Windows 便携包|v0.3.3|[v0.3.3/rmbox-win.zip](https://vbox-down-a.obs.cn-east-3.myhuaweicloud.com/rmbox/v0.3.3/rmbox-win.zip) 75 | Windows 安装程序|v0.3.3|[v0.3.3/rmbox-install.exe](https://vbox-down-a.obs.cn-east-3.myhuaweicloud.com/rmbox/v0.3.3/rmbox-install.exe) 76 | macOS 便携包|v0.3.3|[v0.3.3/rmbox-osx.zip](https://vbox-down-a.obs.cn-east-3.myhuaweicloud.com/rmbox/v0.3.3/rmbox-osx.zip) 77 | macOS 安装映像|v0.3.3|[v0.3.3/rmbox-app.dmg](https://vbox-down-a.obs.cn-east-3.myhuaweicloud.com/rmbox/v0.3.3/rmbox-app.dmg) 78 | Linux 便携包|v0.3.3|[v0.3.3/rmbox-linux.zip](https://vbox-down-a.obs.cn-east-3.myhuaweicloud.com/rmbox/v0.3.3/rmbox-linux.zip) 79 | 80 | #### v0.2.6 81 | 82 | 2021-04-02 发布 83 | 84 | 类型|版本|下载 85 | -|-|- 86 | Windows 便携包|v0.2.6|[v0.2.6/rmbox-win.zip](https://vbox-down-a.obs.cn-east-3.myhuaweicloud.com/rmbox/v0.2.6/rmbox-win.zip) 87 | macOS 便携包|v0.2.6|[v0.2.6/rmbox-osx.zip](https://vbox-down-a.obs.cn-east-3.myhuaweicloud.com/rmbox/v0.2.6/rmbox-osx.zip) 88 | Linux 便携包|v0.2.6|[v0.2.6/rmbox-linux.zip](https://vbox-down-a.obs.cn-east-3.myhuaweicloud.com/rmbox/v0.2.6/rmbox-linux.zip) 89 | 90 | #### v0.2.1 91 | 92 | 2021-03-21 发布 93 | 94 | 类型|版本|下载 95 | -|-|- 96 | Windows 便携包|v0.2.1|[v0.2.1/rmbox-win.zip](https://vbox-down-a.obs.cn-east-3.myhuaweicloud.com/rmbox/v0.2.1/rmbox-win.zip) 97 | macOS 便携包|v0.2.1|[v0.2.1/rmbox-osx.zip](https://vbox-down-a.obs.cn-east-3.myhuaweicloud.com/rmbox/v0.2.1/rmbox-osx.zip) 98 | Linux 便携包|v0.2.1|[v0.2.1/rmbox-linux.zip](https://vbox-down-a.obs.cn-east-3.myhuaweicloud.com/rmbox/v0.2.1/rmbox-linux.zip) 99 | -------------------------------------------------------------------------------- /docs/SimpleBox/Guide/History.md: -------------------------------------------------------------------------------- 1 | --- 2 | pageIndex: 2 3 | title: 更新历史 4 | --- 5 | 6 | 这里是 SimpleBox 的更新历史。 7 | 8 | 特定于通道的更新会在版本的右上角显示 Badge。标为“已弃用”的版本不推荐下载,因为这些版本的自动更新服务已经失效,或在更新到新版本之后旧的偏好设置会被清除。 9 | 10 | ## v0.1.3 11 | 12 | Build 3·2020-09-22 发布 13 | 14 | 请注意,由于这一版本中引入了大量新功能,更新安装的时间可能会大大增长。你可能需要等待长达 10 分钟以安装本次的更新,具体取决于你的计算机性能和网速。 15 | 16 | 但是,更新仍旧会全自动安装,请不必担心。 17 | 18 | 本次更新也升级了系统兼容性要求,详见下方的「注意」部分。 19 | 20 | 改进: 21 | 22 | - 加入了统一的用户登录凭据存储结构。 23 | 24 | - 加入了「拉取」功能。现在,你可以从 SimpleBox 中拉取 Marshmallow 和 Peing 中的提问。 25 | 26 | - 添加了图片导出功能。现在,你可以将提问导出为图片。 27 | 28 | - 重新调整了设置页面的分组和布局。 29 | 30 | - 统一使用 Windows 公用对话框组件。 31 | 32 | - 在「设置」页中加入了图片导出的样式地址选项。 33 | 34 | - 统一了「导入」和「拉取」功能的行为逻辑。 35 | 36 | - 加入了多选模式和相关的多选操作。 37 | 38 | - 现在,在右键菜单中和多选状态下你都可以移动提问到另一个分组了。 39 | 40 | - 现在,删除分组和提问之前会要求确认了。 41 | 42 | 修复: 43 | 44 | - 修复了主窗口上部分字串的格式。 45 | 46 | 注意: 47 | 48 | - 请注意:我们不再支持 32 位(`x86`)处理器架构的系统。若要使用 SimpleBox,请重新安装你的系统至 x64 架构。 49 | 50 | - 请注意:如果你的 Windows 版本在 10 以下,你可能需要安装 [Visual C++ 运行库](https://aka.ms/vs/16/release/vc_redist.x64.exe) 使得程序能够正常运行。 51 | 52 | ## v0.1.2 53 | 54 | Build 2·2020-09-06 发布 55 | 56 | 改进: 57 | 58 | - 弹出菜单现在有配套的分组和图标标识了。 59 | 60 | - 抽象了导入提问的逻辑,现在,所有提问导入时都会新建一个专有分组。 61 | 62 | - 添加了对 Marsher 导出文件的导入支持。 63 | 64 | - 添加了导出分组功能,现在,你可以导出 SimpleBox 的整个分组,并在另一台电脑上重新导入。 65 | 66 | 修复: 67 | 68 | - 修复了一个问题,该问题可能导致自动更新服务不会正常检查更新。 69 | 70 | ## v0.1.1 71 | 72 | Build 1·2020-08-30 发布 73 | 74 | SimpleBox 初版本发布。 75 | -------------------------------------------------------------------------------- /docs/SimpleBox/Guide/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | pageIndex: 1 3 | title: 快速上手 4 | --- 5 | 6 | 快速上手 7 | 8 | ## 更新历史记录 9 | 10 | 轻敲 [这里](./History.html) 以查看更新历史记录。 11 | -------------------------------------------------------------------------------- /docs/SimpleBox/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | home: true 3 | heroImage: /Icons/Icons/SimpleBox/SimpleBox.Colored.Transparent.png 4 | heroText: SimpleBox 5 | tagline: 轻松管理和展示 SimpleBox 提问箱。 6 | actionText: 立即下载 → 7 | actionLink: /SimpleBox/#下载 8 | pageIndex: 9 9 | footer: MIT Licensed | Copyright © 2020 Il Harper 10 | --- 11 | 12 | ## 下载 13 | 14 | ### 下载 SimpleBox 15 | 16 | 轻敲下面的按钮即可下载 SimpleBox 的最新版本。 17 | 18 | 19 | 20 | ## 指南 21 | 22 | 下面是 SimpleBox 的指南和教程部分。 23 | 24 | 25 | 26 | ## 交流和讨论 27 | 28 | SimpleBox 相关交流可以加入群聊 1057348255 或服务器 。 29 | -------------------------------------------------------------------------------- /docs/VBox/Contribute.md: -------------------------------------------------------------------------------- 1 | --- 2 | pageIndex: 3 3 | title: 参与贡献 4 | --- 5 | 6 | 有许多种方法可以参与到 VBox 的贡献当中来。下面列举了一些常见的情况。 7 | 8 | ## 我发现了文档的错误! 9 | 10 | 在任何文档页面中您都可以在文档内容的副标题处找到「编辑此页」的按钮。点击按钮即会弹出就地编辑器。找到错误然后将其改正即可。关于文档的编辑请查看[这里](./Edit)。 11 | 12 | ## 我想要参与文档的维护! 13 | 14 | 我们非常欢迎您加入到我们的维护者团队当中来。请按照下面的[「联系方式」](./Contribute#联系方式)一节与我们取得联系。 15 | 16 | ## 我想要创建新的教程! 17 | 18 | 我们非常欢迎您贡献新的教程资源和产品文档。请按照下面的[「联系方式」](./Contribute#联系方式)一节与我们取得联系。 19 | 20 | ## 联系方式 21 | -------------------------------------------------------------------------------- /docs/VBox/Edit.md: -------------------------------------------------------------------------------- 1 | --- 2 | pageIndex: 4 3 | title: 编辑文档 4 | --- 5 | 6 | 在 VBox 内进行文档编辑是非常愉悦的体验。VBox 的所有文档都使用 Markdown 语法,这使得您即使使用纯文本也可以立刻开始文档的编辑;VBox 还提供了一个在线的就地编辑器,这使得您可以在发现文档的错误的时候立刻将其改正,而无需离开当前页面。 7 | 8 | 不过,如果您想要丰富您的文档页面,则可能需要掌握更多的语法和编辑器知识。下面列举了一些可以在 VBox 的文档编辑中使用的知识和技巧。 9 | 10 | ## 如果只是发现了一个小错误…… 11 | 12 | 打开就地编辑器,找到错误,并且修复!这是最为快捷的方法。大多数情况下,Markdown 语法都是易于理解的,您可以直接参考页面的其他部分来修复您所发现的错误。 13 | 14 | ## Markdown 语法 15 | 16 | [这里](https://github.com/younghz/Markdown)有关于 Markdown 语法的列表。 17 | 18 | [这里](https://vuepress.vuejs.org/zh/guide/markdown.html)是 VuePress 对 Markdown 的扩展,这些扩展的语法也可以在 VBox 中使用。 19 | -------------------------------------------------------------------------------- /docs/VBox/Intro.md: -------------------------------------------------------------------------------- 1 | --- 2 | pageIndex: 1 3 | title: VBox 文档 4 | --- 5 | 6 | ::: tip 7 | 8 | 请注意:这是有关 VBox 本身的文档。如果您在寻找其他产品的文档,请返回[首页](/)。 9 | 10 | ::: 11 | 12 | 确实会有这样的情况——在经过了漫长的浏览文档的时间之后,你可能突然对这个统一文档网站——VBox 产生了兴趣。是的,作为一个人人可以参与贡献的在线文档平台,VBox 也是我们众多产品其中的一项。在这里,你可以了解到更多关于 VBox 的信息。 13 | 14 | ## 在 VBox 背后…… 15 | 16 | ### VBox 由 VuePress 驱动 17 | 18 | VBox 使用 [VuePress](https://vuepress.vuejs.org/zh/) 作为文档框架。VuePress 提供了一个及其精简而又及其快速的静态网站生成器用来在快速生成静态页面的同时保持对 Vue 组件和功能的良好支持,这使得我们在书写文档的时候可以随意使用自定义的 Vue 组件来使我们的文档变得更加丰富;它还提供了许多基于 Markdown 的扩展功能,使得在不增加文档编写难度的同时维护者可以灵活使用这些功能来让文档的结构和逻辑更加清晰。 19 | 20 | ### VBox 使用了许多 VuePress 插件 21 | 22 | VBox 添加了许多 VuePress 插件,使得您可以更加轻松地浏览文档,如「放大图片」插件和「返回顶部」插件等等。 23 | 24 | ### VBox 由「VBox 主题」扩展 25 | 26 | VBox 中含有许多 VuePress 提供的默认主题中所没有的功能,如自动生成文档目录和就地编辑等。这些功能和 VBox 关联较为紧密,因此我们并没有使用插件的方式加载它们,而是使用了由 VuePress 的默认主题派生而来的[「VBox 主题」(vuepress-theme-vbox)](https://github.com/vbox-moe/vuepress-theme-vbox)。许多 VBox 的核心功能都在这个主题内提供。有关 VBox 主题的更多详细信息请转到[这里](./Theme)。 27 | 28 | ## 我想参与贡献! 29 | 30 | 这非常好,我们非常欢迎您能够参与到 VBox 托管的众多文档的编辑、修改和维护当中。请查看[这里](./Contribute)了解更多信息。 31 | -------------------------------------------------------------------------------- /docs/VBox/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | home: true 3 | heroImage: 4 | heroText: VBox 5 | tagline: 人人可参与贡献的在线文档平台 6 | actionText: 了解 → 7 | actionLink: ./Intro 8 | pageIndex: 1000 9 | --- 10 | 11 | 确实会有这样的情况——在经过了漫长的浏览文档的时间之后,你可能突然对这个统一文档网站——VBox 产生了兴趣。是的,作为一个人人可以参与贡献的在线文档平台,VBox 也是我们众多产品其中的一项。在这里,你可以了解到更多关于 VBox 的信息。 12 | -------------------------------------------------------------------------------- /docs/VBox/Theme/Editor.md: -------------------------------------------------------------------------------- 1 | --- 2 | pageIndex: 2 3 | title: 就地编辑器 4 | --- 5 | 6 | 「就地编辑器」是 VBox 中最为核心的功能之一,同时也是 VBox 实现「人人都能参与贡献」的基础。 7 | -------------------------------------------------------------------------------- /docs/VBox/Theme/Layout.md: -------------------------------------------------------------------------------- 1 | --- 2 | pageIndex: 1 3 | title: 布局 4 | --- 5 | 6 | VBox 布局。 7 | -------------------------------------------------------------------------------- /docs/VBox/Theme/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | pageIndex: 2 3 | title: VBox 主题 4 | --- 5 | 6 | VBox 主题是驱动 VBox 文档系统的核心组件,它和「VBox 插件」共同组成 VBox 文档系统。VBox 主题主要负责页面的样式、布局的处理以及就地编辑器相关的部分。 7 | -------------------------------------------------------------------------------- /docs/VNerve/Components/BiLive.md: -------------------------------------------------------------------------------- 1 | --- 2 | pageIndex: 2 3 | title: BiLive 4 | --- 5 | 6 | vNerve BiLive 用于聚合从 Bilibili 直播的实时弹幕数据源获取到的数据。其包括但不限于: 7 | 8 | - 直播弹幕 9 | - 礼物信息 10 | - 醒目留言(即“Super Chat”) 11 | - 直播间人气值 12 | - 开播、下播与被切断直播 13 | - 房间信息变更(标题与分区) 14 | - 舰长购买与续费 15 | - …… 16 | 17 | BiLive 目前仅涉及实时性数据部分。关于如何解读下列文档,您可能需要先阅读《开始使用》(todo:link)。 18 | 19 | ## 实时性数据 20 | 21 | ### 使用 NeuronWire 获取数据 22 | 23 | //TODO @美东 24 | 25 | ### Routing Key 26 | 27 | BiLive 的 Routing Key 内缩写为 `blv`。其 Routing Key 格式服从 `blv..`,其中 `room_id` 为**房间号**(长房间号,非短房间号或主播 UID),而 `type` 为BiLive 提供的消息类型之一。其可能为下列值之一。 28 | 29 | | type | 简介 | Routing Key 示例 | 30 | | ------------- | -------------------------------------------------- | ------------------------ | 31 | | danmaku | 直播间弹幕 | blv.123456.danmaku | 32 | | gift | 直播间礼物 | blv.123456.gift | 33 | | sc | 醒目留言(Super Chat) | blv.123456.sc | 34 | | new_guard | 新的大航海用户(舰长/提督/总督) | blv.123456.new_guard | 35 | | welcome_vip | 欢迎老爷 | blv.123456.welcome_vip | 36 | | welcome_guard | 欢迎舰长 | blv.123456.welcome_guard | 37 | | user_blocked | 直播间用户被禁言 | blv.123456.user_blocked | 38 | | live_status | 直播状态改变(上下播) | blv.123456.live_status | 39 | | room_change | 直播间信息更改(房间名、分区、房管、直播间皮肤等) | blv.123456.room_change | 40 | | room_locked | 直播间被封禁 | blv.123456.room_locked | 41 | | room_warning | 直播间被警告 | blv.123456.room_warning | 42 | | room_limited | 直播间受限(版权等) | blv.123456.room_limited | 43 | | sc_delete | 醒目留言被删除 | blv.123456.sc_delete | 44 | 45 | 特别提示:**vNerve 永不会加入源消息命令 NOTICE_MSG(即全局系统通知)与源消息命令以 SMALL_TV 开头的消息的处理!** 46 | 47 | ### 数据结构 48 | 49 | 首先您可以参考 BiLive 所有的 Protobuf 文件:[vNerveTransmitter/vNerve/bilibili/live](https://github.com/vNerve/vNerveTransmitter/blob/master/vNerve/bilibili/live/) 50 | 51 | 为了重用 Protobuf 消息类型,BiLive 的消息数据结构略有一点清奇,其采用了多层的结构,大致如下: 52 | 53 | ``` 54 | RoomMessage -> user_message -> user (与消息相关的用户) 55 | (根对象) (用户相关消息) -> danmaku 56 | -> super_chat 57 | -> welcome 58 | -> ............... 59 | -> live_status 60 | -> popularity_change 61 | -> info_change 62 | -> ............(其余与特定用户无关消息) 63 | ``` 64 | 65 | 其中不同的消息类型(例如 `user_message` 与 `live_status`,或 `danmaku` 与 `super_chat`)之间通过 Protobuf `oneof` 结构分开。你可以将其类比于 C/C++ 的 `union`,或类似于这样的结构(以 Java 为例): 66 | 67 | ```java 68 | enum RoomMessageType { 69 | USER_MESSAGE, LIVE_STATUS 70 | } 71 | class RoomMessage { 72 | RoomMessageType payloadCase; 73 | Object payload; 74 | } 75 | ``` 76 | 77 | 因此,如果需要处理原始 Protobuf 数据,您的第一步应该是判断消息类型。下面是 C# 上的使用 Google Protobuf 官方库实现的一个小例子。其他语言大同小异: 78 | 79 | ```csharp 80 | var rootMessage = RoomMessage.Parser.ParseFrom(body); // 先解析根对象 81 | uint roomId = rootMessage.RoomId; // 房间号 82 | switch (rootMessage.PayloadCase) 83 | { 84 | case RoomMessage.PayloadOneofCase.None: 85 | break; 86 | case RoomMessage.PayloadOneofCase.PopularityChange: 87 | var popularity = rootMessage.PopularityChange.Popularity; // 直播间人气值 88 | break; 89 | case RoomMessage.PayloadOneofCase.LiveStatus: 90 | var newStatus = rootMessage.LiveStatus.Status; // 上下播 91 | break; 92 | case RoomMessage.PayloadOneofCase.InfoChange: 93 | var newInfo = rootMessage.InfoChange; // 修改直播间信息 94 | switch (newInfo.ChangedCase) 95 | { 96 | case RoomInfoChangedMessage.ChangedOneofCase.None: 97 | break; 98 | case RoomInfoChangedMessage.ChangedOneofCase.BaseInfo: 99 | var newBaseInfo = newInfo.BaseInfo; // 房间名与分区 100 | break; 101 | case RoomInfoChangedMessage.ChangedOneofCase.BackgroundUrl: 102 | var newBackgroundUrl = newInfo.BackgroundUrl; // 房间背景图 103 | break; 104 | case RoomInfoChangedMessage.ChangedOneofCase.SkinId: 105 | var newSkinId = newInfo.SkinId; // 房间皮肤 106 | break; 107 | case RoomInfoChangedMessage.ChangedOneofCase.Admin: 108 | var newRoomAdminUids = newInfo.Admin.Uid.ToList();// 房管变更 109 | break; 110 | default: 111 | throw new ArgumentOutOfRangeException(); 112 | } 113 | break; 114 | case RoomMessage.PayloadOneofCase.RoomLocked: 115 | var expireAt = rootMessage.RoomLocked.LockedUntil; // 房间被锁 116 | break; 117 | case RoomMessage.PayloadOneofCase.RoomWarning: 118 | var message = rootMessage.RoomWarning.Message; // 房间被警告 119 | break; 120 | case RoomMessage.PayloadOneofCase.RoomLimited: 121 | var details = rootMessage.RoomLimited; // 房间受限 122 | break; 123 | case RoomMessage.PayloadOneofCase.SuperchatDelete: 124 | var deletedSCIds = rootMessage.SuperchatDelete.Id.ToList(); // 删除SC 125 | break; 126 | case RoomMessage.PayloadOneofCase.UserMessage: 127 | var userMessage = rootMessage.UserMessage; 128 | var userInfo = userMessage.User; // 下面所有消息涉及到的用户 129 | switch (userMessage.PayloadCase) 130 | { 131 | case UserMessage.PayloadOneofCase.None: 132 | break; 133 | case UserMessage.PayloadOneofCase.Danmaku: 134 | var danmaku = userMessage.Danmaku; // 弹幕 135 | break; 136 | case UserMessage.PayloadOneofCase.Gift: 137 | var gift = userMessage.Gift; // 礼物 138 | break; 139 | case UserMessage.PayloadOneofCase.SuperChat: 140 | var superChat = userMessage.SuperChat; // Super Chat 141 | break; 142 | case UserMessage.PayloadOneofCase.NewGuard: 143 | var newGuard = userMessage.NewGuard; // 大航海 144 | break; 145 | case UserMessage.PayloadOneofCase.WelcomeVip: 146 | var welcomeVip = userMessage.WelcomeVip; // 欢迎老爷 147 | break; 148 | case UserMessage.PayloadOneofCase.WelcomeGuard: 149 | var welcomeGuard = userMessage.WelcomeGuard; // 欢迎舰长 150 | break; 151 | case UserMessage.PayloadOneofCase.UserBlocked: 152 | var userBlocked = userMessage.UserBlocked; // 用户被封禁 153 | break; 154 | default: 155 | throw new ArgumentOutOfRangeException(); 156 | } 157 | break; 158 | default: 159 | throw new ArgumentOutOfRangeException(); 160 | } 161 | ``` 162 | 163 | 实际情况下,**由于 Routing Key 已经过滤了一次消息类型,您并不需要处理所有消息类型**。 164 | 165 | ### 消息类型 166 | 167 | 下面仅会列出各个消息类型。在阅读下述文档时,您应该同时参考 vNerve Transmitter 中的 Proto 源文件。大多数字段的注释等均写在文档中。另外,下面出现的所有信息在 Protobuf 源文件中都有一份,且可能更新。 168 | 169 | #### 凡例 170 | 171 | - Protobuf 类型均在 `vNerve.bilibili.live` 命名空间下 172 | - Protobuf 路径均以 `RoomMessage` 作为根 173 | - 所有的时间戳均为以秒为单位的 UNIX 时间戳,UTC+8 时区。 174 | 175 | #### 用户相关消息 176 | 177 | ``` 178 | Protobuf 类型 - UserMessage 179 | Protobuf 路径 - .user_message 180 | 字段 - user : 该消息对应的用户 181 | - payload : 内容 182 | ``` 183 | 184 | 所有与用户相关(广义上的)的消息,例如用户产生的消息等。注意,在处理 `payload` 之前,您可能先需要从 `user` 获取该消息对应的用户! 185 | 186 | ##### 用户信息 - User 187 | 188 | ``` 189 | Protobuf 类型 - UserInfo 190 | Protobuf 路径 - .user_message.user 191 | 字段 - uid : 用户 UID 192 | - name : 用户昵称 193 | - 更多请参照 Protobuf 源文件 194 | ``` 195 | 196 | 注意:**`User` 类型中仅有 `uid` 保证在所有消息中均可用**。大部分消息中 `name` 可用。其他字段的可用情况会在下面的文档中标明,若未标明则表示此消息中 `user` 对应字段没有数据。 197 | 198 | ##### 弹幕消息 - Danmaku 199 | 200 | ``` 201 | Protobuf 类型 - DanmakuMessage 202 | Protobuf 路径 - .user_message.danmaku 203 | 提供的额外 User 字段 : admin, live_vip_level, user_level_border_color, title, medal, guard_level, phone_verified, regular_user 204 | ``` 205 | 206 | 您可以用 `lottery_type` 过滤节奏风暴、抽奖等可能导致刷屏的消息。注意:`regular_user` 并不可靠。 207 | 208 | ##### 礼物消息 - Gift 209 | 210 | ``` 211 | Protobuf 类型 - GiftMessage 212 | Protobuf 路径 - .user_message.gift 213 | 提供的额外 User 字段 : avatar_url 214 | ``` 215 | 216 | 某些情况下礼物价格可能打折,此时 `single_price_coin_raw` 为未打折的价格。 217 | 218 | ##### 醒目留言 - SuperChat 219 | 220 | ``` 221 | Protobuf 类型 - SuperChatMessage 222 | Protobuf 路径 - .user_message.super_chat 223 | 提供的额外 User 字段 : avatar_url, admin, user_level, user_level_border_color, admin, live_vip_level, title, medal, guard_level 224 | ``` 225 | 226 | `id` 为 醒目留言 ID,可用于后续接收删除醒目留言的消息;`token` 作用未知。 227 | 228 | ##### 大航海/上舰 - NewGuard 229 | 230 | ``` 231 | Protobuf 类型 - NewGuardMessage 232 | Protobuf 路径 - .user_message.new_guard 233 | 提供的额外 User 字段 : guard_level 234 | ``` 235 | 236 | `buy_type` 标识此次操作为新舰长还是续费舰长。`duration_level` 标识的是本次购买/续费是按周购买还是按月,这一字段亦为 `count` 的单位。 237 | 238 | ##### 欢迎老爷 - WelcomeVip 239 | 240 | ``` 241 | Protobuf 类型 - WeicomeVIPMessage 242 | Protobuf 路径 - .user_message.welcome_vip 243 | 提供的额外 User 字段 : admin 244 | ``` 245 | 246 | `admin` 有效性存疑。 247 | 248 | ##### 欢迎舰长 - WelcomeGuard 249 | 250 | ``` 251 | Protobuf 类型 - WeicomeGuardMessage 252 | Protobuf 路径 - .user_message.welcome_guard 253 | 提供的额外 User 字段 : guard_level 254 | ``` 255 | 256 | ##### 用户禁言 - UserBlocked 257 | 258 | ``` 259 | Protobuf 类型 - UserBlockedMessage 260 | Protobuf 路径 - .user_message.user_blocked 261 | 提供的额外 User 字段 : 无 262 | User 的 name 字段不可用 263 | ``` 264 | 265 | #### 直播间人气值更新 - PopularityChange 266 | 267 | ``` 268 | Protobuf 类型 - PopularityChangedMessage 269 | Protobuf 路径 - .popularity_change 270 | ``` 271 | 272 | 此消息每房间约一分钟发送一条。 273 | 274 | #### 上下播 - LiveStatus 275 | 276 | ``` 277 | Protobuf 类型 - LiveStatusChangedMessage 278 | Protobuf 路径 - .live_status 279 | ``` 280 | 281 | `message` 仅在房间被切断(`status` 为 `CUT_OFF`)时可用。 282 | 283 | // TODO 被cutoff的时候会发送 preparing吗,以及round的意义 284 | 285 | #### 房间信息 - InfoChange 286 | 287 | ``` 288 | Protobuf 类型 - RoomInfoChangedMessage 289 | Protobuf 路径 - .info_change 290 | ``` 291 | 292 | 该消息分为三种,您应该判断 `oneof changed` 的类型来判断修改了何种消息。 293 | 294 | - `base_info`:直播间标题与分区 295 | - `background_url`:直播间背景图 296 | - `skin_id`:直播间皮肤 297 | - `admin`:直播间房管列表 298 | 299 | #### 房间被锁定 - RoomLocked 300 | 301 | ``` 302 | Protobuf 类型 - RoomLockedMessage 303 | Protobuf 路径 - .room_locked 304 | ``` 305 | 306 | #### 房间受限 - RoomLimited 307 | 308 | ``` 309 | Protobuf 类型 - RoomLimitedMessage 310 | Protobuf 路径 - .room_limited 311 | ``` 312 | 313 | 发生情况暂时不明。可能在播放带版权的内容时出现,亦可能进入某些特殊分区(放映厅等)时候出现。 314 | 315 | #### 删除醒目留言 - SuperChatDelete 316 | 317 | ``` 318 | Protobuf 类型 - SuperChatDeleteMessage 319 | Protobuf 路径 - .superchat_delete 320 | ``` 321 | 322 | 其中 `id` 列表字段对应前述收到的 Super Chat 消息中的 `id` 字段。 -------------------------------------------------------------------------------- /docs/VNerve/GitHub.md: -------------------------------------------------------------------------------- 1 | --- 2 | pageIndex: 3 3 | title: vNerve on GitHub 4 | --- 5 | 6 | 🎉 PRs and contributions are welcome! 🎉 7 | 8 | Website: 9 | 10 | Organization: [vNerve](https://github.com/vNerve) 11 | 12 | ## Repositories 13 | 14 | ### vNerveBiLiveReceptor 15 | 16 | Bilibili Livestream real-time information crawler for project vNerve. 17 | 18 | GitHub: 19 | 20 | HTTPS: 21 | 22 | SSH: git@github.com:vNerve/vNerveBiLiveReceptor.git 23 | 24 | ### vNerveTransmitter 25 | 26 | GitHub: 27 | 28 | HTTPS: 29 | 30 | SSH: git@github.com:vNerve/vNerveTransmitter.git 31 | 32 | ### vNerveRPC 33 | 34 | GitHub: 35 | 36 | HTTPS: 37 | 38 | SSH: git@github.com:vNerve/vNerveRPC.git 39 | 40 | ### vNerve-Docs 41 | 42 | Docs for vNerve. 43 | 44 | Website: 45 | -------------------------------------------------------------------------------- /docs/VNerve/Guide/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | pageIndex: 1 3 | title: 开始使用 4 | --- 5 | 6 | ## 实时性数据 7 | 8 | 对于 JavaScript 开发者,您可以直接使用 vNerveNeuronWire 库(TODO: @镁矿)拉取实时性数据。对于其他语言开发者,您可参考以下教程。 9 | 10 | ### 前置条件 11 | 12 | - AMQP / WebSTOMP 客户端 13 | - Protobuf 解析库 14 | - [vNerve Transmitter](https://github.com/vNerve/vNerveTransmitter) 15 | 16 | 实时性数据使用消息队列进行传输,因此您首先支持 AMQP 或 WebSTOMP 协议的消息队列客户端。若在本地使用(例如 Java 或 C#),二者皆可使用。若在浏览器上直接连接,仅可选择 WebSTOMP 协议(其内部使用 WebSocket 建立连接)。大部分语言都有对应的 AMQP 与 WebSTOMP 协议客户端。**AMQP 客户端,很多时候会作为 RabbitMQ 的客户端发布。** 例如 C# 上的 [RabbitMQ.Client](https://www.nuget.org/packages/RabbitMQ.Client)。 17 | 18 | 从消息队列获取到的数据必须通过 Protobuf 解析工具解析。对于大部分语言或平台均存在官方或非官方的 Protobuf 解析工具。下列是一些例子。 19 | 20 | - C#:[protobuf-net](https://github.com/protobuf-net/protobuf-net) / [Google.Protobuf](https://developers.google.com/protocol-buffers/docs/csharptutorial) 21 | - Java: [com.google.protobuf](https://developers.google.com/protocol-buffers/docs/javatutorial) 22 | - C++: [protobuf](https://developers.google.com/protocol-buffers/docs/cpptutorial) 23 | - Go: [protobuf](https://developers.google.com/protocol-buffers/docs/gotutorial) 24 | - Rust: [rust-protobuf](https://github.com/stepancheg/rust-protobuf) 25 | 26 | 由于 Protobuf 是一种 Schemaful 的格式,因此为了正常解析收到的数据,还需要获取各种数据类型的定义,即 vNerveTransmitter。 27 | 28 | 若使用 `protobuf-net` 等第三方非常规库,您需要根据 vNerveTransmitter 中定义的 proto 文件手动编写对应的数据结构,而若使用 Google 提供的 Protobuf 解析库,您可以直接从 `vNerveTransmitter` Git 库的 `dist` 分支获取生成好的对应语言的源代码。请参考 Google 官方提供的文档来了解如何使用这些代码,大部分情况下您只需要将对应语言的源文件加入项目并加上 Google Protobuf 的依赖即可。 29 | 30 | 另外,对于 JavaScript 开发者,我们亦提供 `protobuf.js`(目前还没有,晚点配CI,你考虑自己先用pbjs车一个吧@美东)的 static 与 JSON 格式。 31 | 32 | ### 明确需要的数据 33 | 34 | vNerve 通过设置消息队列的 Routing Key 来按照需求投递消息。这使得您可以只关注于您感兴趣的消息类型,避免不必要的带宽浪费。同时,Routing Key 也支持通配符。 35 | 36 | 所有 Routing Key 均以 `.` 开头,其中 `src` 为来源组件的简写,例如 BiLive 数据源的简写为 `blv`。 37 | 38 | 欲了解各个数据源的 Routing Key 格式,请参阅各个数据源的文档。 39 | 40 | - vNerve BiLive: (链接) 41 | 42 | ### 拉取数据 43 | 44 | 为了分散服务器负载,vNerve 通过多个镜像服务器分流数据获取请求。您可以任意选取一个服务器拉取数据,其提供的数据均是相同的的。 45 | 46 | #### 服务器信息 47 | 48 | 所有服务器均支持以下提到的所有协议。 49 | 50 | ``` 51 | mq1.vnerve.dd.center 52 | mq2.vnerve.dd.center 53 | ``` 54 | 55 | - 端口号请参照下方协议。 56 | - 用户名与密码均为 `vnerve`。注意该用户无进行任何写操作的权限。 57 | - 消息队列 Exchange 名称为 `vNerve`,类型为 Topic。 58 | 59 | #### AMQP 协议 60 | 61 | // TODO 62 | 63 | #### WebSTOMP 协议 64 | 65 | WebSTOMP 协议提供了一种简便的,通过 WebSocket 获取数据的方式。 66 | 67 | 端口号: 68 | - WSS - `7216` 69 | - WS - `7215` 70 | 71 | **注意:需要接收二进制消息而非字符串,有时候需要配置对应客户端。** 72 | 73 | 下面给出一个使用 [stompjs](https://stomp-js.github.io/guide/stompjs/using-stompjs-v5.html) 库在浏览器中 JavaScript 上的例子(改编自官方例子)。 74 | 75 | ```javascript 76 | const client = new StompJs.Client({ 77 | brokerURL: "wss://mq1.vnerve.dd.center:7216/ws", 78 | connectHeaders: { 79 | login: "vnerve", 80 | passcode: "vnerve" 81 | }, 82 | reconnectDelay: 10000, 83 | }); 84 | 85 | client.onConnect = function(frame) { 86 | let subscription = client.subscribe("/topic/vNerve/blv.*.sc", function(message) { 87 | let raw_data = message.binaryBody; 88 | // 处理 raw_data 89 | }); 90 | 91 | // 当不再需要时 92 | subscription.unsubscribe(); 93 | }; 94 | 95 | client.activate(); 96 | 97 | // 当不再需要时 98 | client.deactivate(); 99 | 100 | 101 | ``` 102 | 103 | // TODO:测试这段代码 104 | 105 | ### 解析数据 106 | 107 | 上面的例子中提到了需要处理 `raw_data`,即接收到的数据。 108 | 109 | 所有收到的数据均是一个 Protobuf 对象。其类型为对应的感应器组件所提供消息的根类型。例如,BiLive 组件提供的根类型是 `vNerve.bilibili.live.RoomMessage`。具体的各组件对应的根类型可以在(//TODO)查询。因此首先需要明确从消息队列中接收到的来自哪个组件,以选择合适的根类型。对于 AMQP 连接,你可以直接通过收到消息的 Routing Key 判断。通常各个组件的根对象相同,判断出消息属于的组件之后即可进行解析。 110 | 111 | 解析出根类型之后,您需要查阅对应组件的文档(TODO),确定各个消息的结构。 112 | 113 | 下面是通过 [protobuf.js 6](https://github.com/protobufjs/protobuf.js) 库解析哔哩哔哩直播的弹幕消息的一个例子(protobuf.js 属于第三方库,使用与标准库略有不同的加载 proto 文件的方式。另外其性能也更优,因此在 JavaScript 环境您应该始终使用 protobuf.js)。 114 | 115 | ```javascript 116 | let raw_data = message.binaryBody; // 书接上回 117 | 118 | protobuf.load("vNerve_Proto.json", function(err, root) { // 应该在最开始就加载 proto 并保存 root 对象. 119 | if (err) 120 | throw err; 121 | var RoomMessage = root.lookupType("vNerve.bilibili.live.RoomMessage"); 122 | var message = RoomMessage.decode(raw_data); 123 | 124 | const username = message.user_message.user.name; 125 | const content = message.user_message.danmaku.message; 126 | console.log(username + " said: " + content); 127 | } 128 | ``` 129 | 130 | // TODO测试一下 131 | 132 | 关于如何使用解析后的结构,请参阅对应组件的文档。例如 BiLive。(TODO link) **注意:对于使用 vNerveNeuronWire 的用户而言,数据结构略有优化,请参照vNerveNeuronWire 的文档(todo link)来处理。** 133 | 134 | -------------------------------------------------------------------------------- /docs/VNerve/Inside/Processor/CNS/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | pageIndex: 3 3 | title: CNS 4 | --- 5 | 6 | CNS 7 | -------------------------------------------------------------------------------- /docs/VNerve/Inside/Processor/Effector/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | pageIndex: 2 3 | title: Effector 4 | --- 5 | 6 | Effector 7 | -------------------------------------------------------------------------------- /docs/VNerve/Inside/Processor/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | pageIndex: 2 3 | title: Real-Time Processor 4 | --- 5 | 6 | vNerve Processor 在 vNerve 中负责接收和处理实时消息,由 Receptor(接受器)、CNS(中枢神经系统)和 Effector(效应器)构成,核心代码使用 C++ 编写。轻敲以选择您想要查看的内容。 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /docs/VNerve/Inside/Processor/Supervisor/Communication.md: -------------------------------------------------------------------------------- 1 | --- 2 | pageIndex: 1 3 | title: 通信 4 | --- 5 | 6 | ## 前置内容 7 | 8 | ### 单向通信 9 | 10 | **不管是 Supervisor 还是 Worker,确认发出一段消息之后都会立即停止对该消息的处理流程,而不管对方是否成功接收。** 11 | 12 | 「单向通信」是 vNerve 容灾设计的一部分。不管是 Supervisor 还是 Worker,运行时的设备环境网络环境都是非常复杂的,一条简单的网络请求也有不小的可能性会请求失败。此时,因为有限流,物理网络波动等情况,就算是使用多种方法(如重试等)重新请求也并不能保证成功。除此之外,vNerve 还是一个不间断运行的服务,而不管是 Supervisor 还是 Worker 都瘫痪的可能性。这种情况下,如果发送方持续发送消息而又想确保对方收到消息的话,消息处理的性能消耗便会在短时间内急剧增加,且与运行时间成正相关。这是十分可怕的。为了应对这种情况,在 Supervisor 的设计中,每一个 Work 都是由多个 Worker 负责的。这样的设计极大地确保了 Supervisor 能够顺利接收到 Worker 发出的消息,而不管该条消息是由哪个 Worker 发出的;在 Supervisor 向 Worker 传递指令时若有失败的情况也可保证其他 Worker 的正常运行。因此「确保对方成功接收消息」是不必要的。 13 | 14 | ## 通信类型 15 | -------------------------------------------------------------------------------- /docs/VNerve/Inside/Processor/Supervisor/Income.md: -------------------------------------------------------------------------------- 1 | --- 2 | pageIndex: 2 3 | title: Income 4 | --- 5 | 6 | Income 模块负责处理 Worker 传来的消息。 7 | 8 | ## Workflow 9 | 10 | ### 重置 Timeout 11 | 12 | 在 Worker 传来消息后,Supervisor 首先会重置该 Worker 的 Timeout,保证 Supervisor 与 Worker 的连接,参见 [负载均衡](./LoadBalance.md)。 13 | -------------------------------------------------------------------------------- /docs/VNerve/Inside/Processor/Supervisor/LoadBalance.md: -------------------------------------------------------------------------------- 1 | --- 2 | pageIndex: 3 3 | title: 负载均衡 4 | --- 5 | 6 | Supervisor 的这套负载均衡系统既保证了每个 Worker 的工作负载都能保持在最大负载内,又通过对 Worker 的监控保证了整套系统的高可靠性。 7 | 8 | ## 前置内容 9 | 10 | ### 「被动模式」和基于事件的任务分配 11 | 12 | Supervisor 作为 Real-Time Processor 的中心节点,管理着所有的 Worker 和任务。因此,即便是最简单的轮询操作,乘以 Worker 的与每个 Worker 的任务个数之后也会造成不小的性能开销。为了优化性能,我们应该尽量减少 Supervisor 主动执行轮询或请求操作的次数。为了实现这一目标,Supervisor 使用「基于事件的任务分配和负载均衡」这一理念,将「任务查询」「任务分配」等程序逻辑置于事件之内,只有当任务列表发生变化的时候才会去触发相应的事件。这样的设计确保了 Supervisor 的基础性能占用维持在一个较低的水平。 13 | 14 | ### 定义 15 | 16 | - 任务:一个 Worker 上保持的对一个 Bilibili 直播间的会话。此“任务”应该被视为一种长期运行的任务。也就是说,一个 Worker 会同时持有多个任务,一个房间也会被多个任务同时监听。 17 | 18 | ### Worker 状态 19 | 20 | 基于一个事实:在 Bilibili 直播协议中,一个房间在 30s 内必定会传输至少一个数据包(最少情况下为心跳回复包)。因此,如果一个任务在 30s 内无任何数据上报,可以视为该任务连接中断。Supervisor 将会 Unassign 该任务,并删除该任务的信息,产生一个任务空缺。 21 | 22 | 同样,如果一个 Worker 在 30s 内无任何数据上报,可视为该 Worker 连接中断。Supervisor 将会断开该 Worker 的连接,并删除所有与该 Worker 关联的任务信息,产生与 Worker 关联的任务相等数量的任务空缺。 23 | 24 | ### 连接状态 25 | 26 | 在 [通信](./Communication.md) 一节中我们已经提到过,Supervisor 和 Worker 之间传递的所有消息都是单向的,因此二者之间不会有任何的「握手」流程,因此我们也就没有任何办法显式地判断 Supervisor 和任何一个 Worker 是否处于连接状态。因此,下文中提到的任何「连接状态」实际上都是从 Worker 状态获取的。更改这个值则是使用下面将要讲述的「低精度计时器」。 27 | 28 | ### 低精度计时器 29 | 30 | Supervisor 需要实时监视每个 Worker 的运行状态,因此需要为每一个 Worker 设定一个 Timeout,超出 Timeout 仍没有汇报状态的 Worker 就应该被判断为「不可用」(即 `isActive = false`)后由 Supervisor 进行下一步的处理(触发「任务分配」事件)。但若是为每个 Worker 设定一个 Timeout 并且在一次处理之后立刻启动下一轮 Timeout 的话就会始终有不小数量的逻辑代码属于 Timeout 的等待状态。此外,高精度的 Timeout 也会造成不小的性能开销,但是轮询请求和 Timeout 处理的精度要求却远没有这么高。因此,Supervisor 使用一个低精度的计时器来处理所有的轮询事件和 Timeout 事件。 31 | 32 | 这个低精度计时器提供两种注册方法:轮询和 Timeout,分别使用 `轮询时间` 和 `Timeout 时间` 以及另一个 `Complete 事件回调` 作为参数。`轮询时间` 仅会由「任务查询」使用,`Timeout 时间` 仅会在接收到 Worker 消息时重新设置(参见 [Income](./Income.md))。 33 | 34 | ## 生命周期 35 | 36 | ### 1.预热 37 | 38 | Supervisor 在启动后即进入预热状态,等待 Worker 的连接但不分配任务。预热的时间通常在 30 秒左右。预热完成之后 Supervisor 即会开始任务查询。 39 | 40 | ::: tip 为什么需要预热? 41 | 由于每个 Worker 都会定期向 Supervisor 汇报状态和传递数据,不管 Supervisor 状态如何。因此,Supervisor 启动后所有 Worker 并不能同时连接到 Supervisor 上。这种情况下,直接监听 Worker 的连接并立即进行任务分配的做法显然是不合理的。因此,我们需要等待目前在线的全部 Worker 均成功连接之后再继续操作。 42 | ::: 43 | 44 | ### 2.任务查询 45 | 46 | 「任务查询」是一个事件,用来从 Spider 处更新任务列表。 47 | 48 | #### Invoker 49 | 50 | 「任务查询」在以下情况下会被触发: 51 | 52 | - Supervisor 启动时会被触发,获取任务列表并进行全部分配(同时重置 Worker 状态)。 53 | 54 | - 每半个小时任务均会被触发,获取最新的任务列表和现有的任务列表进行比对,若有新增或减少的任务则会进行重新分配。 55 | 56 | ### 3.任务分配 57 | 58 | 「任务分配」是一个事件,使用**两个任务数组作为前两个参数**(分别为 `add` 和 `remove`),表示需要添加和减少的任务;使用另一个布尔值作为第三个参数,表示是否需要重置 Worker 的任务。接收到「任务分配」的指令后,Supervisor 即会开始任务分配流程。 59 | 60 | #### Invoker 61 | 62 | 「任务分配」在以下情况下会被触发: 63 | 64 | - 启动时由「任务查询」触发,分配所有任务。 65 | 66 | - 由低精度计时器的 Timeout 触发,表示一个 Worker 已经不可用。 67 | 68 | 此时,Supervisor 会检查该 Worker 死亡前运行的任务,并将这些任务添加到 `add` 数组中。 69 | 70 | - 定时任务触发「任务查询」后触发,使用刚刚同步的任务列表进行分配。 71 | 72 | #### Workflow 73 | 74 | 在任务分配开始之前,我们先给所有连接的 Worker 设置一个空的「指令集」,「指令集」中的指令可以是「预先重置」、「添加任务」和「结束任务」。 75 | 76 | - 首先,如果参数中指明需要重置 Worker 的任务,则在给 Worker 的指令中加入「预先重置」的指令。(仅用于 Supervisor 的重新启动。) 77 | 78 | ::: tip TIP 79 | Worker 接到「预先重置」的指令之后就要先结束所有的任务吗?不一定。Worker 可以在接收到「预先重置」的指令之后与传来的新的任务做差集,然后结束不再需要的任务。 80 | ::: 81 | 82 | - 然后,Supervisor 遍历所有的 Worker,找出 `remove` 数组中指示的任务后将「结束任务」的指令添加到提供给相关 Worker 的指令中,并从相关 Worker 的 `正在运行的任务数` 中减去相应的任务个数。 83 | 84 | - 然后,Supervisor 会获取所有 Worker 的状态,通过每个 Worker 的 `可新增任务数` 推算出每个 Worker 的权重。`可新增任务数` 由 `最大负载 - 正在运行的任务数` 计算得到。其中,`最大负载` 是由 Worker 提供的。 85 | 86 | - 接着,Supervisor 使用**总的任务数乘以连接数(参见 [通信](./Communication.md),一般为 2-3)再乘以权重后向上取整**,得出每个 Worker 需要增加的任务数。 87 | 88 | - 最后,Supervisor 为每个 Worker 从 `add` 数组中取出一定量的任务加入该 Worker 的「添加任务」的指令集中并发送所有的指令。若 `add` 数组为空即结束分配。 89 | 90 | ### 4.完成 91 | 92 | 此时,Supervisor 的各个事件已经开始监听,低精度计时器开始运行,Supervisor 进入正常运行状态。此时 Supervisor 会开始从各个 Worker 接收有效荷载,执行去重并发送至 RabbitMQ,以传输至下游应用。 93 | -------------------------------------------------------------------------------- /docs/VNerve/Inside/Processor/Supervisor/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | pageIndex: 1 3 | title: Receptor Supervisor 4 | --- 5 | 6 | Supervisor 是实时消息接收的中心节点,使用一套负载均衡系统管理所有 Worker 并接收由其返回的实时数据,同时负责将数据传递至 CNS。 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /docs/VNerve/Inside/Processor/Worker/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | pageIndex: 4 3 | title: Receptor Worker 4 | --- 5 | 6 | Receptor Worker 7 | -------------------------------------------------------------------------------- /docs/VNerve/Inside/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | pageIndex: 2 3 | title: Inside the vNerve 4 | --- 5 | 6 | 作为一套完整的解决方案,vNerve 由许多组件构成。轻敲以选择您想要查看的内容。 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /docs/VNerve/Inside/Spider/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | pageIndex: 1 3 | title: Spider 4 | --- 5 | 6 | Spider 7 | -------------------------------------------------------------------------------- /docs/VNerve/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | home: true 3 | heroImage: /Icons/Icons/VNerve/VNerve.Colored.Transparent.png 4 | heroText: vNerve 5 | tagline: vNerve 文档站。 6 | actionText: 现在开始 → 7 | actionLink: /VNerve/Guide/ 8 | pageIndex: 990 9 | footer: MIT Licensed | Copyright © 2020 DD Center 10 | --- 11 | 12 | # Project vNerve 13 | 14 | vNerve 旨在提供一个与虚拟主播相关的综合数据源,且希望将各种数据以便于查询、整理与统计的方式提供。 15 | 16 | ## 特性 17 | 18 | - 完全公益:我们不会以任何形式提供任何收费服务; 19 | - 文档完善:所有接口与数据结构均有完善的文档。大部分应用具有手把手的教学; 20 | - 高可用性:在实时性数据部分,我们采用了多重镜像的设计以应对网络波动。例如在 BiLive(处理哔哩哔哩直播实时数据接口),同一个直播间的实时数据会同时被多个服务器接收,去重后路由到下游; 21 | - 多语言友好:我们尽可能将接口与数据结构设计得让使用各种语言、平台的开发者均能有良好的开发体验。我们甚至为 JavaScript 开发者单独提供了开发用 SDK; 22 | - 高度模块化:想给 vNerve 增加新的数据源?没有问题!vNerve 的各个部分高度模块化,,阅读本篇文档后半部分有助于您理解 vNerve 的结构; 23 | 24 | ## vNerve 有何作用? 25 | 26 | 考虑如下几个实例: 27 | 28 | ### 实例 I 29 | 30 | M 君想要开发一个录制全 B 站虚拟主播直播间的超级录播机。因此,他需要实时获得大量直播间开播与下播的通知。 31 | 32 | #### 传统思路 33 | 34 | 1. 轮询 bilibili 直播的直播间状态接口; 35 | 2. 监听近 2000 个直播间的直播间实时 WebSocket 接口; 36 | 37 | 然而,思路 1 面临若轮询速度过慢则数据不够实时,而轮询速度过快则可能被 B 站封 IP 的情况;思路 2 中 M 君被迫去了解 B 站的实时接口协议。然而这一协议较复杂,涉及到 Auth Token 的获取、压缩、心跳等等。且 B 站的实时接口完全处于零文档状态,只在 GitHub 上有一些零散的数据包例子。 38 | 39 | #### 使用 vNerve 的思路 40 | 41 | 此时, M 君只需要通过 vNerve Neuron Wire 或手动连接消息队列的方式,绑定一个 Routing Key `blv.*.live_status`,即可接收到整个 DD Center VDB 中收录的虚拟主播的哔哩哔哩直播间的上下播状态。且接收到的数据的格式具有良好文档。最重要的是,**这种情况下的网络资源浪费是最小的,因为有且仅有上下播状态被传输到 M 君的录播机中**。 42 | 43 | ### 实例 II 44 | 45 | X 君与 D 君想要开发一个统计 YouTube 与 B 站虚拟主播直播收入的网站。因此,他们需要获得所有的 Super Chat、礼物、大航海的通知。 46 | 47 | #### 传统思路 48 | 49 | 1. 对于 YouTube,他们需要使用 YouTube 的私有 API 来统计 Super Chat; 50 | 2. 对于 Bilibili,他们必须监听所有虚拟主播房间的实时接口; 51 | 52 | YouTube 暂且略过不谈(vNerve 目前未提供 YouTube 相关接口,但已列入计划)。但对于 Bilibili 而言,即使他们完全理解了 Bilibili 的弹幕协议并实现了出来,仍然面临不稳定的连接等影响,而这可能在高峰期对统计结果产生巨大的影响。 53 | 54 | #### 使用 vNerve 的思路 55 | 56 | 对于 YouTube 他们仍需要采取传统手段; 57 | 58 | 对于 Bilibili,他们可以绑定多个 Routing Key,绑定到 `blv.*.sc`、`blv.*.gift`、`blv.*.new_guard` 即可获取所有付费礼物相关的数据。而由于 vNerve 采取**单个直播间同时由多个接收端同时接受的高可用性镜像机制**,单个连接或单个接收端的网络波动并不会对统计结果产生较大影响。 59 | 60 | ## 数据形式 61 | 62 | 现阶段 vNerve 包含两种数据提供方式。 63 | 64 | ### 实时性数据 65 | 66 | 实时性数据将以**消息队列**的传输方式,`protobuf` 的数据格式提供。可以通过 `AMQP`、`WebSTOMP`、等协议获取。其中 `WebSTOMP` 可以通过 JavaScript 库在浏览器端直接获取。 67 | 68 | ### 非实时性数据 69 | 70 | 非实时性数据将以 HTTP API 形式提供,使用 GraphQL 协议、JSON 数据格式。 71 | 72 | ## 结构与组件 73 | 74 | vNerve 具有高度模块化的结构,但亦具有方便的接口来统一地获取来自各个数据源的数据。对于实时性数据,vNerve 具有如下结构: 75 | 76 | ``` 77 | Receptor -> CNS -> (NeuronWire) -> Effector 78 | ↓ 79 | vNerve Memory 80 | ``` 81 | 82 | 其中: 83 | 84 | - Receptor(感受器)用于从各个数据源获取数据并统一化数据格式、结构;例如 BiLiveReceptor 85 | - CNS(中枢神经系统)在系统中唯一,用于路由消息并递送到下游应用。这一部分由一个消息队列中间人实现 86 | - NeuronWire 为一些平台(目前仅 JavaScript)提供了更加便利的连接到 CNS 的方式,类似于一个 SDK,这使得下游开发者不需要关心连接细节等内容 87 | - Effector(效应器)即下游应用的开发者与 vNerve 交互的部分,为您的应用程序注入数据 88 | 89 | 对于非实时性数据,vNerve 具有以下结构: 90 | 91 | // TODO 92 | 93 | 而在 vNerve 中,由于数据源多种多样,因此每一种数据源都对应一种数据源组件。例如哔哩哔哩直播的实时弹幕处理即对应 BiLive 组件。为了处理来自各个组件的数据,下游开发者需要阅读各个组件的文档。 94 | 95 | - BiLive:(// TODO link) 96 | 97 | ## Roadmap 98 | 99 | 目前 Project vNerve 正在构建 BiLive 与部署 CNS 系统。在 BiLive 实现稳定、高可用的数据提供后,我们会考虑哔哩哔哩的主站相关的数据更新(例如粉丝量等)。 100 | 101 | ## Contribute 102 | 103 | vNerve 所有代码均开源,欢迎您参与开发。[查看我们的组织与仓库](/VNerve/GitHub.html)。 104 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vbox", 3 | "private": "true", 4 | "version": "0.2.1", 5 | "description": "", 6 | "repository": { 7 | "type": "git", 8 | "url": "git+https://github.com/vbox-moe/VBox.git" 9 | }, 10 | "keywords": [ 11 | "vuepress", 12 | "vbox" 13 | ], 14 | "scripts": { 15 | "dev": "vuepress dev docs", 16 | "build": "vuepress build docs" 17 | }, 18 | "author": "Il Harper", 19 | "license": "MIT", 20 | "bugs": { 21 | "url": "https://github.com/vbox-moe/VBox/issues" 22 | }, 23 | "homepage": "https://github.com/vbox-moe/VBox#readme", 24 | "devDependencies": { 25 | "babel-eslint": "^10.1.0", 26 | "eslint": "^6.4.0", 27 | "eslint-config-prettier": "^6.3.0", 28 | "eslint-plugin-prettier": "^3.1.1", 29 | "eslint-plugin-vue": "^5.2.3", 30 | "prettier": "^1.18.2", 31 | "vue-eslint-parser": "^7.1.0", 32 | "vuepress": "^1.5.2", 33 | "vuepress-plugin-vbox": "^0.2.1", 34 | "vuepress-theme-vbox": "^0.2.7" 35 | }, 36 | "dependencies": { 37 | "axios": "^0.21.1", 38 | "dugite": "^1.91.3" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "dist" 4 | }, 5 | "outDir": "./dist", 6 | "include": [ 7 | "src/**/*.ts" 8 | ], 9 | "exclude": [ 10 | "node_modules" 11 | ] 12 | } --------------------------------------------------------------------------------