diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md index ae35d5b..0641406 100644 --- a/.github/CODE_OF_CONDUCT.md +++ b/.github/CODE_OF_CONDUCT.md @@ -60,7 +60,7 @@ representative at an online or offline event. Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at -lianmengkexin@gmail.com. +@MotooriKashin. All complaints will be reviewed and investigated promptly and fairly. All community leaders are obligated to respect the privacy and security of the diff --git a/.github/workflows/github-pages.yml b/.github/workflows/github-pages.yml index bb00dee..3c24c45 100644 --- a/.github/workflows/github-pages.yml +++ b/.github/workflows/github-pages.yml @@ -9,7 +9,8 @@ on: branches: - 'master' paths: - - 'src/docs/**' + - 'src/options/**' + - 'src/player/**' # 也可以在 GitHub Actions 手动运行 workflow_dispatch: @@ -48,11 +49,11 @@ jobs: - name: Build and release run: npm run github-pages - # 上传网页资源(位于 /src/docs/ 目录) + # 上传网页资源(位于 /dist/options/ 目录) - name: Upload Artifact uses: actions/upload-pages-artifact@v3 with: - path: './src/docs/' + path: './dist/options/' # 部署网页 - name: Deploy to GitHub Pages diff --git a/.gitignore b/.gitignore index c61e686..4aa0f46 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,5 @@ node_modules -dist/_metadata -dist/**/*.js -dist/**/*.css +dist *.crx *.pem *.map \ No newline at end of file diff --git a/.vscode/css.css-data.json b/.vscode/css.css-data.json deleted file mode 100644 index 6558d2e..0000000 --- a/.vscode/css.css-data.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "version": 1.1, - "atDirectives": [ - { - "name": "@scope", - "browsers": [ - "S17.4", - "C118", - "O106" - ], - "references": [ - { - "name": "MDN Reference", - "url": "https://developer.mozilla.org/docs/Web/CSS/@scope" - } - ], - "description": "Creates scoped style rules using CSS syntax." - }, - { - "name": "@starting-style", - "browsers": [ - "S17.5", - "C117", - "O103" - ], - "references": [ - { - "name": "MDN Reference", - "url": "https://developer.mozilla.org/docs/Web/CSS/@starting-style" - } - ], - "description": "Define starting values for properties set on an element that you want to transition from when the element receives its first style update, i.e. when an element is first displayed on a previously loaded page." - } - ] -} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 3cdd1f7..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "css.customData": [ - ".vscode/css.css-data.json" - ], - "json.schemas": [ - { - "fileMatch": [ - "dist/manifest.json" - ], - "url": "https://json.schemastore.org/chrome-manifest" - } - ], -} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 2584fd9..b1ba047 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -10,7 +10,7 @@ }, "problemMatcher": [], "label": "npm: build", - "detail": "node scripts/build.mjs" + "detail": "node --experimental-strip-types ./src/build/index.ts" } ] } \ No newline at end of file diff --git a/README.md b/README.md index cf0739b..53b7a3d 100644 --- a/README.md +++ b/README.md @@ -1,55 +1 @@ -
- logo - -# Bilibili 2019 -恢复 2019 年 12 月 09 日前的部分[B站](https://www.bilibili.com/)页面,为了那些念旧的人 - -
- ---- -这是一个[Google Chrome](https://www.google.com/chrome/)的[manifest V3](https://developer.chrome.com/docs/extensions/mv3/manifest/)扩展项目,恢复 2019 年 12 月 09 日前的部分[B站](https://www.bilibili.com/)页面,尤其是那个小电视播放器。 -项目前身是[Bilibili-Old](https://github.com/MotooriKashin/Bilibili-Old)油猴脚本,在B站原始页面的基础上修修补补了四年多,奈何实在老旧,难以为继,于是有了推倒重来的念头。加上[manifest V3](https://developer.chrome.com/docs/extensions/mv3/manifest/)标准的推行,油猴脚本前途未卜,索性转为扩展项目。 -由于 HTML + js + css 都不再复用,从零开始手搓,肯定做不到完全复刻当年的模样,请多见谅! - ---- -# 功能 -各种页面正在慢慢搭建中…… -- 播放器 - + 视频 - - [x] DASH - - [x] flv - - [x] 本地视频文件 - + 弹幕 - - [x] 普通弹幕(mode1/4/5/6) - - [x] 高级弹幕(mode7) - - [x] 代码弹幕(mode8) - - [x] BAS弹幕(mode9) - - [x] 本地弹幕文件 - + 字幕 - - [x] 在线 CC 字幕 - - [x] 本地 vtt 文件 -- 评论 - + [x] 翻页 -- 页面 - + [x] B站主页 - + [x] av - + [x] Bangumi - ---- -# 安装 -- 欢迎在安装之前访问核心[播放器页面](https://motoorikashin.github.io/Bilibili-2019/)体验一下旧版播放器 -- 若要安装,则要求使用最新的[Google Chrome](https://www.google.com/chrome/)浏览器(当前要求核心版本 125 以上,以后只会更高,恕不另行通知,且暂时无暇理会任何兼容性需求)。 - 1. 在[RELEASE](https://github.com/MotooriKashin/Bilibili-2019/releases)页面下载最新的版本 - 2. 解压到本地磁盘任意目录 - 3. 打开 Chrome [管理扩展程序](chrome://extensions/)页面打开右上角的【开发者模式】 - 4. 点击【加载已解压的扩展程序】按钮加载刚解压出的文件所在目录 - 5. 更新版本请重复上述步骤 - -另外,也可以克隆项目到本地手动构建,参看[代码贡献指南](.github/contributing.md) - -# 维护 -欢迎念旧的人一起搭建记忆中的Bilibili,这里有一份简易的[代码贡献指南](.github/contributing.md)供参考 - ---- -# 开源 -[MIT License](LICENSE) +重构 B 站旧版页面 \ No newline at end of file diff --git a/package.json b/package.json index 8e4eb99..d9597a4 100644 --- a/package.json +++ b/package.json @@ -1,17 +1,16 @@ { - "scripts": { - "build": "node scripts/build.mjs", - "arbitrary-extensions": "node scripts/arbitrary-extensions.mjs", - "github-pages": "node scripts/github-pages.mjs" + "dependencies": { + "flv.js": "*", + "protobufjs": "*" }, "devDependencies": { - "@types/chrome": "*", + "@types/fs-extra": "*", "esbuild": "*", - "navigation-api-types": "*" + "fs-extra": "*" }, - "dependencies": { - "buffer": "*", - "events": "*", - "protobufjs": "*" - } + "scripts": { + "build": "node --no-warnings --experimental-strip-types ./src/build/index.ts", + "arbitrary-extensions": "node --no-warnings --experimental-strip-types ./src/build/arbitrary-extensions.ts" + }, + "type": "module" } \ No newline at end of file diff --git a/scripts/README.md b/scripts/README.md deleted file mode 100644 index 98c6e16..0000000 --- a/scripts/README.md +++ /dev/null @@ -1 +0,0 @@ -负责开发编译过程中各种任务处理的脚本,依赖[Node.js](https://nodejs.org/)环境 \ No newline at end of file diff --git a/scripts/build.mjs b/scripts/build.mjs deleted file mode 100644 index f59e611..0000000 --- a/scripts/build.mjs +++ /dev/null @@ -1,72 +0,0 @@ -import { build } from "esbuild"; - -const style = await build({ - entryPoints: [ - 'src/player/style/index.css', - 'src/main/bilibili/html/style/index.css', - 'src/main/bilibili/player/style/index.css', - 'src/main/bilibili/header/style/index.css', - 'src/main/bilibili/footer/style/index.css', - 'src/main/bilibili/comment/style/index.css', - 'src/main/bilibili/info/style/index.css', - 'src/main/bilibili/desc/style/index.css', - 'src/main/bilibili/home/style/index.css', - ], - bundle: true, - minify: true, - outdir: 'dist', - outbase: "src", - format: 'iife', - // 内嵌源映射以便于调试 - sourcemap: 'inline', - treeShaking: true, - charset: 'utf8', - loader: { - '.woff': 'dataurl', - '.gif': 'dataurl', - '.png': 'dataurl', - }, - write: false, -}); - -// 编译代码文件 -await build({ - entryPoints: [ - 'src/sw/index.ts', - 'src/main/index.ts', - 'src/main/WebRTC.ts', - 'src/main/bilibili/space/index.ts', - 'src/main/bilibili/live/index.ts', - ], - bundle: true, - minify: true, - outdir: 'dist', - outbase: "src", - format: 'iife', - // 内嵌源映射以便于调试 - sourcemap: 'inline', - treeShaking: true, - charset: 'utf8', - loader: { - '.svg': 'text', - '.css': 'text', - }, - define: { - /** 基于哈希消息认证码的一次性口令的密钥 */ - __TOTP_KEY__: `'${crypto.randomUUID()}'`, - /** 播放器样式文件 */ - __BOFQI_STYLE__: `'${style.outputFiles[0].text.replaceAll('\n', '\\n')}'`, - __HTML_STYLE__: `'${style.outputFiles[1].text.replaceAll('\n', '\\n')}'`, - __BILI_PLAYER_STYLE__: `'${style.outputFiles[2].text.replaceAll('\n', '\\n')}'`, - __BILI_HEADER_STYLE__: `'${style.outputFiles[3].text.replaceAll('\n', '\\n')}'`, - __BILI_FOOTER_STYLE__: `'${style.outputFiles[4].text.replaceAll('\n', '\\n')}'`, - __BILI_COMMENT_STYLE__: `'${style.outputFiles[5].text.replaceAll('\n', '\\n')}'`, - __BILI_INFO_STYLE__: `'${style.outputFiles[6].text.replaceAll('\n', '\\n')}'`, - __BILI_DESC_STYLE__: `'${style.outputFiles[7].text.replaceAll('\n', '\\n')}'`, - __BILI_HOME_STYLE__: `'${style.outputFiles[8].text.replaceAll('\n', '\\n')}'`, - }, - supported: { - // 装饰器暂未得到任何浏览器支持 - decorators: false, - } -}); diff --git a/scripts/github-pages.mjs b/scripts/github-pages.mjs deleted file mode 100644 index 1591dd0..0000000 --- a/scripts/github-pages.mjs +++ /dev/null @@ -1,49 +0,0 @@ -import { build } from "esbuild"; - -const style = await build({ - entryPoints: [ - 'src/player/style/index.css' - ], - bundle: true, - minify: true, - outbase: "src", - format: 'iife', - // 内嵌源映射以便于调试 - sourcemap: 'inline', - treeShaking: true, - charset: 'utf8', - loader: { - '.woff': 'dataurl', - '.gif': 'dataurl', - '.png': 'dataurl', - }, - write: false, -}); - -// 编译代码文件 -await build({ - entryPoints: [ - 'src/docs/index.ts' - ], - bundle: true, - minify: true, - outdir: 'src/docs/', - outbase: "src/docs/", - format: 'iife', - // 内嵌源映射以便于调试 - treeShaking: true, - charset: 'utf8', - loader: { - '.svg': 'text', - }, - define: { - /** 基于哈希消息认证码的一次性口令的密钥 */ - __TOTP_KEY__: `'${crypto.randomUUID()}'`, - /** 播放器样式文件 */ - __BOFQI_STYLE__: `'${style.outputFiles[0].text.replaceAll('\n', '\\n')}'`, - }, - supported: { - // 装饰器暂未得到任何浏览器支持 - decorators: false, - } -}); \ No newline at end of file diff --git a/src/README.md b/src/README.md deleted file mode 100644 index 0575e33..0000000 --- a/src/README.md +++ /dev/null @@ -1 +0,0 @@ -源代码及资源所在,开发过程基本上在此目录内进行 \ No newline at end of file diff --git a/dist/_locales/zh/messages.json b/src/_locales/zh/messages.json similarity index 100% rename from dist/_locales/zh/messages.json rename to src/_locales/zh/messages.json diff --git a/src/player/assets/image/color.png b/src/assets/color.png similarity index 100% rename from src/player/assets/image/color.png rename to src/assets/color.png diff --git a/dist/assets/declarative_rules/track.json b/src/assets/declarative_rules/track.json similarity index 92% rename from dist/assets/declarative_rules/track.json rename to src/assets/declarative_rules/track.json index 6b332b0..a74471c 100644 --- a/dist/assets/declarative_rules/track.json +++ b/src/assets/declarative_rules/track.json @@ -140,5 +140,20 @@ "xmlhttprequest" ] } + }, + { + "id": 7, + "action": { + "type": "block" + }, + "condition": { + "requestDomains": [ + "s1.hdslb.com" + ], + "urlFilter": "reporter-pb", + "resourceTypes": [ + "script" + ] + } } ] \ No newline at end of file diff --git a/src/docs/favicon.ico b/src/assets/favicon.ico similarity index 100% rename from src/docs/favicon.ico rename to src/assets/favicon.ico diff --git a/dist/assets/icon.png b/src/assets/icon.png similarity index 100% rename from dist/assets/icon.png rename to src/assets/icon.png diff --git a/src/assets/loading.gif b/src/assets/loading.gif new file mode 100644 index 0000000..31f8504 Binary files /dev/null and b/src/assets/loading.gif differ diff --git a/src/player/assets/image/panel.gif b/src/assets/panel.gif similarity index 100% rename from src/player/assets/image/panel.gif rename to src/assets/panel.gif diff --git a/src/assets/svg/12blocksetting.d.svg.ts b/src/assets/svg/12blocksetting.d.svg.ts new file mode 100644 index 0000000..bd6c4ce --- /dev/null +++ b/src/assets/svg/12blocksetting.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_12blocksetting: string; +export default svg_12blocksetting; \ No newline at end of file diff --git a/src/player/assets/svg/block-setting.svg b/src/assets/svg/12blocksetting.svg similarity index 100% rename from src/player/assets/svg/block-setting.svg rename to src/assets/svg/12blocksetting.svg diff --git a/src/assets/svg/12checkbox.d.svg.ts b/src/assets/svg/12checkbox.d.svg.ts new file mode 100644 index 0000000..e76e43f --- /dev/null +++ b/src/assets/svg/12checkbox.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_12checkbox: string; +export default svg_12checkbox; \ No newline at end of file diff --git a/src/player/assets/svg/checkbox.svg b/src/assets/svg/12checkbox.svg similarity index 100% rename from src/player/assets/svg/checkbox.svg rename to src/assets/svg/12checkbox.svg diff --git a/src/assets/svg/12close.d.svg.ts b/src/assets/svg/12close.d.svg.ts new file mode 100644 index 0000000..d45e2d5 --- /dev/null +++ b/src/assets/svg/12close.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_12close: string; +export default svg_12close; \ No newline at end of file diff --git a/src/player/assets/svg/close.svg b/src/assets/svg/12close.svg similarity index 100% rename from src/player/assets/svg/close.svg rename to src/assets/svg/12close.svg diff --git a/src/assets/svg/12danmuhistory.d.svg.ts b/src/assets/svg/12danmuhistory.d.svg.ts new file mode 100644 index 0000000..c21af6b --- /dev/null +++ b/src/assets/svg/12danmuhistory.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_12danmuhistory: string; +export default svg_12danmuhistory; \ No newline at end of file diff --git a/src/player/assets/svg/danmaku-history.svg b/src/assets/svg/12danmuhistory.svg similarity index 100% rename from src/player/assets/svg/danmaku-history.svg rename to src/assets/svg/12danmuhistory.svg diff --git a/src/assets/svg/12danmulist.d.svg.ts b/src/assets/svg/12danmulist.d.svg.ts new file mode 100644 index 0000000..b1c7ef5 --- /dev/null +++ b/src/assets/svg/12danmulist.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_12danmulist: string; +export default svg_12danmulist; \ No newline at end of file diff --git a/src/player/assets/svg/danmaku-list.svg b/src/assets/svg/12danmulist.svg similarity index 100% rename from src/player/assets/svg/danmaku-list.svg rename to src/assets/svg/12danmulist.svg diff --git a/src/assets/svg/12delete.d.svg.ts b/src/assets/svg/12delete.d.svg.ts new file mode 100644 index 0000000..bd3da52 --- /dev/null +++ b/src/assets/svg/12delete.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_12delete: string; +export default svg_12delete; \ No newline at end of file diff --git a/src/player/assets/svg/delete.svg b/src/assets/svg/12delete.svg similarity index 100% rename from src/player/assets/svg/delete.svg rename to src/assets/svg/12delete.svg diff --git a/src/assets/svg/12down.d.svg.ts b/src/assets/svg/12down.d.svg.ts new file mode 100644 index 0000000..3eebafc --- /dev/null +++ b/src/assets/svg/12down.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_12down: string; +export default svg_12down; \ No newline at end of file diff --git a/src/player/assets/svg/down.svg b/src/assets/svg/12down.svg similarity index 100% rename from src/player/assets/svg/down.svg rename to src/assets/svg/12down.svg diff --git a/src/assets/svg/12iconcomment.d.svg.ts b/src/assets/svg/12iconcomment.d.svg.ts new file mode 100644 index 0000000..9f2ca12 --- /dev/null +++ b/src/assets/svg/12iconcomment.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_12iconcomment: string; +export default svg_12iconcomment; \ No newline at end of file diff --git a/src/player/assets/svg/icon-comment.svg b/src/assets/svg/12iconcomment.svg similarity index 100% rename from src/player/assets/svg/icon-comment.svg rename to src/assets/svg/12iconcomment.svg diff --git a/src/assets/svg/12icondanmu.d.svg.ts b/src/assets/svg/12icondanmu.d.svg.ts new file mode 100644 index 0000000..905136c --- /dev/null +++ b/src/assets/svg/12icondanmu.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_12icondanmu: string; +export default svg_12icondanmu; \ No newline at end of file diff --git a/src/player/assets/svg/icon-danmaku.svg b/src/assets/svg/12icondanmu.svg similarity index 100% rename from src/player/assets/svg/icon-danmaku.svg rename to src/assets/svg/12icondanmu.svg diff --git a/src/assets/svg/12iconfollowed.d.svg.ts b/src/assets/svg/12iconfollowed.d.svg.ts new file mode 100644 index 0000000..094f701 --- /dev/null +++ b/src/assets/svg/12iconfollowed.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_12iconfollowed: string; +export default svg_12iconfollowed; \ No newline at end of file diff --git a/src/player/assets/svg/icon-follow.svg b/src/assets/svg/12iconfollowed.svg similarity index 100% rename from src/player/assets/svg/icon-follow.svg rename to src/assets/svg/12iconfollowed.svg diff --git a/src/assets/svg/12iconplayed.d.svg.ts b/src/assets/svg/12iconplayed.d.svg.ts new file mode 100644 index 0000000..c950240 --- /dev/null +++ b/src/assets/svg/12iconplayed.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_12iconplayed: string; +export default svg_12iconplayed; \ No newline at end of file diff --git a/src/player/assets/svg/icon-played.svg b/src/assets/svg/12iconplayed.svg similarity index 100% rename from src/player/assets/svg/icon-played.svg rename to src/assets/svg/12iconplayed.svg diff --git a/src/assets/svg/12left.d.svg.ts b/src/assets/svg/12left.d.svg.ts new file mode 100644 index 0000000..bb571ac --- /dev/null +++ b/src/assets/svg/12left.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_12left: string; +export default svg_12left; \ No newline at end of file diff --git a/src/player/assets/svg/left.svg b/src/assets/svg/12left.svg similarity index 100% rename from src/player/assets/svg/left.svg rename to src/assets/svg/12left.svg diff --git a/src/assets/svg/12more.d.svg.ts b/src/assets/svg/12more.d.svg.ts new file mode 100644 index 0000000..d0926af --- /dev/null +++ b/src/assets/svg/12more.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_12more: string; +export default svg_12more; \ No newline at end of file diff --git a/src/player/assets/svg/more.svg b/src/assets/svg/12more.svg similarity index 100% rename from src/player/assets/svg/more.svg rename to src/assets/svg/12more.svg diff --git a/src/assets/svg/12refresh.d.svg.ts b/src/assets/svg/12refresh.d.svg.ts new file mode 100644 index 0000000..7e1d46f --- /dev/null +++ b/src/assets/svg/12refresh.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_12refresh: string; +export default svg_12refresh; \ No newline at end of file diff --git a/src/player/assets/svg/reflesh.svg b/src/assets/svg/12refresh.svg similarity index 100% rename from src/player/assets/svg/reflesh.svg rename to src/assets/svg/12refresh.svg diff --git a/src/assets/svg/12right.d.svg.ts b/src/assets/svg/12right.d.svg.ts new file mode 100644 index 0000000..9775eb6 --- /dev/null +++ b/src/assets/svg/12right.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_12right: string; +export default svg_12right; \ No newline at end of file diff --git a/src/player/assets/svg/right.svg b/src/assets/svg/12right.svg similarity index 100% rename from src/player/assets/svg/right.svg rename to src/assets/svg/12right.svg diff --git a/src/assets/svg/12selected2.d.svg.ts b/src/assets/svg/12selected2.d.svg.ts new file mode 100644 index 0000000..dea6913 --- /dev/null +++ b/src/assets/svg/12selected2.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_12selected2: string; +export default svg_12selected2; \ No newline at end of file diff --git a/src/player/assets/svg/checkbox-selected.svg b/src/assets/svg/12selected2.svg similarity index 100% rename from src/player/assets/svg/checkbox-selected.svg rename to src/assets/svg/12selected2.svg diff --git a/src/assets/svg/12sellect.d.svg.ts b/src/assets/svg/12sellect.d.svg.ts new file mode 100644 index 0000000..1668526 --- /dev/null +++ b/src/assets/svg/12sellect.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_12sellect: string; +export default svg_12sellect; \ No newline at end of file diff --git a/src/player/assets/svg/select.svg b/src/assets/svg/12sellect.svg similarity index 100% rename from src/player/assets/svg/select.svg rename to src/assets/svg/12sellect.svg diff --git a/src/assets/svg/12sent.d.svg.ts b/src/assets/svg/12sent.d.svg.ts new file mode 100644 index 0000000..68d8343 --- /dev/null +++ b/src/assets/svg/12sent.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_12sent: string; +export default svg_12sent; \ No newline at end of file diff --git a/src/player/assets/svg/sent.svg b/src/assets/svg/12sent.svg similarity index 100% rename from src/player/assets/svg/sent.svg rename to src/assets/svg/12sent.svg diff --git a/src/assets/svg/12spedanmu.d.svg.ts b/src/assets/svg/12spedanmu.d.svg.ts new file mode 100644 index 0000000..aff5fcc --- /dev/null +++ b/src/assets/svg/12spedanmu.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_12spedanmu: string; +export default svg_12spedanmu; \ No newline at end of file diff --git a/src/player/assets/svg/danmaku-special.svg b/src/assets/svg/12spedanmu.svg similarity index 100% rename from src/player/assets/svg/danmaku-special.svg rename to src/assets/svg/12spedanmu.svg diff --git a/src/assets/svg/12suggest.d.svg.ts b/src/assets/svg/12suggest.d.svg.ts new file mode 100644 index 0000000..9b7ff4f --- /dev/null +++ b/src/assets/svg/12suggest.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_12suggest: string; +export default svg_12suggest; \ No newline at end of file diff --git a/src/player/assets/svg/suggest.svg b/src/assets/svg/12suggest.svg similarity index 100% rename from src/player/assets/svg/suggest.svg rename to src/assets/svg/12suggest.svg diff --git a/src/assets/svg/12up.d.svg.ts b/src/assets/svg/12up.d.svg.ts new file mode 100644 index 0000000..6ec5cda --- /dev/null +++ b/src/assets/svg/12up.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_12up: string; +export default svg_12up; \ No newline at end of file diff --git a/src/player/assets/svg/up.svg b/src/assets/svg/12up.svg similarity index 100% rename from src/player/assets/svg/up.svg rename to src/assets/svg/12up.svg diff --git a/src/assets/svg/24back.d.svg.ts b/src/assets/svg/24back.d.svg.ts new file mode 100644 index 0000000..89e2cde --- /dev/null +++ b/src/assets/svg/24back.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_24back: string; +export default svg_24back; \ No newline at end of file diff --git a/src/player/assets/svg/back.svg b/src/assets/svg/24back.svg similarity index 100% rename from src/player/assets/svg/back.svg rename to src/assets/svg/24back.svg diff --git a/src/assets/svg/24color.d.svg.ts b/src/assets/svg/24color.d.svg.ts new file mode 100644 index 0000000..43caa5c --- /dev/null +++ b/src/assets/svg/24color.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_24color: string; +export default svg_24color; \ No newline at end of file diff --git a/src/player/assets/svg/color.svg b/src/assets/svg/24color.svg similarity index 100% rename from src/player/assets/svg/color.svg rename to src/assets/svg/24color.svg diff --git a/src/assets/svg/24danmucurrent.d.svg.ts b/src/assets/svg/24danmucurrent.d.svg.ts new file mode 100644 index 0000000..ce3e195 --- /dev/null +++ b/src/assets/svg/24danmucurrent.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_24danmucurrent: string; +export default svg_24danmucurrent; \ No newline at end of file diff --git a/src/player/assets/svg/danmaku-current.svg b/src/assets/svg/24danmucurrent.svg similarity index 100% rename from src/player/assets/svg/danmaku-current.svg rename to src/assets/svg/24danmucurrent.svg diff --git a/src/assets/svg/24danmuforbid.d.svg.ts b/src/assets/svg/24danmuforbid.d.svg.ts new file mode 100644 index 0000000..8d8f6ce --- /dev/null +++ b/src/assets/svg/24danmuforbid.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_24danmuforbid: string; +export default svg_24danmuforbid; \ No newline at end of file diff --git a/src/player/assets/svg/danmaku-forbid.svg b/src/assets/svg/24danmuforbid.svg similarity index 100% rename from src/player/assets/svg/danmaku-forbid.svg rename to src/assets/svg/24danmuforbid.svg diff --git a/src/assets/svg/24danmuoff.d.svg.ts b/src/assets/svg/24danmuoff.d.svg.ts new file mode 100644 index 0000000..64cf079 --- /dev/null +++ b/src/assets/svg/24danmuoff.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_24danmuoff: string; +export default svg_24danmuoff; \ No newline at end of file diff --git a/src/player/assets/svg/danmaku-off.svg b/src/assets/svg/24danmuoff.svg similarity index 93% rename from src/player/assets/svg/danmaku-off.svg rename to src/assets/svg/24danmuoff.svg index 7662d90..563a056 100644 --- a/src/player/assets/svg/danmaku-off.svg +++ b/src/assets/svg/24danmuoff.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/assets/svg/24danmuon.d.svg.ts b/src/assets/svg/24danmuon.d.svg.ts new file mode 100644 index 0000000..480d05e --- /dev/null +++ b/src/assets/svg/24danmuon.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_24danmuon: string; +export default svg_24danmuon; \ No newline at end of file diff --git a/src/player/assets/svg/danmaku.svg b/src/assets/svg/24danmuon.svg similarity index 85% rename from src/player/assets/svg/danmaku.svg rename to src/assets/svg/24danmuon.svg index 5868303..d7e2728 100644 --- a/src/player/assets/svg/danmaku.svg +++ b/src/assets/svg/24danmuon.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/assets/svg/24danmusetting.d.svg.ts b/src/assets/svg/24danmusetting.d.svg.ts new file mode 100644 index 0000000..e7defba --- /dev/null +++ b/src/assets/svg/24danmusetting.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_24danmusetting: string; +export default svg_24danmusetting; \ No newline at end of file diff --git a/src/player/assets/svg/danmaku-setting.svg b/src/assets/svg/24danmusetting.svg similarity index 100% rename from src/player/assets/svg/danmaku-setting.svg rename to src/assets/svg/24danmusetting.svg diff --git a/src/assets/svg/24exitfullscreen.d.svg.ts b/src/assets/svg/24exitfullscreen.d.svg.ts new file mode 100644 index 0000000..93da660 --- /dev/null +++ b/src/assets/svg/24exitfullscreen.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_24exitfullscreen: string; +export default svg_24exitfullscreen; \ No newline at end of file diff --git a/src/player/assets/svg/screen-full-web-exit.svg b/src/assets/svg/24exitfullscreen.svg similarity index 89% rename from src/player/assets/svg/screen-full-web-exit.svg rename to src/assets/svg/24exitfullscreen.svg index 6249f30..392e22d 100644 --- a/src/player/assets/svg/screen-full-web-exit.svg +++ b/src/assets/svg/24exitfullscreen.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/assets/svg/24exitpip.d.svg.ts b/src/assets/svg/24exitpip.d.svg.ts new file mode 100644 index 0000000..cb4f654 --- /dev/null +++ b/src/assets/svg/24exitpip.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_24exitpip: string; +export default svg_24exitpip; \ No newline at end of file diff --git a/src/assets/svg/24exitpip.svg b/src/assets/svg/24exitpip.svg new file mode 100644 index 0000000..f4e77c9 --- /dev/null +++ b/src/assets/svg/24exitpip.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/svg/24exitwebfull.d.svg.ts b/src/assets/svg/24exitwebfull.d.svg.ts new file mode 100644 index 0000000..a90c4ec --- /dev/null +++ b/src/assets/svg/24exitwebfull.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_24exitwebfull: string; +export default svg_24exitwebfull; \ No newline at end of file diff --git a/src/assets/svg/24exitwebfull.svg b/src/assets/svg/24exitwebfull.svg new file mode 100644 index 0000000..a54755e --- /dev/null +++ b/src/assets/svg/24exitwebfull.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/svg/24fullscreen.d.svg.ts b/src/assets/svg/24fullscreen.d.svg.ts new file mode 100644 index 0000000..2747434 --- /dev/null +++ b/src/assets/svg/24fullscreen.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_24fullscreen: string; +export default svg_24fullscreen; \ No newline at end of file diff --git a/src/player/assets/svg/screen-full.svg b/src/assets/svg/24fullscreen.svg similarity index 96% rename from src/player/assets/svg/screen-full.svg rename to src/assets/svg/24fullscreen.svg index 8631c51..96b29fb 100644 --- a/src/player/assets/svg/screen-full.svg +++ b/src/assets/svg/24fullscreen.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/assets/svg/24more.d.svg.ts b/src/assets/svg/24more.d.svg.ts new file mode 100644 index 0000000..5c5041c --- /dev/null +++ b/src/assets/svg/24more.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_24more: string; +export default svg_24more; \ No newline at end of file diff --git a/src/player/assets/svg/setting-more.svg b/src/assets/svg/24more.svg similarity index 100% rename from src/player/assets/svg/setting-more.svg rename to src/assets/svg/24more.svg diff --git a/src/assets/svg/24pause.d.svg.ts b/src/assets/svg/24pause.d.svg.ts new file mode 100644 index 0000000..ad8f096 --- /dev/null +++ b/src/assets/svg/24pause.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_24pause: string; +export default svg_24pause; \ No newline at end of file diff --git a/src/player/assets/svg/pause.svg b/src/assets/svg/24pause.svg similarity index 100% rename from src/player/assets/svg/pause.svg rename to src/assets/svg/24pause.svg diff --git a/src/assets/svg/24pip.d.svg.ts b/src/assets/svg/24pip.d.svg.ts new file mode 100644 index 0000000..3152953 --- /dev/null +++ b/src/assets/svg/24pip.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_24pip: string; +export default svg_24pip; \ No newline at end of file diff --git a/src/player/assets/svg/screen-pip.svg b/src/assets/svg/24pip.svg similarity index 69% rename from src/player/assets/svg/screen-pip.svg rename to src/assets/svg/24pip.svg index 347d629..2732d18 100644 --- a/src/player/assets/svg/screen-pip.svg +++ b/src/assets/svg/24pip.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/assets/svg/24play.d.svg.ts b/src/assets/svg/24play.d.svg.ts new file mode 100644 index 0000000..320f50f --- /dev/null +++ b/src/assets/svg/24play.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_24play: string; +export default svg_24play; \ No newline at end of file diff --git a/src/player/assets/svg/play.svg b/src/assets/svg/24play.svg similarity index 89% rename from src/player/assets/svg/play.svg rename to src/assets/svg/24play.svg index 6f0d67c..4c64414 100644 --- a/src/player/assets/svg/play.svg +++ b/src/assets/svg/24play.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/assets/svg/24repeatoff.d.svg.ts b/src/assets/svg/24repeatoff.d.svg.ts new file mode 100644 index 0000000..80eba6d --- /dev/null +++ b/src/assets/svg/24repeatoff.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_24repeatoff: string; +export default svg_24repeatoff; \ No newline at end of file diff --git a/src/player/assets/svg/repeat.svg b/src/assets/svg/24repeatoff.svg similarity index 94% rename from src/player/assets/svg/repeat.svg rename to src/assets/svg/24repeatoff.svg index 81525cb..7ecd771 100644 --- a/src/player/assets/svg/repeat.svg +++ b/src/assets/svg/24repeatoff.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/assets/svg/24repeaton.d.svg.ts b/src/assets/svg/24repeaton.d.svg.ts new file mode 100644 index 0000000..ef800df --- /dev/null +++ b/src/assets/svg/24repeaton.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_24repeaton: string; +export default svg_24repeaton; \ No newline at end of file diff --git a/src/player/assets/svg/repeat-on.svg b/src/assets/svg/24repeaton.svg similarity index 92% rename from src/player/assets/svg/repeat-on.svg rename to src/assets/svg/24repeaton.svg index eaf2557..7026b1a 100644 --- a/src/player/assets/svg/repeat-on.svg +++ b/src/assets/svg/24repeaton.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/assets/svg/24setting.d.svg.ts b/src/assets/svg/24setting.d.svg.ts new file mode 100644 index 0000000..fc82ad0 --- /dev/null +++ b/src/assets/svg/24setting.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_24setting: string; +export default svg_24setting; \ No newline at end of file diff --git a/src/player/assets/svg/setting.svg b/src/assets/svg/24setting.svg similarity index 100% rename from src/player/assets/svg/setting.svg rename to src/assets/svg/24setting.svg diff --git a/src/assets/svg/24soundlarge.d.svg.ts b/src/assets/svg/24soundlarge.d.svg.ts new file mode 100644 index 0000000..bc2538d --- /dev/null +++ b/src/assets/svg/24soundlarge.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_24soundlarge: string; +export default svg_24soundlarge; \ No newline at end of file diff --git a/src/player/assets/svg/volume-large.svg b/src/assets/svg/24soundlarge.svg similarity index 88% rename from src/player/assets/svg/volume-large.svg rename to src/assets/svg/24soundlarge.svg index 38c2ae3..4a57c8b 100644 --- a/src/player/assets/svg/volume-large.svg +++ b/src/assets/svg/24soundlarge.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/assets/svg/24soundoff.d.svg.ts b/src/assets/svg/24soundoff.d.svg.ts new file mode 100644 index 0000000..6d07c81 --- /dev/null +++ b/src/assets/svg/24soundoff.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_24soundoff: string; +export default svg_24soundoff; \ No newline at end of file diff --git a/src/player/assets/svg/volume-muted.svg b/src/assets/svg/24soundoff.svg similarity index 95% rename from src/player/assets/svg/volume-muted.svg rename to src/assets/svg/24soundoff.svg index f1451c7..b1ab9af 100644 --- a/src/player/assets/svg/volume-muted.svg +++ b/src/assets/svg/24soundoff.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/assets/svg/24soundsmall.d.svg.ts b/src/assets/svg/24soundsmall.d.svg.ts new file mode 100644 index 0000000..cdd8c04 --- /dev/null +++ b/src/assets/svg/24soundsmall.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_24soundsmall: string; +export default svg_24soundsmall; \ No newline at end of file diff --git a/src/player/assets/svg/volume.svg b/src/assets/svg/24soundsmall.svg similarity index 89% rename from src/player/assets/svg/volume.svg rename to src/assets/svg/24soundsmall.svg index 93892f7..c6e2ffc 100644 --- a/src/player/assets/svg/volume.svg +++ b/src/assets/svg/24soundsmall.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/assets/svg/24subtitle.d.svg.ts b/src/assets/svg/24subtitle.d.svg.ts new file mode 100644 index 0000000..9af2978 --- /dev/null +++ b/src/assets/svg/24subtitle.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_24subtitle: string; +export default svg_24subtitle; \ No newline at end of file diff --git a/src/player/assets/svg/closed-caption.svg b/src/assets/svg/24subtitle.svg similarity index 100% rename from src/player/assets/svg/closed-caption.svg rename to src/assets/svg/24subtitle.svg diff --git a/src/assets/svg/24subtitleon.d.svg.ts b/src/assets/svg/24subtitleon.d.svg.ts new file mode 100644 index 0000000..a9a64d7 --- /dev/null +++ b/src/assets/svg/24subtitleon.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_24subtitleon: string; +export default svg_24subtitleon; \ No newline at end of file diff --git a/src/player/assets/svg/closed-caption-on.svg b/src/assets/svg/24subtitleon.svg similarity index 100% rename from src/player/assets/svg/closed-caption-on.svg rename to src/assets/svg/24subtitleon.svg diff --git a/src/assets/svg/24webfull.d.svg.ts b/src/assets/svg/24webfull.d.svg.ts new file mode 100644 index 0000000..df028a4 --- /dev/null +++ b/src/assets/svg/24webfull.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_24webfull: string; +export default svg_24webfull; \ No newline at end of file diff --git a/src/player/assets/svg/screen-full-web.svg b/src/assets/svg/24webfull.svg similarity index 97% rename from src/player/assets/svg/screen-full-web.svg rename to src/assets/svg/24webfull.svg index 526bbeb..191d633 100644 --- a/src/player/assets/svg/screen-full-web.svg +++ b/src/assets/svg/24webfull.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/assets/svg/24wideoff.d.svg.ts b/src/assets/svg/24wideoff.d.svg.ts new file mode 100644 index 0000000..a1ba609 --- /dev/null +++ b/src/assets/svg/24wideoff.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_24wideoff: string; +export default svg_24wideoff; \ No newline at end of file diff --git a/src/player/assets/svg/screen-wide.svg b/src/assets/svg/24wideoff.svg similarity index 91% rename from src/player/assets/svg/screen-wide.svg rename to src/assets/svg/24wideoff.svg index 30af02e..253e5bb 100644 --- a/src/player/assets/svg/screen-wide.svg +++ b/src/assets/svg/24wideoff.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/assets/svg/24wideon.d.svg.ts b/src/assets/svg/24wideon.d.svg.ts new file mode 100644 index 0000000..8d8feb7 --- /dev/null +++ b/src/assets/svg/24wideon.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_24wideon: string; +export default svg_24wideon; \ No newline at end of file diff --git a/src/player/assets/svg/screen-wide-on.svg b/src/assets/svg/24wideon.svg similarity index 94% rename from src/player/assets/svg/screen-wide-on.svg rename to src/assets/svg/24wideon.svg index 4e654d6..79c2360 100644 --- a/src/player/assets/svg/screen-wide-on.svg +++ b/src/assets/svg/24wideon.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/assets/svg/32coin.d.svg.ts b/src/assets/svg/32coin.d.svg.ts new file mode 100644 index 0000000..a4042fc --- /dev/null +++ b/src/assets/svg/32coin.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_32coin: string; +export default svg_32coin; \ No newline at end of file diff --git a/src/player/assets/svg/coin.svg b/src/assets/svg/32coin.svg similarity index 100% rename from src/player/assets/svg/coin.svg rename to src/assets/svg/32coin.svg diff --git a/src/assets/svg/32replay.d.svg.ts b/src/assets/svg/32replay.d.svg.ts new file mode 100644 index 0000000..812691c --- /dev/null +++ b/src/assets/svg/32replay.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_32replay: string; +export default svg_32replay; \ No newline at end of file diff --git a/src/player/assets/svg/replay.svg b/src/assets/svg/32replay.svg similarity index 100% rename from src/player/assets/svg/replay.svg rename to src/assets/svg/32replay.svg diff --git a/src/assets/svg/32share.d.svg.ts b/src/assets/svg/32share.d.svg.ts new file mode 100644 index 0000000..d6101aa --- /dev/null +++ b/src/assets/svg/32share.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_32share: string; +export default svg_32share; \ No newline at end of file diff --git a/src/player/assets/svg/share.svg b/src/assets/svg/32share.svg similarity index 100% rename from src/player/assets/svg/share.svg rename to src/assets/svg/32share.svg diff --git a/src/assets/svg/48danmuback.d.svg.ts b/src/assets/svg/48danmuback.d.svg.ts new file mode 100644 index 0000000..c9727e6 --- /dev/null +++ b/src/assets/svg/48danmuback.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_48danmuback: string; +export default svg_48danmuback; \ No newline at end of file diff --git a/src/player/assets/svg/danmaku-mode-6.svg b/src/assets/svg/48danmuback.svg similarity index 100% rename from src/player/assets/svg/danmaku-mode-6.svg rename to src/assets/svg/48danmuback.svg diff --git a/src/assets/svg/48danmubottom.d.svg.ts b/src/assets/svg/48danmubottom.d.svg.ts new file mode 100644 index 0000000..cc00210 --- /dev/null +++ b/src/assets/svg/48danmubottom.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_48danmubottom: string; +export default svg_48danmubottom; \ No newline at end of file diff --git a/src/player/assets/svg/danmaku-mode-4.svg b/src/assets/svg/48danmubottom.svg similarity index 100% rename from src/player/assets/svg/danmaku-mode-4.svg rename to src/assets/svg/48danmubottom.svg diff --git a/src/assets/svg/48danmucode.d.svg.ts b/src/assets/svg/48danmucode.d.svg.ts new file mode 100644 index 0000000..6786222 --- /dev/null +++ b/src/assets/svg/48danmucode.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_48danmucode: string; +export default svg_48danmucode; \ No newline at end of file diff --git a/src/player/assets/svg/danmaku-mode-8.svg b/src/assets/svg/48danmucode.svg similarity index 100% rename from src/player/assets/svg/danmaku-mode-8.svg rename to src/assets/svg/48danmucode.svg diff --git a/src/assets/svg/48danmucolor.d.svg.ts b/src/assets/svg/48danmucolor.d.svg.ts new file mode 100644 index 0000000..4a131e5 --- /dev/null +++ b/src/assets/svg/48danmucolor.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_48danmucolor: string; +export default svg_48danmucolor; \ No newline at end of file diff --git a/src/player/assets/svg/danmaku-color.svg b/src/assets/svg/48danmucolor.svg similarity index 100% rename from src/player/assets/svg/danmaku-color.svg rename to src/assets/svg/48danmucolor.svg diff --git a/src/assets/svg/48danmunorm.d.svg.ts b/src/assets/svg/48danmunorm.d.svg.ts new file mode 100644 index 0000000..bd061a8 --- /dev/null +++ b/src/assets/svg/48danmunorm.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_48danmunorm: string; +export default svg_48danmunorm; \ No newline at end of file diff --git a/src/player/assets/svg/danmaku-normal.svg b/src/assets/svg/48danmunorm.svg similarity index 100% rename from src/player/assets/svg/danmaku-normal.svg rename to src/assets/svg/48danmunorm.svg diff --git a/src/assets/svg/48danmunormal.d.svg.ts b/src/assets/svg/48danmunormal.d.svg.ts new file mode 100644 index 0000000..d15d22a --- /dev/null +++ b/src/assets/svg/48danmunormal.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_48danmunormal: string; +export default svg_48danmunormal; \ No newline at end of file diff --git a/src/player/assets/svg/danmaku-mode-1.svg b/src/assets/svg/48danmunormal.svg similarity index 100% rename from src/player/assets/svg/danmaku-mode-1.svg rename to src/assets/svg/48danmunormal.svg diff --git a/src/assets/svg/48danmuspe.d.svg.ts b/src/assets/svg/48danmuspe.d.svg.ts new file mode 100644 index 0000000..455d8f7 --- /dev/null +++ b/src/assets/svg/48danmuspe.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_48danmuspe: string; +export default svg_48danmuspe; \ No newline at end of file diff --git a/src/player/assets/svg/danmaku-mode-7.svg b/src/assets/svg/48danmuspe.svg similarity index 100% rename from src/player/assets/svg/danmaku-mode-7.svg rename to src/assets/svg/48danmuspe.svg diff --git a/src/assets/svg/48danmutext.d.svg.ts b/src/assets/svg/48danmutext.d.svg.ts new file mode 100644 index 0000000..b12f914 --- /dev/null +++ b/src/assets/svg/48danmutext.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_48danmutext: string; +export default svg_48danmutext; \ No newline at end of file diff --git a/src/player/assets/svg/danmaku-text.svg b/src/assets/svg/48danmutext.svg similarity index 100% rename from src/player/assets/svg/danmaku-text.svg rename to src/assets/svg/48danmutext.svg diff --git a/src/assets/svg/48danmutop.d.svg.ts b/src/assets/svg/48danmutop.d.svg.ts new file mode 100644 index 0000000..98a038a --- /dev/null +++ b/src/assets/svg/48danmutop.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_48danmutop: string; +export default svg_48danmutop; \ No newline at end of file diff --git a/src/player/assets/svg/danmaku-mode-5.svg b/src/assets/svg/48danmutop.svg similarity index 100% rename from src/player/assets/svg/danmaku-mode-5.svg rename to src/assets/svg/48danmutop.svg diff --git a/src/assets/svg/48danmuunreg.d.svg.ts b/src/assets/svg/48danmuunreg.d.svg.ts new file mode 100644 index 0000000..19a6a64 --- /dev/null +++ b/src/assets/svg/48danmuunreg.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_48danmuunreg: string; +export default svg_48danmuunreg; \ No newline at end of file diff --git a/src/player/assets/svg/danmaku-unregister.svg b/src/assets/svg/48danmuunreg.svg similarity index 100% rename from src/player/assets/svg/danmaku-unregister.svg rename to src/assets/svg/48danmuunreg.svg diff --git a/src/assets/svg/48kongjian.d.svg.ts b/src/assets/svg/48kongjian.d.svg.ts new file mode 100644 index 0000000..057d92d --- /dev/null +++ b/src/assets/svg/48kongjian.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_48kongjian: string; +export default svg_48kongjian; \ No newline at end of file diff --git a/src/player/assets/svg/qqzone.svg b/src/assets/svg/48kongjian.svg similarity index 100% rename from src/player/assets/svg/qqzone.svg rename to src/assets/svg/48kongjian.svg diff --git a/src/assets/svg/48qq.d.svg.ts b/src/assets/svg/48qq.d.svg.ts new file mode 100644 index 0000000..071d77f --- /dev/null +++ b/src/assets/svg/48qq.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_48qq: string; +export default svg_48qq; \ No newline at end of file diff --git a/src/player/assets/svg/qq.svg b/src/assets/svg/48qq.svg similarity index 100% rename from src/player/assets/svg/qq.svg rename to src/assets/svg/48qq.svg diff --git a/src/assets/svg/48tieba.d.svg.ts b/src/assets/svg/48tieba.d.svg.ts new file mode 100644 index 0000000..3d7c2ca --- /dev/null +++ b/src/assets/svg/48tieba.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_48tieba: string; +export default svg_48tieba; \ No newline at end of file diff --git a/src/player/assets/svg/tieba.svg b/src/assets/svg/48tieba.svg similarity index 100% rename from src/player/assets/svg/tieba.svg rename to src/assets/svg/48tieba.svg diff --git a/src/assets/svg/48weibo.d.svg.ts b/src/assets/svg/48weibo.d.svg.ts new file mode 100644 index 0000000..985661c --- /dev/null +++ b/src/assets/svg/48weibo.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_48weibo: string; +export default svg_48weibo; \ No newline at end of file diff --git a/src/player/assets/svg/weibo.svg b/src/assets/svg/48weibo.svg similarity index 100% rename from src/player/assets/svg/weibo.svg rename to src/assets/svg/48weibo.svg diff --git a/src/assets/svg/Navbar_logo.d.svg.ts b/src/assets/svg/Navbar_logo.d.svg.ts new file mode 100644 index 0000000..6aa6e3b --- /dev/null +++ b/src/assets/svg/Navbar_logo.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_Navbar_logo: string; +export default svg_Navbar_logo; \ No newline at end of file diff --git a/src/player/assets/svg/bilibili.svg b/src/assets/svg/Navbar_logo.svg similarity index 100% rename from src/player/assets/svg/bilibili.svg rename to src/assets/svg/Navbar_logo.svg diff --git a/src/assets/svg/Navbar_mobile.d.svg.ts b/src/assets/svg/Navbar_mobile.d.svg.ts new file mode 100644 index 0000000..35f9fbf --- /dev/null +++ b/src/assets/svg/Navbar_mobile.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_Navbar_mobile: string; +export default svg_Navbar_mobile; \ No newline at end of file diff --git a/src/player/assets/svg/mobile.svg b/src/assets/svg/Navbar_mobile.svg similarity index 100% rename from src/player/assets/svg/mobile.svg rename to src/assets/svg/Navbar_mobile.svg diff --git a/src/assets/svg/UP.d.svg.ts b/src/assets/svg/UP.d.svg.ts new file mode 100644 index 0000000..fc6d93c --- /dev/null +++ b/src/assets/svg/UP.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_UP: string; +export default svg_UP; \ No newline at end of file diff --git a/src/player/assets/svg/upper.svg b/src/assets/svg/UP.svg similarity index 100% rename from src/player/assets/svg/upper.svg rename to src/assets/svg/UP.svg diff --git a/src/assets/svg/activit.d.svg.ts b/src/assets/svg/activit.d.svg.ts new file mode 100644 index 0000000..08d5e6e --- /dev/null +++ b/src/assets/svg/activit.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_activit: string; +export default svg_activit; \ No newline at end of file diff --git a/src/player/assets/svg/activity.svg b/src/assets/svg/activit.svg similarity index 100% rename from src/player/assets/svg/activity.svg rename to src/assets/svg/activit.svg diff --git a/src/player/assets/svg/ad.d.svg.ts b/src/assets/svg/ad.d.svg.ts similarity index 100% rename from src/player/assets/svg/ad.d.svg.ts rename to src/assets/svg/ad.d.svg.ts diff --git a/src/player/assets/svg/ad.svg b/src/assets/svg/ad.svg similarity index 100% rename from src/player/assets/svg/ad.svg rename to src/assets/svg/ad.svg diff --git a/src/player/assets/svg/anime.d.svg.ts b/src/assets/svg/anime.d.svg.ts similarity index 100% rename from src/player/assets/svg/anime.d.svg.ts rename to src/assets/svg/anime.d.svg.ts diff --git a/src/player/assets/svg/anime.svg b/src/assets/svg/anime.svg similarity index 100% rename from src/player/assets/svg/anime.svg rename to src/assets/svg/anime.svg diff --git a/src/assets/svg/biankuangxiaosanjiaoyoushang.d.svg.ts b/src/assets/svg/biankuangxiaosanjiaoyoushang.d.svg.ts new file mode 100644 index 0000000..83ab7a9 --- /dev/null +++ b/src/assets/svg/biankuangxiaosanjiaoyoushang.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_biankuangxiaosanjiaoyoushang: string; +export default svg_biankuangxiaosanjiaoyoushang; \ No newline at end of file diff --git a/src/player/assets/svg/triangle-start-end.svg b/src/assets/svg/biankuangxiaosanjiaoyoushang.svg similarity index 100% rename from src/player/assets/svg/triangle-start-end.svg rename to src/assets/svg/biankuangxiaosanjiaoyoushang.svg diff --git a/src/assets/svg/biankuangxiaosanjiaoyouxia.d.svg.ts b/src/assets/svg/biankuangxiaosanjiaoyouxia.d.svg.ts new file mode 100644 index 0000000..30fcaa2 --- /dev/null +++ b/src/assets/svg/biankuangxiaosanjiaoyouxia.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_biankuangxiaosanjiaoyouxia: string; +export default svg_biankuangxiaosanjiaoyouxia; \ No newline at end of file diff --git a/src/player/assets/svg/triangle-end-end.svg b/src/assets/svg/biankuangxiaosanjiaoyouxia.svg similarity index 100% rename from src/player/assets/svg/triangle-end-end.svg rename to src/assets/svg/biankuangxiaosanjiaoyouxia.svg diff --git a/src/assets/svg/biankuangxiaosanjiaozuoshang.d.svg.ts b/src/assets/svg/biankuangxiaosanjiaozuoshang.d.svg.ts new file mode 100644 index 0000000..39a7b63 --- /dev/null +++ b/src/assets/svg/biankuangxiaosanjiaozuoshang.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_biankuangxiaosanjiaozuoshang: string; +export default svg_biankuangxiaosanjiaozuoshang; \ No newline at end of file diff --git a/src/player/assets/svg/triangle-start-start.svg b/src/assets/svg/biankuangxiaosanjiaozuoshang.svg similarity index 100% rename from src/player/assets/svg/triangle-start-start.svg rename to src/assets/svg/biankuangxiaosanjiaozuoshang.svg diff --git a/src/assets/svg/biankuangxiaosanjiaozuoxia.d.svg.ts b/src/assets/svg/biankuangxiaosanjiaozuoxia.d.svg.ts new file mode 100644 index 0000000..6206fdc --- /dev/null +++ b/src/assets/svg/biankuangxiaosanjiaozuoxia.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_biankuangxiaosanjiaozuoxia: string; +export default svg_biankuangxiaosanjiaozuoxia; \ No newline at end of file diff --git a/src/player/assets/svg/triangle-end-start.svg b/src/assets/svg/biankuangxiaosanjiaozuoxia.svg similarity index 100% rename from src/player/assets/svg/triangle-end-start.svg rename to src/assets/svg/biankuangxiaosanjiaozuoxia.svg diff --git a/src/assets/svg/biaotizhuangshiyou.d.svg.ts b/src/assets/svg/biaotizhuangshiyou.d.svg.ts new file mode 100644 index 0000000..4ad77f0 --- /dev/null +++ b/src/assets/svg/biaotizhuangshiyou.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_biaotizhuangshiyou: string; +export default svg_biaotizhuangshiyou; \ No newline at end of file diff --git a/src/player/assets/svg/nailhead-right.svg b/src/assets/svg/biaotizhuangshiyou.svg similarity index 100% rename from src/player/assets/svg/nailhead-right.svg rename to src/assets/svg/biaotizhuangshiyou.svg diff --git a/src/assets/svg/biaotizhuangshizuo.d.svg.ts b/src/assets/svg/biaotizhuangshizuo.d.svg.ts new file mode 100644 index 0000000..7d4b958 --- /dev/null +++ b/src/assets/svg/biaotizhuangshizuo.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_biaotizhuangshizuo: string; +export default svg_biaotizhuangshizuo; \ No newline at end of file diff --git a/src/player/assets/svg/nailhead-left.svg b/src/assets/svg/biaotizhuangshizuo.svg similarity index 100% rename from src/player/assets/svg/nailhead-left.svg rename to src/assets/svg/biaotizhuangshizuo.svg diff --git a/src/assets/svg/bilibili-tv.d.svg.ts b/src/assets/svg/bilibili-tv.d.svg.ts new file mode 100644 index 0000000..76229b8 --- /dev/null +++ b/src/assets/svg/bilibili-tv.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_bilibili_tv: string; +export default svg_bilibili_tv; \ No newline at end of file diff --git a/src/player/assets/svg/tv.svg b/src/assets/svg/bilibili-tv.svg similarity index 100% rename from src/player/assets/svg/tv.svg rename to src/assets/svg/bilibili-tv.svg diff --git a/src/player/assets/svg/blackroom.d.svg.ts b/src/assets/svg/blackroom.d.svg.ts similarity index 100% rename from src/player/assets/svg/blackroom.d.svg.ts rename to src/assets/svg/blackroom.d.svg.ts diff --git a/src/player/assets/svg/blackroom.svg b/src/assets/svg/blackroom.svg similarity index 100% rename from src/player/assets/svg/blackroom.svg rename to src/assets/svg/blackroom.svg diff --git a/src/assets/svg/blingblingl.d.svg.ts b/src/assets/svg/blingblingl.d.svg.ts new file mode 100644 index 0000000..77bea33 --- /dev/null +++ b/src/assets/svg/blingblingl.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_blingblingl: string; +export default svg_blingblingl; \ No newline at end of file diff --git a/src/player/assets/svg/blingbling-left.svg b/src/assets/svg/blingblingl.svg similarity index 100% rename from src/player/assets/svg/blingbling-left.svg rename to src/assets/svg/blingblingl.svg diff --git a/src/assets/svg/blingblingr.d.svg.ts b/src/assets/svg/blingblingr.d.svg.ts new file mode 100644 index 0000000..e36ac9a --- /dev/null +++ b/src/assets/svg/blingblingr.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_blingblingr: string; +export default svg_blingblingr; \ No newline at end of file diff --git a/src/player/assets/svg/blingbling-right.svg b/src/assets/svg/blingblingr.svg similarity index 100% rename from src/player/assets/svg/blingbling-right.svg rename to src/assets/svg/blingblingr.svg diff --git a/src/assets/svg/bofang.d.svg.ts b/src/assets/svg/bofang.d.svg.ts new file mode 100644 index 0000000..917ce1d --- /dev/null +++ b/src/assets/svg/bofang.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_bofang: string; +export default svg_bofang; \ No newline at end of file diff --git a/src/assets/svg/bofang.svg b/src/assets/svg/bofang.svg new file mode 100644 index 0000000..e8454d3 --- /dev/null +++ b/src/assets/svg/bofang.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/player/assets/svg/cinema.d.svg.ts b/src/assets/svg/cinema.d.svg.ts similarity index 100% rename from src/player/assets/svg/cinema.d.svg.ts rename to src/assets/svg/cinema.d.svg.ts diff --git a/src/player/assets/svg/cinema.svg b/src/assets/svg/cinema.svg similarity index 100% rename from src/player/assets/svg/cinema.svg rename to src/assets/svg/cinema.svg diff --git a/src/player/assets/svg/cinephile.d.svg.ts b/src/assets/svg/cinephile.d.svg.ts similarity index 100% rename from src/player/assets/svg/cinephile.d.svg.ts rename to src/assets/svg/cinephile.d.svg.ts diff --git a/src/player/assets/svg/cinephile.svg b/src/assets/svg/cinephile.svg similarity index 100% rename from src/player/assets/svg/cinephile.svg rename to src/assets/svg/cinephile.svg diff --git a/src/player/assets/svg/close.d.svg.ts b/src/assets/svg/close.d.svg.ts similarity index 100% rename from src/player/assets/svg/close.d.svg.ts rename to src/assets/svg/close.d.svg.ts diff --git a/src/player/assets/svg/cancel.svg b/src/assets/svg/close.svg similarity index 100% rename from src/player/assets/svg/cancel.svg rename to src/assets/svg/close.svg diff --git a/src/player/assets/svg/complete.d.svg.ts b/src/assets/svg/complete.d.svg.ts similarity index 100% rename from src/player/assets/svg/complete.d.svg.ts rename to src/assets/svg/complete.d.svg.ts diff --git a/src/player/assets/svg/complete.svg b/src/assets/svg/complete.svg similarity index 100% rename from src/player/assets/svg/complete.svg rename to src/assets/svg/complete.svg diff --git a/src/player/assets/svg/dance.d.svg.ts b/src/assets/svg/dance.d.svg.ts similarity index 100% rename from src/player/assets/svg/dance.d.svg.ts rename to src/assets/svg/dance.d.svg.ts diff --git a/src/player/assets/svg/dance.svg b/src/assets/svg/dance.svg similarity index 100% rename from src/player/assets/svg/dance.svg rename to src/assets/svg/dance.svg diff --git a/src/assets/svg/daoxubofang.d.svg.ts b/src/assets/svg/daoxubofang.d.svg.ts new file mode 100644 index 0000000..85b5f77 --- /dev/null +++ b/src/assets/svg/daoxubofang.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_daoxubofang: string; +export default svg_daoxubofang; \ No newline at end of file diff --git a/src/player/assets/svg/reverse.svg b/src/assets/svg/daoxubofang.svg similarity index 100% rename from src/player/assets/svg/reverse.svg rename to src/assets/svg/daoxubofang.svg diff --git a/src/player/assets/svg/digital.d.svg.ts b/src/assets/svg/digital.d.svg.ts similarity index 100% rename from src/player/assets/svg/digital.d.svg.ts rename to src/assets/svg/digital.d.svg.ts diff --git a/src/player/assets/svg/digital.svg b/src/assets/svg/digital.svg similarity index 100% rename from src/player/assets/svg/digital.svg rename to src/assets/svg/digital.svg diff --git a/src/player/assets/svg/douga.d.svg.ts b/src/assets/svg/douga.d.svg.ts similarity index 100% rename from src/player/assets/svg/douga.d.svg.ts rename to src/assets/svg/douga.d.svg.ts diff --git a/src/player/assets/svg/douga.svg b/src/assets/svg/douga.svg similarity index 100% rename from src/player/assets/svg/douga.svg rename to src/assets/svg/douga.svg diff --git a/src/player/assets/svg/ent.d.svg.ts b/src/assets/svg/ent.d.svg.ts similarity index 100% rename from src/player/assets/svg/ent.d.svg.ts rename to src/assets/svg/ent.d.svg.ts diff --git a/src/player/assets/svg/ent.svg b/src/assets/svg/ent.svg similarity index 100% rename from src/player/assets/svg/ent.svg rename to src/assets/svg/ent.svg diff --git a/src/player/assets/svg/fashion.d.svg.ts b/src/assets/svg/fashion.d.svg.ts similarity index 100% rename from src/player/assets/svg/fashion.d.svg.ts rename to src/assets/svg/fashion.d.svg.ts diff --git a/src/player/assets/svg/fashion.svg b/src/assets/svg/fashion.svg similarity index 100% rename from src/player/assets/svg/fashion.svg rename to src/assets/svg/fashion.svg diff --git a/src/player/assets/svg/game.d.svg.ts b/src/assets/svg/game.d.svg.ts similarity index 100% rename from src/player/assets/svg/game.d.svg.ts rename to src/assets/svg/game.d.svg.ts diff --git a/src/player/assets/svg/game.svg b/src/assets/svg/game.svg similarity index 100% rename from src/player/assets/svg/game.svg rename to src/assets/svg/game.svg diff --git a/src/assets/svg/general_pullup_s.d.svg.ts b/src/assets/svg/general_pullup_s.d.svg.ts new file mode 100644 index 0000000..1a0be11 --- /dev/null +++ b/src/assets/svg/general_pullup_s.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_general_pullup_s: string; +export default svg_general_pullup_s; \ No newline at end of file diff --git a/src/player/assets/svg/pull-up.svg b/src/assets/svg/general_pullup_s.svg similarity index 100% rename from src/player/assets/svg/pull-up.svg rename to src/assets/svg/general_pullup_s.svg diff --git a/src/assets/svg/general_search.d.svg.ts b/src/assets/svg/general_search.d.svg.ts new file mode 100644 index 0000000..99fda62 --- /dev/null +++ b/src/assets/svg/general_search.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_general_search: string; +export default svg_general_search; \ No newline at end of file diff --git a/src/player/assets/svg/search.svg b/src/assets/svg/general_search.svg similarity index 100% rename from src/player/assets/svg/search.svg rename to src/assets/svg/general_search.svg diff --git a/src/assets/svg/general_upload.d.svg.ts b/src/assets/svg/general_upload.d.svg.ts new file mode 100644 index 0000000..12bef7a --- /dev/null +++ b/src/assets/svg/general_upload.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_general_upload: string; +export default svg_general_upload; \ No newline at end of file diff --git a/src/player/assets/svg/upload.svg b/src/assets/svg/general_upload.svg similarity index 100% rename from src/player/assets/svg/upload.svg rename to src/assets/svg/general_upload.svg diff --git a/src/assets/svg/guochuang.d.svg.ts b/src/assets/svg/guochuang.d.svg.ts new file mode 100644 index 0000000..9ac3525 --- /dev/null +++ b/src/assets/svg/guochuang.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_guochuang: string; +export default svg_guochuang; \ No newline at end of file diff --git a/src/player/assets/svg/donghua.svg b/src/assets/svg/guochuang.svg similarity index 100% rename from src/player/assets/svg/donghua.svg rename to src/assets/svg/guochuang.svg diff --git a/src/assets/svg/ic_gamecenter_blue.d.svg.ts b/src/assets/svg/ic_gamecenter_blue.d.svg.ts new file mode 100644 index 0000000..c7c3195 --- /dev/null +++ b/src/assets/svg/ic_gamecenter_blue.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_ic_gamecenter_blue: string; +export default svg_ic_gamecenter_blue; \ No newline at end of file diff --git a/src/assets/svg/ic_gamecenter_blue.svg b/src/assets/svg/ic_gamecenter_blue.svg new file mode 100644 index 0000000..b062816 --- /dev/null +++ b/src/assets/svg/ic_gamecenter_blue.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/svg/ic_gc_arrow_right_light.d.svg.ts b/src/assets/svg/ic_gc_arrow_right_light.d.svg.ts new file mode 100644 index 0000000..ec7f6f4 --- /dev/null +++ b/src/assets/svg/ic_gc_arrow_right_light.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_ic_gc_arrow_right_light: string; +export default svg_ic_gc_arrow_right_light; \ No newline at end of file diff --git a/src/assets/svg/ic_gc_arrow_right_light.svg b/src/assets/svg/ic_gc_arrow_right_light.svg new file mode 100644 index 0000000..720f890 --- /dev/null +++ b/src/assets/svg/ic_gc_arrow_right_light.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/svg/ic_gc_biggift.d.svg.ts b/src/assets/svg/ic_gc_biggift.d.svg.ts new file mode 100644 index 0000000..ff43802 --- /dev/null +++ b/src/assets/svg/ic_gc_biggift.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_ic_gc_biggift: string; +export default svg_ic_gc_biggift; \ No newline at end of file diff --git a/src/assets/svg/ic_gc_biggift.svg b/src/assets/svg/ic_gc_biggift.svg new file mode 100644 index 0000000..93c33fa --- /dev/null +++ b/src/assets/svg/ic_gc_biggift.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/svg/ic_gc_close_circle.d.svg.ts b/src/assets/svg/ic_gc_close_circle.d.svg.ts new file mode 100644 index 0000000..01765f4 --- /dev/null +++ b/src/assets/svg/ic_gc_close_circle.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_ic_gc_close_circle: string; +export default svg_ic_gc_close_circle; \ No newline at end of file diff --git a/src/assets/svg/ic_gc_close_circle.svg b/src/assets/svg/ic_gc_close_circle.svg new file mode 100644 index 0000000..78a3fed --- /dev/null +++ b/src/assets/svg/ic_gc_close_circle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/svg/ic_gc_close_circle_full.d.svg.ts b/src/assets/svg/ic_gc_close_circle_full.d.svg.ts new file mode 100644 index 0000000..b0ba2f2 --- /dev/null +++ b/src/assets/svg/ic_gc_close_circle_full.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_ic_gc_close_circle_full: string; +export default svg_ic_gc_close_circle_full; \ No newline at end of file diff --git a/src/assets/svg/ic_gc_close_circle_full.svg b/src/assets/svg/ic_gc_close_circle_full.svg new file mode 100644 index 0000000..be491d8 --- /dev/null +++ b/src/assets/svg/ic_gc_close_circle_full.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/svg/ic_gc_comment_more.d.svg.ts b/src/assets/svg/ic_gc_comment_more.d.svg.ts new file mode 100644 index 0000000..0ce4a0c --- /dev/null +++ b/src/assets/svg/ic_gc_comment_more.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_ic_gc_comment_more: string; +export default svg_ic_gc_comment_more; \ No newline at end of file diff --git a/src/assets/svg/ic_gc_comment_more.svg b/src/assets/svg/ic_gc_comment_more.svg new file mode 100644 index 0000000..ef3b86f --- /dev/null +++ b/src/assets/svg/ic_gc_comment_more.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/svg/ic_gc_follow_video.d.svg.ts b/src/assets/svg/ic_gc_follow_video.d.svg.ts new file mode 100644 index 0000000..673a41a --- /dev/null +++ b/src/assets/svg/ic_gc_follow_video.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_ic_gc_follow_video: string; +export default svg_ic_gc_follow_video; \ No newline at end of file diff --git a/src/assets/svg/ic_gc_follow_video.svg b/src/assets/svg/ic_gc_follow_video.svg new file mode 100644 index 0000000..cf335bf --- /dev/null +++ b/src/assets/svg/ic_gc_follow_video.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/svg/ic_gc_game_audio_paused.d.svg.ts b/src/assets/svg/ic_gc_game_audio_paused.d.svg.ts new file mode 100644 index 0000000..41afd11 --- /dev/null +++ b/src/assets/svg/ic_gc_game_audio_paused.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_ic_gc_game_audio_paused: string; +export default svg_ic_gc_game_audio_paused; \ No newline at end of file diff --git a/src/assets/svg/ic_gc_game_audio_paused.svg b/src/assets/svg/ic_gc_game_audio_paused.svg new file mode 100644 index 0000000..61f78ff --- /dev/null +++ b/src/assets/svg/ic_gc_game_audio_paused.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/svg/ic_gc_game_audio_play.d.svg.ts b/src/assets/svg/ic_gc_game_audio_play.d.svg.ts new file mode 100644 index 0000000..6f2aa67 --- /dev/null +++ b/src/assets/svg/ic_gc_game_audio_play.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_ic_gc_game_audio_play: string; +export default svg_ic_gc_game_audio_play; \ No newline at end of file diff --git a/src/assets/svg/ic_gc_game_audio_play.svg b/src/assets/svg/ic_gc_game_audio_play.svg new file mode 100644 index 0000000..7797324 --- /dev/null +++ b/src/assets/svg/ic_gc_game_audio_play.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/svg/ic_gc_game_audio_wave.d.svg.ts b/src/assets/svg/ic_gc_game_audio_wave.d.svg.ts new file mode 100644 index 0000000..2fec7ba --- /dev/null +++ b/src/assets/svg/ic_gc_game_audio_wave.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_ic_gc_game_audio_wave: string; +export default svg_ic_gc_game_audio_wave; \ No newline at end of file diff --git a/src/assets/svg/ic_gc_game_audio_wave.svg b/src/assets/svg/ic_gc_game_audio_wave.svg new file mode 100644 index 0000000..696b4aa --- /dev/null +++ b/src/assets/svg/ic_gc_game_audio_wave.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/svg/ic_gc_game_lamp.d.svg.ts b/src/assets/svg/ic_gc_game_lamp.d.svg.ts new file mode 100644 index 0000000..7cc30ec --- /dev/null +++ b/src/assets/svg/ic_gc_game_lamp.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_ic_gc_game_lamp: string; +export default svg_ic_gc_game_lamp; \ No newline at end of file diff --git a/src/assets/svg/ic_gc_game_lamp.svg b/src/assets/svg/ic_gc_game_lamp.svg new file mode 100644 index 0000000..2ee899e --- /dev/null +++ b/src/assets/svg/ic_gc_game_lamp.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/svg/ic_gc_guide_all.d.svg.ts b/src/assets/svg/ic_gc_guide_all.d.svg.ts new file mode 100644 index 0000000..69cd09e --- /dev/null +++ b/src/assets/svg/ic_gc_guide_all.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_ic_gc_guide_all: string; +export default svg_ic_gc_guide_all; \ No newline at end of file diff --git a/src/assets/svg/ic_gc_guide_all.svg b/src/assets/svg/ic_gc_guide_all.svg new file mode 100644 index 0000000..5ff5733 --- /dev/null +++ b/src/assets/svg/ic_gc_guide_all.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/svg/ic_gc_letters_bg.d.svg.ts b/src/assets/svg/ic_gc_letters_bg.d.svg.ts new file mode 100644 index 0000000..6758ed9 --- /dev/null +++ b/src/assets/svg/ic_gc_letters_bg.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_ic_gc_letters_bg: string; +export default svg_ic_gc_letters_bg; \ No newline at end of file diff --git a/src/assets/svg/ic_gc_letters_bg.svg b/src/assets/svg/ic_gc_letters_bg.svg new file mode 100644 index 0000000..0fdd403 --- /dev/null +++ b/src/assets/svg/ic_gc_letters_bg.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/svg/ic_gc_me_biggift.d.svg.ts b/src/assets/svg/ic_gc_me_biggift.d.svg.ts new file mode 100644 index 0000000..2063476 --- /dev/null +++ b/src/assets/svg/ic_gc_me_biggift.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_ic_gc_me_biggift: string; +export default svg_ic_gc_me_biggift; \ No newline at end of file diff --git a/src/assets/svg/ic_gc_me_biggift.svg b/src/assets/svg/ic_gc_me_biggift.svg new file mode 100644 index 0000000..26c6a0d --- /dev/null +++ b/src/assets/svg/ic_gc_me_biggift.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/svg/ic_gc_me_blog.d.svg.ts b/src/assets/svg/ic_gc_me_blog.d.svg.ts new file mode 100644 index 0000000..580130b --- /dev/null +++ b/src/assets/svg/ic_gc_me_blog.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_ic_gc_me_blog: string; +export default svg_ic_gc_me_blog; \ No newline at end of file diff --git a/src/assets/svg/ic_gc_me_blog.svg b/src/assets/svg/ic_gc_me_blog.svg new file mode 100644 index 0000000..e3d7f53 --- /dev/null +++ b/src/assets/svg/ic_gc_me_blog.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/svg/ic_gc_me_bought.d.svg.ts b/src/assets/svg/ic_gc_me_bought.d.svg.ts new file mode 100644 index 0000000..7fcf83e --- /dev/null +++ b/src/assets/svg/ic_gc_me_bought.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_ic_gc_me_bought: string; +export default svg_ic_gc_me_bought; \ No newline at end of file diff --git a/src/assets/svg/ic_gc_me_bought.svg b/src/assets/svg/ic_gc_me_bought.svg new file mode 100644 index 0000000..c15dcbe --- /dev/null +++ b/src/assets/svg/ic_gc_me_bought.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/svg/ic_gc_me_focus.d.svg.ts b/src/assets/svg/ic_gc_me_focus.d.svg.ts new file mode 100644 index 0000000..caa1543 --- /dev/null +++ b/src/assets/svg/ic_gc_me_focus.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_ic_gc_me_focus: string; +export default svg_ic_gc_me_focus; \ No newline at end of file diff --git a/src/assets/svg/ic_gc_me_focus.svg b/src/assets/svg/ic_gc_me_focus.svg new file mode 100644 index 0000000..32b434d --- /dev/null +++ b/src/assets/svg/ic_gc_me_focus.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/svg/ic_gc_me_gifts.d.svg.ts b/src/assets/svg/ic_gc_me_gifts.d.svg.ts new file mode 100644 index 0000000..5b7c19a --- /dev/null +++ b/src/assets/svg/ic_gc_me_gifts.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_ic_gc_me_gifts: string; +export default svg_ic_gc_me_gifts; \ No newline at end of file diff --git a/src/assets/svg/ic_gc_me_gifts.svg b/src/assets/svg/ic_gc_me_gifts.svg new file mode 100644 index 0000000..86a7a8b --- /dev/null +++ b/src/assets/svg/ic_gc_me_gifts.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/svg/ic_gc_me_mycomment.d.svg.ts b/src/assets/svg/ic_gc_me_mycomment.d.svg.ts new file mode 100644 index 0000000..4ea3c10 --- /dev/null +++ b/src/assets/svg/ic_gc_me_mycomment.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_ic_gc_me_mycomment: string; +export default svg_ic_gc_me_mycomment; \ No newline at end of file diff --git a/src/assets/svg/ic_gc_me_mycomment.svg b/src/assets/svg/ic_gc_me_mycomment.svg new file mode 100644 index 0000000..ab34b23 --- /dev/null +++ b/src/assets/svg/ic_gc_me_mycomment.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/svg/ic_gc_me_ordered.d.svg.ts b/src/assets/svg/ic_gc_me_ordered.d.svg.ts new file mode 100644 index 0000000..2e112d5 --- /dev/null +++ b/src/assets/svg/ic_gc_me_ordered.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_ic_gc_me_ordered: string; +export default svg_ic_gc_me_ordered; \ No newline at end of file diff --git a/src/assets/svg/ic_gc_me_ordered.svg b/src/assets/svg/ic_gc_me_ordered.svg new file mode 100644 index 0000000..848a0a5 --- /dev/null +++ b/src/assets/svg/ic_gc_me_ordered.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/svg/ic_gc_me_played.d.svg.ts b/src/assets/svg/ic_gc_me_played.d.svg.ts new file mode 100644 index 0000000..55ca682 --- /dev/null +++ b/src/assets/svg/ic_gc_me_played.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_ic_gc_me_played: string; +export default svg_ic_gc_me_played; \ No newline at end of file diff --git a/src/assets/svg/ic_gc_me_played.svg b/src/assets/svg/ic_gc_me_played.svg new file mode 100644 index 0000000..8612414 --- /dev/null +++ b/src/assets/svg/ic_gc_me_played.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/svg/ic_gc_me_services.d.svg.ts b/src/assets/svg/ic_gc_me_services.d.svg.ts new file mode 100644 index 0000000..1d7d967 --- /dev/null +++ b/src/assets/svg/ic_gc_me_services.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_ic_gc_me_services: string; +export default svg_ic_gc_me_services; \ No newline at end of file diff --git a/src/assets/svg/ic_gc_me_services.svg b/src/assets/svg/ic_gc_me_services.svg new file mode 100644 index 0000000..ba7b55d --- /dev/null +++ b/src/assets/svg/ic_gc_me_services.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/svg/ic_gc_me_setup.d.svg.ts b/src/assets/svg/ic_gc_me_setup.d.svg.ts new file mode 100644 index 0000000..6cdfe2d --- /dev/null +++ b/src/assets/svg/ic_gc_me_setup.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_ic_gc_me_setup: string; +export default svg_ic_gc_me_setup; \ No newline at end of file diff --git a/src/assets/svg/ic_gc_me_setup.svg b/src/assets/svg/ic_gc_me_setup.svg new file mode 100644 index 0000000..32827d9 --- /dev/null +++ b/src/assets/svg/ic_gc_me_setup.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/svg/ic_gc_me_suggestion.d.svg.ts b/src/assets/svg/ic_gc_me_suggestion.d.svg.ts new file mode 100644 index 0000000..41871d6 --- /dev/null +++ b/src/assets/svg/ic_gc_me_suggestion.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_ic_gc_me_suggestion: string; +export default svg_ic_gc_me_suggestion; \ No newline at end of file diff --git a/src/assets/svg/ic_gc_me_suggestion.svg b/src/assets/svg/ic_gc_me_suggestion.svg new file mode 100644 index 0000000..4461efa --- /dev/null +++ b/src/assets/svg/ic_gc_me_suggestion.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/svg/ic_gc_me_teenagers.d.svg.ts b/src/assets/svg/ic_gc_me_teenagers.d.svg.ts new file mode 100644 index 0000000..70f1b66 --- /dev/null +++ b/src/assets/svg/ic_gc_me_teenagers.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_ic_gc_me_teenagers: string; +export default svg_ic_gc_me_teenagers; \ No newline at end of file diff --git a/src/assets/svg/ic_gc_me_teenagers.svg b/src/assets/svg/ic_gc_me_teenagers.svg new file mode 100644 index 0000000..2ac9e26 --- /dev/null +++ b/src/assets/svg/ic_gc_me_teenagers.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/svg/ic_gc_me_updata.d.svg.ts b/src/assets/svg/ic_gc_me_updata.d.svg.ts new file mode 100644 index 0000000..7525a78 --- /dev/null +++ b/src/assets/svg/ic_gc_me_updata.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_ic_gc_me_updata: string; +export default svg_ic_gc_me_updata; \ No newline at end of file diff --git a/src/assets/svg/ic_gc_me_updata.svg b/src/assets/svg/ic_gc_me_updata.svg new file mode 100644 index 0000000..d70bd13 --- /dev/null +++ b/src/assets/svg/ic_gc_me_updata.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/svg/ic_gc_message.d.svg.ts b/src/assets/svg/ic_gc_message.d.svg.ts new file mode 100644 index 0000000..fb3c828 --- /dev/null +++ b/src/assets/svg/ic_gc_message.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_ic_gc_message: string; +export default svg_ic_gc_message; \ No newline at end of file diff --git a/src/assets/svg/ic_gc_message.svg b/src/assets/svg/ic_gc_message.svg new file mode 100644 index 0000000..f520523 --- /dev/null +++ b/src/assets/svg/ic_gc_message.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/svg/ic_gc_quote01.d.svg.ts b/src/assets/svg/ic_gc_quote01.d.svg.ts new file mode 100644 index 0000000..29fe587 --- /dev/null +++ b/src/assets/svg/ic_gc_quote01.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_ic_gc_quote01: string; +export default svg_ic_gc_quote01; \ No newline at end of file diff --git a/src/assets/svg/ic_gc_quote01.svg b/src/assets/svg/ic_gc_quote01.svg new file mode 100644 index 0000000..9886489 --- /dev/null +++ b/src/assets/svg/ic_gc_quote01.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/svg/ic_gc_search_clear.d.svg.ts b/src/assets/svg/ic_gc_search_clear.d.svg.ts new file mode 100644 index 0000000..05bbe44 --- /dev/null +++ b/src/assets/svg/ic_gc_search_clear.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_ic_gc_search_clear: string; +export default svg_ic_gc_search_clear; \ No newline at end of file diff --git a/src/assets/svg/ic_gc_search_clear.svg b/src/assets/svg/ic_gc_search_clear.svg new file mode 100644 index 0000000..66ceb47 --- /dev/null +++ b/src/assets/svg/ic_gc_search_clear.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/svg/ic_gc_star_full.d.svg.ts b/src/assets/svg/ic_gc_star_full.d.svg.ts new file mode 100644 index 0000000..18dabad --- /dev/null +++ b/src/assets/svg/ic_gc_star_full.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_ic_gc_star_full: string; +export default svg_ic_gc_star_full; \ No newline at end of file diff --git a/src/assets/svg/ic_gc_star_full.svg b/src/assets/svg/ic_gc_star_full.svg new file mode 100644 index 0000000..659fa3d --- /dev/null +++ b/src/assets/svg/ic_gc_star_full.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/svg/ic_partition_broadca.d.svg.ts b/src/assets/svg/ic_partition_broadca.d.svg.ts new file mode 100644 index 0000000..322cbd4 --- /dev/null +++ b/src/assets/svg/ic_partition_broadca.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_ic_partition_broadca: string; +export default svg_ic_partition_broadca; \ No newline at end of file diff --git a/src/player/assets/svg/broadcast.svg b/src/assets/svg/ic_partition_broadca.svg similarity index 100% rename from src/player/assets/svg/broadcast.svg rename to src/assets/svg/ic_partition_broadca.svg diff --git a/src/assets/svg/ico_backstage.d.svg.ts b/src/assets/svg/ico_backstage.d.svg.ts new file mode 100644 index 0000000..e96b1b4 --- /dev/null +++ b/src/assets/svg/ico_backstage.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_ico_backstage: string; +export default svg_ico_backstage; \ No newline at end of file diff --git a/src/player/assets/svg/icon-backstage.svg b/src/assets/svg/ico_backstage.svg similarity index 100% rename from src/player/assets/svg/icon-backstage.svg rename to src/assets/svg/ico_backstage.svg diff --git a/src/assets/svg/ico_collection_ful.d.svg.ts b/src/assets/svg/ico_collection_ful.d.svg.ts new file mode 100644 index 0000000..3207486 --- /dev/null +++ b/src/assets/svg/ico_collection_ful.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_ico_collection_ful: string; +export default svg_ico_collection_ful; \ No newline at end of file diff --git a/src/player/assets/svg/icon-collection-full.svg b/src/assets/svg/ico_collection_ful.svg similarity index 100% rename from src/player/assets/svg/icon-collection-full.svg rename to src/assets/svg/ico_collection_ful.svg diff --git a/src/assets/svg/ico_collection_lin.d.svg.ts b/src/assets/svg/ico_collection_lin.d.svg.ts new file mode 100644 index 0000000..5ef33bb --- /dev/null +++ b/src/assets/svg/ico_collection_lin.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_ico_collection_lin: string; +export default svg_ico_collection_lin; \ No newline at end of file diff --git a/src/player/assets/svg/icon-collection.svg b/src/assets/svg/ico_collection_lin.svg similarity index 100% rename from src/player/assets/svg/icon-collection.svg rename to src/assets/svg/ico_collection_lin.svg diff --git a/src/assets/svg/ico_del.d.svg.ts b/src/assets/svg/ico_del.d.svg.ts new file mode 100644 index 0000000..81edf9c --- /dev/null +++ b/src/assets/svg/ico_del.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_ico_del: string; +export default svg_ico_del; \ No newline at end of file diff --git a/src/player/assets/svg/icon-delete.svg b/src/assets/svg/ico_del.svg similarity index 100% rename from src/player/assets/svg/icon-delete.svg rename to src/assets/svg/ico_del.svg diff --git a/src/assets/svg/ico_ignore.d.svg.ts b/src/assets/svg/ico_ignore.d.svg.ts new file mode 100644 index 0000000..f296295 --- /dev/null +++ b/src/assets/svg/ico_ignore.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_ico_ignore: string; +export default svg_ico_ignore; \ No newline at end of file diff --git a/src/player/assets/svg/icon-ignore.svg b/src/assets/svg/ico_ignore.svg similarity index 100% rename from src/player/assets/svg/icon-ignore.svg rename to src/assets/svg/ico_ignore.svg diff --git a/src/assets/svg/ico_move.d.svg.ts b/src/assets/svg/ico_move.d.svg.ts new file mode 100644 index 0000000..5864858 --- /dev/null +++ b/src/assets/svg/ico_move.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_ico_move: string; +export default svg_ico_move; \ No newline at end of file diff --git a/src/player/assets/svg/icon-move.svg b/src/assets/svg/ico_move.svg similarity index 100% rename from src/player/assets/svg/icon-move.svg rename to src/assets/svg/ico_move.svg diff --git a/src/assets/svg/ico_protect.d.svg.ts b/src/assets/svg/ico_protect.d.svg.ts new file mode 100644 index 0000000..f4cfbf2 --- /dev/null +++ b/src/assets/svg/ico_protect.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_ico_protect: string; +export default svg_ico_protect; \ No newline at end of file diff --git a/src/player/assets/svg/icon-protect.svg b/src/assets/svg/ico_protect.svg similarity index 100% rename from src/player/assets/svg/icon-protect.svg rename to src/assets/svg/ico_protect.svg diff --git a/src/player/assets/svg/kichiku.d.svg.ts b/src/assets/svg/kichiku.d.svg.ts similarity index 100% rename from src/player/assets/svg/kichiku.d.svg.ts rename to src/assets/svg/kichiku.d.svg.ts diff --git a/src/player/assets/svg/kichiku.svg b/src/assets/svg/kichiku.svg similarity index 100% rename from src/player/assets/svg/kichiku.svg rename to src/assets/svg/kichiku.svg diff --git a/src/player/assets/svg/life.d.svg.ts b/src/assets/svg/life.d.svg.ts similarity index 100% rename from src/player/assets/svg/life.d.svg.ts rename to src/assets/svg/life.d.svg.ts diff --git a/src/player/assets/svg/life.svg b/src/assets/svg/life.svg similarity index 100% rename from src/player/assets/svg/life.svg rename to src/assets/svg/life.svg diff --git a/src/player/assets/svg/light.d.svg.ts b/src/assets/svg/light.d.svg.ts similarity index 100% rename from src/player/assets/svg/light.d.svg.ts rename to src/assets/svg/light.d.svg.ts diff --git a/src/player/assets/svg/light.svg b/src/assets/svg/light.svg similarity index 100% rename from src/player/assets/svg/light.svg rename to src/assets/svg/light.svg diff --git a/src/player/assets/svg/live.d.svg.ts b/src/assets/svg/live.d.svg.ts similarity index 100% rename from src/player/assets/svg/live.d.svg.ts rename to src/assets/svg/live.d.svg.ts diff --git a/src/player/assets/svg/live.svg b/src/assets/svg/live.svg similarity index 100% rename from src/player/assets/svg/live.svg rename to src/assets/svg/live.svg diff --git a/src/assets/svg/match.d.svg.ts b/src/assets/svg/match.d.svg.ts new file mode 100644 index 0000000..898862b --- /dev/null +++ b/src/assets/svg/match.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_match: string; +export default svg_match; \ No newline at end of file diff --git a/src/assets/svg/match.svg b/src/assets/svg/match.svg new file mode 100644 index 0000000..e36ef9e --- /dev/null +++ b/src/assets/svg/match.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/player/assets/svg/music.d.svg.ts b/src/assets/svg/music.d.svg.ts similarity index 100% rename from src/player/assets/svg/music.d.svg.ts rename to src/assets/svg/music.d.svg.ts diff --git a/src/player/assets/svg/music.svg b/src/assets/svg/music.svg similarity index 100% rename from src/player/assets/svg/music.svg rename to src/assets/svg/music.svg diff --git a/src/player/assets/svg/play-state.d.svg.ts b/src/assets/svg/play-state.d.svg.ts similarity index 100% rename from src/player/assets/svg/play-state.d.svg.ts rename to src/assets/svg/play-state.d.svg.ts diff --git a/src/player/assets/svg/play-state.svg b/src/assets/svg/play-state.svg similarity index 100% rename from src/player/assets/svg/play-state.svg rename to src/assets/svg/play-state.svg diff --git a/src/assets/svg/q.d.svg.ts b/src/assets/svg/q.d.svg.ts new file mode 100644 index 0000000..45223ee --- /dev/null +++ b/src/assets/svg/q.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_q: string; +export default svg_q; \ No newline at end of file diff --git a/src/player/assets/svg/question.svg b/src/assets/svg/q.svg similarity index 100% rename from src/player/assets/svg/question.svg rename to src/assets/svg/q.svg diff --git a/src/assets/svg/radio_default.d.svg.ts b/src/assets/svg/radio_default.d.svg.ts new file mode 100644 index 0000000..5c2579d --- /dev/null +++ b/src/assets/svg/radio_default.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_radio_default: string; +export default svg_radio_default; \ No newline at end of file diff --git a/src/player/assets/svg/radio.svg b/src/assets/svg/radio_default.svg similarity index 100% rename from src/player/assets/svg/radio.svg rename to src/assets/svg/radio_default.svg diff --git a/src/player/assets/svg/radio-selected.d.svg.ts b/src/assets/svg/radio_selected.d.svg.ts similarity index 100% rename from src/player/assets/svg/radio-selected.d.svg.ts rename to src/assets/svg/radio_selected.d.svg.ts diff --git a/src/player/assets/svg/radio-selected.svg b/src/assets/svg/radio_selected.svg similarity index 100% rename from src/player/assets/svg/radio-selected.svg rename to src/assets/svg/radio_selected.svg diff --git a/src/player/assets/svg/read.d.svg.ts b/src/assets/svg/read.d.svg.ts similarity index 100% rename from src/player/assets/svg/read.d.svg.ts rename to src/assets/svg/read.d.svg.ts diff --git a/src/player/assets/svg/read.svg b/src/assets/svg/read.svg similarity index 100% rename from src/player/assets/svg/read.svg rename to src/assets/svg/read.svg diff --git a/src/player/assets/svg/repeat-on.d.svg.ts b/src/assets/svg/repeat_on.d.svg.ts similarity index 100% rename from src/player/assets/svg/repeat-on.d.svg.ts rename to src/assets/svg/repeat_on.d.svg.ts diff --git a/src/assets/svg/repeat_on.svg b/src/assets/svg/repeat_on.svg new file mode 100644 index 0000000..2af6cee --- /dev/null +++ b/src/assets/svg/repeat_on.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/svg/shanchu.d.svg.ts b/src/assets/svg/shanchu.d.svg.ts new file mode 100644 index 0000000..2ff29dc --- /dev/null +++ b/src/assets/svg/shanchu.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_shanchu: string; +export default svg_shanchu; \ No newline at end of file diff --git a/src/player/assets/svg/remove.svg b/src/assets/svg/shanchu.svg similarity index 100% rename from src/player/assets/svg/remove.svg rename to src/assets/svg/shanchu.svg diff --git a/src/assets/svg/shunxubofang.d.svg.ts b/src/assets/svg/shunxubofang.d.svg.ts new file mode 100644 index 0000000..6ee7799 --- /dev/null +++ b/src/assets/svg/shunxubofang.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_shunxubofang: string; +export default svg_shunxubofang; \ No newline at end of file diff --git a/src/player/assets/svg/sequence-inline.svg b/src/assets/svg/shunxubofang.svg similarity index 100% rename from src/player/assets/svg/sequence-inline.svg rename to src/assets/svg/shunxubofang.svg diff --git a/src/assets/svg/shunxubofang1.d.svg.ts b/src/assets/svg/shunxubofang1.d.svg.ts new file mode 100644 index 0000000..a00d99a --- /dev/null +++ b/src/assets/svg/shunxubofang1.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_shunxubofang1: string; +export default svg_shunxubofang1; \ No newline at end of file diff --git a/src/player/assets/svg/sequence-block.svg b/src/assets/svg/shunxubofang1.svg similarity index 100% rename from src/player/assets/svg/sequence-block.svg rename to src/assets/svg/shunxubofang1.svg diff --git a/src/assets/svg/suijibofang.d.svg.ts b/src/assets/svg/suijibofang.d.svg.ts new file mode 100644 index 0000000..b77f43e --- /dev/null +++ b/src/assets/svg/suijibofang.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_suijibofang: string; +export default svg_suijibofang; \ No newline at end of file diff --git a/src/player/assets/svg/random.svg b/src/assets/svg/suijibofang.svg similarity index 100% rename from src/player/assets/svg/random.svg rename to src/assets/svg/suijibofang.svg diff --git a/src/player/assets/svg/technology.d.svg.ts b/src/assets/svg/technology.d.svg.ts similarity index 100% rename from src/player/assets/svg/technology.d.svg.ts rename to src/assets/svg/technology.d.svg.ts diff --git a/src/player/assets/svg/technology.svg b/src/assets/svg/technology.svg similarity index 100% rename from src/player/assets/svg/technology.svg rename to src/assets/svg/technology.svg diff --git a/src/assets/svg/titlel.d.svg.ts b/src/assets/svg/titlel.d.svg.ts new file mode 100644 index 0000000..b056002 --- /dev/null +++ b/src/assets/svg/titlel.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_titlel: string; +export default svg_titlel; \ No newline at end of file diff --git a/src/player/assets/svg/title-left.svg b/src/assets/svg/titlel.svg similarity index 100% rename from src/player/assets/svg/title-left.svg rename to src/assets/svg/titlel.svg diff --git a/src/assets/svg/titler.d.svg.ts b/src/assets/svg/titler.d.svg.ts new file mode 100644 index 0000000..00a1382 --- /dev/null +++ b/src/assets/svg/titler.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_titler: string; +export default svg_titler; \ No newline at end of file diff --git a/src/player/assets/svg/title-right.svg b/src/assets/svg/titler.svg similarity index 100% rename from src/player/assets/svg/title-right.svg rename to src/assets/svg/titler.svg diff --git a/src/player/assets/svg/topic.d.svg.ts b/src/assets/svg/topic.d.svg.ts similarity index 100% rename from src/player/assets/svg/topic.d.svg.ts rename to src/assets/svg/topic.d.svg.ts diff --git a/src/player/assets/svg/topic.svg b/src/assets/svg/topic.svg similarity index 100% rename from src/player/assets/svg/topic.svg rename to src/assets/svg/topic.svg diff --git a/src/assets/svg/tuoyuan11.d.svg.ts b/src/assets/svg/tuoyuan11.d.svg.ts new file mode 100644 index 0000000..975d17b --- /dev/null +++ b/src/assets/svg/tuoyuan11.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_tuoyuan11: string; +export default svg_tuoyuan11; \ No newline at end of file diff --git a/src/player/assets/svg/circle.svg b/src/assets/svg/tuoyuan11.svg similarity index 100% rename from src/player/assets/svg/circle.svg rename to src/assets/svg/tuoyuan11.svg diff --git a/src/assets/svg/txks.d.svg.ts b/src/assets/svg/txks.d.svg.ts new file mode 100644 index 0000000..c6683f4 --- /dev/null +++ b/src/assets/svg/txks.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_txks: string; +export default svg_txks; \ No newline at end of file diff --git a/src/player/assets/svg/chat-on.svg b/src/assets/svg/txks.svg similarity index 100% rename from src/player/assets/svg/chat-on.svg rename to src/assets/svg/txks.svg diff --git a/src/player/assets/svg/wait-choice.d.svg.ts b/src/assets/svg/wait-choice.d.svg.ts similarity index 100% rename from src/player/assets/svg/wait-choice.d.svg.ts rename to src/assets/svg/wait-choice.d.svg.ts diff --git a/src/player/assets/svg/wait-choice.svg b/src/assets/svg/wait-choice.svg similarity index 100% rename from src/player/assets/svg/wait-choice.svg rename to src/assets/svg/wait-choice.svg diff --git a/src/player/assets/svg/wait-normal.d.svg.ts b/src/assets/svg/wait-normal.d.svg.ts similarity index 100% rename from src/player/assets/svg/wait-normal.d.svg.ts rename to src/assets/svg/wait-normal.d.svg.ts diff --git a/src/player/assets/svg/wait-normal.svg b/src/assets/svg/wait-normal.svg similarity index 100% rename from src/player/assets/svg/wait-normal.svg rename to src/assets/svg/wait-normal.svg diff --git a/src/player/assets/svg/x.d.svg.ts b/src/assets/svg/x.d.svg.ts similarity index 100% rename from src/player/assets/svg/x.d.svg.ts rename to src/assets/svg/x.d.svg.ts diff --git a/src/player/assets/svg/x.svg b/src/assets/svg/x.svg similarity index 100% rename from src/player/assets/svg/x.svg rename to src/assets/svg/x.svg diff --git a/src/assets/svg/xiayiji.d.svg.ts b/src/assets/svg/xiayiji.d.svg.ts new file mode 100644 index 0000000..bc77be9 --- /dev/null +++ b/src/assets/svg/xiayiji.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_xiayiji: string; +export default svg_xiayiji; \ No newline at end of file diff --git a/src/player/assets/svg/next.svg b/src/assets/svg/xiayiji.svg similarity index 100% rename from src/player/assets/svg/next.svg rename to src/assets/svg/xiayiji.svg diff --git a/src/assets/svg/xuweixiaodianshitouxiang.d.svg.ts b/src/assets/svg/xuweixiaodianshitouxiang.d.svg.ts new file mode 100644 index 0000000..6d01e78 --- /dev/null +++ b/src/assets/svg/xuweixiaodianshitouxiang.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_xuweixiaodianshitouxiang: string; +export default svg_xuweixiaodianshitouxiang; \ No newline at end of file diff --git a/src/player/assets/svg/tv-head.svg b/src/assets/svg/xuweixiaodianshitouxiang.svg similarity index 100% rename from src/player/assets/svg/tv-head.svg rename to src/assets/svg/xuweixiaodianshitouxiang.svg diff --git a/src/assets/svg/yinpin.d.svg.ts b/src/assets/svg/yinpin.d.svg.ts new file mode 100644 index 0000000..228ccfd --- /dev/null +++ b/src/assets/svg/yinpin.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_yinpin: string; +export default svg_yinpin; \ No newline at end of file diff --git a/src/assets/svg/yinpin.svg b/src/assets/svg/yinpin.svg new file mode 100644 index 0000000..804bb90 --- /dev/null +++ b/src/assets/svg/yinpin.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/svg/yizhuifan.d.svg.ts b/src/assets/svg/yizhuifan.d.svg.ts new file mode 100644 index 0000000..c1072c8 --- /dev/null +++ b/src/assets/svg/yizhuifan.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_yizhuifan: string; +export default svg_yizhuifan; \ No newline at end of file diff --git a/src/player/assets/svg/love-solid.svg b/src/assets/svg/yizhuifan.svg similarity index 100% rename from src/player/assets/svg/love-solid.svg rename to src/assets/svg/yizhuifan.svg diff --git a/src/assets/svg/zhuifanshu.d.svg.ts b/src/assets/svg/zhuifanshu.d.svg.ts new file mode 100644 index 0000000..e346926 --- /dev/null +++ b/src/assets/svg/zhuifanshu.d.svg.ts @@ -0,0 +1,2 @@ +declare const svg_zhuifanshu: string; +export default svg_zhuifanshu; \ No newline at end of file diff --git a/src/player/assets/svg/heart.svg b/src/assets/svg/zhuifanshu.svg similarity index 100% rename from src/player/assets/svg/heart.svg rename to src/assets/svg/zhuifanshu.svg diff --git a/src/build/README.md b/src/build/README.md new file mode 100644 index 0000000..af74925 --- /dev/null +++ b/src/build/README.md @@ -0,0 +1 @@ +开发脚本目录 \ No newline at end of file diff --git a/scripts/arbitrary-extensions.mjs b/src/build/arbitrary-extensions.ts similarity index 89% rename from scripts/arbitrary-extensions.mjs rename to src/build/arbitrary-extensions.ts index 06908ef..e28721a 100644 --- a/scripts/arbitrary-extensions.mjs +++ b/src/build/arbitrary-extensions.ts @@ -1,13 +1,11 @@ -/** - * @file 生成非代码文件的定义文件 - */ import fs from 'fs/promises'; /** 非代码文件所在路径 */ const paths = [ - 'src/player/assets/svg', + 'src/assets/svg', ]; +// 为非代码文件生成声明文件 for (const path of paths) { fs.readdir(path).then(d => { d.forEach(d => { diff --git a/src/build/esbuild.ts b/src/build/esbuild.ts new file mode 100644 index 0000000..18b3b87 --- /dev/null +++ b/src/build/esbuild.ts @@ -0,0 +1,57 @@ +import { build } from "esbuild"; +import type { PluginBuild } from "esbuild"; + +const cssConstructStylesheetPlugin = { + name: 'css imports', + setup(pluginBuild: PluginBuild) { + pluginBuild.onLoad({ filter: /\.css$/ }, async args => { + if (args.with.type === 'css') { + // 编译 CSS 样式文件 + const result = await build({ + bundle: true, + entryPoints: [args.path], + minify: pluginBuild.initialOptions.minify, + charset: 'utf8', + loader: { + '.png': 'base64', + '.gif': 'base64', + '.svg': 'base64', + }, + write: false, + }); + const contents = ` + const styles = new CSSStyleSheet(); + styles.replaceSync(\`${result.outputFiles[0].text}\`); + export default styles;`; + return { contents, loader: 'js' }; + } + }); + } +} + +// 编译资源文件 +await build({ + entryPoints: [ + 'src/sidebar/index.ts', + 'src/options/index.ts', + 'src/main/index.ts', + ], + outdir: 'dist', + outbase: "src", + bundle: true, + minify: true, + format: 'iife', + sourcemap: true, + treeShaking: true, + charset: 'utf8', + supported: { + decorators: false, // 装饰器暂未得到任何浏览器支持 + }, + plugins: [cssConstructStylesheetPlugin], + define: { + __TOTP_KEY__: `'${crypto.randomUUID()}'`, // 基于哈希消息认证码的一次性口令的密钥 + }, + loader: { + '.svg': 'text', + }, +}); \ No newline at end of file diff --git a/src/build/index.ts b/src/build/index.ts new file mode 100644 index 0000000..2d564b0 --- /dev/null +++ b/src/build/index.ts @@ -0,0 +1,14 @@ +import { copy, emptyDir } from "fs-extra"; + +// 清空目标文件夹 +await emptyDir("./dist"); + +// 拷贝资源文件 +await Promise.all([ + import('./esbuild.ts'), + import('./manifest.ts'), + copy('./src/_locales', './dist/_locales'), + copy('./src/assets', './dist/assets'), + copy('./src/sidebar', './dist/sidebar', { filter: e => e.endsWith('.ts') || e.endsWith('.css') ? false : true }), + copy('./src/options', './dist/options', { filter: e => e.endsWith('.ts') || e.endsWith('.css') ? false : true }), +]); \ No newline at end of file diff --git a/src/build/manifest.ts b/src/build/manifest.ts new file mode 100644 index 0000000..9508569 --- /dev/null +++ b/src/build/manifest.ts @@ -0,0 +1,7 @@ +import pkg from 'fs-extra'; +import manifest from "../manifest.json" with {type: 'json'} + +Reflect.deleteProperty(manifest, '$schema'); // 移除 $schema 键 + +const { writeJson } = pkg; +await writeJson('./dist/manifest.json', manifest, { spaces: '\t' }); \ No newline at end of file diff --git a/src/danmaku/README.md b/src/danmaku/README.md deleted file mode 100644 index 8228f46..0000000 --- a/src/danmaku/README.md +++ /dev/null @@ -1,35 +0,0 @@ -弹幕组件,代码弹幕基于B站[flash播放器](https://static.hdslb.com/play.swf)移植而来: - - 支持普权弹幕使用【换行符】实现的高级排版效果,并基于屏幕分辨率等比放大 - - 支持代码弹幕【mode8】(当然完全复刻flash时代的效果是不现实的,另外弹幕游戏等与用户交互的内容也不支持) - - -### 弹幕模式 -| mode | 1 | 4 | 5 | 6 | 7 | 8 | 9 | -| :-:| :-: | :-: | :-: | :-: | :-: | :-: | :-: | -| 类型 | 普通 | 底部 | 顶部 | 逆向 | 高级 | 代码 | BAS | - -### 测试用例 -| 视频链接 | 弹幕类型 | 备注 | -| - | - | - | -| [【幸运星组曲】「らき☆すた動画」](https://www.bilibili.com/video/av2810) | 普权弹幕 | 普权弹幕使用【换行符】实现的高级排版效果 | -| [【初音ミク】夢と葉桜【PV】](https://www.bilibili.com/video/av297197) | 高级弹幕 | mode7 时代代表作(原始弹幕池丢失严重,请下载[备份弹幕](test/av297197.xml)文件作为【本地弹幕】加载欣赏完全体效果) | -| [【黑屏弹幕】旅(完成)](https://www.bilibili.com/video/av2735163) | 代码弹幕 | mode8 时代末期完美适应屏幕分辨率的集大成者 | -| [【黑屏弹幕】在这狭小的鸟笼之中【SoundHorizon】](https://www.bilibili.com/video/av589840724) | BAS弹幕 | mode9 时代代表作 | -| [\[游客权限弹幕\]キミとふたり-ClariS](https://www.bilibili.com/video/av75432) | 普权弹幕 | | -| [\[黑屏普权弹幕\]白雪公主与变态王子的故事\[SH\]](https://www.bilibili.com/video/av189960) | 普权弹幕 | | -| [【弹幕祭应援】 緋色月下、狂咲ノ絶-1st Anniversary Remix](https://www.bilibili.com/video/av39444/?p=3) | 普权弹幕 | 普权弹幕压力测试(原始弹幕池丢失严重,请下载[备份弹幕](test/av39444.xml)文件作为【本地弹幕】加载欣赏完全体效果) | -| [【白屏弹幕】10 years after](https://www.bilibili.com/video/av202813) | 高级弹幕 | | -| [【弹幕PV】里表一体](https://www.bilibili.com/video/av201763) | 代码弹幕 | 揭开 mode8 时代的先驱者之一 | -| [【BILIBILI合作】2012夜神月圣诞祭](https://www.bilibili.com/video/av222938/) | 代码弹幕 | mode8 漏洞和死亡笔记捏他的完美契合 | -| [\[例大祭⑨応援\]lonely dreaming girl\[敏敏翻唱\]](https://www.bilibili.com/video/av280613) | 代码弹幕 | 惊艳的 mode8 绘图,当然 mode7 部分同样优秀 | -| [\[高级弹幕\]Rolling Girl](https://www.bilibili.com/video/av379138) | 代码弹幕 | | -| [【弹幕】Crazy ∞ nighT](https://www.bilibili.com/video/av392859) | 代码弹幕 | | -| [【黑屏弹幕】TSUBASA(海猫鸣泣之时)](https://www.bilibili.com/video/av393948/?p=2) | 代码弹幕 | | -| [【黑屏弹幕】我们的报复政策](https://www.bilibili.com/video/av397395) | 代码弹幕 | | -| [\[弹幕大赛\]Q&A リサイタル! ~TV ver~](https://www.bilibili.com/video/av399127) | 代码弹幕 | | -| [Psy Phone short ver](https://www.bilibili.com/video/av402034) | 代码弹幕 | | -| [【黑屏弹幕】魔法少女小圆OP-完整版](https://www.bilibili.com/video/av409835) | 代码弹幕 | mode8 制作的魔法少女小圆 OP ,当之无愧的神弹幕 | -| [【弹幕】乾杯 - ( ゜- ゜)つロ \| 代码弹幕](https://www.bilibili.com/video/av411358) | 代码弹幕 | 时代的眼泪音乐绘卷 | -| [【佐藤莎莎拉】不断被替换的渺小存在](https://www.bilibili.com/video/av606355) | 代码弹幕 | mode8 一图流 PV | -| [【CLANNAD】谢谢 一直以来都最喜欢你~](https://www.bilibili.com/video/av856822) | 代码弹幕 | 普权弹幕 + mode7 + mode8 + mode9 群魔乱舞,非官方的高级弹幕试验场 | -| [【黑屏弹幕】人造ENE](https://www.bilibili.com/video/av980264) | 代码弹幕 | | \ No newline at end of file diff --git a/src/danmaku/event.ts b/src/danmaku/event.ts new file mode 100644 index 0000000..f8e5c99 --- /dev/null +++ b/src/danmaku/event.ts @@ -0,0 +1,18 @@ +import { IDanmaku } from "."; + +/** 弹幕事件 */ +export enum DanmakuEvent { + + /** 添加弹幕,注意参数为新增弹幕 */ + DANMAKU_ADD, + + /** 发送弹幕 */ + DANMAKU_SEND, +} + + +/** 弹幕事件对应的数据类型 */ +export interface IDanmakuEvent { + 0: IDanmaku[]; + 1: IDanmaku; +} \ No newline at end of file diff --git a/src/danmaku/index.css b/src/danmaku/index.css new file mode 100644 index 0000000..21e644e --- /dev/null +++ b/src/danmaku/index.css @@ -0,0 +1,61 @@ +@import url(./render/index.css); + +@scope { + :scope { + color-scheme: light dark; + position: absolute; + margin: 0; + padding: 0; + border: 0; + inset: 0; + container: danmaku / size; + pointer-events: none; + overflow: clip; + + /* 字号缩放倍率 */ + --fontSize: 1; + /* 弹幕字体大小 */ + --fontsize: 25; + /* 是否等比放大 */ + --full-screen-sync: 0; + /* 边框样式 */ + --fontBorder: 0; + /* 弹幕阴影颜色 */ + --text-shadow: #000; + /* 渐变弹幕 */ + --colorful: 1; + /* 弹幕字体 */ + --font-family: SimHei, "Microsoft JhengHei"; + /* 弹幕字重 */ + --font-weight: bold; + /* 弹幕透明度 */ + --opacity: 1; + + /* 弹幕动画状态 */ + --animation-play-state: running; + /* 弹幕容器分辨率修正·行向 */ + --inset-inline: 0; + /* 弹幕容器分辨率修正·块向 */ + --inset-block: 0; + } + + div { + box-sizing: border-box; + margin: 0; + padding: 0; + border: 0; + user-select: none; + } + + button { + display: flex; + justify-content: center; + align-items: center; + color: inherit; + background-color: transparent; + padding: 0; + border: none; + cursor: pointer; + } + +} \ No newline at end of file diff --git a/src/danmaku/index.d.css.ts b/src/danmaku/index.d.css.ts new file mode 100644 index 0000000..eb9bc4f --- /dev/null +++ b/src/danmaku/index.d.css.ts @@ -0,0 +1,2 @@ +declare const stylesheet: CSSStyleSheet; +export default stylesheet; \ No newline at end of file diff --git a/src/danmaku/index.ts b/src/danmaku/index.ts index 39cb8e4..dd9b6f0 100644 --- a/src/danmaku/index.ts +++ b/src/danmaku/index.ts @@ -1,5 +1,8 @@ +import { CSSStyleSheet2HTMLStyleElement } from "../utils/CSSStyleSheet2HTMLStyleElement"; import { customElement } from "../utils/Decorator/customElement"; import { DANMAKU } from "./block"; +import { DanmakuEvent, IDanmakuEvent } from "./event"; +import stylesheet from "./index.css" with {type: 'css'}; import { Mode1 } from "./render/mode1"; import { Mode4 } from "./render/mode4"; import { Mode5 } from "./render/mode5"; @@ -7,15 +10,12 @@ import { Mode6 } from "./render/mode6"; import { Mode7 } from "./render/mode7"; import { Mode8 } from "./render/mode8"; import { rootSprite } from "./render/mode8/Display/DisplayObject"; -import { Mode9 } from "./render/mode9"; +import { IDanmakuBAS, Mode9 } from "./render/mode9"; import ParserWorker from './render/mode9/bas-parser'; -/** - * 弹幕组件 - * 需要引入`/style/index.css` - */ -@customElement('div') -export class Danmaku extends HTMLDivElement { +/** 播放器核心 */ +@customElement(undefined, `danmaku-${Date.now()}`) +export class Danmaku extends HTMLElement { /** * 需要监听变动的属性。 @@ -31,11 +31,8 @@ export class Danmaku extends HTMLDivElement { */ // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} - /** 初始化标记 */ - // #inited = false; - /** 每当元素添加到文档中时调用。 */ - // connectedCallback() { } + // connectedCallback() {} /** 每当元素从文档中移除时调用。 */ // disconnectedCallback() {} @@ -43,65 +40,37 @@ export class Danmaku extends HTMLDivElement { /** 每当元素被移动到新文档中时调用。 */ // adoptedCallback() {} - /** - * 事件监听(只监听一次) - * - * @param type 事件类型 - * @param listener 事件回调 - */ - one(type: K, listener: (evt: CustomEvent) => void) { - this.addEventListener('bofqi' + type, listener, { once: true }); - } - - /** - * 事件监听 - * - * @param type 事件类型 - * @param listener 事件回调 - */ - bind = (type: K, listener: (evt: CustomEvent) => void) => { - this.addEventListener('bofqi' + type, listener); - } - - /** - * 取消事件监听 - * - * @param type 事件类型 - * @param listener 事件回调 - */ - unbind = (type: K, listener: (evt: CustomEvent) => void) => { - this.removeEventListener('bofqi' + type, listener); - } - - /** - * 分发事件 - * - * @param type 事件类型 - * @param detail 分发给回调的数据 - */ - trigger = (type: K, detail: IDanmakuEvent[K]) => { - // Promise.resolve().then(() => { - // 原生`dispatchEvent`方法只能同步发送消息,使用`Promise`转为异步以合乎正常事件处理流程 - this.dispatchEvent(new CustomEvent('bofqi' + type, { detail })); - // }); - } - + #video: HTMLVideoElement; /** 原始弹幕序列 */ - $dms: IDanmaku[] = []; + #dms: IDanmaku[] = []; /** 弹幕时间轴序列 */ - protected $timeLine: IDanmaku[] = []; + #timeLine: IDanmaku[] = []; /** 排序延时句柄 */ - #timer?: number; + #timer?: ReturnType; + + /** 渲染句柄 */ + #animationTimer?: number; /** 弹幕渲染起始游标 */ #i = 0; - /** 弹幕基准存活时间 */ + /** 弹幕可见性 */ + #visible = true; + + $host = this.attachShadow({ mode: 'closed' }); + + /** 弹幕基准时间 */ $duration = 4500; + /** 弹幕容器宽度 */ + $width = 0; + + /** 弹幕容器高度 */ + $height = 0; + /** 速度 */ $speedPlus = 1; @@ -111,37 +80,21 @@ export class Danmaku extends HTMLDivElement { /** 弹幕速率倍率 */ $rate = 1; - /** 弹幕区域宽度 */ - $width = 0; - - /** 弹幕区域高度 */ - $height = 0; - - get $heightFix() { - return this.$preventShade ? this.$height * 0.85 : this.$height; - } - - /** 当前弹幕数 */ + /** 当前已渲染弹幕数 */ $danmakuNow = 0; /** 显示区域:0-100(0为无限,50为半屏,100为满屏 */ $danmakuArea = 0; + /** 防挡字幕 */ + $preventShade = false; + /** 弹幕渲染起始时间戳 */ $playTimeStamp = 0; /** 弹幕渲染暂停时间戳 */ $pauseTimeStamp = 0; - /** 当前弹幕时间戳:/s */ - $currentTime = 0; - - /** 渲染句柄 */ - #animationTimer?: number; - - /** 弹幕可见性 */ - #visible = true; - /** 上次渲染弹幕的时间戳 */ $progress = 0; @@ -151,8 +104,14 @@ export class Danmaku extends HTMLDivElement { /** 同屏弹幕密度 */ $danmakuNumber = 0; - /** 防挡字幕 */ - $preventShade = false; + /** 弹幕屏蔽等级 */ + $weiget = 0; + + /** 当前弹幕时间戳:/s */ + $currentTime = 0; + + /** mode7 自适应 */ + $mode7Scale = false; #block = 0; @@ -170,8 +129,9 @@ export class Danmaku extends HTMLDivElement { this.#block = v; } - /** 弹幕屏蔽等级 */ - $weiget = 0; + get $heightFix() { + return this.$preventShade ? this.$height * 0.85 : this.$height; + } private $worker?: Worker; @@ -181,109 +141,161 @@ export class Danmaku extends HTMLDivElement { this.$worker = new ParserWorker(); this.$worker.addEventListener('message', e => { if (!e.data.error) { - this.$dms = this.$dms.concat(e.data.map((d: IDanmakuBAS) => { - Mode9.pretreatDanmaku(d); - return d; - })); - this.sort(); + this.#dms = this.#dms.concat(e.data); + this.#sort(); } }); } return this.$worker; } - constructor( - /** 关联的视频元素 */ - private $video: HTMLVideoElement - ) { + constructor(video: HTMLVideoElement) { super(); - this.classList.add('b-danmaku'); + this.#video = video; + // this.#host.adoptedStyleSheets = [stylesheet]; // 文档画中画模式会导致构造的样式表丢失,暂时取道 style 元素代替 + this.$host.appendChild(CSSStyleSheet2HTMLStyleElement(stylesheet)); // 绑定代码弹幕容器 - rootSprite.$host = this; + rootSprite.$host = this.$host; - this.$video.addEventListener('play', () => { - if (!this.$video.paused) { + new ResizeObserver(this.#resizeObserver).observe(this); + video.addEventListener('play', () => { + if (!video.paused) { this.$playTimeStamp = performance.now(); this.style.removeProperty('--animation-play-state'); - this.time(); + this.#time(); this.dispatchEvent(new Event('play')); } }); - this.$video.addEventListener('pause', () => { - if (this.$video.paused) { + video.addEventListener('pause', () => { + if (video.paused) { this.$pauseTimeStamp = performance.now(); this.style.setProperty('--animation-play-state', 'paused'); - this.pause(); + this.#pause(); this.dispatchEvent(new Event('pause')); } }); - this.$video.addEventListener('playing', () => { + video.addEventListener('playing', () => { this.$playTimeStamp = performance.now(); this.style.removeProperty('--animation-play-state'); - this.time(); + this.#time(); this.dispatchEvent(new Event('play')); }); - this.$video.addEventListener('waiting', () => { + video.addEventListener('waiting', () => { this.$pauseTimeStamp = performance.now(); this.style.setProperty('--animation-play-state', 'paused'); - this.pause(); + this.#pause(); this.dispatchEvent(new Event('pause')); }); - this.$video.addEventListener('ended', () => { - if (this.$video.ended) { + video.addEventListener('ended', () => { + if (video.ended) { this.$pauseTimeStamp = performance.now(); this.style.setProperty('--animation-play-state', 'paused'); - this.pause(); + this.#pause(); this.dispatchEvent(new Event('ended')); } }); - this.$video.addEventListener('emptied', () => { - if (this.$video.paused) { + video.addEventListener('emptied', () => { + if (video.paused) { this.$pauseTimeStamp = performance.now(); this.style.setProperty('--animation-play-state', 'paused'); - this.pause(); + this.#pause(); this.dispatchEvent(new Event('pause')); } }); - this.$video.addEventListener('ratechange', () => { - this.$rate = this.$video.playbackRate; + video.addEventListener('ratechange', () => { + this.$rate = video.playbackRate; }); - this.$video.addEventListener('timeupdate', () => { - this.$currentTime = this.$video.currentTime * 1e3; - this.$video.paused && this.pause(); + video.addEventListener('timeupdate', () => { + this.$currentTime = video.currentTime * 1e3; + video.paused && this.#pause(); }); this.addEventListener('click', () => { - this.$video.paused ? this.$video.play() : this.$video.pause(); + video.paused ? video.play() : video.pause(); }); + } - new ResizeObserver(d => { - for (const entry of d) { - for (const size of entry.borderBoxSize) { - this.$width = size.inlineSize; - this.$height = size.blockSize; - this.style.setProperty('--wraps', (size.blockSize / 440)); + /** + * 事件监听(只监听一次) + * + * @param type 事件类型 + * @param listener 事件回调 + */ + one(type: K, listener: (evt: CustomEvent) => void) { + this.addEventListener('bofqi' + type, listener, { once: true }); + } + + /** + * 事件监听 + * + * @param type 事件类型 + * @param listener 事件回调 + */ + bind = (type: K, listener: (evt: CustomEvent) => void) => { + this.addEventListener('bofqi' + type, listener); + } + + /** + * 取消事件监听 + * + * @param type 事件类型 + * @param listener 事件回调 + */ + unbind = (type: K, listener: (evt: CustomEvent) => void) => { + this.removeEventListener('bofqi' + type, listener); + } + + /** + * 分发事件 + * + * @param type 事件类型 + * @param detail 分发给回调的数据 + */ + trigger = (type: K, detail: IDanmakuEvent[K]) => { + // Promise.resolve().then(() => { + // 原生`dispatchEvent`方法只能同步发送消息,使用`Promise`转为异步以合乎正常事件处理流程 + this.dispatchEvent(new CustomEvent('bofqi' + type, { detail })); + // }); + } + + /** 弹幕容器大小变化回调 */ + #resizeObserver = (entries: ResizeObserverEntry[]) => { + for (const { contentBoxSize } of entries) { + for (const { inlineSize, blockSize } of contentBoxSize) { + this.$width = inlineSize; + this.$height = blockSize; + const { videoWidth, videoHeight } = this.#video; + const radio = inlineSize / blockSize; + const radioVideo = videoWidth / videoHeight; + if (radio > radioVideo) { + this.style.removeProperty('--inset-block'); + this.style.setProperty('--inset-inline', `${(radio - radioVideo) / radio / 2 * 100}cqi`); + } else if (radio < radioVideo) { + this.style.setProperty('--inset-block', `${(radioVideo - radio) / radioVideo / 2 * 100}cqb`); + this.style.removeProperty('--inset-inline'); + } else { + this.style.removeProperty('--inset-block'); + this.style.removeProperty('--inset-inline'); } } - this.modeIdentity(); - }).observe(this); + } } /** 弹幕排序 */ - private sort() { + #sort() { clearTimeout(this.#timer); this.#timer = setTimeout(() => { - this.$dms.sort((a, b) => this.compare(a.idStr, b.idStr)); - this.$dms.sort((a, b) => this.compare(a.progress, b.progress)); - this.$timeLine = this.$dms; + this.#dms.sort((a, b) => this.#compare(a.idStr, b.idStr)); + this.#dms.sort((a, b) => this.#compare(a.progress, b.progress)); + this.#timeLine = this.#dms; this.#i = 0; }); } /** 排序比较算法 */ - private compare(num1: number | string | bigint = 0, num2: number | string | bigint = 0) { + #compare(num1: number | string | bigint = 0, num2: number | string | bigint = 0) { typeof num1 === 'string' && (num1 = BigInt(num1)); typeof num2 === 'string' && (num2 = BigInt(num2)); if (num1 > num2) { @@ -295,8 +307,124 @@ export class Danmaku extends HTMLDivElement { } } + /** 时间主循环 */ + #time = () => { + this.#pause(); // 防止多个主循环触发 + this.#render(); + this.#animationTimer = requestAnimationFrame(this.#time); + } + + /** 暂停循环 */ + #pause = () => { + this.#animationTimer && cancelAnimationFrame(this.#animationTimer); + } + + /** 渲染函数 */ + #render() { + if (this.#visible) { + const time = this.$currentTime; + if (this.$progress > time + 3000 + this.#preTime * this.$rate) { + // 上次渲染弹幕的时间戳远大于视频时间,重置时间戳 + this.#flesh(); + } + for (; this.#i < this.#timeLine.length; this.#i++) { + const progress = this.#timeLine[this.#i].progress || 0; + if (progress < time - 1000) { + continue; + } else if (progress < time + this.#preTime * this.$rate) { + // 初次渲染未必从time=0开始,导致progress=0的弹幕渲染不到,何妨反向获取1000ms + this.#draw(this.#timeLine[this.#i], progress - time); + this.$progress = progress; + } else { + break; + } + } + } + } + + /** 绘制弹幕 */ + #draw(dm: IDanmaku, delay = 0) { + if (!dm.on) { + switch (dm.mode) { + case 1: + case 2: + case 3: { + if (this.$danmakuNumber && this.$danmakuNow > this.$danmakuNumber) break; + if (this.$weiget && 'weight' in dm && dm.weight !== undefined && dm.weight < this.$weiget) { + break; + } + if (this.$block & DANMAKU.SCROLL) break; + if (dm.color && dm.color !== 0xFFFFFF && (this.$block & DANMAKU.COLOR)) break; + new Mode1(dm, this).execute(delay); + break; + } + case 4: { + if (this.$danmakuNumber && this.$danmakuNow > this.$danmakuNumber) break; + if (this.$weiget && 'weight' in dm && dm.weight !== undefined && dm.weight < this.$weiget) { + break; + } + if (this.$block & DANMAKU.BOTTOM) break; + if (dm.color && dm.color !== 0xFFFFFF && (this.$block & DANMAKU.COLOR)) break; + new Mode4(dm, this).execute(delay); + break; + } + case 5: { + if (this.$danmakuNumber && this.$danmakuNow > this.$danmakuNumber) break; + if (this.$weiget && 'weight' in dm && dm.weight !== undefined && dm.weight < this.$weiget) { + break; + } + if (this.$block & DANMAKU.TOP) break; + if (dm.color && dm.color !== 0xFFFFFF && (this.$block & DANMAKU.COLOR)) break; + new Mode5(dm, this).execute(delay); + break; + } + case 6: { + if (this.$weiget && 'weight' in dm && dm.weight !== undefined && dm.weight < this.$weiget) { + break; + } + if (this.$block & DANMAKU.SCROLL) break; + if (dm.color && dm.color !== 0xFFFFFF && (this.$block & DANMAKU.COLOR)) break; + new Mode6(dm, this).execute(delay); + break; + } + case 7: { + if (this.$block & DANMAKU.ADVANCE) break; + new Mode7(dm, this).execute(delay); + break; + } + case 8: { + if (this.$block & DANMAKU.SCRIPT) break; + new Mode8(dm, this).execute(); + break; + } + case 9: { + if (this.$block & DANMAKU.BAS) break; + new Mode9(dm, this).execute(delay); + break; + } + } + } + } + + /** 刷新弹幕 */ + #flesh() { + this.$host.replaceChildren(...this.$host.querySelectorAll('style')); + this.#i = 0; + this.$progress = 0; + this.$danmakuNow = 0; + this.#modeIdentity(); + } + + /** 重置渲染空间 */ + #modeIdentity() { + Mode1.identity(); + Mode4.identity(); + Mode5.identity(); + Mode6.identity(); + } + /** 添加弹幕 */ - add(dms: IDanmaku | IDanmaku[]) { + $add(dms: IDanmaku | IDanmaku[]) { Array.isArray(dms) || (dms = [dms]); const arr: IDanmaku[] = []; const bas: IDanmaku[] = []; @@ -312,9 +440,9 @@ export class Danmaku extends HTMLDivElement { } } } - this.$dms = this.$dms.concat(arr); + this.#dms = this.#dms.concat(arr); bas.length && this.worker.postMessage(bas); - this.sort(); + this.#sort(); this.trigger(DanmakuEvent.DANMAKU_ADD, dms); } @@ -323,7 +451,7 @@ export class Danmaku extends HTMLDivElement { * * @param xml xml文本字符串或文档 */ - fromXML(xml: string | Document) { + $fromXML(xml: string | Document) { if (typeof xml === 'string') { // B站输出的xml可能包含不标准的字符,会引起浏览器自动解析失败 // remove-invalid-xml-characters.js @@ -354,137 +482,17 @@ export class Danmaku extends HTMLDivElement { dms.push(dm); } }); - this.add(dms); - } - - /** 时间主循环 */ - private time = () => { - this.pause(); // 防止多个主循环触发 - this.render(); - this.#animationTimer = requestAnimationFrame(this.time); - } - - /** 暂停循环 */ - private pause = () => { - this.#animationTimer && cancelAnimationFrame(this.#animationTimer); - } - - /** 渲染函数 */ - private render() { - if (this.#visible) { - const time = this.$currentTime; - if (this.$progress > time + 3000 + this.#preTime * this.$rate) { - // 上次渲染弹幕的时间戳远大于视频时间,重置时间戳 - this.flesh(); - } - for (; this.#i < this.$timeLine.length; this.#i++) { - const progress = this.$timeLine[this.#i].progress || 0; - if (progress < time - 1000) { - continue; - } else if (progress < time + this.#preTime * this.$rate) { - // 初次渲染未必从time=0开始,导致progress=0的弹幕渲染不到,何妨反向获取1000ms - this.draw(this.$timeLine[this.#i], progress - time); - this.$progress = progress; - } else { - break; - } - } - } - } - - /** 绘制弹幕 */ - private draw(dm: IDanmaku, delay = 0) { - switch (dm.mode) { - case 1: { - if (this.$danmakuNumber && this.$danmakuNow > this.$danmakuNumber) break; - if (this.$weiget && 'weight' in dm && dm.weight !== undefined && dm.weight < this.$weiget) { - break; - } - if (this.$block & DANMAKU.SCROLL) break; - if (dm.color && dm.color !== 0xFFFFFF && (this.$block & DANMAKU.COLOR)) break; - new Mode1(this, dm).execute(delay); - break; - } - case 2: { - new Mode1(this, dm).execute(delay); - break; - } - case 3: { - new Mode1(this, dm).execute(delay); - break; - } - case 4: { - if (this.$danmakuNumber && this.$danmakuNow > this.$danmakuNumber) break; - if (this.$weiget && 'weight' in dm && dm.weight !== undefined && dm.weight < this.$weiget) { - break; - } - if (this.$block & DANMAKU.BOTTOM) break; - if (dm.color && dm.color !== 0xFFFFFF && (this.$block & DANMAKU.COLOR)) break; - new Mode4(this, dm).execute(delay); - break; - } - case 5: { - if (this.$danmakuNumber && this.$danmakuNow > this.$danmakuNumber) break; - if (this.$weiget && 'weight' in dm && dm.weight !== undefined && dm.weight < this.$weiget) { - break; - } - if (this.$block & DANMAKU.TOP) break; - if (dm.color && dm.color !== 0xFFFFFF && (this.$block & DANMAKU.COLOR)) break; - new Mode5(this, dm).execute(delay); - break; - } - case 6: { - if (this.$weiget && 'weight' in dm && dm.weight !== undefined && dm.weight < this.$weiget) { - break; - } - if (this.$block & DANMAKU.SCROLL) break; - if (dm.color && dm.color !== 0xFFFFFF && (this.$block & DANMAKU.COLOR)) break; - new Mode6(this, dm).execute(delay); - break; - } - case 7: { - if (this.$block & DANMAKU.ADVANCE) break; - new Mode7(this, dm).execute(delay); - break; - } - case 8: { - if (this.$block & DANMAKU.SCRIPT) break; - new Mode8(this, dm).execute(); - break; - } - case 9: { - if (this.$block & DANMAKU.BAS) break; - new Mode9(this, dm).execute(delay); - break; - } - } - } - - /** 刷新弹幕 */ - private flesh() { - this.replaceChildren(); - this.#i = 0; - this.$progress = 0; - this.$danmakuNow = 0; - this.modeIdentity(); - } - - /** 重置渲染空间 */ - private modeIdentity() { - Mode1.identity(); - Mode4.identity(); - Mode5.identity(); - Mode6.identity(); + this.$add(dms); } /** 播放 */ $play() { - this.$video.play(); + this.#video.play(); } $pause() { - this.$video.pause(); + this.#video.pause(); } /** @@ -493,23 +501,24 @@ export class Danmaku extends HTMLDivElement { * @param t 目标时间:/s */ $seek(t: number) { - this.$video.currentTime = t; + this.#video.currentTime = t; } /** 显示弹幕 */ - on() { + $on() { this.#visible = true; } /** 关闭弹幕 */ - off() { + $off() { this.#visible = false; - this.flesh(); + this.#flesh(); } - identify = () => { - this.$timeLine.length = 0; - this.$dms.length = 0; + $indentify = () => { + clearTimeout(this.#timer); + this.#timeLine.length = 0; + this.#dms.length = 0; this.$pauseTimeStamp = 0; this.$playTimeStamp = 0; this.$danmakuNow = 0; @@ -517,7 +526,7 @@ export class Danmaku extends HTMLDivElement { this.style.removeProperty('--animation-play-state'); this.$worker?.terminate(); delete this.$worker; - this.flesh(); + this.#flesh(); } } @@ -566,151 +575,9 @@ export interface IDanmaku { animation?: string; /** 笔触 */ colorful?: number; - /** 图片弹幕 */ picture?: string; -} - -export interface IDanmakuElem { - - /** 唯一id,已超过JavaScript整数上限,如非必要切莫转化为数字 */ - $idStr: string; - - /** 弹幕位于视频中的时间点(单位毫秒) */ - $progress: number; - - /** 弹幕数据 */ - $dm: IDanmaku - - /** - * 绘制 - * - * @param delay 延时:/ms - */ - execute(delay?: number): void; -} - -export interface IDanmakuBAS extends IDanmaku { - // 以下为BAS弹幕专用 - duration: number; - def2set: Record; - defs: IDef[]; - sets: ISet[]; - setsIntervals: Record; -} - -export interface IAnimation { - delay: number; - duration: number; - easing: string; - group: number; - name: string; - valueStart?: IDefAttrs; - valueEnd: IDefAttrs; -} - -export interface IPercentNum { - numType: 'number' | 'percent'; - value: number; -} - -export interface IDef { - attrs: IDefAttrs; - name: string; - obj_type: string; - type: 'DefText' | 'DefButton' | 'DefPath'; - _reg_order: number; -} - -export interface ISet { - type: 'Serial' | 'Parallel' | 'Unit'; - items?: ISet[]; - attrs?: IDefAttrs; - defaultEasing?: string; - default_easing?: string; - duration?: number; - targetName?: string; - target_name?: string; -} - -/** 通用属性 */ -interface ICommon { - x?: IPercentNum | number; - y?: IPercentNum | number; - zIndex?: IPercentNum | number; - scale?: IPercentNum | number; - duration?: number; -} - -/** 文本属性 */ -interface IText extends ICommon { - content?: string; - alpha?: IPercentNum | number; - color?: number; - anchorX?: IPercentNum | number; - anchorY?: IPercentNum | number; - fontSize?: IPercentNum | number; - fontFamily?: string; - bold?: IPercentNum | number; - textShadow?: IPercentNum | number; - strokeWidth?: IPercentNum | number; - strokeColor?: number; - rotateX?: IPercentNum | number; - rotateY?: IPercentNum | number; - rotateZ?: IPercentNum | number; - parent?: string; -} - -/** 按钮属性 */ -interface IButton extends ICommon { - text?: string; - fontSize?: IPercentNum | number; - textColor?: number; - textAlpha?: IPercentNum | number; - fillColor?: number; - fillAlpha?: IPercentNum | number; - target?: IButtonTarget; -} - -/** 按钮回调 */ -interface IButtonTarget { - objType: 'av' | 'bangumi' | 'seek'; - time?: number; - page?: number; - av?: number; - bvid?: string; - seasonId?: number; - episodeId?: number; -} - -interface IPath extends ICommon { - d?: string; - viewBox?: string; - borderColor?: number; - borderAlpha?: IPercentNum | number; - borderWidth?: IPercentNum | number; - fillColor?: number; - fillAlpha?: IPercentNum | number; -} - -export interface IDefAttrs extends IText, IButton, IPath { - width?: number; - height?: number; -} - -/** 弹幕事件 */ -export enum DanmakuEvent { - - /** 添加弹幕,注意参数为新增弹幕 */ - DANMAKU_ADD, - - /** 发送弹幕 */ - DANMAKU_SEND, -} - -/** 弹幕事件对应的数据类型 */ -export interface IDanmakuEvent { - 0: IDanmaku[]; - 1: IDanmaku; + /** 是否正在渲染 */ + on?: boolean; } \ No newline at end of file diff --git a/src/danmaku/render/index.css b/src/danmaku/render/index.css new file mode 100644 index 0000000..bf380ca --- /dev/null +++ b/src/danmaku/render/index.css @@ -0,0 +1,68 @@ +@import url(./mode1.css); +@import url(./mode4.css); +@import url(./mode5.css); +@import url(./mode6.css); +@import url(./mode8/index.css); +@import url(./mode9/index.css); + +.dm { + /* 弹幕基准放大倍率 */ + --font-basis: calc(100cqb / 440); + + line-height: 1.125; + white-space: pre; + pointer-events: none; + align-content: center; + font-size: calc(var(--fontsize) * var(--fontSize) * 1px); + text-shadow: 1px 0 1px var(--text-shadow), 0 1px 1px var(--text-shadow), 0 -1px 1px var(--text-shadow), -1px 0 1px var(--text-shadow); + font-family: var(--font-family); + font-weight: var(--font-weight); + opacity: var(--opacity); + + &.wraps { + font-size: calc(var(--fontsize) * var(--font-basis)); + } + + @container danmaku style(--full-screen-sync: 1) { + &:not(.wraps) { + font-size: calc(var(--fontsize) * var(--fontSize) * var(--font-basis)); + } + } + + @container danmaku style(--fontBorder: 1) { + & { + text-shadow: 0px 0px 1px var(--text-shadow), 0 0 1px var(--text-shadow), 0 0 1px var(--text-shadow); + } + } + + @container danmaku style(--fontBorder: 2) { + & { + text-shadow: 1px 1px 2px var(--text-shadow), 0 0 1px var(--text-shadow); + } + } + + @container danmaku style(--colorful: 1) { + &.colorful { + background-size: cover; + text-shadow: none; + background-image: linear-gradient(to right, #F2509E, #308BCD); + background-clip: text; + -webkit-text-fill-color: #ffffff; + -webkit-text-stroke: 2px transparent; + } + } + + @container danmaku style(--animation-play-state: paused) { + & { + animation-play-state: paused !important; + } + } + + &.pause { + animation-play-state: paused; + } + + >img { + block-size: 1.125em; + } +} \ No newline at end of file diff --git a/src/danmaku/render/index.ts b/src/danmaku/render/index.ts new file mode 100644 index 0000000..ad5931a --- /dev/null +++ b/src/danmaku/render/index.ts @@ -0,0 +1,85 @@ +import { IDanmaku } from ".."; +import { Format } from "../../utils/fomat"; + +/** 普通弹幕 */ +export abstract class Mode extends HTMLElement { + + /** + * 需要监听变动的属性。 + * 与实例方法`attributeChangedCallback`配合使用。 + * 此字符串序列定义了`attributeChangedCallback`回调时的第一个参数的可能值。 + */ + // static observedAttributes = []; + + /** + * 在属性更改、添加、移除或替换时调用。 + * 需要与静态属性`observedAttributes`配合使用。 + * 此回调的第一个参数在`observedAttributes`数组中定义。 + */ + // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} + + /** 每当元素添加到文档中时调用。 */ + // connectedCallback() {} + + /** 每当元素从文档中移除时调用。 */ + // disconnectedCallback() {} + + /** 每当元素被移动到新文档中时调用。 */ + // adoptedCallback() {} + + /** + * @see 文档 {@link https://info.bilibili.co/pages/viewpage.action?pageId=95154953} + * @see 优先级 {@link https://www.tapd.bilibili.co/20062561/prong/stories/view/1120062561001659710} + */ + static parseAction(dm: IDanmaku) { + if (typeof dm.action === 'string') { + const list = dm.action.split(';'); + list.forEach(d => { + const ele = d.split(':'); + if (ele[0] && ele[1]) { + Reflect.set(dm, ele[0], ele[1]); + } + }); + } + } + + /** 开始运动时间戳 */ + $starttimeStamp = 0; + + /** 结束运动时间戳 */ + $endtimeStamp = 0; + + /** 动画时长 */ + $duration = 0; + + /** 动画速度 */ + $speed = 1; + + /** 弹幕宽度 */ + $width = 0; + + /** 弹幕高度 */ + $height = 0; + + /** 是否换行弹幕 */ + $wraps = false; + + constructor( + /** 弹幕数据 */ + protected $dm: IDanmaku, + ) { + super(); + + this.classList.add('dm'); + Mode.parseAction($dm); + if ($dm.content) { + const text = this.innerText = $dm.content.replace(/(\/n|\\n|\n|\r\n)/g, '\n'); + text.split('\n').length > 2 && (this.$wraps = true, this.classList.add('wraps')); + } + $dm.fontsize && this.style.setProperty('--fontsize', $dm.fontsize); + $dm.color || this.style.setProperty('--text-shadow', '#ffffff'); + this.style.color = Format.hexColor($dm.color); + $dm.picture && (this.innerHTML = ``); + $dm.colorful && this.classList.add('colorful'); + } +} \ No newline at end of file diff --git a/src/danmaku/render/mode0.ts b/src/danmaku/render/mode0.ts deleted file mode 100644 index 95e6254..0000000 --- a/src/danmaku/render/mode0.ts +++ /dev/null @@ -1,115 +0,0 @@ -import { Danmaku, IDanmaku } from ".."; -import { customElement } from "../../utils/Decorator/customElement"; -import { Format } from "../../utils/fomat"; -import { ATTR } from "../attr"; - -/** 普通弹幕 */ -@customElement('div') -export class Mode0 extends HTMLDivElement { - - /** - * 需要监听变动的属性。 - * 与实例方法`attributeChangedCallback`配合使用。 - * 此字符串序列定义了`attributeChangedCallback`回调时的第一个参数的可能值。 - */ - // static observedAttributes = []; - - /** - * 在属性更改、添加、移除或替换时调用。 - * 需要与静态属性`observedAttributes`配合使用。 - * 此回调的第一个参数在`observedAttributes`数组中定义。 - */ - // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} - - /** 初始化标记 */ - // #inited = false; - - /** 每当元素添加到文档中时调用。 */ - // connectedCallback() {} - - /** 每当元素从文档中移除时调用。 */ - // disconnectedCallback() {} - - /** 每当元素被移动到新文档中时调用。 */ - // adoptedCallback() {} - - /** 弹幕中换行符数量 */ - protected $wraps = 0; - - /** 是否允许渲染 */ - protected $render = true; - - /** 开始运动时间戳 */ - $starttimeStamp = 0; - - /** 结束运动时间戳 */ - $endtimeStamp = 0; - - /** 动画时长 */ - $duration = 0; - - /** 动画速度 */ - $speed = 1; - - /** 弹幕宽度 */ - $width = 0; - - /** 弹幕高度 */ - $height = 0; - - constructor( - /** 弹幕管理组件 */ - protected $container: Danmaku, - /** 弹幕数据 */ - public $dm: IDanmaku) { - super(); - this.parseAction(); - - this.classList.add('mode0'); - if ($dm.content) { - const text = $dm.content.replace(/(\/n|\\n|\n|\r\n)/g, '\n'); - this.innerText = text; - this.$wraps = text.split('\n').length; - } - $dm.fontsize && this.style.setProperty('--font-size', $dm.fontsize + 'px'); - if (this.$wraps > 10) { - // 双排版弹幕 - this.classList.add('wraps'); - } - $dm.color || this.style.setProperty('--text-shadow', '#ffffff'); - this.style.color = Format.hexColor($dm.color); - if ($dm.picture) { - // 图片弹幕 - this.innerHTML = ``; - this.style.blockSize = `1.125em`; - } - if ($dm.colorful) { - // 大会员弹幕 - this.classList.add('colorful'); - } - if (this.$wraps < 2 && ($dm).attr & ATTR.LIKE) { - this.classList.add('like'); - this.innerHTML = ` - - - -${this.innerHTML}`; - } - } - - /** - * @see 文档 {@link https://info.bilibili.co/pages/viewpage.action?pageId=95154953} - * @see 优先级 {@link https://www.tapd.bilibili.co/20062561/prong/stories/view/1120062561001659710} - */ - private parseAction() { - if (typeof this.$dm.action === 'string') { - const list = this.$dm.action.split(';'); - list.forEach(d => { - const ele = d.split(':'); - if (ele[0] && ele[1]) { - Reflect.set(this.$dm, ele[0], ele[1]); - } - }); - } - } -} \ No newline at end of file diff --git a/src/danmaku/render/mode1.css b/src/danmaku/render/mode1.css new file mode 100644 index 0000000..4232c2b --- /dev/null +++ b/src/danmaku/render/mode1.css @@ -0,0 +1,15 @@ +.mode1 { + --dealy: 0; + --duation: 4500; + + position: absolute; + will-change: translate; + translate: 100cqi; + animation: dmMode1 calc(var(--duation) * 1ms) linear calc(var(--dealy) * 1ms) both; +} + +@keyframes dmMode1 { + to { + translate: -100%; + } +} \ No newline at end of file diff --git a/src/danmaku/render/mode1.ts b/src/danmaku/render/mode1.ts index 840e20d..b4bb440 100644 --- a/src/danmaku/render/mode1.ts +++ b/src/danmaku/render/mode1.ts @@ -1,9 +1,37 @@ +import { Mode } from "."; +import { Danmaku, IDanmaku } from ".."; import { customElement } from "../../utils/Decorator/customElement"; -import { Mode0 } from "./mode0"; -/** 普通弹幕 */ -@customElement('div') -export class Mode1 extends Mode0 { +/** 一般弹幕 */ +@customElement(undefined, `mode1-${Date.now()}`) +export class Mode1 extends Mode { + + /** + * 需要监听变动的属性。 + * 与实例方法`attributeChangedCallback`配合使用。 + * 此字符串序列定义了`attributeChangedCallback`回调时的第一个参数的可能值。 + */ + // static observedAttributes = []; + + /** + * 在属性更改、添加、移除或替换时调用。 + * 需要与静态属性`observedAttributes`配合使用。 + * 此回调的第一个参数在`observedAttributes`数组中定义。 + */ + // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} + + /** 每当元素添加到文档中时调用。 */ + connectedCallback() { + this.$dm.on = true; + } + + /** 每当元素从文档中移除时调用。 */ + disconnectedCallback() { + this.$dm.on = false; + } + + /** 每当元素被移动到新文档中时调用。 */ + // adoptedCallback() {} static space: Mode1[][] = []; @@ -11,12 +39,62 @@ export class Mode1 extends Mode0 { this.space.length = 0; } - private $space() { - if (this.$wraps < 2) { + constructor( + /** 弹幕数据 */ + public $dm: IDanmaku, + /** 弹幕管理器 */ + protected $container: Danmaku, + + ) { + super($dm); + } + + async execute($delay = 0) { + this.$starttimeStamp = this.$endtimeStamp = 0; + this.classList.add('mode1', 'pause'); + this.$container.$host.appendChild(this); + + const { clientWidth, clientHeight } = this; + const { $speedPlus, $speedSync, $rate, $duration, $width } = this.$container; + this.$width = clientWidth; + this.$height = clientHeight; + this.$duration = $duration / $speedPlus / ($speedSync ? $rate : 1) * (this.$width + $width) / (512 + this.$width); + this.$speed = (512 + this.$width) / ($duration / $speedPlus / ($speedSync ? $rate : 1)); + this.addEventListener('animationstart', this.#animationstart, { once: true }); + this.addEventListener('animationend', this.#animationend, { once: true }); + this.style.setProperty('--duation', this.$duration); + this.style.setProperty('--dealy', $delay); + this.#space(); + this.classList.remove('pause'); + this.$wraps || this.$container.$danmakuNow++; + } + + #animationstart = (ev: AnimationEvent) => { + this.$starttimeStamp = ev.timeStamp; + } + + /** 运动结束处理 */ + #animationend = (ev: AnimationEvent) => { + this.$endtimeStamp = ev.timeStamp; + this.remove(); + this.style.animation = ''; + this.$wraps || this.$container.$danmakuNow--; + } + + /** 取消弹幕 */ + #cancel() { + this.removeEventListener('animationend', this.#animationend); + this.remove(); + this.style.animation = ''; + this.$wraps || this.$container.$danmakuNow--; + } + + #space() { + if (!this.$wraps) { const { $danmakuArea, $heightFix } = this.$container; for (let i = 0; i <= Mode1.space.length; i++) { if ($danmakuArea && i) { - return this.$cancel(); + return this.#cancel(); } if (!Mode1.space[i]) { // 弹幕层尚未创建,直接创建 @@ -25,7 +103,7 @@ export class Mode1 extends Mode0 { layer.fill(this, 0, Math.ceil(this.$height)); Mode1.space.push(layer); break; - } else if (this.$layer(Mode1.space[i])) { + } else if (this.#layer(Mode1.space[i])) { // 弹幕层可以进入 break; } @@ -39,11 +117,11 @@ export class Mode1 extends Mode0 { * @param layer 弹幕层 * @returns 是否可以添加弹幕 */ - private $layer(layer: Mode1[]) { + #layer(layer: Mode1[]) { const { $heightFix } = this.$container; layer.length = Math.ceil($heightFix); for (let i = 0; i + this.$height < $heightFix; i += this.$height) { - if (this.$add(layer, i, i + this.$height)) { + if (this.#add(layer, i, i + this.$height)) { return true; } } @@ -58,7 +136,7 @@ export class Mode1 extends Mode0 { * @param to 弹幕下限高度 * @returns 是否允许添加 */ - private $add(layer: Mode1[], from: number, to: number) { + #add(layer: Mode1[], from: number, to: number) { const set = new Set(); for (let i = Math.ceil(from); i < Math.ceil(to); i++) { layer[i] && set.add(layer[i]); @@ -75,7 +153,7 @@ export class Mode1 extends Mode0 { return false; } // 计算弹幕距离 - const pauseStamp = this.$pause(pre); + const pauseStamp = this.#pause(pre); if ((timeStamp - pre.$starttimeStamp - pauseStamp) * pre.$speed <= pre.$width) { // 本行弹幕尚未完全显示,必定追尾,拒绝进入 return false; @@ -95,7 +173,7 @@ export class Mode1 extends Mode0 { return false; } } - this.style.translate = `0 ${from}px`; + this.style.insetBlockStart = `${from}px`; layer.fill(this, Math.ceil(from), Math.ceil(to)); return true; } @@ -106,7 +184,7 @@ export class Mode1 extends Mode0 { * @param pre 当前行参数 * @returns */ - private $pause(pre: Mode1) { + #pause(pre: Mode1) { if (!this.$container.$pauseTimeStamp) { // 弹幕未曾暂停过,无需修正处理 return 0; @@ -126,47 +204,4 @@ export class Mode1 extends Mode0 { return 0; } } - - private $animationstart = (ev: AnimationEvent) => { - this.$starttimeStamp = ev.timeStamp; - } - - /** 运动结束处理 */ - private $animationend = (ev: AnimationEvent) => { - this.$endtimeStamp = ev.timeStamp; - this.remove(); - this.style.animation = ''; - this.$wraps > 1 || this.$container.$danmakuNow--; - this.$render = true; - } - - /** 取消弹幕 */ - private $cancel() { - this.removeEventListener('animationend', this.$animationend); - this.remove(); - this.style.animation = ''; - this.$wraps > 1 || this.$container.$danmakuNow--; - this.$render = true; - } - - async execute(delay = 0) { - if (this.$render) { - this.$render = false; - this.$endtimeStamp = this.$starttimeStamp = 0; - this.style.transform = 'translateX(100cqw)'; - this.$container.appendChild(this); - - const { clientWidth, clientHeight } = this; - const { $speedPlus, $speedSync, $rate, $duration, $width } = this.$container - this.$width = clientWidth; - this.$height = clientHeight; - this.$duration = $duration / $speedPlus / ($speedSync ? $rate : 1) * (this.$width + $width) / (512 + this.$width); - this.$speed = (512 + this.$width) / ($duration / $speedPlus / ($speedSync ? $rate : 1)); - this.addEventListener('animationstart', this.$animationstart, { once: true }); - this.addEventListener('animationend', this.$animationend, { once: true }); - this.$space(); - this.style.animation = `${this.$duration}ms linear ${delay}ms both dmMode1`; - this.$wraps > 1 || this.$container.$danmakuNow++; - } - } } \ No newline at end of file diff --git a/src/danmaku/render/mode4.css b/src/danmaku/render/mode4.css new file mode 100644 index 0000000..52630a6 --- /dev/null +++ b/src/danmaku/render/mode4.css @@ -0,0 +1,11 @@ +.mode4 { + --dealy: 0; + --duation: 4500; + + position: absolute; + will-change: translate; + translate: calc(50cqi - 50%); + animation: dmMode4 calc(var(--duation) * 1ms) linear calc(var(--dealy) * 1ms) both; +} + +@keyframes dmMode4 {} \ No newline at end of file diff --git a/src/danmaku/render/mode4.ts b/src/danmaku/render/mode4.ts index 6abb38f..494b72d 100644 --- a/src/danmaku/render/mode4.ts +++ b/src/danmaku/render/mode4.ts @@ -1,9 +1,37 @@ +import { Mode } from "."; +import { IDanmaku, Danmaku } from ".."; import { customElement } from "../../utils/Decorator/customElement"; -import { Mode0 } from "./mode0"; /** 底部弹幕 */ -@customElement('div') -export class Mode4 extends Mode0 { +@customElement(undefined, `mode4-${Date.now()}`) +export class Mode4 extends Mode { + + /** + * 需要监听变动的属性。 + * 与实例方法`attributeChangedCallback`配合使用。 + * 此字符串序列定义了`attributeChangedCallback`回调时的第一个参数的可能值。 + */ + // static observedAttributes = []; + + /** + * 在属性更改、添加、移除或替换时调用。 + * 需要与静态属性`observedAttributes`配合使用。 + * 此回调的第一个参数在`observedAttributes`数组中定义。 + */ + // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} + + /** 每当元素添加到文档中时调用。 */ + connectedCallback() { + this.$dm.on = true; + } + + /** 每当元素从文档中移除时调用。 */ + disconnectedCallback() { + this.$dm.on = false; + } + + /** 每当元素被移动到新文档中时调用。 */ + // adoptedCallback() {} static space: Mode4[][] = []; @@ -11,12 +39,54 @@ export class Mode4 extends Mode0 { this.space.length = 0; } - private $space() { - if (this.$wraps < 2) { - const { $danmakuArea, $heightFix } = this.$container + constructor( + /** 弹幕数据 */ + public $dm: IDanmaku, + /** 弹幕管理器 */ + protected $container: Danmaku, + ) { + super($dm); + } + + async execute($delay = 0) { + this.$starttimeStamp = this.$endtimeStamp = 0; + this.classList.add('mode4', 'pause'); + this.$container.$host.appendChild(this); + + const { clientHeight } = this; + const { $duration } = this.$container + this.$height = clientHeight; + this.$duration = $duration; + this.addEventListener('animationend', this.#animationend, { once: true }); + this.style.setProperty('--duation', this.$duration); + this.style.setProperty('--dealy', $delay); + this.#space(); + this.classList.remove('pause'); + this.$wraps || this.$container.$danmakuNow++; + } + + /** 运动结束处理 */ + #animationend = (ev: AnimationEvent) => { + this.$endtimeStamp = ev.timeStamp; + this.remove(); + this.style.animation = ''; + this.$wraps || this.$container.$danmakuNow--; + } + + /** 取消弹幕 */ + #cancel() { + this.removeEventListener('animationend', this.#animationend); + this.remove(); + this.style.animation = ''; + this.$wraps || this.$container.$danmakuNow--; + } + + #space() { + if (!this.$wraps) { + const { $danmakuArea, $heightFix } = this.$container; for (let i = 0; i <= Mode4.space.length; i++) { if ($danmakuArea && i) { - return this.$cancel(); + return this.#cancel(); } if (!Mode4.space[i]) { // 弹幕层尚未创建,直接创建 @@ -25,7 +95,7 @@ export class Mode4 extends Mode0 { layer.fill(this, 0, Math.ceil(this.$height)); Mode4.space.push(layer); break; - } else if (this.$layer(Mode4.space[i])) { + } else if (this.#layer(Mode4.space[i])) { // 弹幕层可以进入 break; } @@ -39,11 +109,11 @@ export class Mode4 extends Mode0 { * @param layer 弹幕层 * @returns 是否可以添加弹幕 */ - private $layer(layer: Mode4[]) { - const { $heightFix } = this.$container + #layer(layer: Mode4[]) { + const { $heightFix } = this.$container; layer.length = Math.ceil($heightFix); for (let i = 0; i + this.$height < $heightFix; i += this.$height) { - if (this.$add(layer, i, i + this.$height)) { + if (this.#add(layer, i, i + this.$height)) { return true; } } @@ -58,7 +128,7 @@ export class Mode4 extends Mode0 { * @param to 弹幕下限高度 * @returns 是否允许添加 */ - private $add(layer: Mode4[], from: number, to: number) { + #add(layer: Mode4[], from: number, to: number) { const set = new Set(); for (let i = Math.ceil(from); i < Math.ceil(to); i++) { layer[i] && set.add(layer[i]); @@ -70,44 +140,8 @@ export class Mode4 extends Mode0 { return false } } - this.style.translate = `0 ${this.$container.$heightFix - to}px`; + this.style.insetBlockEnd = `${from}px`; layer.fill(this, Math.ceil(from), Math.ceil(to)); return true; } - - /** 运动结束处理 */ - private $animationend = (ev: AnimationEvent) => { - this.$endtimeStamp = ev.timeStamp; - this.remove(); - this.style.animation = ''; - this.$wraps > 1 || this.$container.$danmakuNow--; - this.$render = true; - } - - /** 取消弹幕 */ - private $cancel() { - this.removeEventListener('animationend', this.$animationend); - this.remove(); - this.style.animation = ''; - this.$wraps > 1 || this.$container.$danmakuNow--; - this.$render = true; - } - - async execute(delay = 0) { - if (this.$render) { - this.$render = false; - this.$endtimeStamp = this.$starttimeStamp = 0; - this.style.transform = 'translateX(calc(50cqw - 50%))'; - this.$container.appendChild(this); - - const { clientHeight } = this; - const { $duration } = this.$container - this.$height = clientHeight; - this.$duration = $duration; - this.addEventListener('animationend', this.$animationend, { once: true }); - this.$space(); - this.style.animation = `0ms linear ${delay + this.$duration}ms both dmMode4`; - this.$wraps > 1 || this.$container.$danmakuNow++; - } - } } \ No newline at end of file diff --git a/src/danmaku/render/mode5.css b/src/danmaku/render/mode5.css new file mode 100644 index 0000000..679d817 --- /dev/null +++ b/src/danmaku/render/mode5.css @@ -0,0 +1,11 @@ +.mode5 { + --dealy: 0; + --duation: 4500; + + position: absolute; + will-change: translate; + translate: calc(50cqi - 50%); + animation: dmMode5 calc(var(--duation) * 1ms) linear calc(var(--dealy) * 1ms) both; +} + +@keyframes dmMode5 {} \ No newline at end of file diff --git a/src/danmaku/render/mode5.ts b/src/danmaku/render/mode5.ts index c689370..fd070b1 100644 --- a/src/danmaku/render/mode5.ts +++ b/src/danmaku/render/mode5.ts @@ -1,9 +1,37 @@ +import { Mode } from "."; +import { IDanmaku, Danmaku } from ".."; import { customElement } from "../../utils/Decorator/customElement"; -import { Mode0 } from "./mode0"; -/** 顶部 */ -@customElement('div') -export class Mode5 extends Mode0 { +/** 顶部弹幕 */ +@customElement(undefined, `mode5-${Date.now()}`) +export class Mode5 extends Mode { + + /** + * 需要监听变动的属性。 + * 与实例方法`attributeChangedCallback`配合使用。 + * 此字符串序列定义了`attributeChangedCallback`回调时的第一个参数的可能值。 + */ + // static observedAttributes = []; + + /** + * 在属性更改、添加、移除或替换时调用。 + * 需要与静态属性`observedAttributes`配合使用。 + * 此回调的第一个参数在`observedAttributes`数组中定义。 + */ + // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} + + /** 每当元素添加到文档中时调用。 */ + connectedCallback() { + this.$dm.on = true; + } + + /** 每当元素从文档中移除时调用。 */ + disconnectedCallback() { + this.$dm.on = false; + } + + /** 每当元素被移动到新文档中时调用。 */ + // adoptedCallback() {} static space: Mode5[][] = []; @@ -11,12 +39,54 @@ export class Mode5 extends Mode0 { this.space.length = 0; } - private $space() { - if (this.$wraps < 2) { + constructor( + /** 弹幕数据 */ + public $dm: IDanmaku, + /** 弹幕管理器 */ + protected $container: Danmaku, + ) { + super($dm); + } + + async execute($delay = 0) { + this.$starttimeStamp = this.$endtimeStamp = 0; + this.classList.add('mode5', 'pause'); + this.$container.$host.appendChild(this); + + const { clientHeight } = this; + const { $duration } = this.$container + this.$height = clientHeight; + this.$duration = $duration; + this.addEventListener('animationend', this.#animationend, { once: true }); + this.style.setProperty('--duation', this.$duration); + this.style.setProperty('--dealy', $delay); + this.#space(); + this.classList.remove('pause'); + this.$wraps || this.$container.$danmakuNow++; + } + + /** 运动结束处理 */ + #animationend = (ev: AnimationEvent) => { + this.$endtimeStamp = ev.timeStamp; + this.remove(); + this.style.animation = ''; + this.$wraps || this.$container.$danmakuNow--; + } + + /** 取消弹幕 */ + #cancel() { + this.removeEventListener('animationend', this.#animationend); + this.remove(); + this.style.animation = ''; + this.$wraps || this.$container.$danmakuNow--; + } + + #space() { + if (!this.$wraps) { const { $danmakuArea, $heightFix } = this.$container; for (let i = 0; i <= Mode5.space.length; i++) { if ($danmakuArea && i) { - return this.$cancel(); + return this.#cancel(); } if (!Mode5.space[i]) { // 弹幕层尚未创建,直接创建 @@ -25,7 +95,7 @@ export class Mode5 extends Mode0 { layer.fill(this, 0, Math.ceil(this.$height)); Mode5.space.push(layer); break; - } else if (this.$layer(Mode5.space[i])) { + } else if (this.#layer(Mode5.space[i])) { // 弹幕层可以进入 break; } @@ -39,11 +109,11 @@ export class Mode5 extends Mode0 { * @param layer 弹幕层 * @returns 是否可以添加弹幕 */ - private $layer(layer: Mode5[]) { + #layer(layer: Mode5[]) { const { $heightFix } = this.$container; layer.length = Math.ceil($heightFix); for (let i = 0; i + this.$height < $heightFix; i += this.$height) { - if (this.$add(layer, i, i + this.$height)) { + if (this.#add(layer, i, i + this.$height)) { return true; } } @@ -58,7 +128,7 @@ export class Mode5 extends Mode0 { * @param to 弹幕下限高度 * @returns 是否允许添加 */ - private $add(layer: Mode5[], from: number, to: number) { + #add(layer: Mode5[], from: number, to: number) { const set = new Set(); for (let i = Math.ceil(from); i < Math.ceil(to); i++) { layer[i] && set.add(layer[i]); @@ -70,45 +140,8 @@ export class Mode5 extends Mode0 { return false } } - this.style.translate = `0 ${from}px`; + this.style.insetBlockStart = `${from}px`; layer.fill(this, Math.ceil(from), Math.ceil(to)); return true; } - - - /** 运动结束处理 */ - private $animationend = (ev: AnimationEvent) => { - this.$endtimeStamp = ev.timeStamp; - this.remove(); - this.style.animation = ''; - this.$wraps > 1 || this.$container.$danmakuNow--; - this.$render = true; - } - - /** 取消弹幕 */ - private $cancel() { - this.removeEventListener('animationend', this.$animationend); - this.remove(); - this.style.animation = ''; - this.$wraps > 1 || this.$container.$danmakuNow--; - this.$render = true; - } - - async execute(delay = 0) { - if (this.$render) { - this.$render = false; - this.$endtimeStamp = this.$starttimeStamp = 0; - this.style.transform = 'translateX(calc(50cqw - 50%))'; - this.$container.appendChild(this); - - const { clientHeight } = this; - const { $duration } = this.$container - this.$height = clientHeight; - this.$duration = $duration; - this.addEventListener('animationend', this.$animationend, { once: true }); - this.$space(); - this.style.animation = `0ms linear ${delay + this.$duration}ms both dmMode5`; - this.$wraps > 1 || this.$container.$danmakuNow++; - } - } } \ No newline at end of file diff --git a/src/danmaku/render/mode6.css b/src/danmaku/render/mode6.css new file mode 100644 index 0000000..9a95eb3 --- /dev/null +++ b/src/danmaku/render/mode6.css @@ -0,0 +1,15 @@ +.mode6 { + --dealy: 0; + --duation: 4500; + + position: absolute; + will-change: translate; + translate: -100%; + animation: dmMode6 calc(var(--duation) * 1ms) linear calc(var(--dealy) * 1ms) both; +} + +@keyframes dmMode6 { + to { + translate: 100cqi; + } +} \ No newline at end of file diff --git a/src/danmaku/render/mode6.ts b/src/danmaku/render/mode6.ts index a40b3fc..ac99d01 100644 --- a/src/danmaku/render/mode6.ts +++ b/src/danmaku/render/mode6.ts @@ -1,9 +1,37 @@ +import { Mode } from "."; +import { IDanmaku, Danmaku } from ".."; import { customElement } from "../../utils/Decorator/customElement"; -import { Mode0 } from "./mode0"; /** 逆向弹幕 */ -@customElement('div') -export class Mode6 extends Mode0 { +@customElement(undefined, `mode6-${Date.now()}`) +export class Mode6 extends Mode { + + /** + * 需要监听变动的属性。 + * 与实例方法`attributeChangedCallback`配合使用。 + * 此字符串序列定义了`attributeChangedCallback`回调时的第一个参数的可能值。 + */ + // static observedAttributes = []; + + /** + * 在属性更改、添加、移除或替换时调用。 + * 需要与静态属性`observedAttributes`配合使用。 + * 此回调的第一个参数在`observedAttributes`数组中定义。 + */ + // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} + + /** 每当元素添加到文档中时调用。 */ + connectedCallback() { + this.$dm.on = true; + } + + /** 每当元素从文档中移除时调用。 */ + disconnectedCallback() { + this.$dm.on = false; + } + + /** 每当元素被移动到新文档中时调用。 */ + // adoptedCallback() {} static space: Mode6[][] = []; @@ -11,12 +39,61 @@ export class Mode6 extends Mode0 { this.space.length = 0; } + constructor( + /** 弹幕数据 */ + public $dm: IDanmaku, + /** 弹幕管理器 */ + protected $container: Danmaku, + ) { + super($dm); + } + + async execute($delay = 0) { + this.$starttimeStamp = this.$endtimeStamp = 0; + this.classList.add('mode6', 'pause'); + this.$container.$host.appendChild(this); + + const { clientWidth, clientHeight } = this; + const { $speedPlus, $speedSync, $rate, $duration, $width } = this.$container; + this.$width = clientWidth; + this.$height = clientHeight; + this.$duration = $duration / $speedPlus / ($speedSync ? $rate : 1) * (this.$width + $width) / (512 + this.$width); + this.$speed = (512 + this.$width) / ($duration / $speedPlus / ($speedSync ? $rate : 1)); + this.addEventListener('animationstart', this.#animationstart, { once: true }); + this.addEventListener('animationend', this.#animationend, { once: true }); + this.style.setProperty('--duation', this.$duration); + this.style.setProperty('--dealy', $delay); + this.$space(); + this.classList.remove('pause'); + this.$wraps || this.$container.$danmakuNow++; + } + + #animationstart = (ev: AnimationEvent) => { + this.$starttimeStamp = ev.timeStamp; + } + + /** 运动结束处理 */ + #animationend = (ev: AnimationEvent) => { + this.$endtimeStamp = ev.timeStamp; + this.remove(); + this.style.animation = ''; + this.$wraps || this.$container.$danmakuNow--; + } + + /** 取消弹幕 */ + #cancel() { + this.removeEventListener('animationend', this.#animationend); + this.remove(); + this.style.animation = ''; + this.$wraps || this.$container.$danmakuNow--; + } + private $space() { - if (this.$wraps < 2) { + if (!this.$wraps) { const { $danmakuArea, $heightFix } = this.$container; for (let i = 0; i <= Mode6.space.length; i++) { if ($danmakuArea && i) { - return this.$cancel(); + return this.#cancel(); } if (!Mode6.space[i]) { // 弹幕层尚未创建,直接创建 @@ -25,7 +102,7 @@ export class Mode6 extends Mode0 { layer.fill(this, 0, Math.ceil(this.$height)); Mode6.space.push(layer); break; - } else if (this.$layer(Mode6.space[i])) { + } else if (this.#layer(Mode6.space[i])) { // 弹幕层可以进入 break; } @@ -39,11 +116,11 @@ export class Mode6 extends Mode0 { * @param layer 弹幕层 * @returns 是否可以添加弹幕 */ - private $layer(layer: Mode6[]) { + #layer(layer: Mode6[]) { const { $heightFix } = this.$container; layer.length = Math.ceil($heightFix); for (let i = 0; i + this.$height < $heightFix; i += this.$height) { - if (this.$add(layer, i, i + this.$height)) { + if (this.#add(layer, i, i + this.$height)) { return true; } } @@ -58,7 +135,7 @@ export class Mode6 extends Mode0 { * @param to 弹幕下限高度 * @returns 是否允许添加 */ - private $add(layer: Mode6[], from: number, to: number) { + #add(layer: Mode6[], from: number, to: number) { const set = new Set(); for (let i = Math.ceil(from); i < Math.ceil(to); i++) { layer[i] && set.add(layer[i]); @@ -75,7 +152,7 @@ export class Mode6 extends Mode0 { return false; } // 计算弹幕距离 - const pauseStamp = this.$pause(pre); + const pauseStamp = this.#pause(pre); if ((timeStamp - pre.$starttimeStamp - pauseStamp) * pre.$speed <= pre.$width) { // 本行弹幕尚未完全显示,必定追尾,拒绝进入 return false; @@ -95,7 +172,7 @@ export class Mode6 extends Mode0 { return false; } } - this.style.translate = `0 ${from}px`; + this.style.insetBlockStart = `${from}px`; layer.fill(this, Math.ceil(from), Math.ceil(to)); return true; } @@ -106,7 +183,7 @@ export class Mode6 extends Mode0 { * @param pre 当前行参数 * @returns */ - private $pause(pre: Mode6) { + #pause(pre: Mode6) { if (!this.$container.$pauseTimeStamp) { // 弹幕未曾暂停过,无需修正处理 return 0; @@ -126,46 +203,4 @@ export class Mode6 extends Mode0 { return 0; } } - - private $animationstart = (ev: AnimationEvent) => { - this.$starttimeStamp = ev.timeStamp; - } - - /** 运动结束处理 */ - private $animationend = (ev: AnimationEvent) => { - this.$endtimeStamp = ev.timeStamp; - this.remove(); - this.style.animation = ''; - this.$wraps > 1 || this.$container.$danmakuNow--; - this.$render = true; - } - - /** 取消弹幕 */ - private $cancel() { - this.removeEventListener('animationend', this.$animationend); - this.remove(); - this.style.animation = ''; - this.$wraps > 1 || this.$container.$danmakuNow--; - this.$render = true; - } - - async execute(delay = 0) { - if (this.$render) { - this.$render = false; - this.$endtimeStamp = this.$starttimeStamp = 0; - this.style.transform = 'translateX(-100%)'; - this.$container.appendChild(this); - - const { clientWidth, clientHeight } = this; - const { $speedPlus, $speedSync, $rate, $duration, $width } = this.$container - this.$width = clientWidth; - this.$height = clientHeight; - this.$duration = $duration / $speedPlus / ($speedSync ? $rate : 1) * (this.$width + $width) / (512 + this.$width); - this.$speed = (512 + this.$width) / ($duration / $speedPlus / ($speedSync ? $rate : 1)); - this.addEventListener('animationstart', this.$animationstart, { once: true }); - this.addEventListener('animationend', this.$animationend, { once: true }); - this.$space(); - this.style.animation = `${this.$duration}ms linear ${delay}ms both dmMode6`; - } - } } \ No newline at end of file diff --git a/src/danmaku/render/mode7.ts b/src/danmaku/render/mode7.ts deleted file mode 100644 index 921ceba..0000000 --- a/src/danmaku/render/mode7.ts +++ /dev/null @@ -1,437 +0,0 @@ -import { Danmaku, IDanmaku } from ".."; -import { customElement } from "../../utils/Decorator/customElement"; -import { Element } from "../../utils/element"; -import { Format } from "../../utils/fomat"; - -/** 高级弹幕 */ -@customElement('div') -export class Mode7 extends HTMLDivElement { - - /** 初始透明度 */ - protected $sOpacity = 1; - - /** 最终透明度 */ - protected $eOpacity = 1; - - /** 初始横坐标 */ - protected $startX = 0; - - /** 初始纵坐标 */ - protected $startY = 0; - - /** 生存时间 */ - protected $duration = 4500; - - /** 显示文本 */ - protected $text = ''; - - /** z轴旋转角 */ - protected $zRotate = 0; - - /** y轴旋转角 */ - protected $yRotate = 0; - - /** 最终横坐标 */ - protected $endX = 0; - - /** 最终纵坐标 */ - protected $endY = 0; - - /** 运动时间 */ - protected $aTime = 0; - - /** 初始位置暂停时间 */ - protected $aDelay = 0; - - /** 描边 */ - protected $stroked = 1; - - /** 字体 */ - protected $family = '黑体'; - - /** 线性加速 */ - protected $linearSpeedUp = 0; - - /** - * 路径追踪 - * 参考svg path算法 - * - * @example - * 'M107,82L108,83L109,84L111,88' - */ - protected $path = ''; - - /** 已解析路径 */ - protected $paths: [number, number][] = []; - - protected $root = this.attachShadow({ mode: 'closed' }); - - protected $div = this.$root.appendChild(document.createElement('div')); - - protected $style = this.$root.appendChild(document.createElement('style')); - - protected $pre = Element.add('pre', undefined, this.$div); - - constructor( - /** 弹幕管理组件 */ - protected $container: Danmaku, - /** 弹幕数据 */ - public $dm: IDanmaku, - ) { - super(); - - this.$decode(); - this.$initStyle(); - } - - private $decode() { - try { - typeof this.$dm.content === 'string' && (this.$dm.content = JSON.parse(this.$dm.content)); - const [ - startX, - startY, - opacity, - duration, - text, - zRotate, - yRotate, - endX, - endY, - aTime, - aDelay, - stroked, - family, - linearSpeedUp, - path, - ] = this.$dm.content; - startX && (this.$startX = +startX); - startY && (this.$startY = +startY); - opacity.split('-').forEach((d: number, i: number) => { - if (i) { - d >= 0 && (this.$eOpacity = +d); - } else { - d >= 0 && (this.$sOpacity = +d); - } - }); - duration >= 0 && (this.$duration = duration * 1000); - text && (this.$text = text.replace(/(\/n|\\n|\n|\r\n)/g, '\n')); - zRotate && (this.$zRotate = +zRotate); - yRotate && (this.$yRotate = +yRotate); - this.$endX = endX ?? this.$startX; - this.$endY = endY ?? this.$startY; - aTime && (this.$aTime = +aTime); - aDelay && (this.$aDelay = +aDelay); - stroked && stroked !== 'false' && (this.$stroked = 1); - family && (this.$family = family); - linearSpeedUp && linearSpeedUp !== '0' && (this.$linearSpeedUp = 1); - if (path) { - this.$path = path; - this.$paths = this.$path.slice(1).split('L').map(d => d.split(',')); - } - } catch { } - } - - private $initStyle() { - this.classList.add('mode7'); - this.$div.style.cssText = `display: inline-block; position: absolute; inset-block-start: 0px; inset-inline-start: 0px; inline-size: 100%; block-size: 100%; perspective: calc(100cqi / 2 / tan((${Math.PI} / 180) * (55 / 2))); opacity: 1;`; - // 一般样式 - this.$pre.style.display = 'inline-block'; - this.$pre.style.lineHeight = '1'; - this.$pre.style.transformOrigin = '0% 0% 0px'; - this.$pre.style.setProperty('content-visibility', 'auto'); - this.$pre.style.fontFamily = 'bold'; - this.$pre.style.margin = '0'; - // 字体大小 - this.$pre.style.fontSize = `${this.$dm.fontsize}px`; - // 字体颜色 - this.$pre.style.color = Format.hexColor(this.$dm.color || 0); - // 描边 - const shadowColor = this.$dm.color !== 0 ? '#000' : '#fff'; - this.$stroked && (this.$pre.style.textShadow = `1px 0 1px ${shadowColor},0 1px 1px ${shadowColor},0 -1px 1px ${shadowColor},-1px 0 1px ${shadowColor}`); - // 字体 - this.$family && (this.$pre.style.fontFamily = `${this.$family}, Arial, Helvetica, sans-serif`); - // 文本 - this.$pre.innerText = this.$text; - } - - private initPath() { - const pre = `matrix3d(calc(cos(${this.$yRotate}deg) * cos(${this.$zRotate}deg)),calc(cos(${this.$yRotate}deg) * sin(${this.$zRotate}deg)),calc(sin(${this.$yRotate}deg)),0,calc(0 - sin(${this.$zRotate}deg)),calc(cos(${this.$zRotate}deg)),0,0,calc(0 - sin(${this.$yRotate}deg) * cos(${this.$zRotate}deg)),calc(0 - sin(${this.$yRotate}deg) * sin(${this.$zRotate}deg)),calc(cos(${this.$yRotate}deg)),0,`; - if (!this.$aTime) { - return `@keyframes dmMode7 { - from { - opacity: ${this.$sOpacity}; - } - to { - opacity: ${this.$eOpacity}; - } -}`; - } else if (this.$paths.length) { - // 使用路径跟随 - if (this.$paths.length === 1) { - // 路径点只有一个,那就等于没动 - return `@keyframes dmMode7 { - from { - opacity: ${this.$sOpacity}; - } - to { - opacity: ${this.$eOpacity}; - } -}`; - } else { - if (this.$aDelay) { - // 有运动延时 - const aD = Math.min(this.$aDelay, this.$duration); - if (aD < this.$duration) { - // 运动延时小于存活时间 - const aT = Math.min(aD + this.$aTime, this.$duration); - if (aT < this.$duration) { - // 运动时间小于存活时间 - const s0 = aD / this.$duration; - const dd = (this.$aTime / (this.$paths.length - 1)) / this.$duration; - const arr = [`from { - opacity: ${this.$sOpacity}; - transform: ${pre}${this.getPx(this.$paths.at(0)![0]) || 0},${this.getPx(this.$paths.at(0)![1], true) || 0},0,1); -}`]; - for (let i = 0; i < this.$paths.length; i++) { - const [x, y] = this.$paths[i]; - arr.push(`${(s0 + dd * i) * 100}% { - transform: ${pre}${this.getPx(x) || 0},${this.getPx(y, true) || 0},0,1); -}`); - } - arr.push(`to { - opacity: ${this.$eOpacity}; - transform: ${pre}${this.getPx(this.$paths.at(-1)![0]) || 0},${this.getPx(this.$paths.at(-1)![1], true) || 0},0,1); -}`); - return `@keyframes dmMode7 { -${arr.join('\n\t')} -}`; - } else { - // 运动时间不小于存活时间 - const s0 = aD / this.$duration; - const dd = (this.$aTime / (this.$paths.length - 1)) / this.$duration; - const arr = [`from { - opacity: ${this.$sOpacity}; - transform: ${pre}${this.getPx(this.$paths.at(0)![0]) || 0},${this.getPx(this.$paths.at(0)![1], true) || 0},0,1); -}`]; - for (let i = 0; i < this.$paths.length; i++) { - const [x, y] = this.$paths[i]; - switch (i) { - case this.$paths.length - 1: { - arr.push(`to { - opacity: ${this.$eOpacity}; - transform: ${pre}${this.getPx(x) || 0},${this.getPx(y, true) || 0},0,1); -}`); - break; - } - default: { - arr.push(`${(s0 + dd * i) * 100}% { - transform: ${pre}${this.getPx(x) || 0},${this.getPx(y, true) || 0},0,1); -}`); - break; - } - } - } - return `@keyframes dmMode7 { -${arr.join('\n\t')} -}`; - } - } else { - // 运动延时不小于存活时间 - return `@keyframes dmMode7 { - from { - opacity: ${this.$sOpacity}; - } - to { - opacity: ${this.$eOpacity}; - } -}`; - } - } else { - // 无运动延时 - const aT = Math.min(this.$aTime, this.$duration); - if (aT < this.$duration) { - // 运动时间小于存活时间 - const dd = (this.$aTime / (this.$paths.length - 1)) / this.$duration; - const arr: string[] = []; - for (let i = 0; i < this.$paths.length; i++) { - const [x, y] = this.$paths[i]; - switch (i) { - case 0: { - arr.push(`from { - opacity: ${this.$sOpacity}; - transform: ${pre}${this.getPx(x) || 0},${this.getPx(y, true) || 0},0,1); -}`); - break; - } - default: { - arr.push(`${dd * i * 100}% { - transform: ${pre}${this.getPx(x) || 0},${this.getPx(y, true) || 0},0,1); -}`); - break; - } - } - } - arr.push(`to { - opacity: ${this.$eOpacity}; - transform: ${pre}${this.getPx(this.$paths.at(-1)![0]) || 0},${this.getPx(this.$paths.at(-1)![1], true) || 0},0,1); -}`); - return `@keyframes dmMode7 { -${arr.join('\n\t')} -}`; - } else { - // 运动时间不小于存活时间 - const dd = (this.$duration / (this.$paths.length - 1)) / this.$duration; - const arr: string[] = []; - for (let i = 0; i < this.$paths.length; i++) { - const [x, y] = this.$paths[i]; - switch (i) { - case 0: { - arr.push(`from { - opacity: ${this.$sOpacity}; - transform: ${pre}${this.getPx(x) || 0},${this.getPx(y, true) || 0},0,1); -}`); - break; - } - case this.$paths.length - 1: { - arr.push(`to { - opacity: ${this.$eOpacity}; - transform: ${pre}${this.getPx(x) || 0},${this.getPx(y, true) || 0},0,1); -}`); - break; - } - default: { - arr.push(`${dd * i * 100}% { - transform: ${pre}${this.getPx(x) || 0},${this.getPx(y, true) || 0},0,1); -}`); - break - } - } - } - return `@keyframes dmMode7 { -${arr.join('\n\t')} -}`; - } - } - } - } else { - // 使用起始路径 - if (this.$aDelay) { - // 有运动延时 - const aD = Math.min(this.$aDelay, this.$duration); - if (aD < this.$duration) { - // 运动延时小于存活时间 - const aT = Math.min(aD + this.$aTime, this.$duration); - if (aT < this.$duration) { - // 运动时间小于存活时间 - const s0 = aD / this.$duration; - const se = aT / this.$duration; - return `@keyframes dmMode7 { - from { - opacity: ${this.$sOpacity}; - transform: ${pre}${this.getPx(this.$startX) || 0},${this.getPx(this.$startY, true) || 0},0,1); - } - ${s0 * 100}% { - transform: ${pre}${this.getPx(this.$startX) || 0},${this.getPx(this.$startY, true) || 0},0,1); - } - ${se * 100}% { - transform: ${pre}${this.getPx(this.$endX) || 0},${this.getPx(this.$endY, true) || 0},0,1); - } - to { - opacity: ${this.$eOpacity}; - transform: ${pre}${this.getPx(this.$endX) || 0},${this.getPx(this.$endY, true) || 0},0,1); - } -}`; - } else { - // 运动时间不小于存活时间 - const s0 = aD / this.$duration; - return `@keyframes dmMode7 { - from { - opacity: ${this.$sOpacity}; - transform: ${pre}${this.getPx(this.$startX) || 0},${this.getPx(this.$startY, true) || 0},0,1); - } - ${s0 * 100}% { - transform: ${pre}${this.getPx(this.$startX) || 0},${this.getPx(this.$startY, true) || 0},0,1); - } - to { - opacity: ${this.$eOpacity}; - transform: ${pre}${this.getPx(this.$startX) || 0},${this.getPx(this.$startY, true) || 0},0,1); - } -}`; - } - } else { - // 运动延时不小于存活时间 - return `@keyframes dmMode7 { - from { - opacity: ${this.$sOpacity}; - transform: ${pre}${this.getPx(this.$startX) || 0},${this.getPx(this.$startY, true) || 0},0,1); - } - to { - opacity: ${this.$eOpacity}; - transform: ${pre}${this.getPx(this.$endX) || 0},${this.getPx(this.$endY, true) || 0},0,1); - } -}`; - } - } else { - // 无运动延时 - const aT = Math.min(this.$aTime, this.$duration); - if (aT < this.$duration) { - // 运动时间小于存活时间 - const se = aT / this.$duration; - return `@keyframes dmMode7 { - from { - opacity: ${this.$sOpacity}; - transform: ${pre}${this.getPx(this.$startX) || 0},${this.getPx(this.$startY, true) || 0},0,1); - } - ${se * 100}% { - transform: ${pre}${this.getPx(this.$endX) || 0},${this.getPx(this.$endY, true) || 0},0,1); - } - to { - opacity: ${this.$eOpacity}; - transform: ${pre}${this.getPx(this.$endX) || 0},${this.getPx(this.$endY, true) || 0},0,1); - } -}`; - } else { - // 运动时间不小于存活时间 - return `@keyframes dmMode7 { - from { - opacity: ${this.$sOpacity}; - transform: ${pre}${this.getPx(this.$startX) || 0},${this.getPx(this.$startY, true) || 0},0,1); - } - to { - opacity: ${this.$eOpacity}; - transform: ${pre}${this.getPx(this.$endX) || 0},${this.getPx(this.$endY, true) || 0},0,1); - } -}`; - } - } - } - } - - /** - * 获取实际坐标值 - * 原始值可能是一个百分比 - * - * @param v 原始值 - * @param x 改为基于纵坐标,默认基于横坐标 - */ - private getPx(v: number, x = true) { - if (v > 0 && v < 1) { - return x ? this.$container.$height * v : this.$container.$width * v; - } - return v; - } - - /** 运动结束处理 */ - private $animationend = (ev: AnimationEvent) => { - this.remove(); - this.style.animation = ''; - } - - async execute(delay = 0) { - this.$style.textContent = this.initPath(); - this.$pre.addEventListener('animationend', this.$animationend, { once: true }); - this.$pre.style.animation = `${this.$duration}ms ${this.$linearSpeedUp ? 'ease-in' : 'linear'} ${delay}ms both dmMode7`; - this.$pre.style.setProperty('animation-play-state', 'var(--animation-play-state)'); - this.$container.appendChild(this); - } -} \ No newline at end of file diff --git a/src/danmaku/render/mode7/index.css b/src/danmaku/render/mode7/index.css new file mode 100644 index 0000000..5b3aade --- /dev/null +++ b/src/danmaku/render/mode7/index.css @@ -0,0 +1,129 @@ +@scope { + :scope { + display: inline-block; + position: absolute; + /* 修正弹幕容器分辨率以适应视频分辨率 */ + inset-block: var(--inset-block); + inset-inline: var(--inset-inline); + /* 高级弹幕透视效果在原版是固定的 288 多一点,在不同播放器分辨率下显示会形变,要等比放大 */ + perspective: calc((100cqb - var(--inset-block)) / 440 * 288.1473083496094); + opacity: 1; + container: mode7 / size; + } +} + +pre { + /* 弹幕基准放大倍率 */ + --font-basis: calc(100cqb / 440); + + /** 初始透明度 */ + --sOpacity: 1; + /** 最终透明度 */ + --eOpacity: 1; + /** 初始横坐标 */ + --startX: 0; + /** 初始纵坐标 */ + --startY: 0; + /** 最终横坐标 */ + --endX: 0; + /** 最终纵坐标 */ + --endY: 0; + /** z轴旋转角 */ + --zRotate: 0; + /** y轴旋转角 */ + --yRotate: 0; + /** 生存时间 */ + --duration: 4500; + /** 运动时间 */ + --aTime: 0; + /** 初始位置暂停时间 */ + --aDelay: 0; + /* 动画统一延时 */ + --delay: 0; + /** + * 路径追踪 + * 参考svg path算法 + * + * @example + * 'M107,82L108,83L109,84L111,88' + */ + --path: ""; + + position: absolute; + line-height: 1; + display: inline-block; + line-height: 1; + transform-origin: 0% 0% 0px; + content-visibility: auto; + margin: 0; + animation-timing-function: linear; + /* 高级弹幕的一大困境是字体大小不会随着播放器分辨率变大,这里强制等比放大 */ + font-size: calc(var(--fontsize) * var(--font-basis)); + font-weight: var(--font-weight); + /* y z 轴同时旋转始终不知道如何用 rotate 函数处理,只能强行写复杂的 matrix3d 函数,16 位数调试起来简直地狱难度 */ + transform: matrix3d(calc(cos(var(--yRotate) * 1deg) * cos(var(--zRotate) * 1deg)), calc(cos(var(--yRotate) * 1deg) * sin(var(--zRotate) * 1deg)), calc(sin(var(--yRotate) * 1deg)), 0, calc(0 - sin(var(--zRotate) * 1deg)), calc(cos(var(--zRotate) * 1deg)), 0, 0, calc(0 - sin(var(--yRotate) * 1deg) * cos(var(--zRotate) * 1deg)), calc(0 - sin(var(--yRotate) * 1deg) * sin(var(--zRotate) * 1deg)), calc(cos(var(--yRotate) * 1deg)), 0, 0, 0, 0, 1); + animation-name: inset, opacity; + animation-duration: calc(var(--aTime) * 1ms), calc(var(--duration) * 1ms); + animation-delay: calc((var(--aDelay) + var(--delay)) * 1ms), calc(var(--delay) * 1ms); + animation-fill-mode: both; + + &:not(.no-stroked) { + text-shadow: 1px 0 1px var(--text-shadow), 0 1px 1px var(--text-shadow), 0 -1px 1px var(--text-shadow), -1px 0 1px var(--text-shadow); + + @container danmaku style(--fontBorder: 1) { + & { + text-shadow: 0px 0px 1px var(--text-shadow), 0 0 1px var(--text-shadow), 0 0 1px var(--text-shadow); + } + } + + @container danmaku style(--fontBorder: 2) { + & { + text-shadow: 1px 1px 2px var(--text-shadow), 0 0 1px var(--text-shadow); + } + } + } + + &.path { + /* 使用 CSS 运动路径的一大难题是 path 路径取值如何等比放大,以后碰到用例再优化吧,或者考虑回滚 inset 动画 */ + offset-path: path(var(--path)); + animation-name: path, opacity; + } + + @container danmaku style(--animation-play-state: paused) { + & { + animation-play-state: paused !important; + } + } +} + +@keyframes opacity { + from { + opacity: var(--sOpacity); + } + + to { + opacity: var(--eOpacity); + } +} + +@keyframes path { + from { + offset-distance: 0%; + } + + to { + offset-distance: 100%; + } +} + +@keyframes inset { + from { + inset-inline-start: var(--startX); + inset-block-start: var(--startY); + } + + to { + inset-inline-start: var(--endX); + inset-block-start: var(--endY); + } +} \ No newline at end of file diff --git a/src/danmaku/render/mode7/index.d.css.ts b/src/danmaku/render/mode7/index.d.css.ts new file mode 100644 index 0000000..eb9bc4f --- /dev/null +++ b/src/danmaku/render/mode7/index.d.css.ts @@ -0,0 +1,2 @@ +declare const stylesheet: CSSStyleSheet; +export default stylesheet; \ No newline at end of file diff --git a/src/danmaku/render/mode7/index.ts b/src/danmaku/render/mode7/index.ts new file mode 100644 index 0000000..246142e --- /dev/null +++ b/src/danmaku/render/mode7/index.ts @@ -0,0 +1,123 @@ +import { Danmaku, IDanmaku } from "../.."; +import { CSSStyleSheet2HTMLStyleElement } from "../../../utils/CSSStyleSheet2HTMLStyleElement"; +import { customElement } from "../../../utils/Decorator/customElement"; +import { Format } from "../../../utils/fomat"; +import stylesheet from "./index.css" with {type: 'css'}; + +@customElement(undefined, `mode7-${Date.now()}`) +export class Mode7 extends HTMLElement { + + /** + * 需要监听变动的属性。 + * 与实例方法`attributeChangedCallback`配合使用。 + * 此字符串序列定义了`attributeChangedCallback`回调时的第一个参数的可能值。 + */ + // static observedAttributes = []; + + /** + * 在属性更改、添加、移除或替换时调用。 + * 需要与静态属性`observedAttributes`配合使用。 + * 此回调的第一个参数在`observedAttributes`数组中定义。 + */ + // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} + + /** 每当元素添加到文档中时调用。 */ + connectedCallback() { + this.$dm.on = true; + } + + /** 每当元素从文档中移除时调用。 */ + disconnectedCallback() { + this.$dm.on = false; + } + + /** 每当元素被移动到新文档中时调用。 */ + // adoptedCallback() {} + + private $host = this.attachShadow({ mode: 'closed' }); + + private $pre = this.$host.appendChild(document.createElement('pre')); + + constructor( + /** 弹幕数据 */ + public $dm: IDanmaku, + /** 弹幕管理器 */ + private $container: Danmaku, + ) { + super(); + + // this.#host.adoptedStyleSheets = [stylesheet]; // 文档画中画模式会导致构造的样式表丢失,暂时取道 style 元素代替 + this.$host.appendChild(CSSStyleSheet2HTMLStyleElement(stylesheet)); + + this.$pre.addEventListener('animationend', this.$animationend); + } + + async execute($delay = 0) { + this.$decode(); + $delay && this.$pre.style.setProperty('--delay', $delay); + this.$container.$host.appendChild(this); + } + + /** 位移等比放大处理 */ + private $insetFix(v: number) { + if (v > 0 && v < 1) { + // 位移值在 0-1 之间的说明是百分比 + return Math.floor(v * 100) + 'cqi'; + } + return this.$container.$mode7Scale ? `calc(100cqb / 440 * ${v})` : v + 'px'; + } + + private $insetFiy(v: number) { + if (v > 0 && v < 1) { + return Math.floor(v * 100) + 'cqb'; + } + return this.$container.$mode7Scale ? `calc(100cqb / 440 * ${v})` : v + 'px'; + } + + private $decode() { + try { + typeof this.$dm.content === 'string' && (this.$dm.content = JSON.parse(this.$dm.content)); + const [startX, startY, opacity, duration, text, zRotate, yRotate, endX, endY, aTime, aDelay, stroked, family, linearSpeedUp, path] = this.$dm.content; + startX && this.$pre.style.setProperty('--startX', this.$insetFix(startX)); + startY && this.$pre.style.setProperty('--startY', this.$insetFiy(startY)); + opacity.split('-').forEach((d: number, i: number) => { + if (i) { + d >= 0 && this.$pre.style.setProperty('--eOpacity', d); + } else { + d >= 0 && this.$pre.style.setProperty('--sOpacity', d); + } + }); + duration >= 0 && this.$pre.style.setProperty('--duration', (duration * 1000)); + if (text) { + this.$pre.innerText = text.replace(/(\/n|\\n|\n|\r\n)/g, '\n'); + text.split('\n').length > 2 && this.$pre.classList.add('wraps'); + } + zRotate && this.$pre.style.setProperty('--zRotate', zRotate); + yRotate && this.$pre.style.setProperty('--yRotate', yRotate); + this.$pre.style.setProperty('--endX', this.$insetFix(endX ?? startX)); + this.$pre.style.setProperty('--endY', this.$insetFiy(endY ?? startY)); + aTime && this.$pre.style.setProperty('--aTime', aTime); + aDelay && this.$pre.style.setProperty('--aDelay', aDelay); + stroked === 'false' && this.$pre.classList.add('no-stroked'); + family && (this.$pre.style.fontFamily = `${family}, Arial, Helvetica, sans-serif`); + linearSpeedUp && linearSpeedUp !== '0' && (this.$pre.style.animationTimingFunction = 'ease-in'); + if (path) { + this.$pre.style.setProperty('--path', path); + this.$pre.classList.add('path'); + } + // 字体大小 + this.$container.$mode7Scale ? this.$pre.style.setProperty('--fontsize', this.$dm.fontsize) : (this.$pre.style.fontSize = this.$dm.fontsize + 'px'); + // 字体颜色 + this.$dm.color || this.$pre.style.setProperty('--text-shadow', '#ffffff'); + this.$pre.style.color = Format.hexColor(this.$dm.color || 0); + } catch { } + } + + /** 运动结束处理 */ + private $animationend = ({ animationName }: AnimationEvent) => { + if (animationName === 'opacity') { + this.$pre.removeEventListener('animationend', this.$animationend); + this.remove(); + } + } +} \ No newline at end of file diff --git a/src/danmaku/render/mode8/Display/Bitmap.ts b/src/danmaku/render/mode8/Display/Bitmap.ts index 5cc9491..60e27b6 100644 --- a/src/danmaku/render/mode8/Display/Bitmap.ts +++ b/src/danmaku/render/mode8/Display/Bitmap.ts @@ -1,9 +1,9 @@ -import { Element } from "../../../../utils/element"; import { BitmapFilter } from "../filters/BitmapFilter"; import { ColorTransform } from "../geom/ColorTransform"; import { Matrix } from "../geom/Matrix"; import { Point } from "../geom/Point"; import { Rectangle } from "../geom/Rectangle"; +import { addElement } from "../Utils/element"; import { BlendMode } from "./BlendMode"; import { ByteArray } from "./ByteArray"; import { DirtyArea } from "./DirtyArea"; @@ -983,7 +983,7 @@ export class Bitmap extends DisplayObject { } constructor(param: IBitmap) { - super(param, true, Element.add('canvas', { class: 'as3-danmaku-item' })); + super(param, true, addElement('canvas', { class: 'as3-danmaku-item' })); this.$host.style.cssText = `position: absolute; inset-block-start: 0; inset-inline-start: 0; transform-origin: 0 0 0;`; param.bitmapData && (this.bitmapData = param.bitmapData); this.init(); diff --git a/src/danmaku/render/mode8/Display/CommentButton.ts b/src/danmaku/render/mode8/Display/CommentButton.ts index 6b8522d..502fbbc 100644 --- a/src/danmaku/render/mode8/Display/CommentButton.ts +++ b/src/danmaku/render/mode8/Display/CommentButton.ts @@ -1,4 +1,4 @@ -import { Element } from "../../../../utils/element"; +import { addElement } from "../Utils/element"; import { IDisplay } from "./Display"; import { UIComponent } from "./UIComponent"; @@ -6,7 +6,7 @@ export class CommentButton extends UIComponent { constructor(param: ICommentButton) { super(param); - this.$host = Element.add('button', { class: 'as3-danmaku-item' }); + this.$host = addElement('button', { class: 'as3-danmaku-item' }); this.$host.classList.add('as3-button'); this.$host.innerText = param.text.replace(/\/n/g, '\n'); param.onclick && this.$host.addEventListener('click', param.onclick); diff --git a/src/danmaku/render/mode8/Display/DisplayObject.ts b/src/danmaku/render/mode8/Display/DisplayObject.ts index b0ecae7..c9e19b7 100644 --- a/src/danmaku/render/mode8/Display/DisplayObject.ts +++ b/src/danmaku/render/mode8/Display/DisplayObject.ts @@ -1,5 +1,4 @@ import { IDanmaku } from "../../.."; -import { Element } from "../../../../utils/element"; import { Format } from "../../../../utils/fomat"; import { BitmapFilter } from "../filters/BitmapFilter"; import { BlurFilter } from "../filters/BlurFilter"; @@ -10,6 +9,7 @@ import { Point } from "../geom/Point"; import { Rectangle } from "../geom/Rectangle"; import { Transform } from "../geom/Transform"; import { Vector3D } from "../geom/Vector3D"; +import { addElement } from "../Utils/element"; import { AccessibilityProperties } from "./AccessibilityProperties"; import { BitmapData } from "./Bitmap"; import { BlendMode } from "./BlendMode"; @@ -623,7 +623,7 @@ export abstract class DisplayObject { this._updateBox(true); } - constructor(private $param = {}, private $append = true, public $host = Element.add('div', { class: 'as3-danmaku-item' })) { } + constructor(private $param = {}, private $append = true, public $host = addElement('div', { class: 'as3-danmaku-item' })) { } protected init() { 'alpha' in this.$param && (this.alpha = this.$param.alpha); diff --git a/src/danmaku/render/mode8/Display/Graphics.ts b/src/danmaku/render/mode8/Display/Graphics.ts index 0444f86..cf36391 100644 --- a/src/danmaku/render/mode8/Display/Graphics.ts +++ b/src/danmaku/render/mode8/Display/Graphics.ts @@ -1,6 +1,6 @@ -import { Element } from "../../../../utils/element"; import { Format } from "../../../../utils/fomat"; import { Matrix } from "../geom/Matrix"; +import { addSvg } from "../Utils/element"; import { BitmapData } from "./Bitmap"; import { CapsStyle } from "./CapsStyle"; import { GradientType } from "./GradientType"; @@ -23,13 +23,13 @@ import { TriangleCulling } from "./TriangleCulling"; */ export class Graphics { - $defaultContainer = Element.addSvg('g'); + $defaultContainer = addSvg('g'); - $defaultGroup = Element.addSvg('g'); + $defaultGroup = addSvg('g'); - $globalDefs = Element.addSvg('defs'); + $globalDefs = addSvg('defs'); - $defaultEffects = Element.addSvg('defs'); + $defaultEffects = addSvg('defs'); /** 当前 SVGPathElement 节点 */ @@ -133,11 +133,11 @@ export class Graphics { focalPointRatio = 0, ) { const gradId = `gradient-${type}-${this.$globalDefs.childNodes.length}`; - const grad = type === GradientType.RADIAL ? Element.addSvg('radialGradient') : Element.addSvg('linearGradient'); + const grad = type === GradientType.RADIAL ? addSvg('radialGradient') : addSvg('linearGradient'); grad.id = gradId; grad.setAttribute('spreadMethod', spreadMethod.trim()); for (let i = 0, len = ratios.length; i < len; i++) { - Element.addSvg( + addSvg( 'stop', { 'offset': (ratios[i] / 255) || 0, @@ -218,7 +218,7 @@ export class Graphics { anchorY = 0, ) { if (!this.$lastPath) { - this.$lastPath = Element.addSvg('path', { d: 'M0 0' }); + this.$lastPath = addSvg('path', { d: 'M0 0' }); this.applyFill(this.$lastPath); this.applyStroke(this.$lastPath); this.$defaultGroup.append(this.$lastPath); @@ -248,7 +248,7 @@ export class Graphics { anchorY = 0, ) { if (!this.$lastPath) { - this.$lastPath = Element.addSvg('path', { d: 'M0 0' }); + this.$lastPath = addSvg('path', { d: 'M0 0' }); this.applyFill(this.$lastPath); this.applyStroke(this.$lastPath); this.$defaultGroup.append(this.$lastPath); @@ -270,7 +270,7 @@ export class Graphics { radius = 0, ) { if (radius >= 0) { - const c = Element.addSvg('circle', { + const c = addSvg('circle', { cx: x, cy: y, r: radius, @@ -296,7 +296,7 @@ export class Graphics { width = 0, height = 0, ) { - const e = Element.addSvg('ellipse', { + const e = addSvg('ellipse', { cx: x + width / 2, cy: y + height / 2, rx: width / 2, @@ -382,7 +382,7 @@ export class Graphics { } } } - const path = Element.addSvg('path', { d }); + const path = addSvg('path', { d }); this.applyFill(path); this.applyStroke(path); this.$defaultGroup.appendChild(path); @@ -411,7 +411,7 @@ export class Graphics { y += height; height = -height; } - const r = Element.addSvg('rect', { + const r = addSvg('rect', { x, y, width: width || 0, @@ -449,7 +449,7 @@ export class Graphics { y += height; height = -height; } - const r = Element.addSvg('rect', { + const r = addSvg('rect', { x, y, width, @@ -635,7 +635,7 @@ export class Graphics { */ lineTo(x = 0, y = 0) { if (!this.$lastPath) { - this.$lastPath = Element.addSvg('path', { d: 'M0 0' }); + this.$lastPath = addSvg('path', { d: 'M0 0' }); this.applyFill(this.$lastPath); this.applyStroke(this.$lastPath); this.$defaultGroup.append(this.$lastPath); @@ -650,7 +650,7 @@ export class Graphics { * @param y 一个表示相对于父显示对象注册点的垂直位置的数字(以像素为单位)。 */ moveTo(x = 0, y = 0) { - this.$lastPath = Element.addSvg('path', { d: `M${x || 0} ${y || 0}` }); + this.$lastPath = addSvg('path', { d: `M${x || 0} ${y || 0}` }); this.applyFill(this.$lastPath); this.applyStroke(this.$lastPath); this.$defaultGroup.append(this.$lastPath); diff --git a/src/danmaku/render/mode8/Display/Shape.ts b/src/danmaku/render/mode8/Display/Shape.ts index 31cc277..f0f50d6 100644 --- a/src/danmaku/render/mode8/Display/Shape.ts +++ b/src/danmaku/render/mode8/Display/Shape.ts @@ -1,6 +1,6 @@ -import { Element } from "../../../../utils/element"; import { BlurFilter } from "../filters/BlurFilter"; import { GlowFilter } from "../filters/GlowFilter"; +import { addSvg } from "../Utils/element"; import { IDisplay } from "./Display"; import { DisplayObject } from "./DisplayObject"; import { Graphics } from "./Graphics"; @@ -23,10 +23,10 @@ export class Shape extends DisplayObject { this.$filters = value; if (value) { this.#graphics.$defaultEffects.remove(); - this.#graphics.$defaultEffects = Element.addSvg('defs'); + this.#graphics.$defaultEffects = addSvg('defs'); for (let i = 0, len = value.length; i < len; i++) { const filter = value[i]; - const dFilter = Element.addSvg('filter', { + const dFilter = addSvg('filter', { id: `fe${i}`, x: '-50%', y: '-50%', @@ -34,7 +34,7 @@ export class Shape extends DisplayObject { height: '200cqb', }); if (filter instanceof BlurFilter) { - Element.addSvg('feGaussianBlur', { + addSvg('feGaussianBlur', { in: 'SourceGraphic', stdDeviation: `${filter.blurX} ${filter.blurY}`, }, dFilter); @@ -48,24 +48,24 @@ export class Shape extends DisplayObject { 0, 0, 0, cB / 256, 0, 0, 0, 0, 1, 0, ]; - Element.addSvg('feColorMatrix', { + addSvg('feColorMatrix', { type: 'matrix', values: cMatrix.join(' '), }, dFilter); - Element.addSvg('feGaussianBlur', { + addSvg('feGaussianBlur', { stdDeviation: `${filter.blurX} ${filter.blurY}`, result: 'coloredBlur', }, dFilter); - const m = Element.addSvg('feMerge', undefined, dFilter); - Element.addSvg('feMergeNode', { in: 'coloredBlur' }, m); - Element.addSvg('feMergeNode', { in: 'SourceGraphic' }, m); + const m = addSvg('feMerge', undefined, dFilter); + addSvg('feMergeNode', { in: 'coloredBlur' }, m); + addSvg('feMergeNode', { in: 'SourceGraphic' }, m); } } this.$host.append(this.#graphics.$defaultEffects); this.#graphics.$defaultGroup.remove(); let tGroup = this.#graphics.$defaultContainer; for (let i = 0, len = value.length; i < len; i++) { - const layeredG = Element.addSvg('g', { filter: `url(#fe${i})` }); + const layeredG = addSvg('g', { filter: `url(#fe${i})` }); layeredG.appendChild(tGroup); tGroup = layeredG; } @@ -102,10 +102,9 @@ export class Shape extends DisplayObject { } constructor(param: IDisplay) { - super(param, true, Element.addSvg('svg', { class: 'as3-danmaku-item' })); - const { width, height } = this.root.$host.getBoundingClientRect(); + super(param, true, addSvg('svg', { class: 'as3-danmaku-item' })); this.$host.style.cssText = `position: absolute; inline-size: 200cqi; block-size: 200cqb; translate: -50% -50%;`; - this.#graphics.$defaultContainer.style.cssText = `translate: ${width}px ${height}px;`; + this.#graphics.$defaultContainer.style.cssText = `translate: 100cqi 100cqb;`; this.$host.appendChild(this.#graphics.$globalDefs); this.$host.appendChild(this.#graphics.$defaultEffects); this.$host.appendChild(this.#graphics.$defaultContainer); diff --git a/src/danmaku/render/mode8/Player/Player.ts b/src/danmaku/render/mode8/Player/Player.ts index a446b96..fea4bb3 100644 --- a/src/danmaku/render/mode8/Player/Player.ts +++ b/src/danmaku/render/mode8/Player/Player.ts @@ -1,4 +1,5 @@ -import { Danmaku, DanmakuEvent, IDanmaku } from "../../.."; +import { Danmaku, IDanmaku } from "../../.."; +import { DanmakuEvent } from "../../../event"; import { DisplayObject, rootSprite } from "../Display/DisplayObject"; import { CommentData } from "./CommentData"; import { Sound } from "./Sound"; diff --git a/src/danmaku/render/mode8/Utils/element.ts b/src/danmaku/render/mode8/Utils/element.ts new file mode 100644 index 0000000..335a8c1 --- /dev/null +++ b/src/danmaku/render/mode8/Utils/element.ts @@ -0,0 +1,51 @@ +/** + * 生成svg系节点 + * + * @param tag 节点名称 + * @param attribute 节点属性对象 + * @param parrent 添加到的父节点 + */ +export function addSvg( + tag: T, + attribute?: Record, + parrent?: ParentNode, +) { + const svg = document.createElementNS("http://www.w3.org/2000/svg", tag); + attribute && (Object.entries(attribute).forEach(d => { + Object.hasOwn(attribute, d[0]) && svg.setAttribute(d[0], d[1]) + })); + parrent && parrent.append(svg); + return svg; +} + +/** + * 创建HTML节点 + * + * @param tag 节点名称 + * @param attribute 节点属性对象 + * @param parrent 添加到的父节点 + * @param innerHTML 节点的innerHTML + * @param top 是否在父节点中置顶 + * @param replaced 替换节点而不是添加,被替换的节点,将忽略父节点相关参数 + */ +export function addElement( + tag: T, + attribute?: Record, + parrent?: HTMLElement, + innerHTML?: string, + top?: boolean, + replaced?: Element, +): HTMLElementTagNameMap[T] { + const element = document.createElement(tag); + attribute && (Object.entries(attribute).forEach(d => { + Object.hasOwn(attribute, d[0]) && element.setAttribute(d[0], d[1]) + })); + innerHTML && element.insertAdjacentHTML('beforeend', innerHTML); + replaced + ? replaced.replaceWith(element) + : parrent && (top + ? parrent.insertAdjacentElement('afterbegin', element) + : parrent.insertAdjacentElement('beforeend', element) + ) + return element; +} \ No newline at end of file diff --git a/src/danmaku/style/render/mode8.css b/src/danmaku/render/mode8/index.css similarity index 83% rename from src/danmaku/style/render/mode8.css rename to src/danmaku/render/mode8/index.css index de33e30..b0e0927 100644 --- a/src/danmaku/style/render/mode8.css +++ b/src/danmaku/render/mode8/index.css @@ -12,15 +12,13 @@ outline: none; user-select: none; overflow: visible; - font-size: 12px; + font-size: 16px; color: #99a2aa; border: 1px solid #ccd0d7; border-radius: 4px; background-color: #ffffff; - padding-inline: 8px; - padding-block: 4px; - block-size: 20px; - line-height: 20px; + padding-inline: 20px; + padding-block: 10px; &:hover { color: #222; diff --git a/src/danmaku/render/mode8/index.ts b/src/danmaku/render/mode8/index.ts index 1ff4323..cb7da74 100644 --- a/src/danmaku/render/mode8/index.ts +++ b/src/danmaku/render/mode8/index.ts @@ -1,4 +1,5 @@ import { Danmaku, IDanmaku } from "../.."; +import { toastr } from "../../../toastr"; import { Script } from "./Script"; import { Parser } from "./Script/Parser"; import { Scanner } from "./Script/Scanner"; @@ -8,17 +9,18 @@ import { VirtualMachine } from "./Script/VirtualMachine"; export class Mode8 { /** 解码后的弹幕实例 */ - $vm: VirtualMachine; + #vm: VirtualMachine; constructor( - /** 弹幕管理组件 */ - protected $container: Danmaku, /** 弹幕数据 */ public $dm: IDanmaku, + /** 弹幕管理组件 */ + protected $container: Danmaku, ) { - this.$vm = new VirtualMachine(new Script($container)); + this.#vm = new VirtualMachine(new Script($container)); this.prase().catch(e => { + toastr.error('代码弹幕解析出错~', '这通常只会影响该条弹幕本身您可以放心忽略~', e) console.error(new SyntaxError('代码弹幕解析出错~', { cause: e }), $dm); }); } @@ -37,12 +39,12 @@ export class Mode8 { }[a] })); const p = new Parser(s); - this.$vm.rewind(); - this.$vm.setByteCode(p.parse(this.$vm)); + this.#vm.rewind(); + this.#vm.setByteCode(p.parse(this.#vm)); } async execute() { - this.$vm.execute(); - this.$vm.rewind(); + this.#vm.execute(); + this.#vm.rewind(); } } \ No newline at end of file diff --git a/src/danmaku/style/render/mode9.css b/src/danmaku/render/mode9/index.css similarity index 100% rename from src/danmaku/style/render/mode9.css rename to src/danmaku/render/mode9/index.css diff --git a/src/danmaku/render/mode9/index.ts b/src/danmaku/render/mode9/index.ts index 946ff12..ff4a5dd 100644 --- a/src/danmaku/render/mode9/index.ts +++ b/src/danmaku/render/mode9/index.ts @@ -1,4 +1,4 @@ -import { Danmaku, IAnimation, IDanmakuBAS, IDef, IDefAttrs, IPercentNum, ISet } from "../.."; +import { Danmaku, IDanmaku } from "../.."; import { customElement } from "../../../utils/Decorator/customElement"; import { AV } from "../../../utils/av"; import { Format } from "../../../utils/fomat"; @@ -210,6 +210,16 @@ export class Mode9 extends HTMLDivElement { } } + /** 每当元素添加到文档中时调用。 */ + connectedCallback() { + this.$dm.on = true; + } + + /** 每当元素从文档中移除时调用。 */ + disconnectedCallback() { + this.$dm.on = false; + } + private get xProportion() { return this.$container.$width / 100; }; @@ -232,13 +242,14 @@ export class Mode9 extends HTMLDivElement { private $style = document.createElement('style'); constructor( - /** 弹幕管理组件 */ - protected $container: Danmaku, /** 弹幕数据 */ public $dm: IDanmakuBAS, + /** 弹幕管理组件 */ + protected $container: Danmaku, ) { super(); + Mode9.pretreatDanmaku($dm); this.defs = $dm.defs; this.sets = $dm.sets; } @@ -360,7 +371,7 @@ export class Mode9 extends HTMLDivElement { } } - this.$container.appendChild(this); + this.$container.$host.appendChild(this); } /** 动画回调处理函数 */ @@ -688,4 +699,129 @@ export class Mode9 extends HTMLDivElement { this.percentObj2Num(this.$dm); this.init(); } -} \ No newline at end of file +} + +export interface IDanmakuBAS extends IDanmaku { + // 以下为BAS弹幕专用 + duration: number; + def2set: Record; + defs: IDef[]; + sets: ISet[]; + setsIntervals: Record; +} + +/** 通用属性 */ +interface ICommon { + x?: IPercentNum | number; + y?: IPercentNum | number; + zIndex?: IPercentNum | number; + scale?: IPercentNum | number; + duration?: number; +} + +/** 文本属性 */ +interface IText extends ICommon { + content?: string; + alpha?: IPercentNum | number; + color?: number; + anchorX?: IPercentNum | number; + anchorY?: IPercentNum | number; + fontSize?: IPercentNum | number; + fontFamily?: string; + bold?: IPercentNum | number; + textShadow?: IPercentNum | number; + strokeWidth?: IPercentNum | number; + strokeColor?: number; + rotateX?: IPercentNum | number; + rotateY?: IPercentNum | number; + rotateZ?: IPercentNum | number; + parent?: string; +} + +/** 按钮属性 */ +interface IButton extends ICommon { + text?: string; + fontSize?: IPercentNum | number; + textColor?: number; + textAlpha?: IPercentNum | number; + fillColor?: number; + fillAlpha?: IPercentNum | number; + target?: IButtonTarget; +} + +/** 按钮回调 */ +interface IButtonTarget { + objType: 'av' | 'bangumi' | 'seek'; + time?: number; + page?: number; + av?: number; + bvid?: string; + seasonId?: number; + episodeId?: number; +} + +interface IPath extends ICommon { + d?: string; + viewBox?: string; + borderColor?: number; + borderAlpha?: IPercentNum | number; + borderWidth?: IPercentNum | number; + fillColor?: number; + fillAlpha?: IPercentNum | number; +} + +export interface IDefAttrs extends IText, IButton, IPath { + width?: number; + height?: number; +} + +/** 弹幕事件 */ +export enum DanmakuEvent { + + /** 添加弹幕,注意参数为新增弹幕 */ + DANMAKU_ADD, + + /** 发送弹幕 */ + DANMAKU_SEND, +} + + +/** 弹幕事件对应的数据类型 */ +export interface IDanmakuEvent { + 0: IDanmaku[]; + 1: IDanmaku; +} + +export interface IAnimation { + delay: number; + duration: number; + easing: string; + group: number; + name: string; + valueStart?: IDefAttrs; + valueEnd: IDefAttrs; +} + +export interface IPercentNum { + numType: 'number' | 'percent'; + value: number; +} + +export interface IDef { + attrs: IDefAttrs; + name: string; + obj_type: string; + type: 'DefText' | 'DefButton' | 'DefPath'; + _reg_order: number; +} + +export interface ISet { + type: 'Serial' | 'Parallel' | 'Unit'; + items?: ISet[]; + attrs?: IDefAttrs; + defaultEasing?: string; + default_easing?: string; + duration?: number; + targetName?: string; + target_name?: string; +} diff --git a/src/danmaku/style/index.css b/src/danmaku/style/index.css deleted file mode 100644 index 44503d3..0000000 --- a/src/danmaku/style/index.css +++ /dev/null @@ -1,26 +0,0 @@ -@import url(./render/mode0.css); - -.b-danmaku { - position: absolute; - margin: 0; - padding: 0; - border: 0; - inset: 0; - container: danmaku / size; - pointer-events: none; - overflow: hidden; - - --animation-play-state: running; - - --font-size: 25px; - --text-shadow: #000; - --fontSize: 1; - --wraps: 1; - --fontBorder: 0; - --font-family: SimHei, "Microsoft JhengHei"; - --font-weight: bold; - --opacity: 1; - --like: 1; - --colorful: 1; - --full-screen-sync: 0; -} \ No newline at end of file diff --git a/src/danmaku/style/render/mode0.css b/src/danmaku/style/render/mode0.css deleted file mode 100644 index 6ff6fbb..0000000 --- a/src/danmaku/style/render/mode0.css +++ /dev/null @@ -1,121 +0,0 @@ -@import url(./mode1.css); -@import url(./mode4.css); -@import url(./mode5.css); -@import url(./mode6.css); -@import url(./mode8.css); -@import url(./mode9.css); - -.mode0 { - position: absolute; - line-height: 1.125; - white-space: pre; - pointer-events: none; - will-change: transform; - contain: content; - font-size: calc(var(--font-size) * var(--fontSize)); - font-family: var(--font-family); - font-weight: var(--font-weight); - opacity: var(--opacity); - display: flex; - align-items: center; - - &.wraps { - font-size: calc(var(--font-size) * var(--wraps)); - } - - .bg { - display: none; - inline-size: 1.74em; - block-size: 1.8em; - position: absolute; - inset-block-start: .2em; - inset-inline-start: 0; - z-index: -1; - } - - .bg1 { - display: none; - inline-size: 100%; - border-start-start-radius: 1em; - border-end-start-radius: 1em; - background: linear-gradient(90deg, #ffffff4d, #fff0); - position: absolute; - block-size: 1.8em; - inset-inline-start: 1.74em; - padding-inline-start: 1.6em; - margin-inline-start: -1.74em; - } - - .bg2 { - display: none; - inline-size: 18px; - block-size: 25px; - position: relative; - font-size: 0; - } -} - -@container danmaku style(--fontBorder: 0) { - .mode0 { - text-shadow: 1px 0 1px var(--text-shadow), 0 1px 1px var(--text-shadow), 0 -1px 1px var(--text-shadow), -1px 0 1px var(--text-shadow); - } -} - -@container danmaku style(--fontBorder: 1) { - .mode0 { - text-shadow: 0px 0px 1px var(--text-shadow), 0 0 1px var(--text-shadow), 0 0 1px var(--text-shadow); - } -} - -@container danmaku style(--fontBorder: 2) { - .mode0 { - text-shadow: 1px 1px 2px var(--text-shadow), 0 0 1px var(--text-shadow); - } -} - -@container danmaku style(--animation-play-state: paused) { - .mode0 { - animation-play-state: paused !important; - } -} - -@container danmaku style(--like: 1) { - .mode0.like:not(.wraps) { - padding-block: .6em; - padding-inline-start: .6em; - padding-inline-end: 2.5em; - line-height: 1; - - .bg, - .bg1, - .bg2 { - display: inline-block; - - svg { - inline-size: 100%; - block-size: 100%; - } - } - - .content { - margin-inline-start: .4em; - } - } -} - -@container danmaku style(--colorful: 1) { - .mode0.colorful { - background-size: cover; - text-shadow: none; - background-image: linear-gradient(to right, #F2509E, #308BCD); - background-clip: text; - -webkit-text-fill-color: #ffffff; - -webkit-text-stroke: 2px transparent; - } -} - -@container danmaku style(--full-screen-sync: 1) { - .mode0 { - font-size: calc(var(--font-size) * var(--fontSize) * var(--wraps)); - } -} \ No newline at end of file diff --git a/src/danmaku/style/render/mode1.css b/src/danmaku/style/render/mode1.css deleted file mode 100644 index 703b71e..0000000 --- a/src/danmaku/style/render/mode1.css +++ /dev/null @@ -1,5 +0,0 @@ -@keyframes dmMode1 { - to { - transform: translateX(-100%); - } -} \ No newline at end of file diff --git a/src/danmaku/style/render/mode4.css b/src/danmaku/style/render/mode4.css deleted file mode 100644 index 30b8c44..0000000 --- a/src/danmaku/style/render/mode4.css +++ /dev/null @@ -1,5 +0,0 @@ -@keyframes dmMode4 { - to { - opacity: 0; - } -} \ No newline at end of file diff --git a/src/danmaku/style/render/mode5.css b/src/danmaku/style/render/mode5.css deleted file mode 100644 index ee35e6b..0000000 --- a/src/danmaku/style/render/mode5.css +++ /dev/null @@ -1,5 +0,0 @@ -@keyframes dmMode5 { - to { - opacity: 0; - } -} \ No newline at end of file diff --git a/src/danmaku/style/render/mode6.css b/src/danmaku/style/render/mode6.css deleted file mode 100644 index b696fd4..0000000 --- a/src/danmaku/style/render/mode6.css +++ /dev/null @@ -1,5 +0,0 @@ -@keyframes dmMode6 { - to { - transform: translateX(100cqw); - } -} \ No newline at end of file diff --git a/src/danmaku/test/av297197.xml b/src/danmaku/test/av297197.xml deleted file mode 100644 index 57417de..0000000 --- a/src/danmaku/test/av297197.xml +++ /dev/null @@ -1,6259 +0,0 @@ -chat.bilibili.tv4699700k-v本视频全程黑屏,所有画面都是弹幕组成,初次观看推荐屏蔽普通弹幕 -我去! -太闪了! -神の弹幕 -技术宅拯救世界。。 -打卡表示 诚意 -这视频好牛啊 -碉堡了 -神弹幕 -逆天了 -怎么做到的 -神啊,这~~不~~科~~学~~ -我勒个去 -神级弹幕 -壮哉我大天朝 -这是什么原理 -实验证明这是一帧一帧做出来了弹幕。。。 -实验证明这是一帧一帧做出来了弹幕。。。 -实验证明这是一帧一帧做出来了弹幕。。。 -实验证明这是一帧一帧做出来了弹幕。。。 -碉堡了 -实验证明这是一帧一帧做出来了弹幕。。。 -厉害,弹幕凶残 - -弹幕碉堡了 -跪了 -长跪不起 - -弹幕!!!!!!!!!!!!!!!!!! -这不科学 -每日打卡 -每日打卡 -这不科学= =太凶残了。。 -milk -wooooo~~~~~ -大爱字幕君 -!!! -这是秀弹幕的么 -我根本不会发弹幕嘛混蛋 -真心不科学! -颤抖.................. -每次听都很美啊 -好屌的, -我是看了标题滚进来的/n -牛逼弹幕 -这都可以1 -碉堡了 -神弹幕 -这弹幕 -美爆啦 -逆了个天00 -绮礼么,麻婆么 -这是人干的事的么?? -这弹幕要逆天啊! -神弹幕!可惜全屏没效啊 -给跪了。。。。 -QAQ -膜拜 -这是弹幕.. -我去 -我去.. -我去.. -打卡+1 -让人目瞪口呆的弹幕。。。 -不科学 -凶残啊 -神弹幕,跪了 - -美丽の物 -真的给跪了OTZ -a/n/n/n/n/n -mv好 -美丽之物 -啊嘞??? -弹幕都那么神 UP主高能 -看傻了 -日常打卡 -CPU突破天际了。。 - -何等凶残 -为什么这么强力 -神弹幕 - -技术宅拯救世界 -给神弹幕跪了 -碉堡了!!!!!!! -太帅了 -高能字幕 -NB -神级弹幕 -好牛的弹幕 -不。。这不是弹幕 -。。 -不会发弹幕+1 -一关闭弹幕就全没了 -..好强 -不错 -太神了…… -这两年B站白发这么多弹幕了…… - -不知道说什么了…… -不敢发弹幕了啊魂淡!!!! -无敌啊 这神弹幕 -为什么很感动啊QAQ -UP主请收我为徒!!>.< -感谢UP主分享这么好的视频。- = -跪拜~~OTZ -窝的弹幕果然弱爆了 -火焰碉堡了!!!!!!!!!!!!!!! -突然哭了TUT -= =怎么做到的 -弹幕软件?? -这不是FLASH么= = -.. -跪了 -卧槽!!! -这不科学这不科学!!!! -我跪了 神弹幕 -逆天人才 -不自觉的就跟着唱了起来~~~~要哭了~~ -阿姨西铁路 ~~~~~~~~~~~~~~~~~~~~~~ -真的是弹幕吗。。。 -这。。。。 -藏族自治州......... -mmmm -完全,,,哔~~~~~~~~~~~~~~ -nandei -技术宅伤不起啊。。。。 -糟了。。感觉腐朽的我在消失=。= -这怎么办到的!! -泪了 -wusou。。 -123 -(*゜ロ゜)ノ好厉害!! -膜拜职人 -不科学!!! -职人辛苦了 -鸟肌 -来膜拜弹幕。。。。。。 -科学君又死了一次-A- -sh!!!!!!!! -SoundHorizon!!!! -好美的弹幕1!!QAQ -美死了 -给跪了!!!!!!!!!! -给跪了 -不科学 -ORZ -口琴- = -结束了 -不可直视啊!!!!!!!!!!!!!!! -怎么做到的 -噢噢噢噢 -太神了这弹幕 -碉堡了! -卧槽 马萨卡 -这或是弹幕? -美物啊 -- -- ->< -好厉害 - -好美TWT/n -弹幕好神T口T -这不科学啊!!!!!!!!!!!! -跟着唱+3 -田村的声音吧 -最后一句是田村说的? - -技术宅拯救世界 -触手 -壮哉我大SH -打卡 -牛逼 -更好 -碉堡了 -跪了·居然一句也听不懂········· -这个很强大。。。。。 神啊 。。。。。 -我服了 。。。。。 -不解释,神级 -GJ!! -给神弹幕跪了 -卧槽~碉堡了啊!!! -这是弹幕?? -强悍啊 -我天朝果然神人多.. -来人,赏 -我擦 神弹幕~! -硬币- - 不给对不起那一瞬间的感动 -给跪了啊= = -看哭了= = -我看哭了...................... -丢了硬币 再看一遍- - -看弹幕感动了 - - -太美了 -=wwwwww= -UP主神化啦 -好赞 -跪了 -好怀念 -压力山大 -.. -科技拯救 未来 。。 -听到歌声就感到被治愈了有木有~~~ -膜拜神弹幕 -太凶残了。。 -YUUKI!! -Yuuki -我是来膜拜的 -给跪了 -j -碉堡了 -太强大了 -跪了 -第一? -呜哇哇哇!!! -作为国民很自豪…… -催泪 -神弹幕。。。。 -真的尼玛是弹幕啊 - = -up主碉堡了。。。 -尼玛这年头做弹幕,都开挂了吧。。。 -11骚年 你们好 -好可怕、、 -不科学的弹幕 -才看完就被顶上来了 -这是高级碳亩宣传 绝对 -感动了,怎么办 -看不见本ID最后几个字母的都是⑨~ -确实不舍得破坏。。。 -= = 这不科学 -好厉害QAQ -湿了 -这全是弹幕啊。 -给跪了。。。 -这世界从没科学过... -好美! -泪目中 -你得多闲啊~~~ -这弹幕…… -好牛X的牡丹 -留爪... -. -好凶残 -神弹~ -OSU撸这首的都是触! -神弹幕。。。。 -这个这个,弹幕不可以这么凶残的啊。。。 -这个。。。。。。。。 -真美 -.. -神弹幕。。。。。。。。。。 -碉堡了。。。。。。。。。。。。。 -好强大。。。 -...神人 -再听一遍!! -确定这是弹幕咩? -cest = 法语=是的意思 -神弹幕万岁 -我的手机铃声。。 -神弹幕! -技术宅毁灭科学 -技术宅毁灭科学 -漂亮,真的很漂亮 -泪目 -roman里面好像用的都是法语 -2012年7月29日 10:36:33 -无敌的神弹幕 -- -,神一样的up主 -又顶上来了............... -这一段禁普通弹幕了? -厉害。。 -这一段美绝了!Q皿Q -各种伤感QAQ -haoting -卧槽啊 -好厉害啊 -技术宅啊 -大家注意这里是音阶...... -无语 -这个真心碉堡 -技术宅拯救世界!!! -神弹幕...= =.好凶残 -碉堡了 -牛啊这。。 -何等凶残! -何等凶残.. -还不错,我被攻略了 -不忍发弹幕 -马赛克化的小姑娘 -太帅了。。。 -好厉害 -好厉害啊 -膜拜弹幕 这不科学。。。。 -我又来了 -每次都会被弹幕打动 -没有弹幕 -膜拜,但又不忍破坏,好纠结啊 -老物在次上榜 -屏蔽弹幕真的啥都没了! -wocao -花火大会 -好美 -!!! -up主碉堡! -四核50%cpu,压力大。。 -UP主太屌了…… -CPU... -已经不是弹幕了。。 -我这是4核啊。CPU50%。。 -神弹幕 -总觉得旋律好熟 -没看一遍都让我无力,白混了。 --。- -这些全是弹幕啊》》》。 -如果清弹幕会怎样。。= = -最喜欢这个世界了 -QAQ这不科学 -神弹幕 -UP主神人!!! -这弹幕怎么做出来的? -我靠 这是啥 -我擦 哑巴里都 -视屏V5 -这是什么啊 -这弹幕。。。。。 -四核CPU42% -打卡签到 -なるほどですね -弹幕啊。。 -腐朽的我迷失了 -太牛逼了! -wo cao -高能了 -漂亮 -存货。 -好凶残的弹幕 --神BGM- -小妹妹-实弹木马? - -神了 -我去 -好美啊!! -CPU30-40%表示压力不算太大 -碉堡了 -哭了…… -太帅气了 -TUT 大爱SH -给跪了…… -哭了..眼疲劳吧 -碉堡了= = -这神马 点阵图啊 -卡哇伊 -神弹幕 -~ -看见sound horizon就滚进来了... -太碉堡了... -科学哭了!! -我来围观弹幕 -果然神弹幕围观不解释 -看不见本ID最后几个字母的都是⑨~ -删弹幕了? -另一个观众你好~ -美爆了 -.. -不科学啊!!!!!!!!!! -这不科学。。 -我去这不科学!!!! -太牛了。。 -…… -不科学…… -神级技术宅 -想哭。。 -好! -给大神跪了QAQ -QAQ -好厉害啊! -这是人做的是么!!! -牛X啊 -!!! -神弹幕啊 -mmmm -弹幕神了 -神の神の神の神の神の神の神の神の神の神の -屌炸 -太牛了 -神了啊 -膜拜神弹幕 -漂亮 -好强大卧槽。。 -卧槽、真心碉堡了= = -我去。。。。 -来打卡了 -膜拜! -碉堡了。。 -美しきもの~~ -如何做到的。。。 -super............... -弹幕呢 - -他的口琴把琴谱打出来了……远目 -88% -壮哉我大SH -CUP38= = -蝉鸣如时雨 -弹幕逆天了 -这不科学啊!!!!! -支持 -本要被烧坏了 -弹屏强大!!! -幻想乐团大爱! -神弹幕- - -换个姿势吧 -哈哈 -一到重要的片段大家都不弹幕,很赞 -gj -膜拜 -神弹幕。。。 -貌/n似/n很/n厉/n害/n的/n样/n子/n/n/n/n/n/n -这/n 不/n 科/n 学 -↑不是大家都不留言 是留言被背景弹幕挡住了 -dang -神弹幕 -怀念 -泪目 -吐槽这只口琴,又不是复音口琴又不是十孔的 - -国民来顶,碉堡了 -. -··············· -我来搞破坏 -双核 CPU 30 - 给跪 -我这挂台风 -厉害。。。 -好美啊\(^o^)/~ -神啊!!! -a ki 秋~~~~ - -我喜欢这个歌!!!! -流弊 -卡了 -弹幕呢 - -跪。 - -擦 神弹幕啊 -弹幕之神嫁给我吧! -啊啊啊啊啊啊啊啊啊 -强制使用弹幕 -又被治愈了 -平时都是玩弹幕 这次被弹幕玩了 -弹幕怎么做到的、 -膜拜大神 -我就遮掩被攻略了 -无敌的弹幕 -一定要顶上去啊~~ -卧槽 -凶残+1 -神弹幕、 -这神弹幕膜拜啊 -00. -内牛满面啊 -牛…… -ce -这TM不科学 -吓尿了 -泪目啊 -感动 -有一凶残 -碉堡了碉堡了碉堡了!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - -很久以前就很喜欢这首歌了~ -其实我是来听歌的 -神弹♂ -神了 -看到SH就滚进来了 -经典~ -好久没听过这首歌了的说 -神 啊。。。 -其实这歌挺惨白的。 -弹幕=泪目=跪了膜拜 -口风琴碉堡了 -跪了... -打卡 -,,, -神马情况啊,求解说 -欸这是什么歌 -这是神马歌 -萌妹子 -我擦神弹幕啊 -美丽之物 -卧槽 -我靠 -尼玛这不科学啊,怎么可能 -好美的雪花啊 -菝葜 -菝葜 -怎么办我想死我想死啊 -尼玛尼玛啊这技术啊尼玛啊 -高能:“神之光” -卧槽彩虹啊 -尼玛这不是人类能做出来的 -太不科学了 -每日打卡 -美死了 -弹幕菌在哪? -有背景的人才行啊 -弹幕不科学 -弹幕不科学 -太多牛B给跪了太多牛B给跪了太多牛B给跪 - -.. -垃圾双核表示爆表了 -好晕~~ -卧槽屏蔽弹幕就什么都看不见了啊! -真是弹幕啊……orz -跪拜技术宅 -我艹 这原来只是弹幕啊 -神弹幕逆天了! -这些樱花是怎样成型的啊啊啊啊啊 -此曲每闻必唱= =! -可爱的弹幕~~~ - -怎么做的啊 - -神弹幕。。。 -我弹 -弹幕真好玩 -技术宅啊…… -你好... -神弹幕啊=X= -洗脑on -跪了 -哭了我去QAQ -CPU20% 大丈夫 -各种美啊~~~~~~~ -~♩ -神弹幕!! -是镶嵌在视频里的吧 -神弹幕 -神弹幕! -膜拜字幕大神 -真舍不得破坏这个美感啊 -跪了!! -==强大了 -碉堡了 -把科学还给我!! -哦不好厉害啊于是 -各种无力啊围观 -好可怕啊口胡 -怎么做的 -各种不科学啊 -那是怎么做的 -弹幕谁信 -神马还真的是弹幕耶 -好可怕QAQ -口琴君QAQ -这不科学。。 -美哭了QAQ -弹幕之神啊我去,这是怎么做到的 -好喜欢~ -这TM的不科学啊Σ(`д′*ノ)ノ ! -卡爆了! -全屏看太美了。 - - - - 这么玩的么 -神弹幕啊~围观之 -神弹幕,跪拜 -好久没看见神弹幕了 -看来我是要砸硬币了 -泪目 -被清弹幕了··· -太凶残了。。 -啊啊啊啊啊 -膜拜大神 -一直只有10%一下。。。 -好凶残 -≥﹏≤ -●▂● -@(一-一)@ -碉堡了 -弹幕.... -怎么做到的= = -0. -屌啊 -碉堡了啊 次奥 -歌不错 -思过意 -卧槽 -啊啊啊 -只有神知道的弹幕世界... -osu- - -hao -这不科学 -神! -好美的歌声 -Orz -这 弹幕要 逆天啊 -这不科学。。。 -神一样的弹幕啊啊啊啊! -听了无数遍的歌...最喜欢 没之一 -天啊神弹幕 -歌好美 -全程弹幕= = -神人啊啊 -已经忘记弹幕是什么 了 -好き -好厉害QUQ -复习复习。。 -全程都跟着唱了啊。。 -太不科学。。。 -. -双核20%的笑了 - -mayaa!!! !!!! -神弹幕+神曲 -又首页了 -神了。。 -[膜拜]_| ̄|○ → _|\○_ → _/\○_ → ____○_ -泪目啊 -╭( ̄m ̄*)╮ 直接跳到這裡?? -神弹幕 -厉害 -这花怎么弄的!0.0 -跪拜大神 -谈科学你就输了 -关掉弹幕吓尿了。。。。 -日常了 -好喜欢这首歌 -大爱这首歌 -to be way-0801 - -卧槽高级弹幕! -敢挖我就敢顶 -弄这个弹幕要弄多久。 -直接跪地膜拜。。 - -这不科学...... -神了 -←c'est在法语里是“这是”的意思好吧 -看到美丽之物就滚来了 -我去这些都是弹幕!!! - 救了个命 出不去了 -haha -什么情况啊 这是 -r -哎呦又被顶上来了?歌我是不想听了,顶顶就行 -弹幕菌好强大 -好凶残的弹幕菌 -四锅伊! -四锅伊 -古墓又一次升起来了!!!!! -前来膜拜神弹幕~~~~~!!!!!!!!!!!!!!!!!!! -哭过的 路过。。。。。。。。。。。。。。。。。。。 -好厉害的样子啊 -膜拜 -真心美了。。。。。。。。。 -dingyige -我已近麻木了~ -日常打卡= = -美死了 哭了 -看弹幕看流泪了- - 膜拜 -真心好! -我不会发弹幕 -顶!美丽之物啊 最爱 没有之一 -科学什么的节操掉尽 -这太魔法了。 -. -~~~~~ -怒跪!!! -膝盖都烂掉了。。 -我擦。。。 -美爆了 -高技術啊,前來膜拜 -osu..... -真的吗? -0.0. -跪了 -擦来看神弹幕 -啊啊啊/n -卧槽这弹幕这不科学!! -卧槽弹幕 -坑爹的时钟 -后面发弹幕 -ddddddd -太漂亮了 -销魂曲 -消魂曲 -我是神经 -牛XXXXXXXXXXXXXXXXXXX -屏蔽了所有弹幕就真的黑了!!!!不行我已经不信这么高技术的弹幕了…… -UP主求视频00 -纯粹的听歌向 泪目 你们懂得 -Hello I am a moron -^_^ -=3 -=w= -这女的弹幕啊- -神了!!! -弹幕被清了。。。? -这竟然是弹mmmmmmmmmmmmmmmmmmm -=3 -Hello world!~ ->:D -Love this song!~ ^w^ -=w= ->:D -:3 -LA!~ :D -^w^ -Hello world!~ :D -=w= -\^o^/ -:3 -Hello I am a dork -LA LA LA -U dunnos! D: -=w= -lolololololololololo -Rainbows FTW! -SONG IS AWESOME -:U -<______> -Night FLOWER -PerfectCherryBlossom -=3 =3 =3 =3 =3 =3 =3 -__w__ -PINK!!!!11 -Touhou~ ->:D -CCCHHHHEEEENNNNNNN!! -Flandre Scarlet -OwO -:D -Cookie~ =w= -Love this song!~ ^w^ -T^T -Rainbows!!!!! -Hey peeps! :D -:D -;___; -Sad song is sad! ;_; -:D -XD -XD XD XD XD XD XD XD -Comment sapm!!!!! -^W^W^W^W^W^W^W^W^W^W -Any Touhou players h -=w= -Touhou FTW! -Anyone want a cookie -Waffles! -Trololololol. -Hello!~ -:D -Hello world! -神~~~~~~~~~ -好帅气...............!! -跪了 -帅气!!!!....... -帅哭了..... -膜拜了! -我是来看热闹的说 -这歌被天朝盗了几遍 -这首歌调子很好但个人认为歌词和故事的关联十分少啊 -好立体我都快晕了 -啊啊啊啊啊好棒 -这是让我们看弹幕听着BGM是吧!这是让我们看弹幕听着BGM是吧! -好厉害啊真的 -好厉害啊 -好厉害呢 -太棒 -啊啊啊啊好厉害 -刷冥王的什么心态!!! -日常打卡 -美shi了_(:з」∠)_ -Flag 立起来了! -哎呀呀呀呀 -SH -啊啊啊 -这个弹幕 -如此强大 -其实好久以前就听过这个了 -竟然有故事的 -神弹幕 -=3 -复习来了~ -技术宅拯救世界啊!!! -卧槽。吓尿了。电脑感觉有点卡了 -完全已经不知道弹幕是什么了 -(:3 ∫ -再也不相信弹幕了 -神弹幕!! -叼爆了 -这怎么了 -拯救世界啊 -- - -又上首页了 -科技拯救未来 -听过就进来了... -太神了吧。。。。。。 -只有600硬币不科学啊~! -哭了 -都在擦眼泪 发毛的弹幕 -老早的歌了 -老歌了。、、 -神曲加神弹幕 -帅没了...... -面条泪.. -卡住了 -啦啦啦 -= =神弹幕 -恩,居然又见一起的打卡哥~ -膜拜 -吓尿 -包括Laurant的份一起 -另一个观众 你好啊~ -神同步 -满足了 -这尼玛略碉堡啊! -表示略卡 -关掉弹幕在来一遍 -好吧 关掉之后什么都没了 -baga 当着我看字幕了 -不科学 碉堡了字幕 -kuso 收藏的格子不够了 -跪了 -真心膜拜 -高能 爆表了 -火焰碉堡了 -美爆!!! -做了半年的神弹幕。。。。 -目前所见背景最漂亮的弹幕 -我们弱爆了。。。 -CPU哭了 -噢艹!美爆! -UP住自制好强。。。。 -哭了 -美啊! -很梦幻 -双核41%。。 - -好好强大…… -来膜拜神弹幕 -很好玩吼 -这神弹幕又被顶上来了啊 -又挖上来? -碉堡的弹幕碉堡的up主 -神字幕吖~~~~ -啊哈哈哈哈哈 -哈哈哈哈哈哈 -不对这已经不是弹幕了= = -订报了啊 -好厉害wwww -bleach -自己关掉弹幕就知道了 -每次过来看都很碉堡 -肿么弄的 QAQ 好厉害 -QAQ感动了 -手机播放表示只能跑30帧左右。。 -手机测试刚刚卡爆了。。。。。。 -又来了我。 -好 - - -使用率百分之六十,渣CPU给跪了 -Hello world!~ -Hai!~ -Hello world!~ -这是啥 -打卡 - -在b站的第一次啊 -壮哉我大银河! -65 -恒星? -全是高级弹幕 -重复的。。 -不科学 -(T-T) -Q0Q -历/n害 -碉堡了 -碉堡了!!!!!!!!!!!!!!!!! -大爱!!!!!!~~~~~~~~~~~~ -开完系 -犀利 -Soundhorizon -这首歌最喜欢了~~ -(>﹏<)戴斯key -- - -碉堡了 -@花花 -@花花 -@花花 -膜拜! -关掉弹幕瞬间吓尿 -太不科学了 -niu -卡了 -神啊。。。。/n -光是弹幕我就收藏了。。不用说这首歌了。。 -双核CPU用了我40% -技术宅拯救世界!!!!!! -碉堡了 -神人 -好厉害!!! - -不科学 -good -厉害 -卧槽……/n -卡了。。。。。 -这歌好煽情啊 -卧槽给跪了 -=.= -看完之后顿时感觉以前从未发过真正的弹幕 -四国亿。。。。 -真好听。。 -真心美 -科学吗? -神弹幕!! -爪机卡了o(︶︿︶)o -别说弹幕了我用幻灯片都做不出来这效果 -原来真的是弟控之歌。。。。 -40% -关掉弹幕吓死了 -膜拜 -为什么弄全屏这么卡- - -全屏卡死- -。。 -神曲敢顶就敢看 -这是弹幕吗,我艹 -暖床 我爱死你了 -给跪 -美丽之物 -擦泪++++ -看看能不能顶上去 -好喜欢这首歌OTL -神弹幕 -ROMAN是法语主打,卖肾是德语主打,8TH会是啥?拉丁?希伯来?意大利? -打卡 -打卡嘤嘤嘤 -QMQ逆天了 -我艹!这弹幕。。逆天了! -8核CPU 勉强运行。。 -大触…… -原来后面还有啊... -果然是弹幕 -打卡複習 -眼睛裡進彈幕了 -神字幕 -给跪了 -尼玛 尼玛 这技术 -神了 -CPU 10%以下笑而不语 -看到SH我就进来了 -跪了 -看哭了 -神弹幕啊 -神 啊 - 喜欢 -虽然是老物了 ,不过美丽之物永远美丽 -美丽的万物 -春夏秋冬4季 -碉堡了有木有!!!!!!!!!!!!!! -手机党笑而不语。。 -好美 -神弹幕 -这真的是弹幕?! -技术宅 -碉堡了 -白发这么久的弹幕了 -给跪! -美哭了 -最近好多牛逼弹幕啊 -还厉害 -天呐~~~~宅技术拯救世界啊 -神制作,辛苦了 -我的CPU! -最爱的歌QAQ -双核电脑伤不起啊,卡了 -看得蓝屏重启是什么水平? -UP主赛高 -火焰太霸气了,爪机身亡 -竟然是弹幕 -关弹幕字幕也没了 -我被弹幕玩弄了 -传说中的高级弹幕??? -手机看不到--... -厉害 -超级神 -卡了→_→ -你为什么这么叼 -我怎么又进来了 -手机崩溃了 -膜拜 -逆天 -k -看来5410解码能力不错 -["118","106","0.2-0.2","10","君","350","0","118","106","500","0","false","黑体","0"] -["106","106","0.2-0.2","10","君","350","0","106","106","500","0","false","黑体","0"] -["112","112","0.2-0.2","10","君","350","0","112","112","500","0","false","黑体","0"] -["112","100","0.2-0.21","10","君","350","0","112","100","500","0","false","黑体","0"] -["109","103","0.2-0.2","10","君","350","0","109","103","500","0","false","黑体","0"] -["115","103","0.2-0.2","10","君","350","0","115","103","500","0","false","黑体","0"] -["109","109","0.2-0.2","10","君","350","0","109","109","500","0","false","黑体","0"] -["115","109","0.2-0.2","10","君","350","0","115","109","500","0","false","黑体","0"] -["112","106","1-1","10","君","350","0","112","106","500","0","false","黑体","0"] -["108","96","1-0","2.5","█","0","0","108","96","500","0","false","黑体","0"] -["172","103","0.2-0.2","8.5","の","10","0","172","103","500","0","false","黑体","0"] -["184","103","0.2-0.2","8.5","の","10","0","184","103","500","0","false","黑体","0"] -["178","109","0.2-0.2","8.5","の","10","0","178","109","500","0","false","黑体","0"] -["178","97","0.2-0.2","8.5","の","10","0","178","97","500","0","false","黑体","0"] -["175","106","0.2-0.2","8.5","の","10","0","175","106","500","0","false","黑体","0"] -["181","106","0.2-0.2","8.5","の","10","0","181","106","500","0","false","黑体","0"] -["175","100","0.2-0.2","8.5","の","10","0","175","100","500","0","false","黑体","0"] -["181","100","0.2-0.2","8.5","の","10","0","181","100","500","0","false","黑体","0"] -["178","103","1-1","8.5","の","10","0","178","103","500","0","false","黑体","0"] -["170","101","1-0","2.5","█","0","0","170","101","500","0","false","黑体","0"] -["1","78","1-1","4.5","██████/n██████","0","0","1","78","500","0","false","黑体","0"] -["1","145","1-1","4.5","██████","0","0","1","145","500","0","false","黑体","0"] -["1","78","1-1","4.5"," ██████/n ██████","0","0","1","78","500","0","false","黑体","0"] -["1","145","1-1","4.5"," ██████","0","0","1","145","500","0","false","黑体","0"] -["3","199","1-1","7","__/n__/n__/n__/n__","330","0","3","199","500","0","false","黑体","0"] -["24","187","1-1","7","__/n__/n__/n__/n__","335","0","24","187","500","0","false","黑体","0"] -["42","179","1-1","7","__/n__/n__/n__/n__","338","0","42","179","500","0","false","黑体","0"] -["63","171","1-1","7","__/n__/n__/n__/n__","341","0","63","171","500","0","false","黑体","0"] -["81","165","1-1","7","__/n__/n__/n__/n__","344","0","81","165","500","0","false","黑体","0"] -["103","159","1-1","7","__/n__/n__/n__/n__","347","0","103","159","500","0","false","黑体","0"] -["126","154","1-1","7","__/n__/n__/n__/n__","350","0","126","154","500","0","false","黑体","0"] -["149","150","1-1","7","__/n__/n__/n__/n__","353","0","149","150","500","0","false","黑体","0"] -["173","147","1-1","7","__/n__/n__/n__/n__","356","0","173","147","500","0","false","黑体","0"] -["193","146","1-1","7","__/n__/n__/n__/n__","0","0","193","146","500","0","false","黑体","0"] -["214","146","1-1","7","__/n__/n__/n__/n__","3","0","214","146","500","0","false","黑体","0"] -["237","147","1-1","7","__/n__/n__/n__/n__","6","0","237","147","500","0","false","黑体","0"] -["258","149","1-1","7","__/n__/n__/n__/n__","9","0","258","149","500","0","false","黑体","0"] -["280","153","1-1","7","__/n__/n__/n__/n__","13","0","280","153","500","0","false","黑体","0"] -["301","158","1-1","7","__/n__/n__/n__/n__","16","0","301","158","500","0","false","黑体","0"] -["316","163","1-1","7","__/n__/n__/n__/n__","13","0","316","163","500","0","false","黑体","0"] -["330","167","1-1","7","__/n__/n__/n__/n__","10","0","330","167","500","0","false","黑体","0"] -["346","170","1-1","7","__/n__/n__/n__/n__","7","0","346","170","500","0","false","黑体","0"] -["360","172","1-1","7","__/n__/n__/n__/n__","4","0","360","172","500","0","false","黑体","0"] -["376","174","1-1","7","__/n__/n__/n__/n__","1","0","376","174","500","0","false","黑体","0"] -["390","175","1-1","7","__/n__/n__/n__/n__","357","0","390","175","500","0","false","黑体","0"] -["407","174","1-1","7","__/n__/n__/n__/n__","354","0","407","174","500","0","false","黑体","0"] -["423","173","1-1","7","__/n__/n__/n__/n__","350","0","423","173","500","0","false","黑体","0"] -["440","170","1-1","7","__/n__/n__/n__/n__","347","0","440","170","500","0","false","黑体","0"] -["455","167","1-1","7","__/n__/n__/n__/n__","344","0","455","167","500","0","false","黑体","0"] -["475","162","1-1","7","__/n__/n__/n__/n__","344","0","475","162","500","0","false","黑体","0"] -["50","191","1-1","0.2","♪","350","0","50","191","500","0","true","宋体","0"] -["122","188","1-1","0.7","♬","355","0","122","188","500","0","true","宋体","0"] -["174","151","1-1","1.2","♩","0","0","174","151","500","0","true","宋体","0"] -["247","173","1-1","2.1","♫","0","0","247","173","500","0","true","宋体","0"] -["332","192","1-1","2.7","♭","4","0","332","192","500","0","true","宋体","0"] -["411","176","1-1","2.7","♬","4","0","411","176","500","0","true","宋体","0"] -["55","202","1-1","4.5","大","338","0","55","202","500","0","true","黑体","0"] -["47","199","1-0","0.5","大","338","0","47","199","500","0","true","黑体","0"] -["110","199","1-1","4.5","好き","350","0","110","199","500","0","true","黑体","0"] -["101","195","1-0","0.5","好き","350","0","101","195","500","0","true","黑体","0"] -["185","160","1-1","4.5","な","0","0","185","160","500","0","true","黑体","0"] -["180","154","1-0","0.5","な","0","0","180","154","500","0","true","黑体","0"] -["237","176","1-1","4.5","この","13","0","237","176","500","0","true","黑体","0"] -["230","167","1-0","0.5","この","13","0","230","167","500","0","true","黑体","0"] -["318","200","1-1","4.5","旋律","5","0","318","200","500","0","true","黑体","0"] -["308","187","1-0","0.5","旋律","5","0","308","187","500","0","true","黑体","0"] -["393","200","1-1","4.5","(Mélodie)","350","0","393","200","500","0","true","黑体","0"] -["388","200","1-0","0.5","(Mélodie)","350","0","388","200","500","0","true","黑体","0"] -["8","114","1-1","4.5","███████","0","0","8","114","500","0","false","MS UI Gothic","0"] -["8","114","1-1","4.5"," ██████","0","0","8","114","500","0","false","MS UI Gothic","0"] -["-5","-5","1-1","10","███████","0","0","-5","-5","500","0","false","MS UI Gothic","0"] -["-5","-5","1-1","10"," ██████","0","0","-5","-5","500","0","false","MS UI Gothic","0"] -["-5","10","1-1","10","███████","0","0","-5","10","500","0","false","MS UI Gothic","0"] -["-5","10","1-1","10"," ██████","0","0","-5","10","500","0","false","MS UI Gothic","0"] -["-5","32","1-1","10","███████","0","0","-5","32","500","0","false","MS UI Gothic","0"] -["-5","32","1-1","10"," ██████","0","0","-5","32","500","0","false","MS UI Gothic","0"] -["-5","63","1-1","10","███████","0","0","-5","63","500","0","false","MS UI Gothic","0"] -["-5","63","1-1","10"," ██████","0","0","-5","63","500","0","false","MS UI Gothic","0"] -["-5","100","1-1","10","███████","0","0","-5","100","500","0","false","MS UI Gothic","0"] -["-5","100","1-1","10"," ██████","0","0","-5","100","500","0","false","MS UI Gothic","0"] -["-5","124","1-1","10","███████/n","0","0","-5","124","500","0","false","MS UI Gothic","0"] -["-5","124","1-1","10"," ██████/n","0","0","-5","124","500","0","false","MS UI Gothic","0"] -["-5","148","1-1","10","███████/n","0","0","-5","148","500","0","false","MS UI Gothic","0"] -["-5","148","1-1","10"," ██████/n","0","0","-5","148","500","0","false","MS UI Gothic","0"] -["-5","175","1-1","10","███████/n","0","0","-5","175","500","0","false","MS UI Gothic","0"] -["-5","175","1-1","10"," ██████/n","0","0","-5","175","500","0","false","MS UI Gothic","0"] -["-5","201","1-1","10","███████/n","0","0","-5","201","500","0","false","MS UI Gothic","0"] -["-5","201","1-1","10"," ██████/n","0","0","-5","201","500","0","false","MS UI Gothic","0"] -["-5","234","1-1","10","███████/n/n","0","0","-5","234","500","0","false","MS UI Gothic","0"] -["-5","234","1-1","10"," ██████/n/n","0","0","-5","234","500","0","false","MS UI Gothic","0"] -["-5","275","1-1","10","███████/n/n","0","0","-5","275","500","0","false","MS UI Gothic","0"] -["-5","275","1-1","10"," ██████/n/n","0","0","-5","275","500","0","false","MS UI Gothic","0"] -["59","71","1-1","10","●","0","0","59","71","500","0","false","黑体","0"] -["17","87","1-1","10","●","0","0","17","87","500","0","false","黑体","0"] -["130","78","1-1","10","●","0","0","130","78","500","0","false","黑体","0"] -["169","71","1-1","10","●","0","0","169","71","500","0","false","黑体","0"] -["23","94","1-1","10","●","0","0","23","94","500","0","false","黑体","0"] -["69","83","1-1","10","●","0","0","69","83","500","0","false","黑体","0"] -["141","86","1-1","10","●","0","0","141","86","500","0","false","黑体","0"] -["173","77","1-1","10","●","0","0","173","77","500","0","false","黑体","0"] -["30","99","1-1","10","●","0","0","30","99","500","0","false","黑体","0"] -["73","90","1-1","10","●","0","0","73","90","500","0","false","黑体","0"] -["132","89","1-1","10","●","0","0","132","89","500","0","false","黑体","0"] -["172","86","1-1","10","●","0","0","172","86","500","0","false","黑体","0"] -["175","90","1-1","10","●","0","0","175","90","500","0","false","黑体","0"] -["137","96","1-1","10","●","0","0","137","96","500","0","false","黑体","0"] -["80","96","1-1","10","●","0","0","80","96","500","0","false","黑体","0"] -["36","105","1-1","10","●","0","0","36","105","500","0","false","黑体","0"] -["313","26","1-1","9","●","0","0","313","26","500","0","false","黑体","0"] -["362","14","1-1","9","●","0","0","362","14","500","0","false","黑体","0"] -["436","19","1-1","9","●","0","0","436","19","500","0","false","黑体","0"] -["369","17","1-1","9","●","0","0","369","17","500","0","false","黑体","0"] -["325","30","1-1","9","●","0","0","325","30","500","0","false","黑体","0"] -["438","32","1-1","9","●","0","0","438","32","500","0","false","黑体","0"] -["376","27","1-1","9","●","0","0","376","27","500","0","false","黑体","0"] -["332","36","1-1","9","●","0","0","332","36","500","0","false","黑体","0"] -["438","42","1-1","9","●","0","0","438","42","500","0","false","黑体","0"] -["383","33","1-1","9","●","0","0","383","33","500","0","false","黑体","0"] -["341","44","1-1","9","●","0","0","341","44","500","0","false","黑体","0"] -["439","47","1-1","9","●","0","0","439","47","500","0","false","黑体","0"] -["46","132","0.3-1","2","大空へと響け","353","0","46","132","500","0","false","黑体","0"] -["46","132","1-0","3","大空へと響け","353","0","46","132","500","0","false","黑体","0"] -["210","220","0-1","3","口風琴(Harmonica)","355","0","210","220","500","0","false","黑体","0"] -["210","220","1-0","2.3","口風琴(Harmonica)","355","0","200","220","2300","0","false","黑体","0"] -["210","220","1-0","2.3","口風琴(Harmonica)","355","0","220","220","2300","0","false","黑体","0"] -["210","220","1-0","2.3","口風琴(Harmonica)","355","0","210","220","2300","0","false","黑体","0"] -["284","317","0.8-1","4.5","█████","350","0","284","317","500","0","true","黑体","0"] -["284","317","0.8-1","4.5"," ████","350","0","284","317","500","0","false","黑体","0"] -["288","309","0.8-1","4.5","█████","350","0","288","309","500","0","true","黑体","0"] -["288","309","0.8-1","4.5"," ████","350","0","288","309","500","0","false","黑体","0"] -["291","310","0.8-1","4.5","████████▋","80","0","291","310","500","0","true","黑体","0"] -["293","348","0.8-1","4.5","███████████████████████████████████","350","0","293","348","500","0","true","黑体","0"] -["291","310","0.8-1","4.5","████████▋","80","0","291","310","500","0","false","黑体","0"] -["307","345","0.8-1","4.5","-","97","0","307","345","500","0","false","黑体","0"] -["298","311","0.8-1","4.5","██████/n","350","0","298","311","500","0","true","黑体","0"] -["298","311","0.8-1","4.5"," █████/n","350","0","298","311","500","0","false","黑体","0"] -["316","303","1-1","4.5","████▌","350","0","316","303","500","0","true","黑体","0"] -["316","303","1-1","4.5"," ████","350","0","316","303","500","0","false","黑体","0"] -["317","300","1-1","4.5","████▌","350","0","317","300","500","0","true","黑体","0"] -["317","300","1-1","4.5"," ████","350","0","317","300","500","0","false","黑体","0"] -["308","320","0.8-1","4.5","●","0","0","308","320","500","0","true","黑体","0"] -["471","291","0.8-1","4.5","●","0","0","471","291","500","0","true","黑体","0"] -["319","303","0.8-1","4.5","███████","350","0","319","303","500","0","false","黑体","0"] -["468","326","0.8-1","4.5","█ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ ","180","45","468","326","500","0","false","黑体","0"] -["497","302","1-1","4.5","█","0","0","497","302","500","0","false","黑体","0"] -["255","299","1-1","4.5","█ ","0","0","255","299","500","0","false","黑体","0"] -["313","311","0.8-1","4.5","__","95","0","313","311","500","0","false","黑体","0"] -["512","311","0.8-1","4.5","-","95","0","512","311","500","0","false","黑体","0"] -["272","257","1-0","2.5","███████/n███████/n███████","0","0","272","257","2500","0","false","黑体","0"] -["272","257","1-0","2.5"," ███████/n ███████/n ███████","0","0","272","257","2500","0","false","黑体","0"] -["272","275","1-0","2.5","███████/n███████","0","0","272","275","500","0","false","黑体","0"] -["272","275","1-0","2.5"," ███████/n ███████","0","0","272","275","500","0","false","黑体","0"] -["-5","-5","1-1","10","███████","0","0","-5","-5","500","0","false","MS UI Gothic","0"] -["-5","-5","1-1","10"," ██████","0","0","-5","-5","500","0","false","MS UI Gothic","0"] -["-5","10","1-1","10","███████","0","0","-5","10","500","0","false","MS UI Gothic","0"] -["-5","10","1-1","10"," ██████","0","0","-5","10","500","0","false","MS UI Gothic","0"] -["-5","32","1-1","10","███████","0","0","-5","32","500","0","false","MS UI Gothic","0"] -["-5","32","1-1","10"," ██████","0","0","-5","32","500","0","false","MS UI Gothic","0"] -["-5","63","1-1","10","███████","0","0","-5","63","500","0","false","MS UI Gothic","0"] -["-5","63","1-1","10"," ██████","0","0","-5","63","500","0","false","MS UI Gothic","0"] -["-5","100","1-1","10","███████","0","0","-5","100","500","0","false","MS UI Gothic","0"] -["-5","100","1-1","10"," ██████","0","0","-5","100","500","0","false","MS UI Gothic","0"] -["-5","124","1-1","10","███████/n","0","0","-5","124","500","0","false","MS UI Gothic","0"] -["-5","124","1-1","10"," ██████/n","0","0","-5","124","500","0","false","MS UI Gothic","0"] -["-5","148","1-1","10","███████/n","0","0","-5","148","500","0","false","MS UI Gothic","0"] -["-5","148","1-1","10"," ██████/n","0","0","-5","148","500","0","false","MS UI Gothic","0"] -["-5","175","1-1","10","███████/n","0","0","-5","175","500","0","false","MS UI Gothic","0"] -["-5","175","1-1","10"," ██████/n","0","0","-5","175","500","0","false","MS UI Gothic","0"] -["-5","201","1-1","10","███████/n","0","0","-5","201","500","0","false","MS UI Gothic","0"] -["-5","201","1-1","10"," ██████/n","0","0","-5","201","500","0","false","MS UI Gothic","0"] -["-5","234","1-1","10","███████/n/n","0","0","-5","234","500","0","false","MS UI Gothic","0"] -["-5","234","1-1","10"," ██████/n/n","0","0","-5","234","500","0","false","MS UI Gothic","0"] -["-5","275","1-1","10","███████/n/n","0","0","-5","275","500","0","false","MS UI Gothic","0"] -["-5","275","1-1","10"," ██████/n/n","0","0","-5","275","500","0","false","MS UI Gothic","0"] -["59","71","1-1","5.4","●","0","0","79","71","5400","0","false","黑体","0"] -["17","87","1-1","5.4","●","0","0","37","87","5400","0","false","黑体","0"] -["130","78","1-1","5.4","●","0","0","150","78","5400","0","false","黑体","0"] -["169","71","1-1","5.4","●","0","0","189","71","5400","0","false","黑体","0"] -["23","94","1-1","5.4","●","0","0","43","94","5400","0","false","黑体","0"] -["69","83","1-1","5.4","●","0","0","89","83","5400","0","false","黑体","0"] -["141","86","1-1","5.4","●","0","0","161","86","5400","0","false","黑体","0"] -["173","77","1-1","5.4","●","0","0","193","77","5400","0","false","黑体","0"] -["30","99","1-1","5.4","●","0","0","50","99","5400","0","false","黑体","0"] -["73","90","1-1","5.4","●","0","0","93","90","5400","0","false","黑体","0"] -["132","89","1-1","5.4","●","0","0","152","89","5400","0","false","黑体","0"] -["172","86","1-1","5.4","●","0","0","192","86","5400","0","false","黑体","0"] -["175","90","1-1","5.4","●","0","0","195","90","5400","0","false","黑体","0"] -["137","96","1-1","5.4","●","0","0","157","96","5400","0","false","黑体","0"] -["80","96","1-1","5.4","●","0","0","100","96","5400","0","false","黑体","0"] -["36","105","1-1","5.4","●","0","0","56","105","5400","0","false","黑体","0"] -["135","42","1-1","4.5","███████/n█     █/n█     █/n█     █/n███████/n█     █/n█     █/n█     █/n███████","0","130","135","42","0","0","true","MS UI Gothic","0"] -["404","42","1-1","4.5","███████/n█     █/n█     █/n█     █/n███████/n█     █/n█     █/n█     █/n███████","0","50","404","42","0","0","true","MS UI Gothic","0"] -["-8","0","1-1","4.5","███████████████████████████","0","0","-8","0","0","0","true","MS UI Gothic","0"] -["-8","0","1-1","4.5"," ██████████████████████████","0","0","-8","0","0","0","false","MS UI Gothic","0"] -["-8","350","1-1","4.5","███████████████████████████","0","0","-8","350","0","0","true","MS UI Gothic","0"] -["-8","350","1-1","4.5"," ██████████████████████████","0","0","-8","350","0","0","false","MS UI Gothic","0"] -["-4","0","1-1","4.5","█/n█/n█/n█/n█/n█/n█/n█/n█/n█/n█/n█/n","0","0","-4","0","0","0","true","MS UI Gothic","0"] -["-4","5","1-1","4.5","█/n█/n█/n█/n█/n█/n█/n█/n█/n█/n█/n█/n","0","0","-4","5","0","0","false","MS UI Gothic","0"] -["519","0","1-1","4.5","█/n█/n█/n█/n█/n█/n█/n█/n█/n█/n█/n█/n","0","0","519","0","0","0","true","MS UI Gothic","0"] -["519","5","1-1","4.5","█/n█/n█/n█/n█/n█/n█/n█/n█/n█/n█/n█/n","0","0","519","5","0","0","false","MS UI Gothic","0"] -["313","26","1-1","5.4","●","0","0","333","26","5400","0","false","黑体","0"] -["362","14","1-1","5.4","●","0","0","382","14","5400","0","false","黑体","0"] -["436","19","1-1","5.4","●","0","0","456","19","5400","0","false","黑体","0"] -["369","17","1-1","5.4","●","0","0","389","17","5400","0","false","黑体","0"] -["325","30","1-1","5.4","●","0","0","345","30","5400","0","false","黑体","0"] -["438","32","1-1","5.4","●","0","0","458","32","5400","0","false","黑体","0"] -["376","27","1-1","5.4","●","0","0","396","27","5400","0","false","黑体","0"] -["332","36","1-1","5.4","●","0","0","352","36","5400","0","false","黑体","0"] -["438","42","1-1","5.4","●","0","0","458","42","5400","0","false","黑体","0"] -["383","33","1-1","5.4","●","0","0","403","33","5400","0","false","黑体","0"] -["341","44","1-1","5.4","●","0","0","361","44","5400","0","false","黑体","0"] -["439","47","1-1","5.4","●","0","0","459","47","5400","0","false","黑体","0"] -["181","236","0.5-0.5","3","□","0","0","181","236","500","0","false","宋体","0"] -["156","236","0.5-0.5","3","□","0","0","156","236","500","0","false","宋体","0"] -["160","238","1-1","3","窓枠の畫布(Toile)","0","0","160","238","500","0","false","黑体","0"] -["48","191","0.3-1","2.5","ねぇ…その風景画(paysage)","0","0","67","221","400","0","false","黑体","0"] -["67","221","1-0.0","1.5","ねぇ…その風景画(paysage)","0","0","67","201","1500","0","false","黑体","0"] -["67","221","1-0.0","1.5","ねぇ…その風景画(paysage)","0","0","67","241","1500","0","false","黑体","0"] -["48","191","0.3-1","2.8","綺麗かしら?","0","0","161","263","400","0","false","黑体","0"] -["161","263","1-0","1.5","綺麗かしら?","0","0","161","243","1500","0","false","黑体","0"] -["161","263","1-0","1.5","綺麗かしら?","0","0","161","283","1500","0","false","黑体","0"] -["-5","-5","1-1","5.3","███████","0","0","-5","-5","500","0","false","MS UI Gothic","0"] -["-5","-5","1-1","5.3"," ██████","0","0","-5","-5","500","0","false","MS UI Gothic","0"] -["-5","10","1-1","5.3","███████","0","0","-5","10","500","0","false","MS UI Gothic","0"] -["-5","10","1-1","5.3"," ██████","0","0","-5","10","500","0","false","MS UI Gothic","0"] -["-5","32","1-1","5.3","███████","0","0","-5","32","500","0","false","MS UI Gothic","0"] -["-5","32","1-1","5.3"," ██████","0","0","-5","32","500","0","false","MS UI Gothic","0"] -["-5","63","1-1","5.3","███████","0","0","-5","63","500","0","false","MS UI Gothic","0"] -["-5","63","1-1","5.3"," ██████","0","0","-5","63","500","0","false","MS UI Gothic","0"] -["-5","100","1-1","5.3","███████","0","0","-5","100","500","0","false","MS UI Gothic","0"] -["-5","100","1-1","5.3"," ██████","0","0","-5","100","500","0","false","MS UI Gothic","0"] -["-5","124","1-1","5.3","███████/n","0","0","-5","124","500","0","false","MS UI Gothic","0"] -["-5","124","1-1","5.3"," ██████/n","0","0","-5","124","500","0","false","MS UI Gothic","0"] -["-5","148","1-1","5.3","███████/n","0","0","-5","148","500","0","false","MS UI Gothic","0"] -["-5","148","1-1","5.3"," ██████/n","0","0","-5","148","500","0","false","MS UI Gothic","0"] -["-5","175","1-1","5.3","███████/n","0","0","-5","175","500","0","false","MS UI Gothic","0"] -["-5","175","1-1","5.3"," ██████/n","0","0","-5","175","500","0","false","MS UI Gothic","0"] -["-5","201","1-1","5.3","███████/n","0","0","-5","201","500","0","false","MS UI Gothic","0"] -["-5","201","1-1","5.3"," ██████/n","0","0","-5","201","500","0","false","MS UI Gothic","0"] -["-5","234","1-1","5.3","███████/n/n","0","0","-5","234","500","0","false","MS UI Gothic","0"] -["-5","234","1-1","5.3"," ██████/n/n","0","0","-5","234","500","0","false","MS UI Gothic","0"] -["-5","275","1-1","5.3","███████/n/n","0","0","-5","275","500","0","false","MS UI Gothic","0"] -["-5","275","1-1","5.3"," ██████/n/n","0","0","-5","275","500","0","false","MS UI Gothic","0"] -["79","71","1-1","5.3","●","0","0","99","71","5300","0","false","黑体","0"] -["37","87","1-1","5.3","●","0","0","57","87","5300","0","false","黑体","0"] -["150","78","1-1","5.3","●","0","0","170","78","5300","0","false","黑体","0"] -["189","71","1-1","5.3","●","0","0","209","71","5300","0","false","黑体","0"] -["43","94","1-1","5.3","●","0","0","63","94","5300","0","false","黑体","0"] -["89","83","1-1","5.3","●","0","0","109","83","5300","0","false","黑体","0"] -["161","86","1-1","5.3","●","0","0","181","86","5300","0","false","黑体","0"] -["193","77","1-1","5.3","●","0","0","213","77","5300","0","false","黑体","0"] -["50","99","1-1","5.3","●","0","0","70","99","5300","0","false","黑体","0"] -["93","90","1-1","5.3","●","0","0","113","90","5300","0","false","黑体","0"] -["152","89","1-1","5.3","●","0","0","172","89","5300","0","false","黑体","0"] -["192","86","1-1","5.3","●","0","0","212","86","5300","0","false","黑体","0"] -["195","90","1-1","5.3","●","0","0","215","90","5300","0","false","黑体","0"] -["157","96","1-1","5.3","●","0","0","177","96","5300","0","false","黑体","0"] -["100","96","1-1","5.3","●","0","0","120","96","5300","0","false","黑体","0"] -["56","105","1-1","5.3","●","0","0","76","105","5300","0","false","黑体","0"] -["333","26","1-1","5.3","●","0","0","353","26","5300","0","false","黑体","0"] -["382","14","1-1","5.3","●","0","0","402","14","5300","0","false","黑体","0"] -["456","19","1-1","5.3","●","0","0","476","19","5300","0","false","黑体","0"] -["389","17","1-1","5.3","●","0","0","409","17","5300","0","false","黑体","0"] -["345","30","1-1","5.3","●","0","0","365","30","5300","0","false","黑体","0"] -["458","32","1-1","5.3","●","0","0","478","32","5300","0","false","黑体","0"] -["396","27","1-1","5.3","●","0","0","416","27","5300","0","false","黑体","0"] -["352","36","1-1","5.3","●","0","0","372","36","5300","0","false","黑体","0"] -["458","42","1-1","5.3","●","0","0","478","42","5300","0","false","黑体","0"] -["403","33","1-1","5.3","●","0","0","423","33","5300","0","false","黑体","0"] -["361","44","1-1","5.3","●","0","0","381","44","5300","0","false","黑体","0"] -["459","47","1-1","5.3","●","0","0","479","47","5300","0","false","黑体","0"] -["150","219","1-1","4.5","たい抱が使天","0","0","150","219","500","0","false","黑体","0"] -["281","219","1-1","4.5","天使が抱いた","0","0","281","219","500","0","false","黑体","0"] -["143","207","0.9-0","4.5","たい抱が使天","5","0","143","207","500","0","false","黑体","0"] -["288","216","0.9-0","4.5","天使が抱いた","355","0","288","216","500","0","false","黑体","0"] -["140","195","0.8-0","4.5","たい抱が使天","10","0","140","195","500","0","false","黑体","0"] -["296","212","0.8-0","4.5","天使が抱いた","350","0","296","212","500","0","false","黑体","0"] -["137","182","0.7-0","4.5","たい抱が使天","15","0","137","182","500","0","false","黑体","0"] -["305","207","0.7-0","4.5","天使が抱いた","345","0","305","207","500","0","false","黑体","0"] -["135","167","0.6-0","4.5","たい抱が使天","20","0","135","167","500","0","false","黑体","0"] -["311","201","0.6-0","4.5","天使が抱いた","340","0","311","201","500","0","false","黑体","0"] -["302","170","0.9-0","4.5","○","90","55","302","170","500","0","false","宋体","0"] -["234","236","1-1","4.5","◆","73","76","234","236","500","0","false","黑体","0"] -["292","248","1-1","4.5","◆","286","70","292","248","500","0","false","黑体","0"] -["294","244","1-1","4.5","█","9","0","294","244","500","0","true","黑体","0"] -["193","255","1-1","4.5","█","346","0","193","255","500","0","true","黑体","0"] -["222","268","1-1","4.5","█","59","50","222","268","500","0","true","黑体","0"] -["293","294","1-1","4.5","█","300","48","293","294","500","0","true","黑体","0"] -["228","253","1-1","4.5","███","0","0","228","253","500","0","false","黑体","0"] -["232","253","1-1","4.5","█/n","0","0","232","253","500","0","false","黑体","0"] -["218","267","1-1","4.5","█","0","0","218","267","500","0","false","黑体","0"] -["279","267","1-1","4.5","█","0","0","279","267","500","0","false","黑体","0"] -["248","248","1-1","4.5","██","0","0","248","248","500","0","false","黑体","0"] -["248","248","1-1","4.5"," █","0","0","248","248","500","0","false","黑体","0"] -["224","249","1-1","4.5","█","0","0","224","249","500","0","false","黑体","0"] -["228","240","1-1","4.5","████","0","0","228","240","500","0","false","黑体","0"] -["228","240","1-1","4.5"," ████","0","0","228","240","500","0","false","黑体","0"] -["212","269","1-1","4.5","█","0","0","212","269","500","0","false","黑体","0"] -["216","260","1-1","4.5","█","0","0","216","260","500","0","false","黑体","0"] -["207","283","1-1","4.5","█","0","0","207","283","500","0","false","黑体","0"] -["290","249","1-1","4.5","█","0","0","290","249","500","0","false","黑体","0"] -["295","257","1-1","4.5","█","0","0","295","257","500","0","false","黑体","0"] -["301","266","1-1","4.5","█","0","0","301","266","500","0","false","黑体","0"] -["307","275","1-1","4.5","█","0","0","307","275","500","0","false","黑体","0"] -["225","296","1-1","4.5","█/n","0","0","225","296","500","0","false","黑体","0"] -["287","290","1-1","4.5","█","0","0","287","290","500","0","false","黑体","0"] -["229","293","1-1","4.5","█","0","0","229","293","500","0","false","黑体","0"] -["249","256","1-1","4.5","██","0","0","249","256","500","0","false","黑体","0"] -["248","256","1-1","4.5","██","0","0","248","256","500","0","false","黑体","0"] -["211","298","1-1","4.5","█","0","0","211","298","500","0","false","黑体","0"] -["300","293","1-1","4.5","█","0","0","300","293","500","0","false","黑体","0"] -["305","283","1-1","4.5","█","0","0","305","283","500","0","false","黑体","0"] -["232","247","1-1","4.5","█","0","0","232","247","500","0","false","黑体","0"] -["285","246","1-1","4.5","█","0","0","285","246","500","0","false","黑体","0"] -["279","244","1-1","4.5","█","0","0","279","244","500","0","false","黑体","0"] -["306","280","1-1","4.5","█","0","0","306","280","500","0","false","黑体","0"] -["300","267","1-1","4.5","█","0","0","300","267","500","0","false","黑体","0"] -["254","267","1-1","4.5","█","0","0","254","267","500","0","false","黑体","0"] -["259","267","1-1","4.5","█","0","0","259","267","500","0","false","黑体","0"] -["210","295","1-1","4.5","█","0","0","210","295","500","0","false","黑体","0"] -["210","308","1-1","4.5","█","0","0","210","308","500","0","false","黑体","0"] -["300","306","1-1","4.5","█","0","0","300","306","500","0","false","黑体","0"] -["299","308","1-1","4.5","█","0","0","299","308","500","0","false","黑体","0"] -["295","311","1-1","4.5","█","0","0","295","311","500","0","false","黑体","0"] -["300","321","1-1","4.5","█","0","0","300","321","500","0","false","黑体","0"] -["208","316","1-1","4.5","█","0","0","208","316","500","0","false","黑体","0"] -["243","236","1-1","4.5","█","0","0","243","236","500","0","false","黑体","0"] -["276","235","1-1","4.5","█","0","0","276","235","500","0","false","黑体","0"] -["210","293","1-1","4.5","█","0","0","210","293","500","0","false","黑体","0"] -["298","302","1-1","4.5","█","0","0","298","302","500","0","false","黑体","0"] -["215","302","1-1","4.5","█","0","0","215","302","500","0","false","黑体","0"] -["220","311","1-1","4.5","█","0","0","220","311","500","0","false","黑体","0"] -["289","308","1-1","4.5","█","0","0","289","308","500","0","false","黑体","0"] -["227","304","1-1","4.5","████","0","0","232","304","4500","0","false","黑体","0"] -["229","304","1-1","4.5","████","0","0","234","304","4500","0","false","黑体","0"] -["230","305","1-1","4.5","█████","0","0","239","305","4500","0","false","黑体","0"] -["275","303","1-1","4.5",")","90","0","275","303","500","0","false","黑体","0"] -["296","285","1-1","4.5",")","90","0","296","285","500","0","false","黑体","0"] -["254","285","1-1","4.5",")","90","0","254","285","500","0","false","黑体","0"] -["221","325","1-1","4.5","█","90","0","221","325","500","0","false","黑体","0"] -["308","312","1-1","4.5","█","0","0","308","312","500","0","false","黑体","0"] -["301","295","1-1","4.5","█","0","0","301","295","500","0","false","黑体","0"] -["313","324","1-1","4.5","█","0","0","313","324","500","0","false","黑体","0"] -["239","318","1-1","4.5","███","0","0","239","318","500","0","false","黑体","0"] -["241","318","1-1","4.5","███","0","0","241","318","500","0","false","黑体","0"] -["231","332","1-1","4.5","████","0","0","231","332","500","0","false","黑体","0"] -["228","332","1-1","4.5","████","0","0","228","332","500","0","false","黑体","0"] -["234","346","1-1","4.5","████","0","0","234","346","500","0","false","黑体","0"] -["230","346","1-1","4.5","████","0","0","230","346","500","0","false","黑体","0"] -["224","314","1-1","4.5","█","0","0","228","314","4500","0","false","黑体","0"] -["226","325","1-1","4.5","█","0","0","229","325","4500","0","false","黑体","0"] -["287","314","1-1","4.5","█","0","0","291","314","4500","0","false","黑体","0"] -["284","327","1-1","4.5","█","0","0","287","327","4500","0","false","黑体","0"] -["233","360","1-1","4.5","████","0","0","233","360","500","0","false","黑体","0"] -["231","360","1-1","4.5","████","0","0","231","360","500","0","false","黑体","0"] -["226","340","1-1","4.5","█","0","0","226","340","500","0","false","黑体","0"] -["223","351","1-1","4.5","█","0","0","223","351","500","0","false","黑体","0"] -["221","362","1-1","4.5","█","0","0","221","362","500","0","false","黑体","0"] -["278","348","1-1","4.5","█","0","0","278","348","500","0","false","黑体","0"] -["288","353","1-1","4.5","█","0","0","288","353","500","0","false","黑体","0"] -["294","363","1-1","4.5","█","0","0","294","363","500","0","false","黑体","0"] -["212","375","1-1","4.5","███████","0","0","212","375","500","0","false","黑体","0"] -["219","370","1-1","4.5","██████","0","0","219","370","500","0","false","黑体","0"] -["221","370","1-1","4.5","██████","0","0","221","370","500","0","false","黑体","0"] -["238","233","1-1","4.5","█","0","0","238","233","500","0","false","黑体","0"] -["245","233","1-1","4.5","███","0","0","245","233","500","0","false","黑体","0"] -["243","233","1-1","4.5","███","0","0","243","233","500","0","false","黑体","0"] -["234","236","1-1","10","◆","73","76","234","236","500","0","false","黑体","0"] -["292","248","1-1","10","◆","286","70","292","248","500","0","false","黑体","0"] -["294","244","1-1","10","█","9","0","294","244","500","0","true","黑体","0"] -["193","255","1-1","10","█","346","0","193","255","500","0","true","黑体","0"] -["222","268","1-1","10","█","59","50","222","268","500","0","true","黑体","0"] -["293","294","1-1","10","█","300","48","293","294","500","0","true","黑体","0"] -["228","253","1-1","10","███","0","0","228","253","500","0","false","黑体","0"] -["232","253","1-1","10","█/n","0","0","232","253","500","0","false","黑体","0"] -["218","267","1-1","10","█","0","0","218","267","500","0","false","黑体","0"] -["279","267","1-1","10","█","0","0","279","267","500","0","false","黑体","0"] -["248","248","1-1","10","██","0","0","248","248","500","0","false","黑体","0"] -["248","248","1-1","10"," █","0","0","248","248","500","0","false","黑体","0"] -["224","249","1-1","10","█","0","0","224","249","500","0","false","黑体","0"] -["228","240","1-1","10","████","0","0","228","240","500","0","false","黑体","0"] -["228","240","1-1","10"," ████","0","0","228","240","500","0","false","黑体","0"] -["212","269","1-1","10","█","0","0","212","269","500","0","false","黑体","0"] -["216","260","1-1","10","█","0","0","216","260","500","0","false","黑体","0"] -["207","283","1-1","10","█","0","0","207","283","500","0","false","黑体","0"] -["290","249","1-1","10","█","0","0","290","249","500","0","false","黑体","0"] -["295","257","1-1","10","█","0","0","295","257","500","0","false","黑体","0"] -["301","266","1-1","10","█","0","0","301","266","500","0","false","黑体","0"] -["307","275","1-1","10","█","0","0","307","275","500","0","false","黑体","0"] -["225","296","1-1","10","█/n","0","0","225","296","500","0","false","黑体","0"] -["287","290","1-1","10","█","0","0","287","290","500","0","false","黑体","0"] -["229","293","1-1","10","█","0","0","229","293","500","0","false","黑体","0"] -["249","256","1-1","10","██","0","0","249","256","500","0","false","黑体","0"] -["248","256","1-1","10","██","0","0","248","256","500","0","false","黑体","0"] -["211","298","1-1","10","█","0","0","211","298","500","0","false","黑体","0"] -["300","293","1-1","10","█","0","0","300","293","500","0","false","黑体","0"] -["305","283","1-1","10","█","0","0","305","283","500","0","false","黑体","0"] -["232","247","1-1","10","█","0","0","232","247","500","0","false","黑体","0"] -["285","246","1-1","10","█","0","0","285","246","500","0","false","黑体","0"] -["279","244","1-1","10","█","0","0","279","244","500","0","false","黑体","0"] -["306","280","1-1","10","█","0","0","306","280","500","0","false","黑体","0"] -["300","267","1-1","10","█","0","0","300","267","500","0","false","黑体","0"] -["254","267","1-1","10","█","0","0","254","267","500","0","false","黑体","0"] -["259","267","1-1","10","█","0","0","259","267","500","0","false","黑体","0"] -["210","295","1-1","10","█","0","0","210","295","500","0","false","黑体","0"] -["210","308","1-1","10","█","0","0","210","308","500","0","false","黑体","0"] -["300","306","1-1","10","█","0","0","300","306","500","0","false","黑体","0"] -["299","308","1-1","10","█","0","0","299","308","500","0","false","黑体","0"] -["295","311","1-1","10","█","0","0","295","311","500","0","false","黑体","0"] -["300","321","1-1","10","█","0","0","300","321","500","0","false","黑体","0"] -["208","316","1-1","10","█","0","0","208","316","500","0","false","黑体","0"] -["243","236","1-1","10","█","0","0","243","236","500","0","false","黑体","0"] -["276","235","1-1","10","█","0","0","276","235","500","0","false","黑体","0"] -["210","293","1-1","10","█","0","0","210","293","500","0","false","黑体","0"] -["298","302","1-1","10","█","0","0","298","302","500","0","false","黑体","0"] -["215","302","1-1","10","█","0","0","215","302","500","0","false","黑体","0"] -["220","311","1-1","10","█","0","0","220","311","500","0","false","黑体","0"] -["289","308","1-1","10","█","0","0","289","308","500","0","false","黑体","0"] -["232","304","1-1","5","████","0","0","227","304","4500","0","false","黑体","0"] -["234","304","1-1","5","████","0","0","229","304","4500","0","false","黑体","0"] -["239","305","1-1","5","█████","0","0","230","305","4500","0","false","黑体","0"] -["275","303","1-1","10",")","90","0","275","303","500","0","false","黑体","0"] -["296","285","1-1","10",")","90","0","296","285","500","0","false","黑体","0"] -["254","285","1-1","10",")","90","0","254","285","500","0","false","黑体","0"] -["221","325","1-1","10","█","90","0","221","325","500","0","false","黑体","0"] -["308","312","1-1","10","█","0","0","308","312","500","0","false","黑体","0"] -["301","295","1-1","10","█","0","0","301","295","500","0","false","黑体","0"] -["313","324","1-1","10","█","0","0","313","324","500","0","false","黑体","0"] -["239","318","1-1","10","███","0","0","239","318","500","0","false","黑体","0"] -["241","318","1-1","10","███","0","0","241","318","500","0","false","黑体","0"] -["231","332","1-1","10","████","0","0","231","332","500","0","false","黑体","0"] -["228","332","1-1","10","████","0","0","228","332","500","0","false","黑体","0"] -["234","346","1-1","10","████","0","0","234","346","500","0","false","黑体","0"] -["230","346","1-1","10","████","0","0","230","346","500","0","false","黑体","0"] -["228","314","1-1","5","█","0","0","224","314","4500","0","false","黑体","0"] -["229","325","1-1","5","█","0","0","226","325","4500","0","false","黑体","0"] -["291","314","1-1","5","█","0","0","287","314","4500","0","false","黑体","0"] -["287","327","1-1","5","█","0","0","284","327","4500","0","false","黑体","0"] -["233","360","1-1","10","████","0","0","233","360","500","0","false","黑体","0"] -["231","360","1-1","10","████","0","0","231","360","500","0","false","黑体","0"] -["226","340","1-1","10","█","0","0","226","340","500","0","false","黑体","0"] -["223","351","1-1","10","█","0","0","223","351","500","0","false","黑体","0"] -["221","362","1-1","10","█","0","0","221","362","500","0","false","黑体","0"] -["278","348","1-1","10","█","0","0","278","348","500","0","false","黑体","0"] -["288","353","1-1","10","█","0","0","288","353","500","0","false","黑体","0"] -["294","363","1-1","10","█","0","0","294","363","500","0","false","黑体","0"] -["212","375","1-1","10","███████","0","0","212","375","500","0","false","黑体","0"] -["219","370","1-1","10","██████","0","0","219","370","500","0","false","黑体","0"] -["221","370","1-1","10","██████","0","0","221","370","500","0","false","黑体","0"] -["238","233","1-1","10","█","0","0","238","233","500","0","false","黑体","0"] -["245","233","1-1","10","███","0","0","245","233","500","0","false","黑体","0"] -["243","233","1-1","10","███","0","0","243","233","500","0","false","黑体","0"] -["227","304","1-1","5.5","████","0","0","232","304","5500","0","false","黑体","0"] -["229","304","1-1","5.5","████","0","0","234","304","5500","0","false","黑体","0"] -["230","305","1-1","5.5","█████","0","0","239","305","4500","0","false","黑体","0"] -["275","303","1-1","5.5",")","90","0","275","303","500","0","false","黑体","0"] -["224","314","1-1","5.5","█","0","0","228","314","5500","0","false","黑体","0"] -["226","325","1-1","5.5","█","0","0","229","325","5500","0","false","黑体","0"] -["287","314","1-1","5.5","█","0","0","291","314","5500","0","false","黑体","0"] -["284","327","1-1","5.5","█","0","0","287","327","5500","0","false","黑体","0"] -["234","236","1-1","10","◆","73","76","234","236","500","0","false","黑体","0"] -["292","248","1-1","10","◆","286","70","292","248","500","0","false","黑体","0"] -["294","244","1-1","10","█","9","0","294","244","500","0","true","黑体","0"] -["193","255","1-1","10","█","346","0","193","255","500","0","true","黑体","0"] -["222","268","1-1","10","█","59","50","222","268","500","0","true","黑体","0"] -["293","294","1-1","10","█","300","48","293","294","500","0","true","黑体","0"] -["228","253","1-1","10","███","0","0","228","253","500","0","false","黑体","0"] -["232","253","1-1","10","█/n","0","0","232","253","500","0","false","黑体","0"] -["218","267","1-1","10","█","0","0","218","267","500","0","false","黑体","0"] -["279","267","1-1","10","█","0","0","279","267","500","0","false","黑体","0"] -["248","248","1-1","10","██","0","0","248","248","500","0","false","黑体","0"] -["248","248","1-1","10"," █","0","0","248","248","500","0","false","黑体","0"] -["224","249","1-1","10","█","0","0","224","249","500","0","false","黑体","0"] -["228","240","1-1","10","████","0","0","228","240","500","0","false","黑体","0"] -["228","240","1-1","10"," ████","0","0","228","240","500","0","false","黑体","0"] -["212","269","1-1","10","█","0","0","212","269","500","0","false","黑体","0"] -["216","260","1-1","10","█","0","0","216","260","500","0","false","黑体","0"] -["207","283","1-1","10","█","0","0","207","283","500","0","false","黑体","0"] -["290","249","1-1","10","█","0","0","290","249","500","0","false","黑体","0"] -["295","257","1-1","10","█","0","0","295","257","500","0","false","黑体","0"] -["301","266","1-1","10","█","0","0","301","266","500","0","false","黑体","0"] -["307","275","1-1","10","█","0","0","307","275","500","0","false","黑体","0"] -["225","296","1-1","10","█/n","0","0","225","296","500","0","false","黑体","0"] -["287","290","1-1","10","█","0","0","287","290","500","0","false","黑体","0"] -["229","293","1-1","10","█","0","0","229","293","500","0","false","黑体","0"] -["249","256","1-1","10","██","0","0","249","256","500","0","false","黑体","0"] -["248","256","1-1","10","██","0","0","248","256","500","0","false","黑体","0"] -["211","298","1-1","10","█","0","0","211","298","500","0","false","黑体","0"] -["300","293","1-1","10","█","0","0","300","293","500","0","false","黑体","0"] -["305","283","1-1","10","█","0","0","305","283","500","0","false","黑体","0"] -["232","247","1-1","10","█","0","0","232","247","500","0","false","黑体","0"] -["285","246","1-1","10","█","0","0","285","246","500","0","false","黑体","0"] -["279","244","1-1","10","█","0","0","279","244","500","0","false","黑体","0"] -["306","280","1-1","10","█","0","0","306","280","500","0","false","黑体","0"] -["300","267","1-1","10","█","0","0","300","267","500","0","false","黑体","0"] -["254","267","1-1","10","█","0","0","254","267","500","0","false","黑体","0"] -["259","267","1-1","10","█","0","0","259","267","500","0","false","黑体","0"] -["210","295","1-1","10","█","0","0","210","295","500","0","false","黑体","0"] -["210","308","1-1","10","█","0","0","210","308","500","0","false","黑体","0"] -["300","306","1-1","10","█","0","0","300","306","500","0","false","黑体","0"] -["299","308","1-1","10","█","0","0","299","308","500","0","false","黑体","0"] -["295","311","1-1","10","█","0","0","295","311","500","0","false","黑体","0"] -["300","321","1-1","10","█","0","0","300","321","500","0","false","黑体","0"] -["208","316","1-1","10","█","0","0","208","316","500","0","false","黑体","0"] -["243","236","1-1","10","█","0","0","243","236","500","0","false","黑体","0"] -["276","235","1-1","10","█","0","0","276","235","500","0","false","黑体","0"] -["210","293","1-1","10","█","0","0","210","293","500","0","false","黑体","0"] -["298","302","1-1","10","█","0","0","298","302","500","0","false","黑体","0"] -["215","302","1-1","10","█","0","0","215","302","500","0","false","黑体","0"] -["220","311","1-1","10","█","0","0","220","311","500","0","false","黑体","0"] -["289","308","1-1","10","█","0","0","289","308","500","0","false","黑体","0"] -["227","304","1-1","5","████","0","0","232","304","4500","0","false","黑体","0"] -["229","304","1-1","5","████","0","0","234","304","4500","0","false","黑体","0"] -["230","305","1-1","5","█████","0","0","239","305","4500","0","false","黑体","0"] -["275","303","1-1","10",")","90","0","275","303","500","0","false","黑体","0"] -["296","285","1-1","10",")","90","0","296","285","500","0","false","黑体","0"] -["254","285","1-1","10",")","90","0","254","285","500","0","false","黑体","0"] -["221","325","1-1","10","█","90","0","221","325","500","0","false","黑体","0"] -["308","312","1-1","10","█","0","0","308","312","500","0","false","黑体","0"] -["301","295","1-1","10","█","0","0","301","295","500","0","false","黑体","0"] -["313","324","1-1","10","█","0","0","313","324","500","0","false","黑体","0"] -["239","318","1-1","10","███","0","0","239","318","500","0","false","黑体","0"] -["241","318","1-1","10","███","0","0","241","318","500","0","false","黑体","0"] -["231","332","1-1","10","████","0","0","231","332","500","0","false","黑体","0"] -["228","332","1-1","10","████","0","0","228","332","500","0","false","黑体","0"] -["234","346","1-1","10","████","0","0","234","346","500","0","false","黑体","0"] -["230","346","1-1","10","████","0","0","230","346","500","0","false","黑体","0"] -["224","314","1-1","5","█","0","0","228","314","4500","0","false","黑体","0"] -["226","325","1-1","5","█","0","0","229","325","4500","0","false","黑体","0"] -["287","314","1-1","5","█","0","0","291","314","4500","0","false","黑体","0"] -["284","327","1-1","5","█","0","0","287","327","4500","0","false","黑体","0"] -["233","360","1-1","10","████","0","0","233","360","500","0","false","黑体","0"] -["231","360","1-1","10","████","0","0","231","360","500","0","false","黑体","0"] -["226","340","1-1","10","█","0","0","226","340","500","0","false","黑体","0"] -["223","351","1-1","10","█","0","0","223","351","500","0","false","黑体","0"] -["221","362","1-1","10","█","0","0","221","362","500","0","false","黑体","0"] -["278","348","1-1","10","█","0","0","278","348","500","0","false","黑体","0"] -["288","353","1-1","10","█","0","0","288","353","500","0","false","黑体","0"] -["294","363","1-1","10","█","0","0","294","363","500","0","false","黑体","0"] -["212","375","1-1","10","███████","0","0","212","375","500","0","false","黑体","0"] -["219","370","1-1","10","██████","0","0","219","370","500","0","false","黑体","0"] -["221","370","1-1","10","██████","0","0","221","370","500","0","false","黑体","0"] -["238","233","1-1","10","█","0","0","238","233","500","0","false","黑体","0"] -["245","233","1-1","10","███","0","0","245","233","500","0","false","黑体","0"] -["243","233","1-1","10","███","0","0","243","233","500","0","false","黑体","0"] -["232","304","1-1","5.5","████","0","0","227","304","5500","0","false","黑体","0"] -["234","304","1-1","5.5","████","0","0","229","304","5500","0","false","黑体","0"] -["239","305","1-1","5.5","█████","0","0","230","305","5500","0","false","黑体","0"] -["275","303","1-1","5.5",")","90","0","275","303","500","0","false","黑体","0"] -["228","314","1-1","5.5","█","0","0","224","314","5500","0","false","黑体","0"] -["229","325","1-1","5.5","█","0","0","226","325","5500","0","false","黑体","0"] -["291","314","1-1","5.5","█","0","0","287","314","5500","0","false","黑体","0"] -["287","327","1-1","5.5","█","0","0","284","327","5500","0","false","黑体","0"] -["234","236","1-1","9","◆","73","76","234","236","500","0","false","黑体","0"] -["292","248","1-1","9","◆","286","70","292","248","500","0","false","黑体","0"] -["294","244","1-1","9","█","9","0","294","244","500","0","true","黑体","0"] -["193","255","1-1","9","█","346","0","193","255","500","0","true","黑体","0"] -["222","268","1-1","9","█","59","50","222","268","500","0","true","黑体","0"] -["293","294","1-1","9","█","300","48","293","294","500","0","true","黑体","0"] -["228","253","1-1","9","███","0","0","228","253","500","0","false","黑体","0"] -["232","253","1-1","9","█/n","0","0","232","253","500","0","false","黑体","0"] -["218","267","1-1","9","█","0","0","218","267","500","0","false","黑体","0"] -["279","267","1-1","9","█","0","0","279","267","500","0","false","黑体","0"] -["248","248","1-1","9","██","0","0","248","248","500","0","false","黑体","0"] -["248","248","1-1","9"," █","0","0","248","248","500","0","false","黑体","0"] -["224","249","1-1","9","█","0","0","224","249","500","0","false","黑体","0"] -["228","240","1-1","9","████","0","0","228","240","500","0","false","黑体","0"] -["228","240","1-1","9"," ████","0","0","228","240","500","0","false","黑体","0"] -["212","269","1-1","9","█","0","0","212","269","500","0","false","黑体","0"] -["216","260","1-1","9","█","0","0","216","260","500","0","false","黑体","0"] -["207","283","1-1","9","█","0","0","207","283","500","0","false","黑体","0"] -["290","249","1-1","9","█","0","0","290","249","500","0","false","黑体","0"] -["295","257","1-1","9","█","0","0","295","257","500","0","false","黑体","0"] -["301","266","1-1","9","█","0","0","301","266","500","0","false","黑体","0"] -["307","275","1-1","9","█","0","0","307","275","500","0","false","黑体","0"] -["225","296","1-1","9","█/n","0","0","225","296","500","0","false","黑体","0"] -["287","290","1-1","9","█","0","0","287","290","500","0","false","黑体","0"] -["229","293","1-1","9","█","0","0","229","293","500","0","false","黑体","0"] -["249","256","1-1","9","██","0","0","249","256","500","0","false","黑体","0"] -["248","256","1-1","9","██","0","0","248","256","500","0","false","黑体","0"] -["211","298","1-1","9","█","0","0","211","298","500","0","false","黑体","0"] -["300","293","1-1","9","█","0","0","300","293","500","0","false","黑体","0"] -["305","283","1-1","9","█","0","0","305","283","500","0","false","黑体","0"] -["232","247","1-1","9","█","0","0","232","247","500","0","false","黑体","0"] -["285","246","1-1","9","█","0","0","285","246","500","0","false","黑体","0"] -["279","244","1-1","9","█","0","0","279","244","500","0","false","黑体","0"] -["306","280","1-1","9","█","0","0","306","280","500","0","false","黑体","0"] -["300","267","1-1","9","█","0","0","300","267","500","0","false","黑体","0"] -["254","267","1-1","9","█","0","0","254","267","500","0","false","黑体","0"] -["259","267","1-1","9","█","0","0","259","267","500","0","false","黑体","0"] -["210","295","1-1","9","█","0","0","210","295","500","0","false","黑体","0"] -["210","308","1-1","9","█","0","0","210","308","500","0","false","黑体","0"] -["300","306","1-1","9","█","0","0","300","306","500","0","false","黑体","0"] -["299","308","1-1","9","█","0","0","299","308","500","0","false","黑体","0"] -["295","311","1-1","9","█","0","0","295","311","500","0","false","黑体","0"] -["300","321","1-1","9","█","0","0","300","321","500","0","false","黑体","0"] -["208","316","1-1","9","█","0","0","208","316","500","0","false","黑体","0"] -["243","236","1-1","9","█","0","0","243","236","500","0","false","黑体","0"] -["276","235","1-1","9","█","0","0","276","235","500","0","false","黑体","0"] -["210","293","1-1","9","█","0","0","210","293","500","0","false","黑体","0"] -["298","302","1-1","9","█","0","0","298","302","500","0","false","黑体","0"] -["215","302","1-1","9","█","0","0","215","302","500","0","false","黑体","0"] -["220","311","1-1","9","█","0","0","220","311","500","0","false","黑体","0"] -["289","308","1-1","9","█","0","0","289","308","500","0","false","黑体","0"] -["232","304","1-1","5","████","0","0","227","304","4500","0","false","黑体","0"] -["234","304","1-1","5","████","0","0","229","304","4500","0","false","黑体","0"] -["239","305","1-1","5","█████","0","0","230","305","4500","0","false","黑体","0"] -["275","303","1-1","9",")","90","0","275","303","500","0","false","黑体","0"] -["296","285","1-1","9",")","90","0","296","285","500","0","false","黑体","0"] -["254","285","1-1","9",")","90","0","254","285","500","0","false","黑体","0"] -["221","325","1-1","9","█","90","0","221","325","500","0","false","黑体","0"] -["308","312","1-1","9","█","0","0","308","312","500","0","false","黑体","0"] -["301","295","1-1","9","█","0","0","301","295","500","0","false","黑体","0"] -["313","324","1-1","9","█","0","0","313","324","500","0","false","黑体","0"] -["239","318","1-1","9","███","0","0","239","318","500","0","false","黑体","0"] -["241","318","1-1","9","███","0","0","241","318","500","0","false","黑体","0"] -["231","332","1-1","9","████","0","0","231","332","500","0","false","黑体","0"] -["228","332","1-1","9","████","0","0","228","332","500","0","false","黑体","0"] -["234","346","1-1","9","████","0","0","234","346","500","0","false","黑体","0"] -["230","346","1-1","9","████","0","0","230","346","500","0","false","黑体","0"] -["228","314","1-1","5","█","0","0","224","314","4500","0","false","黑体","0"] -["229","325","1-1","5","█","0","0","226","325","4500","0","false","黑体","0"] -["291","314","1-1","5","█","0","0","287","314","4500","0","false","黑体","0"] -["287","327","1-1","5","█","0","0","284","327","4500","0","false","黑体","0"] -["233","360","1-1","9","████","0","0","233","360","500","0","false","黑体","0"] -["231","360","1-1","9","████","0","0","231","360","500","0","false","黑体","0"] -["226","340","1-1","9","█","0","0","226","340","500","0","false","黑体","0"] -["223","351","1-1","9","█","0","0","223","351","500","0","false","黑体","0"] -["221","362","1-1","9","█","0","0","221","362","500","0","false","黑体","0"] -["278","348","1-1","9","█","0","0","278","348","500","0","false","黑体","0"] -["288","353","1-1","9","█","0","0","288","353","500","0","false","黑体","0"] -["294","363","1-1","9","█","0","0","294","363","500","0","false","黑体","0"] -["212","375","1-1","9","███████","0","0","212","375","500","0","false","黑体","0"] -["219","370","1-1","9","██████","0","0","219","370","500","0","false","黑体","0"] -["221","370","1-1","9","██████","0","0","221","370","500","0","false","黑体","0"] -["238","233","1-1","9","█","0","0","238","233","500","0","false","黑体","0"] -["245","233","1-1","9","███","0","0","245","233","500","0","false","黑体","0"] -["243","233","1-1","9","███","0","0","243","233","500","0","false","黑体","0"] -["227","304","1-1","4","████","0","0","232","304","4000","0","false","黑体","0"] -["229","304","1-1","4","████","0","0","234","304","4000","0","false","黑体","0"] -["230","305","1-1","4","█████","0","0","239","305","4000","0","false","黑体","0"] -["275","303","1-1","4",")","90","0","275","303","500","0","false","黑体","0"] -["224","314","1-1","4","█","0","0","228","314","4000","0","false","黑体","0"] -["226","325","1-1","4","█","0","0","229","325","4000","0","false","黑体","0"] -["287","314","1-1","4","█","0","0","291","314","4000","0","false","黑体","0"] -["284","327","1-1","4","█","0","0","287","327","4000","0","false","黑体","0"] -/n█/n█/n█/n█/n█/n█/n█/n/n/n/n/n/n/n/n/n/n/n/n -/n █ /n/n/n █ /n/n/n/n/n/n/n/n/n/n/n/n/n/n/n -/n  █  /n/n/n  █  /n  █  /n/n/n/n/n/n/n/n/n/n/n/n/n/n -/n   █   /n/n/n   █   /n/n   █   /n/n/n/n/n/n/n/n/n/n/n/n/n -/n/n    █    /n    █    /n/n/n/n    █    /n/n/n/n/n/n/n/n/n/n/n/n -/n/n/n/n█/n█/n█/n/n/n/n/n/n/n/n/n/n/n/n/n -/n/n/n █ /n/n/n/n █ /n/n/n/n/n/n/n/n/n/n/n/n -/n/n/n  █  /n/n/n/n  █  /n/n/n/n/n/n/n/n/n/n/n/n -/n/n/n   █   /n/n/n/n   █   /n/n/n/n/n/n/n/n/n/n/n/n -/n/n/n/n    █    /n    █    /n    █    /n/n/n/n/n/n/n/n/n/n/n/n/n -/n/n/n/n█/n█/n█/n█/n/n/n/n/n/n/n/n/n/n/n/n -/n/n/n █ /n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n -/n/n/n/n  █  /n  █  /n  █  /n  █  /n/n/n/n/n/n/n/n/n/n/n/n -/n/n/n   █   /n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n -/n/n/n/n    █    /n    █    /n    █    /n    █    /n/n/n/n/n/n/n/n/n/n/n/n -/n/n/n/n█/n█/n█/n/n/n/n/n/n/n/n/n/n/n/n/n -/n/n/n █ /n/n/n/n █ /n/n/n/n/n/n/n/n/n/n/n/n -/n/n/n  █  /n/n/n/n  █  /n/n/n/n/n/n/n/n/n/n/n/n -/n/n/n   █   /n/n/n/n   █   /n/n/n/n/n/n/n/n/n/n/n/n -/n/n/n/n    █    /n    █    /n    █    /n/n/n/n/n/n/n/n/n/n/n/n/n -/n/n/n/n/n/n/n     █     /n/n/n/n/n/n/n/n/n/n/n/n -/n/n/n█/n█/n█/n█/n█/n/n/n/n/n/n/n/n/n/n/n/n -/n/n/n/n █ /n/n/n/n/n/n/n/n/n/n/n/n/n/n/n -/n/n/n  █  /n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n -/n/n/n   █   /n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n -/n/n/n/n    █    /n    █    /n    █    /n    █    /n/n/n/n/n/n/n/n/n/n/n/n -["213","69","0-1","3","Soundhorizon","0","0","193","69","2500","0","false","黑体","0"] -["193","69","1-0","3","Soundhorizon","0","0","173","69","3000","0","false","黑体","0"] -["213","69","0-1","3","美しきもの/n","0","0","193","69","2500","0","false","黑体","0"] -["232","117","0-1","3","美丽之物","0","0","212","117","2500","0","false","黑体","0"] -["193","69","1-0","3","美しきもの/n","0","0","173","69","3000","0","false","黑体","0"] -["212","117","1-0","3","美丽之物","0","0","192","117","3000","0","false","黑体","0"] -["162","77","0-1","3","_____________________","0","0","142","77","2500","0","false","黑体","0"] -["142","77","1-0","3","_____________________","0","0","122","77","3000","0","false","黑体","0"] -["156","78","0-1","3","_____________________","0","0","136","78","2500","0","false","黑体","0"] -["136","78","1-0","3","_____________________","0","0","116","78","3000","0","false","黑体","0"] -["156","78","0-1","3","_____________________","0","0","136","78","2500","0","false","黑体","0"] -["136","78","1-0","3","_____________________","0","0","116","78","3000","0","false","黑体","0"] -["261","75","0-1","3","作词:Revo /n作曲:Revo ","0","0","241","75","2500","0","false","黑体","0"] -["241","75","1-0","3","作词:Revo /n作曲:Revo ","0","0","221","75","3000","0","false","黑体","0"] -["156","78","0-1","3","_____________________","0","0","136","78","2500","0","false","黑体","0"] -["136","78","1-0","3","_____________________","0","0","116","78","3000","0","false","黑体","0"] -["256","74","0-1","3","歌:YUUKI ","0","0","236","74","2500","0","false","黑体","0"] -["236","74","1-0","3","歌:YUUKI ","0","0","216","74","3000","0","false","黑体","0"] -["209","69","0-1","3","Soundhorizon","0","0","189","69","2500","0","false","黑体","0"] -["189","69","1-0","3","Soundhorizon","0","0","169","69","3000","0","false","黑体","0"] -["209","69","0-1","3","美しきもの/n","0","0","189","69","2500","0","false","黑体","0"] -["228","117","0-1","3","美丽之物","0","0","208","117","2500","0","false","黑体","0"] -["189","69","1-0","3","美しきもの/n","0","0","169","69","3000","0","false","黑体","0"] -["208","117","1-0","3","美丽之物","0","0","188","117","3000","0","false","黑体","0"] -["158","77","0-1","3","_____________________","0","0","138","77","2500","0","false","黑体","0"] -["138","77","1-0","3","_____________________","0","0","118","77","3000","0","false","黑体","0"] -["152","78","0-1","3","_____________________","0","0","132","78","2500","0","false","黑体","0"] -["132","78","1-0","3","_____________________","0","0","112","78","3000","0","false","黑体","0"] -["152","78","0-1","3","_____________________","0","0","132","78","2500","0","false","黑体","0"] -["132","78","1-0","3","_____________________","0","0","112","78","3000","0","false","黑体","0"] -["257","75","0-1","3","作词:Revo /n作曲:Revo ","0","0","237","75","2500","0","false","黑体","0"] -["237","75","1-0","3","作词:Revo /n作曲:Revo ","0","0","217","75","3000","0","false","黑体","0"] -["152","78","0-1","3","_____________________","0","0","132","78","2500","0","false","黑体","0"] -["132","78","1-0","3","_____________________","0","0","112","78","3000","0","false","黑体","0"] -["252","74","0-1","3","歌:YUUKI ","0","0","232","74","2500","0","false","黑体","0"] -["232","74","1-0","3","歌:YUUKI ","0","0","212","74","3000","0","false","黑体","0"] -["129","356","0-1","5.5","你最喜欢的这段旋律(Mélodie)","0","0","129","356","500","0","true","黑体","0"] -["129","356","1-0","3","你最喜欢的这段旋律(Mélodie)","0","0","129","356","500","0","true","黑体","0"] -["40","332","0.3-1","4.5","在天空中回响的","0","0","40","332","500","0","true","黑体","0"] -["64","361","0.3-1","4.5","口琴声(Harmonica)","0","0","64","361","500","0","true","黑体","0"] -["161","305","0.3-1","4.5","天使拥抱着/n 窗沿为框的画布(Toile)","0","0","161","305","500","0","true","黑体","0"] -["233","339","0-1","2","呐…这幅风景画(Paysage)","0","0","233","339","500","0","true","黑体","0"] -["233","339","1-0","2.5","呐…这幅风景画(Paysage)","0","0","233","339","500","0","true","黑体","0"] -["407","359","0-1","2","漂亮吗?","0","0","407","359","500","0","true","黑体","0"] -["407","359","1-0","1","漂亮吗?","0","0","407","359","500","0","true","黑体","0"] -["93","332","0-1","2","景中是(C'est)——","0","0","93","332","500","0","true","黑体","0"] -["93","332","1-0","2.5","景中是(C'est)——","0","0","93","332","500","0","true","黑体","0"] -["116","355","0-1","2","被风带来的…","0","0","116","355","500","0","true","黑体","0"] -["116","355","1-0","2.5","被风带来的…","0","0","116","355","500","0","true","黑体","0"] -["116","355","0-1","2","      淡色的花瓣","0","0","116","355","500","0","true","黑体","0"] -["116","355","1-0","2.5","      淡色的花瓣","0","0","116","355","500","0","true","黑体","0"] -["64","330","0-1","2","景中是(C'est)——","0","0","64","330","500","0","true","黑体","0"] -["64","330","1-0","2.5","景中是(C'est)——","0","0","64","330","500","0","true","黑体","0"] -["93","355","0-1","2","与穹苍紧连的…","0","0","93","355","500","0","true","黑体","0"] -["93","355","1-0","2.5","与穹苍紧连的…","0","0","93","355","500","0","true","黑体","0"] -["228","355","0-1","2","流动的云","0","0","228","355","500","0","true","黑体","0"] -["228","355","1-0","2.5","流动的云","0","0","228","355","500","0","true","黑体","0"] -["318","355","0-1","2","夏之追忆","0","0","318","355","500","0","true","黑体","0"] -["318","355","1-0","2.5","夏之追忆","0","0","318","355","500","0","true","黑体","0"] -["80","332","0-1","2","美丽的音色","0","0","80","332","500","0","true","黑体","0"] -["80","332","1-1","2.5","美丽的音色","0","0","80","332","500","0","true","黑体","0"] -["80","332","0-1","2","      唱歌的少女(Monica)","0","0","80","332","500","0","true","黑体","0"] -["80","332","1-0","2.5","      唱歌的少女(Monica)","0","0","80","332","500","0","true","黑体","0"] -["100","358","1-0","4.5","      指针的前进 →","0","0","100","358","500","0","true","黑体","0"] -["87","334","1-0","4.5","真美呢…","0","0","87","334","500","0","true","黑体","0"] -["87","334","0-1","4.5","     你所说的景色…","0","0","87","334","500","0","true","黑体","0"] -["209","269","1-0","2.5","♪~","30","0","165","236","2500","0","true","宋体","0"] -["209","269","1-0","2.5","♪~","30","0","165","236","2500","0","true","宋体","0"] -["209","269","1-0","2.5","♪~","30","0","165","236","2500","0","true","宋体","0"] -["209","269","1-0","2.5","♪~","30","0","165","236","2500","0","true","宋体","0"] -["209","269","1-0","2.5","♪~","30","0","165","236","2500","0","true","宋体","0"] -["324","277","1-0","2.5","~♬","340","0","382","253","2500","0","true","宋体","0"] -["324","277","1-0","2.5","~♬","340","0","382","253","2500","0","true","宋体","0"] -["324","277","1-0","2.5","~♬","340","0","382","253","2500","0","true","宋体","0"] -["322","253","1-0","2.5","~♩","315","0","365","208","2500","0","true","宋体","0"] -["322","253","1-0","2.5","~♩","315","0","365","208","2500","0","true","宋体","0"] -["322","253","1-0","2.5","~♩","315","0","365","208","2500","0","true","宋体","0"] -["219","234","1-0","2.5","♫~","40","0","188","195","2500","0","true","宋体","0"] -["219","234","1-0","2.5","♫~","40","0","188","195","2500","0","true","宋体","0"] -["219","234","1-0","2.5","♫~","40","0","188","195","2500","0","true","宋体","0"] -["283","251","1-0","2.5","~♭","280","0","307","221","2500","0","true","宋体","0"] -["283","251","1-0","2.5","~♭","280","0","307","221","2500","0","true","宋体","0"] -["283","251","1-0","2.5","~♭","280","0","307","221","2500","0","true","宋体","0"] -["230","243","1-0","2.5","♬~","70","0","210","201","2500","0","true","宋体","0"] -["230","243","1-0","2.5","♬~","70","0","210","201","2500","0","true","宋体","0"] -["230","243","1-0","2.5","♬~","70","0","210","201","2500","0","true","宋体","0"] -["260","242","1-0","2.5","~♪","270","0","276","196","2500","0","true","宋体","0"] -["260","242","1-0","2.5","~♪","270","0","276","196","2500","0","true","宋体","0"] -["260","242","1-0","2.5","~♪","270","0","276","196","2500","0","true","宋体","0"] -["260","242","1-0","2.5","~♪","270","0","276","196","2500","0","true","宋体","0"] -["260","242","1-0","2.5","~♪","270","0","276","196","2500","0","true","宋体","0"] -["260","242","1-0","2.5","~♪","270","0","276","196","2500","0","true","宋体","0"] -["260","242","1-0","2.5","~♪","270","0","276","196","2500","0","true","宋体","0"] -["260","242","1-0","2.5","~♪","270","0","276","196","2500","0","true","宋体","0"] -["260","242","1-0","2.5","~♪","270","0","276","196","2500","0","true","宋体","0"] -["261","208","1-0","2.5","♪~","80","0","239","170","2500","0","true","宋体","0"] -["261","208","1-0","2.5","♪~","80","0","239","170","2500","0","true","宋体","0"] -["261","208","1-0","2.5","♪~","80","0","239","170","2500","0","true","宋体","0"] -["261","208","1-0","2.5","♪~","80","0","239","170","2500","0","true","宋体","0"] -["261","208","1-0","2.5","♪~","80","0","239","170","2500","0","true","宋体","0"] -["261","208","1-0","2.5","♪~","80","0","239","170","2500","0","true","宋体","0"] -["261","208","1-0","2.5","♪~","80","0","239","170","2500","0","true","宋体","0"] -["261","208","1-0","2.5","♪~","80","0","239","170","2500","0","true","宋体","0"] -["261","208","1-0","2.5","♪~","80","0","239","170","2500","0","true","宋体","0"] -["219","234","1-0","2.5","♫~","40","0","188","195","2500","0","true","宋体","0"] -["219","234","1-0","2.5","♫~","40","0","188","195","2500","0","true","宋体","0"] -["219","234","1-0","2.5","♫~","40","0","188","195","2500","0","true","宋体","0"] -["260","242","1-0","2.5","~♪","270","0","276","196","2500","0","true","宋体","0"] -["322","253","1-0","2.5","~♩","315","0","365","208","2500","0","true","宋体","0"] -["230","243","1-0","2.5","♬~","70","0","210","201","2500","0","true","宋体","0"] -["310","294","1-0","2.5","~♭","5","0","359","312","2500","0","true","宋体","0"] -["324","282","1-0","2.5","~♩","350","0","391","267","2500","0","true","宋体","0"] -["324","282","1-0","2.5","~♩","350","0","391","267","2500","0","true","宋体","0"] -["324","282","1-0","2.5","~♩","350","0","391","267","2500","0","true","宋体","0"] -["206","276","1-0","2.5","♫~","20","0","179","266","2500","0","true","宋体","0"] -["322","253","1-0","2.5","~♩","315","0","365","208","2500","0","true","宋体","0"] -["230","243","1-0","2.5","♬~","70","0","210","201","2500","0","true","宋体","0"] -["188","317","1-0","3.5","♩~","350","0","138","336","3500","0","true","宋体","0"] -["209","269","1-0","2.5","♪~","30","0","165","236","2500","0","true","宋体","0"] -["310","294","1-0","2.5","~♭","5","0","359","312","2500","0","true","宋体","0"] -["260","242","1-0","2.5","~♪","270","0","276","196","2500","0","true","宋体","0"] -["260","242","1-0","2.5","~♪","270","0","276","196","2500","0","true","宋体","0"] -["310","294","1-0","2.5","~♭","5","0","359","312","2500","0","true","宋体","0"] -["481","66","0.7-1","4.5","♥","80","45","195","176","4500","0","true","宋体","0"] -["502","13","0.7-1","4.5","♥","70","45","236","220","4500","0","true","宋体","0"] -["281","12","0.7-1","4.5","♥","60","45","72","161","4500","0","true","宋体","0"] -["528","3","0.7-1","4.5","♥","60","45","367","123","4500","0","true","宋体","0"] -["430","43","0.7-1","4.5","♥","60","45","262","125","4500","0","true","宋体","0"] -["405","12","0.7-1","4.5","♥","45","45","180","243","4500","0","true","宋体","0"] -["348","4","0.7-1","4.5","♥","40","45","33","145","4500","0","true","宋体","0"] -["534","86","0.7-1","4.5","♥","40","45","48","288","4500","0","true","宋体","0"] -["222","58","0.7-1","4.5","♥","20","45","120","300","4500","0","true","宋体","0"] -["483","95","0.7-1","4.5","♥","20","45","393","266","4500","0","true","宋体","0"] -["374","32","0.7-1","4.5","♥","20","45","194","248","4500","0","true","宋体","0"] -["174","28","0.7-1","4.5","♥","30","45","222","268","4500","0","true","宋体","0"] -["479","175","0.7-1","4.5","♥","30","45","62","282","4500","0","true","宋体","0"] -["344","54","0.7-1","4.5","♥","5","45","254","238","4500","0","true","宋体","0"] -["290","32","0.7-1","4.5","♥","5","45","44","302","4500","0","true","宋体","0"] -["276","61","0-1","3","其れは(C'est)——","350","0","69","93","2000","0","true","黑体","0"] -["279","61","0-1","3","其れは(C'est)——","350","0","72","93","2000","0","true","黑体","0"] -["282","61","0-1","3","其れは(C'est)——","350","0","75","93","2000","0","true","黑体","0"] -["285","61","0-1","3","其れは(C'est)——","350","0","78","93","2000","0","true","黑体","0"] -["132","190","1-1","2.5","█","0","0","112","190","500","0","ture","黑体","0"] -["112","190","1-0","2","█","0","0","92","190","2000","0","ture","黑体","0"] -["132","190","1-1","2.5","風","0","0","112","190","500","0","ture","黑体","0"] -["112","190","1-0","2","風","0","0","92","190","2000","0","ture","黑体","0"] -["135","190","1-1","2.5"," が運んだ","0","0","115","190","500","0","false","黑体","0"] -["115","190","1-0","2"," が運んだ","0","0","95","190","2000","0","false","黑体","0"] -["135","190","1-1","2.5","/n 淡い花弁","0","0","115","190","500","0","false","黑体","0"] -["115","190","1-0","2","/n 淡い花弁","0","0","95","190","2000","0","false","黑体","0"] -["210","100","0-1","3.5","春の追想","0","0","220","110","1500","0","ture","MS UI Gothic","0"] -["230","120","0-1","3","春の追想","0","0","220","110","1500","0","ture","MS UI Gothic","0"] -["210","120","0-1","3","春の追想","0","0","220","110","1500","0","ture","MS UI Gothic","0"] -["230","100","0-1","3","春の追想","0","0","220","110","1500","0","ture","MS UI Gothic","0"] -["220","110","0.8-0","2","春の追想","0","0","190","110","2000","0","ture","MS UI Gothic","0"] -["220","110","0.8-0","2","春の追想","0","0","250","110","2000","0","ture","MS UI Gothic","0"] -["212","98","0-1","3.5","春","0","0","222","108","1500","0","ture","MS UI Gothic","0"] -["232","118","0-1","3","春","0","0","222","108","1500","0","ture","MS UI Gothic","0"] -["212","118","0-1","3","春","0","0","222","108","1500","0","ture","MS UI Gothic","0"] -["232","98","0-1","3","春","0","0","222","108","1500","0","ture","MS UI Gothic","0"] -["222","108","0.8-0","2","春","0","0","192","108","2000","0","ture","MS UI Gothic","0"] -["222","108","0.8-0","2","春","0","0","252","108","2000","0","ture","MS UI Gothic","0"] -["250","185","1-1","3","綺","0","0","250","185","500","0","true","黑体","0"] -["250","165","0.5-1","1","♪","0","0","250","185","1000","0","true","宋体","0"] -["250","205","0.5-1","1","♪","0","0","250","185","1000","0","true","宋体","0"] -["250","185","1-1","3"," 麗  ","0","0","250","185","500","0","true","黑体","0"] -["250","165","0.5-1","1"," ♬  ","0","0","250","185","1000","0","true","宋体","0"] -["250","205","0.5-1","1"," ♬  ","0","0","250","185","1000","0","true","宋体","0"] -["250","185","1-1","3","  な ","0","0","250","185","500","0","true","黑体","0"] -["250","165","0.5-1","1","  ♩ ","0","0","250","185","1000","0","true","宋体","0"] -["250","205","0.5-1","1","  ♩ ","0","0","250","185","1000","0","true","宋体","0"] -["250","185","1-1","3","   音","0","0","250","185","500","0","true","黑体","0"] -["250","165","0.5-1","1","   ♫","0","0","250","185","1000","0","true","宋体","0"] -["250","205","0.5-1","1","   ♫","0","0","250","185","1000","0","true","宋体","0"] -/n/n/n/n/n/n唄う少女 (Monica)/n/n/n/n/n/n/n/n/n/n/n/n/n -/n/n/n/n/n/n唄う少女 (Monica)/n/n/n/n/n/n/n/n/n/n/n/n/n -/n/n/n/n/n/n 唄う少女 (Monica) /n/n/n/n/n/n/n/n/n/n/n/n/n -/n/n/n/n/n/n 唄う少女 (Monica) /n/n/n/n/n/n/n/n/n/n/n/n/n -/n/n/n/n/n/n  唄う少女 (Monica)  /n/n/n/n/n/n/n/n/n/n/n/n/n -/n/n/n/n/n/n  唄う少女 (Monica)  /n/n/n/n/n/n/n/n/n/n/n/n/n -["188","121","1-0","4.5","█████████","0","0","188","121","500","0","true","黑体","0"] -["190","121","1-0","4.5","唄う少女 (Monica)","0","0","190","121","500","0","false","黑体","0"] -/n/n/n/n/n/n/n/n鳥の囀/n/n/n/n/n/n/n/n/n/n/n -/n/n/n/n/n/n/n/n鳥の囀/n/n/n/n/n/n/n/n/n/n/n -/n/n/n/n/n/n/n/n 鳥の囀 /n/n/n/n/n/n/n/n/n/n/n -/n/n/n/n/n/n/n/n 鳥の囀 /n/n/n/n/n/n/n/n/n/n/n -/n/n/n/n/n/n/n/n  鳥の囀  /n/n/n/n/n/n/n/n/n/n/n -/n/n/n/n/n/n/n/n  鳥の囀  /n/n/n/n/n/n/n/n/n/n/n -["243","160","1-0","4.5","███","0","0","243","160","500","0","true","黑体","0"] -["243","160","1-0","4.5","鳥の囀","0","0","243","160","500","0","false","黑体","0"] -["105","196","1-1","4.5","◯","0","0","105","196","500","0","false","宋体","0"] -["158","209","1-1","4.5","12","0","0","158","209","500","0","false","宋体","0"] -["164","290","1-1","4.5","6","0","0","164","290","500","0","false","宋体","0"] -["203","247","1-1","4.5","3","0","0","203","247","500","0","false","宋体","0"] -["125","247","1-1","4.5","9","0","0","125","247","500","0","false","宋体","0"] -["164","244","1-1","4.5","。","0","0","164","244","500","0","false","宋体","0"] -["155","230","1-1","4.5","↑","0","0","155","230","500","0","false","宋体","0"] -["169","239","1-0","1","→","0","0","169","239","500","0","false","宋体","0"] -["136","228","1-1","4.5","-","30","0","136","228","500","0","false","宋体","0"] -["157","214","1-1","4.5","-","60","0","157","214","500","0","false","宋体","0"] -["206","225","1-1","4.5","-","120","0","206","225","500","0","false","黑体","0"] -["217","245","1-1","4.5","-","150","0","217","245","500","0","false","黑体","0"] -["205","270","1-1","4.5","-","21","0","205","270","500","0","false","黑体","0"] -["210","296","1-1","4.5","-","210","0","210","296","500","0","false","黑体","0"] -["188","307","1-1","4.5","-","240","0","188","307","500","0","false","黑体","0"] -["135","297","1-1","4.5","-","300","0","135","297","500","0","false","黑体","0"] -["126","276","1-1","4.5","-","330","0","126","276","500","0","false","黑体","0"] -["171","239","1-0","1","→","10","0","171","239","500","0","false","宋体","0"] -["174","239","1-0","1","→","20","0","174","239","500","0","false","宋体","0"] -["176","239","1-0","1","→","30","0","176","239","500","0","false","宋体","0"] -["180","239","1-0","1","→","40","0","180","239","500","0","false","宋体","0"] -["210","181","1-0","4.5","針","30","0","210","181","500","0","true","黑体","0"] -["247","209","1-0","4.5","は","60","0","247","209","500","0","true","黑体","0"] -["261","249","1-0","4.5","進","90","0","261","249","500","0","true","黑体","0"] -["254","295","1-0","4.5","ん","120","0","254","295","500","0","true","黑体","0"] -["228","329","1-0","4.5","だ","150","0","228","329","500","0","true","黑体","0"] -["428","132","0-1","3","其れは(C'est)——","30","0","292","45","2000","0","true","黑体","0"] -["425","132","0-1","3","其れは(C'est)——","30","0","289","45","2000","0","true","黑体","0"] -["422","132","0-1","3","其れは(C'est)——","30","0","286","45","2000","0","true","黑体","0"] -["419","132","0-1","3","其れは(C'est)——","30","0","283","45","2000","0","true","黑体","0"] -["96","170","1-0","3.5","█","0","0","96","170","500","0","false","黑体","0"] -["96","170","1-0","3.5","蒼","0","0","96","170","500","0","false","黑体","0"] -["138","186","1-0","3.5","が繫いで","0","0","138","186","500","0","true","黑体","0"] -/n/n/n/n/n流/n/n/n/n/n/n/n/n/n -/n/n/n/n/n流/n/n/n/n/n/n/n/n/n -/n/n/n/n/n/n れ /n/n/n/n/n/n/n/n -/n/n/n/n/n/n れ /n/n/n/n/n/n/n/n -/n/n/n/n/n/n/n  る  /n/n/n/n/n/n/n -/n/n/n/n/n/n/n  る  /n/n/n/n/n/n/n -/n/n/n/n/n/n/n/n   雲   /n/n/n/n/n/n -/n/n/n/n/n/n/n/n   雲   /n/n/n/n/n/n -["-10","122","0.5-1","4.5","███████","0","0","-10","122","500","0","false","MS UI Gothic","0"] -["-10","122","0.5-1","4.5"," ██████","0","0","-10","122","500","0","false","MS UI Gothic","0"] -["198","96","0.4-1","2.5","●","0","0","198","96","2500","0","false","黑体","0"] -["147","140","0.4-1","2.5","●","0","0","147","140","2500","0","false","黑体","0"] -["269","142","0.4-1","2.5","●","0","0","269","142","2500","0","false","黑体","0"] -["214","152","0.4-1","2.5","●","0","0","214","152","2500","0","false","黑体","0"] -["257","134","0.5-1","2.5","流","0","0","257","134","500","0","true","黑体","0"] -["257","134","0.5-1","2.5","/nれ","0","0","257","134","500","0","true","黑体","0"] -["257","134","0.5-1","2.5","/n/nる","0","0","257","134","500","0","true","黑体","0"] -["257","134","0.5-1","2.5","/n/n/n雲","0","0","257","134","500","0","true","黑体","0"] -["304","84","0-1","3","夏","0","0","324","94","1500","0","true","MS UI Gothic","0"] -["304","104","0-1","3","夏","0","0","324","94","1500","0","true","MS UI Gothic","0"] -["314","84","0-1","3"," の","0","0","324","94","1500","0","true","MS UI Gothic","0"] -["314","104","0-1","3"," の","0","0","324","94","1500","0","true","MS UI Gothic","0"] -["334","84","0-1","3","  追","0","0","324","94","1500","0","true","MS UI Gothic","0"] -["334","104","0-1","3","  追","0","0","324","94","1500","0","true","MS UI Gothic","0"] -["344","84","0-1","3","   想","0","0","324","94","1500","0","true","MS UI Gothic","0"] -["344","104","0-1","3","   想","0","0","324","94","1500","0","true","MS UI Gothic","0"] -/n/n/n/n/n/n   綺   /n/n/n/n/n/n/n/n -/n/n/n/n/n/n  綺  /n/n/n/n/n/n/n/n -/n/n/n/n/n/n 綺 /n/n/n/n/n/n/n/n -/n/n/n/n/n/n綺/n/n/n/n/n/n/n/n -/n/n/n/n/n/n/n   麗   /n/n/n/n/n/n/n -/n/n/n/n/n/n/n  麗  /n/n/n/n/n/n/n -/n/n/n/n/n/n/n 麗 /n/n/n/n/n/n/n -/n/n/n/n/n/n/n麗/n/n/n/n/n/n/n -/n/n/n/n/n/n/n/n   な   /n/n/n/n/n/n -/n/n/n/n/n/n/n/n  な  /n/n/n/n/n/n -/n/n/n/n/n/n/n/n な /n/n/n/n/n/n -/n/n/n/n/n/n/n/nな/n/n/n/n/n/n -/n/n/n/n/n/n/n/n/n   音   /n/n/n/n/n -/n/n/n/n/n/n/n/n/n  音  /n/n/n/n/n -/n/n/n/n/n/n/n/n/n 音 /n/n/n/n/n -/n/n/n/n/n/n/n/n/n音/n/n/n/n/n -["221","173","1-1","0.1","謡う少女(Monica)","0","70","221","173","500","0","true","黑体","0"] -["221","173","1-1","0.1","謡う少女(Monica)","0","65","221","173","500","0","true","黑体","0"] -["221","173","1-1","0.1","謡う少女(Monica)","0","60","221","173","500","0","true","黑体","0"] -["221","173","1-1","0.1","謡う少女(Monica)","0","55","221","173","500","0","true","黑体","0"] -["221","173","1-1","0.1","謡う少女(Monica)","0","50","221","173","500","0","true","黑体","0"] -["221","173","1-1","0.1","謡う少女(Monica)","0","45","221","173","500","0","true","黑体","0"] -["221","173","1-1","0.1","謡う少女(Monica)","0","40","221","173","500","0","true","黑体","0"] -["221","173","1-1","0.1","謡う少女(Monica)","0","35","221","173","500","0","true","黑体","0"] -["221","173","1-1","0.1","謡う少女(Monica)","0","30","221","173","500","0","true","黑体","0"] -["221","173","1-1","0.1","謡う少女(Monica)","0","25","221","173","500","0","true","黑体","0"] -["221","173","1-1","0.1","謡う少女(Monica)","0","20","221","173","500","0","true","黑体","0"] -["221","173","1-1","0.1","謡う少女(Monica)","0","15","221","173","500","0","true","黑体","0"] -["221","173","1-1","0.1","謡う少女(Monica)","0","10","221","173","500","0","true","黑体","0"] -["221","173","1-1","0.1","謡う少女(Monica)","0","5","221","173","500","0","true","黑体","0"] -["221","173","1-1","0.8","謡う少女(Monica)","0","0","221","173","500","0","true","黑体","0"] -["60","-10","1-1","4.5","█████████████████████████████","90","0","60","-10","500","0","false","MS UI Gothic","0"] -["60","-10","1-1","4.5"," ████████████████████████████","90","0","60","-10","500","0","false","MS UI Gothic","0"] -["50","-10","1-1","4.5","█████████████████████████████","90","0","50","-10","500","0","false","MS UI Gothic","0"] -["50","-10","1-1","4.5"," ████████████████████████████","90","0","50","-10","500","0","false","MS UI Gothic","0"] -["40","-10","1-1","4.5","█████████████████████████████","90","0","40","-10","500","0","false","MS UI Gothic","0"] -["40","-10","1-1","4.5"," ████████████████████████████","90","0","40","-10","500","0","false","MS UI Gothic","0"] -["30","-10","1-1","4.5","█████████████████████████████","90","0","30","-10","500","0","false","MS UI Gothic","0"] -["30","-10","1-1","4.5"," ████████████████████████████","90","0","30","-10","500","0","false","MS UI Gothic","0"] -["20","-10","1-1","4.5","█████████████████████████████","90","0","20","-10","500","0","false","MS UI Gothic","0"] -["20","-10","1-1","4.5"," ████████████████████████████","90","0","20","-10","500","0","false","MS UI Gothic","0"] -["40","59","1-1","4.5","███","350","0","40","59","500","0","false","黑体","0"] -["40","59","1-1","4.5"," ██","350","0","40","59","500","0","false","黑体","0"] -["98","50","1-1","4.5","███","355","0","98","50","500","0","false","黑体","0"] -["98","50","1-1","4.5"," ██","355","0","98","50","500","0","false","黑体","0"] -["153","46","1-1","4.5","███","0","0","153","46","500","0","false","黑体","0"] -["153","46","1-1","4.5"," ██","0","0","153","46","500","0","false","黑体","0"] -["202","49","1-1","4.5","███","10","0","202","49","500","0","false","黑体","0"] -["202","49","1-1","4.5"," ██","10","0","202","49","500","0","false","黑体","0"] -["238","58","1-1","4.5","████","15","0","238","58","500","0","false","MS UI Gothic","0"] -["238","58","1-1","4.5"," ███","15","0","238","58","500","0","false","MS UI Gothic","0"] -["48","51","1-1","4.5","███","350","0","48","51","500","0","false","黑体","0"] -["48","51","1-1","4.5"," ██","350","0","48","51","500","0","false","黑体","0"] -["46","50","1-1","4.5","█","355","0","46","50","500","0","false","黑体","0"] -["46","50","1-1","4.5"," █","355","0","46","50","500","0","false","黑体","0"] -["98","43","1-1","4.5","███","355","0","98","43","500","0","false","黑体","0"] -["98","43","1-1","4.5"," ██","355","0","98","43","500","0","false","黑体","0"] -["144","40","1-1","4.5","███","2","0","144","40","500","0","false","黑体","0"] -["144","40","1-1","4.5"," ██","2","0","144","40","500","0","false","黑体","0"] -["183","42","1-1","4.5","████","7","0","183","42","500","0","false","MS UI Gothic","0"] -["183","42","1-1","4.5"," ███","7","0","183","42","500","0","false","MS UI Gothic","0"] -["207","45","1-1","4.5","█████","10","0","207","45","500","0","false","MS UI Gothic","0"] -["207","45","1-1","4.5"," ████","10","0","207","45","500","0","false","MS UI Gothic","0"] -["232","50","1-1","4.5","███████","18","0","232","50","500","0","false","MS UI Gothic","0"] -["232","50","1-1","4.5"," ██████","18","0","232","50","500","0","false","MS UI Gothic","0"] -["121","5","1-1","4.5","◆","15","60","121","5","500","0","false","黑体","0"] -["250","71","1-1","4.5","◆","320","70","250","71","500","0","false","黑体","0"] -["252","26","1-1","4.5","◆","45","70","252","26","500","0","false","黑体","0"] -["157","26","1-1","4.5","◆","60","60","157","26","500","0","false","黑体","0"] -["180","42","1-1","4.5","█████","340","0","180","42","500","0","false","MS UI Gothic","0"] -["180","42","1-1","4.5"," ████","340","0","180","42","500","0","false","MS UI Gothic","0"] -["209","32","1-1","4.5","████","350","0","209","32","500","0","false","MS UI Gothic","0"] -["209","32","1-1","4.5"," ███","350","0","209","32","500","0","false","MS UI Gothic","0"] -["241","7","1-1","4.5","◆","33","65","241","7","500","0","false","黑体","0"] -["215","13","1-1","4.5","◆","344","65","215","13","500","0","false","黑体","0"] -["247","71","1-1","4.5","◆","334","65","247","71","500","0","false","黑体","0"] -["285","30","1-1","4.5","◆","60","60","285","30","500","0","false","黑体","0"] -["127","69","1-1","4.5","◆","320","60","127","69","500","0","false","黑体","0"] -["140","12","1-1","4.5","◆","40","60","140","12","500","0","false","黑体","0"] -["95","155","1-1","4.5","●","90","60","95","155","500","0","true","黑体","0"] -["70","162","1-1","4.5","■","355","0","70","162","500","0","false","黑体","0"] -["70","169","1-1","4.5","■","357","0","70","169","500","0","false","黑体","0"] -["70","181","1-1","4.5","●","20","60","70","181","500","0","false","黑体","0"] -["82","168","1-1","4.5","■","25","62","82","168","500","0","false","黑体","0"] -["72","170","1-1","4.5","●","20","60","72","170","500","0","false","黑体","0"] -["76","158","1-1","4.5","●","15","60","76","158","500","0","false","黑体","0"] -["59","172","1-1","4.5","__","0","0","59","172","500","0","false","黑体","0"] -["60","150","1-1","4.5","__","15","0","60","150","500","0","false","黑体","0"] -["57","158","1-1","4.5","__","350","0","57","158","500","0","false","黑体","0"] -["61","142","1-1","4.5","__","18","0","61","142","500","0","false","黑体","0"] -["62","122","1-1","4.5","_","18","0","62","122","500","0","false","黑体","0"] -["75","128","1-1","4.5","_","60","0","75","128","500","0","false","黑体","0"] -["75","130","1-1","4.5","__","48","0","75","130","500","0","false","黑体","0"] -["85","146","1-1","4.5","▃","90","0","85","146","500","0","false","黑体","0"] -["70","165","1-1","4.5","■","0","0","70","165","500","0","false","黑体","0"] -["68","176","1-1","4.5","■","0","0","68","176","500","0","false","黑体","0"] -["50","198","1-1","4.5","_","290","0","50","198","500","0","false","黑体","0"] -["50","192","1-1","4.5","_","300","0","50","192","500","0","false","黑体","0"] -["57","182","1-1","4.5","_","330","0","57","182","500","0","false","黑体","0"] -["70","161","0.6-0.6","4.5"," □□/n/n/n/n/n/n □□□/n","10","50","70","161","500","0","false","黑体","0"] -["70","161","0.6-0.6","4.5","/n □□/n/n/n/n□□□□/n/n","10","50","70","161","500","0","false","黑体","0"] -["70","161","0.6-0.6","4.5","/n/n □□□/n/n□□□□/n/n/n","10","50","70","161","500","0","false","黑体","0"] -["70","161","0.6-0.6","4.5","/n/n/n□□□□/n/n/n/n","10","50","70","161","500","0","false","黑体","0"] -["70","167","0.6-0.6","4.5"," □□/n/n/n/n/n/n □□□/n","10","50","70","167","500","0","false","黑体","0"] -["70","167","0.6-0.6","4.5","/n □□/n/n/n/n□□□□/n/n","10","50","70","167","500","0","false","黑体","0"] -["70","167","0.6-0.6","4.5","/n/n □□□/n/n□□□□/n/n/n","10","50","70","167","500","0","false","黑体","0"] -["70","167","0.6-0.6","4.5","/n/n/n□□□□/n/n/n/n","10","50","70","167","500","0","false","黑体","0"] -["148","127","0-1","1.5","蟬の時雨","0","0","148","127","500","0","true","黑体","0"] -["146","125","0-1","1.5","蟬","0","0","146","125","500","0","true","黑体","0"] -["148","127","1-1","3","蟬   ","0","0","148","227","800","100","true","黑体","1"] -["148","127","1-1","3"," の  ","0","0","148","227","800","300","true","黑体","1"] -["148","127","1-1","3","  時 ","0","0","148","227","800","500","true","黑体","1"] -["148","127","1-1","3","   雨","0","0","148","227","800","700","true","黑体","1"] -["146","125","1-1","3","蟬","0","0","146","225","800","100","true","黑体","1"] -["111","151","1-1","4.5","_","110","0","111","151","500","0","false","黑体","0"] -["107","150","1-1","4.5","_","96","0","107","150","500","0","false","黑体","0"] -["305","176","1-1","4.5","◯","0","0","305","176","500","0","false","宋体","0"] -["358","189","1-1","4.5","12","0","0","358","189","500","0","false","宋体","0"] -["364","270","1-1","4.5","6","0","0","364","270","500","0","false","宋体","0"] -["403","227","1-1","4.5","3","0","0","403","227","500","0","false","宋体","0"] -["325","227","1-1","4.5","9","0","0","325","227","500","0","false","宋体","0"] -["364","224","1-1","4.5","。","0","0","364","224","500","0","false","宋体","0"] -["336","208","1-1","4.5","-","30","0","336","208","500","0","false","宋体","0"] -["357","194","1-1","4.5","-","60","0","357","194","500","0","false","宋体","0"] -["406","205","1-1","4.5","-","120","0","406","205","500","0","false","黑体","0"] -["417","225","1-1","4.5","-","150","0","417","225","500","0","false","黑体","0"] -["405","250","1-1","4.5","-","21","0","405","250","500","0","false","黑体","0"] -["410","276","1-1","4.5","-","210","0","410","276","500","0","false","黑体","0"] -["388","287","1-1","4.5","-","240","0","388","287","500","0","false","黑体","0"] -["335","277","1-1","4.5","-","300","0","335","277","500","0","false","黑体","0"] -["326","256","1-1","4.5","-","330","0","326","256","500","0","false","黑体","0"] -["370","224","1-1","4.5","→","0","0","370","224","500","0","true","黑体","0"] -["394","237","1-0","1","→","90","0","394","237","500","0","true","黑体","0"] -["393","240","1-0","1","→","100","0","393","240","500","0","true","黑体","0"] -["392","243","1-0","1","→","110","0","392","243","500","0","true","黑体","0"] -["391","246","1-0","1","→","120","0","391","246","500","0","true","黑体","0"] -["390","249","1-0","1","→","130","0","390","249","500","0","true","黑体","0"] -["429","286","1-0","4.5","針","300","0","429","286","500","0","true","黑体","0"] -["399","306","1-0","4.5","は","330","0","399","306","500","0","true","黑体","0"] -["361","312","1-0","4.5","進","0","0","361","312","500","0","true","黑体","0"] -["323","294","1-0","4.5","ん","30","0","323","294","500","0","true","黑体","0"] -["304","262","1-0","4.5","だ","60","0","304","262","500","0","true","黑体","0"] -["219","49","1-1","4.5","綺","0","0","239","69","500","0","true","黑体","0"] -["229","49","1-1","4.5"," 麗","0","0","239","69","500","0","true","黑体","0"] -["249","49","1-1","4.5","  だ","0","0","239","69","500","0","true","黑体","0"] -["259","49","1-1","4.5","   と","0","0","239","69","500","0","true","黑体","0"] -["219","158","0.5-0.5","4.5","綺","180","180","239","138","500","0","true","黑体","0"] -["229","158","0.5-0.5","4.5"," 麗","180","180","239","138","500","0","true","黑体","0"] -["249","158","0.5-0.5","4.5","  だ","180","180","239","138","500","0","true","黑体","0"] -["259","158","0.5-0.5","4.5","   と","180","180","239","138","500","0","true","黑体","0"] -["-102","173","1-1","4.5","君が言った  ","0","0","102","173","1000","0","true","黑体","0"] -["-102","228","0.5-0.5","4.5","君が言った  ","180","180","102","228","1000","0","true","黑体","0"] -["548","310","0.5-0.5","4","きっと    ","180","180","248","310","1000","0","true","黑体","0"] -["548","258","1-1","4","きっと    ","0","0","248","258","1000","0","true","黑体","0"] -["-102","173","1-1","4.5","     景色","0","0","102","173","1000","0","true","黑体","0"] -["-102","228","0.5-0.5","4.5","     景色","180","180","102","228","1000","0","true","黑体","0"] -["548","310","0.5-0.5","4","   忘れない","180","180","248","310","1000","0","true","黑体","0"] -["548","258","1-1","4","   忘れない","0","0","248","258","1000","0","true","黑体","0"] -["102","173","1-1","4.5","君が言った","0","0","602","173","2000","0","true","黑体","0"] -["102","228","0.5-0.5","4.5","君が言った","180","180","602","228","2000","0","true","黑体","0"] -["102","173","1-1","4.5","     景色","0","0","602","173","2000","0","true","黑体","0"] -["102","228","0.5-0.5","4.5","     景色","180","180","602","228","2000","0","true","黑体","0"] -["248","310","0.5-0.5","4.5","きっと    ","180","180","-200","310","2000","0","true","黑体","0"] -["248","258","1-1","4.5","きっと    ","0","0","-200","258","2000","0","true","黑体","0"] -["248","310","0.5-0.5","4.5","   忘れない","180","180","-200","310","2000","0","true","黑体","0"] -["248","258","1-1","4.5","   忘れない","0","0","-200","258","2000","0","true","黑体","0"] -["79","134","1-0","4.5","美しきもの","0","0","79","134","500","0","true","黑体","0"] -["79","134","1-0","4.5","美しきもの","10","0","79","134","500","0","true","黑体","0"] -["79","134","1-0","4.5","美しきもの/n","20","0","79","134","500","0","true","黑体","0"] -["79","134","1-0","4.5","美しきもの/n","30","0","79","134","500","0","true","黑体","0"] -["79","134","1-0","4.5","美しきもの/n","40","0","79","134","500","0","true","黑体","0"] -["79","134","1-0","4.5","美しきもの","50","0","79","134","500","0","true","黑体","0"] -["79","134","1-0","4.5","美しきもの/n","60","0","79","134","500","0","true","黑体","0"] -["79","134","1-0","4.5","美しきもの/n","70","0","79","134","500","0","true","黑体","0"] -["79","134","1-0","4.5","美しきもの/n","80","0","79","134","500","0","true","黑体","0"] -["79","134","1-0","4.5","美しきもの","90","0","79","134","500","0","true","黑体","0"] -["79","134","1-0","4.5","美しきもの/n","100","0","79","134","500","0","true","黑体","0"] -["79","134","1-0","4.5","美しきもの","110","0","79","134","500","0","true","黑体","0"] -["79","134","1-0","4.5","美しきもの/n","120","0","79","134","500","0","true","黑体","0"] -["79","134","1-0","4.5","美しきもの/n","130","0","79","134","500","0","true","黑体","0"] -["79","134","1-0","4.5","美しきもの/n","140","0","79","134","500","0","true","黑体","0"] -["79","134","1-0","4.5","美しきもの/n","150","0","79","134","500","0","true","黑体","0"] -["79","134","1-0","4.5","美しきもの/n","160","0","79","134","500","0","true","黑体","0"] -["79","134","1-0","4.5","美しきもの/n","170","0","79","134","500","0","true","黑体","0"] -["79","134","1-0","4.5","美しきもの/n","180","0","79","134","500","0","true","黑体","0"] -["79","134","1-0","4.5","美しきもの","190","0","79","134","500","0","true","黑体","0"] -["79","134","1-0","4.5","美しきもの","200","0","79","134","500","0","true","黑体","0"] -["79","134","1-0","4.5","美しきもの/n","210","0","79","134","500","0","true","黑体","0"] -["79","134","1-0","4.5","美しきもの/n","220","0","79","134","500","0","true","黑体","0"] -["79","134","1-0","4.5","美しきもの/n","230","0","79","134","500","0","true","黑体","0"] -["79","134","1-0","4.5","美しきもの/n","240","0","79","134","500","0","true","黑体","0"] -["79","134","1-0","4.5","美しきもの","250","0","79","134","500","0","true","黑体","0"] -["79","134","1-0","4.5","美しきもの","260","0","79","134","500","0","true","黑体","0"] -["79","134","1-0","4.5","美しきもの/n","270","0","79","134","500","0","true","黑体","0"] -["79","134","1-0","4.5","美しきもの/n","280","0","79","134","500","0","true","黑体","0"] -["79","134","1-0","4.5","美しきもの/n","290","0","79","134","500","0","true","黑体","0"] -["79","134","1-0","4.5","美しきもの/n","300","0","79","134","500","0","true","黑体","0"] -["79","134","1-0","4.5","美しきもの","310","0","79","134","500","0","true","黑体","0"] -["79","134","1-0","4.5","美しきもの","320","0","79","134","500","0","true","黑体","0"] -["79","134","1-0","4.5","美しきもの/n","330","0","79","134","500","0","true","黑体","0"] -["79","134","1-0","4.5","美しきもの/n","340","0","79","134","500","0","true","黑体","0"] -["79","134","1-0","4.5","美しきもの","350","0","79","134","500","0","true","黑体","0"] -["79","134","1-1","4","美しきもの","360","0","79","134","500","0","true","黑体","0"] -["308","203","1-0","2","集める為に","0","0","308","203","500","0","true","黑体","0"] -["308","203","1-0","2","集める為に","0","0","308","203","500","0","true","黑体","0"] -["308","203","1-0","2","集める為に","0","0","308","203","500","0","true","黑体","0"] -["308","203","1-0","2","集める為に/n","0","0","308","203","500","0","true","黑体","0"] -["308","203","1-0","2","集める為に/n","0","0","308","203","500","0","true","黑体","0"] -["308","203","1-0","2","集める為に/n","0","0","308","203","500","0","true","黑体","0"] -["308","203","1-0","2","集める為に/n","0","0","308","203","500","0","true","黑体","0"] -["308","203","1-0","2","集める為に/n","0","0","308","203","500","0","true","黑体","0"] -["308","203","1-0","2","集める為に/n","0","0","308","203","500","0","true","黑体","0"] -["308","203","1-0","2","集める為に","0","0","308","203","500","0","true","黑体","0"] -["308","203","1-0","2","集める為に/n","0","0","308","203","500","0","true","黑体","0"] -["308","203","1-0","2","集める為に/n","0","0","308","203","500","0","true","黑体","0"] -["308","203","1-0","2","集める為に","0","0","308","203","500","0","true","黑体","0"] -["308","203","1-0","2","集める為に/n","0","0","308","203","500","0","true","黑体","0"] -["308","203","1-0","2","集める為に","0","0","308","203","500","0","true","黑体","0"] -["308","203","1-0","2","集める為に/n","0","0","308","203","500","0","true","黑体","0"] -["308","203","1-0","2","集める為に/n","0","0","308","203","500","0","true","黑体","0"] -["308","203","1-0","2","集める為に/n","0","0","308","203","500","0","true","黑体","0"] -["308","203","1-0","2","集める為に/n","0","0","308","203","500","0","true","黑体","0"] -["308","203","1-0","2","集める為に","0","0","308","203","500","0","true","黑体","0"] -["308","203","1-0","2","集める為に/n","0","0","308","203","500","0","true","黑体","0"] -["308","203","1-0","2","集める為に/n","0","0","308","203","500","0","true","黑体","0"] -["308","203","1-0","4","集める為に/n","0","0","308","203","500","0","true","黑体","0"] -["20","138","0-1","1.5","生命(ひと)","350","30","107","114","1500","0","false","MS UI Gothic","0"] -["327","106","0-1","1.5","は遣って來る","350","30","184","137","1500","0","false","MS UI Gothic","0"] -["107","114","1-1","1.1","生命(ひと)","350","30","107","114","1500","0","false","MS UI Gothic","0"] -["184","137","1-1","1.1","は遣って來る","350","30","184","137","1500","0","false","MS UI Gothic","0"] -["107","114","1-0","1.5","生命(ひと)","350","30","107","114","1500","0","false","MS UI Gothic","0"] -["184","137","1-0","1.5","は遣って來る","350","30","184","137","1500","0","false","MS UI Gothic","0"] -["222","196","1-0","0.5","君 ","0","0","222","196","500","0","true","黑体","0"] -["222","196","1-0","0.5"," が","0","0","222","196","500","0","true","黑体","0"] -["228","201","1-1","3.8","君","0","0","228","201","500","0","false","黑体","0"] -["228","201","1-1","2.2"," が","0","0","228","201","500","0","false","黑体","0"] -["228","201","1-0","3","君が","0","0","228","201","500","0","false","黑体","0"] -["224","242","1-0","4.5","抱","10","0","224","242","500","0","false","黑体","0"] -["254","250","1-0","4.5","き","0","0","254","250","500","0","false","黑体","0"] -["280","248","1-0","4.5","し","340","0","280","248","500","0","false","黑体","0"] -["300","233","1-0","4.5","め","300","0","300","233","500","0","false","黑体","0"] -["306","207","1-0","4.5","た","260","0","306","207","500","0","false","黑体","0"] -["190","237","1-0","4.5","短","270","0","190","237","500","0","false","黑体","0"] -["200","187","1-0","4.5","い","310","0","200","187","500","0","false","黑体","0"] -["235","161","1-0","4.5","季","340","0","235","161","500","0","false","黑体","0"] -["274","149","1-0","4.5","節","350","0","274","149","500","0","false","黑体","0"] -["306","145","1-0","4.5","(Saison)","0","0","306","145","500","0","false","黑体","0"] -["321","36","1-1","0.5","痛","30","0","147","288","500","0","false","黑体","0"] -["463","38","1-1","0.5","み/n","30","0","276","283","500","0","true","黑体","0"] -["459","10","1-1","0.6","の","30","0","212","267","500","0","true","黑体","0"] -["468","58","1-1","0.6","雨","15","0","347","315","500","0","true","黑体","0"] -["470","2","1-1","0.6","に","10","0","369","242","500","0","true","黑体","0"] -["356","23","1-1","0.6","打","40","0","134","230","500","0","true","黑体","0"] -["377","39","1-1","0.6","た","20","0","239","244","500","0","true","黑体","0"] -["362","11","1-1","0.6","れ","10","0","296","286","500","0","true","黑体","0"] -["419","26","1-1","0.6","な","15","0","350","280","500","0","true","黑体","0"] -["465","18","1-1","0.6","が","5","0","427","315","500","0","true","黑体","0"] -["406","46","1-1","0.6","ら","50","0","194","203","500","0","true","黑体","0"] -["147","288","1-0","3.5","痛","30","0","147","308","3500","0","false","黑体","0"] -["276","283","1-0","3.5","み/n","30","0","276","303","3500","0","true","黑体","0"] -["212","267","1-0","3.5","の","30","0","212","287","3500","0","true","黑体","0"] -["347","315","1-0","3.5","雨","15","0","347","335","3500","0","true","黑体","0"] -["369","242","1-0","3.5","に","10","0","369","262","3500","0","true","黑体","0"] -["134","230","1-0","3.5","打","40","0","134","250","3500","0","true","黑体","0"] -["239","244","1-0","3.5","た","20","0","239","264","3500","0","true","黑体","0"] -["296","286","1-0","3.5","れ","10","0","296","306","3500","0","true","黑体","0"] -["350","280","1-0","3.5","な","15","0","350","300","3500","0","true","黑体","0"] -["427","315","1-0","3.5","が","5","0","427","335","3500","0","true","黑体","0"] -["194","203","1-0","3.5","ら","50","0","194","223","3500","0","true","黑体","0"] -["180","291","0.7-0.7","0.1","◯","94","71","180","291","500","0","false","宋体","0"] -["184","291","0.7-0.7","0.1","◯","94","71","184","291","500","0","false","宋体","0"] -["190","290","0.7-0.7","0.1","◯","94","71","190","290","500","0","false","宋体","0"] -["196","288","0.7-0.7","0.1","◯","94","71","196","288","500","0","false","宋体","0"] -["202","288","0.7-0.7","0.1","◯","94","71","202","288","500","0","false","宋体","0"] -["209","286","0.7-0.7","0.1","◯","94","71","209","286","500","0","false","宋体","0"] -["218","284","0.7-0.7","0.1","◯","94","71","218","284","500","0","false","宋体","0"] -["300","290","0.7-0.7","0.1","◯","90","71","300","290","500","0","false","宋体","0"] -["305","289","0.7-0.7","0.1","◯","90","71","305","289","500","0","false","宋体","0"] -["310","287","0.7-0.7","0.1","◯","90","71","310","287","500","0","false","宋体","0"] -["316","285","0.7-0.7","0.1","◯","90","71","316","285","500","0","false","宋体","0"] -["321","283","0.7-0.7","0.1","◯","90","71","321","283","500","0","false","宋体","0"] -["327","281","0.7-0.7","0.1","◯","90","71","327","281","500","0","false","宋体","0"] -["335","278","0.7-0.7","0.1","◯","90","71","335","278","500","0","false","宋体","0"] -["235","276","0.7-0.7","0.1","◯","91","71","235","276","500","0","false","宋体","0"] -["241","275","0.7-0.7","0.1","◯","91","71","241","275","500","0","false","宋体","0"] -["247","273","0.7-0.7","0.1","◯","91","71","247","273","500","0","false","宋体","0"] -["252","271","0.7-0.7","0.1","◯","91","71","252","271","500","0","false","宋体","0"] -["258","270","0.7-0.7","0.1","◯","91","71","258","270","500","0","false","宋体","0"] -["263","268","0.7-0.7","0.1","◯","91","71","263","268","500","0","false","宋体","0"] -["271","266","0.7-0.7","0.1","◯","91","71","271","266","500","0","false","宋体","0"] -["380","319","0.7-0.7","0.1","◯","88","70","380","319","500","0","false","宋体","0"] -["385","318","0.7-0.7","0.1","◯","88","70","385","318","500","0","false","宋体","0"] -["390","316","0.7-0.7","0.1","◯","88","70","390","316","500","0","false","宋体","0"] -["396","314","0.7-0.7","0.1","◯","88","70","396","314","500","0","false","宋体","0"] -["401","312","0.7-0.7","0.1","◯","88","70","401","312","500","0","false","宋体","0"] -["407","310","0.7-0.7","0.1","◯","88","70","407","310","500","0","false","宋体","0"] -["412","308","0.7-0.7","0.1","◯","88","70","412","308","500","0","false","宋体","0"] -["417","307","0.7-0.7","0.1","◯","88","70","417","307","500","0","false","宋体","0"] -["395","244","0.7-0.7","0.1","◯","88","70","395","244","500","0","false","宋体","0"] -["400","243","0.7-0.7","0.1","◯","88","70","400","243","500","0","false","宋体","0"] -["405","241","0.7-0.7","0.1","◯","88","70","405","241","500","0","false","宋体","0"] -["409","239","0.7-0.7","0.1","◯","88","70","409","239","500","0","false","宋体","0"] -["414","237","0.7-0.7","0.1","◯","88","70","414","237","500","0","false","宋体","0"] -["419","236","0.7-0.7","0.1","◯","88","70","419","236","500","0","false","宋体","0"] -["423","234","0.7-0.7","0.1","◯","88","70","423","234","500","0","false","宋体","0"] -["160","235","0.7-0.7","0.1","◯","94","70","160","235","500","0","false","宋体","0"] -["165","234","0.7-0.7","0.1","◯","94","70","165","234","500","0","false","宋体","0"] -["171","232","0.7-0.7","0.1","◯","94","70","171","232","500","0","false","宋体","0"] -["177","230","0.7-0.7","0.1","◯","94","70","177","230","500","0","false","宋体","0"] -["182","228","0.7-0.7","0.1","◯","94","70","182","228","500","0","false","宋体","0"] -["188","227","0.7-0.7","0.1","◯","94","70","188","227","500","0","false","宋体","0"] -["197","225","0.7-0.7","0.1","◯","94","70","197","225","500","0","false","宋体","0"] -["263","246","0.7-0.7","0.1","◯","91","70","263","246","500","0","false","宋体","0"] -["269","244","0.7-0.7","0.1","◯","91","70","269","244","500","0","false","宋体","0"] -["275","243","0.7-0.7","0.1","◯","91","70","275","243","500","0","false","宋体","0"] -["280","241","0.7-0.7","0.1","◯","91","70","280","241","500","0","false","宋体","0"] -["286","240","0.7-0.7","0.1","◯","91","70","286","240","500","0","false","宋体","0"] -["291","238","0.7-0.7","0.1","◯","91","70","291","238","500","0","false","宋体","0"] -["299","236","0.7-0.7","0.1","◯","91","70","299","236","500","0","false","宋体","0"] -["328","292","0.7-0.7","0.1","◯","90","70","328","292","500","0","false","宋体","0"] -["333","290","0.7-0.7","0.1","◯","90","70","333","290","500","0","false","宋体","0"] -["338","288","0.7-0.7","0.1","◯","90","70","338","288","500","0","false","宋体","0"] -["343","287","0.7-0.7","0.1","◯","90","70","343","287","500","0","false","宋体","0"] -["349","285","0.7-0.7","0.1","◯","90","70","349","285","500","0","false","宋体","0"] -["354","283","0.7-0.7","0.1","◯","90","70","354","283","500","0","false","宋体","0"] -["362","281","0.7-0.7","0.1","◯","90","70","362","281","500","0","false","宋体","0"] -["378","282","0.7-0.7","0.1","◯","89","70","378","282","500","0","false","宋体","0"] -["382","280","0.7-0.7","0.1","◯","89","70","382","280","500","0","false","宋体","0"] -["387","279","0.7-0.7","0.1","◯","89","70","387","279","500","0","false","宋体","0"] -["392","277","0.7-0.7","0.1","◯","89","70","392","277","500","0","false","宋体","0"] -["396","276","0.7-0.7","0.1","◯","89","70","396","276","500","0","false","宋体","0"] -["401","274","0.7-0.7","0.1","◯","89","70","401","274","500","0","false","宋体","0"] -["408","272","0.7-0.7","0.1","◯","89","70","408","272","500","0","false","宋体","0"] -["462","313","0.7-0.7","0.1","◯","85","70","462","313","500","0","false","宋体","0"] -["467","311","0.7-0.7","0.1","◯","85","70","467","311","500","0","false","宋体","0"] -["471","309","0.7-0.7","0.1","◯","85","70","471","309","500","0","false","宋体","0"] -["475","307","0.7-0.7","0.1","◯","85","70","475","307","500","0","false","宋体","0"] -["479","306","0.7-0.7","0.1","◯","85","70","479","306","500","0","false","宋体","0"] -["483","304","0.7-0.7","0.1","◯","85","70","483","304","500","0","false","宋体","0"] -["487","302","0.7-0.7","0.1","◯","85","70","487","302","500","0","false","宋体","0"] -["491","301","0.7-0.7","0.1","◯","85","70","491","301","500","0","false","宋体","0"] -["209","208","0.7-0.7","0.1","◯","93","70","209","208","500","0","false","宋体","0"] -["215","206","0.7-0.7","0.1","◯","93","70","215","206","500","0","false","宋体","0"] -["221","205","0.7-0.7","0.1","◯","93","70","221","205","500","0","false","宋体","0"] -["226","203","0.7-0.7","0.1","◯","93","70","226","203","500","0","false","宋体","0"] -["231","202","0.7-0.7","0.1","◯","93","70","231","202","500","0","false","宋体","0"] -["236","200","0.7-0.7","0.1","◯","93","70","236","200","500","0","false","宋体","0"] -["241","199","0.7-0.7","0.1","◯","93","70","241","199","500","0","false","宋体","0"] -["250","197","0.7-0.7","0.1","◯","93","70","250","197","500","0","false","宋体","0"] -["11","102","0-1","3","尽/n管/n饱/n受/n痛/n苦/n之/n雨/n的/n敲/n打","0","0","11","102","500","0","true","黑体","0"] -["11","102","1-0","3","尽/n管/n饱/n受/n痛/n苦/n之/n雨/n的/n敲/n打","0","0","11","102","500","0","true","黑体","0"] -["260","75","0-1","1.5","「心配ないよ」","0","0","165","75","1500","0","true","黑体","0"] -["165","75","1-0","3","「心配ないよ」","0","0","165","75","1000","0","true","黑体","0"] -["199","215","1-0","4.5","笑","50","0","177","187","1000","0","true","黑体","0"] -["235","233","1-0","4.5","っ","30","0","199","215","1000","0","true","黑体","0"] -["260","233","1-0","4.5","て","10","0","235","233","1000","0","true","黑体","0"] -["260","233","1-0","4.5","言","350","0","280","233","1000","0","true","黑体","0"] -["280","237","1-0","4.5","っ","330","0","315","227","1000","0","true","黑体","0"] -["315","227","1-0","4.5","た","310","0","347","205","1000","0","true","黑体","0"] -["142","95","1-1","4.5","█","0","0","142","95","500","0","true","黑体","0"] -["142","95","1-1","4.5","君","0","0","142","95","500","0","false","黑体","0"] -["150","105","1-1","3.8"," の","0","0","150","105","500","0","false","黑体","0"] -["188","84","0-1","1.5","様相(Visage)","0","0","208","104","1500","0","false","黑体","0"] -["188","124","0-1","1.5","様相(Visage)","0","0","208","104","1500","0","false","黑体","0"] -["228","124","0-1","1.5","様相(Visage)","0","0","208","104","1500","0","false","黑体","0"] -["228","84","0-1","1.5","様相(Visage)","0","0","208","104","1500","0","false","黑体","0"] -["208","104","1-1","2","様相(Visage)","0","0","208","104","2500","0","false","黑体","0"] -["278","146","1-1","3.5","忘れないよ","0","0","278","146","500","0","true","黑体","0"] -["278","126","0-1","1.5","忘","0","0","278","146","1500","0","false","黑体","0"] -["278","126","0-1","1.5"," れ","0","0","278","146","1500","0","false","黑体","0"] -["278","126","0-1","1.5","  な","0","0","278","146","1500","0","false","黑体","0"] -["278","126","0-1","1.5","   い","0","0","278","146","1500","0","false","黑体","0"] -["278","126","0-1","1.5","    よ","0","0","278","146","1500","0","false","黑体","0"] -["278","166","0-1","1.5","忘","0","0","278","146","1500","0","false","黑体","0"] -["278","166","0-1","1.5"," れ","0","0","278","146","1500","0","false","黑体","0"] -["278","166","0-1","1.5","  な","0","0","278","146","1500","0","false","黑体","0"] -["278","166","0-1","1.5","   い","0","0","278","146","1500","0","false","黑体","0"] -["278","166","0-1","1.5","    よ","0","0","278","146","1500","0","false","黑体","0"] -["278","146","1-1","2.5","忘","0","0","278","146","1500","0","false","黑体","0"] -["278","146","1-1","2.2"," れ","0","0","278","146","1500","0","false","黑体","0"] -["278","146","1-1","1.5","  な","0","0","278","146","1500","0","false","黑体","0"] -["278","146","1-1","1.1","   い","0","0","278","146","1500","0","false","黑体","0"] -["278","146","1-1","0.5","    よ","0","0","278","146","1500","0","false","黑体","0"] -["193","335","0-1","4.5","“不用担心哦”","0","0","193","335","500","0","true","黑体","0"] -["193","360","0-1","4.5","你笑着这么说道","0","0","193","360","500","0","true","黑体","0"] -["132","337","0-1","4.5","你的样子(Visage)","0","0","132","337","500","0","true","黑体","0"] -["241","360","0-1","4.5","永远不忘…","0","0","241","360","500","0","true","黑体","0"] -["142","240","1-0","1.5","♪","0","0","92","240","1500","0","false","宋体","0"] -["142","240","1-0","1.5","♪","0","0","192","240","1500","0","false","宋体","0"] -["142","240","1-0","1.5","♪","0","0","142","290","1500","0","false","宋体","0"] -["142","240","1-0","1.5","♪","0","0","142","190","1500","0","false","宋体","0"] -["142","240","1-0","1.5","♪","0","0","107","205","1500","0","false","宋体","0"] -["142","240","1-0","1.5","♪","0","0","107","275","1500","0","false","宋体","0"] -["142","240","1-0","1.5","♪","0","0","177","205","1500","0","false","宋体","0"] -["142","240","1-0","1.5","♪","0","0","177","275","1500","0","false","宋体","0"] -["225","108","1-0","1.5","♩","0","0","275","108","1500","0","false","宋体","0"] -["225","108","1-0","1.5","♩","0","0","175","108","1500","0","false","宋体","0"] -["225","108","1-0","1.5","♩","0","0","225","158","1500","0","false","宋体","0"] -["225","108","1-0","1.5","♩","0","0","225","58","1500","0","false","宋体","0"] -["225","108","1-0","1.5","♩","0","0","260","143","1500","0","false","宋体","0"] -["225","108","1-0","1.5","♩","0","0","260","73","1500","0","false","宋体","0"] -["225","108","1-0","1.5","♩","0","0","190","143","1500","0","false","宋体","0"] -["225","108","1-0","1.5","♩","0","0","190","73","1500","0","false","宋体","0"] -["346","254","1-0","1.5","♭","0","0","396","254","1500","0","false","宋体","0"] -["346","254","1-0","1.5","♭","0","0","296","254","1500","0","false","宋体","0"] -["346","254","1-0","1.5","♭","0","0","346","304","1500","0","false","宋体","0"] -["346","254","1-0","1.5","♭","0","0","346","204","1500","0","false","宋体","0"] -["346","254","1-0","1.5","♭","0","0","381","289","1500","0","false","宋体","0"] -["346","254","1-0","1.5","♭","0","0","381","219","1500","0","false","宋体","0"] -["346","254","1-0","1.5","♭","0","0","311","289","1500","0","false","宋体","0"] -["346","254","1-0","1.5","♭","0","0","311","219","1500","0","false","宋体","0"] -["400","97","1-0","1.5","♬","0","0","350","97","1500","0","false","宋体","0"] -["400","97","1-0","1.5","♬","0","0","450","97","1500","0","false","宋体","0"] -["400","97","1-0","1.5","♬","0","0","400","147","1500","0","false","宋体","0"] -["400","97","1-0","1.5","♬","0","0","400","47","1500","0","false","宋体","0"] -["400","97","1-0","1.5","♬","0","0","435","132","1500","0","false","宋体","0"] -["400","97","1-0","1.5","♬","0","0","435","62","1500","0","false","宋体","0"] -["400","97","1-0","1.5","♬","0","0","365","62","1500","0","false","宋体","0"] -["400","97","1-0","1.5","♬","0","0","365","132","1500","0","false","宋体","0"] -["390","87","1-0","1.5","♬","0","0","340","87","1500","0","false","宋体","0"] -["390","87","1-0","1.5","♬","0","0","440","87","1500","0","false","宋体","0"] -["390","87","1-0","1.5","♬","0","0","390","137","1500","0","false","宋体","0"] -["390","87","1-0","1.5","♬","0","0","390","37","1500","0","false","宋体","0"] -["390","87","1-0","1.5","♬","0","0","425","122","1500","0","false","宋体","0"] -["390","87","1-0","1.5","♬","0","0","425","52","1500","0","false","宋体","0"] -["390","87","1-0","1.5","♬","0","0","355","52","1500","0","false","宋体","0"] -["390","87","1-0","1.5","♬","0","0","355","122","1500","0","false","宋体","0"] -["207","281","1-0","1.5","♪","0","0","257","281","1500","0","false","宋体","0"] -["207","281","1-0","1.5","♪","0","0","157","281","1500","0","false","宋体","0"] -["207","281","1-0","1.5","♪","0","0","207","331","1500","0","false","宋体","0"] -["207","281","1-0","1.5","♪","0","0","207","231","1500","0","false","宋体","0"] -["207","281","1-0","1.5","♪","0","0","242","315","1500","0","false","宋体","0"] -["207","281","1-0","1.5","♪","0","0","242","246","1500","0","false","宋体","0"] -["207","281","1-0","1.5","♪","0","0","172","315","1500","0","false","宋体","0"] -["207","281","1-0","1.5","♪","0","0","172","246","1500","0","false","宋体","0"] -["158","59","1-0","1.5","♫","0","0","108","59","1500","0","false","宋体","0"] -["158","59","1-0","1.5","♫","0","0","208","59","1500","0","false","宋体","0"] -["158","59","1-0","1.5","♫","0","0","158","9","1500","0","false","宋体","0"] -["158","59","1-0","1.5","♫","0","0","158","109","1500","0","false","宋体","0"] -["158","59","1-0","1.5","♫","0","0","193","94","1500","0","false","宋体","0"] -["158","59","1-0","1.5","♫","0","0","193","24","1500","0","false","宋体","0"] -["158","59","1-0","1.5","♫","0","0","123","24","1500","0","false","宋体","0"] -["158","59","1-0","1.5","♫","0","0","123","94","1500","0","false","宋体","0"] -["152","214","1-0","1.5","♭","0","0","102","214","1500","0","false","宋体","0"] -["152","214","1-0","1.5","♭","0","0","202","214","1500","0","false","宋体","0"] -["152","214","1-0","1.5","♭","0","0","152","264","1500","0","false","宋体","0"] -["152","214","1-0","1.5","♭","0","0","152","164","1500","0","false","宋体","0"] -["152","214","1-0","1.5","♭","0","0","187","249","1500","0","false","宋体","0"] -["152","214","1-0","1.5","♭","0","0","187","179","1500","0","false","宋体","0"] -["152","214","1-0","1.5","♭","0","0","117","249","1500","0","false","宋体","0"] -["152","214","1-0","1.5","♭","0","0","117","179","1500","0","false","宋体","0"] -["329","164","1-0","1.5","♩","0","0","379","164","1500","0","false","宋体","0"] -["329","164","1-0","1.5","♩","0","0","279","164","1500","0","false","宋体","0"] -["329","164","1-0","1.5","♩","0","0","329","214","1500","0","false","宋体","0"] -["329","164","1-0","1.5","♩","0","0","329","114","1500","0","false","宋体","0"] -["329","164","1-0","1.5","♩","0","0","364","199","1500","0","false","宋体","0"] -["329","164","1-0","1.5","♩","0","0","364","129","1500","0","false","宋体","0"] -["329","164","1-0","1.5","♩","0","0","294","199","1500","0","false","宋体","0"] -["329","164","1-0","1.5","♩","0","0","294","129","1500","0","false","宋体","0"] -["287","206","1-0","1.5","♭","0","0","337","206","1500","0","false","宋体","0"] -["287","206","1-0","1.5","♭","0","0","237","206","1500","0","false","宋体","0"] -["287","206","1-0","1.5","♭","0","0","287","256","1500","0","false","宋体","0"] -["287","206","1-0","1.5","♭","0","0","287","156","1500","0","false","宋体","0"] -["287","206","1-0","1.5","♭","0","0","322","241","1500","0","false","宋体","0"] -["287","206","1-0","1.5","♭","0","0","322","171","1500","0","false","宋体","0"] -["287","206","1-0","1.5","♭","0","0","252","241","1500","0","false","宋体","0"] -["287","206","1-0","1.5","♭","0","0","252","171","1500","0","false","宋体","0"] -["134","113","1-0","1.5","♪","0","0","184","113","1500","0","false","宋体","0"] -["134","113","1-0","1.5","♪","0","0","84","113","1500","0","false","宋体","0"] -["134","113","1-0","1.5","♪","0","0","134","163","1500","0","false","宋体","0"] -["134","113","1-0","1.5","♪","0","0","134","63","1500","0","false","宋体","0"] -["134","113","1-0","1.5","♪","0","0","169","148","1500","0","false","宋体","0"] -["134","113","1-0","1.5","♪","0","0","169","78","1500","0","false","宋体","0"] -["134","113","1-0","1.5","♪","0","0","99","78","1500","0","false","宋体","0"] -["134","113","1-0","1.5","♪","0","0","99","148","1500","0","false","宋体","0"] -["279","218","1-0","1.5","♩","0","0","329","218","1500","0","false","宋体","0"] -["279","218","1-0","1.5","♩","0","0","229","218","1500","0","false","宋体","0"] -["279","218","1-0","1.5","♩","0","0","279","268","1500","0","false","宋体","0"] -["279","218","1-0","1.5","♩","0","0","279","168","1500","0","false","宋体","0"] -["279","218","1-0","1.5","♩","0","0","314","253","1500","0","false","宋体","0"] -["279","218","1-0","1.5","♩","0","0","314","183","1500","0","false","宋体","0"] -["279","218","1-0","1.5","♩","0","0","244","253","1500","0","false","宋体","0"] -["279","218","1-0","1.5","♩","0","0","244","183","1500","0","false","宋体","0"] -["343","177","1-0","1.5","♪","0","0","393","177","1500","0","false","宋体","0"] -["343","177","1-0","1.5","♪","0","0","293","177","1500","0","false","宋体","0"] -["343","177","1-0","1.5","♪","0","0","343","227","1500","0","false","宋体","0"] -["343","177","1-0","1.5","♪","0","0","343","127","1500","0","false","宋体","0"] -["343","177","1-0","1.5","♪","0","0","308","212","1500","0","false","宋体","0"] -["343","177","1-0","1.5","♪","0","0","308","142","1500","0","false","宋体","0"] -["343","177","1-0","1.5","♪","0","0","378","142","1500","0","false","宋体","0"] -["343","177","1-0","1.5","♪","0","0","378","212","1500","0","false","宋体","0"] -["164","132","1-0","1.5","♭","0","0","214","132","1500","0","false","宋体","0"] -["164","132","1-0","1.5","♭","0","0","114","132","1500","0","false","宋体","0"] -["164","132","1-0","1.5","♭","0","0","164","182","1500","0","false","宋体","0"] -["164","132","1-0","1.5","♭","0","0","164","82","1500","0","false","宋体","0"] -["164","132","1-0","1.5","♭","0","0","199","167","1500","0","false","宋体","0"] -["164","132","1-0","1.5","♭","0","0","199","97","1500","0","false","宋体","0"] -["164","132","1-0","1.5","♭","0","0","129","167","1500","0","false","宋体","0"] -["164","132","1-0","1.5","♭","0","0","129","97","1500","0","false","宋体","0"] -["309","71","1-0","1.5","♪","0","0","359","71","1500","0","false","宋体","0"] -["309","71","1-0","1.5","♪","0","0","259","71","1500","0","false","宋体","0"] -["309","71","1-0","1.5","♪","0","0","309","121","1500","0","false","宋体","0"] -["309","71","1-0","1.5","♪","0","0","309","21","1500","0","false","宋体","0"] -["309","71","1-0","1.5","♪","0","0","344","106","1500","0","false","宋体","0"] -["309","71","1-0","1.5","♪","0","0","344","36","1500","0","false","宋体","0"] -["309","71","1-0","1.5","♪","0","0","274","36","1500","0","false","宋体","0"] -["309","71","1-0","1.5","♪","0","0","274","106","1500","0","false","宋体","0"] -["233","247","1-0","1.5","♬","0","0","283","247","1500","0","false","宋体","0"] -["233","247","1-0","1.5","♬","0","0","183","247","1500","0","false","宋体","0"] -["233","247","1-0","1.5","♬","0","0","233","297","1500","0","false","宋体","0"] -["233","247","1-0","1.5","♬","0","0","233","197","1500","0","false","宋体","0"] -["233","247","1-0","1.5","♬","0","0","268","282","1500","0","false","宋体","0"] -["233","247","1-0","1.5","♬","0","0","268","212","1500","0","false","宋体","0"] -["233","247","1-0","1.5","♬","0","0","198","212","1500","0","false","宋体","0"] -["233","247","1-0","1.5","♬","0","0","198","282","1500","0","false","宋体","0"] -["193","125","1-0","1.5","♩","0","0","243","125","1500","0","false","宋体","0"] -["193","125","1-0","1.5","♩","0","0","143","125","1500","0","false","宋体","0"] -["193","125","1-0","1.5","♩","0","0","193","175","1500","0","false","宋体","0"] -["193","125","1-0","1.5","♩","0","0","193","75","1500","0","false","宋体","0"] -["193","125","1-0","1.5","♩","0","0","228","160","1500","0","false","宋体","0"] -["193","125","1-0","1.5","♩","0","0","228","90","1500","0","false","宋体","0"] -["193","125","1-0","1.5","♩","0","0","158","160","1500","0","false","宋体","0"] -["193","125","1-0","1.5","♩","0","0","158","90","1500","0","false","宋体","0"] -["223","155","1-0","1.5","♩","0","0","273","155","1500","0","false","宋体","0"] -["223","155","1-0","1.5","♩","0","0","173","155","1500","0","false","宋体","0"] -["223","155","1-0","1.5","♩","0","0","223","205","1500","0","false","宋体","0"] -["223","155","1-0","1.5","♩","0","0","223","105","1500","0","false","宋体","0"] -["223","155","1-0","1.5","♩","0","0","258","190","1500","0","false","宋体","0"] -["223","155","1-0","1.5","♩","0","0","258","120","1500","0","false","宋体","0"] -["223","155","1-0","1.5","♩","0","0","188","190","1500","0","false","宋体","0"] -["223","155","1-0","1.5","♩","0","0","188","120","1500","0","false","宋体","0"] -["345","83","1-0","1.5","♫","0","0","395","83","1500","0","false","宋体","0"] -["345","83","1-0","1.5","♫","0","0","295","83","1500","0","false","宋体","0"] -["345","83","1-0","1.5","♫","0","0","345","133","1500","0","false","宋体","0"] -["345","83","1-0","1.5","♫","0","0","345","33","1500","0","false","宋体","0"] -["345","83","1-0","1.5","♫","0","0","380","118","1500","0","false","宋体","0"] -["345","83","1-0","1.5","♫","0","0","380","48","1500","0","false","宋体","0"] -["345","83","1-0","1.5","♫","0","0","310","48","1500","0","false","宋体","0"] -["345","83","1-0","1.5","♫","0","0","310","118","1500","0","false","宋体","0"] -["407","133","1-0","1.5","♪","0","0","457","133","1500","0","false","宋体","0"] -["407","133","1-0","1.5","♪","0","0","357","133","1500","0","false","宋体","0"] -["407","133","1-0","1.5","♪","0","0","407","183","1500","0","false","宋体","0"] -["407","133","1-0","1.5","♪","0","0","407","83","1500","0","false","宋体","0"] -["407","133","1-0","1.5","♪","0","0","442","168","1500","0","false","宋体","0"] -["407","133","1-0","1.5","♪","0","0","442","98","1500","0","false","宋体","0"] -["407","133","1-0","1.5","♪","0","0","372","98","1500","0","false","宋体","0"] -["407","133","1-0","1.5","♪","0","0","372","168","1500","0","false","宋体","0"] -["142","179","1-0","1.5","♭","0","0","192","179","1500","0","false","宋体","0"] -["142","179","1-0","1.5","♭","0","0","92","179","1500","0","false","宋体","0"] -["142","179","1-0","1.5","♭","0","0","142","229","1500","0","false","宋体","0"] -["142","179","1-0","1.5","♭","0","0","142","129","1500","0","false","宋体","0"] -["142","179","1-0","1.5","♭","0","0","177","214","1500","0","false","宋体","0"] -["142","179","1-0","1.5","♭","0","0","107","214","1500","0","false","宋体","0"] -["142","179","1-0","1.5","♭","0","0","177","144","1500","0","false","宋体","0"] -["142","179","1-0","1.5","♭","0","0","107","144","1500","0","false","宋体","0"] -["229","224","1-0","1.5","♬","0","0","279","224","1500","0","false","宋体","0"] -["229","224","1-0","1.5","♬","0","0","179","224","1500","0","false","宋体","0"] -["229","224","1-0","1.5","♬","0","0","229","274","1500","0","false","宋体","0"] -["229","224","1-0","1.5","♬","0","0","229","174","1500","0","false","宋体","0"] -["229","224","1-0","1.5","♬","0","0","264","259","1500","0","false","宋体","0"] -["229","224","1-0","1.5","♬","0","0","264","189","1500","0","false","宋体","0"] -["229","224","1-0","1.5","♬","0","0","194","259","1500","0","false","宋体","0"] -["229","224","1-0","1.5","♬","0","0","194","189","1500","0","false","宋体","0"] -["365","166","1-0","1.5","♪","0","0","415","166","1500","0","false","宋体","0"] -["365","166","1-0","1.5","♪","0","0","315","166","1500","0","false","宋体","0"] -["365","166","1-0","1.5","♪","0","0","365","216","1500","0","false","宋体","0"] -["365","166","1-0","1.5","♪","0","0","365","116","1500","0","false","宋体","0"] -["365","166","1-0","1.5","♪","0","0","400","201","1500","0","false","宋体","0"] -["365","166","1-0","1.5","♪","0","0","400","131","1500","0","false","宋体","0"] -["365","166","1-0","1.5","♪","0","0","330","131","1500","0","false","宋体","0"] -["365","166","1-0","1.5","♪","0","0","330","201","1500","0","false","宋体","0"] -["365","166","1-0","1.5","♪","0","0","415","166","1500","0","false","宋体","0"] -["365","166","1-0","1.5","♪","0","0","315","166","1500","0","false","宋体","0"] -["365","166","1-0","1.5","♪","0","0","365","216","1500","0","false","宋体","0"] -["365","166","1-0","1.5","♪","0","0","365","116","1500","0","false","宋体","0"] -["365","166","1-0","1.5","♪","0","0","400","201","1500","0","false","宋体","0"] -["365","166","1-0","1.5","♪","0","0","400","131","1500","0","false","宋体","0"] -["365","166","1-0","1.5","♪","0","0","330","131","1500","0","false","宋体","0"] -["365","166","1-0","1.5","♪","0","0","330","201","1500","0","false","宋体","0"] -["178","261","1-0","1.5","♬","0","0","228","261","1500","0","false","宋体","0"] -["178","261","1-0","1.5","♬","0","0","128","261","1500","0","false","宋体","0"] -["178","261","1-0","1.5","♬","0","0","178","311","1500","0","false","宋体","0"] -["178","261","1-0","1.5","♬","0","0","178","211","1500","0","false","宋体","0"] -["178","261","1-0","1.5","♬","0","0","213","296","1500","0","false","宋体","0"] -["178","261","1-0","1.5","♬","0","0","213","226","1500","0","false","宋体","0"] -["178","261","1-0","1.5","♬","0","0","143","226","1500","0","false","宋体","0"] -["178","261","1-0","1.5","♬","0","0","143","296","1500","0","false","宋体","0"] -["154","195","1-0","1.5","♩","0","0","204","195","1500","0","false","宋体","0"] -["154","195","1-0","1.5","♩","0","0","104","195","1500","0","false","宋体","0"] -["154","195","1-0","1.5","♩","0","0","154","245","1500","0","false","宋体","0"] -["154","195","1-0","1.5","♩","0","0","154","145","1500","0","false","宋体","0"] -["154","195","1-0","1.5","♩","0","0","189","230","1500","0","false","宋体","0"] -["154","195","1-0","1.5","♩","0","0","189","160","1500","0","false","宋体","0"] -["154","195","1-0","1.5","♩","0","0","119","160","1500","0","false","宋体","0"] -["154","195","1-0","1.5","♩","0","0","119","230","1500","0","false","宋体","0"] -["276","122","1-0","1.5","♫","0","0","326","122","1500","0","false","宋体","0"] -["276","122","1-0","1.5","♫","0","0","226","122","1500","0","false","宋体","0"] -["276","122","1-0","1.5","♫","0","0","276","172","1500","0","false","宋体","0"] -["276","122","1-0","1.5","♫","0","0","276","72","1500","0","false","宋体","0"] -["276","122","1-0","1.5","♫","0","0","311","157","1500","0","false","宋体","0"] -["276","122","1-0","1.5","♫","0","0","311","87","1500","0","false","宋体","0"] -["276","122","1-0","1.5","♫","0","0","241","87","1500","0","false","宋体","0"] -["276","122","1-0","1.5","♫","0","0","241","157","1500","0","false","宋体","0"] -["367","168","1-0","1.5","♪","0","0","417","168","1500","0","false","宋体","0"] -["367","168","1-0","1.5","♪","0","0","317","168","1500","0","false","宋体","0"] -["367","168","1-0","1.5","♪","0","0","367","218","1500","0","false","宋体","0"] -["367","168","1-0","1.5","♪","0","0","367","118","1500","0","false","宋体","0"] -["367","168","1-0","1.5","♪","0","0","402","203","1500","0","false","宋体","0"] -["367","168","1-0","1.5","♪","0","0","332","203","1500","0","false","宋体","0"] -["367","168","1-0","1.5","♪","0","0","402","133","1500","0","false","宋体","0"] -["367","168","1-0","1.5","♪","0","0","332","133","1500","0","false","宋体","0"] -["256","257","1-0","1.5","♭","0","0","306","257","1500","0","false","宋体","0"] -["256","257","1-0","1.5","♭","0","0","206","257","1500","0","false","宋体","0"] -["256","257","1-0","1.5","♭","0","0","256","307","1500","0","false","宋体","0"] -["256","257","1-0","1.5","♭","0","0","256","207","1500","0","false","宋体","0"] -["256","257","1-0","1.5","♭","0","0","291","292","1500","0","false","宋体","0"] -["256","257","1-0","1.5","♭","0","0","291","222","1500","0","false","宋体","0"] -["256","257","1-0","1.5","♭","0","0","221","292","1500","0","false","宋体","0"] -["256","257","1-0","1.5","♭","0","0","221","222","1500","0","false","宋体","0"] -["103","155","1-0","1.5","♩","0","0","153","155","1500","0","false","宋体","0"] -["103","155","1-0","1.5","♩","0","0","53","155","1500","0","false","宋体","0"] -["103","155","1-0","1.5","♩","0","0","103","105","1500","0","false","宋体","0"] -["103","155","1-0","1.5","♩","0","0","103","205","1500","0","false","宋体","0"] -["103","155","1-0","1.5","♩","0","0","138","190","1500","0","false","宋体","0"] -["103","155","1-0","1.5","♩","0","0","138","120","1500","0","false","宋体","0"] -["103","155","1-0","1.5","♩","0","0","68","120","1500","0","false","宋体","0"] -["103","155","1-0","1.5","♩","0","0","68","190","1500","0","false","宋体","0"] -["139","136","1-0","1.5","♪","0","0","189","136","1500","0","false","宋体","0"] -["139","136","1-0","1.5","♪","0","0","89","136","1500","0","false","宋体","0"] -["139","136","1-0","1.5","♪","0","0","139","186","1500","0","false","宋体","0"] -["139","136","1-0","1.5","♪","0","0","139","86","1500","0","false","宋体","0"] -["139","136","1-0","1.5","♪","0","0","174","171","1500","0","true","宋体","0"] -["139","136","1-0","1.5","♪","0","0","174","101","1500","0","false","宋体","0"] -["139","136","1-0","1.5","♪","0","0","104","171","1500","0","false","宋体","0"] -["139","136","1-0","1.5","♪","0","0","104","101","1500","0","false","宋体","0"] -["308","220","1-0","1.5","♬","0","0","358","220","1500","0","false","宋体","0"] -["308","220","1-0","1.5","♬","0","0","258","220","1500","0","false","宋体","0"] -["308","220","1-0","1.5","♬","0","0","308","270","1500","0","false","宋体","0"] -["308","220","1-0","1.5","♬","0","0","308","170","1500","0","false","宋体","0"] -["308","220","1-0","1.5","♬","0","0","343","255","1500","0","false","宋体","0"] -["308","220","1-0","1.5","♬","0","0","343","185","1500","0","false","宋体","0"] -["308","220","1-0","1.5","♬","0","0","273","185","1500","0","false","宋体","0"] -["308","220","1-0","1.5","♬","0","0","273","255","1500","0","false","宋体","0"] -["143","259","1-0","1.5","♭","0","0","193","259","1500","0","false","宋体","0"] -["143","259","1-0","1.5","♭","0","0","93","259","1500","0","false","宋体","0"] -["143","259","1-0","1.5","♭","0","0","143","309","1500","0","false","宋体","0"] -["143","259","1-0","1.5","♭","0","0","143","209","1500","0","false","宋体","0"] -["143","259","1-0","1.5","♭","0","0","178","224","1500","0","false","宋体","0"] -["143","259","1-0","1.5","♭","0","0","178","294","1500","0","false","宋体","0"] -["143","259","1-0","1.5","♭","0","0","108","224","1500","0","false","宋体","0"] -["143","259","1-0","1.5","♭","0","0","108","294","1500","0","false","宋体","0"] -["207","101","1-0","1.5","♪","0","0","257","101","1500","0","false","宋体","0"] -["207","101","1-0","1.5","♪","0","0","157","101","1500","0","false","宋体","0"] -["207","101","1-0","1.5","♪","0","0","207","151","1500","0","false","宋体","0"] -["207","101","1-0","1.5","♪","0","0","207","51","1500","0","false","宋体","0"] -["207","101","1-0","1.5","♪","0","0","242","136","1500","0","false","宋体","0"] -["207","101","1-0","1.5","♪","0","0","242","66","1500","0","false","宋体","0"] -["207","101","1-0","1.5","♪","0","0","172","66","1500","0","false","宋体","0"] -["207","101","1-0","1.5","♪","0","0","172","136","1500","0","false","宋体","0"] -["409","52","1-0","1.5","♫","0","0","459","52","1500","0","false","宋体","0"] -["409","52","1-0","1.5","♫","0","0","359","52","1500","0","false","宋体","0"] -["409","52","1-0","1.5","♫","0","0","409","102","1500","0","false","宋体","0"] -["409","52","1-0","1.5","♫","0","0","409","2","1500","0","false","宋体","0"] -["409","52","1-0","1.5","♫","0","0","444","87","1500","0","false","宋体","0"] -["409","52","1-0","1.5","♫","0","0","444","17","1500","0","false","宋体","0"] -["409","52","1-0","1.5","♫","0","0","374","87","1500","0","false","宋体","0"] -["409","52","1-0","1.5","♫","0","0","374","17","1500","0","false","宋体","0"] -["222","218","1-0","1.5","♩","0","0","272","218","1500","0","false","宋体","0"] -["222","218","1-0","1.5","♩","0","0","172","218","1500","0","false","宋体","0"] -["222","218","1-0","1.5","♩","0","0","222","268","1500","0","false","宋体","0"] -["222","218","1-0","1.5","♩","0","0","222","168","1500","0","false","宋体","0"] -["222","218","1-0","1.5","♩","0","0","257","253","1500","0","false","宋体","0"] -["222","218","1-0","1.5","♩","0","0","257","183","1500","0","false","宋体","0"] -["222","218","1-0","1.5","♩","0","0","187","183","1500","0","false","宋体","0"] -["222","218","1-0","1.5","♩","0","0","187","253","1500","0","false","宋体","0"] -["246","144","1-0","1.5","♪","0","0","196","144","1500","0","false","宋体","0"] -["246","144","1-0","1.5","♪","0","0","296","144","1500","0","false","宋体","0"] -["246","144","1-0","1.5","♪","0","0","246","194","1500","0","false","宋体","0"] -["246","144","1-0","1.5","♪","0","0","246","94","1500","0","false","宋体","0"] -["246","144","1-0","1.5","♪","0","0","281","179","1500","0","false","宋体","0"] -["246","144","1-0","1.5","♪","0","0","281","109","1500","0","false","宋体","0"] -["246","144","1-0","1.5","♪","0","0","211","109","1500","0","false","宋体","0"] -["246","144","1-0","1.5","♪","0","0","211","179","1500","0","false","宋体","0"] -["154","117","1-0","1.5","♭","0","0","204","117","1500","0","false","宋体","0"] -["154","117","1-0","1.5","♭","0","0","104","117","1500","0","false","宋体","0"] -["154","117","1-0","1.5","♭","0","0","154","167","1500","0","false","宋体","0"] -["154","117","1-0","1.5","♭","0","0","154","67","1500","0","false","宋体","0"] -["154","117","1-0","1.5","♭","0","0","189","152","1500","0","false","宋体","0"] -["154","117","1-0","1.5","♭","0","0","189","82","1500","0","false","宋体","0"] -["154","117","1-0","1.5","♭","0","0","119","152","1500","0","false","宋体","0"] -["154","117","1-0","1.5","♭","0","0","119","82","1500","0","false","宋体","0"] -["96","207","1-0","1.5","♫","0","0","146","207","1500","0","false","宋体","0"] -["96","207","1-0","1.5","♫","0","0","46","207","1500","0","false","宋体","0"] -["96","207","1-0","1.5","♫","0","0","96","257","1500","0","false","宋体","0"] -["96","207","1-0","1.5","♫","0","0","96","157","1500","0","false","宋体","0"] -["96","207","1-0","1.5","♫","0","0","131","242","1500","0","false","宋体","0"] -["96","207","1-0","1.5","♫","0","0","131","172","1500","0","false","宋体","0"] -["96","207","1-0","1.5","♫","0","0","61","172","1500","0","false","宋体","0"] -["96","207","1-0","1.5","♫","0","0","61","242","1500","0","false","宋体","0"] -["230","189","1-0","1.5","♪","0","0","280","189","1500","0","false","宋体","0"] -["230","189","1-0","1.5","♪","0","0","180","189","1500","0","false","宋体","0"] -["230","189","1-0","1.5","♪","0","0","230","239","1500","0","false","宋体","0"] -["230","189","1-0","1.5","♪","0","0","230","139","1500","0","false","宋体","0"] -["230","189","1-0","1.5","♪","0","0","265","224","1500","0","false","宋体","0"] -["230","189","1-0","1.5","♪","0","0","195","224","1500","0","false","宋体","0"] -["230","189","1-0","1.5","♪","0","0","265","154","1500","0","false","宋体","0"] -["230","189","1-0","1.5","♪","0","0","195","154","1500","0","false","宋体","0"] -["367","90","1-0","1.5","♬","0","0","417","90","1500","0","false","宋体","0"] -["367","90","1-0","1.5","♬","0","0","317","90","1500","0","false","宋体","0"] -["367","90","1-0","1.5","♬","0","0","367","140","1500","0","false","宋体","0"] -["367","90","1-0","1.5","♬","0","0","367","40","1500","0","false","宋体","0"] -["367","90","1-0","1.5","♬","0","0","402","125","1500","0","false","宋体","0"] -["367","90","1-0","1.5","♬","0","0","402","55","1500","0","false","宋体","0"] -["367","90","1-0","1.5","♬","0","0","332","55","1500","0","false","宋体","0"] -["367","90","1-0","1.5","♬","0","0","332","125","1500","0","false","宋体","0"] -["323","215","1-0","1.5","♭","0","0","373","215","1500","0","false","宋体","0"] -["323","215","1-0","1.5","♭","0","0","273","215","1500","0","false","宋体","0"] -["323","215","1-0","1.5","♭","0","0","323","265","1500","0","false","宋体","0"] -["323","215","1-0","1.5","♭","0","0","323","165","1500","0","false","宋体","0"] -["323","215","1-0","1.5","♭","0","0","358","250","1500","0","false","宋体","0"] -["323","215","1-0","1.5","♭","0","0","358","180","1500","0","false","宋体","0"] -["323","215","1-0","1.5","♭","0","0","288","180","1500","0","false","宋体","0"] -["323","215","1-0","1.5","♭","0","0","288","250","1500","0","false","宋体","0"] -["240","269","1-0","1.5","♪","0","0","290","269","1500","0","false","宋体","0"] -["240","269","1-0","1.5","♪","0","0","190","269","1500","0","false","宋体","0"] -["240","269","1-0","1.5","♪","0","0","240","319","1500","0","false","宋体","0"] -["240","269","1-0","1.5","♪","0","0","240","219","1500","0","false","宋体","0"] -["240","269","1-0","1.5","♪","0","0","275","304","1500","0","false","宋体","0"] -["240","269","1-0","1.5","♪","0","0","205","304","1500","0","false","宋体","0"] -["240","269","1-0","1.5","♪","0","0","275","234","1500","0","false","宋体","0"] -["240","269","1-0","1.5","♪","0","0","205","234","1500","0","false","宋体","0"] -["148","145","1-0","1.5","♫","0","0","198","145","1500","0","false","宋体","0"] -["148","145","1-0","1.5","♫","0","0","98","145","1500","0","false","宋体","0"] -["148","145","1-0","1.5","♫","0","0","148","195","1500","0","false","宋体","0"] -["148","145","1-0","1.5","♫","0","0","148","95","1500","0","false","宋体","0"] -["148","145","1-0","1.5","♫","0","0","183","180","1500","0","false","宋体","0"] -["148","145","1-0","1.5","♫","0","0","183","110","1500","0","false","宋体","0"] -["148","145","1-0","1.5","♫","0","0","113","110","1500","0","false","宋体","0"] -["148","145","1-0","1.5","♫","0","0","113","180","1500","0","false","宋体","0"] -["111","236","1-0","1.5","♩","0","0","161","236","1500","0","false","宋体","0"] -["111","236","1-0","1.5","♩","0","0","61","236","1500","0","false","宋体","0"] -["111","236","1-0","1.5","♩","0","0","111","286","1500","0","false","宋体","0"] -["111","236","1-0","1.5","♩","0","0","111","186","1500","0","false","宋体","0"] -["111","236","1-0","1.5","♩","0","0","146","271","1500","0","false","宋体","0"] -["111","236","1-0","1.5","♩","0","0","146","201","1500","0","false","宋体","0"] -["111","236","1-0","1.5","♩","0","0","76","271","1500","0","false","宋体","0"] -["111","236","1-0","1.5","♩","0","0","76","201","1500","0","false","宋体","0"] -["251","59","1-0","1.5","♪","0","0","301","59","1500","0","false","宋体","0"] -["251","59","1-0","1.5","♪","0","0","201","59","1500","0","false","宋体","0"] -["251","59","1-0","1.5","♪","0","0","251","109","1500","0","false","宋体","0"] -["251","59","1-0","1.5","♪","0","0","251","9","1500","0","false","宋体","0"] -["251","59","1-0","1.5","♪","0","0","286","94","1500","0","false","宋体","0"] -["251","59","1-0","1.5","♪","0","0","286","24","1500","0","false","宋体","0"] -["251","59","1-0","1.5","♪","0","0","216","24","1500","0","false","宋体","0"] -["251","59","1-0","1.5","♪","0","0","216","94","1500","0","false","宋体","0"] -["298","228","1-0","1.5","♭","0","0","348","228","1500","0","false","宋体","0"] -["298","228","1-0","1.5","♭","0","0","248","228","1500","0","false","宋体","0"] -["298","228","1-0","1.5","♭","0","0","298","278","1500","0","false","宋体","0"] -["298","228","1-0","1.5","♭","0","0","298","178","1500","0","false","宋体","0"] -["298","228","1-0","1.5","♭","0","0","333","263","1500","0","false","宋体","0"] -["298","228","1-0","1.5","♭","0","0","333","193","1500","0","false","宋体","0"] -["298","228","1-0","1.5","♭","0","0","263","263","1500","0","false","宋体","0"] -["298","228","1-0","1.5","♭","0","0","263","193","1500","0","false","宋体","0"] -["369","120","1-0","1.5","♬","0","0","419","120","1500","0","false","宋体","0"] -["369","120","1-0","1.5","♬","0","0","319","120","1500","0","false","宋体","0"] -["369","120","1-0","1.5","♬","0","0","369","170","1500","0","false","宋体","0"] -["369","120","1-0","1.5","♬","0","0","369","70","1500","0","false","宋体","0"] -["369","120","1-0","1.5","♬","0","0","404","155","1500","0","false","宋体","0"] -["369","120","1-0","1.5","♬","0","0","404","85","1500","0","false","宋体","0"] -["369","120","1-0","1.5","♬","0","0","334","85","1500","0","false","宋体","0"] -["369","120","1-0","1.5","♬","0","0","334","155","1500","0","false","宋体","0"] -["369","120","1-0","1.5","♬","0","0","419","120","1500","0","false","宋体","0"] -["369","120","1-0","1.5","♬","0","0","319","120","1500","0","false","宋体","0"] -["369","120","1-0","1.5","♬","0","0","369","170","1500","0","false","宋体","0"] -["369","120","1-0","1.5","♬","0","0","369","70","1500","0","false","宋体","0"] -["369","120","1-0","1.5","♬","0","0","404","155","1500","0","false","宋体","0"] -["369","120","1-0","1.5","♬","0","0","404","85","1500","0","false","宋体","0"] -["369","120","1-0","1.5","♬","0","0","334","85","1500","0","false","宋体","0"] -["369","120","1-0","1.5","♬","0","0","334","155","1500","0","false","宋体","0"] -["135","221","1-0","1.5","♪","0","0","185","221","1500","0","false","宋体","0"] -["135","221","1-0","1.5","♪","0","0","85","221","1500","0","false","宋体","0"] -["135","221","1-0","1.5","♪","0","0","135","271","1500","0","false","宋体","0"] -["135","221","1-0","1.5","♪","0","0","135","171","1500","0","false","宋体","0"] -["135","221","1-0","1.5","♪","0","0","170","256","1500","0","false","宋体","0"] -["135","221","1-0","1.5","♪","0","0","100","256","1500","0","false","宋体","0"] -["135","221","1-0","1.5","♪","0","0","170","186","1500","0","false","宋体","0"] -["135","221","1-0","1.5","♪","0","0","100","186","1500","0","false","宋体","0"] -["286","106","1-0","1.5","♩","0","0","336","106","1500","0","false","宋体","0"] -["286","106","1-0","1.5","♩","0","0","236","106","1500","0","false","宋体","0"] -["286","106","1-0","1.5","♩","0","0","286","156","1500","0","false","宋体","0"] -["286","106","1-0","1.5","♩","0","0","286","56","1500","0","false","宋体","0"] -["286","106","1-0","1.5","♩","0","0","251","141","1500","0","false","宋体","0"] -["286","106","1-0","1.5","♩","0","0","321","141","1500","0","false","宋体","0"] -["286","106","1-0","1.5","♩","0","0","251","71","1500","0","false","宋体","0"] -["286","106","1-0","1.5","♩","0","0","321","71","1500","0","false","宋体","0"] -["334","239","1-0","1.5","♭","0","0","384","239","1500","0","false","宋体","0"] -["334","239","1-0","1.5","♭","0","0","284","239","1500","0","false","宋体","0"] -["334","239","1-0","1.5","♭","0","0","334","289","1500","0","false","宋体","0"] -["334","239","1-0","1.5","♭","0","0","334","189","1500","0","false","宋体","0"] -["334","239","1-0","1.5","♭","0","0","369","204","1500","0","false","宋体","0"] -["334","239","1-0","1.5","♭","0","0","299","204","1500","0","false","宋体","0"] -["334","239","1-0","1.5","♭","0","0","369","274","1500","0","false","宋体","0"] -["334","239","1-0","1.5","♭","0","0","299","274","1500","0","false","宋体","0"] -["152","110","1-0","1.5","♬","0","0","202","110","1500","0","false","宋体","0"] -["152","110","1-0","1.5","♬","0","0","102","110","1500","0","false","宋体","0"] -["152","110","1-0","1.5","♬","0","0","152","160","1500","0","false","宋体","0"] -["152","110","1-0","1.5","♬","0","0","152","60","1500","0","false","宋体","0"] -["152","110","1-0","1.5","♬","0","0","187","145","1500","0","false","宋体","0"] -["152","110","1-0","1.5","♬","0","0","117","145","1500","0","false","宋体","0"] -["152","110","1-0","1.5","♬","0","0","187","75","1500","0","false","宋体","0"] -["152","110","1-0","1.5","♬","0","0","117","75","1500","0","false","宋体","0"] -["221","257","1-0","1.5","♫","0","0","271","257","1500","0","false","宋体","0"] -["221","257","1-0","1.5","♫","0","0","171","257","1500","0","false","宋体","0"] -["221","257","1-0","1.5","♫","0","0","221","307","1500","0","false","宋体","0"] -["221","257","1-0","1.5","♫","0","0","221","207","1500","0","false","宋体","0"] -["221","257","1-0","1.5","♫","0","0","256","292","1500","0","false","宋体","0"] -["221","257","1-0","1.5","♫","0","0","256","222","1500","0","false","宋体","0"] -["221","257","1-0","1.5","♫","0","0","186","292","1500","0","false","宋体","0"] -["221","257","1-0","1.5","♫","0","0","186","222","1500","0","false","宋体","0"] -["330","191","1-0","1.5","♪","0","0","380","191","1500","0","false","宋体","0"] -["330","191","1-0","1.5","♪","0","0","280","191","1500","0","false","宋体","0"] -["330","191","1-0","1.5","♪","0","0","330","241","1500","0","false","宋体","0"] -["330","191","1-0","1.5","♪","0","0","330","141","1500","0","false","宋体","0"] -["330","191","1-0","1.5","♪","0","0","365","226","1500","0","false","宋体","0"] -["330","191","1-0","1.5","♪","0","0","365","156","1500","0","false","宋体","0"] -["330","191","1-0","1.5","♪","0","0","295","156","1500","0","false","宋体","0"] -["330","191","1-0","1.5","♪","0","0","295","226","1500","0","false","宋体","0"] -["187","134","1-0","1.5","♬","0","0","237","134","1500","0","false","宋体","0"] -["187","134","1-0","1.5","♬","0","0","137","134","1500","0","false","宋体","0"] -["187","134","1-0","1.5","♬","0","0","187","184","1500","0","false","宋体","0"] -["187","134","1-0","1.5","♬","0","0","187","84","1500","0","false","宋体","0"] -["187","134","1-0","1.5","♬","0","0","222","169","1500","0","false","宋体","0"] -["187","134","1-0","1.5","♬","0","0","222","99","1500","0","false","宋体","0"] -["187","134","1-0","1.5","♬","0","0","152","99","1500","0","false","宋体","0"] -["187","134","1-0","1.5","♬","0","0","152","169","1500","0","false","宋体","0"] -["236","272","1-0","1.5","♪","0","0","286","272","1500","0","false","宋体","0"] -["236","272","1-0","1.5","♪","0","0","186","272","1500","0","false","宋体","0"] -["236","272","1-0","1.5","♪","0","0","236","322","1500","0","false","宋体","0"] -["236","272","1-0","1.5","♪","0","0","236","222","1500","0","false","宋体","0"] -["236","272","1-0","1.5","♪","0","0","271","307","1500","0","false","宋体","0"] -["236","272","1-0","1.5","♪","0","0","271","237","1500","0","false","宋体","0"] -["236","272","1-0","1.5","♪","0","0","201","237","1500","0","false","宋体","0"] -["236","272","1-0","1.5","♪","0","0","201","307","1500","0","false","宋体","0"] -["277","117","1-0","1.5","♫","0","0","327","117","1500","0","false","宋体","0"] -["277","117","1-0","1.5","♫","0","0","227","117","1500","0","false","宋体","0"] -["277","117","1-0","1.5","♫","0","0","277","167","1500","0","false","宋体","0"] -["277","117","1-0","1.5","♫","0","0","277","67","1500","0","false","宋体","0"] -["277","117","1-0","1.5","♫","0","0","312","152","1500","0","false","宋体","0"] -["277","117","1-0","1.5","♫","0","0","312","82","1500","0","false","宋体","0"] -["277","117","1-0","1.5","♫","0","0","242","82","1500","0","false","宋体","0"] -["277","117","1-0","1.5","♫","0","0","242","152","1500","0","false","宋体","0"] -["221","257","1-0","1.5","♫","0","0","271","257","1500","0","false","宋体","0"] -["221","257","1-0","1.5","♫","0","0","171","257","1500","0","false","宋体","0"] -["221","257","1-0","1.5","♫","0","0","221","307","1500","0","false","宋体","0"] -["221","257","1-0","1.5","♫","0","0","221","207","1500","0","false","宋体","0"] -["221","257","1-0","1.5","♫","0","0","256","292","1500","0","false","宋体","0"] -["221","257","1-0","1.5","♫","0","0","256","222","1500","0","false","宋体","0"] -["221","257","1-0","1.5","♫","0","0","186","292","1500","0","false","宋体","0"] -["221","257","1-0","1.5","♫","0","0","186","222","1500","0","false","宋体","0"] -["330","191","1-0","1.5","♪","0","0","380","191","1500","0","false","宋体","0"] -["330","191","1-0","1.5","♪","0","0","280","191","1500","0","false","宋体","0"] -["330","191","1-0","1.5","♪","0","0","330","241","1500","0","false","宋体","0"] -["330","191","1-0","1.5","♪","0","0","330","141","1500","0","false","宋体","0"] -["330","191","1-0","1.5","♪","0","0","365","226","1500","0","false","宋体","0"] -["330","191","1-0","1.5","♪","0","0","365","156","1500","0","false","宋体","0"] -["330","191","1-0","1.5","♪","0","0","295","156","1500","0","false","宋体","0"] -["330","191","1-0","1.5","♪","0","0","295","226","1500","0","false","宋体","0"] -["187","134","1-0","1.5","♬","0","0","237","134","1500","0","false","宋体","0"] -["187","134","1-0","1.5","♬","0","0","137","134","1500","0","false","宋体","0"] -["187","134","1-0","1.5","♬","0","0","187","184","1500","0","false","宋体","0"] -["187","134","1-0","1.5","♬","0","0","187","84","1500","0","false","宋体","0"] -["187","134","1-0","1.5","♬","0","0","222","169","1500","0","false","宋体","0"] -["187","134","1-0","1.5","♬","0","0","222","99","1500","0","false","宋体","0"] -["187","134","1-0","1.5","♬","0","0","152","99","1500","0","false","宋体","0"] -["187","134","1-0","1.5","♬","0","0","152","169","1500","0","false","宋体","0"] -["146","267","1-0","1.5","♪","0","0","196","267","1500","0","false","宋体","0"] -["146","267","1-0","1.5","♪","0","0","96","267","1500","0","false","宋体","0"] -["146","267","1-0","1.5","♪","0","0","146","317","1500","0","false","宋体","0"] -["146","267","1-0","1.5","♪","0","0","146","217","1500","0","false","宋体","0"] -["146","267","1-0","1.5","♪","0","0","181","302","1500","0","false","宋体","0"] -["146","267","1-0","1.5","♪","0","0","181","232","1500","0","false","宋体","0"] -["146","267","1-0","1.5","♪","0","0","111","232","1500","0","false","宋体","0"] -["146","267","1-0","1.5","♪","0","0","111","302","1500","0","false","宋体","0"] -["245","232","1-0","1.5","♬","0","0","295","232","1500","0","false","宋体","0"] -["245","232","1-0","1.5","♬","0","0","195","232","1500","0","false","宋体","0"] -["245","232","1-0","1.5","♬","0","0","245","282","1500","0","false","宋体","0"] -["245","232","1-0","1.5","♬","0","0","245","182","1500","0","false","宋体","0"] -["245","232","1-0","1.5","♬","0","0","280","267","1500","0","false","宋体","0"] -["245","232","1-0","1.5","♬","0","0","280","197","1500","0","false","宋体","0"] -["245","232","1-0","1.5","♬","0","0","210","267","1500","0","false","宋体","0"] -["245","232","1-0","1.5","♬","0","0","210","197","1500","0","false","宋体","0"] -["328","170","1-0","1.5","♩","0","0","378","170","1500","0","false","宋体","0"] -["328","170","1-0","1.5","♩","0","0","278","170","1500","0","false","宋体","0"] -["328","170","1-0","1.5","♩","0","0","328","120","1500","0","false","宋体","0"] -["328","170","1-0","1.5","♩","0","0","328","220","1500","0","false","宋体","0"] -["328","170","1-0","1.5","♩","0","0","363","205","1500","0","false","宋体","0"] -["328","170","1-0","1.5","♩","0","0","363","135","1500","0","false","宋体","0"] -["328","170","1-0","1.5","♩","0","0","293","135","1500","0","false","宋体","0"] -["328","170","1-0","1.5","♩","0","0","293","205","1500","0","false","宋体","0"] -["373","96","1-0","1.5","♫","0","0","373","146","1500","0","false","宋体","0"] -["373","96","1-0","1.5","♫","0","0","373","46","1500","0","false","宋体","0"] -["373","96","1-0","1.5","♫","0","0","423","96","1500","0","false","宋体","0"] -["373","96","1-0","1.5","♫","0","0","323","96","1500","0","false","宋体","0"] -["373","96","1-0","1.5","♫","0","0","408","131","1500","0","false","宋体","0"] -["373","96","1-0","1.5","♫","0","0","408","61","1500","0","false","宋体","0"] -["373","96","1-0","1.5","♫","0","0","338","131","1500","0","false","宋体","0"] -["373","96","1-0","1.5","♫","0","0","338","61","1500","0","false","宋体","0"] -["255","299","0-1","3","其れは(C'est)——","10","0","68","262","2000","0","true","黑体","0"] -["252","299","0-1","3","其れは(C'est)——","10","0","65","262","2000","0","true","黑体","0"] -["249","299","0-1","3","其れは(C'est)——","10","0","62","262","2000","0","true","黑体","0"] -["246","299","0-1","3","其れは(C'est)——","10","0","59","262","2000","0","true","黑体","0"] -["135","42","1-1","4.5","███████/n█     █/n█     █/n█     █/n███████/n█     █/n█     █/n█     █/n███████","0","130","135","42","0","0","true","MS UI Gothic","0"] -["404","42","1-1","4.5","███████/n█     █/n█     █/n█     █/n███████/n█     █/n█     █/n█     █/n███████","0","50","404","42","0","0","true","MS UI Gothic","0"] -["-8","0","1-1","4.5","███████████████████████████","0","0","-8","0","0","0","true","MS UI Gothic","0"] -["-8","0","1-1","4.5"," ██████████████████████████","0","0","-8","0","0","0","false","MS UI Gothic","0"] -["-8","350","1-1","4.5","███████████████████████████","0","0","-8","350","0","0","true","MS UI Gothic","0"] -["-8","350","1-1","4.5"," ██████████████████████████","0","0","-8","350","0","0","false","MS UI Gothic","0"] -["-4","0","1-1","4.5","█/n█/n█/n█/n█/n█/n█/n█/n█/n█/n█/n█/n","0","0","-4","0","0","0","true","MS UI Gothic","0"] -["-4","5","1-1","4.5","█/n█/n█/n█/n█/n█/n█/n█/n█/n█/n█/n█/n","0","0","-4","5","0","0","false","MS UI Gothic","0"] -["519","0","1-1","4.5","█/n█/n█/n█/n█/n█/n█/n█/n█/n█/n█/n█/n","0","0","519","0","0","0","true","MS UI Gothic","0"] -["519","5","1-1","4.5","█/n█/n█/n█/n█/n█/n█/n█/n█/n█/n█/n█/n","0","0","519","5","0","0","false","MS UI Gothic","0"] -["246","248","0.7-1","4.5","夜の窓辺に","0","0","203","248","800","0","true","黑体","0"] -["305","216","0.7-1","3.4","微笑む月","0","0","249","216","500","0","true","黑体","0"] -["313","58","1-1","6.1","●","0","0","313","58","500","0","true","黑体","0"] -["307","48","1-1","6.1","●","0","0","307","48","500","0","false","黑体","0"] -["207","77","0-1","3","★","10","0","207","77","500","0","false","黑体","0"] -["451","129","0-1","3","★","45","0","451","129","500","0","false","黑体","0"] -["267","139","0-1","3","★","45","0","267","139","500","0","false","黑体","0"] -["165","135","0-1","3","★","60","0","165","135","500","0","false","黑体","0"] -["77","107","0-1","3","★","70","0","77","107","500","0","false","黑体","0"] -["477","100","0-1","3","★","70","0","477","100","500","0","false","黑体","0"] -["344","176","0-1","3","★","20","0","344","176","500","0","false","黑体","0"] -["101","216","0-1","3","★","20","0","101","216","500","0","false","黑体","0"] -["141","73","0-1","3","★","10","0","141","73","500","0","false","黑体","0"] -["207","77","1-0","3","★","10","0","207","77","500","0","false","黑体","0"] -["451","129","1-0","3","★","45","0","451","129","500","0","false","黑体","0"] -["267","139","1-0","3","★","45","0","267","139","500","0","false","黑体","0"] -["165","135","1-0","3","★","60","0","165","135","500","0","false","黑体","0"] -["77","107","1-0","3","★","70","0","77","107","500","0","false","黑体","0"] -["477","100","1-0","3","★","70","0","477","100","500","0","false","黑体","0"] -["344","176","1-0","3","★","20","0","344","176","500","0","false","黑体","0"] -["101","216","1-0","3","★","20","0","101","216","500","0","false","黑体","0"] -["141","73","1-0","3","★","10","0","141","73","500","0","false","黑体","0"] -["197","145","0-1","3.5","秋","0","0","177","125","1500","0","false","MS UI Gothic","0"] -["157","105","0-1","3.5","秋","0","0","177","125","1500","0","false","MS UI Gothic","0"] -["197","145","0-1","3.5"," の","0","0","187","125","1500","0","false","MS UI Gothic","0"] -["167","105","0-1","3.5"," の","0","0","187","125","1500","0","false","MS UI Gothic","0"] -["211","145","0-1","3.5","  追","0","0","191","125","1500","0","false","MS UI Gothic","0"] -["171","105","0-1","3.5","  追","0","0","191","125","1500","0","false","MS UI Gothic","0"] -["220","145","0-1","3.5","   想","0","0","200","125","1500","0","false","MS UI Gothic","0"] -["180","105","0-1","3.5","   想","0","0","200","125","1500","0","false","MS UI Gothic","0"] -["366","124","0-1","4","◆","70","70","366","124","500","0","true","黑体","0"] -["370","153","0-1","4","◆","109","100","370","153","500","0","true","黑体","0"] -["358","130","0-1","4","◆","30","75","358","130","500","0","true","黑体","0"] -["334","107","0-1","4","◆","330","75","334","107","500","0","true","黑体","0"] -["354","140","0-1","4","◆","344","75","354","140","500","0","true","黑体","0"] -["358","110","0-1","4","__","344","75","358","110","500","0","true","黑体","0"] -/n/n/n/n/n/n/n/n/n/n/n綺   /n/n/n -/n/n/n/n/n/n/n/n/n/n/n   綺/n/n/n -/n/n/n/n/n/n/n/n/n/n 麗  /n/n/n/n -/n/n/n/n/n/n/n/n/n/n  麗 /n/n/n/n -/n/n/n/n/n/n/n/n/n  な /n/n/n/n/n -/n/n/n/n/n/n/n/n/n な  /n/n/n/n/n -/n/n/n/n/n/n/n/n   音/n/n/n/n/n/n -/n/n/n/n/n/n/n/n音   /n/n/n/n/n/n -["258","297","1-1","3","█","0","0","258","297","500","0","false","宋体","0"] -["258","270","1-1","3","█","0","0","258","270","500","0","false","宋体","0"] -["258","244","1-1","3","█","0","0","258","244","500","0","false","宋体","0"] -["258","217","1-1","3","█","0","0","258","217","500","0","false","宋体","0"] -["258","297","1-1","3","綺","0","0","258","297","500","0","false","黑体","0"] -["258","270","1-1","3","麗","0","0","258","270","500","0","false","黑体","0"] -["258","244","1-1","3","な","0","0","258","244","500","0","false","黑体","0"] -["258","217","1-1","3","音","0","0","258","217","500","0","false","黑体","0"] -["207","91","1-1","3","詠","350","0","207","91","500","0","true","黑体","0"] -["207","91","1-1","3"," う","350","0","207","91","500","0","true","黑体","0"] -["210","91","1-1","3","  少","15","0","210","91","500","0","true","黑体","0"] -["210","91","1-1","3","   女","15","0","210","91","500","0","true","黑体","0"] -["210","91","1-1","3","    (Monica)","15","0","210","91","500","0","true","黑体","0"] -["192","92","1-0","1.1","♪","0","0","192","92","500","0","false","宋体","0"] -["227","72","1-0","1.1","♩","0","0","227","72","500","0","false","宋体","0"] -["254","91","1-0","1.1","♬","0","0","254","91","500","0","false","宋体","0"] -["163","173","0-1","4.5","蟲の羽音","0","0","163","173","500","0","true","黑体","0"] -["120","344","1-1","4.5","█","350","45","120","344","500","0","false","黑体","0"] -["61","362","1-1","4.5","█","340","30","61","362","500","0","false","黑体","0"] -["53","351","1-1","4.5","█","330","40","53","351","500","0","false","黑体","0"] -["38","337","1-1","4.5","█","315","45","38","337","500","0","false","黑体","0"] -["23","323","1-1","4.5","█","310","50","23","323","500","0","false","黑体","0"] -["13","315","1-1","4.5","█","305","55","13","315","500","0","false","黑体","0"] -["3","307","1-1","4.5","█","305","58","3","307","500","0","false","黑体","0"] -["0","303","1-1","4.5","█","305","63","0","303","500","0","false","黑体","0"] -["0","308","1-1","4.5","█","308","63","0","308","500","0","false","黑体","0"] -["22","326","1-1","4.5","█","312","60","22","326","500","0","false","黑体","0"] -["40","344","1-1","4.5","█","318","55","40","344","500","0","false","黑体","0"] -["58","359","1-1","4.5","█","340","50","58","359","500","0","false","黑体","0"] -["132","353","1-1","4.5","█","0","0","132","353","500","0","true","黑体","0"] -["143","323","1-1","4.5","█","10","30","143","323","500","0","false","黑体","0"] -["154","302","1-1","4.5","█","14","30","154","302","500","0","false","黑体","0"] -["167","282","1-1","4.5","█","22","35","167","282","500","0","false","黑体","0"] -["179","267","1-1","4.5","█","30","35","179","267","500","0","false","黑体","0"] -["197","252","1-1","4.5","█","35","50","197","252","500","0","false","黑体","0"] -["214","242","1-1","4.5","█","40","60","214","242","500","0","false","黑体","0"] -["231","233","1-1","4.5","█","48","70","231","233","500","0","false","黑体","0"] -["250","224","1-1","4.5","█","48","83","250","224","500","0","false","黑体","0"] -["82","354","1-1","4.5","█","0","20","82","354","500","0","false","黑体","0"] -["78","333","1-1","4.5","█","350","20","78","333","500","0","false","黑体","0"] -["72","311","1-1","4.5","█","345","23","72","311","500","0","false","黑体","0"] -["58","296","1-1","4.5","█","325","30","58","296","500","0","false","黑体","0"] -["44","281","1-1","4.5","█","315","40","44","281","500","0","false","黑体","0"] -["28","266","1-1","4.5","█","310","50","28","266","500","0","false","黑体","0"] -["9","252","1-1","4.5","█","305","60","9","252","500","0","false","黑体","0"] -["0","244","1-1","4.5","█","305","70","0","244","500","0","false","黑体","0"] -["0","244","1-1","4.5","█","293","70","0","244","500","0","false","黑体","0"] -["22","253","1-1","4.5","█","293","70","22","253","500","0","false","黑体","0"] -["46","264","1-1","4.5","█","302","65","46","264","500","0","false","黑体","0"] -["64","278","1-1","4.5","█","310","58","64","278","500","0","false","黑体","0"] -["83","296","1-1","4.5","█","322","52","83","296","500","0","false","黑体","0"] -["96","314","1-1","4.5","█","340","48","96","314","500","0","false","黑体","0"] -["107","338","1-1","4.5","█","355","48","107","338","500","0","false","黑体","0"] -["110","363","1-1","4.5","█","0","48","110","363","500","0","false","黑体","0"] -["254","227","1-1","4.5","█","73","82","254","227","500","0","false","黑体","0"] -["227","236","1-1","4.5","█","65","78","227","236","500","0","false","黑体","0"] -["206","246","1-1","4.5","█","57","74","206","246","500","0","false","黑体","0"] -["183","260","1-1","4.5","█","45","70","183","260","500","0","false","黑体","0"] -["168","275","1-1","4.5","█","40","64","168","275","500","0","false","黑体","0"] -["148","297","1-1","4.5","█","28","60","148","297","500","0","false","黑体","0"] -["134","324","1-1","4.5","█","20","55","134","324","500","0","false","黑体","0"] -["124","353","1-1","4.5","█","10","55","124","353","500","0","false","黑体","0"] -["177","288","1-1","4.5","__","88","0","177","288","500","0","false","黑体","0"] -["177","264","1-1","4.5","_","90","0","177","264","500","0","false","黑体","0"] -["180","246","1-1","4.5","_","95","0","180","246","500","0","true","黑体","0"] -["184","226","1-1","4.5","_","98","0","184","226","500","0","false","黑体","0"] -["190","208","1-1","4.5","_","104","0","190","208","500","0","false","黑体","0"] -["159","308","1-1","4.5","▁","88","0","159","308","500","0","false","黑体","0"] -["159","282","1-1","4.5","▁","90","0","159","282","500","0","false","黑体","0"] -["160","252","1-1","4.5","▁","92","0","160","252","500","0","false","黑体","0"] -["164","225","1-1","4.5","▁","94","10","164","225","500","0","false","黑体","0"] -["170","203","1-1","4.5","▁","98","28","170","203","500","0","false","黑体","0"] -["168","188","1-1","4.5","▁","102","0","168","188","500","0","false","黑体","0"] -["145","141","1-1","4.5","●","22","60","145","141","500","0","false","宋体","0"] -["147","151","1-1","4.5","●","21","60","147","151","500","0","false","宋体","0"] -["284","264","0-0.7","1.5","●","0","0","284","264","500","0","false","黑体","0"] -["286","266","0-1","1.5","●","0","0","286","266","500","0","false","黑体","0"] -["80","216","0-0.7","1.5","●","0","0","80","216","500","0","false","黑体","0"] -["84","219","0-1","1.5","●","0","0","84","219","500","0","false","黑体","0"] -["354","212","0-0.7","1.5","●","0","0","354","212","500","0","false","黑体","0"] -["358","216","0-1","1.5","●","0","0","358","216","500","0","false","黑体","0"] -["200","66","0-0.7","1.5","●","0","0","200","66","500","0","false","黑体","0"] -["204","70","0-1","1.5","●","0","0","204","70","500","0","false","黑体","0"] -["200","122","0-0.7","1.5","●","0","0","200","122","500","0","true","黑体","0"] -["203","126","0-1","1.5","●","0","0","203","126","500","0","false","黑体","0"] -["53","110","0-0.7","1.5","●","0","0","53","110","500","0","false","黑体","0"] -["57","116","0-1","1.5","●","0","0","57","116","500","0","false","黑体","0"] -["386","326","0-0.7","1.5","●","0","0","386","326","500","0","false","黑体","0"] -["390","328","1-1","1.5","●","0","0","390","328","500","0","false","黑体","0"] -["284","264","0.7-0","1.5","●","0","0","284","264","500","0","false","黑体","0"] -["286","266","1-0","1.5","●","0","0","286","266","500","0","false","黑体","0"] -["80","216","0.7-0","1.5","●","0","0","80","216","500","0","false","黑体","0"] -["84","219","1-0","1.5","●","0","0","84","219","500","0","false","黑体","0"] -["354","212","0.7-0","1.5","●","0","0","354","212","500","0","false","黑体","0"] -["358","216","1-0","1.5","●","0","0","358","216","500","0","false","黑体","0"] -["200","66","0.7-0","1.5","●","0","0","200","66","500","0","false","黑体","0"] -["204","70","1-0","1.5","●","0","0","204","70","500","0","false","黑体","0"] -["200","122","0.7-0","1.5","●","0","0","200","122","500","0","true","黑体","0"] -["203","126","1-0","1.5","●","0","0","203","126","500","0","false","黑体","0"] -["53","110","0.7-0","1.5","●","0","0","53","110","500","0","false","黑体","0"] -["57","116","1-0","1.5","●","0","0","57","116","500","0","false","黑体","0"] -["386","326","0.7-0","1.5","●","0","0","386","326","500","0","false","黑体","0"] -["390","328","1-0","1.5","●","0","0","390","328","500","0","false","黑体","0"] -["345","76","1-1","4.5","◯","0","0","345","76","500","0","false","宋体","0"] -["398","89","1-1","4.5","12","0","0","398","89","500","0","false","宋体","0"] -["404","170","1-1","4.5","6","0","0","404","170","500","0","false","宋体","0"] -["443","127","1-1","4.5","3","0","0","443","127","500","0","false","宋体","0"] -["365","127","1-1","4.5","9","0","0","365","127","500","0","false","宋体","0"] -["404","124","1-1","4.5","。","0","0","404","124","500","0","false","宋体","0"] -["376","108","1-1","4.5","-","30","0","376","108","500","0","false","宋体","0"] -["397","94","1-1","4.5","-","60","0","397","94","500","0","false","宋体","0"] -["446","105","1-1","4.5","-","120","0","446","105","500","0","false","黑体","0"] -["457","125","1-1","4.5","-","150","0","457","125","500","0","false","黑体","0"] -["445","150","1-1","4.5","-","21","0","445","150","500","0","false","黑体","0"] -["450","176","1-1","4.5","-","210","0","450","176","500","0","false","黑体","0"] -["428","187","1-1","4.5","-","240","0","428","187","500","0","false","黑体","0"] -["375","177","1-1","4.5","-","300","0","375","177","500","0","false","黑体","0"] -["366","156","1-1","4.5","-","330","0","366","156","500","0","false","黑体","0"] -["394","137","1-1","4.5","↓","0","0","394","137","500","0","true","黑体","0"] -["417","118","1-0","1","↓","90","0","417","118","500","0","true","黑体","0"] -["421","119","1-0","1","↓","100","0","421","119","500","0","true","黑体","0"] -["424","120","1-0","1","↓","110","0","424","120","500","0","true","黑体","0"] -["428","122","1-0","1","↓","120","0","428","122","500","0","true","黑体","0"] -["432","124","1-0","1","↓","130","0","432","124","500","0","true","黑体","0"] -["363","195","1-0","4.5","針","30","0","363","195","500","0","true","黑体","0"] -["341","165","1-0","4.5","は","60","0","341","165","500","0","true","黑体","0"] -["340","128","1-0","4.5","進","90","0","340","128","500","0","true","黑体","0"] -["354","93","1-0","4.5","ん","120","0","354","93","500","0","true","黑体","0"] -["387","73","1-0","4.5","だ","150","0","387","73","500","0","true","黑体","0"] -["361","286","0-1","3","其れは(C'est)——","350","0","226","309","2000","0","true","黑体","0"] -["361","283","0-1","3","其れは(C'est)——","350","0","226","306","2000","0","true","黑体","0"] -["361","280","0-1","3","其れは(C'est)——","350","0","226","303","2000","0","true","黑体","0"] -["361","277","0-1","3","其れは(C'est)——","350","0","226","300","2000","0","true","黑体","0"] -["175","215","0-1","2","大地を包み","0","0","125","215","2000","0","true","黑体","0"] -["223","245","0-1","2","微眠む雪","0","0","173","245","2000","0","true","黑体","0"] -["226","245","0-1","2","   雪","0","0","176","245","2000","0","true","黑体","0"] -["125","215","1-0","2","大地を包み","0","0","75","215","2000","0","true","黑体","0"] -["173","245","1-0","2","微眠む雪","0","0","123","245","2000","0","true","黑体","0"] -["176","245","1-0","2","   雪","0","0","126","245","2000","0","true","黑体","0"] -["352","35","1-1","4.5","❄","10","0","335","185","4500","0","true","宋体","0"] -["143","16","1-1","4.5","❄","340","0","231","307","4500","0","true","宋体","0"] -["274","42","1-1","4.5","❅","340","0","91","318","4500","0","true","宋体","0"] -["470","52","1-1","4.5","❅","5","0","423","314","4500","0","true","宋体","0"] -["229","26","1-1","4.5","❅","355","0","252","316","4500","0","true","宋体","0"] -["87","24","1-1","4.5","❆","340","10","191","285","4500","0","true","宋体","0"] -["420","13","1-1","4.5","❆","0","10","402","292","4500","0","true","宋体","0"] -["42","74","1-1","4.5","❄","0","10","130","325","4500","0","true","宋体","0"] -["97","73","1-1","4.5","❅","30","10","235","303","4500","0","true","宋体","0"] -["312","43","1-1","4.5","❄","30","0","328","240","4500","0","true","宋体","0"] -["174","30","1-1","4.5","❄","30","0","157","239","4500","0","true","宋体","0"] -["242","101","1-1","1.1","❄","0","0","242","131","1100","0","true","MS UI Gothic","0"] -["252","101","1-1","1.1"," ❅","0","0","252","131","1100","0","true","MS UI Gothic","0"] -["256","101","1-1","1.1","  ❆","0","0","256","131","1100","0","true","MS UI Gothic","0"] -["265","101","1-1","1.1","   ❄","0","0","265","131","1100","0","true","MS UI Gothic","0"] -["242","131","1-1","4.5","冬","0","0","242","131","500","0","true","MS UI Gothic","0"] -["252","131","1-1","4.5"," の","0","0","252","131","500","0","true","MS UI Gothic","0"] -["256","131","1-1","4.5","  追","0","0","256","131","500","0","true","MS UI Gothic","0"] -["265","131","1-1","4.5","   想","0","0","265","131","500","0","true","MS UI Gothic","0"] -["-20","45","1-1","4.5","♪","0","0","600","45","4000","0","true","宋体","0"] -["53","43","0.1-0.1","3","綺","0","0","53","43","500","0","true","黑体","0"] -["53","43","0.2-0.2","3","綺","0","0","53","43","500","0","true","黑体","0"] -["53","43","0.3-0.3","3","綺","0","0","53","43","500","0","true","黑体","0"] -["53","43","0.4-0.4","3","綺","0","0","53","43","500","0","true","黑体","0"] -["53","43","0.5-0.5","3","綺","0","0","53","43","500","0","true","黑体","0"] -["53","43","0.6-0.6","3","綺","0","0","53","43","500","0","true","黑体","0"] -["53","43","0.7-0.7","3","綺","0","0","53","43","500","0","true","黑体","0"] -["53","43","0.8-0.8","3","綺","0","0","53","43","500","0","true","黑体","0"] -["53","43","0.9-0.9","3","綺","0","0","53","43","500","0","true","黑体","0"] -["53","43","1-1","3","綺","0","0","53","43","500","0","true","黑体","0"] -["143","43","0.1-0.1","3","麗","0","0","143","43","500","0","true","黑体","0"] -["143","43","0.2-0.2","3","麗","0","0","143","43","500","0","true","黑体","0"] -["143","43","0.3-0.3","3","麗","0","0","143","43","500","0","true","黑体","0"] -["143","43","0.4-0.4","3","麗","0","0","143","43","500","0","true","黑体","0"] -["143","43","0.5-0.5","3","麗","0","0","143","43","500","0","true","黑体","0"] -["143","43","0.6-0.6","3","麗","0","0","143","43","500","0","true","黑体","0"] -["143","43","0.7-0.7","3","麗","0","0","143","43","500","0","true","黑体","0"] -["143","43","0.8-0.8","3","麗","0","0","143","43","500","0","true","黑体","0"] -["143","43","0.9-0.9","3","麗","0","0","143","43","500","0","true","黑体","0"] -["143","43","1-1","3","麗","0","0","143","43","500","0","true","黑体","0"] -["233","43","0.1-0.1","3","だ","0","0","233","43","500","0","true","黑体","0"] -["233","43","0.2-0.2","3","だ","0","0","233","43","500","0","true","黑体","0"] -["233","43","0.3-0.3","3","だ","0","0","233","43","500","0","true","黑体","0"] -["233","43","0.4-0.4","3","だ","0","0","233","43","500","0","true","黑体","0"] -["233","43","0.5-0.5","3","だ","0","0","233","43","500","0","true","黑体","0"] -["233","43","0.6-0.6","3","だ","0","0","233","43","500","0","true","黑体","0"] -["233","43","0.7-0.7","3","だ","0","0","233","43","500","0","true","黑体","0"] -["233","43","0.8-0.8","3","だ","0","0","233","43","500","0","true","黑体","0"] -["233","43","0.9-0.9","3","だ","0","0","233","43","500","0","true","黑体","0"] -["233","43","1-1","3","だ","0","0","233","43","500","0","true","黑体","0"] -["323","43","0.1-0.1","3","ね","0","0","323","43","500","0","true","黑体","0"] -["323","43","0.2-0.2","3","ね","0","0","323","43","500","0","true","黑体","0"] -["323","43","0.3-0.3","3","ね","0","0","323","43","500","0","true","黑体","0"] -["323","43","0.4-0.4","3","ね","0","0","323","43","500","0","true","黑体","0"] -["323","43","0.5-0.5","3","ね","0","0","323","43","500","0","true","黑体","0"] -["323","43","0.6-0.6","3","ね","0","0","323","43","500","0","true","黑体","0"] -["323","43","0.7-0.7","3","ね","0","0","323","43","500","0","true","黑体","0"] -["323","43","0.8-0.8","3","ね","0","0","323","43","500","0","true","黑体","0"] -["323","43","0.9-0.9","3","ね","0","0","323","43","500","0","true","黑体","0"] -["323","43","1-1","3","ね","0","0","323","43","500","0","true","黑体","0"] -["540","300","1-1","4.5","♬","0","0","-145","300","4000","0","true","宋体","0"] -["454","298","0.1-0.1","3","詩","0","0","454","298","500","0","true","黑体","0"] -["454","298","0.2-0.2","3","詩","0","0","454","298","500","0","true","黑体","0"] -["454","298","0.3-0.3","3","詩","0","0","454","298","500","0","true","黑体","0"] -["454","298","0.4-0.4","3","詩","0","0","454","298","500","0","true","黑体","0"] -["454","298","0.5-0.5","3","詩","0","0","454","298","500","0","true","黑体","0"] -["454","298","0.6-0.6","3","詩","0","0","454","298","500","0","true","黑体","0"] -["454","298","0.7-0.7","3","詩","0","0","454","298","500","0","true","黑体","0"] -["454","298","0.8-0.8","3","詩","0","0","454","298","500","0","true","黑体","0"] -["454","298","0.9-0.9","3","詩","0","0","454","298","500","0","true","黑体","0"] -["454","298","1-1","3","詩","0","0","454","298","500","0","true","黑体","0"] -["364","298","0.1-0.1","3","う","0","0","364","298","500","0","true","黑体","0"] -["364","298","0.2-0.2","3","う","0","0","364","298","500","0","true","黑体","0"] -["364","298","0.3-0.3","3","う","0","0","364","298","500","0","true","黑体","0"] -["364","298","0.4-0.4","3","う","0","0","364","298","500","0","true","黑体","0"] -["364","298","0.5-0.5","3","う","0","0","364","298","500","0","true","黑体","0"] -["364","298","0.6-0.6","3","う","0","0","364","298","500","0","true","黑体","0"] -["364","298","0.7-0.7","3","う","0","0","364","298","500","0","true","黑体","0"] -["364","298","0.8-0.8","3","う","0","0","364","298","500","0","true","黑体","0"] -["364","298","0.9-0.9","3","う","0","0","364","298","500","0","true","黑体","0"] -["364","298","1-1","3","う","0","0","364","298","500","0","true","黑体","0"] -["274","298","0.1-0.1","3","少","0","0","274","298","500","0","true","黑体","0"] -["274","298","0.2-0.2","3","少","0","0","274","298","500","0","true","黑体","0"] -["274","298","0.3-0.3","3","少","0","0","274","298","500","0","true","黑体","0"] -["274","298","0.4-0.4","3","少","0","0","274","298","500","0","true","黑体","0"] -["274","298","0.5-0.5","3","少","0","0","274","298","500","0","true","黑体","0"] -["274","298","0.6-0.6","3","少","0","0","274","298","500","0","true","黑体","0"] -["274","298","0.7-0.7","3","少","0","0","274","298","500","0","true","黑体","0"] -["274","298","0.8-0.8","3","少","0","0","274","298","500","0","true","黑体","0"] -["274","298","0.9-0.9","3","少","0","0","274","298","500","0","true","黑体","0"] -["274","298","1-1","3","少","0","0","274","298","500","0","true","黑体","0"] -["184","298","0.1-0.1","3","女","0","0","184","298","500","0","true","黑体","0"] -["184","298","0.2-0.2","3","女","0","0","184","298","500","0","true","黑体","0"] -["184","298","0.3-0.3","3","女","0","0","184","298","500","0","true","黑体","0"] -["184","298","0.4-0.4","3","女","0","0","184","298","500","0","true","黑体","0"] -["184","298","0.5-0.5","3","女","0","0","184","298","500","0","true","黑体","0"] -["184","298","0.6-0.6","3","女","0","0","184","298","500","0","true","黑体","0"] -["184","298","0.7-0.7","3","女","0","0","184","298","500","0","true","黑体","0"] -["184","298","0.8-0.8","3","女","0","0","184","298","500","0","true","黑体","0"] -["184","298","0.9-0.9","3","女","0","0","184","298","500","0","true","黑体","0"] -["184","298","1-1","3","女","0","0","184","298","500","0","true","黑体","0"] -["343","130","1-1","0.6","時","0","0","343","130","500","0","false","黑体","1"] -["343","130","1-1","1.2"," の","0","0","343","130","500","0","false","黑体","1"] -["343","130","1-1","1.8","  木","0","0","343","130","500","0","false","黑体","1"] -["343","130","1-1","2.4","   枯","0","0","343","130","500","0","false","黑体","1"] -["343","130","1-0","2","時","0","0","343","220","2000","0","false","黑体","1"] -["343","130","1-0","2"," の","0","0","343","220","2000","0","false","黑体","1"] -["343","130","1-0","2","  木","0","0","343","220","2000","0","false","黑体","1"] -["343","130","1-0","2","   枯","0","0","343","220","2000","0","false","黑体","1"] -["343","130","1-0","2","時","0","0","343","225","2000","0","false","黑体","1"] -["343","130","1-0","2"," の","0","0","343","225","2000","0","false","黑体","1"] -["343","130","1-0","2","  木","0","0","343","225","2000","0","false","黑体","1"] -["343","130","1-0","2","   枯","0","0","343","225","2000","0","false","黑体","1"] -["343","130","1-0","2","時","0","0","343","230","2000","0","false","黑体","1"] -["343","130","1-0","2"," の","0","0","343","230","2000","0","false","黑体","1"] -["343","130","1-0","2","  木","0","0","343","230","2000","0","false","黑体","1"] -["343","130","1-0","2","   枯","0","0","343","230","2000","0","false","黑体","1"] -["343","130","1-0","2","時","0","0","343","235","2000","0","false","黑体","1"] -["343","130","1-0","2"," の","0","0","343","235","2000","0","false","黑体","1"] -["343","130","1-0","2","  木","0","0","343","235","2000","0","false","黑体","1"] -["343","130","1-0","2","   枯","0","0","343","235","2000","0","false","黑体","1"] -["343","130","1-0","2","時","0","0","343","240","2000","0","false","黑体","1"] -["343","130","1-0","2"," の","0","0","343","240","2000","0","false","黑体","1"] -["343","130","1-0","2","  木","0","0","343","240","2000","0","false","黑体","1"] -["343","130","1-0","2","   枯","0","0","343","240","2000","0","false","黑体","1"] -["343","130","1-0","2","時","0","0","343","245","2000","0","false","黑体","1"] -["343","130","1-0","2"," の","0","0","343","245","2000","0","false","黑体","1"] -["343","130","1-0","2","  木","0","0","343","245","2000","0","false","黑体","1"] -["343","130","1-0","2","   枯","0","0","343","245","2000","0","false","黑体","1"] -["343","130","1-0","2","時","0","0","343","250","2000","0","false","黑体","1"] -["343","130","1-0","2"," の","0","0","343","250","2000","0","false","黑体","1"] -["343","130","1-0","2","  木","0","0","343","250","2000","0","false","黑体","1"] -["343","130","1-0","2","   枯","0","0","343","250","2000","0","false","黑体","1"] -["343","130","1-0","2","時","0","0","343","255","2000","0","false","黑体","1"] -["343","130","1-0","2"," の","0","0","343","255","2000","0","false","黑体","1"] -["343","130","1-0","2","  木","0","0","343","255","2000","0","false","黑体","1"] -["343","130","1-0","2","   枯","0","0","343","255","2000","0","false","黑体","1"] -["145","76","1-1","4.5","◯","0","0","145","76","500","0","false","宋体","0"] -["198","89","1-1","4.5","12","0","0","198","89","500","0","false","宋体","0"] -["204","170","1-1","4.5","6","0","0","204","170","500","0","false","宋体","0"] -["243","127","1-1","4.5","3","0","0","243","127","500","0","false","宋体","0"] -["165","127","1-1","4.5","9","0","0","165","127","500","0","false","宋体","0"] -["204","124","1-1","4.5","。","0","0","204","124","500","0","false","宋体","0"] -["176","108","1-1","4.5","-","30","0","176","108","500","0","false","宋体","0"] -["197","94","1-1","4.5","-","60","0","197","94","500","0","false","宋体","0"] -["246","105","1-1","4.5","-","120","0","246","105","500","0","false","黑体","0"] -["257","125","1-1","4.5","-","150","0","257","125","500","0","false","黑体","0"] -["245","150","1-1","4.5","-","21","0","245","150","500","0","false","黑体","0"] -["250","176","1-1","4.5","-","210","0","250","176","500","0","false","黑体","0"] -["228","187","1-1","4.5","-","240","0","228","187","500","0","false","黑体","0"] -["175","177","1-1","4.5","-","300","0","175","177","500","0","false","黑体","0"] -["166","156","1-1","4.5","-","330","0","166","156","500","0","false","黑体","0"] -["177","122","1-1","4.5","←","0","0","177","122","500","0","true","黑体","0"] -["190","100","1-0","1","↑","0","0","190","100","500","0","true","黑体","0"] -["198","97","1-0","1","↑","10","0","198","97","500","0","true","黑体","0"] -["205","95","1-0","1","↑","20","0","205","95","500","0","true","黑体","0"] -["213","95","1-0","1","↑","30","0","213","95","500","0","true","黑体","0"] -["220","94","1-0","1","↑","40","0","220","94","500","0","true","黑体","0"] -["126","105","1-0","4.5","針","300","0","126","105","500","0","true","黑体","0"] -["155","70","1-0","4.5","は","330","0","155","70","500","0","true","黑体","0"] -["199","50","1-0","4.5","進","0","0","199","50","500","0","true","黑体","0"] -["246","60","1-0","4.5","ん","30","0","246","60","500","0","true","黑体","0"] -["280","83","1-0","4.5","だ","60","0","280","83","500","0","true","黑体","0"] -["62","333","0-1","2.2","景中是(C'est)——","0","0","62","333","500","0","true","黑体","0"] -["173","359","0-1","4","夜晚的窗边","0","0","173","359","500","0","true","黑体","0"] -["245","325","0-1","4.5","微笑的月亮","0","0","245","325","500","0","true","黑体","0"] -["206","351","0-1","4.5","秋之追忆","0","0","206","351","500","0","true","黑体","0"] -["85","340","0-1","4.5","美丽的音色","0","0","85","340","500","0","true","黑体","0"] -["136","362","0-1","4.5","       歌唱的少女(Monica)","0","0","136","362","500","0","true","黑体","0"] -["219","340","0-1","4.5","昆虫的振翅","0","0","219","340","500","0","true","黑体","0"] -["266","363","1-0","4.5","指针的前进 →","0","0","290","363","4500","0","true","黑体","0"] -["333","342","0-1","3","景中是(C'est)——","0","0","333","342","500","0","true","黑体","0"] -["145","360","0-1","2","包裹大地的","0","0","145","360","500","0","true","黑体","0"] -["145","360","1-0","2.5","包裹大地的","0","0","145","360","500","0","true","黑体","0"] -["145","360","0-1","2","      洁白的雪花","0","0","145","360","500","0","true","黑体","0"] -["145","360","1-0","2.5","      洁白的雪花","0","0","145","360","500","0","true","黑体","0"] -["211","338","0-1","2","冬之追忆","0","0","211","338","500","0","true","黑体","0"] -["211","338","1-0","2.5","冬之追忆","0","0","211","338","500","0","true","黑体","0"] -["76","343","0-1","4.5","美丽的音色","0","0","76","343","500","0","true","黑体","0"] -["300","357","0-1","4.5","歌吟的少女(Monica)","0","0","300","357","500","0","true","黑体","0"] -["175","336","1-0","4.5","时间的凋零…","0","0","175","386","4500","0","true","黑体","0"] -["304","340","1-0","4.5","指针的前进 →","0","0","354","340","4500","0","true","黑体","0"] -["318","228","1-1","4","綺麗だね","0","0","318","228","500","0","true","黑体","0"] -["298","228","0-1","2","綺","0","0","318","228","2000","0","false","黑体","0"] -["298","228","0-1","2"," 麗","0","0","318","228","2000","0","false","黑体","0"] -["298","228","0-1","2","  だ","0","0","318","228","2000","0","false","黑体","0"] -["298","228","0-1","2","   ね","0","0","318","228","2000","0","false","黑体","0"] -["338","228","0-1","2","綺","0","0","318","228","2000","0","false","黑体","0"] -["338","228","0-1","2"," 麗","0","0","318","228","2000","0","false","黑体","0"] -["338","228","0-1","2","  だ","0","0","318","228","2000","0","false","黑体","0"] -["338","228","0-1","2","   ね","0","0","318","228","2000","0","false","黑体","0"] -["318","228","1-1","2.5","綺","0","0","318","228","2500","0","false","黑体","0"] -["318","228","1-1","2.5"," 麗","0","0","318","228","2500","0","false","黑体","0"] -["318","228","1-1","2.5","  だ","0","0","318","228","2500","0","false","黑体","0"] -["318","228","1-1","2.5","   ね","0","0","318","228","2500","0","false","黑体","0"] -["1","111","1-1","11","██████████/n██████████/n██████████","0","320","1","111","500","0","false","MS UI Gothic","0"] -["1","111","1-1","11"," █████████/n █████████/n █████████","0","320","1","111","500","0","false","MS UI Gothic","0"] -["1","121","1-1","11","██████████/n██████████/n","0","320","1","121","500","0","false","MS UI Gothic","0"] -["1","121","1-1","11"," █████████/n █████████/n","0","320","1","121","500","0","false","MS UI Gothic","0"] -["540","127","0-1","3","君","0","310","8","127","3000","0","true","黑体","0"] -["540","127","0-1","3"," が","0","310","8","127","3000","0","true","黑体","0"] -["540","127","0-1","3","  生","0","310","8","127","3000","0","true","黑体","0"] -["540","127","0-1","3","   き","0","310","8","127","3000","0","true","黑体","0"] -["540","127","0-1","3","    た","0","310","8","127","3000","0","true","黑体","0"] -["540","127","0-1","3","     景","0","310","8","127","3000","0","true","黑体","0"] -["540","127","0-1","3","      色","0","310","8","127","3000","0","true","黑体","0"] -["8","127","1-1","8","君","0","310","8","127","3000","0","true","黑体","0"] -["8","127","1-1","7.5"," が","0","310","8","127","3000","0","true","黑体","0"] -["8","127","1-1","7","  生","0","310","8","127","3000","0","true","黑体","0"] -["8","127","1-1","6.5","   き","0","310","8","127","3000","0","true","黑体","0"] -["8","127","1-1","6","    た","0","310","8","127","3000","0","true","黑体","0"] -["8","127","1-1","5.5","     景","0","310","8","127","3000","0","true","黑体","0"] -["8","127","1-1","5","      色","0","310","8","127","3000","0","true","黑体","0"] -["540","127","0-1","3","/nず","0","310","8","127","3000","0","true","黑体","0"] -["540","127","0-1","3","/n っ","0","310","8","127","3000","0","true","黑体","0"] -["540","127","0-1","3","/n  と","0","310","8","127","3000","0","true","黑体","0"] -["540","127","0-1","3","/n   忘","0","310","8","127","3000","0","true","黑体","0"] -["540","127","0-1","3","/n    れ","0","310","8","127","3000","0","true","黑体","0"] -["540","127","0-1","3","/n     な","0","310","8","127","3000","0","true","黑体","0"] -["540","127","0-1","3","/n      い","0","310","8","127","3000","0","true","黑体","0"] -["8","127","1-1","3","/nず","0","310","8","127","3000","0","true","黑体","0"] -["8","127","1-1","2.7","/n っ","0","310","8","127","3000","0","true","黑体","0"] -["8","127","1-1","2.4","/n  と","0","310","8","127","3000","0","true","黑体","0"] -["8","127","1-1","2.1","/n   忘","0","310","8","127","3000","0","true","黑体","0"] -["8","127","1-1","1.8","/n    れ","0","310","8","127","3000","0","true","黑体","0"] -["8","127","1-1","1.5","/n     な","0","310","8","127","3000","0","true","黑体","0"] -["8","127","1-1","1.2","/n      い","0","310","8","127","3000","0","true","黑体","0"] -["300","280","0-1","2","『     』","0","0","300","280","500","0","true","黑体","0"] -["300","280","0-1","2"," 美","0","0","300","280","500","0","true","黑体","0"] -["300","280","0-1","2","  し","0","0","300","280","500","0","true","黑体","0"] -["300","280","0-1","2","   き","0","0","300","280","500","0","true","黑体","0"] -["300","280","0-1","2","    も","0","0","300","280","500","0","true","黑体","0"] -["300","280","0-1","2","     の","0","0","300","280","500","0","true","黑体","0"] -["300","280","1-1","2","『     』","0","0","300","280","500","0","true","黑体","0"] -["300","280","1-1","2"," 美","0","0","300","280","500","0","true","黑体","0"] -["300","280","1-1","2","  し","0","0","300","280","500","0","true","黑体","0"] -["300","280","1-1","2","   き","0","0","300","280","500","0","true","黑体","0"] -["300","280","1-1","2","    も","0","0","300","280","500","0","true","黑体","0"] -["300","280","1-1","2","     の","0","0","300","280","500","0","true","黑体","0"] -["93","118","0-1","2","集","0","0","253","236","2000","0","true","黑体","0"] -["124","50","0-1","2"," め","0","0","253","236","2000","0","true","黑体","0"] -["252","15","0-1","2","  る","0","0","253","236","2000","0","true","黑体","0"] -["334","50","0-1","2","   為","0","0","253","236","2000","0","true","黑体","0"] -["399","115","0-1","2","    に","0","0","253","236","2000","0","true","黑体","0"] -["253","236","1-0","3","集","0","0","253","236","2000","0","true","黑体","0"] -["253","236","1-0","3"," め","0","0","253","236","2000","0","true","黑体","0"] -["253","236","1-0","3","  る","0","0","253","236","2000","0","true","黑体","0"] -["253","236","1-0","3","   為","0","0","253","236","2000","0","true","黑体","0"] -["253","236","1-0","3","    に","0","0","253","236","2000","0","true","黑体","0"] -["200","130","1-1","1.1","生命(ひと)は過ぎて行く","0","0","200","130","500","0","true","黑体","0"] -["600","187","1-0","3.5","➡","180","0","-100","187","3500","0","false","宋体","0"] -["200","130","1-0","3.5","生命(ひと)は過ぎて行く","0","0","0","130","3500","0","true","黑体","0"] -["190","120","1-1","4.5","█████████████████","0","0","190","100","1000","0","false","MS UI Gothic","0"] -["190","120","1-1","4.5"," ████████████████","0","0","190","100","1000","0","false","MS UI Gothic","0"] -["190","140","1-1","4.5","█████████████████","0","0","190","160","1000","0","false","MS UI Gothic","0"] -["190","140","1-1","4.5"," ████████████████","0","0","190","160","1000","0","false","MS UI Gothic","0"] -["362","340","0-1","4.5","真美呢","0","0","362","340","500","0","true","黑体","0"] -["33","314","0-1","4.5","你生活过的景色","0","0","33","314","500","0","true","黑体","0"] -["59","338","0-1","4.5","永远不忘","0","0","59","338","500","0","true","黑体","0"] -["341","334","0-1","4.5","“美丽之物”","0","0","341","334","500","0","true","黑体","0"] -["332","359","0-1","4.5","为了将它采集","0","0","332","359","500","0","true","黑体","0"] -["233","337","0-1","1.1","生命(人)才在世上前行","0","0","233","337","500","0","true","黑体","0"] -["233","337","1-0","3.5","生命(人)才在世上前行","0","0","33","337","3500","0","true","黑体","0"] -["540","90","1-1","3","君","0","0","0","90","3000","0","false","黑体","0"] -["560","70","1-1","3","君","0","0","-20","70","3000","0","false","黑体","0"] -["580","50","1-1","3","君","0","0","-40","50","3000","0","false","黑体","0"] -["600","30","1-1","3","君","0","0","-60","30","3000","0","false","黑体","0"] -["540","150","1-1","3","が","0","0","0","150","3000","0","false","黑体","0"] -["560","138","1-1","3","が","0","0","-20","138","3000","0","false","黑体","0"] -["580","126","1-1","3","が","0","0","-40","126","3000","0","false","黑体","0"] -["600","114","1-1","3","が","0","0","-60","114","3000","0","false","黑体","0"] -["540","184","1-1","3","駈","0","0","0","184","3000","0","false","黑体","0"] -["560","180","1-1","3","駈","0","0","-20","180","3000","0","false","黑体","0"] -["580","176","1-1","3","駈","0","0","-40","176","3000","0","false","黑体","0"] -["600","172","1-1","3","駈","0","0","-60","172","3000","0","false","黑体","0"] -["540","240","1-1","3","け","0","0","0","240","3000","0","false","黑体","0"] -["560","246","1-1","3","け","0","0","-20","246","3000","0","false","黑体","0"] -["580","252","1-1","3","け","0","0","-40","252","3000","0","false","黑体","0"] -["600","258","1-1","3","け","0","0","-60","258","3000","0","false","黑体","0"] -["540","307","1-1","3","抜","0","0","0","307","3000","0","false","黑体","0"] -["560","325","1-1","3","抜","0","0","-20","325","3000","0","false","黑体","0"] -["580","345","1-1","3","抜","0","0","-40","345","3000","0","false","黑体","0"] -["600","365","1-1","3","抜","0","0","-60","365","3000","0","false","黑体","0"] -["540","270","1-1","3","け","0","0","0","270","3000","0","false","黑体","0"] -["560","285","1-1","3","け","0","0","-20","285","3000","0","false","黑体","0"] -["580","300","1-1","3","け","0","0","-40","300","3000","0","false","黑体","0"] -["600","315","1-1","3","け","0","0","-60","315","3000","0","false","黑体","0"] -["540","240","1-1","3","た","0","0","0","240","3000","0","false","黑体","0"] -["560","246","1-1","3","た","0","0","-20","246","3000","0","false","黑体","0"] -["580","252","1-1","3","た","0","0","-40","252","3000","0","false","黑体","0"] -["600","258","1-1","3","た","0","0","-60","258","3000","0","false","黑体","0"] -["540","184","1-1","3","昡","0","0","0","184","3000","0","false","黑体","0"] -["560","180","1-1","3","昡","0","0","-20","180","3000","0","false","黑体","0"] -["580","176","1-1","3","昡","0","0","-40","176","3000","0","false","黑体","0"] -["600","172","1-1","3","昡","0","0","-60","172","3000","0","false","黑体","0"] -["540","120","1-1","3","い","0","0","0","120","3000","0","false","黑体","0"] -["560","104","1-1","3","い","0","0","-20","104","3000","0","false","黑体","0"] -["580","88","1-1","3","い","0","0","-40","88","3000","0","false","黑体","0"] -["600","72","1-1","3","い","0","0","-60","72","3000","0","false","黑体","0"] -["540","213","1-1","3","季","0","0","0","213","3000","0","false","黑体","0"] -["560","217","1-1","3","季","0","0","-20","217","3000","0","false","黑体","0"] -["580","221","1-1","3","季","0","0","-40","221","3000","0","false","黑体","0"] -["600","225","1-1","3","季","0","0","-60","225","3000","0","false","黑体","0"] -["540","305","1-1","3","節","0","0","0","305","3000","0","false","黑体","0"] -["560","323","1-1","3","節","0","0","-20","323","3000","0","false","黑体","0"] -["580","343","1-1","3","節","0","0","-40","343","3000","0","false","黑体","0"] -["600","363","1-1","3","節","0","0","-60","363","3000","0","false","黑体","0"] -["171","287","0-1","3","病","0","0","171","287","500","0","true","黑体","0"] -["171","287","0-1","3"," の","0","0","171","287","500","0","true","黑体","0"] -["171","287","0-1","3","  焔","0","0","171","287","500","0","false","黑体","0"] -["171","287","0-1","3","   に","0","0","171","287","500","0","false","黑体","0"] -["171","287","0-1","3","    灼","0","0","171","287","500","0","false","黑体","0"] -["171","287","0-1","3","     か","0","0","171","287","500","0","false","黑体","0"] -["171","287","0-1","3","      れ","0","0","171","287","500","0","false","黑体","0"] -["171","287","0-1","3","       な","0","0","171","287","500","0","false","黑体","0"] -["171","287","1-1","3.6","病","0","0","171","287","500","0","true","黑体","0"] -["171","287","1-1","3"," の","0","0","171","287","500","0","true","黑体","0"] -["171","287","1-1","2.4","  焔","0","0","171","287","500","0","false","黑体","0"] -["171","287","1-1","1.6","   に","0","0","171","287","500","0","false","黑体","0"] -["171","287","1-1","1.2","    灼","0","0","171","287","500","0","false","黑体","0"] -["171","287","1-1","0.6","     か","0","0","171","287","500","0","false","黑体","0"] -["171","287","1-1","0.2","      れ","0","0","171","287","500","0","false","黑体","0"] -["73","286","1-0","0.5","♠","30","40","73","286","500","0","false","宋体","0"] -["173","286","1-0","0.5","♠","40","50","173","286","500","0","false","宋体","0"] -["341","282","1-0","0.5","♠","25","50","341","282","500","0","false","宋体","0"] -["306","289","1-0","0.5","♠","25","50","306","289","500","0","false","宋体","0"] -["503","285","1-0","0.5","♠","25","70","503","285","500","0","false","宋体","0"] -["446","274","1-0","0.5","♠","350","70","446","274","500","0","false","宋体","0"] -["422","292","1-0","0.5","♠","350","70","422","292","500","0","false","宋体","0"] -["368","297","1-0","0.5","♠","350","50","368","297","500","0","false","宋体","0"] -["372","271","1-0","0.5","♠","10","70","372","271","500","0","false","宋体","0"] -["235","295","1-0","0.5","♠","346","60","235","295","500","0","false","宋体","0"] -["192","299","1-0","0.5","♠","346","40","192","299","500","0","false","宋体","0"] -["181","277","1-0","0.5","♠","0","60","181","277","500","0","false","宋体","0"] -["129","289","1-0","0.5","♠","340","48","129","289","500","0","false","宋体","0"] -["101","290","1-0","0.5","♠","340","48","101","290","500","0","false","宋体","0"] -["58","309","1-0","0.5","♠","320","40","58","309","500","0","false","宋体","0"] -["2","299","1-0","0.5","♠","340","30","2","299","500","0","false","宋体","0"] -["41","287","1-0","0.5","♠","330","40","41","287","500","0","false","宋体","0"] -["34","314","1-0","0.5","♠","330","40","34","314","500","0","false","宋体","0"] -["39","344","1-0","0.5","♠","330","40","39","344","500","0","false","宋体","0"] -["57","335","1-0","0.5","♠","340","40","57","335","500","0","false","宋体","0"] -["84","331","1-0","0.5","♠","345","40","84","331","500","0","false","宋体","0"] -["126","332","1-0","0.5","♠","345","45","126","332","500","0","false","宋体","0"] -["149","335","1-0","0.5","♠","345","45","149","335","500","0","false","宋体","0"] -["180","331","1-0","0.5","♠","345","60","180","331","500","0","false","宋体","0"] -["213","330","1-0","0.5","♠","10","60","213","330","500","0","false","宋体","0"] -["221","339","1-0","0.5","♠","355","60","221","339","500","0","false","宋体","0"] -["245","333","1-0","0.5","♠","355","60","245","333","500","0","false","宋体","0"] -["294","336","1-0","0.5","♠","10","60","294","336","500","0","false","宋体","0"] -["324","337","1-0","0.5","♠","27","70","324","337","500","0","false","宋体","0"] -["353","334","1-0","0.5","♠","27","70","353","334","500","0","false","宋体","0"] -["370","333","1-0","0.5","♠","20","70","370","333","500","0","false","宋体","0"] -["393","329","1-0","0.5","♠","20","70","393","329","500","0","false","宋体","0"] -["393","344","1-0","0.5","♠","350","70","393","344","500","0","false","宋体","0"] -["441","340","1-0","0.5","♠","350","70","441","340","500","0","false","宋体","0"] -["460","325","1-0","0.5","♠","355","70","460","325","500","0","false","宋体","0"] -["484","327","1-0","0.5","♠","17","70","484","327","500","0","false","宋体","0"] -["0","317","1-0","0.5","♠","350","20","0","317","500","0","false","宋体","0"] -["52","309","1-0","0.5","♠","5","40","52","309","500","0","false","宋体","0"] -["97","303","1-0","0.5","♠","20","60","97","303","500","0","false","宋体","0"] -["116","313","1-0","0.5","♠","20","45","116","313","500","0","false","宋体","0"] -["133","324","1-0","0.5","♠","0","45","133","324","500","0","false","宋体","0"] -["176","306","1-0","0.5","♠","15","50","176","306","500","0","false","宋体","0"] -["209","316","1-0","0.5","♠","15","50","209","316","500","0","false","宋体","0"] -["237","312","1-0","0.5","♠","3","60","237","312","500","0","false","宋体","0"] -["509","304","1-0","0.5","♠","23","70","509","304","500","0","false","宋体","0"] -["472","299","1-0","0.5","♠","16","74","472","299","500","0","false","宋体","0"] -["432","298","1-0","0.5","♠","350","74","432","298","500","0","false","宋体","0"] -["400","305","1-0","0.5","♠","345","74","400","305","500","0","false","宋体","0"] -["343","308","1-0","0.5","♠","345","60","343","308","500","0","false","宋体","0"] -["376","309","1-0","0.5","♠","0","60","376","309","500","0","false","宋体","0"] -["315","308","1-0","0.5","♠","0","60","315","308","500","0","false","宋体","0"] -["265","300","1-0","0.5","♠","10","60","265","300","500","0","false","宋体","0"] -["288","286","1-0","0.5","♠","10","60","288","286","500","0","false","宋体","0"] -["502","354","1-0","0.5","♠","25","75","502","354","500","0","false","宋体","0"] -["482","352","1-0","0.5","♠","10","75","482","352","500","0","false","宋体","0"] -["465","356","1-0","0.5","♠","345","75","465","356","500","0","false","宋体","0"] -["397","350","1-0","0.5","♠","345","75","397","350","500","0","false","宋体","0"] -["421","350","1-0","0.5","♠","345","75","421","350","500","0","false","宋体","0"] -["376","357","1-0","0.5","♠","345","60","376","357","500","0","false","宋体","0"] -["323","354","1-0","0.5","♠","18","60","323","354","500","0","false","宋体","0"] -["293","338","1-0","0.5","♠","2","60","293","338","500","0","false","宋体","0"] -["276","348","1-0","0.5","♠","2","60","276","348","500","0","false","宋体","0"] -["254","362","1-0","0.5","♠","350","60","254","362","500","0","false","宋体","0"] -["209","357","1-0","0.5","♠","14","60","209","357","500","0","false","宋体","0"] -["176","347","1-0","0.5","♠","6","60","176","347","500","0","false","宋体","0"] -["150","360","1-0","0.5","♠","350","60","150","360","500","0","false","宋体","0"] -["24","352","1-0","0.5","♠","350","30","24","352","500","0","false","宋体","0"] -["55","352","1-0","0.5","♠","4","45","55","352","500","0","false","宋体","0"] -["74","356","1-0","0.5","♠","6","50","74","356","500","0","false","宋体","0"] -["112","355","1-0","0.5","♠","20","50","112","355","500","0","false","宋体","0"] -["485","279","1-0","0.5","♠","17","70","485","279","500","0","false","宋体","0"] -["453","291","1-0","0.5","♠","0","70","453","291","500","0","false","宋体","0"] -["418","292","1-0","0.5","♠","350","70","418","292","500","0","false","宋体","0"] -["382","303","1-0","0.5","♠","330","60","382","303","500","0","false","宋体","0"] -["363","320","1-0","0.5","♠","330","60","363","320","500","0","false","宋体","0"] -["318","318","1-0","0.5","♠","330","50","318","318","500","0","false","宋体","0"] -["296","300","1-0","0.5","♠","354","50","296","300","500","0","false","宋体","0"] -["247","318","1-0","0.5","♠","330","50","247","318","500","0","false","宋体","0"] -["213","325","1-0","0.5","♠","318","50","213","325","500","0","false","宋体","0"] -["198","305","1-0","0.5","♠","350","50","198","305","500","0","false","宋体","0"] -["179","299","1-0","0.5","♠","355","45","179","299","500","0","false","宋体","0"] -["163","297","1-0","0.5","♠","355","55","163","297","500","0","false","宋体","0"] -["110","311","1-0","0.5","♠","330","48","110","311","500","0","false","宋体","0"] -["74","326","1-0","0.5","♠","320","48","74","326","500","0","false","宋体","0"] -["21","320","1-0","0.5","♠","320","30","21","320","500","0","false","宋体","0"] -["0","334","1-0","0.5","♠","330","30","0","334","500","0","false","宋体","0"] -["0","298","1-0","0.5","♠","350","30","0","298","500","0","false","宋体","0"] -["31","342","1-0","0.5","♠","350","30","31","342","500","0","false","宋体","0"] -["70","351","1-0","0.5","♠","330","30","70","351","500","0","false","宋体","0"] -["104","351","1-0","0.5","♠","330","45","104","351","500","0","false","宋体","0"] -["147","351","1-0","0.5","♠","330","50","147","351","500","0","false","宋体","0"] -["178","346","1-0","0.5","♠","350","50","178","346","500","0","false","宋体","0"] -["194","342","1-0","0.5","♠","5","50","194","342","500","0","false","宋体","0"] -["214","339","1-0","0.5","♠","5","50","214","339","500","0","false","宋体","0"] -["253","348","1-0","0.5","♠","340","60","253","348","500","0","false","宋体","0"] -["274","347","1-0","0.5","♠","340","60","274","347","500","0","false","宋体","0"] -["320","336","1-0","0.5","♠","0","60","320","336","500","0","false","宋体","0"] -["366","344","1-0","0.5","♠","350","60","366","344","500","0","false","宋体","0"] -["393","349","1-0","0.5","♠","345","60","393","349","500","0","false","宋体","0"] -["424","344","1-0","0.5","♠","340","60","424","344","500","0","false","宋体","0"] -["447","342","1-0","0.5","♠","346","60","447","342","500","0","false","宋体","0"] -["484","339","1-0","0.5","♠","12","70","484","339","500","0","false","宋体","0"] -["73","286","1-0","0.5","♠","30","40","73","286","500","0","false","宋体","0"] -["173","286","1-0","0.5","♠","40","50","173","286","500","0","false","宋体","0"] -["341","282","1-0","0.5","♠","25","50","341","282","500","0","false","宋体","0"] -["306","289","1-0","0.5","♠","25","50","306","289","500","0","false","宋体","0"] -["503","285","1-0","0.5","♠","25","70","503","285","500","0","false","宋体","0"] -["446","274","1-0","0.5","♠","350","70","446","274","500","0","false","宋体","0"] -["422","292","1-0","0.5","♠","350","70","422","292","500","0","false","宋体","0"] -["368","297","1-0","0.5","♠","350","50","368","297","500","0","false","宋体","0"] -["372","271","1-0","0.5","♠","10","70","372","271","500","0","false","宋体","0"] -["235","295","1-0","0.5","♠","346","60","235","295","500","0","false","宋体","0"] -["192","299","1-0","0.5","♠","346","40","192","299","500","0","false","宋体","0"] -["181","277","1-0","0.5","♠","0","60","181","277","500","0","false","宋体","0"] -["129","289","1-0","0.5","♠","340","48","129","289","500","0","false","宋体","0"] -["101","290","1-0","0.5","♠","340","48","101","290","500","0","false","宋体","0"] -["58","309","1-0","0.5","♠","320","40","58","309","500","0","false","宋体","0"] -["2","299","1-0","0.5","♠","340","30","2","299","500","0","false","宋体","0"] -["41","287","1-0","0.5","♠","330","40","41","287","500","0","false","宋体","0"] -["34","314","1-0","0.5","♠","330","40","34","314","500","0","false","宋体","0"] -["39","344","1-0","0.5","♠","330","40","39","344","500","0","false","宋体","0"] -["57","335","1-0","0.5","♠","340","40","57","335","500","0","false","宋体","0"] -["84","331","1-0","0.5","♠","345","40","84","331","500","0","false","宋体","0"] -["126","332","1-0","0.5","♠","345","45","126","332","500","0","false","宋体","0"] -["149","335","1-0","0.5","♠","345","45","149","335","500","0","false","宋体","0"] -["180","331","1-0","0.5","♠","345","60","180","331","500","0","false","宋体","0"] -["213","330","1-0","0.5","♠","10","60","213","330","500","0","false","宋体","0"] -["221","339","1-0","0.5","♠","355","60","221","339","500","0","false","宋体","0"] -["245","333","1-0","0.5","♠","355","60","245","333","500","0","false","宋体","0"] -["294","336","1-0","0.5","♠","10","60","294","336","500","0","false","宋体","0"] -["324","337","1-0","0.5","♠","27","70","324","337","500","0","false","宋体","0"] -["353","334","1-0","0.5","♠","27","70","353","334","500","0","false","宋体","0"] -["370","333","1-0","0.5","♠","20","70","370","333","500","0","false","宋体","0"] -["393","329","1-0","0.5","♠","20","70","393","329","500","0","false","宋体","0"] -["393","344","1-0","0.5","♠","350","70","393","344","500","0","false","宋体","0"] -["441","340","1-0","0.5","♠","350","70","441","340","500","0","false","宋体","0"] -["460","325","1-0","0.5","♠","355","70","460","325","500","0","false","宋体","0"] -["484","327","1-0","0.5","♠","17","70","484","327","500","0","false","宋体","0"] -["0","317","1-0","0.5","♠","350","20","0","317","500","0","false","宋体","0"] -["52","309","1-0","0.5","♠","5","40","52","309","500","0","false","宋体","0"] -["97","303","1-0","0.5","♠","20","60","97","303","500","0","false","宋体","0"] -["116","313","1-0","0.5","♠","20","45","116","313","500","0","false","宋体","0"] -["133","324","1-0","0.5","♠","0","45","133","324","500","0","false","宋体","0"] -["176","306","1-0","0.5","♠","15","50","176","306","500","0","false","宋体","0"] -["209","316","1-0","0.5","♠","15","50","209","316","500","0","false","宋体","0"] -["237","312","1-0","0.5","♠","3","60","237","312","500","0","false","宋体","0"] -["509","304","1-0","0.5","♠","23","70","509","304","500","0","false","宋体","0"] -["472","299","1-0","0.5","♠","16","74","472","299","500","0","false","宋体","0"] -["432","298","1-0","0.5","♠","350","74","432","298","500","0","false","宋体","0"] -["400","305","1-0","0.5","♠","345","74","400","305","500","0","false","宋体","0"] -["343","308","1-0","0.5","♠","345","60","343","308","500","0","false","宋体","0"] -["376","309","1-0","0.5","♠","0","60","376","309","500","0","false","宋体","0"] -["315","308","1-0","0.5","♠","0","60","315","308","500","0","false","宋体","0"] -["265","300","1-0","0.5","♠","10","60","265","300","500","0","false","宋体","0"] -["288","286","1-0","0.5","♠","10","60","288","286","500","0","false","宋体","0"] -["502","354","1-0","0.5","♠","25","75","502","354","500","0","false","宋体","0"] -["482","352","1-0","0.5","♠","10","75","482","352","500","0","false","宋体","0"] -["465","356","1-0","0.5","♠","345","75","465","356","500","0","false","宋体","0"] -["397","350","1-0","0.5","♠","345","75","397","350","500","0","false","宋体","0"] -["421","350","1-0","0.5","♠","345","75","421","350","500","0","false","宋体","0"] -["376","357","1-0","0.5","♠","345","60","376","357","500","0","false","宋体","0"] -["323","354","1-0","0.5","♠","18","60","323","354","500","0","false","宋体","0"] -["293","338","1-0","0.5","♠","2","60","293","338","500","0","false","宋体","0"] -["276","348","1-0","0.5","♠","2","60","276","348","500","0","false","宋体","0"] -["254","362","1-0","0.5","♠","350","60","254","362","500","0","false","宋体","0"] -["209","357","1-0","0.5","♠","14","60","209","357","500","0","false","宋体","0"] -["176","347","1-0","0.5","♠","6","60","176","347","500","0","false","宋体","0"] -["150","360","1-0","0.5","♠","350","60","150","360","500","0","false","宋体","0"] -["24","352","1-0","0.5","♠","350","30","24","352","500","0","false","宋体","0"] -["55","352","1-0","0.5","♠","4","45","55","352","500","0","false","宋体","0"] -["74","356","1-0","0.5","♠","6","50","74","356","500","0","false","宋体","0"] -["112","355","1-0","0.5","♠","20","50","112","355","500","0","false","宋体","0"] -["485","279","1-0","0.5","♠","17","70","485","279","500","0","false","宋体","0"] -["453","291","1-0","0.5","♠","0","70","453","291","500","0","false","宋体","0"] -["418","292","1-0","0.5","♠","350","70","418","292","500","0","false","宋体","0"] -["382","303","1-0","0.5","♠","330","60","382","303","500","0","false","宋体","0"] -["363","320","1-0","0.5","♠","330","60","363","320","500","0","false","宋体","0"] -["318","318","1-0","0.5","♠","330","50","318","318","500","0","false","宋体","0"] -["296","300","1-0","0.5","♠","354","50","296","300","500","0","false","宋体","0"] -["247","318","1-0","0.5","♠","330","50","247","318","500","0","false","宋体","0"] -["213","325","1-0","0.5","♠","318","50","213","325","500","0","false","宋体","0"] -["198","305","1-0","0.5","♠","350","50","198","305","500","0","false","宋体","0"] -["179","299","1-0","0.5","♠","355","45","179","299","500","0","false","宋体","0"] -["163","297","1-0","0.5","♠","355","55","163","297","500","0","false","宋体","0"] -["110","311","1-0","0.5","♠","330","48","110","311","500","0","false","宋体","0"] -["74","326","1-0","0.5","♠","320","48","74","326","500","0","false","宋体","0"] -["21","320","1-0","0.5","♠","320","30","21","320","500","0","false","宋体","0"] -["0","334","1-0","0.5","♠","330","30","0","334","500","0","false","宋体","0"] -["0","298","1-0","0.5","♠","350","30","0","298","500","0","false","宋体","0"] -["31","342","1-0","0.5","♠","350","30","31","342","500","0","false","宋体","0"] -["70","351","1-0","0.5","♠","330","30","70","351","500","0","false","宋体","0"] -["104","351","1-0","0.5","♠","330","45","104","351","500","0","false","宋体","0"] -["147","351","1-0","0.5","♠","330","50","147","351","500","0","false","宋体","0"] -["178","346","1-0","0.5","♠","350","50","178","346","500","0","false","宋体","0"] -["194","342","1-0","0.5","♠","5","50","194","342","500","0","false","宋体","0"] -["214","339","1-0","0.5","♠","5","50","214","339","500","0","false","宋体","0"] -["253","348","1-0","0.5","♠","340","60","253","348","500","0","false","宋体","0"] -["274","347","1-0","0.5","♠","340","60","274","347","500","0","false","宋体","0"] -["320","336","1-0","0.5","♠","0","60","320","336","500","0","false","宋体","0"] -["366","344","1-0","0.5","♠","350","60","366","344","500","0","false","宋体","0"] -["393","349","1-0","0.5","♠","345","60","393","349","500","0","false","宋体","0"] -["424","344","1-0","0.5","♠","340","60","424","344","500","0","false","宋体","0"] -["447","342","1-0","0.5","♠","346","60","447","342","500","0","false","宋体","0"] -["484","339","1-0","0.5","♠","12","70","484","339","500","0","false","宋体","0"] -["73","286","1-0","0.5","♠","30","40","73","286","500","0","false","宋体","0"] -["173","286","1-0","0.5","♠","40","50","173","286","500","0","false","宋体","0"] -["341","282","1-0","0.5","♠","25","50","341","282","500","0","false","宋体","0"] -["306","289","1-0","0.5","♠","25","50","306","289","500","0","false","宋体","0"] -["503","285","1-0","0.5","♠","25","70","503","285","500","0","false","宋体","0"] -["446","274","1-0","0.5","♠","350","70","446","274","500","0","false","宋体","0"] -["422","292","1-0","0.5","♠","350","70","422","292","500","0","false","宋体","0"] -["368","297","1-0","0.5","♠","350","50","368","297","500","0","false","宋体","0"] -["372","271","1-0","0.5","♠","10","70","372","271","500","0","false","宋体","0"] -["235","295","1-0","0.5","♠","346","60","235","295","500","0","false","宋体","0"] -["192","299","1-0","0.5","♠","346","40","192","299","500","0","false","宋体","0"] -["181","277","1-0","0.5","♠","0","60","181","277","500","0","false","宋体","0"] -["129","289","1-0","0.5","♠","340","48","129","289","500","0","false","宋体","0"] -["101","290","1-0","0.5","♠","340","48","101","290","500","0","false","宋体","0"] -["58","309","1-0","0.5","♠","320","40","58","309","500","0","false","宋体","0"] -["2","299","1-0","0.5","♠","340","30","2","299","500","0","false","宋体","0"] -["41","287","1-0","0.5","♠","330","40","41","287","500","0","false","宋体","0"] -["34","314","1-0","0.5","♠","330","40","34","314","500","0","false","宋体","0"] -["39","344","1-0","0.5","♠","330","40","39","344","500","0","false","宋体","0"] -["57","335","1-0","0.5","♠","340","40","57","335","500","0","false","宋体","0"] -["84","331","1-0","0.5","♠","345","40","84","331","500","0","false","宋体","0"] -["126","332","1-0","0.5","♠","345","45","126","332","500","0","false","宋体","0"] -["149","335","1-0","0.5","♠","345","45","149","335","500","0","false","宋体","0"] -["180","331","1-0","0.5","♠","345","60","180","331","500","0","false","宋体","0"] -["213","330","1-0","0.5","♠","10","60","213","330","500","0","false","宋体","0"] -["221","339","1-0","0.5","♠","355","60","221","339","500","0","false","宋体","0"] -["245","333","1-0","0.5","♠","355","60","245","333","500","0","false","宋体","0"] -["294","336","1-0","0.5","♠","10","60","294","336","500","0","false","宋体","0"] -["324","337","1-0","0.5","♠","27","70","324","337","500","0","false","宋体","0"] -["353","334","1-0","0.5","♠","27","70","353","334","500","0","false","宋体","0"] -["370","333","1-0","0.5","♠","20","70","370","333","500","0","false","宋体","0"] -["393","329","1-0","0.5","♠","20","70","393","329","500","0","false","宋体","0"] -["393","344","1-0","0.5","♠","350","70","393","344","500","0","false","宋体","0"] -["441","340","1-0","0.5","♠","350","70","441","340","500","0","false","宋体","0"] -["460","325","1-0","0.5","♠","355","70","460","325","500","0","false","宋体","0"] -["484","327","1-0","0.5","♠","17","70","484","327","500","0","false","宋体","0"] -["0","317","1-0","0.5","♠","350","20","0","317","500","0","false","宋体","0"] -["52","309","1-0","0.5","♠","5","40","52","309","500","0","false","宋体","0"] -["97","303","1-0","0.5","♠","20","60","97","303","500","0","false","宋体","0"] -["116","313","1-0","0.5","♠","20","45","116","313","500","0","false","宋体","0"] -["133","324","1-0","0.5","♠","0","45","133","324","500","0","false","宋体","0"] -["176","306","1-0","0.5","♠","15","50","176","306","500","0","false","宋体","0"] -["209","316","1-0","0.5","♠","15","50","209","316","500","0","false","宋体","0"] -["237","312","1-0","0.5","♠","3","60","237","312","500","0","false","宋体","0"] -["509","304","1-0","0.5","♠","23","70","509","304","500","0","false","宋体","0"] -["472","299","1-0","0.5","♠","16","74","472","299","500","0","false","宋体","0"] -["432","298","1-0","0.5","♠","350","74","432","298","500","0","false","宋体","0"] -["400","305","1-0","0.5","♠","345","74","400","305","500","0","false","宋体","0"] -["343","308","1-0","0.5","♠","345","60","343","308","500","0","false","宋体","0"] -["376","309","1-0","0.5","♠","0","60","376","309","500","0","false","宋体","0"] -["315","308","1-0","0.5","♠","0","60","315","308","500","0","false","宋体","0"] -["265","300","1-0","0.5","♠","10","60","265","300","500","0","false","宋体","0"] -["288","286","1-0","0.5","♠","10","60","288","286","500","0","false","宋体","0"] -["502","354","1-0","0.5","♠","25","75","502","354","500","0","false","宋体","0"] -["482","352","1-0","0.5","♠","10","75","482","352","500","0","false","宋体","0"] -["465","356","1-0","0.5","♠","345","75","465","356","500","0","false","宋体","0"] -["397","350","1-0","0.5","♠","345","75","397","350","500","0","false","宋体","0"] -["421","350","1-0","0.5","♠","345","75","421","350","500","0","false","宋体","0"] -["376","357","1-0","0.5","♠","345","60","376","357","500","0","false","宋体","0"] -["323","354","1-0","0.5","♠","18","60","323","354","500","0","false","宋体","0"] -["293","338","1-0","0.5","♠","2","60","293","338","500","0","false","宋体","0"] -["276","348","1-0","0.5","♠","2","60","276","348","500","0","false","宋体","0"] -["254","362","1-0","0.5","♠","350","60","254","362","500","0","false","宋体","0"] -["209","357","1-0","0.5","♠","14","60","209","357","500","0","false","宋体","0"] -["176","347","1-0","0.5","♠","6","60","176","347","500","0","false","宋体","0"] -["150","360","1-0","0.5","♠","350","60","150","360","500","0","false","宋体","0"] -["24","352","1-0","0.5","♠","350","30","24","352","500","0","false","宋体","0"] -["55","352","1-0","0.5","♠","4","45","55","352","500","0","false","宋体","0"] -["74","356","1-0","0.5","♠","6","50","74","356","500","0","false","宋体","0"] -["112","355","1-0","0.5","♠","20","50","112","355","500","0","false","宋体","0"] -["485","279","1-0","0.5","♠","17","70","485","279","500","0","false","宋体","0"] -["453","291","1-0","0.5","♠","0","70","453","291","500","0","false","宋体","0"] -["418","292","1-0","0.5","♠","350","70","418","292","500","0","false","宋体","0"] -["382","303","1-0","0.5","♠","330","60","382","303","500","0","false","宋体","0"] -["363","320","1-0","0.5","♠","330","60","363","320","500","0","false","宋体","0"] -["318","318","1-0","0.5","♠","330","50","318","318","500","0","false","宋体","0"] -["296","300","1-0","0.5","♠","354","50","296","300","500","0","false","宋体","0"] -["247","318","1-0","0.5","♠","330","50","247","318","500","0","false","宋体","0"] -["213","325","1-0","0.5","♠","318","50","213","325","500","0","false","宋体","0"] -["198","305","1-0","0.5","♠","350","50","198","305","500","0","false","宋体","0"] -["179","299","1-0","0.5","♠","355","45","179","299","500","0","false","宋体","0"] -["163","297","1-0","0.5","♠","355","55","163","297","500","0","false","宋体","0"] -["110","311","1-0","0.5","♠","330","48","110","311","500","0","false","宋体","0"] -["74","326","1-0","0.5","♠","320","48","74","326","500","0","false","宋体","0"] -["21","320","1-0","0.5","♠","320","30","21","320","500","0","false","宋体","0"] -["0","334","1-0","0.5","♠","330","30","0","334","500","0","false","宋体","0"] -["0","298","1-0","0.5","♠","350","30","0","298","500","0","false","宋体","0"] -["31","342","1-0","0.5","♠","350","30","31","342","500","0","false","宋体","0"] -["70","351","1-0","0.5","♠","330","30","70","351","500","0","false","宋体","0"] -["104","351","1-0","0.5","♠","330","45","104","351","500","0","false","宋体","0"] -["147","351","1-0","0.5","♠","330","50","147","351","500","0","false","宋体","0"] -["178","346","1-0","0.5","♠","350","50","178","346","500","0","false","宋体","0"] -["194","342","1-0","0.5","♠","5","50","194","342","500","0","false","宋体","0"] -["214","339","1-0","0.5","♠","5","50","214","339","500","0","false","宋体","0"] -["253","348","1-0","0.5","♠","340","60","253","348","500","0","false","宋体","0"] -["274","347","1-0","0.5","♠","340","60","274","347","500","0","false","宋体","0"] -["320","336","1-0","0.5","♠","0","60","320","336","500","0","false","宋体","0"] -["366","344","1-0","0.5","♠","350","60","366","344","500","0","false","宋体","0"] -["393","349","1-0","0.5","♠","345","60","393","349","500","0","false","宋体","0"] -["424","344","1-0","0.5","♠","340","60","424","344","500","0","false","宋体","0"] -["447","342","1-0","0.5","♠","346","60","447","342","500","0","false","宋体","0"] -["484","339","1-0","0.5","♠","12","70","484","339","500","0","false","宋体","0"] -["73","286","1-0","0.5","♠","30","40","73","286","500","0","false","宋体","0"] -["173","286","1-0","0.5","♠","40","50","173","286","500","0","false","宋体","0"] -["341","282","1-0","0.5","♠","25","50","341","282","500","0","false","宋体","0"] -["306","289","1-0","0.5","♠","25","50","306","289","500","0","false","宋体","0"] -["503","285","1-0","0.5","♠","25","70","503","285","500","0","false","宋体","0"] -["446","274","1-0","0.5","♠","350","70","446","274","500","0","false","宋体","0"] -["422","292","1-0","0.5","♠","350","70","422","292","500","0","false","宋体","0"] -["368","297","1-0","0.5","♠","350","50","368","297","500","0","false","宋体","0"] -["372","271","1-0","0.5","♠","10","70","372","271","500","0","false","宋体","0"] -["235","295","1-0","0.5","♠","346","60","235","295","500","0","false","宋体","0"] -["192","299","1-0","0.5","♠","346","40","192","299","500","0","false","宋体","0"] -["181","277","1-0","0.5","♠","0","60","181","277","500","0","false","宋体","0"] -["129","289","1-0","0.5","♠","340","48","129","289","500","0","false","宋体","0"] -["101","290","1-0","0.5","♠","340","48","101","290","500","0","false","宋体","0"] -["58","309","1-0","0.5","♠","320","40","58","309","500","0","false","宋体","0"] -["2","299","1-0","0.5","♠","340","30","2","299","500","0","false","宋体","0"] -["41","287","1-0","0.5","♠","330","40","41","287","500","0","false","宋体","0"] -["34","314","1-0","0.5","♠","330","40","34","314","500","0","false","宋体","0"] -["39","344","1-0","0.5","♠","330","40","39","344","500","0","false","宋体","0"] -["57","335","1-0","0.5","♠","340","40","57","335","500","0","false","宋体","0"] -["84","331","1-0","0.5","♠","345","40","84","331","500","0","false","宋体","0"] -["126","332","1-0","0.5","♠","345","45","126","332","500","0","false","宋体","0"] -["149","335","1-0","0.5","♠","345","45","149","335","500","0","false","宋体","0"] -["180","331","1-0","0.5","♠","345","60","180","331","500","0","false","宋体","0"] -["213","330","1-0","0.5","♠","10","60","213","330","500","0","false","宋体","0"] -["221","339","1-0","0.5","♠","355","60","221","339","500","0","false","宋体","0"] -["245","333","1-0","0.5","♠","355","60","245","333","500","0","false","宋体","0"] -["294","336","1-0","0.5","♠","10","60","294","336","500","0","false","宋体","0"] -["324","337","1-0","0.5","♠","27","70","324","337","500","0","false","宋体","0"] -["353","334","1-0","0.5","♠","27","70","353","334","500","0","false","宋体","0"] -["370","333","1-0","0.5","♠","20","70","370","333","500","0","false","宋体","0"] -["393","329","1-0","0.5","♠","20","70","393","329","500","0","false","宋体","0"] -["393","344","1-0","0.5","♠","350","70","393","344","500","0","false","宋体","0"] -["441","340","1-0","0.5","♠","350","70","441","340","500","0","false","宋体","0"] -["460","325","1-0","0.5","♠","355","70","460","325","500","0","false","宋体","0"] -["484","327","1-0","0.5","♠","17","70","484","327","500","0","false","宋体","0"] -["0","317","1-0","0.5","♠","350","20","0","317","500","0","false","宋体","0"] -["52","309","1-0","0.5","♠","5","40","52","309","500","0","false","宋体","0"] -["97","303","1-0","0.5","♠","20","60","97","303","500","0","false","宋体","0"] -["116","313","1-0","0.5","♠","20","45","116","313","500","0","false","宋体","0"] -["133","324","1-0","0.5","♠","0","45","133","324","500","0","false","宋体","0"] -["176","306","1-0","0.5","♠","15","50","176","306","500","0","false","宋体","0"] -["209","316","1-0","0.5","♠","15","50","209","316","500","0","false","宋体","0"] -["237","312","1-0","0.5","♠","3","60","237","312","500","0","false","宋体","0"] -["509","304","1-0","0.5","♠","23","70","509","304","500","0","false","宋体","0"] -["472","299","1-0","0.5","♠","16","74","472","299","500","0","false","宋体","0"] -["432","298","1-0","0.5","♠","350","74","432","298","500","0","false","宋体","0"] -["400","305","1-0","0.5","♠","345","74","400","305","500","0","false","宋体","0"] -["343","308","1-0","0.5","♠","345","60","343","308","500","0","false","宋体","0"] -["376","309","1-0","0.5","♠","0","60","376","309","500","0","false","宋体","0"] -["315","308","1-0","0.5","♠","0","60","315","308","500","0","false","宋体","0"] -["265","300","1-0","0.5","♠","10","60","265","300","500","0","false","宋体","0"] -["288","286","1-0","0.5","♠","10","60","288","286","500","0","false","宋体","0"] -["502","354","1-0","0.5","♠","25","75","502","354","500","0","false","宋体","0"] -["482","352","1-0","0.5","♠","10","75","482","352","500","0","false","宋体","0"] -["465","356","1-0","0.5","♠","345","75","465","356","500","0","false","宋体","0"] -["397","350","1-0","0.5","♠","345","75","397","350","500","0","false","宋体","0"] -["421","350","1-0","0.5","♠","345","75","421","350","500","0","false","宋体","0"] -["376","357","1-0","0.5","♠","345","60","376","357","500","0","false","宋体","0"] -["323","354","1-0","0.5","♠","18","60","323","354","500","0","false","宋体","0"] -["293","338","1-0","0.5","♠","2","60","293","338","500","0","false","宋体","0"] -["276","348","1-0","0.5","♠","2","60","276","348","500","0","false","宋体","0"] -["254","362","1-0","0.5","♠","350","60","254","362","500","0","false","宋体","0"] -["209","357","1-0","0.5","♠","14","60","209","357","500","0","false","宋体","0"] -["176","347","1-0","0.5","♠","6","60","176","347","500","0","false","宋体","0"] -["150","360","1-0","0.5","♠","350","60","150","360","500","0","false","宋体","0"] -["24","352","1-0","0.5","♠","350","30","24","352","500","0","false","宋体","0"] -["55","352","1-0","0.5","♠","4","45","55","352","500","0","false","宋体","0"] -["74","356","1-0","0.5","♠","6","50","74","356","500","0","false","宋体","0"] -["112","355","1-0","0.5","♠","20","50","112","355","500","0","false","宋体","0"] -["485","279","1-0","0.5","♠","17","70","485","279","500","0","false","宋体","0"] -["453","291","1-0","0.5","♠","0","70","453","291","500","0","false","宋体","0"] -["418","292","1-0","0.5","♠","350","70","418","292","500","0","false","宋体","0"] -["382","303","1-0","0.5","♠","330","60","382","303","500","0","false","宋体","0"] -["363","320","1-0","0.5","♠","330","60","363","320","500","0","false","宋体","0"] -["318","318","1-0","0.5","♠","330","50","318","318","500","0","false","宋体","0"] -["296","300","1-0","0.5","♠","354","50","296","300","500","0","false","宋体","0"] -["247","318","1-0","0.5","♠","330","50","247","318","500","0","false","宋体","0"] -["213","325","1-0","0.5","♠","318","50","213","325","500","0","false","宋体","0"] -["198","305","1-0","0.5","♠","350","50","198","305","500","0","false","宋体","0"] -["179","299","1-0","0.5","♠","355","45","179","299","500","0","false","宋体","0"] -["163","297","1-0","0.5","♠","355","55","163","297","500","0","false","宋体","0"] -["110","311","1-0","0.5","♠","330","48","110","311","500","0","false","宋体","0"] -["74","326","1-0","0.5","♠","320","48","74","326","500","0","false","宋体","0"] -["21","320","1-0","0.5","♠","320","30","21","320","500","0","false","宋体","0"] -["0","334","1-0","0.5","♠","330","30","0","334","500","0","false","宋体","0"] -["0","298","1-0","0.5","♠","350","30","0","298","500","0","false","宋体","0"] -["31","342","1-0","0.5","♠","350","30","31","342","500","0","false","宋体","0"] -["70","351","1-0","0.5","♠","330","30","70","351","500","0","false","宋体","0"] -["104","351","1-0","0.5","♠","330","45","104","351","500","0","false","宋体","0"] -["147","351","1-0","0.5","♠","330","50","147","351","500","0","false","宋体","0"] -["178","346","1-0","0.5","♠","350","50","178","346","500","0","false","宋体","0"] -["194","342","1-0","0.5","♠","5","50","194","342","500","0","false","宋体","0"] -["214","339","1-0","0.5","♠","5","50","214","339","500","0","false","宋体","0"] -["253","348","1-0","0.5","♠","340","60","253","348","500","0","false","宋体","0"] -["274","347","1-0","0.5","♠","340","60","274","347","500","0","false","宋体","0"] -["320","336","1-0","0.5","♠","0","60","320","336","500","0","false","宋体","0"] -["366","344","1-0","0.5","♠","350","60","366","344","500","0","false","宋体","0"] -["393","349","1-0","0.5","♠","345","60","393","349","500","0","false","宋体","0"] -["424","344","1-0","0.5","♠","340","60","424","344","500","0","false","宋体","0"] -["447","342","1-0","0.5","♠","346","60","447","342","500","0","false","宋体","0"] -["484","339","1-0","0.5","♠","12","70","484","339","500","0","false","宋体","0"] -["73","286","1-0","0.5","♠","30","40","73","286","500","0","false","宋体","0"] -["173","286","1-0","0.5","♠","40","50","173","286","500","0","false","宋体","0"] -["341","282","1-0","0.5","♠","25","50","341","282","500","0","false","宋体","0"] -["306","289","1-0","0.5","♠","25","50","306","289","500","0","false","宋体","0"] -["503","285","1-0","0.5","♠","25","70","503","285","500","0","false","宋体","0"] -["446","274","1-0","0.5","♠","350","70","446","274","500","0","false","宋体","0"] -["422","292","1-0","0.5","♠","350","70","422","292","500","0","false","宋体","0"] -["368","297","1-0","0.5","♠","350","50","368","297","500","0","false","宋体","0"] -["372","271","1-0","0.5","♠","10","70","372","271","500","0","false","宋体","0"] -["235","295","1-0","0.5","♠","346","60","235","295","500","0","false","宋体","0"] -["192","299","1-0","0.5","♠","346","40","192","299","500","0","false","宋体","0"] -["181","277","1-0","0.5","♠","0","60","181","277","500","0","false","宋体","0"] -["129","289","1-0","0.5","♠","340","48","129","289","500","0","false","宋体","0"] -["101","290","1-0","0.5","♠","340","48","101","290","500","0","false","宋体","0"] -["58","309","1-0","0.5","♠","320","40","58","309","500","0","false","宋体","0"] -["2","299","1-0","0.5","♠","340","30","2","299","500","0","false","宋体","0"] -["41","287","1-0","0.5","♠","330","40","41","287","500","0","false","宋体","0"] -["34","314","1-0","0.5","♠","330","40","34","314","500","0","false","宋体","0"] -["39","344","1-0","0.5","♠","330","40","39","344","500","0","false","宋体","0"] -["57","335","1-0","0.5","♠","340","40","57","335","500","0","false","宋体","0"] -["84","331","1-0","0.5","♠","345","40","84","331","500","0","false","宋体","0"] -["126","332","1-0","0.5","♠","345","45","126","332","500","0","false","宋体","0"] -["149","335","1-0","0.5","♠","345","45","149","335","500","0","false","宋体","0"] -["180","331","1-0","0.5","♠","345","60","180","331","500","0","false","宋体","0"] -["213","330","1-0","0.5","♠","10","60","213","330","500","0","false","宋体","0"] -["221","339","1-0","0.5","♠","355","60","221","339","500","0","false","宋体","0"] -["245","333","1-0","0.5","♠","355","60","245","333","500","0","false","宋体","0"] -["294","336","1-0","0.5","♠","10","60","294","336","500","0","false","宋体","0"] -["324","337","1-0","0.5","♠","27","70","324","337","500","0","false","宋体","0"] -["353","334","1-0","0.5","♠","27","70","353","334","500","0","false","宋体","0"] -["370","333","1-0","0.5","♠","20","70","370","333","500","0","false","宋体","0"] -["393","329","1-0","0.5","♠","20","70","393","329","500","0","false","宋体","0"] -["393","344","1-0","0.5","♠","350","70","393","344","500","0","false","宋体","0"] -["441","340","1-0","0.5","♠","350","70","441","340","500","0","false","宋体","0"] -["460","325","1-0","0.5","♠","355","70","460","325","500","0","false","宋体","0"] -["484","327","1-0","0.5","♠","17","70","484","327","500","0","false","宋体","0"] -["146","8","0-1","3","你所跑过的短暂的季节(Saison)","0","0","146","8","500","0","true","黑体","0"] -["146","8","1-1","4","你所跑过的短暂的季节(Saison)","0","0","146","8","500","0","true","黑体","0"] -["158","30","0-1","3","尽管满是疾病之火的灼烧","0","0","158","30","500","0","true","黑体","0"] -["158","30","1-1","2","尽管满是疾病之火的灼烧","0","0","158","30","500","0","true","黑体","0"] -["154","65","0-1","2.5","「嗚呼…綺麗だね」","0","0","154","65","500","0","false","黑体","0"] -["154","65","1-0","2","「嗚呼…綺麗だね」","0","0","184","95","2000","0","false","黑体","1"] -["154","65","1-0","2","「嗚呼…綺麗だね」","0","0","184","35","2000","0","false","黑体","1"] -["154","65","1-0","2","「嗚呼…綺麗だね」","0","0","124","95","2000","0","false","黑体","1"] -["154","65","1-0","2","「嗚呼…綺麗だね」","0","0","124","35","2000","0","false","黑体","1"] -["154","65","1-0","2","「嗚呼…綺麗だね」","0","0","189","100","2000","0","false","黑体","1"] -["154","65","1-0","2","「嗚呼…綺麗だね」","0","0","189","30","2000","0","false","黑体","1"] -["154","65","1-0","2","「嗚呼…綺麗だね」","0","0","119","100","2000","0","false","黑体","1"] -["154","65","1-0","2","「嗚呼…綺麗だね」","0","0","119","30","2000","0","false","黑体","1"] -["154","65","1-0","2","「嗚呼…綺麗だね」","0","0","194","105","2000","0","false","黑体","1"] -["154","65","1-0","2","「嗚呼…綺麗だね」","0","0","194","25","2000","0","false","黑体","1"] -["154","65","1-0","2","「嗚呼…綺麗だね」","0","0","114","105","2000","0","false","黑体","1"] -["154","65","1-0","2","「嗚呼…綺麗だね」","0","0","114","25","2000","0","false","黑体","1"] -["154","65","1-0","2","「嗚呼…綺麗だね」","0","0","199","110","2000","0","false","黑体","1"] -["154","65","1-0","2","「嗚呼…綺麗だね」","0","0","199","20","2000","0","false","黑体","1"] -["154","65","1-0","2","「嗚呼…綺麗だね」","0","0","109","110","2000","0","false","黑体","1"] -["154","65","1-0","2","「嗚呼…綺麗だね」","0","0","109","20","2000","0","false","黑体","1"] -["154","65","1-0","2","「嗚呼…綺麗だね」","0","0","204","115","2000","0","false","黑体","1"] -["154","65","1-0","2","「嗚呼…綺麗だね」","0","0","204","15","2000","0","false","黑体","1"] -["154","65","1-0","2","「嗚呼…綺麗だね」","0","0","104","115","2000","0","false","黑体","1"] -["154","65","1-0","2","「嗚呼…綺麗だね」","0","0","104","15","2000","0","false","黑体","1"] -["199","215","1-0","4.5","笑","50","0","177","187","1000","0","true","黑体","0"] -["235","233","1-0","4.5","っ","30","0","199","215","1000","0","true","黑体","0"] -["260","233","1-0","4.5","て","10","0","235","233","1000","0","true","黑体","0"] -["260","233","1-0","4.5","逝","350","0","280","233","1000","0","true","黑体","0"] -["280","237","1-0","4.5","っ","330","0","315","227","1000","0","true","黑体","0"] -["315","227","1-0","4.5","た","310","0","347","205","1000","0","true","黑体","0"] -["158","91","1-1","4.5","█","0","0","158","91","500","0","false","宋体","0"] -["158","91","1-1","4.5","君","0","0","158","91","500","0","false","黑体","0"] -["194","100","1-1","4.5","の","0","0","194","100","500","0","false","黑体","0"] -["194","120","0-1","1.5"," 面影 (Image)","0","0","194","100","1500","0","false","黑体","1"] -["194","80","0-1","1.5"," 面影 (Image)","0","0","194","100","1500","0","false","黑体","1"] -["194","120","0-1","1.5"," 面影 (Image)","0","0","194","100","1500","0","false","黑体","1"] -["194","75","0-1","1.5"," 面影 (Image)","0","0","194","100","1500","0","false","黑体","1"] -["194","125","0-1","1.5"," 面影 (Image)","0","0","194","100","1500","0","false","黑体","1"] -["194","70","0-1","1.5"," 面影 (Image)","0","0","194","100","1500","0","false","黑体","1"] -["194","130","0-1","1.5"," 面影 (Image)","0","0","194","100","1500","0","false","黑体","1"] -["194","65","0-1","1.5"," 面影 (Image)","0","0","194","100","1500","0","false","黑体","1"] -["194","135","0-1","1.5"," 面影 (Image)","0","0","194","100","1500","0","false","黑体","1"] -["194","60","0-1","1.5"," 面影 (Image)","0","0","194","100","1500","0","false","黑体","0"] -["194","140","0-1","1.5"," 面影 (Image)","0","0","194","100","1500","0","false","黑体","1"] -["194","55","0-1","1.5"," 面影 (Image)","0","0","194","100","1500","0","false","黑体","1"] -["194","100","1-1","2"," 面影 (Image)","0","0","194","100","2000","0","false","黑体","1"] -["245","178","0-1","2","忘","0","0","225","158","2000","0","false","黑体","1"] -["245","138","0-1","2","忘","0","0","225","158","2000","0","false","黑体","1"] -["205","178","0-1","2","忘","0","0","225","158","2000","0","false","黑体","1"] -["205","138","0-1","2","忘","0","0","225","158","2000","0","false","黑体","1"] -["255","188","0-1","2","忘","0","0","225","158","2000","0","false","黑体","1"] -["255","128","0-1","2","忘","0","0","225","158","2000","0","false","黑体","1"] -["195","188","0-1","2","忘","0","0","225","158","2000","0","false","黑体","1"] -["195","128","0-1","2","忘","0","0","225","158","2000","0","false","黑体","1"] -["265","198","0-1","2","忘","0","0","225","158","2000","0","false","黑体","1"] -["265","118","0-1","2","忘","0","0","225","158","2000","0","false","黑体","1"] -["185","198","0-1","2","忘","0","0","225","158","2000","0","false","黑体","1"] -["185","118","0-1","2","忘","0","0","225","158","2000","0","false","黑体","1"] -["275","208","0-1","2","忘","0","0","225","158","2000","0","false","黑体","1"] -["275","108","0-1","2","忘","0","0","225","158","2000","0","false","黑体","1"] -["175","208","0-1","2","忘","0","0","225","158","2000","0","false","黑体","1"] -["175","108","0-1","2","忘","0","0","225","158","2000","0","false","黑体","1"] -["225","158","1-1","2.4","忘","0","0","225","158","2000","0","false","黑体","0"] -["275","108","0-1","2"," れ","0","0","225","158","2000","0","false","黑体","1"] -["275","208","0-1","2"," れ","0","0","225","158","2000","0","false","黑体","1"] -["175","108","0-1","2"," れ","0","0","225","158","2000","0","false","黑体","1"] -["175","208","0-1","2"," れ","0","0","225","158","2000","0","false","黑体","1"] -["265","118","0-1","2"," れ","0","0","225","158","2000","0","false","黑体","1"] -["265","198","0-1","2"," れ","0","0","225","158","2000","0","false","黑体","1"] -["185","118","0-1","2"," れ","0","0","225","158","2000","0","false","黑体","1"] -["185","198","0-1","2"," れ","0","0","225","158","2000","0","false","黑体","1"] -["255","128","0-1","2"," れ","0","0","225","158","2000","0","false","黑体","1"] -["255","188","0-1","2"," れ","0","0","225","158","2000","0","false","黑体","1"] -["195","128","0-1","2"," れ","0","0","225","158","2000","0","false","黑体","1"] -["195","188","0-1","2"," れ","0","0","225","158","2000","0","false","黑体","1"] -["245","138","0-1","2"," れ","0","0","225","158","2000","0","false","黑体","1"] -["245","178","0-1","2"," れ","0","0","225","158","2000","0","false","黑体","1"] -["205","138","0-1","2"," れ","0","0","225","158","2000","0","false","黑体","1"] -["205","178","0-1","2"," れ","0","0","225","158","2000","300","false","黑体","1"] -["225","158","1-1","1.7"," れ","0","0","225","158","2000","0","false","黑体","0"] -["275","108","0-1","2","  な","0","0","225","158","2000","0","false","黑体","1"] -["275","208","0-1","2","  な","0","0","225","158","2000","0","false","黑体","1"] -["175","108","0-1","2","  な","0","0","225","158","2000","0","false","黑体","1"] -["175","208","0-1","2","  な","0","0","225","158","2000","0","false","黑体","1"] -["265","118","0-1","2","  な","0","0","225","158","2000","0","false","黑体","1"] -["265","198","0-1","2","  な","0","0","225","158","2000","0","false","黑体","1"] -["185","118","0-1","2","  な","0","0","225","158","2000","0","false","黑体","1"] -["185","198","0-1","2","  な","0","0","225","158","2000","0","false","黑体","1"] -["255","128","0-1","2","  な","0","0","225","158","2000","0","false","黑体","1"] -["255","188","0-1","2","  な","0","0","225","158","2000","0","false","黑体","1"] -["195","128","0-1","2","  な","0","0","225","158","2000","0","false","黑体","1"] -["195","188","0-1","2","  な","0","0","225","158","2000","0","false","黑体","1"] -["245","138","0-1","2","  な","0","0","225","158","2000","0","false","黑体","1"] -["245","178","0-1","2","  な","0","0","225","158","2000","0","false","黑体","1"] -["205","138","0-1","2","  な","0","0","225","158","2000","0","false","黑体","1"] -["205","178","0-1","2","  な","0","0","225","158","2000","0","false","黑体","1"] -["225","158","1-1","1.1","  な","0","0","225","158","2000","0","false","黑体","0"] -["275","108","0-1","2","   い","0","0","225","158","2000","0","false","黑体","1"] -["275","208","0-1","2","   い","0","0","225","158","2000","0","false","黑体","1"] -["175","108","0-1","2","   い","0","0","225","158","2000","0","false","黑体","1"] -["175","208","0-1","2","   い","0","0","225","158","2000","0","false","黑体","1"] -["265","118","0-1","2","   い","0","0","225","158","2000","0","false","黑体","1"] -["265","198","0-1","2","   い","0","0","225","158","2000","0","false","黑体","1"] -["185","118","0-1","2","   い","0","0","225","158","2000","0","false","黑体","1"] -["185","198","0-1","2","   い","0","0","225","158","2000","0","false","黑体","1"] -["255","128","0-1","2","   い","0","0","225","158","2000","0","false","黑体","1"] -["255","188","0-1","2","   い","0","0","225","158","2000","0","false","黑体","1"] -["195","128","0-1","2","   い","0","0","225","158","2000","0","false","黑体","1"] -["195","188","0-1","2","   い","0","0","225","158","2000","0","false","黑体","1"] -["245","138","0-1","2","   い","0","0","225","158","2000","0","false","黑体","1"] -["245","178","0-1","2","   い","0","0","225","158","2000","0","false","黑体","1"] -["205","138","0-1","2","   い","0","0","225","158","2000","0","false","黑体","1"] -["205","178","0-1","2","   い","0","0","225","158","2000","0","false","黑体","1"] -["225","158","1-1","0.8","   い","0","0","225","158","2000","0","false","黑体","1"] -["275","108","0-1","2","    よ","0","0","225","158","2000","0","false","黑体","1"] -["275","208","0-1","2","    よ","0","0","225","158","2000","0","false","黑体","1"] -["175","108","0-1","2","    よ","0","0","225","158","2000","0","false","黑体","1"] -["175","208","0-1","2","    よ","0","0","225","158","2000","0","false","黑体","1"] -["265","118","0-1","2","    よ","0","0","225","158","2000","0","false","黑体","1"] -["265","198","0-1","2","    よ","0","0","225","158","2000","0","false","黑体","1"] -["185","118","0-1","2","    よ","0","0","225","158","2000","0","false","黑体","1"] -["185","198","0-1","2","    よ","0","0","225","158","2000","0","false","黑体","1"] -["255","128","0-1","2","    よ","0","0","225","158","2000","0","false","黑体","1"] -["255","188","0-1","2","    よ","0","0","225","158","2000","0","false","黑体","1"] -["195","128","0-1","2","    よ","0","0","225","158","2000","0","false","黑体","1"] -["195","188","0-1","2","    よ","0","0","225","158","2000","0","false","黑体","1"] -["245","138","0-1","2","    よ","0","0","225","158","2000","0","false","黑体","1"] -["245","178","0-1","2","    よ","0","0","225","158","2000","0","false","黑体","1"] -["205","138","0-1","2","    よ","0","0","225","158","2000","0","false","黑体","1"] -["205","178","0-1","2","    よ","0","0","225","158","2000","0","false","黑体","1"] -["196","8","0-1","2","“啊…真美呢”","0","0","196","8","500","0","false","黑体","0"] -["196","8","1-0","2.5","“啊…真美呢”","0","0","196","8","500","0","false","黑体","0"] -["188","31","0-1","1.5","微笑着离开了世界","0","0","188","31","500","0","false","黑体","0"] -["188","31","1-0","3","微笑着离开了世界","0","0","188","31","500","0","false","黑体","0"] -["207","7","0-1","4.5","你的面容(Image)","0","0","207","7","500","0","false","黑体","0"] -["263","35","0-1","4.5","永远不忘","0","0","263","35","500","0","false","黑体","0"] -["66","370","1-1","4.5","♪","0","0","66","-35","2000","0","false","MS UI Gothic","0"] -["66","370","0.8-0.8","4.5","♪","0","0","66","-35","2000","100","false","MS UI Gothic","0"] -["66","370","0.6-0.6","4.5","♪","0","0","66","-35","2000","200","false","MS UI Gothic","0"] -["66","370","0.4-0.4","4.5","♪","0","0","66","-35","2000","300","false","MS UI Gothic","0"] -["156","370","1-1","4.5","♬","0","0","156","-35","2000","0","false","MS UI Gothic","0"] -["156","370","0.8-0.8","4.5","♬","0","0","156","-35","2000","100","false","MS UI Gothic","0"] -["156","370","0.6-0.6","4.5","♬","0","0","156","-35","2000","200","false","MS UI Gothic","0"] -["156","370","0.4-0.4","4.5","♬","0","0","156","-35","2000","300","false","MS UI Gothic","0"] -["246","370","1-1","4.5","♩","0","0","246","-35","2000","0","false","MS UI Gothic","0"] -["246","370","0.8-0.8","4.5","♩","0","0","246","-35","2000","100","false","MS UI Gothic","0"] -["246","370","0.6-0.6","4.5","♩","0","0","246","-35","2000","200","false","MS UI Gothic","0"] -["246","370","0.4-0.4","4.5","♩","0","0","246","-35","2000","300","false","MS UI Gothic","0"] -["336","370","1-1","4.5","♫","0","0","336","-35","2000","0","false","MS UI Gothic","0"] -["336","370","0.8-0.8","4.5","♫","0","0","336","-35","2000","100","false","MS UI Gothic","0"] -["336","370","0.6-0.6","4.5","♫","0","0","336","-35","2000","200","false","MS UI Gothic","0"] -["336","370","0.4-0.4","4.5","♫","0","0","336","-35","2000","300","false","MS UI Gothic","0"] -["426","370","1-1","4.5","♭","0","0","426","-35","2000","0","false","MS UI Gothic","0"] -["426","370","0.8-0.8","4.5","♭","0","0","426","-35","2000","100","false","MS UI Gothic","0"] -["426","370","0.6-0.6","4.5","♭","0","0","426","-35","2000","200","false","MS UI Gothic","0"] -["426","370","0.4-0.4","4.5","♭","0","0","426","-35","2000","300","false","MS UI Gothic","0"] -["336","370","1-1","4.5","♪","0","0","336","-35","2000","0","false","MS UI Gothic","0"] -["336","370","0.8-0.8","4.5","♪","0","0","336","-35","2000","100","false","MS UI Gothic","0"] -["336","370","0.6-0.6","4.5","♪","0","0","336","-35","2000","200","false","MS UI Gothic","0"] -["336","370","0.4-0.4","4.5","♪","0","0","336","-35","2000","300","false","MS UI Gothic","0"] -["426","370","1-1","4.5","♭","0","0","426","-35","2000","0","false","MS UI Gothic","0"] -["426","370","0.8-0.8","4.5","♭","0","0","426","-35","2000","100","false","MS UI Gothic","0"] -["426","370","0.6-0.6","4.5","♭","0","0","426","-35","2000","200","false","MS UI Gothic","0"] -["426","370","0.4-0.4","4.5","♭","0","0","426","-35","2000","300","false","MS UI Gothic","0"] -["259","370","1-1","4.5","♬","0","0","259","-35","2000","0","false","MS UI Gothic","0"] -["259","370","0.8-0.8","4.5","♬","0","0","259","-35","2000","100","false","MS UI Gothic","0"] -["259","370","0.6-0.6","4.5","♬","0","0","259","-35","2000","200","false","MS UI Gothic","0"] -["259","370","0.4-0.4","4.5","♬","0","0","259","-35","2000","300","false","MS UI Gothic","0"] -["172","370","1-1","4.5","♩","0","0","172","-35","2000","0","false","MS UI Gothic","0"] -["172","370","0.8-0.8","4.5","♩","0","0","172","-35","2000","100","false","MS UI Gothic","0"] -["172","370","0.6-0.6","4.5","♩","0","0","172","-35","2000","200","false","MS UI Gothic","0"] -["172","370","0.4-0.4","4.5","♩","0","0","172","-35","2000","300","false","MS UI Gothic","0"] -["92","370","1-1","4.5","♫","0","0","92","-35","2000","0","false","MS UI Gothic","0"] -["92","370","0.8-0.8","4.5","♫","0","0","92","-35","2000","100","false","MS UI Gothic","0"] -["92","370","0.6-0.6","4.5","♫","0","0","92","-35","2000","200","false","MS UI Gothic","0"] -["92","370","0.4-0.4","4.5","♫","0","0","92","-35","2000","300","false","MS UI Gothic","0"] -["153","370","1-1","4.5","♭","0","0","153","-35","2000","0","false","MS UI Gothic","0"] -["153","370","0.8-0.8","4.5","♭","0","0","153","-35","2000","100","false","MS UI Gothic","0"] -["153","370","0.6-0.6","4.5","♭","0","0","153","-35","2000","200","false","MS UI Gothic","0"] -["153","370","0.4-0.4","4.5","♭","0","0","153","-35","2000","300","false","MS UI Gothic","0"] -["228","370","1-1","4.5","♪","0","0","228","-35","2000","0","false","MS UI Gothic","0"] -["228","370","0.8-0.8","4.5","♪","0","0","228","-35","2000","100","false","MS UI Gothic","0"] -["228","370","0.6-0.6","4.5","♪","0","0","228","-35","2000","200","false","MS UI Gothic","0"] -["228","370","0.4-0.4","4.5","♪","0","0","228","-35","2000","300","false","MS UI Gothic","0"] -["306","370","1-1","4.5","♬","0","0","306","-35","2000","0","false","MS UI Gothic","0"] -["306","370","0.8-0.8","4.5","♬","0","0","306","-35","2000","100","false","MS UI Gothic","0"] -["306","370","0.6-0.6","4.5","♬","0","0","306","-35","2000","200","false","MS UI Gothic","0"] -["306","370","0.4-0.4","4.5","♬","0","0","306","-35","2000","300","false","MS UI Gothic","0"] -["385","370","1-1","4.5","♩","0","0","385","-35","2000","0","false","MS UI Gothic","0"] -["385","370","0.8-0.8","4.5","♩","0","0","385","-35","2000","100","false","MS UI Gothic","0"] -["385","370","0.6-0.6","4.5","♩","0","0","385","-35","2000","200","false","MS UI Gothic","0"] -["385","370","0.4-0.4","4.5","♩","0","0","385","-35","2000","300","false","MS UI Gothic","0"] -["306","370","1-1","4.5","♭","0","0","306","-35","2000","0","false","MS UI Gothic","0"] -["306","370","0.8-0.8","4.5","♭","0","0","306","-35","2000","100","false","MS UI Gothic","0"] -["306","370","0.6-0.6","4.5","♭","0","0","306","-35","2000","200","false","MS UI Gothic","0"] -["306","370","0.4-0.4","4.5","♭","0","0","306","-35","2000","300","false","MS UI Gothic","0"] -["385","370","1-1","4.5","♩","0","0","385","-35","2000","0","false","MS UI Gothic","0"] -["385","370","0.8-0.8","4.5","♩","0","0","385","-35","2000","100","false","MS UI Gothic","0"] -["385","370","0.6-0.6","4.5","♩","0","0","385","-35","2000","200","false","MS UI Gothic","0"] -["385","370","0.4-0.4","4.5","♩","0","0","385","-35","2000","300","false","MS UI Gothic","0"] -["306","370","1-1","4.5","♭","0","0","306","-35","2000","0","false","MS UI Gothic","0"] -["306","370","0.8-0.8","4.5","♭","0","0","306","-35","2000","100","false","MS UI Gothic","0"] -["306","370","0.6-0.6","4.5","♭","0","0","306","-35","2000","200","false","MS UI Gothic","0"] -["306","370","0.4-0.4","4.5","♭","0","0","306","-35","2000","300","false","MS UI Gothic","0"] -["220","370","1-1","4.5","♫","0","0","220","-35","2000","0","false","MS UI Gothic","0"] -["220","370","0.8-0.8","4.5","♫","0","0","220","-35","2000","100","false","MS UI Gothic","0"] -["220","370","0.6-0.6","4.5","♫","0","0","220","-35","2000","200","false","MS UI Gothic","0"] -["220","370","0.4-0.4","4.5","♫","0","0","220","-35","2000","300","false","MS UI Gothic","0"] -["133","370","1-1","4.5","♪","0","0","133","-35","2000","0","false","MS UI Gothic","0"] -["133","370","0.8-0.8","4.5","♪","0","0","133","-35","2000","100","false","MS UI Gothic","0"] -["133","370","0.6-0.6","4.5","♪","0","0","133","-35","2000","200","false","MS UI Gothic","0"] -["133","370","0.4-0.4","4.5","♪","0","0","133","-35","2000","300","false","MS UI Gothic","0"] -["123","370","1-1","4.5","♪","0","0","123","-35","2000","0","false","MS UI Gothic","0"] -["123","370","0.8-0.8","4.5","♪","0","0","123","-35","2000","100","false","MS UI Gothic","0"] -["123","370","0.6-0.6","4.5","♪","0","0","123","-35","2000","200","false","MS UI Gothic","0"] -["123","370","0.4-0.4","4.5","♪","0","0","123","-35","2000","300","false","MS UI Gothic","0"] -["189","370","1-1","4.5","♬","0","0","189","-35","2000","0","false","MS UI Gothic","0"] -["189","370","0.8-0.8","4.5","♬","0","0","189","-35","2000","100","false","MS UI Gothic","0"] -["189","370","0.6-0.6","4.5","♬","0","0","189","-35","2000","200","false","MS UI Gothic","0"] -["189","370","0.4-0.4","4.5","♬","0","0","189","-35","2000","300","false","MS UI Gothic","0"] -["133","370","1-1","4.5","♪","0","0","133","-35","2000","0","false","MS UI Gothic","0"] -["133","370","0.8-0.8","4.5","♪","0","0","133","-35","2000","100","false","MS UI Gothic","0"] -["133","370","0.6-0.6","4.5","♪","0","0","133","-35","2000","200","false","MS UI Gothic","0"] -["133","370","0.4-0.4","4.5","♪","0","0","133","-35","2000","300","false","MS UI Gothic","0"] -["94","370","1-1","4.5","♭","0","0","94","-35","2000","0","false","MS UI Gothic","0"] -["94","370","0.8-0.8","4.5","♭","0","0","94","-35","2000","100","false","MS UI Gothic","0"] -["94","370","0.6-0.6","4.5","♭","0","0","94","-35","2000","200","false","MS UI Gothic","0"] -["94","370","0.4-0.4","4.5","♭","0","0","94","-35","2000","300","false","MS UI Gothic","0"] -["177","370","1-1","4.5","♫","0","0","177","-35","2000","0","false","MS UI Gothic","0"] -["177","370","0.8-0.8","4.5","♫","0","0","177","-35","2000","100","false","MS UI Gothic","0"] -["177","370","0.6-0.6","4.5","♫","0","0","177","-35","2000","200","false","MS UI Gothic","0"] -["177","370","0.4-0.4","4.5","♫","0","0","177","-35","2000","300","false","MS UI Gothic","0"] -["260","370","1-1","4.5","♩","0","0","260","-35","2000","0","false","MS UI Gothic","0"] -["260","370","0.8-0.8","4.5","♩","0","0","260","-35","2000","100","false","MS UI Gothic","0"] -["260","370","0.6-0.6","4.5","♩","0","0","260","-35","2000","200","false","MS UI Gothic","0"] -["260","370","0.4-0.4","4.5","♩","0","0","260","-35","2000","300","false","MS UI Gothic","0"] -["352","370","1-1","4.5","♬","0","0","352","-35","2000","0","false","MS UI Gothic","0"] -["352","370","0.8-0.8","4.5","♬","0","0","352","-35","2000","100","false","MS UI Gothic","0"] -["352","370","0.6-0.6","4.5","♬","0","0","352","-35","2000","200","false","MS UI Gothic","0"] -["352","370","0.4-0.4","4.5","♬","0","0","352","-35","2000","300","false","MS UI Gothic","0"] -["435","370","1-1","4.5","♪","0","0","435","-35","2000","0","false","MS UI Gothic","0"] -["435","370","0.8-0.8","4.5","♪","0","0","435","-35","2000","100","false","MS UI Gothic","0"] -["435","370","0.6-0.6","4.5","♪","0","0","435","-35","2000","200","false","MS UI Gothic","0"] -["435","370","0.4-0.4","4.5","♪","0","0","435","-35","2000","300","false","MS UI Gothic","0"] -["440","370","1-1","4.5","♪","0","0","440","-35","2000","0","false","MS UI Gothic","0"] -["440","370","0.8-0.8","4.5","♪","0","0","440","-35","2000","100","false","MS UI Gothic","0"] -["440","370","0.6-0.6","4.5","♪","0","0","440","-35","2000","200","false","MS UI Gothic","0"] -["440","370","0.4-0.4","4.5","♪","0","0","440","-35","2000","300","false","MS UI Gothic","0"] -["445","370","1-1","4.5","♪","0","0","445","-35","2000","0","false","MS UI Gothic","0"] -["445","370","0.8-0.8","4.5","♪","0","0","445","-35","2000","100","false","MS UI Gothic","0"] -["445","370","0.6-0.6","4.5","♪","0","0","445","-35","2000","200","false","MS UI Gothic","0"] -["445","370","0.4-0.4","4.5","♪","0","0","445","-35","2000","300","false","MS UI Gothic","0"] -["370","370","1-1","4.5","♩","0","0","370","-35","2000","0","false","MS UI Gothic","0"] -["370","370","0.8-0.8","4.5","♩","0","0","370","-35","2000","100","false","MS UI Gothic","0"] -["370","370","0.6-0.6","4.5","♩","0","0","370","-35","2000","200","false","MS UI Gothic","0"] -["370","370","0.4-0.4","4.5","♩","0","0","370","-35","2000","300","false","MS UI Gothic","0"] -["435","370","1-1","4.5","♪","0","0","435","-35","2000","0","false","MS UI Gothic","0"] -["435","370","0.8-0.8","4.5","♪","0","0","435","-35","2000","100","false","MS UI Gothic","0"] -["435","370","0.6-0.6","4.5","♪","0","0","435","-35","2000","200","false","MS UI Gothic","0"] -["435","370","0.4-0.4","4.5","♪","0","0","435","-35","2000","300","false","MS UI Gothic","0"] -["259","370","1-1","4.5","♫","0","0","259","-35","2000","0","false","MS UI Gothic","0"] -["259","370","0.8-0.8","4.5","♫","0","0","259","-35","2000","100","false","MS UI Gothic","0"] -["259","370","0.6-0.6","4.5","♫","0","0","259","-35","2000","200","false","MS UI Gothic","0"] -["259","370","0.4-0.4","4.5","♫","0","0","259","-35","2000","300","false","MS UI Gothic","0"] -["180","370","1-1","4.5","♭","0","0","180","-35","2000","0","false","MS UI Gothic","0"] -["180","370","0.8-0.8","4.5","♭","0","0","180","-35","2000","100","false","MS UI Gothic","0"] -["180","370","0.6-0.6","4.5","♭","0","0","180","-35","2000","200","false","MS UI Gothic","0"] -["180","370","0.4-0.4","4.5","♭","0","0","180","-35","2000","300","false","MS UI Gothic","0"] -["259","370","1-1","4.5","♫","0","0","259","-35","2000","0","false","MS UI Gothic","0"] -["259","370","0.8-0.8","4.5","♫","0","0","259","-35","2000","100","false","MS UI Gothic","0"] -["259","370","0.6-0.6","4.5","♫","0","0","259","-35","2000","200","false","MS UI Gothic","0"] -["259","370","0.4-0.4","4.5","♫","0","0","259","-35","2000","300","false","MS UI Gothic","0"] -["393","370","1-1","4.5","♪","0","0","393","-35","2000","0","false","MS UI Gothic","0"] -["393","370","0.8-0.8","4.5","♪","0","0","393","-35","2000","100","false","MS UI Gothic","0"] -["393","370","0.6-0.6","4.5","♪","0","0","393","-35","2000","200","false","MS UI Gothic","0"] -["393","370","0.4-0.4","4.5","♪","0","0","393","-35","2000","300","false","MS UI Gothic","0"] -["180","370","1-1","4.5","♭","0","0","180","-35","2000","0","false","MS UI Gothic","0"] -["180","370","0.8-0.8","4.5","♭","0","0","180","-35","2000","100","false","MS UI Gothic","0"] -["180","370","0.6-0.6","4.5","♭","0","0","180","-35","2000","200","false","MS UI Gothic","0"] -["180","370","0.4-0.4","4.5","♭","0","0","180","-35","2000","300","false","MS UI Gothic","0"] -["417","370","1-1","4.5","♩","0","0","417","-35","2000","0","false","MS UI Gothic","0"] -["417","370","0.8-0.8","4.5","♩","0","0","417","-35","2000","100","false","MS UI Gothic","0"] -["417","370","0.6-0.6","4.5","♩","0","0","417","-35","2000","200","false","MS UI Gothic","0"] -["417","370","0.4-0.4","4.5","♩","0","0","417","-35","2000","300","false","MS UI Gothic","0"] -["310","370","1-1","4.5","♬","0","0","310","-35","2000","0","false","MS UI Gothic","0"] -["310","370","0.8-0.8","4.5","♬","0","0","310","-35","2000","100","false","MS UI Gothic","0"] -["310","370","0.6-0.6","4.5","♬","0","0","310","-35","2000","200","false","MS UI Gothic","0"] -["310","370","0.4-0.4","4.5","♬","0","0","310","-35","2000","300","false","MS UI Gothic","0"] -["325","370","1-1","4.5","♬","0","0","325","-35","2000","0","false","MS UI Gothic","0"] -["325","370","0.8-0.8","4.5","♬","0","0","325","-35","2000","100","false","MS UI Gothic","0"] -["325","370","0.6-0.6","4.5","♬","0","0","325","-35","2000","200","false","MS UI Gothic","0"] -["325","370","0.4-0.4","4.5","♬","0","0","325","-35","2000","300","false","MS UI Gothic","0"] -["227","370","1-1","4.5","♫","0","0","227","-35","2000","0","false","MS UI Gothic","0"] -["227","370","0.8-0.8","4.5","♫","0","0","227","-35","2000","100","false","MS UI Gothic","0"] -["227","370","0.6-0.6","4.5","♫","0","0","227","-35","2000","200","false","MS UI Gothic","0"] -["227","370","0.4-0.4","4.5","♫","0","0","227","-35","2000","300","false","MS UI Gothic","0"] -["144","370","1-1","4.5","♪","0","0","144","-35","2000","0","false","MS UI Gothic","0"] -["144","370","0.8-0.8","4.5","♪","0","0","144","-35","2000","100","false","MS UI Gothic","0"] -["144","370","0.6-0.6","4.5","♪","0","0","144","-35","2000","200","false","MS UI Gothic","0"] -["144","370","0.4-0.4","4.5","♪","0","0","144","-35","2000","300","false","MS UI Gothic","0"] -["58","370","1-1","4.5","♭","0","0","58","-35","2000","0","false","MS UI Gothic","0"] -["58","370","0.8-0.8","4.5","♭","0","0","58","-35","2000","100","false","MS UI Gothic","0"] -["58","370","0.6-0.6","4.5","♭","0","0","58","-35","2000","200","false","MS UI Gothic","0"] -["58","370","0.4-0.4","4.5","♭","0","0","58","-35","2000","300","false","MS UI Gothic","0"] -["144","370","1-1","4.5","♪","0","0","144","-35","2000","0","false","MS UI Gothic","0"] -["144","370","0.8-0.8","4.5","♪","0","0","144","-35","2000","100","false","MS UI Gothic","0"] -["144","370","0.6-0.6","4.5","♪","0","0","144","-35","2000","200","false","MS UI Gothic","0"] -["144","370","0.4-0.4","4.5","♪","0","0","144","-35","2000","300","false","MS UI Gothic","0"] -["87","370","1-1","4.5","♫","0","0","87","-35","2000","0","false","MS UI Gothic","0"] -["87","370","0.8-0.8","4.5","♫","0","0","87","-35","2000","100","false","MS UI Gothic","0"] -["87","370","0.6-0.6","4.5","♫","0","0","87","-35","2000","200","false","MS UI Gothic","0"] -["87","370","0.4-0.4","4.5","♫","0","0","87","-35","2000","300","false","MS UI Gothic","0"] -["60","355","0.9-0","0.5","●","0","0","60","355","500","0","false","宋体","0"] -["93","291","0.9-0","0.5","●","0","0","93","291","500","0","false","宋体","0"] -["75","254","0.9-0","0.5","●","0","0","75","254","500","0","false","宋体","0"] -["64","182","0.9-0","0.5","●","0","0","64","182","500","0","false","宋体","0"] -["94","107","0.9-0","0.5","●","0","0","94","107","500","0","false","宋体","0"] -["86","128","0.9-0","0.5","●","0","0","86","128","500","0","false","宋体","0"] -["63","44","0.9-0","0.5","●","0","0","63","44","500","0","false","宋体","0"] -["166","350","0.9-0","0.5","●","0","0","166","350","500","0","false","宋体","0"] -["154","274","0.9-0","0.5","●","0","0","154","274","500","0","false","宋体","0"] -["173","186","0.9-0","0.5","●","0","0","173","186","500","0","false","宋体","0"] -["158","106","0.9-0","0.5","●","0","0","158","106","500","0","false","宋体","0"] -["185","44","0.9-0","0.5","●","0","0","185","44","500","0","false","宋体","0"] -["146","16","0.9-0","0.5","●","0","0","146","16","500","0","false","宋体","0"] -["256","351","0.9-0","0.5","●","0","0","256","351","500","0","false","宋体","0"] -["241","287","0.9-0","0.5","●","0","0","241","287","500","0","false","宋体","0"] -["266","205","0.9-0","0.5","●","0","0","266","205","500","0","false","宋体","0"] -["232","189","0.9-0","0.5","●","0","0","232","189","500","0","false","宋体","0"] -["256","83","0.9-0","0.5","●","0","0","256","83","500","0","false","宋体","0"] -["235","9","0.9-0","0.5","●","0","0","235","9","500","0","false","宋体","0"] -["357","361","0.9-0","0.5","●","0","0","357","361","500","0","false","宋体","0"] -["327","319","0.9-0","0.5","●","0","0","327","319","500","0","false","宋体","0"] -["353","236","0.9-0","0.5","●","0","0","353","236","500","0","false","宋体","0"] -["322","154","0.9-0","0.5","●","0","0","322","154","500","0","false","宋体","0"] -["337","56","0.9-0","0.5","●","0","0","337","56","500","0","false","宋体","0"] -["362","6","0.9-0","0.5","●","0","0","362","6","500","0","false","宋体","0"] -["412","342","0.9-0","0.5","●","0","0","412","342","500","0","false","宋体","0"] -["451","365","0.9-0","0.5","●","0","0","451","365","500","0","false","宋体","0"] -["438","260","0.9-0","0.5","●","0","0","438","260","500","0","false","宋体","0"] -["417","163","0.9-0","0.5","●","0","0","417","163","500","0","false","宋体","0"] -["443","66","0.9-0","0.5","●","0","0","443","66","500","0","false","宋体","0"] -["431","15","0.9-0","0.5","●","0","0","431","15","500","0","false","宋体","0"] -["323","309","0.9-0","0.5","●","0","0","323","309","500","0","false","宋体","0"] -["356","352","0.9-0","0.5","●","0","0","356","352","500","0","false","宋体","0"] -["355","224","0.9-0","0.5","●","0","0","355","224","500","0","false","宋体","0"] -["336","137","0.9-0","0.5","●","0","0","336","137","500","0","false","宋体","0"] -["353","57","0.9-0","0.5","●","0","0","353","57","500","0","false","宋体","0"] -["324","5","0.9-0","0.5","●","0","0","324","5","500","0","false","宋体","0"] -["416","363","0.9-0","0.5","●","0","0","416","363","500","0","false","宋体","0"] -["442","290","0.9-0","0.5","●","0","0","442","290","500","0","false","宋体","0"] -["414","264","0.9-0","0.5","●","0","0","414","264","500","0","false","宋体","0"] -["434","171","0.9-0","0.5","●","0","0","434","171","500","0","false","宋体","0"] -["423","94","0.9-0","0.5","●","0","0","423","94","500","0","false","宋体","0"] -["443","15","0.9-0","0.5","●","0","0","443","15","500","0","false","宋体","0"] -["272","352","0.9-0","0.5","●","0","0","272","352","500","0","false","宋体","0"] -["252","282","0.9-0","0.5","●","0","0","252","282","500","0","false","宋体","0"] -["273","254","0.9-0","0.5","●","0","0","273","254","500","0","false","宋体","0"] -["246","186","0.9-0","0.5","●","0","0","246","186","500","0","false","宋体","0"] -["256","105","0.9-0","0.5","●","0","0","256","105","500","0","false","宋体","0"] -["278","10","0.9-0","0.5","●","0","0","278","10","500","0","false","宋体","0"] -["157","264","0.9-0","0.5","●","0","0","157","264","500","0","false","宋体","0"] -["184","349","0.9-0","0.5","●","0","0","184","349","500","0","false","宋体","0"] -["175","180","0.9-0","0.5","●","0","0","175","180","500","0","false","宋体","0"] -["156","146","0.9-0","0.5","●","0","0","156","146","500","0","false","宋体","0"] -["184","58","0.9-0","0.5","●","0","0","184","58","500","0","false","宋体","0"] -["152","12","0.9-0","0.5","●","0","0","152","12","500","0","false","宋体","0"] -["105","368","0.9-0","0.5","●","0","0","105","368","500","0","false","宋体","0"] -["85","295","0.9-0","0.5","●","0","0","85","295","500","0","false","宋体","0"] -["105","221","0.9-0","0.5","●","0","0","105","221","500","0","false","宋体","0"] -["81","196","0.9-0","0.5","●","0","0","81","196","500","0","false","宋体","0"] -["99","103","0.9-0","0.5","●","0","0","99","103","500","0","false","宋体","0"] -["79","22","0.9-0","0.5","●","0","0","79","22","500","0","false","宋体","0"] -["147","363","0.9-0","0.5","●","0","0","147","363","500","0","false","宋体","0"] -["170","293","0.9-0","0.5","●","0","0","170","293","500","0","false","宋体","0"] -["148","283","0.9-0","0.5","●","0","0","148","283","500","0","false","宋体","0"] -["162","201","0.9-0","0.5","●","0","0","162","201","500","0","false","宋体","0"] -["141","108","0.9-0","0.5","●","0","0","141","108","500","0","false","宋体","0"] -["181","30","0.9-0","0.5","●","0","0","181","30","500","0","false","宋体","0"] -["235","363","0.9-0","0.5","●","0","0","235","363","500","0","false","宋体","0"] -["221","322","0.9-0","0.5","●","0","0","221","322","500","0","false","宋体","0"] -["253","249","0.9-0","0.5","●","0","0","253","249","500","0","false","宋体","0"] -["229","184","0.9-0","0.5","●","0","0","229","184","500","0","false","宋体","0"] -["239","95","0.9-0","0.5","●","0","0","239","95","500","0","false","宋体","0"] -["229","21","0.9-0","0.5","●","0","0","229","21","500","0","false","宋体","0"] -["330","358","0.9-0","0.5","●","0","0","330","358","500","0","false","宋体","0"] -["302","279","0.9-0","0.5","●","0","0","302","279","500","0","false","宋体","0"] -["322","222","0.9-0","0.5","●","0","0","322","222","500","0","false","宋体","0"] -["297","156","0.9-0","0.5","●","0","0","297","156","500","0","false","宋体","0"] -["333","80","0.9-0","0.5","●","0","0","333","80","500","0","false","宋体","0"] -["299","12","0.9-0","0.5","●","0","0","299","12","500","0","false","宋体","0"] -["371","332","0.9-0","0.5","●","0","0","371","332","500","0","false","宋体","0"] -["403","248","0.9-0","0.5","●","0","0","403","248","500","0","false","宋体","0"] -["367","183","0.9-0","0.5","●","0","0","367","183","500","0","false","宋体","0"] -["383","98","0.9-0","0.5","●","0","0","383","98","500","0","false","宋体","0"] -["356","76","0.9-0","0.5","●","0","0","356","76","500","0","false","宋体","0"] -["375","13","0.9-0","0.5","●","0","0","375","13","500","0","false","宋体","0"] -["304","363","0.9-0","0.5","●","0","0","304","363","500","0","false","宋体","0"] -["324","284","0.9-0","0.5","●","0","0","324","284","500","0","false","宋体","0"] -["287","220","0.9-0","0.5","●","0","0","287","220","500","0","false","宋体","0"] -["319","193","0.9-0","0.5","●","0","0","319","193","500","0","false","宋体","0"] -["296","107","0.9-0","0.5","●","0","0","296","107","500","0","false","宋体","0"] -["311","31","0.9-0","0.5","●","0","0","311","31","500","0","false","宋体","0"] -["395","351","0.9-0","0.5","●","0","0","395","351","500","0","false","宋体","0"] -["369","282","0.9-0","0.5","●","0","0","369","282","500","0","false","宋体","0"] -["389","210","0.9-0","0.5","●","0","0","389","210","500","0","false","宋体","0"] -["377","128","0.9-0","0.5","●","0","0","377","128","500","0","false","宋体","0"] -["405","54","0.9-0","0.5","●","0","0","405","54","500","0","false","宋体","0"] -["361","17","0.9-0","0.5","●","0","0","361","17","500","0","false","宋体","0"] -["324","347","0.9-0","0.5","●","0","0","324","347","500","0","false","宋体","0"] -["297","280","0.9-0","0.5","●","0","0","297","280","500","0","false","宋体","0"] -["324","252","0.9-0","0.5","●","0","0","324","252","500","0","false","宋体","0"] -["304","183","0.9-0","0.5","●","0","0","304","183","500","0","false","宋体","0"] -["329","105","0.9-0","0.5","●","0","0","329","105","500","0","false","宋体","0"] -["298","33","0.9-0","0.5","●","0","0","298","33","500","0","false","宋体","0"] -["335","16","0.9-0","0.5","●","0","0","335","16","500","0","false","宋体","0"] -["212","353","0.9-0","0.5","●","0","0","212","353","500","0","true","宋体","0"] -["235","292","0.9-0","0.5","●","0","0","235","292","500","0","true","宋体","0"] -["220","202","0.9-0","0.5","●","0","0","220","202","500","0","true","宋体","0"] -["226","118","0.9-0","0.5","●","0","0","226","118","500","0","true","宋体","0"] -["196","49","0.9-0","0.5","●","0","0","196","49","500","0","true","宋体","0"] -["239","9","0.9-0","0.5","●","0","0","239","9","500","0","true","宋体","0"] -["154","359","0.9-0","0.5","●","0","0","154","359","500","0","false","宋体","0"] -["129","286","0.9-0","0.5","●","0","0","129","286","500","0","false","宋体","0"] -["149","218","0.9-0","0.5","●","0","0","149","218","500","0","false","宋体","0"] -["120","195","0.9-0","0.5","●","0","0","120","195","500","0","false","宋体","0"] -["135","115","0.9-0","0.5","●","0","0","135","115","500","0","false","宋体","0"] -["122","17","0.9-0","0.5","●","0","0","122","17","500","0","false","宋体","0"] -["115","357","0.9-0","0.5","●","0","0","115","357","500","0","false","黑体","0"] -["138","303","0.9-0","0.5","●","0","0","138","303","500","0","false","黑体","0"] -["117","231","0.9-0","0.5","●","0","0","117","231","500","0","false","黑体","0"] -["158","198","0.9-0","0.5","●","0","0","158","198","500","0","false","黑体","0"] -["123","110","0.9-0","0.5","●","0","0","123","110","500","0","false","黑体","0"] -["151","33","0.9-0","0.5","●","0","0","151","33","500","0","false","黑体","0"] -["118","10","0.9-0","0.5","●","0","0","118","10","500","0","false","黑体","0"] -["203","349","0.9-0","0.5","●","0","0","203","349","500","0","false","宋体","0"] -["184","270","0.9-0","0.5","●","0","0","184","270","500","0","false","宋体","0"] -["210","201","0.9-0","0.5","●","0","0","210","201","500","0","false","宋体","0"] -["182","182","0.9-0","0.5","●","0","0","182","182","500","0","false","宋体","0"] -["197","100","0.9-0","0.5","●","0","0","197","100","500","0","false","宋体","0"] -["179","24","0.9-0","0.5","●","0","0","179","24","500","0","false","宋体","0"] -["152","352","0.9-0","0.5","●","0","0","152","352","500","0","false","宋体","0"] -["121","284","0.9-0","0.5","●","0","0","121","284","500","0","false","宋体","0"] -["165","212","0.9-0","0.5","●","0","0","165","212","500","0","false","宋体","0"] -["137","139","0.9-0","0.5","●","0","0","137","139","500","0","false","宋体","0"] -["165","99","0.9-0","0.5","●","0","0","165","99","500","0","false","宋体","0"] -["123","23","0.9-0","0.5","●","0","0","123","23","500","0","false","宋体","0"] -["83","351","0.9-0","0.5","●","0","0","83","351","500","0","false","宋体","0"] -["118","291","0.9-0","0.5","●","0","0","118","291","500","0","false","宋体","0"] -["88","213","0.9-0","0.5","●","0","0","88","213","500","0","false","宋体","0"] -["119","180","0.9-0","0.5","●","0","0","119","180","500","0","false","宋体","0"] -["100","120","0.9-0","0.5","●","0","0","100","120","500","0","false","宋体","0"] -["122","42","0.9-0","0.5","●","0","0","122","42","500","0","false","宋体","0"] -["99","19","0.9-0","0.5","●","0","0","99","19","500","0","false","宋体","0"] -["189","340","0.9-0","0.5","●","0","0","189","340","500","0","false","宋体","0"] -["167","271","0.9-0","0.5","●","0","0","167","271","500","0","false","宋体","0"] -["195","225","0.9-0","0.5","●","0","0","195","225","500","0","false","宋体","0"] -["172","136","0.9-0","0.5","●","0","0","172","136","500","0","false","宋体","0"] -["196","59","0.9-0","0.5","●","0","0","196","59","500","0","false","宋体","0"] -["173","21","0.9-0","0.5","●","0","0","173","21","500","0","false","宋体","0"] -["261","326","0.9-0","0.5","●","0","0","261","326","500","0","false","黑体","0"] -["242","256","0.9-0","0.5","●","0","0","242","256","500","0","false","黑体","0"] -["271","204","0.9-0","0.5","●","0","0","271","204","500","0","false","黑体","0"] -["260","132","0.9-0","0.5","●","0","0","260","132","500","0","false","黑体","0"] -["240","51","0.9-0","0.5","●","0","0","240","51","500","0","false","黑体","0"] -["278","21","0.9-0","0.5","●","0","0","278","21","500","0","false","黑体","0"] -["372","341","0.9-0","0.5","●","0","0","372","341","500","0","false","黑体","0"] -["347","281","0.9-0","0.5","●","0","0","347","281","500","0","false","黑体","0"] -["371","206","0.9-0","0.5","●","0","0","371","206","500","0","false","黑体","0"] -["359","144","0.9-0","0.5","●","0","0","359","144","500","0","false","黑体","0"] -["376","64","0.9-0","0.5","●","0","0","376","64","500","0","false","黑体","0"] -["337","28","0.9-0","0.5","●","0","0","337","28","500","0","false","黑体","0"] -["428","344","0.9-0","0.5","●","0","0","428","344","500","0","false","宋体","0"] -["468","323","0.9-0","0.5","●","0","0","468","323","500","0","false","宋体","0"] -["440","294","0.9-0","0.5","●","0","0","440","294","500","0","false","宋体","0"] -["463","239","0.9-0","0.5","●","0","0","463","239","500","0","false","宋体","0"] -["430","179","0.9-0","0.5","●","0","0","430","179","500","0","false","宋体","0"] -["455","114","0.9-0","0.5","●","0","0","455","114","500","0","false","宋体","0"] -["437","74","0.9-0","0.5","●","0","0","437","74","500","0","false","宋体","0"] -["462","42","0.9-0","0.5","●","0","0","462","42","500","0","false","宋体","0"] -["420","10","0.9-0","0.5","●","0","0","420","10","500","0","false","宋体","0"] -["443","351","0.9-0","0.5","●","0","0","443","351","500","0","false","宋体","0"] -["469","277","0.9-0","0.5","●","0","0","469","277","500","0","false","宋体","0"] -["440","203","0.9-0","0.5","●","0","0","440","203","500","0","false","宋体","0"] -["461","148","0.9-0","0.5","●","0","0","461","148","500","0","false","宋体","0"] -["451","69","0.9-0","0.5","●","0","0","451","69","500","0","false","宋体","0"] -["473","32","0.9-0","0.5","●","0","0","473","32","500","0","false","宋体","0"] -["355","335","0.9-0","0.5","●","0","0","355","335","500","0","false","宋体","0"] -["378","268","0.9-0","0.5","●","0","0","378","268","500","0","false","宋体","0"] -["356","235","0.9-0","0.5","●","0","0","356","235","500","0","false","宋体","0"] -["377","173","0.9-0","0.5","●","0","0","377","173","500","0","false","宋体","0"] -["354","117","0.9-0","0.5","●","0","0","354","117","500","0","false","宋体","0"] -["394","60","0.9-0","0.5","●","0","0","394","60","500","0","false","宋体","0"] -["350","18","0.9-0","0.5","●","0","0","350","18","500","0","false","宋体","0"] -["457","306","0.9-0","0.5","●","0","0","457","306","500","0","false","黑体","0"] -["435","241","0.9-0","0.5","●","0","0","435","241","500","0","false","黑体","0"] -["461","182","0.9-0","0.5","●","0","0","461","182","500","0","false","黑体","0"] -["433","142","0.9-0","0.5","●","0","0","433","142","500","0","false","黑体","0"] -["468","67","0.9-0","0.5","●","0","0","468","67","500","0","false","黑体","0"] -["437","23","0.9-0","0.5","●","0","0","437","23","500","0","false","黑体","0"] -["240","314","0.9-0","0.5","●","0","0","240","314","500","0","false","宋体","0"] -["274","250","0.9-0","0.5","●","0","0","274","250","500","0","false","宋体","0"] -["255","192","0.9-0","0.5","●","0","0","255","192","500","0","false","宋体","0"] -["229","124","0.9-0","0.5","●","0","0","229","124","500","0","false","宋体","0"] -["263","73","0.9-0","0.5","●","0","0","263","73","500","0","false","宋体","0"] -["244","18","0.9-0","0.5","●","0","0","244","18","500","0","false","宋体","0"] -["200","342","0.9-0","0.5","●","0","0","200","342","500","0","false","宋体","0"] -["181","275","0.9-0","0.5","●","0","0","181","275","500","0","false","宋体","0"] -["213","237","0.9-0","0.5","●","0","0","213","237","500","0","false","宋体","0"] -["192","148","0.9-0","0.5","●","0","0","192","148","500","0","false","宋体","0"] -["163","64","0.9-0","0.5","●","0","0","163","64","500","0","false","宋体","0"] -["196","15","0.9-0","0.5","●","0","0","196","15","500","0","false","宋体","0"] -["189","361","0.9-0","0.5","●","0","0","189","361","500","0","false","宋体","0"] -["246","350","0.9-0","0.5","●","0","0","246","350","500","0","false","宋体","0"] -["275","291","0.9-0","0.5","●","0","0","275","291","500","0","false","宋体","0"] -["259","228","0.9-0","0.5","●","0","0","259","228","500","0","false","宋体","0"] -["289","205","0.9-0","0.5","●","0","0","289","205","500","0","false","宋体","0"] -["271","124","0.9-0","0.5","●","0","0","271","124","500","0","false","宋体","0"] -["242","43","0.9-0","0.5","●","0","0","242","43","500","0","false","宋体","0"] -["278","12","0.9-0","0.5","●","0","0","278","12","500","0","false","宋体","0"] -["416","345","0.9-0","0.5","●","0","0","416","345","500","0","false","宋体","0"] -["391","282","0.9-0","0.5","●","0","0","391","282","500","0","false","宋体","0"] -["419","225","0.9-0","0.5","●","0","0","419","225","500","0","false","宋体","0"] -["407","167","0.9-0","0.5","●","0","0","407","167","500","0","false","宋体","0"] -["387","72","0.9-0","0.5","●","0","0","387","72","500","0","false","宋体","0"] -["420","14","0.9-0","0.5","●","0","0","420","14","500","0","false","宋体","0"] -["178","314","0.9-0","0.5","●","0","0","178","314","500","0","false","宋体","0"] -["205","245","0.9-0","0.5","●","0","0","205","245","500","0","false","宋体","0"] -["173","208","0.9-0","0.5","●","0","0","173","208","500","0","false","宋体","0"] -["205","143","0.9-0","0.5","●","0","0","205","143","500","0","false","宋体","0"] -["175","71","0.9-0","0.5","●","0","0","175","71","500","0","false","宋体","0"] -["207","23","0.9-0","0.5","●","0","0","207","23","500","0","false","宋体","0"] -["428","347","0.9-0","0.5","●","0","0","428","347","500","0","false","黑体","0"] -["405","282","0.9-0","0.5","●","0","0","405","282","500","0","false","黑体","0"] -["431","226","0.9-0","0.5","●","0","0","431","226","500","0","false","黑体","0"] -["420","147","0.9-0","0.5","●","0","0","420","147","500","0","false","黑体","0"] -["434","78","0.9-0","0.5","●","0","0","434","78","500","0","false","黑体","0"] -["396","14","0.9-0","0.5","●","0","0","396","14","500","0","false","黑体","0"] -["342","317","0.9-0","0.5","●","0","0","342","317","500","0","false","黑体","0"] -["308","272","0.9-0","0.5","●","0","0","308","272","500","0","false","黑体","0"] -["342","210","0.9-0","0.5","●","0","0","342","210","500","0","false","黑体","0"] -["321","151","0.9-0","0.5","●","0","0","321","151","500","0","false","黑体","0"] -["335","89","0.9-0","0.5","●","0","0","335","89","500","0","false","黑体","0"] -["309","29","0.9-0","0.5","●","0","0","309","29","500","0","false","黑体","0"] -["349","5","0.9-0","0.5","●","0","0","349","5","500","0","false","黑体","0"] -["303","323","0.9-0","0.5","●","0","0","303","323","500","0","false","宋体","0"] -["328","262","0.9-0","0.5","●","0","0","328","262","500","0","false","宋体","0"] -["309","191","0.9-0","0.5","●","0","0","309","191","500","0","false","宋体","0"] -["344","119","0.9-0","0.5","●","0","0","344","119","500","0","false","宋体","0"] -["323","56","0.9-0","0.5","●","0","0","323","56","500","0","false","宋体","0"] -["337","3","0.9-0","0.5","●","0","0","337","3","500","0","false","宋体","0"] -["239","360","0.9-0","0.5","●","0","0","239","360","500","0","false","宋体","0"] -["214","297","0.9-0","0.5","●","0","0","214","297","500","0","false","宋体","0"] -["247","258","0.9-0","0.5","●","0","0","247","258","500","0","false","宋体","0"] -["233","189","0.9-0","0.5","●","0","0","233","189","500","0","false","宋体","0"] -["207","116","0.9-0","0.5","●","0","0","207","116","500","0","false","宋体","0"] -["256","76","0.9-0","0.5","●","0","0","256","76","500","0","false","宋体","0"] -["242","11","0.9-0","0.5","●","0","0","242","11","500","0","false","宋体","0"] -["72","335","0.9-0","0.5","●","0","0","72","335","500","0","true","黑体","0"] -["53","262","0.9-0","0.5","●","0","0","53","262","500","0","true","黑体","0"] -["81","228","0.9-0","0.5","●","0","0","81","228","500","0","true","黑体","0"] -["70","152","0.9-0","0.5","●","0","0","70","152","500","0","true","黑体","0"] -["56","76","0.9-0","0.5","●","0","0","56","76","500","0","true","黑体","0"] -["90","36","0.9-0","0.5","●","0","0","90","36","500","0","true","黑体","0"] -["172","333","0.9-0","0.5","●","0","0","172","333","500","0","false","黑体","0"] -["147","262","0.9-0","0.5","●","0","0","147","262","500","0","false","黑体","0"] -["171","198","0.9-0","0.5","●","0","0","171","198","500","0","false","黑体","0"] -["134","166","0.9-0","0.5","●","0","0","134","166","500","0","false","黑体","0"] -["164","79","0.9-0","0.5","●","0","0","164","79","500","0","false","黑体","0"] -["143","28","0.9-0","0.5","●","0","0","143","28","500","0","false","黑体","0"] -["104","349","0.9-0","0.5","●","0","0","104","349","500","0","false","宋体","0"] -["76","279","0.9-0","0.5","●","0","0","76","279","500","0","false","宋体","0"] -["103","236","0.9-0","0.5","●","0","0","103","236","500","0","false","宋体","0"] -["82","162","0.9-0","0.5","●","0","0","82","162","500","0","false","宋体","0"] -["112","93","0.9-0","0.5","●","0","0","112","93","500","0","false","宋体","0"] -["73","15","0.9-0","0.5","●","0","0","73","15","500","0","false","宋体","0"] -["164","333","0.9-0","0.5","●","0","0","164","333","500","0","false","宋体","0"] -["143","267","0.9-0","0.5","●","0","0","143","267","500","0","false","宋体","0"] -["171","243","0.9-0","0.5","●","0","0","171","243","500","0","false","宋体","0"] -["155","170","0.9-0","0.5","●","0","0","155","170","500","0","false","宋体","0"] -["182","97","0.9-0","0.5","●","0","0","182","97","500","0","false","宋体","0"] -["139","32","0.9-0","0.5","●","0","0","139","32","500","0","false","宋体","0"] -["172","5","0.9-0","0.5","●","0","0","172","5","500","0","false","宋体","0"] -["24","44","0-1","2","●","0","0","24","44","500","0","false","黑体","0"] -["24","44","1-1","4.5","●","0","0","385","44","4500","0","false","黑体","0"] -["119","84","1-1","4.5","君が生まれた朝","0","0","119","84","500","0","false","黑体","0"] -["118","160","1-1","0.5","●","0","0","118","235","500","0","false","黑体","0"] -["120","148","1-1","0.5","▲","0","0","120","223","500","0","false","黑体","0"] -["121","258","1-0","0.5","●","0","0","93","242","500","0","false","黑体","0"] -["121","258","1-0","0.5","●","0","0","85","253","500","0","false","黑体","0"] -["131","257","1-0","0.5","●","0","0","173","247","500","0","false","黑体","0"] -["131","257","1-0","0.4","●","0","0","163","251","400","0","false","黑体","0"] -["115","262","1-1","4.5","泣き蟲","0","0","115","262","500","0","true","黑体","0"] -["115","262","1-1","4.5","泣","0","0","115","262","500","0","true","黑体","0"] -["115","262","1-1","4.5","泣","0","0","115","262","500","0","true","黑体","0"] -["115","262","1-1","4.5","泣","0","0","115","262","500","0","true","黑体","0"] -["115","262","1-1","4.5","泣","0","0","115","262","500","0","true","黑体","0"] -["115","262","1-1","4.5","泣","0","0","115","262","500","0","true","黑体","0"] -["115","262","1-1","4.5","泣","0","0","115","262","500","0","true","黑体","0"] -["115","262","1-1","4.5","泣","0","0","115","262","500","0","true","黑体","0"] -["115","262","1-1","4.5"," き","0","0","115","262","500","0","true","黑体","0"] -["115","262","1-1","4.5"," き","0","0","115","262","500","0","true","黑体","0"] -["115","262","1-1","4.5"," き","0","0","115","262","500","0","true","黑体","0"] -["115","262","1-1","4.5"," き","0","0","115","262","500","0","true","黑体","0"] -["115","262","1-1","4.5"," き","0","0","115","262","500","0","true","黑体","0"] -["115","262","1-1","4.5"," き","0","0","115","262","500","0","true","黑体","0"] -["115","262","1-1","4.5"," き","0","0","115","262","500","0","true","黑体","0"] -["115","262","1-1","4.5","  蟲","0","0","115","262","500","0","true","黑体","0"] -["115","262","1-1","4.5","  蟲","0","0","115","262","500","0","true","黑体","0"] -["115","262","1-1","4.5","  蟲","0","0","115","262","500","0","true","黑体","0"] -["115","262","1-1","4.5","  蟲","0","0","115","262","500","0","true","黑体","0"] -["115","262","1-1","4.5","  蟲","0","0","115","262","500","0","true","黑体","0"] -["115","262","1-1","4.5","  蟲","0","0","115","262","500","0","true","黑体","0"] -["115","262","1-1","4.5","  蟲","0","0","115","262","500","0","true","黑体","0"] -["173","304","0-1","4.5","だった","0","0","173","304","500","0","true","黑体","0"] -["284","287","0-1","2","私は","0","0","284","287","500","0","true","黑体","0"] -["281","284","0-1","2","私は","0","0","281","284","500","0","true","黑体","0"] -["284","287","1-1","2.5","私は","0","0","284","287","500","0","true","黑体","0"] -["281","284","1-1","2.5","私は","0","0","281","284","500","0","true","黑体","0"] -["347","212","1-1","4.5","小さくても","340","0","292","234","1500","0","true","黑体","0"] -["347","212","1-1","4.5","小さく","340","0","292","234","1500","0","true","黑体","0"] -["347","212","1-1","4.5","/n  姉となった—","340","0","292","234","1500","0","true","黑体","0"] -["347","212","1-1","4.5","/n  姉","340","0","292","234","1500","0","true","黑体","0"] -["106","90","0-1","2","嬉し","0","0","106","90","500","0","true","黑体","0"] -["103","87","0-1","2","嬉し","0","0","103","87","500","0","true","黑体","0"] -["106","90","1-1","2.5","嬉し","0","0","106","90","500","0","true","黑体","0"] -["103","87","1-1","2.5","嬉し","0","0","103","87","500","0","true","黑体","0"] -["149","135","0-1","4.5","くて","0","0","149","135","500","0","true","黑体","0"] -["125","234","0-1","4.5","少し","0","0","125","234","500","0","true","黑体","0"] -["198","217","0-1","2","照れ","0","0","198","217","500","0","true","黑体","0"] -["195","214","0-1","2","照れ","0","0","195","214","500","0","true","黑体","0"] -["198","217","1-1","2","照れ","0","0","198","217","500","0","true","黑体","0"] -["195","214","1-1","2","照れ","0","0","195","214","500","0","true","黑体","0"] -["247","258","1-0","3","くさ","340","0","247","258","500","0","true","黑体","0"] -["302","239","1-0","3","くて","310","0","302","239","500","0","true","黑体","0"] -["0","-75","1-1","5","██████████████████████","28","0","0","-75","500","0","false","黑体","0"] -["0","-75","1-1","5"," ██████████████████████","28","0","0","-75","500","0","false","黑体","0"] -["186","42","1-1","5","██████████","37","0","186","42","500","0","false","黑体","0"] -["186","42","1-1","5"," ██████████","37","0","186","42","500","0","false","黑体","0"] -["325","122","1-1","5","██████","32","0","325","122","500","0","false","黑体","0"] -["325","122","1-1","5"," ██████","32","0","325","122","500","0","false","黑体","0"] -["0","-90","1-1","5","██████████████████████","40","0","0","-90","500","0","false","黑体","0"] -["0","-90","1-1","5"," ██████████████████████","40","0","0","-90","500","0","false","黑体","0"] -["295","131","1-1","5","█","0","0","295","131","500","0","false","黑体","0"] -["328","103","1-1","5","とても/n","30","25","328","103","500","0","true","黑体","0"] -["344","160","1-1","5","誇","40","30","344","160","500","0","true","黑体","0"] -["344","160","1-1","5"," らし","40","30","344","160","500","0","true","黑体","0"] -["407","203","1-1","5","か","40","30","407","203","500","0","true","黑体","0"] -["347","159","1-1","5","   っ","40","30","347","159","500","0","true","黑体","0"] -["347","159","1-1","5","    た","40","30","347","159","500","0","true","黑体","0"] -["0","-160","1-1","0.2"," ████████████/n ████████████","34","0","0","-160","500","0","false","黑体","0"] -["0","-160","1-1","0.2","████████████/n████████████","34","0","0","-160","500","0","false","黑体","0"] -["518","342","1-1","0.5","█","0","0","518","342","500","0","false","黑体","0"] -["0","-160","1-1","0.1","████████████","33","0","0","-160","500","0","false","黑体","0"] -["0","-160","1-1","0.1"," ████████████","33","0","0","-160","500","0","false","黑体","0"] -["0","-160","1-1","0.1","/n ████████████","35","0","0","-160","500","0","false","黑体","0"] -["0","-160","1-1","0.1","/n████████████","35","0","0","-160","500","0","false","黑体","0"] -["0","-160","1-1","0.1","████████████","32","0","0","-160","500","0","false","黑体","0"] -["0","-160","1-1","0.1"," ████████████","32","0","0","-160","500","0","false","黑体","0"] -["0","-160","1-1","0.1","/n ████████████","36","0","0","-160","500","0","false","黑体","0"] -["0","-160","1-1","0.1","/n████████████","36","0","0","-160","500","0","false","黑体","0"] -["0","-160","1-1","0.1","████████████","31","0","0","-160","500","0","false","黑体","0"] -["0","-160","1-1","0.1"," ████████████","31","0","0","-160","500","0","false","黑体","0"] -["0","-160","1-1","0.1","/n ████████████","37","0","0","-160","500","0","false","黑体","0"] -["0","-160","1-1","0.1","/n████████████","37","0","0","-160","500","0","false","黑体","0"] -["0","-160","1-1","0.1","████████████","30","0","0","-160","500","0","false","黑体","0"] -["0","-160","1-1","0.1"," ████████████","30","0","0","-160","500","0","false","黑体","0"] -["0","-160","1-1","0.1","/n ████████████","38","0","0","-160","500","0","false","黑体","0"] -["0","-160","1-1","0.1","/n████████████","38","0","0","-160","500","0","false","黑体","0"] -["0","-160","1-1","0.1","████████████","29","0","0","-160","500","0","false","黑体","0"] -["0","-160","1-1","0.1"," ████████████","29","0","0","-160","500","0","false","黑体","0"] -["0","-160","1-1","0.1","/n ████████████","39","0","0","-160","500","0","false","黑体","0"] -["0","-160","1-1","0.1","/n████████████","39","0","0","-160","500","0","false","黑体","0"] -["0","-160","1-1","0.1","████████████","28","0","0","-160","500","0","false","黑体","0"] -["0","-160","1-1","0.1"," ████████████","28","0","0","-160","500","0","false","黑体","0"] -["0","-160","1-1","0.1","/n ████████████","40","0","0","-160","500","0","false","黑体","0"] -["0","-160","1-1","0.1","/n████████████","40","0","0","-160","500","0","false","黑体","0"] -["101","111","0-1","0.5","苦しみに揺蕩う","0","0","101","111","500","0","false","宋体","0"] -["101","111","1-1","2","苦しみに揺蕩う","0","0","101","111","500","0","false","宋体","0"] -["101","111","1-0","0.5","苦しみに揺蕩う","0","0","101","111","500","0","false","宋体","0"] -["400","118","1-1","0.5","_________________","0","0","100","118","500","0","true","宋体","0"] -["100","118","1-1","2","_________________","0","0","45","118","2000","0","true","宋体","0"] -["45","118","1-1","0.5","_________________","0","0","-365","118","500","0","true","宋体","0"] -["-365","77","1-1","0.5","_________________","0","0","45","77","500","0","true","宋体","0"] -["45","77","1-1","2","_________________","0","0","100","77","2000","0","true","宋体","0"] -["100","77","1-1","0.5","_________________","0","0","400","77","500","0","true","宋体","0"] -["341","-212","1-1","0.5","___","90","0","341","88","500","0","true","黑体","0"] -["341","88","1-1","2","___","90","0","341","109","2000","0","true","黑体","0"] -["341","109","1-1","0.5","___","90","0","341","409","500","0","true","黑体","0"] -["128","409","1-1","0.5","___","90","0","128","109","500","0","true","黑体","0"] -["128","109","1-1","2","___","90","0","128","88","2000","0","true","黑体","0"] -["128","88","1-1","0.5","___","90","0","128","-212","500","0","true","黑体","0"] -["235","205","0-1","0.5","生存の荒野を","0","0","235","205","500","0","true","黑体","0"] -["235","205","1-1","2","生存の荒野を","0","0","235","205","500","0","true","黑体","0"] -["235","205","1-0","0.5","生存の荒野を","0","0","235","205","500","0","true","黑体","0"] -["-135","170","1-1","0.5","________________","0","0","165","170","500","0","true","黑体","0"] -["165","170","1-1","2","________________","0","0","230","170","2000","0","true","黑体","0"] -["230","170","1-1","0.5","________________","0","0","530","170","500","0","true","黑体","0"] -["530","213","1-1","0.5","________________","0","0","230","213","500","0","true","黑体","0"] -["230","213","1-1","2","________________","0","0","165","213","2000","0","true","黑体","0"] -["165","213","1-1","0.5","________________","0","0","-135","213","500","0","true","黑体","0"] -["445","-119","1-1","0.5","___","90","0","445","181","500","0","true","黑体","0"] -["445","181","1-1","2","___","90","0","445","204","2000","0","true","黑体","0"] -["445","204","1-1","0.5","___","90","0","445","504","500","0","true","黑体","0"] -["263","504","1-1","0.5","___","90","0","263","204","500","0","true","黑体","0"] -["263","204","1-1","2","___","90","0","263","181","2000","0","true","黑体","0"] -["263","181","1-1","0.5","___","90","0","263","-119","500","0","true","黑体","0"] -["800","132","1-1","0.5","『","0","0","150","132","500","0","true","黑体","1"] -["150","132","1-1","0.9","『","0","0","160","140","900","0","true","黑体","1"] -["160","140","1-1","0.9","『","0","0","153","135","900","0","true","黑体","1"] -["153","135","1-1","0.5","『","0","0","-497","135","500","0","true","黑体","1"] -["800","132","1-1","0.5"," 美","0","0","150","132","500","0","true","黑体","1"] -["150","132","1-1","0.9"," 美","0","0","160","140","900","0","true","黑体","1"] -["160","140","1-1","0.9"," 美","0","0","153","135","900","0","true","黑体","1"] -["153","135","1-1","0.5"," 美","0","0","-497","135","500","0","true","黑体","1"] -["800","132","1-1","0.5","  し","0","0","150","132","500","0","true","黑体","1"] -["150","132","1-1","0.9","  し","0","0","160","140","900","0","true","黑体","1"] -["160","140","1-1","0.9","  し","0","0","153","135","900","0","true","黑体","1"] -["153","135","1-1","0.5","  し","0","0","-497","135","500","0","true","黑体","1"] -["800","132","1-1","0.5","   き","0","0","150","132","500","0","true","黑体","1"] -["150","132","1-1","0.9","   き","0","0","160","140","900","0","true","黑体","1"] -["160","140","1-1","0.9","   き","0","0","153","135","900","0","true","黑体","1"] -["153","135","1-1","0.5","   き","0","0","-497","135","500","0","true","黑体","1"] -["800","132","1-1","0.5","    も","0","0","150","132","500","0","true","黑体","1"] -["150","132","1-1","0.9","    も","0","0","160","140","900","0","true","黑体","1"] -["160","140","1-1","0.9","    も","0","0","153","135","900","0","true","黑体","1"] -["153","135","1-1","0.5","    も","0","0","-497","135","500","0","true","黑体","1"] -["800","132","1-1","0.5","     の","0","0","150","132","500","0","true","黑体","1"] -["150","132","1-1","0.9","     の","0","0","160","140","900","0","true","黑体","1"] -["160","140","1-1","0.9","     の","0","0","153","135","900","0","true","黑体","1"] -["153","135","1-1","0.5","     の","0","0","-497","135","500","0","true","黑体","1"] -["800","132","1-1","0.5","      』","0","0","150","132","500","0","true","黑体","1"] -["150","132","1-1","0.9","      』","0","0","160","140","900","0","true","黑体","1"] -["160","140","1-1","0.9","      』","0","0","153","135","900","0","true","黑体","1"] -["153","135","1-1","0.5","      』","0","0","-497","135","500","0","true","黑体","1"] -["-800","92","1-1","4.5","______________________","0","0","800","92","500","0","true","黑体","0"] -["-800","110","1-1","4.5","______________________","0","0","800","110","500","0","true","黑体","0"] -["-800","144","1-1","4.5","______________________","0","0","800","144","500","0","true","黑体","0"] -["-800","117","1-1","4.5","______________________","0","0","800","117","500","0","true","黑体","0"] -["-800","160","1-1","4.5","______________________","0","0","800","160","500","0","true","黑体","0"] -["-800","126","1-1","4.5","______________________","0","0","800","126","500","0","true","黑体","0"] -["-800","106","1-1","4.5","______________________","0","0","800","106","500","0","true","黑体","0"] -["-800","186","1-1","4.5","______________________","0","0","800","186","500","0","true","黑体","0"] -["-800","106","1-1","4.5","______________________","0","0","800","106","500","0","true","黑体","0"] -["-800","220","1-1","4.5","______________________","0","0","800","220","500","0","true","黑体","0"] -["-800","207","1-1","4.5","______________________","0","0","800","207","500","0","true","黑体","0"] -["-800","188","1-1","4.5","______________________","0","0","800","188","500","0","true","黑体","0"] -["-800","144","1-1","4.5","______________________","0","0","800","144","500","0","true","黑体","0"] -["-800","180","1-1","4.5","______________________","0","0","800","180","500","0","true","黑体","0"] -["-800","200","1-1","4.5","______________________","0","0","800","200","500","0","true","黑体","0"] -["-800","160","1-1","4.5","______________________","0","0","800","160","500","0","true","黑体","0"] -["-800","184","1-1","4.5","______________________","0","0","800","184","500","0","true","黑体","0"] -["-800","129","1-1","4.5","______________________","0","0","800","129","500","0","true","黑体","0"] -["768","199","1-1","0.5","探","0","0","118","199","500","0","true","黑体","0"] -["118","199","1-1","0.9","探","0","0","121","195","1200","0","true","黑体","0"] -["121","195","1-1","0.9","探","0","0","118","197","1200","0","true","黑体","0"] -["118","197","1-1","0.9","探","0","0","116","198","1200","0","true","黑体","0"] -["116","198","1-1","0.5","探","0","0","-534","198","500","0","true","黑体","0"] -["768","199","1-1","0.5"," す","0","0","118","199","500","0","true","黑体","0"] -["118","199","1-1","0.9"," す","0","0","121","195","1200","0","true","黑体","0"] -["121","195","1-1","0.9"," す","0","0","118","197","1200","0","true","黑体","0"] -["118","197","1-1","0.9"," す","0","0","116","198","1200","0","true","黑体","0"] -["116","198","1-1","0.5"," す","0","0","-534","198","500","0","true","黑体","0"] -["768","199","1-1","0.5","  よ","0","0","118","199","500","0","true","黑体","0"] -["118","199","1-1","0.9","  よ","0","0","121","195","1200","0","true","黑体","0"] -["121","195","1-1","0.9","  よ","0","0","118","197","1200","0","true","黑体","0"] -["118","197","1-1","0.9","  よ","0","0","116","198","1200","0","true","黑体","0"] -["116","198","1-1","0.5","  よ","0","0","-534","198","500","0","true","黑体","0"] -["768","199","1-1","0.5","   う","0","0","118","199","500","0","true","黑体","0"] -["118","199","1-1","0.9","   う","0","0","121","195","1200","0","true","黑体","0"] -["121","195","1-1","0.9","   う","0","0","118","197","1200","0","true","黑体","0"] -["118","197","1-1","0.9","   う","0","0","116","198","1200","0","true","黑体","0"] -["116","198","1-1","0.5","   う","0","0","-534","198","500","0","true","黑体","0"] -["768","199","1-1","0.5","    に","0","0","118","199","500","0","true","黑体","0"] -["118","199","1-1","0.9","    に","0","0","121","195","1200","0","true","黑体","0"] -["121","195","1-1","0.9","    に","0","0","118","197","1200","0","true","黑体","0"] -["118","197","1-1","0.9","    に","0","0","116","198","1200","0","true","黑体","0"] -["116","198","1-1","0.5","    に","0","0","-534","198","500","0","true","黑体","0"] -["768","199","1-1","0.5","     駈","0","0","118","199","500","0","true","黑体","0"] -["118","199","1-1","0.9","     駈","0","0","121","195","1200","0","true","黑体","0"] -["121","195","1-1","0.9","     駈","0","0","118","197","1200","0","true","黑体","0"] -["118","197","1-1","0.9","     駈","0","0","116","198","1200","0","true","黑体","0"] -["116","198","1-1","0.5","     駈","0","0","-534","198","500","0","true","黑体","0"] -["768","199","1-1","0.5","      け","0","0","118","199","500","0","true","黑体","0"] -["118","199","1-1","0.9","      け","0","0","121","195","1200","0","true","黑体","0"] -["121","195","1-1","0.9","      け","0","0","118","197","1200","0","true","黑体","0"] -["118","197","1-1","0.9","      け","0","0","116","198","1200","0","true","黑体","0"] -["116","198","1-1","0.5","      け","0","0","-534","198","500","0","true","黑体","0"] -["768","199","1-1","0.5","       抜","0","0","118","199","500","0","true","黑体","0"] -["118","199","1-1","0.9","       抜","0","0","121","195","1200","0","true","黑体","0"] -["121","195","1-1","0.9","       抜","0","0","118","197","1200","0","true","黑体","0"] -["118","197","1-1","0.9","       抜","0","0","116","198","1200","0","true","黑体","0"] -["116","198","1-1","0.5","       抜","0","0","-534","198","500","0","true","黑体","0"] -["768","199","1-1","0.5","        け","0","0","118","199","500","0","true","黑体","0"] -["118","199","1-1","0.9","        け","0","0","121","195","1200","0","true","黑体","0"] -["121","195","1-1","0.9","        け","0","0","118","197","1200","0","true","黑体","0"] -["118","197","1-1","0.9","        け","0","0","116","198","1200","0","true","黑体","0"] -["116","198","1-1","0.5","        け","0","0","-534","198","500","0","true","黑体","0"] -["768","199","1-1","0.5","         た","0","0","118","199","500","0","true","黑体","0"] -["118","199","1-1","0.9","         た","0","0","121","195","1200","0","true","黑体","0"] -["121","195","1-1","0.9","         た","0","0","118","197","1200","0","true","黑体","0"] -["118","197","1-1","0.9","         た","0","0","116","198","1200","0","true","黑体","0"] -["116","198","1-1","0.5","         た","0","0","-534","198","500","0","true","黑体","0"] -["135","112","1-0","0.5","果てしなき地平へ","0","0","135","112","500","0","true","黑体","0"] -["133","120","1-0","0.5","_______________","0","0","133","120","500","0","true","黑体","0"] -["138","109","1-0","0.5","果てしなき地平へ","0","0","138","109","500","0","true","黑体","0"] -["137","115","1-0","0.5","_______________","0","0","137","115","500","0","true","黑体","0"] -["141","106","1-0","0.5","果てしなき地平へ","0","0","141","106","500","0","true","黑体","0"] -["141","110","1-0","0.5","_______________","0","0","141","110","500","0","true","黑体","0"] -["145","103","1-0","0.5","果てしなき地平へ","0","0","145","103","500","0","true","黑体","0"] -["145","105","1-0","0.5","_______________","0","0","141","105","500","0","true","黑体","0"] -["149","100","1-0","0.5","果てしなき地平へ","0","0","149","100","500","0","true","黑体","0"] -["149","100","1-0","0.5","_______________","0","0","149","100","500","0","true","黑体","0"] -["154","97","1-0","0.5","果てしなき地平へ","0","0","154","97","500","0","true","黑体","0"] -["154","98","1-0","0.5","_______________","0","0","154","98","500","0","true","黑体","0"] -["158","95","1-0","0.5","果てしなき地平へ","0","0","158","95","500","0","true","黑体","0"] -["158","96","1-0","0.5","_______________","0","0","158","96","500","0","true","黑体","0"] -["162","92","1-0","0.5","果てしなき地平へ","0","0","162","92","500","0","true","黑体","0"] -["162","93","1-0","0.5","_______________","0","0","162","93","500","0","true","黑体","0"] -["165","90","1-0","0.5","果てしなき地平へ","0","0","165","90","500","0","true","黑体","0"] -["165","91","1-0","0.5","_______________","0","0","165","91","500","0","true","黑体","0"] -["169","88","1-0","0.5","果てしなき地平へ","0","0","169","88","500","0","true","黑体","0"] -["169","89","1-0","0.5","_______________","0","0","169","89","500","0","true","黑体","0"] -["173","85","1-0","0.5","果てしなき地平へ","0","0","173","85","500","0","true","黑体","0"] -["173","87","1-0","0.5","_______________","0","0","173","87","500","0","true","黑体","0"] -["178","83","1-0","0.5","果てしなき地平へ","0","0","178","83","500","0","true","黑体","0"] -["178","84","1-0","0.5","_______________","0","0","178","84","500","0","true","黑体","0"] -["182","82","1-0","0.5","果てしなき地平へ","0","0","182","82","500","0","true","黑体","0"] -["182","83","1-0","0.5","_______________","0","0","182","83","500","0","true","黑体","0"] -["186","81","1-0","0.5","果てしなき地平へ","0","0","186","81","500","0","true","黑体","0"] -["186","82","1-0","0.5","_______________","0","0","186","82","500","0","true","黑体","0"] -["190","79","1-0","0.5","果てしなき地平へ","0","0","190","79","500","0","true","黑体","0"] -["190","80","1-0","0.5","_______________","0","0","190","80","500","0","true","黑体","0"] -["194","78","1-0","0.5","果てしなき地平へ","0","0","194","78","500","0","true","黑体","0"] -["194","79","1-0","0.5","_______________","0","0","194","79","500","0","true","黑体","0"] -["198","76","1-0","0.5","果てしなき地平へ","0","0","198","76","500","0","true","黑体","0"] -["198","77","1-0","0.5","_______________","0","0","198","77","500","0","true","黑体","0"] -["202","74","1-0","0.5","果てしなき地平へ","0","0","202","74","500","0","true","黑体","0"] -["202","75","1-0","0.5","_______________","0","0","202","75","500","0","true","黑体","0"] -["207","73","1-0","0.5","果てしなき地平へ","0","0","207","73","500","0","true","黑体","0"] -["207","74","1-0","0.5","_______________","0","0","207","74","500","0","true","黑体","0"] -["211","71","1-0","0.5","果てしなき地平へ","0","0","211","71","500","0","true","黑体","0"] -["211","72","1-0","0.5","_______________","0","0","211","72","500","0","true","黑体","0"] -["215","70","1-0","0.5","果てしなき地平へ","0","0","215","70","500","0","true","黑体","0"] -["215","71","1-0","0.5","_______________","0","0","215","71","500","0","true","黑体","0"] -["219","69","1-0","0.5","果てしなき地平へ","0","0","219","69","500","0","true","黑体","0"] -["219","70","1-0","0.5","_______________","0","0","219","70","500","0","true","黑体","0"] -["223","68","1-0","0.5","果てしなき地平へ","0","0","223","68","500","0","true","黑体","0"] -["223","69","1-0","0.5","_______________","0","0","223","69","500","0","true","黑体","0"] -["228","67","1-0","0.5","果てしなき地平へ","0","0","228","67","500","0","true","黑体","0"] -["228","68","1-0","0.5","_______________","0","0","228","68","500","0","true","黑体","0"] -["232","66","1-0","0.5","果てしなき地平へ","0","0","232","66","500","0","true","黑体","0"] -["232","67","1-0","0.5","_______________","0","0","232","67","500","0","true","黑体","0"] -["236","65","1-0","0.5","果てしなき地平へ","0","0","236","65","500","0","true","黑体","0"] -["236","66","1-0","0.5","_______________","0","0","236","66","500","0","true","黑体","0"] -["239","64","1-0","0.5","果てしなき地平へ","0","0","239","64","500","0","true","黑体","0"] -["239","65","1-0","0.5","_______________","0","0","239","65","500","0","true","黑体","0"] -["135","112","1-0","0.5","  旅立つ君の","0","0","135","112","500","0","true","黑体","0"] -["133","120","1-0","0.5","_______________","0","0","133","120","500","0","true","黑体","0"] -["138","109","1-0","0.5","  旅立つ君の","0","0","138","109","500","0","true","黑体","0"] -["137","115","1-0","0.5","_______________","0","0","137","115","500","0","true","黑体","0"] -["141","106","1-0","0.5","  旅立つ君の","0","0","141","106","500","0","true","黑体","0"] -["141","110","1-0","0.5","_______________","0","0","141","110","500","0","true","黑体","0"] -["145","103","1-0","0.5","  旅立つ君の","0","0","145","103","500","0","true","黑体","0"] -["145","105","1-0","0.5","_______________","0","0","141","105","500","0","true","黑体","0"] -["149","100","1-0","0.5","  旅立つ君の","0","0","149","100","500","0","true","黑体","0"] -["149","100","1-0","0.5","_______________","0","0","149","100","500","0","true","黑体","0"] -["154","97","1-0","0.5","  旅立つ君の","0","0","154","97","500","0","true","黑体","0"] -["154","98","1-0","0.5","_______________","0","0","154","98","500","0","true","黑体","0"] -["158","95","1-0","0.5","  旅立つ君の","0","0","158","95","500","0","true","黑体","0"] -["158","96","1-0","0.5","_______________","0","0","158","96","500","0","true","黑体","0"] -["162","92","1-0","0.5","  旅立つ君の","0","0","162","92","500","0","true","黑体","0"] -["162","93","1-0","0.5","_______________","0","0","162","93","500","0","true","黑体","0"] -["165","90","1-0","0.5","  旅立つ君の","0","0","165","90","500","0","true","黑体","0"] -["165","91","1-0","0.5","_______________","0","0","165","91","500","0","true","黑体","0"] -["169","88","1-0","0.5","  旅立つ君の","0","0","169","88","500","0","true","黑体","0"] -["169","89","1-0","0.5","_______________","0","0","169","89","500","0","true","黑体","0"] -["173","85","1-0","0.5","  旅立つ君の","0","0","173","85","500","0","true","黑体","0"] -["173","87","1-0","0.5","_______________","0","0","173","87","500","0","true","黑体","0"] -["178","83","1-0","0.5","  旅立つ君の","0","0","178","83","500","0","true","黑体","0"] -["178","84","1-0","0.5","_______________","0","0","178","84","500","0","true","黑体","0"] -["182","82","1-0","0.5","  旅立つ君の","0","0","182","82","500","0","true","黑体","0"] -["182","83","1-0","0.5","_______________","0","0","182","83","500","0","true","黑体","0"] -["186","81","1-0","0.5","  旅立つ君の","0","0","186","81","500","0","true","黑体","0"] -["186","82","1-0","0.5","_______________","0","0","186","82","500","0","true","黑体","0"] -["190","79","1-0","0.5","  旅立つ君の","0","0","190","79","500","0","true","黑体","0"] -["190","80","1-0","0.5","_______________","0","0","190","80","500","0","true","黑体","0"] -["194","78","1-0","0.5","  旅立つ君の","0","0","194","78","500","0","true","黑体","0"] -["194","79","1-0","0.5","_______________","0","0","194","79","500","0","true","黑体","0"] -["198","76","1-0","0.5","  旅立つ君の","0","0","198","76","500","0","true","黑体","0"] -["198","77","1-0","0.5","_______________","0","0","198","77","500","0","true","黑体","0"] -["202","74","1-0","0.5","  旅立つ君の","0","0","202","74","500","0","true","黑体","0"] -["202","75","1-0","0.5","_______________","0","0","202","75","500","0","true","黑体","0"] -["207","73","1-0","0.5","  旅立つ君の","0","0","207","73","500","0","true","黑体","0"] -["207","74","1-0","0.5","_______________","0","0","207","74","500","0","true","黑体","0"] -["211","71","1-0","0.5","  旅立つ君の","0","0","211","71","500","0","true","黑体","0"] -["211","72","1-0","0.5","_______________","0","0","211","72","500","0","true","黑体","0"] -["215","70","1-0","0.5","  旅立つ君の","0","0","215","70","500","0","true","黑体","0"] -["215","71","1-0","0.5","_______________","0","0","215","71","500","0","true","黑体","0"] -["219","69","1-0","0.5","  旅立つ君の","0","0","219","69","500","0","true","黑体","0"] -["219","70","1-0","0.5","_______________","0","0","219","70","500","0","true","黑体","0"] -["223","68","1-0","0.5","  旅立つ君の","0","0","223","68","500","0","true","黑体","0"] -["223","69","1-0","0.5","_______________","0","0","223","69","500","0","true","黑体","0"] -["228","67","1-0","0.5","  旅立つ君の","0","0","228","67","500","0","true","黑体","0"] -["228","68","1-0","0.5","_______________","0","0","228","68","500","0","true","黑体","0"] -["232","66","1-0","0.5","  旅立つ君の","0","0","232","66","500","0","true","黑体","0"] -["232","67","1-0","0.5","_______________","0","0","232","67","500","0","true","黑体","0"] -["236","65","1-0","0.5","  旅立つ君の","0","0","236","65","500","0","true","黑体","0"] -["236","66","1-0","0.5","_______________","0","0","236","66","500","0","true","黑体","0"] -["239","64","1-0","0.5","  旅立つ君の","0","0","239","64","500","0","true","黑体","0"] -["239","65","1-0","0.5","_______________","0","0","239","65","500","0","true","黑体","0"] -["135","112","1-0","0.5","   寢顔","0","0","135","112","500","0","true","黑体","0"] -["133","120","1-0","0.5","_______________","0","0","133","120","500","0","true","黑体","0"] -["138","109","1-0","0.5","   寢顔","0","0","138","109","500","0","true","黑体","0"] -["137","115","1-0","0.5","_______________","0","0","137","115","500","0","true","黑体","0"] -["141","106","1-0","0.5","   寢顔","0","0","141","106","500","0","true","黑体","0"] -["141","110","1-0","0.5","_______________","0","0","141","110","500","0","true","黑体","0"] -["145","103","1-0","0.5","   寢顔","0","0","145","103","500","0","true","黑体","0"] -["145","105","1-0","0.5","_______________","0","0","141","105","500","0","true","黑体","0"] -["149","100","1-0","0.5","   寢顔","0","0","149","100","500","0","true","黑体","0"] -["149","100","1-0","0.5","_______________","0","0","149","100","500","0","true","黑体","0"] -["154","97","1-0","0.5","   寢顔","0","0","154","97","500","0","true","黑体","0"] -["154","98","1-0","0.5","_______________","0","0","154","98","500","0","true","黑体","0"] -["158","95","1-0","0.5","   寢顔","0","0","158","95","500","0","true","黑体","0"] -["158","96","1-0","0.5","_______________","0","0","158","96","500","0","true","黑体","0"] -["162","92","1-0","0.5","   寢顔","0","0","162","92","500","0","true","黑体","0"] -["162","93","1-0","0.5","_______________","0","0","162","93","500","0","true","黑体","0"] -["165","90","1-0","0.5","   寢顔","0","0","165","90","500","0","true","黑体","0"] -["165","91","1-0","0.5","_______________","0","0","165","91","500","0","true","黑体","0"] -["169","88","1-0","0.5","   寢顔","0","0","169","88","500","0","true","黑体","0"] -["169","89","1-0","0.5","_______________","0","0","169","89","500","0","true","黑体","0"] -["173","85","1-0","0.5","   寢顔","0","0","173","85","500","0","true","黑体","0"] -["173","87","1-0","0.5","_______________","0","0","173","87","500","0","true","黑体","0"] -["178","83","1-0","0.5","   寢顔","0","0","178","83","500","0","true","黑体","0"] -["178","84","1-0","0.5","_______________","0","0","178","84","500","0","true","黑体","0"] -["182","82","1-0","0.5","   寢顔","0","0","182","82","500","0","true","黑体","0"] -["182","83","1-0","0.5","_______________","0","0","182","83","500","0","true","黑体","0"] -["186","81","1-0","0.5","   寢顔","0","0","186","81","500","0","true","黑体","0"] -["186","82","1-0","0.5","_______________","0","0","186","82","500","0","true","黑体","0"] -["190","79","1-0","0.5","   寢顔","0","0","190","79","500","0","true","黑体","0"] -["190","80","1-0","0.5","_______________","0","0","190","80","500","0","true","黑体","0"] -["194","78","1-0","0.5","   寢顔","0","0","194","78","500","0","true","黑体","0"] -["194","79","1-0","0.5","_______________","0","0","194","79","500","0","true","黑体","0"] -["198","76","1-0","0.5","   寢顔","0","0","198","76","500","0","true","黑体","0"] -["198","77","1-0","0.5","_______________","0","0","198","77","500","0","true","黑体","0"] -["202","74","1-0","0.5","   寢顔","0","0","202","74","500","0","true","黑体","0"] -["202","75","1-0","0.5","_______________","0","0","202","75","500","0","true","黑体","0"] -["207","73","1-0","0.5","   寢顔","0","0","207","73","500","0","true","黑体","0"] -["207","74","1-0","0.5","_______________","0","0","207","74","500","0","true","黑体","0"] -["211","71","1-0","0.5","   寢顔","0","0","211","71","500","0","true","黑体","0"] -["211","72","1-0","0.5","_______________","0","0","211","72","500","0","true","黑体","0"] -["215","70","1-0","0.5","   寢顔","0","0","215","70","500","0","true","黑体","0"] -["215","71","1-0","0.5","_______________","0","0","215","71","500","0","true","黑体","0"] -["219","69","1-0","0.5","   寢顔","0","0","219","69","500","0","true","黑体","0"] -["219","70","1-0","0.5","_______________","0","0","219","70","500","0","true","黑体","0"] -["223","68","1-0","0.5","   寢顔","0","0","223","68","500","0","true","黑体","0"] -["223","69","1-0","0.5","_______________","0","0","223","69","500","0","true","黑体","0"] -["228","67","1-0","0.5","   寢顔","0","0","228","67","500","0","true","黑体","0"] -["228","68","1-0","0.5","_______________","0","0","228","68","500","0","true","黑体","0"] -["232","66","1-0","0.5","   寢顔","0","0","232","66","500","0","true","黑体","0"] -["232","67","1-0","0.5","_______________","0","0","232","67","500","0","true","黑体","0"] -["236","65","1-0","0.5","   寢顔","0","0","236","65","500","0","true","黑体","0"] -["236","66","1-0","0.5","_______________","0","0","236","66","500","0","true","黑体","0"] -["239","64","1-0","0.5","   寢顔","0","0","239","64","500","0","true","黑体","0"] -["239","65","1-0","0.5","_______________","0","0","239","65","500","0","true","黑体","0"] -["135","112","1-0","0.5","  何","0","0","135","112","500","0","true","黑体","0"] -["133","120","1-0","0.5","_______________","0","0","133","120","500","0","true","黑体","0"] -["138","109","1-0","0.5","  何","0","0","138","109","500","0","true","黑体","0"] -["137","115","1-0","0.5","_______________","0","0","137","115","500","0","true","黑体","0"] -["141","106","1-0","0.5","  何","0","0","141","106","500","0","true","黑体","0"] -["141","110","1-0","0.5","_______________","0","0","141","110","500","0","true","黑体","0"] -["145","103","1-0","0.5","  何","0","0","145","103","500","0","true","黑体","0"] -["145","105","1-0","0.5","_______________","0","0","141","105","500","0","true","黑体","0"] -["149","100","1-0","0.5","  何","0","0","149","100","500","0","true","黑体","0"] -["149","100","1-0","0.5","_______________","0","0","149","100","500","0","true","黑体","0"] -["154","97","1-0","0.5","  何","0","0","154","97","500","0","true","黑体","0"] -["154","98","1-0","0.5","_______________","0","0","154","98","500","0","true","黑体","0"] -["158","95","1-0","0.5","  何","0","0","158","95","500","0","true","黑体","0"] -["158","96","1-0","0.5","_______________","0","0","158","96","500","0","true","黑体","0"] -["162","92","1-0","0.5","  何","0","0","162","92","500","0","true","黑体","0"] -["162","93","1-0","0.5","_______________","0","0","162","93","500","0","true","黑体","0"] -["165","90","1-0","0.5","  何","0","0","165","90","500","0","true","黑体","0"] -["165","91","1-0","0.5","_______________","0","0","165","91","500","0","true","黑体","0"] -["169","88","1-0","0.5","  何","0","0","169","88","500","0","true","黑体","0"] -["169","89","1-0","0.5","_______________","0","0","169","89","500","0","true","黑体","0"] -["173","85","1-0","0.5","  何","0","0","173","85","500","0","true","黑体","0"] -["173","87","1-0","0.5","_______________","0","0","173","87","500","0","true","黑体","0"] -["178","83","1-0","0.5","  何","0","0","178","83","500","0","true","黑体","0"] -["178","84","1-0","0.5","_______________","0","0","178","84","500","0","true","黑体","0"] -["182","82","1-0","0.5","  何","0","0","182","82","500","0","true","黑体","0"] -["182","83","1-0","0.5","_______________","0","0","182","83","500","0","true","黑体","0"] -["186","81","1-0","0.5","  何","0","0","186","81","500","0","true","黑体","0"] -["186","82","1-0","0.5","_______________","0","0","186","82","500","0","true","黑体","0"] -["190","79","1-0","0.5","  何","0","0","190","79","500","0","true","黑体","0"] -["190","80","1-0","0.5","_______________","0","0","190","80","500","0","true","黑体","0"] -["194","78","1-0","0.5","  何","0","0","194","78","500","0","true","黑体","0"] -["194","79","1-0","0.5","_______________","0","0","194","79","500","0","true","黑体","0"] -["198","76","1-0","0.5","  何","0","0","198","76","500","0","true","黑体","0"] -["198","77","1-0","0.5","_______________","0","0","198","77","500","0","true","黑体","0"] -["202","74","1-0","0.5","  何","0","0","202","74","500","0","true","黑体","0"] -["202","75","1-0","0.5","_______________","0","0","202","75","500","0","true","黑体","0"] -["207","73","1-0","0.5","  何","0","0","207","73","500","0","true","黑体","0"] -["207","74","1-0","0.5","_______________","0","0","207","74","500","0","true","黑体","0"] -["211","71","1-0","0.5","  何","0","0","211","71","500","0","true","黑体","0"] -["211","72","1-0","0.5","_______________","0","0","211","72","500","0","true","黑体","0"] -["215","70","1-0","0.5","  何","0","0","215","70","500","0","true","黑体","0"] -["215","71","1-0","0.5","_______________","0","0","215","71","500","0","true","黑体","0"] -["219","69","1-0","0.5","  何","0","0","219","69","500","0","true","黑体","0"] -["219","70","1-0","0.5","_______________","0","0","219","70","500","0","true","黑体","0"] -["223","68","1-0","0.5","  何","0","0","223","68","500","0","true","黑体","0"] -["223","69","1-0","0.5","_______________","0","0","223","69","500","0","true","黑体","0"] -["228","67","1-0","0.5","  何","0","0","228","67","500","0","true","黑体","0"] -["228","68","1-0","0.5","_______________","0","0","228","68","500","0","true","黑体","0"] -["232","66","1-0","0.5","  何","0","0","232","66","500","0","true","黑体","0"] -["232","67","1-0","0.5","_______________","0","0","232","67","500","0","true","黑体","0"] -["236","65","1-0","0.5","  何","0","0","236","65","500","0","true","黑体","0"] -["236","66","1-0","0.5","_______________","0","0","236","66","500","0","true","黑体","0"] -["239","64","1-0","0.5","  何","0","0","239","64","500","0","true","黑体","0"] -["239","65","1-0","0.5","_______________","0","0","239","65","500","0","true","黑体","0"] -["135","112","1-0","0.5","   よ","0","0","135","112","500","0","true","黑体","0"] -["138","109","1-0","0.5","   よ","0","0","138","109","500","0","true","黑体","0"] -["141","106","1-0","0.5","   よ","0","0","141","106","500","0","true","黑体","0"] -["145","103","1-0","0.5","   よ","0","0","145","103","500","0","true","黑体","0"] -["149","100","1-0","0.5","   よ","0","0","149","100","500","0","true","黑体","0"] -["154","97","1-0","0.5","   よ","0","0","154","97","500","0","true","黑体","0"] -["158","95","1-0","0.5","   よ","0","0","158","95","500","0","true","黑体","0"] -["162","92","1-0","0.5","   よ","0","0","162","92","500","0","true","黑体","0"] -["165","90","1-0","0.5","   よ","0","0","165","90","500","0","true","黑体","0"] -["169","88","1-0","0.5","   よ","0","0","169","88","500","0","true","黑体","0"] -["173","85","1-0","0.5","   よ","0","0","173","85","500","0","true","黑体","0"] -["178","83","1-0","0.5","   よ","0","0","178","83","500","0","true","黑体","0"] -["182","82","1-0","0.5","   よ","0","0","182","82","500","0","true","黑体","0"] -["186","81","1-0","0.5","   よ","0","0","186","81","500","0","true","黑体","0"] -["190","79","1-0","0.5","   よ","0","0","190","79","500","0","true","黑体","0"] -["194","78","1-0","0.5","   よ","0","0","194","78","500","0","true","黑体","0"] -["198","76","1-0","0.5","   よ","0","0","198","76","500","0","true","黑体","0"] -["202","74","1-0","0.5","   よ","0","0","202","74","500","0","true","黑体","0"] -["207","73","1-0","0.5","   よ","0","0","207","73","500","0","true","黑体","0"] -["211","71","1-0","0.5","   よ","0","0","211","71","500","0","true","黑体","0"] -["215","70","1-0","0.5","   よ","0","0","215","70","500","0","true","黑体","0"] -["219","69","1-0","0.5","   よ","0","0","219","69","500","0","true","黑体","0"] -["223","68","1-0","0.5","   よ","0","0","223","68","500","0","true","黑体","0"] -["228","67","1-0","0.5","   よ","0","0","228","67","500","0","true","黑体","0"] -["232","66","1-0","0.5","   よ","0","0","232","66","500","0","true","黑体","0"] -["236","65","1-0","0.5","   よ","0","0","236","65","500","0","true","黑体","0"] -["239","64","1-0","0.5","   よ","0","0","239","64","500","0","true","黑体","0"] -["135","112","1-0","0.5","    美","0","0","135","112","500","0","true","黑体","0"] -["138","109","1-0","0.5","    美","0","0","138","109","500","0","true","黑体","0"] -["141","106","1-0","0.5","    美","0","0","141","106","500","0","true","黑体","0"] -["145","103","1-0","0.5","    美","0","0","145","103","500","0","true","黑体","0"] -["149","100","1-0","0.5","    美","0","0","149","100","500","0","true","黑体","0"] -["154","97","1-0","0.5","    美","0","0","154","97","500","0","true","黑体","0"] -["158","95","1-0","0.5","    美","0","0","158","95","500","0","true","黑体","0"] -["162","92","1-0","0.5","    美","0","0","162","92","500","0","true","黑体","0"] -["165","90","1-0","0.5","    美","0","0","165","90","500","0","true","黑体","0"] -["169","88","1-0","0.5","    美","0","0","169","88","500","0","true","黑体","0"] -["173","85","1-0","0.5","    美","0","0","173","85","500","0","true","黑体","0"] -["178","83","1-0","0.5","    美","0","0","178","83","500","0","true","黑体","0"] -["182","82","1-0","0.5","    美","0","0","182","82","500","0","true","黑体","0"] -["186","81","1-0","0.5","    美","0","0","186","81","500","0","true","黑体","0"] -["190","79","1-0","0.5","    美","0","0","190","79","500","0","true","黑体","0"] -["194","78","1-0","0.5","    美","0","0","194","78","500","0","true","黑体","0"] -["198","76","1-0","0.5","    美","0","0","198","76","500","0","true","黑体","0"] -["202","74","1-0","0.5","    美","0","0","202","74","500","0","true","黑体","0"] -["207","73","1-0","0.5","    美","0","0","207","73","500","0","true","黑体","0"] -["211","71","1-0","0.5","    美","0","0","211","71","500","0","true","黑体","0"] -["215","70","1-0","0.5","    美","0","0","215","70","500","0","true","黑体","0"] -["219","69","1-0","0.5","    美","0","0","219","69","500","0","true","黑体","0"] -["223","68","1-0","0.5","    美","0","0","223","68","500","0","true","黑体","0"] -["228","67","1-0","0.5","    美","0","0","228","67","500","0","true","黑体","0"] -["232","66","1-0","0.5","    美","0","0","232","66","500","0","true","黑体","0"] -["236","65","1-0","0.5","    美","0","0","236","65","500","0","true","黑体","0"] -["239","64","1-0","0.5","    美","0","0","239","64","500","0","true","黑体","0"] -["135","112","1-0","0.5","     し","0","0","135","112","500","0","true","黑体","0"] -["138","109","1-0","0.5","     し","0","0","138","109","500","0","true","黑体","0"] -["141","106","1-0","0.5","     し","0","0","141","106","500","0","true","黑体","0"] -["145","103","1-0","0.5","     し","0","0","145","103","500","0","true","黑体","0"] -["149","100","1-0","0.5","     し","0","0","149","100","500","0","true","黑体","0"] -["154","97","1-0","0.5","     し","0","0","154","97","500","0","true","黑体","0"] -["158","95","1-0","0.5","     し","0","0","158","95","500","0","true","黑体","0"] -["162","92","1-0","0.5","     し","0","0","162","92","500","0","true","黑体","0"] -["165","90","1-0","0.5","     し","0","0","165","90","500","0","true","黑体","0"] -["169","88","1-0","0.5","     し","0","0","169","88","500","0","true","黑体","0"] -["173","85","1-0","0.5","     し","0","0","173","85","500","0","true","黑体","0"] -["178","83","1-0","0.5","     し","0","0","178","83","500","0","true","黑体","0"] -["182","82","1-0","0.5","     し","0","0","182","82","500","0","true","黑体","0"] -["186","81","1-0","0.5","     し","0","0","186","81","500","0","true","黑体","0"] -["190","79","1-0","0.5","     し","0","0","190","79","500","0","true","黑体","0"] -["194","78","1-0","0.5","     し","0","0","194","78","500","0","true","黑体","0"] -["198","76","1-0","0.5","     し","0","0","198","76","500","0","true","黑体","0"] -["202","74","1-0","0.5","     し","0","0","202","74","500","0","true","黑体","0"] -["207","73","1-0","0.5","     し","0","0","207","73","500","0","true","黑体","0"] -["211","71","1-0","0.5","     し","0","0","211","71","500","0","true","黑体","0"] -["215","70","1-0","0.5","     し","0","0","215","70","500","0","true","黑体","0"] -["219","69","1-0","0.5","     し","0","0","219","69","500","0","true","黑体","0"] -["223","68","1-0","0.5","     し","0","0","223","68","500","0","true","黑体","0"] -["228","67","1-0","0.5","     し","0","0","228","67","500","0","true","黑体","0"] -["232","66","1-0","0.5","     し","0","0","232","66","500","0","true","黑体","0"] -["236","65","1-0","0.5","     し","0","0","236","65","500","0","true","黑体","0"] -["239","64","1-0","0.5","     し","0","0","239","64","500","0","true","黑体","0"] -["135","112","1-0","0.5","      い","0","0","135","112","500","0","true","黑体","0"] -["138","109","1-0","0.5","      い","0","0","138","109","500","0","true","黑体","0"] -["141","106","1-0","0.5","      い","0","0","141","106","500","0","true","黑体","0"] -["145","103","1-0","0.5","      い","0","0","145","103","500","0","true","黑体","0"] -["149","100","1-0","0.5","      い","0","0","149","100","500","0","true","黑体","0"] -["154","97","1-0","0.5","      い","0","0","154","97","500","0","true","黑体","0"] -["158","95","1-0","0.5","      い","0","0","158","95","500","0","true","黑体","0"] -["162","92","1-0","0.5","      い","0","0","162","92","500","0","true","黑体","0"] -["165","90","1-0","0.5","      い","0","0","165","90","500","0","true","黑体","0"] -["169","88","1-0","0.5","      い","0","0","169","88","500","0","true","黑体","0"] -["173","85","1-0","0.5","      い","0","0","173","85","500","0","true","黑体","0"] -["178","83","1-0","0.5","      い","0","0","178","83","500","0","true","黑体","0"] -["182","82","1-0","0.5","      い","0","0","182","82","500","0","true","黑体","0"] -["186","81","1-0","0.5","      い","0","0","186","81","500","0","true","黑体","0"] -["190","79","1-0","0.5","      い","0","0","190","79","500","0","true","黑体","0"] -["194","78","1-0","0.5","      い","0","0","194","78","500","0","true","黑体","0"] -["198","76","1-0","0.5","      い","0","0","198","76","500","0","true","黑体","0"] -["202","74","1-0","0.5","      い","0","0","202","74","500","0","true","黑体","0"] -["207","73","1-0","0.5","      い","0","0","207","73","500","0","true","黑体","0"] -["211","71","1-0","0.5","      い","0","0","211","71","500","0","true","黑体","0"] -["215","70","1-0","0.5","      い","0","0","215","70","500","0","true","黑体","0"] -["219","69","1-0","0.5","      い","0","0","219","69","500","0","true","黑体","0"] -["223","68","1-0","0.5","      い","0","0","223","68","500","0","true","黑体","0"] -["228","67","1-0","0.5","      い","0","0","228","67","500","0","true","黑体","0"] -["232","66","1-0","0.5","      い","0","0","232","66","500","0","true","黑体","0"] -["236","65","1-0","0.5","      い","0","0","236","65","500","0","true","黑体","0"] -["239","64","1-0","0.5","      い","0","0","239","64","500","0","true","黑体","0"] -["135","112","1-0","0.5","  と思ったよ","0","0","135","112","500","0","true","黑体","0"] -["133","120","1-0","0.5","_______________","0","0","133","120","500","0","true","黑体","0"] -["138","109","1-0","0.5","  と思ったよ","0","0","138","109","500","0","true","黑体","0"] -["137","115","1-0","0.5","_______________","0","0","137","115","500","0","true","黑体","0"] -["141","106","1-0","0.5","  と思ったよ","0","0","141","106","500","0","true","黑体","0"] -["141","110","1-0","0.5","_______________","0","0","141","110","500","0","true","黑体","0"] -["145","103","1-0","0.5","  と思ったよ","0","0","145","103","500","0","true","黑体","0"] -["145","105","1-0","0.5","_______________","0","0","141","105","500","0","true","黑体","0"] -["149","100","1-0","0.5","  と思ったよ","0","0","149","100","500","0","true","黑体","0"] -["149","100","1-0","0.5","_______________","0","0","149","100","500","0","true","黑体","0"] -["154","97","1-0","0.5","  と思ったよ","0","0","154","97","500","0","true","黑体","0"] -["154","98","1-0","0.5","_______________","0","0","154","98","500","0","true","黑体","0"] -["158","95","1-0","0.5","  と思ったよ","0","0","158","95","500","0","true","黑体","0"] -["158","96","1-0","0.5","_______________","0","0","158","96","500","0","true","黑体","0"] -["162","92","1-0","0.5","  と思ったよ","0","0","162","92","500","0","true","黑体","0"] -["162","93","1-0","0.5","_______________","0","0","162","93","500","0","true","黑体","0"] -["165","90","1-0","0.5","  と思ったよ","0","0","165","90","500","0","true","黑体","0"] -["165","91","1-0","0.5","_______________","0","0","165","91","500","0","true","黑体","0"] -["169","88","1-0","0.5","  と思ったよ","0","0","169","88","500","0","true","黑体","0"] -["169","89","1-0","0.5","_______________","0","0","169","89","500","0","true","黑体","0"] -["173","85","1-0","0.5","  と思ったよ","0","0","173","85","500","0","true","黑体","0"] -["173","87","1-0","0.5","_______________","0","0","173","87","500","0","true","黑体","0"] -["178","83","1-0","0.5","  と思ったよ","0","0","178","83","500","0","true","黑体","0"] -["178","84","1-0","0.5","_______________","0","0","178","84","500","0","true","黑体","0"] -["182","82","1-0","0.5","  と思ったよ","0","0","182","82","500","0","true","黑体","0"] -["182","83","1-0","0.5","_______________","0","0","182","83","500","0","true","黑体","0"] -["186","81","1-0","0.5","  と思ったよ","0","0","186","81","500","0","true","黑体","0"] -["186","82","1-0","0.5","_______________","0","0","186","82","500","0","true","黑体","0"] -["190","79","1-0","0.5","  と思ったよ","0","0","190","79","500","0","true","黑体","0"] -["190","80","1-0","0.5","_______________","0","0","190","80","500","0","true","黑体","0"] -["194","78","1-0","0.5","  と思ったよ","0","0","194","78","500","0","true","黑体","0"] -["194","79","1-0","0.5","_______________","0","0","194","79","500","0","true","黑体","0"] -["198","76","1-0","0.5","  と思ったよ","0","0","198","76","500","0","true","黑体","0"] -["198","77","1-0","0.5","_______________","0","0","198","77","500","0","true","黑体","0"] -["202","74","1-0","0.5","  と思ったよ","0","0","202","74","500","0","true","黑体","0"] -["202","75","1-0","0.5","_______________","0","0","202","75","500","0","true","黑体","0"] -["207","73","1-0","0.5","  と思ったよ","0","0","207","73","500","0","true","黑体","0"] -["207","74","1-0","0.5","_______________","0","0","207","74","500","0","true","黑体","0"] -["211","71","1-0","0.5","  と思ったよ","0","0","211","71","500","0","true","黑体","0"] -["211","72","1-0","0.5","_______________","0","0","211","72","500","0","true","黑体","0"] -["215","70","1-0","0.5","  と思ったよ","0","0","215","70","500","0","true","黑体","0"] -["215","71","1-0","0.5","_______________","0","0","215","71","500","0","true","黑体","0"] -["219","69","1-0","0.5","  と思ったよ","0","0","219","69","500","0","true","黑体","0"] -["219","70","1-0","0.5","_______________","0","0","219","70","500","0","true","黑体","0"] -["223","68","1-0","0.5","  と思ったよ","0","0","223","68","500","0","true","黑体","0"] -["223","69","1-0","0.5","_______________","0","0","223","69","500","0","true","黑体","0"] -["228","67","1-0","0.5","  と思ったよ","0","0","228","67","500","0","true","黑体","0"] -["228","68","1-0","0.5","_______________","0","0","228","68","500","0","true","黑体","0"] -["232","66","1-0","0.5","  と思ったよ","0","0","232","66","500","0","true","黑体","0"] -["232","67","1-0","0.5","_______________","0","0","232","67","500","0","true","黑体","0"] -["236","65","1-0","0.5","  と思ったよ","0","0","236","65","500","0","true","黑体","0"] -["236","66","1-0","0.5","_______________","0","0","236","66","500","0","true","黑体","0"] -["239","64","1-0","0.5","  と思ったよ","0","0","239","64","500","0","true","黑体","0"] -["239","65","1-0","0.5","_______________","0","0","239","65","500","0","true","黑体","0"] -["372","278","1-0","1.1","◆","30","70","372","278","500","0","false","黑体","0"] -["102","282","1-0","1.1","◆","0","70","102","282","500","0","false","黑体","0"] -["311","271","1-0","1.1","◆","0","70","311","271","500","0","false","黑体","0"] -["483","288","1-0","1.1","◆","50","80","483","288","500","0","false","黑体","0"] -["38","274","1-0","1.1","◆","340","60","38","274","500","0","false","黑体","0"] -["127","259","1-0","1.1","◆","340","60","127","259","500","0","false","黑体","0"] -["236","300","1-0","1.1","◆","350","60","236","300","500","0","false","黑体","0"] -["332","289","1-0","1.1","◆","350","70","332","289","500","0","false","黑体","0"] -["437","276","1-0","1.1","◆","10","80","437","276","500","0","false","黑体","0"] -["288","288","1-0","1.1","◆","10","80","288","288","500","0","false","黑体","0"] -["109","265","1-0","1.1","◆","10","60","109","265","500","0","false","黑体","0"] -["53","284","1-0","1.1","◆","325","60","53","284","500","0","false","黑体","0"] -["245","289","1-0","1.1","◆","325","60","245","289","500","0","false","黑体","0"] -["421","300","1-0","1.1","◆","325","60","421","300","500","0","false","黑体","0"] -["334","304","1-0","1.1","◆","325","60","334","304","500","0","false","黑体","0"] -["119","306","1-0","1.1","◆","325","60","119","306","500","0","false","黑体","0"] -["233","285","1-0","1.1","◆","325","60","233","285","500","0","false","黑体","0"] -["160","282","1-0","1.1","◆","300","60","160","282","500","0","false","黑体","0"] -["523","291","1-0","1.1","◆","20","90","523","291","500","0","false","黑体","0"] -["300","313","1-0","1.1","◆","20","90","300","313","500","0","false","黑体","0"] -["135","268","1-0","1.1","◆","20","60","135","268","500","0","false","黑体","0"] -["386","307","1-0","1.1","◆","20","60","386","307","500","0","false","黑体","0"] -["407","268","1-0","1.1","◆","20","77","407","268","500","0","false","黑体","0"] -["288","308","1-0","1.1","◆","20","77","288","308","500","0","false","黑体","0"] -["49","242","1-0","1.1","◆","350","60","49","242","500","0","false","黑体","0"] -["190","251","1-0","1.1","◆","340","60","190","251","500","0","false","黑体","0"] -["482","289","1-0","1.1","◆","340","75","482","289","500","0","false","黑体","0"] -["457","249","1-0","1.1","◆","10","75","457","249","500","0","false","黑体","0"] -["292","242","1-0","1.1","◆","355","70","292","242","500","0","false","黑体","0"] -["286","283","1-0","1.1","◆","355","70","286","283","500","0","false","黑体","0"] -["427","273","1-0","1.1","◆","15","80","427","273","500","0","false","黑体","0"] -["16","268","1-0","1.1","◆","345","48","16","268","500","0","false","黑体","0"] -["218","309","1-0","1.1","◆","345","55","218","309","500","0","false","黑体","0"] -["286","302","1-0","1.1","◆","5","60","286","302","500","0","false","黑体","0"] -["264","295","1-0","1.1","◆","5","60","264","295","500","0","false","黑体","0"] -["238","266","1-0","1.1","◆","10","60","238","266","500","0","false","黑体","0"] -["180","7","0-1","4.5","在你诞生的那个早晨","0","0","180","7","500","0","true","黑体","0"] -["80","355","0-1","4.5","只是个爱哭鬼的我","0","0","80","355","500","0","true","黑体","0"] -["284","355","0-1","4.5","变成了小小的姐姐——","0","0","284","355","500","0","true","黑体","0"] -["84","343","0-1","4.5","真的很高兴啊","0","0","84","343","500","0","true","黑体","0"] -["120","362","0-1","4.5","虽然也有点不好意思","0","0","120","362","500","0","true","黑体","0"] -["292","30","0-1","4.5","但那真是值得骄傲的事","28","20","292","30","500","0","true","黑体","0"] -["111","333","0-1","4.5","在这摇曳着痛苦的","0","0","111","333","500","0","true","黑体","0"] -["253","359","0-1","4.5","生存的荒野上","0","0","253","359","500","0","true","黑体","0"] -["133","333","0-1","3","为了寻找“美丽之物”而纵横驰骋","0","0","133","333","500","0","true","黑体","0"] -["133","333","1-1","1.5","为了寻找“美丽之物”而纵横驰骋","0","0","133","333","500","0","true","黑体","0"] -["133","333","1-0","1.1","为了寻找“美丽之物”而纵横驰骋","0","0","0","333","1100","0","true","黑体","0"] -["180","325","1-0","4.5","往没有尽头的地平线","0","0","180","205","4500","0","true","黑体","0"] -["180","325","1-0","4.5","  旅行而去的","0","0","180","205","4500","0","true","黑体","0"] -["180","325","1-0","4.5","   你的睡脸","0","0","180","205","4500","0","true","黑体","0"] -["180","325","1-0","4.5","  比任何东西","0","0","180","205","4500","0","true","黑体","0"] -["180","325","1-0","4.5","  都要更加美丽","0","0","180","205","4500","0","true","黑体","0"] -["114","93","0.2-0.2","4.5","君の","0","0","114","93","500","0","false","黑体","0"] -["106","93","0.2-0.2","4.5","君の","0","0","106","93","500","0","false","黑体","0"] -["110","97","0.2-0.2","4.5","君の","0","0","110","97","500","0","false","黑体","0"] -["110","89","0.2-0.2","4.5","君の","0","0","110","89","500","0","false","黑体","0"] -["112","91","0.2-0.2","4.5","君の","0","0","112","91","500","0","false","黑体","0"] -["112","95","0.2-0.2","4.5","君の","0","0","112","95","500","0","false","黑体","0"] -["108","91","0.2-0.2","4.5","君の","0","0","108","91","500","0","false","黑体","0"] -["108","95","0.2-0.2","4.5","君の","0","0","108","95","500","0","false","黑体","0"] -["114","96","1-1","4.5","君","0","0","114","96","500","0","false","黑体","0"] -["160","96","1-1","4.5","の","0","0","160","96","500","0","false","黑体","0"] -["1","68","1-1","0.5","♪","0","0","99","211","500","0","true","宋体","0"] -["88","216","1-1","10","大","330","0","88","216","500","0","true","黑体","0"] -["88","216","0-1","0.8","大","330","0","88","216","500","0","true","黑体","0"] -["88","216","1-1","10","大","330","0","88","216","500","0","true","黑体","0"] -["26","1","1-1","0.5","♬","0","0","125","194","500","0","true","宋体","0"] -["125","194","1-1","10","好","335","0","125","194","500","0","true","黑体","0"] -["125","194","0-1","0.8","好","335","0","125","194","500","0","true","黑体","0"] -["125","194","1-1","10","好","335","0","125","194","500","0","true","黑体","0"] -["107","4","1-1","0.5","♩","0","0","166","175","500","0","true","宋体","0"] -["166","175","1-1","10","き","340","0","166","175","500","0","true","黑体","0"] -["166","175","0-1","0.8","き","340","0","166","175","500","0","true","黑体","0"] -["166","175","1-1","10","き","340","0","166","175","500","0","true","黑体","0"] -["168","3","1-1","0.5","♫","0","0","210","162","500","0","true","宋体","0"] -["210","162","1-1","10","な","345","0","210","162","500","0","true","黑体","0"] -["210","162","0-1","0.8","な","345","0","210","162","500","0","true","黑体","0"] -["210","162","1-1","10","な","345","0","210","162","500","0","true","黑体","0"] -["247","0","1-1","0.5","♭","0","0","255","153","500","0","true","宋体","0"] -["255","153","1-1","10","こ","350","0","255","153","500","0","true","黑体","0"] -["255","153","0-1","0.8","こ","350","0","255","153","500","0","true","黑体","0"] -["255","153","1-1","10","こ","350","0","255","153","500","0","true","黑体","0"] -["316","0","1-1","0.5","♪","0","0","306","155","500","0","true","宋体","0"] -["306","155","1-1","10","の","0","0","306","155","500","0","true","黑体","0"] -["306","155","0-1","0.8","の","0","0","306","155","500","0","true","黑体","0"] -["306","155","1-1","10","の","0","0","306","155","500","0","true","黑体","0"] -["394","0","1-1","0.5","♬","0","0","352","193","500","0","true","宋体","0"] -["270","194","1-1","10","旋律(Mélodie)","0","0","270","194","500","0","true","黑体","0"] -["270","194","0-1","0.8","旋律(Mélodie)","0","0","270","194","500","0","true","黑体","0"] -["270","194","1-1","10","旋律(Mélodie)","0","0","270","194","500","0","true","黑体","0"] -["11","139","1-1","10","████","0","0","11","139","500","0","false","黑体","0"] -["11","139","1-1","10"," ███","0","0","11","139","500","0","false","黑体","0"] -["134","334","0-1","3","你最喜欢的这段旋律(Mélodie)","0","0","134","334","500","0","true","黑体","0"] -["134","334","1-1","2.6","你最喜欢的这段旋律(Mélodie)","0","0","134","334","500","0","true","黑体","0"] -["-5","-5","1-1","5","███████","0","0","-5","-5","500","0","false","MS UI Gothic","0"] -["-5","-5","1-1","5"," ██████","0","0","-5","-5","500","0","false","MS UI Gothic","0"] -["-5","5","1-1","5","███████","0","0","-5","5","500","0","false","MS UI Gothic","0"] -["-5","5","1-1","5"," ██████","0","0","-5","5","500","0","false","MS UI Gothic","0"] -["-5","32","1-1","5","███████","0","0","-5","32","500","0","false","MS UI Gothic","0"] -["-5","32","1-1","5"," ██████","0","0","-5","32","500","0","false","MS UI Gothic","0"] -["-5","63","1-1","5","███████","0","0","-5","63","500","0","false","MS UI Gothic","0"] -["-5","63","1-1","5"," ██████","0","0","-5","63","500","0","false","MS UI Gothic","0"] -["-5","100","1-1","5","███████","0","0","-5","100","500","0","false","MS UI Gothic","0"] -["-5","100","1-1","5"," ██████","0","0","-5","100","500","0","false","MS UI Gothic","0"] -["-5","124","1-1","5","███████/n","0","0","-5","124","500","0","false","MS UI Gothic","0"] -["-5","124","1-1","5"," ██████/n","0","0","-5","124","500","0","false","MS UI Gothic","0"] -["-5","148","1-1","5","███████/n","0","0","-5","148","500","0","false","MS UI Gothic","0"] -["-5","148","1-1","5"," ██████/n","0","0","-5","148","500","0","false","MS UI Gothic","0"] -["-5","175","1-1","5","███████/n","0","0","-5","175","500","0","false","MS UI Gothic","0"] -["-5","175","1-1","5"," ██████/n","0","0","-5","175","500","0","false","MS UI Gothic","0"] -["-5","201","1-1","5","███████/n","0","0","-5","201","500","0","false","MS UI Gothic","0"] -["-5","201","1-1","5"," ██████/n","0","0","-5","201","500","0","false","MS UI Gothic","0"] -["-5","234","1-1","5","███████/n/n","0","0","-5","234","500","0","false","MS UI Gothic","0"] -["-5","234","1-1","5"," ██████/n/n","0","0","-5","234","500","0","false","MS UI Gothic","0"] -["-5","275","1-1","5","███████/n/n","0","0","-5","275","500","0","false","MS UI Gothic","0"] -["-5","275","1-1","5"," ██████/n/n","0","0","-5","275","500","0","false","MS UI Gothic","0"] -["-10","134","1-1","5","██","320","0","-10","134","500","0","false","黑体","0"] -["16","114","1-1","5","██","322","0","16","114","500","0","false","黑体","0"] -["39","97","1-1","5","██","326","0","39","97","500","0","false","黑体","0"] -["60","83","1-1","5","██","331","0","60","83","500","0","false","黑体","0"] -["86","69","1-1","5","██","335","0","86","69","500","0","false","黑体","0"] -["113","57","1-1","5","██","339","0","113","57","500","0","false","黑体","0"] -["137","48","1-1","5","██","343","0","137","48","500","0","false","黑体","0"] -["164","40","1-1","5","██","348","0","164","40","500","0","false","黑体","0"] -["192","34","1-1","5","██","351","0","192","34","500","0","false","黑体","0"] -["221","30","1-1","5","██","355","0","221","30","500","0","false","黑体","0"] -["247","28","1-1","5","██","358","0","247","28","500","0","false","黑体","0"] -["274","27","1-1","5","██","2","0","274","27","500","0","false","黑体","0"] -["301","28","1-1","5","██","5","0","301","28","500","0","false","黑体","0"] -["326","30","1-1","5","██","9","0","326","30","500","0","false","黑体","0"] -["354","34","1-1","5","██","13","0","354","34","500","0","false","黑体","0"] -["388","41","1-1","5","██","15","0","388","41","500","0","false","宋体","0"] -["414","48","1-1","5","██","16","0","414","48","500","0","false","宋体","0"] -["435","54","1-1","5","██","20","0","435","54","500","0","false","黑体","0"] -["458","62","1-1","5","██","24","0","458","62","500","0","false","黑体","0"] -["475","69","1-1","5","██","27","0","475","69","500","0","false","黑体","0"] -["388","41","1-1","5"," ██","15","0","388","41","500","0","false","宋体","0"] -["414","48","1-1","5"," ██","16","0","414","48","500","0","false","宋体","0"] -["435","54","1-1","5"," ██","20","0","435","54","500","0","false","黑体","0"] -["458","62","1-1","5"," ██","24","0","458","62","500","0","false","黑体","0"] -["475","69","1-1","5"," ██","27","0","475","69","500","0","false","黑体","0"] -["-10","134","1-1","5"," ██","320","0","-10","134","500","0","false","黑体","0"] -["16","114","1-1","5"," ██","322","0","16","114","500","0","false","黑体","0"] -["39","97","1-1","5"," ██","326","0","39","97","500","0","false","黑体","0"] -["60","83","1-1","5"," ██","331","0","60","83","500","0","false","黑体","0"] -["86","69","1-1","5"," ██","335","0","86","69","500","0","false","黑体","0"] -["113","57","1-1","5"," ██","339","0","113","57","500","0","false","黑体","0"] -["137","48","1-1","5"," ██","343","0","137","48","500","0","false","黑体","0"] -["164","40","1-1","5"," ██","348","0","164","40","500","0","false","黑体","0"] -["192","34","1-1","5"," ██","351","0","192","34","500","0","false","黑体","0"] -["221","30","1-1","5"," ██","355","0","221","30","500","0","false","黑体","0"] -["247","28","1-1","5"," ██","358","0","247","28","500","0","false","黑体","0"] -["274","27","1-1","5"," ██","2","0","274","27","500","0","false","黑体","0"] -["301","28","1-1","5"," ██","10","0","301","28","500","0","false","黑体","0"] -["326","30","1-1","5"," ██","9","0","326","30","500","0","false","黑体","0"] -["354","34","1-1","5"," ██","13","0","354","34","500","0","false","黑体","0"] -["7","122","1-1","5","█","320","0","7","122","500","0","false","黑体","0"] -["-10","152","1-1","5","██","320","0","-10","152","500","0","false","黑体","0"] -["14","133","1-1","5","██","322","0","14","133","500","0","false","黑体","0"] -["36","116","1-1","5","██","325","0","36","116","500","0","false","黑体","0"] -["59","100","1-1","5","██","330","0","59","100","500","0","false","黑体","0"] -["86","85","1-1","5","██","333","0","86","85","500","0","false","黑体","0"] -["109","74","1-1","5","██","337","0","109","74","500","0","false","黑体","0"] -["133","64","1-1","5","██","341","0","133","64","500","0","false","黑体","0"] -["162","54","1-1","5","██","345","0","162","54","500","0","false","黑体","0"] -["188","47","1-1","5","██","349","0","188","47","500","0","false","黑体","0"] -["217","41","1-1","5","██","355","0","217","41","500","0","false","黑体","0"] -["246","39","1-1","5","██","0","0","246","39","500","0","false","黑体","0"] -["274","38","1-1","5","██","2","0","274","38","500","0","false","黑体","0"] -["302","39","1-1","5","██","6","0","302","39","500","0","false","黑体","0"] -["329","42","1-1","5","██","10","0","329","42","500","0","false","黑体","0"] -["357","47","1-1","5","██","13","0","357","47","500","0","false","黑体","0"] -["-10","152","1-1","5"," ██","320","0","-10","152","500","0","false","黑体","0"] -["14","133","1-1","5"," ██","322","0","14","133","500","0","false","黑体","0"] -["36","116","1-1","5"," ██","325","0","36","116","500","0","false","黑体","0"] -["59","100","1-1","5"," ██","330","0","59","100","500","0","false","黑体","0"] -["86","85","1-1","5"," ██","333","0","86","85","500","0","false","黑体","0"] -["109","74","1-1","5"," ██","337","0","109","74","500","0","false","黑体","0"] -["133","64","1-1","5"," ██","341","0","133","64","500","0","false","黑体","0"] -["162","54","1-1","5"," ██","345","0","162","54","500","0","false","黑体","0"] -["188","47","1-1","5"," ██","349","0","188","47","500","0","false","黑体","0"] -["217","41","1-1","5"," ██","355","0","217","41","500","0","false","黑体","0"] -["246","39","1-1","5"," ██","0","0","246","39","500","0","false","黑体","0"] -["274","38","1-1","5"," ██","2","0","274","38","500","0","false","黑体","0"] -["302","39","1-1","5"," ██","6","0","302","39","500","0","false","黑体","0"] -["329","42","1-1","5"," ██","10","0","329","42","500","0","false","黑体","0"] -["357","47","1-1","5"," ██","13","0","357","47","500","0","false","黑体","0"] -["389","54","1-1","5","██","15","0","389","54","500","0","false","黑体","0"] -["416","61","1-1","5","██","19","0","416","61","500","0","false","黑体","0"] -["442","69","1-1","5","██","22","0","442","69","500","0","false","黑体","0"] -["464","77","1-1","5","██","25","0","464","77","500","0","false","黑体","0"] -["490","88","1-1","5","██","28","0","490","88","500","0","false","黑体","0"] -["389","54","1-1","5"," ██","15","0","389","54","500","0","false","黑体","0"] -["416","61","1-1","5"," ██","19","0","416","61","500","0","false","黑体","0"] -["442","69","1-1","5"," ██","22","0","442","69","500","0","false","黑体","0"] -["464","77","1-1","5"," ██","25","0","464","77","500","0","false","黑体","0"] -["490","88","1-1","5"," █","28","0","490","88","500","0","false","黑体","0"] -["-10","169","1-1","5","██","320","0","-10","169","500","0","false","黑体","0"] -["13","150","1-1","5","██","322","0","13","150","500","0","false","黑体","0"] -["34","134","1-1","5","██","325","0","34","134","500","0","false","黑体","0"] -["59","117","1-1","5","██","328","0","59","117","500","0","false","黑体","0"] -["76","107","1-1","5","██","331","0","76","107","500","0","false","黑体","0"] -["103","92","1-1","5","██","335","0","103","92","500","0","false","黑体","0"] -["132","79","1-1","5","██","339","0","132","79","500","0","false","黑体","0"] -["159","69","1-1","5","██","342","0","159","69","500","0","false","黑体","0"] -["187","60","1-1","5","██","347","0","187","60","500","0","false","黑体","0"] -["212","54","1-1","5","██","354","0","212","54","500","0","false","黑体","0"] -["239","51","1-1","5","██","0","0","239","51","500","0","false","黑体","0"] -["263","51","1-1","5","██","2","0","263","51","500","0","false","黑体","0"] -["288","52","1-1","5","██","3","0","288","52","500","0","false","黑体","0"] -["316","54","1-1","5","██","7","0","316","54","500","0","false","黑体","0"] -["347","58","1-1","5","██","12","0","347","58","500","0","false","黑体","0"] -["371","63","1-1","5","██","15","0","371","63","500","0","false","黑体","0"] -["-10","169","1-1","5"," ██","320","0","-10","169","500","0","false","黑体","0"] -["13","150","1-1","5"," ██","322","0","13","150","500","0","false","黑体","0"] -["34","134","1-1","5"," ██","325","0","34","134","500","0","false","黑体","0"] -["59","117","1-1","5"," ██","328","0","59","117","500","0","false","黑体","0"] -["76","107","1-1","5"," ██","331","0","76","107","500","0","false","黑体","0"] -["103","92","1-1","5"," ██","335","0","103","92","500","0","false","黑体","0"] -["132","79","1-1","5"," ██","339","0","132","79","500","0","false","黑体","0"] -["159","69","1-1","5"," ██","342","0","159","69","500","0","false","黑体","0"] -["187","60","1-1","5"," ██","347","0","187","60","500","0","false","黑体","0"] -["212","54","1-1","5"," ██","354","0","212","54","500","0","false","黑体","0"] -["239","51","1-1","5"," ██","0","0","239","51","500","0","false","黑体","0"] -["263","51","1-1","5"," ██","2","0","263","51","500","0","false","黑体","0"] -["288","52","1-1","5"," ██","3","0","288","52","500","0","false","黑体","0"] -["316","54","1-1","5"," ██","7","0","316","54","500","0","false","黑体","0"] -["347","58","1-1","5"," ██","12","0","347","58","500","0","false","黑体","0"] -["371","63","1-1","5"," █","15","0","371","63","500","0","false","黑体","0"] -["400","70","1-1","5","██","19","0","400","70","500","0","false","黑体","0"] -["426","78","1-1","5","██","21","0","426","78","500","0","false","黑体","0"] -["451","87","1-1","5","██","24","0","451","87","500","0","false","黑体","0"] -["477","98","1-1","5","██","26","0","477","98","500","0","false","黑体","0"] -["400","70","1-1","5"," ██","19","0","400","70","500","0","false","黑体","0"] -["426","78","1-1","5"," ██","21","0","426","78","500","0","false","黑体","0"] -["451","87","1-1","5"," ██","24","0","451","87","500","0","false","黑体","0"] -["477","98","1-1","5"," ██","26","0","477","98","500","0","false","黑体","0"] -["-10","187","1-1","5","██","320","0","-10","187","500","0","false","黑体","0"] -["13","168","1-1","5","██","322","0","13","168","500","0","false","黑体","0"] -["37","151","1-1","5","██","324","0","37","151","500","0","false","黑体","0"] -["62","133","1-1","5","██","327","0","62","133","500","0","false","黑体","0"] -["86","118","1-1","5","██","331","0","86","118","500","0","false","黑体","0"] -["113","104","1-1","5","██","334","0","113","104","500","0","false","黑体","0"] -["142","91","1-1","5","██","339","0","142","91","500","0","false","黑体","0"] -["171","80","1-1","5","██","343","0","171","80","500","0","false","黑体","0"] -["201","71","1-1","5","██","349","0","201","71","500","0","false","黑体","0"] -["229","65","1-1","5","██","356","0","229","65","500","0","false","黑体","0"] -["257","63","1-1","5","██","2","0","257","63","500","0","false","黑体","0"] -["289","64","1-1","5","██","6","0","289","64","500","0","false","黑体","0"] -["319","67","1-1","5","██","8","0","319","67","500","0","false","黑体","0"] -["345","70","1-1","5","██","12","0","345","70","500","0","false","黑体","0"] -["373","76","1-1","5","██","16","0","373","76","500","0","false","黑体","0"] -["-10","187","1-1","5"," ██","320","0","-10","187","500","0","false","黑体","0"] -["13","168","1-1","5"," ██","322","0","13","168","500","0","false","黑体","0"] -["37","151","1-1","5"," ██","324","0","37","151","500","0","false","黑体","0"] -["62","133","1-1","5"," ██","327","0","62","133","500","0","false","黑体","0"] -["86","118","1-1","5"," ██","331","0","86","118","500","0","false","黑体","0"] -["113","104","1-1","5"," ██","334","0","113","104","500","0","false","黑体","0"] -["142","91","1-1","5"," ██","339","0","142","91","500","0","false","黑体","0"] -["171","80","1-1","5"," ██","343","0","171","80","500","0","false","黑体","0"] -["201","71","1-1","5"," ██","349","0","201","71","500","0","false","黑体","0"] -["229","65","1-1","5"," ██","356","0","229","65","500","0","false","黑体","0"] -["257","63","1-1","5"," ██","2","0","257","63","500","0","false","黑体","0"] -["289","64","1-1","5"," ██","6","0","289","64","500","0","false","黑体","0"] -["319","67","1-1","5"," ██","8","0","319","67","500","0","false","黑体","0"] -["345","70","1-1","5"," ██","12","0","345","70","500","0","false","黑体","0"] -["373","76","1-1","5"," ██","16","0","373","76","500","0","false","黑体","0"] -["408","86","1-1","5","██","19","0","408","86","500","0","false","黑体","0"] -["436","95","1-1","5","██","22","0","436","95","500","0","false","黑体","0"] -["459","104","1-1","5","██","24","0","459","104","500","0","false","黑体","0"] -["483","114","1-1","5","██","25","0","483","114","500","0","false","黑体","0"] -["408","86","1-1","5"," ██","19","0","408","86","500","0","false","黑体","0"] -["436","95","1-1","5"," ██","22","0","436","95","500","0","false","黑体","0"] -["459","104","1-1","5"," ██","24","0","459","104","500","0","false","黑体","0"] -["483","114","1-1","5"," ██","25","0","483","114","500","0","false","黑体","0"] -["-10","205","1-1","5","██","320","0","-10","205","500","0","false","黑体","0"] -["13","186","1-1","5","██","321","0","13","186","500","0","false","黑体","0"] -["36","168","1-1","5","██","324","0","36","168","500","0","false","黑体","0"] -["61","150","1-1","5","██","327","0","61","150","500","0","false","黑体","0"] -["85","135","1-1","5","██","330","0","85","135","500","0","false","黑体","0"] -["111","120","1-1","5","██","334","0","111","120","500","0","false","黑体","0"] -["138","107","1-1","5","██","338","0","138","107","500","0","false","黑体","0"] -["166","96","1-1","5","██","341","0","166","96","500","0","false","黑体","0"] -["193","87","1-1","5","██","348","0","193","87","500","0","false","黑体","0"] -["222","81","1-1","5","██","354","0","222","81","500","0","false","黑体","0"] -["250","78","1-1","5","██","0","0","250","78","500","0","false","黑体","0"] -["276","78","1-1","5","██","4","0","276","78","500","0","false","黑体","0"] -["306","80","1-1","5","██","7","0","306","80","500","0","false","黑体","0"] -["333","83","1-1","5","██","10","0","333","83","500","0","false","黑体","0"] -["364","89","1-1","5","██","12","0","364","89","500","0","false","黑体","0"] -["386","95","1-1","5","██","13","0","386","95","500","0","false","黑体","0"] -["-10","205","1-1","5"," ██","320","0","-10","205","500","0","false","黑体","0"] -["13","186","1-1","5"," ██","321","0","13","186","500","0","false","黑体","0"] -["36","168","1-1","5"," ██","324","0","36","168","500","0","false","黑体","0"] -["61","150","1-1","5"," ██","327","0","61","150","500","0","false","黑体","0"] -["85","135","1-1","5"," ██","330","0","85","135","500","0","false","黑体","0"] -["111","120","1-1","5"," ██","334","0","111","120","500","0","false","黑体","0"] -["138","107","1-1","5"," ██","338","0","138","107","500","0","false","黑体","0"] -["166","96","1-1","5"," ██","341","0","166","96","500","0","false","黑体","0"] -["193","87","1-1","5"," ██","348","0","193","87","500","0","false","黑体","0"] -["222","81","1-1","5"," ██","354","0","222","81","500","0","false","黑体","0"] -["250","78","1-1","5"," ██","0","0","250","78","500","0","false","黑体","0"] -["276","78","1-1","5"," ██","4","0","276","78","500","0","false","黑体","0"] -["306","80","1-1","5"," ██","7","0","306","80","500","0","false","黑体","0"] -["333","83","1-1","5"," ██","10","0","333","83","500","0","false","黑体","0"] -["364","89","1-1","5"," ██","12","0","364","89","500","0","false","黑体","0"] -["386","95","1-1","5"," █","13","0","386","95","500","0","false","黑体","0"] -["415","101","1-1","5","██","19","0","415","101","500","0","false","黑体","0"] -["441","109","1-1","5","██","24","0","441","109","500","0","false","黑体","0"] -["466","119","1-1","5","██","25","0","466","119","500","0","false","黑体","0"] -["489","129","1-1","5","██","27","0","489","129","500","0","false","黑体","0"] -["415","101","1-1","5"," ██","19","0","415","101","500","0","false","黑体","0"] -["441","109","1-1","5"," ██","24","0","441","109","500","0","false","黑体","0"] -["466","119","1-1","5"," ██","25","0","466","119","500","0","false","黑体","0"] -["489","129","1-1","5"," ██","27","0","489","129","500","0","false","黑体","0"] -["-10","224","1-1","5","██","320","0","-10","224","500","0","false","黑体","0"] -["14","204","1-1","5","██","321","0","14","204","500","0","false","黑体","0"] -["37","186","1-1","5","██","324","0","37","186","500","0","false","黑体","0"] -["60","169","1-1","5","██","326","0","60","169","500","0","false","黑体","0"] -["81","155","1-1","5","██","328","0","81","155","500","0","false","黑体","0"] -["106","140","1-1","5","██","332","0","106","140","500","0","false","黑体","0"] -["132","127","1-1","5","██","335","0","132","127","500","0","false","黑体","0"] -["160","114","1-1","5","██","339","0","160","114","500","0","false","黑体","0"] -["189","103","1-1","5","██","346","0","189","103","500","0","false","黑体","0"] -["217","96","1-1","5","██","352","0","217","96","500","0","false","黑体","0"] -["247","92","1-1","5","██","358","0","247","92","500","0","false","黑体","0"] -["277","91","1-1","5","██","3","0","277","91","500","0","false","黑体","0"] -["306","92","1-1","5","██","7","0","306","92","500","0","false","黑体","0"] -["334","95","1-1","5","██","9","0","334","95","500","0","false","黑体","0"] -["364","100","1-1","5","██","12","0","364","100","500","0","false","黑体","0"] -["390","106","1-1","5","██","13","0","390","106","500","0","false","黑体","0"] -["-10","224","1-1","5"," ██","320","0","-10","224","500","0","false","黑体","0"] -["14","204","1-1","5"," ██","321","0","14","204","500","0","false","黑体","0"] -["37","186","1-1","5"," ██","324","0","37","186","500","0","false","黑体","0"] -["60","169","1-1","5"," ██","326","0","60","169","500","0","false","黑体","0"] -["81","155","1-1","5"," ██","328","0","81","155","500","0","false","黑体","0"] -["106","140","1-1","5"," ██","332","0","106","140","500","0","false","黑体","0"] -["132","127","1-1","5"," ██","335","0","132","127","500","0","false","黑体","0"] -["160","114","1-1","5"," ██","339","0","160","114","500","0","false","黑体","0"] -["189","103","1-1","5"," ██","346","0","189","103","500","0","false","黑体","0"] -["217","96","1-1","5"," ██","352","0","217","96","500","0","false","黑体","0"] -["247","92","1-1","5"," ██","358","0","247","92","500","0","false","黑体","0"] -["277","91","1-1","5"," ██","3","0","277","91","500","0","false","黑体","0"] -["306","92","1-1","5"," ██","7","0","306","92","500","0","false","黑体","0"] -["334","95","1-1","5"," ██","9","0","334","95","500","0","false","黑体","0"] -["364","100","1-1","5"," ██","12","0","364","100","500","0","false","黑体","0"] -["390","106","1-1","5"," █","13","0","390","106","500","0","false","黑体","0"] -["416","112","1-1","5","██","20","0","416","112","500","0","false","黑体","0"] -["442","121","1-1","5","██","23","0","442","121","500","0","false","黑体","0"] -["468","132","1-1","5","██","25","0","468","132","500","0","false","黑体","0"] -["494","144","1-1","5","██","27","0","494","144","500","0","false","黑体","0"] -["416","112","1-1","5"," ██","20","0","416","112","500","0","false","黑体","0"] -["442","121","1-1","5"," ██","23","0","442","121","500","0","false","黑体","0"] -["468","132","1-1","5"," ██","25","0","468","132","500","0","false","黑体","0"] -["494","144","1-1","5"," ██","27","0","494","144","500","0","false","黑体","0"] -["-10","243","1-1","5","██","320","0","-10","243","500","0","false","黑体","0"] -["12","225","1-1","5","██","321","0","12","225","500","0","false","黑体","0"] -["32","209","1-1","5","██","323","0","32","209","500","0","false","黑体","0"] -["52","193","1-1","5","██","325","0","52","193","500","0","false","黑体","0"] -["74","177","1-1","5","██","327","0","74","177","500","0","false","黑体","0"] -["97","162","1-1","5","██","330","0","97","162","500","0","false","黑体","0"] -["119","148","1-1","5","██","335","0","119","148","500","0","false","黑体","0"] -["143","136","1-1","5","██","337","0","143","136","500","0","false","黑体","0"] -["164","127","1-1","5","██","340","0","164","127","500","0","false","黑体","0"] -["190","117","1-1","5","██","345","0","190","117","500","0","false","黑体","0"] -["216","110","1-1","5","██","350","0","216","110","500","0","false","黑体","0"] -["242","105","1-1","5","██","357","0","242","105","500","0","false","黑体","0"] -["271","103","1-1","5","██","3","0","271","103","500","0","false","黑体","0"] -["298","104","1-1","5","██","6","0","298","104","500","0","false","黑体","0"] -["325","107","1-1","5","██","9","0","325","107","500","0","false","黑体","0"] -["351","111","1-1","5","██","12","0","351","111","500","0","false","黑体","0"] -["377","116","1-1","5","██","13","0","377","116","500","0","false","黑体","0"] -["399","121","1-1","5","██","13","0","399","121","500","0","false","黑体","0"] -["-10","243","1-1","5"," ██","320","0","-10","243","500","0","false","黑体","0"] -["12","225","1-1","5"," ██","321","0","12","225","500","0","false","黑体","0"] -["32","209","1-1","5"," ██","323","0","32","209","500","0","false","黑体","0"] -["52","193","1-1","5"," ██","325","0","52","193","500","0","false","黑体","0"] -["74","177","1-1","5"," ██","327","0","74","177","500","0","false","黑体","0"] -["97","162","1-1","5"," ██","330","0","97","162","500","0","false","黑体","0"] -["119","148","1-1","5"," ██","335","0","119","148","500","0","false","黑体","0"] -["143","136","1-1","5"," ██","337","0","143","136","500","0","false","黑体","0"] -["164","127","1-1","5"," ██","340","0","164","127","500","0","false","黑体","0"] -["190","117","1-1","5"," ██","345","0","190","117","500","0","false","黑体","0"] -["216","110","1-1","5"," ██","350","0","216","110","500","0","false","黑体","0"] -["242","105","1-1","5"," ██","357","0","242","105","500","0","false","黑体","0"] -["271","103","1-1","5"," ██","3","0","271","103","500","0","false","黑体","0"] -["298","104","1-1","5"," ██","6","0","298","104","500","0","false","黑体","0"] -["325","107","1-1","5"," ██","9","0","325","107","500","0","false","黑体","0"] -["351","111","1-1","5"," ██","12","0","351","111","500","0","false","黑体","0"] -["377","116","1-1","5"," ██","13","0","377","116","500","0","false","黑体","0"] -["399","121","1-1","5"," █","13","0","399","121","500","0","false","黑体","0"] -["422","126","1-1","5","██","20","0","422","126","500","0","false","黑体","0"] -["448","135","1-1","5","██","23","0","448","135","500","0","false","黑体","0"] -["473","145","1-1","5","██","25","0","473","145","500","0","false","黑体","0"] -["499","157","1-1","5","██","27","0","499","157","500","0","false","黑体","0"] -["422","126","1-1","5"," ██","20","0","422","126","500","0","false","黑体","0"] -["448","135","1-1","5"," ██","23","0","448","135","500","0","false","黑体","0"] -["473","145","1-1","5"," ██","25","0","473","145","500","0","false","黑体","0"] -["499","157","1-1","5"," ██","27","0","499","157","500","0","false","黑体","0"] -["356","96","1-1","5","●","0","0","356","96","500","0","false","黑体","0"] -["422","49","1-1","5","●","0","0","422","49","500","0","false","黑体","0"] -["430","127","1-1","5","●","0","0","430","127","500","0","false","黑体","0"] -["479","107","1-1","5","●","0","0","479","107","500","0","false","黑体","0"] -["365","104","1-1","5","●","0","0","365","104","500","0","false","黑体","0"] -["429","59","1-1","5","●","0","0","429","59","500","0","false","黑体","0"] -["434","129","1-1","5","●","0","0","434","129","500","0","false","黑体","0"] -["488","114","1-1","5","●","0","0","488","114","500","0","false","黑体","0"] -["379","112","1-1","5","●","0","0","379","112","500","0","false","黑体","0"] -["434","123","1-1","5","●","0","0","434","123","500","0","false","黑体","0"] -["436","74","1-1","5","●","0","0","436","74","500","0","false","黑体","0"] -["491","123","1-1","5","●","0","0","491","123","500","0","false","黑体","0"] -["441","75","1-1","5","●","0","0","441","75","500","0","false","黑体","0"] -["392","114","1-1","5","●","0","0","392","114","500","0","false","黑体","0"] -["441","127","1-1","5","●","0","0","441","127","500","0","false","黑体","0"] -["488","126","1-1","5","●","0","0","488","126","500","0","false","黑体","0"] -["257","236","1-1","5.5","大","325","0","106","189","500","0","false","黑体","0"] -["257","236","1-1","5.5","空","330","0","144","164","500","0","false","黑体","0"] -["257","236","1-1","5.5","へ","335","0","185","147","500","0","false","黑体","0"] -["257","236","1-1","5.5","と","348","0","227","132","500","0","false","黑体","0"] -["257","236","1-1","5.5","響","3","0","268","126","500","0","false","黑体","0"] -["257","236","1-1","5.5","け","10","0","314","129","500","0","false","黑体","0"] -["257","236","1-1","2.7","口","320","0","110","230","500","0","false","黑体","0"] -["257","236","1-1","2.7","風","328","0","146","202","500","0","false","黑体","0"] -["257","236","1-1","2.7","琴","332","0","182","180","500","0","false","黑体","0"] -["257","236","1-1","2.7","(","340","0","217","165","500","0","false","黑体","0"] -["257","236","1-1","2.7","H","348","0","238","157","500","0","false","黑体","0"] -["257","236","1-1","2.7","a","358","0","262","153","500","0","false","黑体","0"] -["257","236","1-1","2.7","r","4","0","289","154","500","0","false","黑体","0"] -["257","236","1-1","2.7","m","9","0","312","155","500","0","false","黑体","0"] -["257","236","1-1","2.7","o","14","0","338","160","500","0","false","黑体","0"] -["257","236","1-1","2.7","n","18","0","361","167","500","0","false","黑体","0"] -["257","236","1-1","2.7","i","21","0","382","176","500","0","false","黑体","0"] -["257","236","1-1","2.7","c","22","0","401","183","500","0","false","黑体","0"] -["257","236","1-1","2.7","a","23","0","421","194","500","0","false","黑体","0"] -["257","236","1-1","2.7",")","25","0","439","206","500","0","false","黑体","0"] -["-5","-5","1-1","10","███████","0","0","-5","-5","500","0","false","MS UI Gothic","0"] -["-5","-5","1-1","10"," ██████","0","0","-5","-5","500","0","false","MS UI Gothic","0"] -["-5","10","1-1","10","███████","0","0","-5","10","500","0","false","MS UI Gothic","0"] -["-5","10","1-1","10"," ██████","0","0","-5","10","500","0","false","MS UI Gothic","0"] -["-5","32","1-1","10","███████","0","0","-5","32","500","0","false","MS UI Gothic","0"] -["-5","32","1-1","10"," ██████","0","0","-5","32","500","0","false","MS UI Gothic","0"] -["-5","63","1-1","10","███████","0","0","-5","63","500","0","false","MS UI Gothic","0"] -["-5","63","1-1","10"," ██████","0","0","-5","63","500","0","false","MS UI Gothic","0"] -["-5","100","1-1","10","███████","0","0","-5","100","500","0","false","MS UI Gothic","0"] -["-5","100","1-1","10"," ██████","0","0","-5","100","500","0","false","MS UI Gothic","0"] -["-5","124","1-1","10","███████/n","0","0","-5","124","500","0","false","MS UI Gothic","0"] -["-5","124","1-1","10"," ██████/n","0","0","-5","124","500","0","false","MS UI Gothic","0"] -["-5","148","1-1","10","███████/n","0","0","-5","148","500","0","false","MS UI Gothic","0"] -["-5","148","1-1","10"," ██████/n","0","0","-5","148","500","0","false","MS UI Gothic","0"] -["-5","175","1-1","10","███████/n","0","0","-5","175","500","0","false","MS UI Gothic","0"] -["-5","175","1-1","10"," ██████/n","0","0","-5","175","500","0","false","MS UI Gothic","0"] -["-5","201","1-1","10","███████/n","0","0","-5","201","500","0","false","MS UI Gothic","0"] -["-5","201","1-1","10"," ██████/n","0","0","-5","201","500","0","false","MS UI Gothic","0"] -["-5","234","1-1","10","███████/n/n","0","0","-5","234","500","0","false","MS UI Gothic","0"] -["-5","234","1-1","10"," ██████/n/n","0","0","-5","234","500","0","false","MS UI Gothic","0"] -["-5","275","1-1","10","███████/n/n","0","0","-5","275","500","0","false","MS UI Gothic","0"] -["-5","275","1-1","10"," ██████/n/n","0","0","-5","275","500","0","false","MS UI Gothic","0"] -["-10","134","1-1","10","██","320","0","-10","134","500","0","false","黑体","0"] -["16","114","1-1","10","██","322","0","16","114","500","0","false","黑体","0"] -["39","97","1-1","10","██","326","0","39","97","500","0","false","黑体","0"] -["60","83","1-1","10","██","331","0","60","83","500","0","false","黑体","0"] -["86","69","1-1","10","██","335","0","86","69","500","0","false","黑体","0"] -["113","57","1-1","10","██","339","0","113","57","500","0","false","黑体","0"] -["137","48","1-1","10","██","343","0","137","48","500","0","false","黑体","0"] -["164","40","1-1","10","██","348","0","164","40","500","0","false","黑体","0"] -["192","34","1-1","10","██","351","0","192","34","500","0","false","黑体","0"] -["221","30","1-1","10","██","355","0","221","30","500","0","false","黑体","0"] -["247","28","1-1","10","██","358","0","247","28","500","0","false","黑体","0"] -["274","27","1-1","10","██","2","0","274","27","500","0","false","黑体","0"] -["301","28","1-1","10","██","5","0","301","28","500","0","false","黑体","0"] -["326","30","1-1","10","██","9","0","326","30","500","0","false","黑体","0"] -["354","34","1-1","10","██","13","0","354","34","500","0","false","黑体","0"] -["388","41","1-1","10","██","15","0","388","41","500","0","false","宋体","0"] -["414","48","1-1","10","██","16","0","414","48","500","0","false","宋体","0"] -["435","54","1-1","10","██","20","0","435","54","500","0","false","黑体","0"] -["458","62","1-1","10","██","24","0","458","62","500","0","false","黑体","0"] -["475","69","1-1","10","██","27","0","475","69","500","0","false","黑体","0"] -["388","41","1-1","10"," ██","15","0","388","41","500","0","false","宋体","0"] -["414","48","1-1","10"," ██","16","0","414","48","500","0","false","宋体","0"] -["435","54","1-1","10"," ██","20","0","435","54","500","0","false","黑体","0"] -["458","62","1-1","10"," ██","24","0","458","62","500","0","false","黑体","0"] -["475","69","1-1","10"," ██","27","0","475","69","500","0","false","黑体","0"] -["-10","134","1-1","10"," ██","320","0","-10","134","500","0","false","黑体","0"] -["16","114","1-1","10"," ██","322","0","16","114","500","0","false","黑体","0"] -["39","97","1-1","10"," ██","326","0","39","97","500","0","false","黑体","0"] -["60","83","1-1","10"," ██","331","0","60","83","500","0","false","黑体","0"] -["86","69","1-1","10"," ██","335","0","86","69","500","0","false","黑体","0"] -["113","57","1-1","10"," ██","339","0","113","57","500","0","false","黑体","0"] -["137","48","1-1","10"," ██","343","0","137","48","500","0","false","黑体","0"] -["164","40","1-1","10"," ██","348","0","164","40","500","0","false","黑体","0"] -["192","34","1-1","10"," ██","351","0","192","34","500","0","false","黑体","0"] -["221","30","1-1","10"," ██","355","0","221","30","500","0","false","黑体","0"] -["247","28","1-1","10"," ██","358","0","247","28","500","0","false","黑体","0"] -["274","27","1-1","10"," ██","2","0","274","27","500","0","false","黑体","0"] -["301","28","1-1","10"," ██","5","0","301","28","500","0","false","黑体","0"] -["326","30","1-1","10"," ██","9","0","326","30","500","0","false","黑体","0"] -["354","34","1-1","10"," ██","13","0","354","34","500","0","false","黑体","0"] -["7","122","1-1","10","█","320","0","7","122","500","0","false","黑体","0"] -["-10","152","1-1","10","██","320","0","-10","152","500","0","false","黑体","0"] -["14","133","1-1","10","██","322","0","14","133","500","0","false","黑体","0"] -["36","116","1-1","10","██","325","0","36","116","500","0","false","黑体","0"] -["59","100","1-1","10","██","330","0","59","100","500","0","false","黑体","0"] -["86","85","1-1","10","██","333","0","86","85","500","0","false","黑体","0"] -["109","74","1-1","10","██","337","0","109","74","500","0","false","黑体","0"] -["133","64","1-1","10","██","341","0","133","64","500","0","false","黑体","0"] -["162","54","1-1","10","██","345","0","162","54","500","0","false","黑体","0"] -["188","47","1-1","10","██","349","0","188","47","500","0","false","黑体","0"] -["217","41","1-1","10","██","355","0","217","41","500","0","false","黑体","0"] -["246","39","1-1","10","██","0","0","246","39","500","0","false","黑体","0"] -["274","38","1-1","10","██","2","0","274","38","500","0","false","黑体","0"] -["302","39","1-1","10","██","6","0","302","39","500","0","false","黑体","0"] -["329","42","1-1","10","██","10","0","329","42","500","0","false","黑体","0"] -["357","47","1-1","10","██","13","0","357","47","500","0","false","黑体","0"] -["-10","152","1-1","10"," ██","320","0","-10","152","500","0","false","黑体","0"] -["14","133","1-1","10"," ██","322","0","14","133","500","0","false","黑体","0"] -["36","116","1-1","10"," ██","325","0","36","116","500","0","false","黑体","0"] -["59","100","1-1","10"," ██","330","0","59","100","500","0","false","黑体","0"] -["86","85","1-1","10"," ██","333","0","86","85","500","0","false","黑体","0"] -["109","74","1-1","10"," ██","337","0","109","74","500","0","false","黑体","0"] -["133","64","1-1","10"," ██","341","0","133","64","500","0","false","黑体","0"] -["162","54","1-1","10"," ██","345","0","162","54","500","0","false","黑体","0"] -["188","47","1-1","10"," ██","349","0","188","47","500","0","false","黑体","0"] -["217","41","1-1","10"," ██","355","0","217","41","500","0","false","黑体","0"] -["246","39","1-1","10"," ██","0","0","246","39","500","0","false","黑体","0"] -["274","38","1-1","10"," ██","2","0","274","38","500","0","false","黑体","0"] -["302","39","1-1","10"," ██","6","0","302","39","500","0","false","黑体","0"] -["329","42","1-1","10"," ██","10","0","329","42","500","0","false","黑体","0"] -["357","47","1-1","10"," ██","13","0","357","47","500","0","false","黑体","0"] -["389","54","1-1","10","██","15","0","389","54","500","0","false","黑体","0"] -["416","61","1-1","10","██","19","0","416","61","500","0","false","黑体","0"] -["442","69","1-1","10","██","22","0","442","69","500","0","false","黑体","0"] -["464","77","1-1","10","██","25","0","464","77","500","0","false","黑体","0"] -["490","88","1-1","10","██","28","0","490","88","500","0","false","黑体","0"] -["389","54","1-1","10"," ██","15","0","389","54","500","0","false","黑体","0"] -["416","61","1-1","10"," ██","19","0","416","61","500","0","false","黑体","0"] -["442","69","1-1","10"," ██","22","0","442","69","500","0","false","黑体","0"] -["464","77","1-1","10"," ██","25","0","464","77","500","0","false","黑体","0"] -["490","88","1-1","10"," █","28","0","490","88","500","0","false","黑体","0"] -["-10","169","1-1","10","██","320","0","-10","169","500","0","false","黑体","0"] -["13","150","1-1","10","██","322","0","13","150","500","0","false","黑体","0"] -["34","134","1-1","10","██","325","0","34","134","500","0","false","黑体","0"] -["59","117","1-1","10","██","328","0","59","117","500","0","false","黑体","0"] -["76","107","1-1","10","██","331","0","76","107","500","0","false","黑体","0"] -["103","92","1-1","10","██","335","0","103","92","500","0","false","黑体","0"] -["132","79","1-1","10","██","339","0","132","79","500","0","false","黑体","0"] -["159","69","1-1","10","██","342","0","159","69","500","0","false","黑体","0"] -["187","60","1-1","10","██","347","0","187","60","500","0","false","黑体","0"] -["212","54","1-1","10","██","354","0","212","54","500","0","false","黑体","0"] -["239","51","1-1","10","██","0","0","239","51","500","0","false","黑体","0"] -["263","51","1-1","10","██","2","0","263","51","500","0","false","黑体","0"] -["288","52","1-1","10","██","3","0","288","52","500","0","false","黑体","0"] -["316","54","1-1","10","██","7","0","316","54","500","0","false","黑体","0"] -["347","58","1-1","10","██","12","0","347","58","500","0","false","黑体","0"] -["371","63","1-1","10","██","15","0","371","63","500","0","false","黑体","0"] -["-10","169","1-1","10"," ██","320","0","-10","169","500","0","false","黑体","0"] -["13","150","1-1","10"," ██","322","0","13","150","500","0","false","黑体","0"] -["34","134","1-1","10"," ██","325","0","34","134","500","0","false","黑体","0"] -["59","117","1-1","10"," ██","328","0","59","117","500","0","false","黑体","0"] -["76","107","1-1","10"," ██","331","0","76","107","500","0","false","黑体","0"] -["103","92","1-1","10"," ██","335","0","103","92","500","0","false","黑体","0"] -["132","79","1-1","10"," ██","339","0","132","79","500","0","false","黑体","0"] -["159","69","1-1","10"," ██","342","0","159","69","500","0","false","黑体","0"] -["187","60","1-1","10"," ██","347","0","187","60","500","0","false","黑体","0"] -["212","54","1-1","10"," ██","354","0","212","54","500","0","false","黑体","0"] -["239","51","1-1","10"," ██","0","0","239","51","500","0","false","黑体","0"] -["263","51","1-1","10"," ██","2","0","263","51","500","0","false","黑体","0"] -["288","52","1-1","10"," ██","3","0","288","52","500","0","false","黑体","0"] -["316","54","1-1","10"," ██","7","0","316","54","500","0","false","黑体","0"] -["347","58","1-1","10"," ██","12","0","347","58","500","0","false","黑体","0"] -["371","63","1-1","10"," █","15","0","371","63","500","0","false","黑体","0"] -["400","70","1-1","10","██","19","0","400","70","500","0","false","黑体","0"] -["426","78","1-1","10","██","21","0","426","78","500","0","false","黑体","0"] -["451","87","1-1","10","██","24","0","451","87","500","0","false","黑体","0"] -["477","98","1-1","10","██","26","0","477","98","500","0","false","黑体","0"] -["400","70","1-1","10"," ██","19","0","400","70","500","0","false","黑体","0"] -["426","78","1-1","10"," ██","21","0","426","78","500","0","false","黑体","0"] -["451","87","1-1","10"," ██","24","0","451","87","500","0","false","黑体","0"] -["477","98","1-1","10"," ██","26","0","477","98","500","0","false","黑体","0"] -["-10","187","1-1","10","██","320","0","-10","187","500","0","false","黑体","0"] -["13","168","1-1","10","██","322","0","13","168","500","0","false","黑体","0"] -["37","151","1-1","10","██","324","0","37","151","500","0","false","黑体","0"] -["62","133","1-1","10","██","327","0","62","133","500","0","false","黑体","0"] -["86","118","1-1","10","██","331","0","86","118","500","0","false","黑体","0"] -["113","104","1-1","10","██","334","0","113","104","500","0","false","黑体","0"] -["142","91","1-1","10","██","339","0","142","91","500","0","false","黑体","0"] -["171","80","1-1","10","██","343","0","171","80","500","0","false","黑体","0"] -["201","71","1-1","10","██","349","0","201","71","500","0","false","黑体","0"] -["229","65","1-1","10","██","356","0","229","65","500","0","false","黑体","0"] -["257","63","1-1","10","██","2","0","257","63","500","0","false","黑体","0"] -["289","64","1-1","10","██","6","0","289","64","500","0","false","黑体","0"] -["319","67","1-1","10","██","8","0","319","67","500","0","false","黑体","0"] -["345","70","1-1","10","██","12","0","345","70","500","0","false","黑体","0"] -["373","76","1-1","10","██","16","0","373","76","500","0","false","黑体","0"] -["-10","187","1-1","10"," ██","320","0","-10","187","500","0","false","黑体","0"] -["13","168","1-1","10"," ██","322","0","13","168","500","0","false","黑体","0"] -["37","151","1-1","10"," ██","324","0","37","151","500","0","false","黑体","0"] -["62","133","1-1","10"," ██","327","0","62","133","500","0","false","黑体","0"] -["86","118","1-1","10"," ██","331","0","86","118","500","0","false","黑体","0"] -["113","104","1-1","10"," ██","334","0","113","104","500","0","false","黑体","0"] -["142","91","1-1","10"," ██","339","0","142","91","500","0","false","黑体","0"] -["171","80","1-1","10"," ██","343","0","171","80","500","0","false","黑体","0"] -["201","71","1-1","10"," ██","349","0","201","71","500","0","false","黑体","0"] -["229","65","1-1","10"," ██","356","0","229","65","500","0","false","黑体","0"] -["257","63","1-1","10"," ██","2","0","257","63","500","0","false","黑体","0"] -["289","64","1-1","10"," ██","6","0","289","64","500","0","false","黑体","0"] -["319","67","1-1","10"," ██","8","0","319","67","500","0","false","黑体","0"] -["345","70","1-1","10"," ██","12","0","345","70","500","0","false","黑体","0"] -["373","76","1-1","10"," ██","16","0","373","76","500","0","false","黑体","0"] -["408","86","1-1","10","██","19","0","408","86","500","0","false","黑体","0"] -["436","95","1-1","10","██","22","0","436","95","500","0","false","黑体","0"] -["459","104","1-1","10","██","24","0","459","104","500","0","false","黑体","0"] -["483","114","1-1","10","██","25","0","483","114","500","0","false","黑体","0"] -["408","86","1-1","10"," ██","19","0","408","86","500","0","false","黑体","0"] -["436","95","1-1","10"," ██","22","0","436","95","500","0","false","黑体","0"] -["459","104","1-1","10"," ██","24","0","459","104","500","0","false","黑体","0"] -["483","114","1-1","10"," ██","25","0","483","114","500","0","false","黑体","0"] -["-10","205","1-1","10","██","320","0","-10","205","500","0","false","黑体","0"] -["13","186","1-1","10","██","321","0","13","186","500","0","false","黑体","0"] -["36","168","1-1","10","██","324","0","36","168","500","0","false","黑体","0"] -["61","150","1-1","10","██","327","0","61","150","500","0","false","黑体","0"] -["85","135","1-1","10","██","330","0","85","135","500","0","false","黑体","0"] -["111","120","1-1","10","██","334","0","111","120","500","0","false","黑体","0"] -["138","107","1-1","10","██","338","0","138","107","500","0","false","黑体","0"] -["166","96","1-1","10","██","341","0","166","96","500","0","false","黑体","0"] -["193","87","1-1","10","██","348","0","193","87","500","0","false","黑体","0"] -["222","81","1-1","10","██","354","0","222","81","500","0","false","黑体","0"] -["250","78","1-1","10","██","0","0","250","78","500","0","false","黑体","0"] -["276","78","1-1","10","██","4","0","276","78","500","0","false","黑体","0"] -["306","80","1-1","10","██","7","0","306","80","500","0","false","黑体","0"] -["333","83","1-1","10","██","10","0","333","83","500","0","false","黑体","0"] -["364","89","1-1","10","██","12","0","364","89","500","0","false","黑体","0"] -["386","95","1-1","10","██","13","0","386","95","500","0","false","黑体","0"] -["-10","205","1-1","10"," ██","320","0","-10","205","500","0","false","黑体","0"] -["13","186","1-1","10"," ██","321","0","13","186","500","0","false","黑体","0"] -["36","168","1-1","10"," ██","324","0","36","168","500","0","false","黑体","0"] -["61","150","1-1","10"," ██","327","0","61","150","500","0","false","黑体","0"] -["85","135","1-1","10"," ██","330","0","85","135","500","0","false","黑体","0"] -["111","120","1-1","10"," ██","334","0","111","120","500","0","false","黑体","0"] -["138","107","1-1","10"," ██","338","0","138","107","500","0","false","黑体","0"] -["166","96","1-1","10"," ██","341","0","166","96","500","0","false","黑体","0"] -["193","87","1-1","10"," ██","348","0","193","87","500","0","false","黑体","0"] -["222","81","1-1","10"," ██","354","0","222","81","500","0","false","黑体","0"] -["250","78","1-1","10"," ██","0","0","250","78","500","0","false","黑体","0"] -["276","78","1-1","10"," ██","4","0","276","78","500","0","false","黑体","0"] -["306","80","1-1","10"," ██","7","0","306","80","500","0","false","黑体","0"] -["333","83","1-1","10"," ██","10","0","333","83","500","0","false","黑体","0"] -["364","89","1-1","10"," ██","12","0","364","89","500","0","false","黑体","0"] -["386","95","1-1","10"," █","13","0","386","95","500","0","false","黑体","0"] -["415","101","1-1","10","██","19","0","415","101","500","0","false","黑体","0"] -["441","109","1-1","10","██","24","0","441","109","500","0","false","黑体","0"] -["466","119","1-1","10","██","25","0","466","119","500","0","false","黑体","0"] -["489","129","1-1","10","██","27","0","489","129","500","0","false","黑体","0"] -["415","101","1-1","10"," ██","19","0","415","101","500","0","false","黑体","0"] -["441","109","1-1","10"," ██","24","0","441","109","500","0","false","黑体","0"] -["466","119","1-1","10"," ██","25","0","466","119","500","0","false","黑体","0"] -["489","129","1-1","10"," ██","27","0","489","129","500","0","false","黑体","0"] -["-10","224","1-1","10","██","320","0","-10","224","500","0","false","黑体","0"] -["14","204","1-1","10","██","321","0","14","204","500","0","false","黑体","0"] -["37","186","1-1","10","██","324","0","37","186","500","0","false","黑体","0"] -["60","169","1-1","10","██","326","0","60","169","500","0","false","黑体","0"] -["81","155","1-1","10","██","328","0","81","155","500","0","false","黑体","0"] -["106","140","1-1","10","██","332","0","106","140","500","0","false","黑体","0"] -["132","127","1-1","10","██","335","0","132","127","500","0","false","黑体","0"] -["160","114","1-1","10","██","339","0","160","114","500","0","false","黑体","0"] -["189","103","1-1","10","██","346","0","189","103","500","0","false","黑体","0"] -["217","96","1-1","10","██","352","0","217","96","500","0","false","黑体","0"] -["247","92","1-1","10","██","358","0","247","92","500","0","false","黑体","0"] -["277","91","1-1","10","██","3","0","277","91","500","0","false","黑体","0"] -["306","92","1-1","10","██","7","0","306","92","500","0","false","黑体","0"] -["334","95","1-1","10","██","9","0","334","95","500","0","false","黑体","0"] -["364","100","1-1","10","██","12","0","364","100","500","0","false","黑体","0"] -["390","106","1-1","10","██","13","0","390","106","500","0","false","黑体","0"] -["-10","224","1-1","10"," ██","320","0","-10","224","500","0","false","黑体","0"] -["14","204","1-1","10"," ██","321","0","14","204","500","0","false","黑体","0"] -["37","186","1-1","10"," ██","324","0","37","186","500","0","false","黑体","0"] -["60","169","1-1","10"," ██","326","0","60","169","500","0","false","黑体","0"] -["81","155","1-1","10"," ██","328","0","81","155","500","0","false","黑体","0"] -["106","140","1-1","10"," ██","332","0","106","140","500","0","false","黑体","0"] -["132","127","1-1","10"," ██","335","0","132","127","500","0","false","黑体","0"] -["160","114","1-1","10"," ██","339","0","160","114","500","0","false","黑体","0"] -["189","103","1-1","10"," ██","346","0","189","103","500","0","false","黑体","0"] -["217","96","1-1","10"," ██","352","0","217","96","500","0","false","黑体","0"] -["247","92","1-1","10"," ██","358","0","247","92","500","0","false","黑体","0"] -["277","91","1-1","10"," ██","3","0","277","91","500","0","false","黑体","0"] -["306","92","1-1","10"," ██","7","0","306","92","500","0","false","黑体","0"] -["334","95","1-1","10"," ██","9","0","334","95","500","0","false","黑体","0"] -["364","100","1-1","10"," ██","12","0","364","100","500","0","false","黑体","0"] -["390","106","1-1","10"," █","13","0","390","106","500","0","false","黑体","0"] -["416","112","1-1","10","██","20","0","416","112","500","0","false","黑体","0"] -["442","121","1-1","10","██","23","0","442","121","500","0","false","黑体","0"] -["468","132","1-1","10","██","25","0","468","132","500","0","false","黑体","0"] -["494","144","1-1","10","██","27","0","494","144","500","0","false","黑体","0"] -["416","112","1-1","10"," ██","20","0","416","112","500","0","false","黑体","0"] -["442","121","1-1","10"," ██","23","0","442","121","500","0","false","黑体","0"] -["468","132","1-1","10"," ██","25","0","468","132","500","0","false","黑体","0"] -["494","144","1-1","10"," ██","27","0","494","144","500","0","false","黑体","0"] -["-10","243","1-1","10","██","320","0","-10","243","500","0","false","黑体","0"] -["12","225","1-1","10","██","321","0","12","225","500","0","false","黑体","0"] -["32","209","1-1","10","██","323","0","32","209","500","0","false","黑体","0"] -["52","193","1-1","10","██","325","0","52","193","500","0","false","黑体","0"] -["74","177","1-1","10","██","327","0","74","177","500","0","false","黑体","0"] -["97","162","1-1","10","██","330","0","97","162","500","0","false","黑体","0"] -["119","148","1-1","10","██","335","0","119","148","500","0","false","黑体","0"] -["143","136","1-1","10","██","337","0","143","136","500","0","false","黑体","0"] -["164","127","1-1","10","██","340","0","164","127","500","0","false","黑体","0"] -["190","117","1-1","10","██","345","0","190","117","500","0","false","黑体","0"] -["216","110","1-1","10","██","350","0","216","110","500","0","false","黑体","0"] -["242","105","1-1","10","██","357","0","242","105","500","0","false","黑体","0"] -["271","103","1-1","10","██","3","0","271","103","500","0","false","黑体","0"] -["298","104","1-1","10","██","6","0","298","104","500","0","false","黑体","0"] -["325","107","1-1","10","██","9","0","325","107","500","0","false","黑体","0"] -["351","111","1-1","10","██","12","0","351","111","500","0","false","黑体","0"] -["377","116","1-1","10","██","13","0","377","116","500","0","false","黑体","0"] -["399","121","1-1","10","██","13","0","399","121","500","0","false","黑体","0"] -["-10","243","1-1","10"," ██","320","0","-10","243","500","0","false","黑体","0"] -["12","225","1-1","10"," ██","321","0","12","225","500","0","false","黑体","0"] -["32","209","1-1","10"," ██","323","0","32","209","500","0","false","黑体","0"] -["52","193","1-1","10"," ██","325","0","52","193","500","0","false","黑体","0"] -["74","177","1-1","10"," ██","327","0","74","177","500","0","false","黑体","0"] -["97","162","1-1","10"," ██","330","0","97","162","500","0","false","黑体","0"] -["119","148","1-1","10"," ██","335","0","119","148","500","0","false","黑体","0"] -["143","136","1-1","10"," ██","337","0","143","136","500","0","false","黑体","0"] -["164","127","1-1","10"," ██","340","0","164","127","500","0","false","黑体","0"] -["190","117","1-1","10"," ██","345","0","190","117","500","0","false","黑体","0"] -["216","110","1-1","10"," ██","350","0","216","110","500","0","false","黑体","0"] -["242","105","1-1","10"," ██","357","0","242","105","500","0","false","黑体","0"] -["271","103","1-1","10"," ██","3","0","271","103","500","0","false","黑体","0"] -["298","104","1-1","10"," ██","6","0","298","104","500","0","false","黑体","0"] -["325","107","1-1","10"," ██","9","0","325","107","500","0","false","黑体","0"] -["351","111","1-1","10"," ██","12","0","351","111","500","0","false","黑体","0"] -["377","116","1-1","10"," ██","13","0","377","116","500","0","false","黑体","0"] -["399","121","1-1","10"," █","13","0","399","121","500","0","false","黑体","0"] -["422","126","1-1","10","██","20","0","422","126","500","0","false","黑体","0"] -["448","135","1-1","10","██","23","0","448","135","500","0","false","黑体","0"] -["473","145","1-1","10","██","25","0","473","145","500","0","false","黑体","0"] -["499","157","1-1","10","██","27","0","499","157","500","0","false","黑体","0"] -["422","126","1-1","10"," ██","20","0","422","126","500","0","false","黑体","0"] -["448","135","1-1","10"," ██","23","0","448","135","500","0","false","黑体","0"] -["473","145","1-1","10"," ██","25","0","473","145","500","0","false","黑体","0"] -["499","157","1-1","10"," ██","27","0","499","157","500","0","false","黑体","0"] -["356","96","1-1","10","●","0","0","356","96","500","0","false","黑体","0"] -["422","49","1-1","10","●","0","0","422","49","500","0","false","黑体","0"] -["430","127","1-1","10","●","0","0","430","127","500","0","false","黑体","0"] -["479","107","1-1","10","●","0","0","479","107","500","0","false","黑体","0"] -["365","104","1-1","10","●","0","0","365","104","500","0","false","黑体","0"] -["429","59","1-1","10","●","0","0","429","59","500","0","false","黑体","0"] -["434","129","1-1","10","●","0","0","434","129","500","0","false","黑体","0"] -["488","114","1-1","10","●","0","0","488","114","500","0","false","黑体","0"] -["379","112","1-1","10","●","0","0","379","112","500","0","false","黑体","0"] -["434","123","1-1","10","●","0","0","434","123","500","0","false","黑体","0"] -["436","74","1-1","10","●","0","0","436","74","500","0","false","黑体","0"] -["491","123","1-1","10","●","0","0","491","123","500","0","false","黑体","0"] -["441","75","1-1","10","●","0","0","441","75","500","0","false","黑体","0"] -["392","114","1-1","10","●","0","0","392","114","500","0","false","黑体","0"] -["441","127","1-1","10","●","0","0","441","127","500","0","false","黑体","0"] -["488","126","1-1","10","●","0","0","488","126","500","0","false","黑体","0"] -["340","145","1-0","4.5","○","90","70","340","145","500","0","false","黑体","0"] -["338","240","1-0","3","天使が抱いた","30","0","338","240","500","0","false","黑体","0"] -["67","317","1-0","3","たい抱が使天","330","0","67","317","500","0","false","黑体","0"] -["59","290","1-0","3","たい抱が使天","340","0","59","290","500","0","false","黑体","0"] -["336","237","1-0","3","天使が抱いた","20","0","336","237","500","0","false","黑体","0"] -["334","237","1-0","3","天使が抱いた","10","0","334","237","500","0","false","黑体","0"] -["56","263","1-0","3","たい抱が使天","350","0","56","263","500","0","false","黑体","0"] -["56","236","1-0","3","たい抱が使天","0","0","56","236","500","0","false","黑体","0"] -["333","236","1-0","3","天使が抱いた","0","0","333","236","500","0","false","黑体","0"] -["333","236","1-0","3","天使が抱いた","350","0","333","236","500","0","false","黑体","0"] -["61","208","1-0","3","たい抱が使天","10","0","61","208","500","0","false","黑体","0"] -["61","181","1-0","3","たい抱が使天","20","0","61","181","500","0","false","黑体","0"] -["336","236","1-0","3","天使が抱いた","340","0","336","236","500","0","false","黑体","0"] -["343","236","1-0","3","天使が抱いた","330","0","343","236","500","0","false","黑体","0"] -["65","157","1-0","3","たい抱が使天","30","0","65","157","500","0","false","黑体","0"] -["70","134","1-0","3","たい抱が使天","35","0","70","134","500","0","false","黑体","0"] -["350","225","1-0","3","天使が抱いた","325","0","350","225","500","0","false","黑体","0"] -["354","213","1-0","3","天使が抱いた","320","0","354","213","500","0","false","黑体","0"] -["73","111","1-0","3","たい抱が使天","40","0","73","111","500","0","false","黑体","0"] -["362","200","1-0","3","天使が抱いた","315","0","362","200","500","0","false","黑体","0"] -["76","92","1-0","3","たい抱が使天","45","0","76","92","500","0","false","黑体","0"] -["80","69","1-0","3","たい抱が使天","50","0","80","69","500","0","false","黑体","0"] -["369","186","1-0","3","天使が抱いた","310","0","369","186","500","0","false","黑体","0"] -["378","173","1-0","3","天使が抱いた","305","0","378","173","500","0","false","黑体","0"] -["84","46","1-0","3","たい抱が使天","55","0","84","46","500","0","false","黑体","0"] -["104","44","1-0","3","たい抱が使天","58","0","104","44","500","0","false","黑体","0"] -["365","172","1-0","3","天使が抱いた","302","0","365","172","500","0","false","黑体","0"] -["135","42","1-1","4.5","███████/n█     █/n█     █/n█     █/n███████/n█     █/n█     █/n█     █/n███████","0","130","135","42","0","0","true","MS UI Gothic","0"] -["404","42","1-1","4.5","███████/n█     █/n█     █/n█     █/n███████/n█     █/n█     █/n█     █/n███████","0","50","404","42","0","0","true","MS UI Gothic","0"] -["-8","0","1-1","4.5","███████████████████████████","0","0","-8","0","0","0","true","MS UI Gothic","0"] -["-8","0","1-1","4.5"," ██████████████████████████","0","0","-8","0","0","0","false","MS UI Gothic","0"] -["-8","350","1-1","4.5","███████████████████████████","0","0","-8","350","0","0","true","MS UI Gothic","0"] -["-8","350","1-1","4.5"," ██████████████████████████","0","0","-8","350","0","0","false","MS UI Gothic","0"] -["-4","0","1-1","4.5","█/n█/n█/n█/n█/n█/n█/n█/n█/n█/n█/n█/n","0","0","-4","0","0","0","true","MS UI Gothic","0"] -["-4","5","1-1","4.5","█/n█/n█/n█/n█/n█/n█/n█/n█/n█/n█/n█/n","0","0","-4","5","0","0","false","MS UI Gothic","0"] -["519","0","1-1","4.5","█/n█/n█/n█/n█/n█/n█/n█/n█/n█/n█/n█/n","0","0","519","0","0","0","true","MS UI Gothic","0"] -["519","5","1-1","4.5","█/n█/n█/n█/n█/n█/n█/n█/n█/n█/n█/n█/n","0","0","519","5","0","0","false","MS UI Gothic","0"] -["165","353","0-1","4.5","窓枠の畫布(Toile)","0","0","165","353","500","0","true","黑体","0"] -["97","180","0-1","0.5","ねぇ…その風景畫(Paysage)","0","0","127","242","500","0","false","黑体","0"] -["127","242","1-1","2","ねぇ…その風景畫(Paysage)","0","0","127","242","500","0","false","黑体","0"] -["127","242","1-0","2","ねぇ…その風景畫(Paysage)","0","0","107","242","2000","0","false","黑体","0"] -["127","242","1-0","2","ねぇ…その風景畫(Paysage)","0","0","147","242","2000","0","false","黑体","0"] -["127","242","1-0","2","ねぇ…その風景畫(Paysage)","0","0","112","242","2000","0","false","黑体","0"] -["127","242","1-0","2","ねぇ…その風景畫(Paysage)","0","0","142","242","2000","0","false","黑体","0"] -["127","242","1-0","2","ねぇ…その風景畫(Paysage)","0","0","117","242","2000","0","false","黑体","0"] -["127","242","1-0","2","ねぇ…その風景畫(Paysage)","0","0","137","242","2000","0","false","黑体","0"] -["127","242","1-0","2","ねぇ…その風景畫(Paysage)","0","0","122","242","2000","0","false","黑体","0"] -["127","242","1-0","2","ねぇ…その風景畫(Paysage)","0","0","132","242","2000","0","false","黑体","0"] -["97","180","0-1","0.5","綺麗かしら?","0","0","201","285","500","0","false","黑体","0"] -["201","285","1-1","2","綺麗かしら?","0","0","201","285","2000","0","false","黑体","0"] -["201","285","1-0","2","綺麗かしら?","0","0","206","285","2000","0","false","黑体","0"] -["201","285","1-0","2","綺麗かしら?","0","0","196","285","2000","0","false","黑体","0"] -["201","285","1-0","2","綺麗かしら?","0","0","211","285","2000","0","false","黑体","0"] -["201","285","1-0","2","綺麗かしら?","0","0","191","285","2000","0","false","黑体","0"] -["201","285","1-0","2","綺麗かしら?","0","0","216","285","2000","0","false","黑体","0"] -["201","285","1-0","2","綺麗かしら?","0","0","186","285","2000","0","false","黑体","0"] -["201","285","1-0","2","綺麗かしら?","0","0","221","285","2000","0","false","黑体","0"] -["201","285","1-0","2","綺麗かしら?","0","0","181","285","2000","0","false","黑体","0"] -["119","329","0-1","4.5","在天空中回响的","0","0","119","329","500","0","true","黑体","0"] -["119","329","0-1","4","/n    口琴的乐声(Harmonica)","0","0","119","329","500","0","true","黑体","0"] -["123","328","0-1","5.5","天使拥抱着窗沿为框的画布(Toile)","0","0","123","328","500","0","true","黑体","0"] -["162","321","0-1","2","呐…这幅风景画(Paysage)","0","0","162","321","500","0","true","黑体","0"] -["237","347","0-1","2","漂亮吗?/n","0","0","237","347","500","0","true","黑体","0"] -["162","321","1-0","2","呐…这幅风景画(Paysage)","0","0","162","321","500","0","true","黑体","0"] -["237","347","1-0","2","漂亮吗?/n","0","0","237","347","500","0","true","黑体","0"] -["253","93","0-0.8","2","わたしは","0","0","153","93","2000","0","false","黑体","0"] -["153","93","0.8-0","2","わたしは","0","0","53","93","2000","0","false","黑体","0"] -["203","93","0-0.8","2","/n世界で一番美しい光を見った","0","0","103","93","2000","0","false","黑体","0"] -["103","93","0.8-0","2","/n世界で一番美しい光を見った","0","0","3","93","2000","0","false","黑体","0"] -["253","85","0-0.8","2","その花を胸に抱いて","0","0","153","85","2000","0","false","黑体","0"] -["153","85","0.8-0","2","その花を胸に抱いて","0","0","53","85","2000","0","false","黑体","0"] -["263","85","0-0.8","2","/nLaurantの分も","0","0","163","85","2000","0","false","黑体","0"] -["163","85","0.8-0","2","/nLaurantの分も","0","0","63","85","2000","0","false","黑体","0"] -["273","85","0-0.8","2","/n/n詠い続けよう","0","0","173","85","2000","0","false","黑体","0"] -["173","85","0.8-0","2","/n/n詠い続けよう","0","0","73","85","2000","0","false","黑体","0"] -["250","93","0-1","2","わたしは","0","0","150","93","2000","0","true","黑体","0"] -["150","93","1-0","2","わたしは","0","0","50","93","2000","0","true","黑体","0"] -["200","93","0-1","2","/n世界で一番美しい光を見った","0","0","100","93","2000","0","true","黑体","0"] -["100","93","1-0","2","/n世界で一番美しい光を見った","0","0","0","93","2000","0","true","黑体","0"] -["250","85","0-1","2","その花を胸に抱いて","0","0","150","85","2000","0","true","黑体","0"] -["150","85","1-0","2","その花を胸に抱いて","0","0","50","85","2000","0","true","黑体","0"] -["260","85","0-1","2","/nLaurantの分も","0","0","160","85","2000","0","true","黑体","0"] -["160","85","1-0","2","/nLaurantの分も","0","0","60","85","2000","0","true","黑体","0"] -["270","85","0-1","2","/n/n詠い続けよう","0","0","170","85","2000","0","true","黑体","0"] -["170","85","1-0","2","/n/n詠い続けよう","0","0","70","85","2000","0","true","黑体","0"] -["117","224","0-0.5","2","「其処にロマンは在るのかしら?」","0","0","117","224","500","0","false","黑体","0"] -["117","224","0.5-0","2","「其処にロマンは在るのかしら?」","0","0","117","224","500","0","false","黑体","0"] -["121","224","0-0.5","2","「其処にロマンは在るのかしら?」","0","0","121","224","500","0","false","黑体","0"] -["121","224","0.5-0","2","「其処にロマンは在るのかしら?」","0","0","121","224","500","0","false","黑体","0"] -["119","222","0-0.5","2","「其処にロマンは在るのかしら?」","0","0","119","222","500","0","false","黑体","0"] -["119","222","0.5-0","2","「其処にロマンは在るのかしら?」","0","0","119","222","500","0","false","黑体","0"] -["119","226","0-0.5","2","「其処にロマンは在るのかしら?」","0","0","119","226","500","0","false","黑体","0"] -["119","226","0.5-0","2","「其処にロマンは在るのかしら?」","0","0","119","226","500","0","false","黑体","0"] -["119","224","0-1","2","「其処にロマンは在るのかしら?」","0","0","119","224","500","0","true","黑体","0"] -["119","224","1-0","2","「其処にロマンは在るのかしら?」","0","0","119","224","500","0","true","黑体","0"] -["234","236","1-1","4.5","◆","73","76","234","236","500","0","false","黑体","0"] -["292","248","1-1","4.5","◆","286","70","292","248","500","0","false","黑体","0"] -["294","244","1-1","4.5","█","9","0","294","244","500","0","true","黑体","0"] -["193","255","1-1","4.5","█","346","0","193","255","500","0","true","黑体","0"] -["222","268","1-1","4.5","█","59","50","222","268","500","0","true","黑体","0"] -["293","294","1-1","4.5","█","300","48","293","294","500","0","true","黑体","0"] -["228","253","1-1","4.5","███","0","0","228","253","500","0","false","黑体","0"] -["232","253","1-1","4.5","█/n","0","0","232","253","500","0","false","黑体","0"] -["218","267","1-1","4.5","█","0","0","218","267","500","0","false","黑体","0"] -["279","267","1-1","4.5","█","0","0","279","267","500","0","false","黑体","0"] -["248","248","1-1","4.5","██","0","0","248","248","500","0","false","黑体","0"] -["248","248","1-1","4.5"," █","0","0","248","248","500","0","false","黑体","0"] -["224","249","1-1","4.5","█","0","0","224","249","500","0","false","黑体","0"] -["228","240","1-1","4.5","████","0","0","228","240","500","0","false","黑体","0"] -["228","240","1-1","4.5"," ████","0","0","228","240","500","0","false","黑体","0"] -["212","269","1-1","4.5","█","0","0","212","269","500","0","false","黑体","0"] -["216","260","1-1","4.5","█","0","0","216","260","500","0","false","黑体","0"] -["207","283","1-1","4.5","█","0","0","207","283","500","0","false","黑体","0"] -["290","249","1-1","4.5","█","0","0","290","249","500","0","false","黑体","0"] -["295","257","1-1","4.5","█","0","0","295","257","500","0","false","黑体","0"] -["301","266","1-1","4.5","█","0","0","301","266","500","0","false","黑体","0"] -["307","275","1-1","4.5","█","0","0","307","275","500","0","false","黑体","0"] -["225","296","1-1","4.5","█/n","0","0","225","296","500","0","false","黑体","0"] -["287","290","1-1","4.5","█","0","0","287","290","500","0","false","黑体","0"] -["229","293","1-1","4.5","█","0","0","229","293","500","0","false","黑体","0"] -["249","256","1-1","4.5","██","0","0","249","256","500","0","false","黑体","0"] -["248","256","1-1","4.5","██","0","0","248","256","500","0","false","黑体","0"] -["211","298","1-1","4.5","█","0","0","211","298","500","0","false","黑体","0"] -["300","293","1-1","4.5","█","0","0","300","293","500","0","false","黑体","0"] -["305","283","1-1","4.5","█","0","0","305","283","500","0","false","黑体","0"] -["232","247","1-1","4.5","█","0","0","232","247","500","0","false","黑体","0"] -["285","246","1-1","4.5","█","0","0","285","246","500","0","false","黑体","0"] -["279","244","1-1","4.5","█","0","0","279","244","500","0","false","黑体","0"] -["306","280","1-1","4.5","█","0","0","306","280","500","0","false","黑体","0"] -["300","267","1-1","4.5","█","0","0","300","267","500","0","false","黑体","0"] -["254","267","1-1","4.5","█","0","0","254","267","500","0","false","黑体","0"] -["259","267","1-1","4.5","█","0","0","259","267","500","0","false","黑体","0"] -["210","295","1-1","4.5","█","0","0","210","295","500","0","false","黑体","0"] -["210","308","1-1","4.5","█","0","0","210","308","500","0","false","黑体","0"] -["300","306","1-1","4.5","█","0","0","300","306","500","0","false","黑体","0"] -["299","308","1-1","4.5","█","0","0","299","308","500","0","false","黑体","0"] -["295","311","1-1","4.5","█","0","0","295","311","500","0","false","黑体","0"] -["300","321","1-1","4.5","█","0","0","300","321","500","0","false","黑体","0"] -["208","316","1-1","4.5","█","0","0","208","316","500","0","false","黑体","0"] -["243","236","1-1","4.5","█","0","0","243","236","500","0","false","黑体","0"] -["276","235","1-1","4.5","█","0","0","276","235","500","0","false","黑体","0"] -["210","293","1-1","4.5","█","0","0","210","293","500","0","false","黑体","0"] -["298","302","1-1","4.5","█","0","0","298","302","500","0","false","黑体","0"] -["215","302","1-1","4.5","█","0","0","215","302","500","0","false","黑体","0"] -["220","311","1-1","4.5","█","0","0","220","311","500","0","false","黑体","0"] -["289","308","1-1","4.5","█","0","0","289","308","500","0","false","黑体","0"] -["227","304","1-1","4.5","████","0","0","232","304","4500","0","false","黑体","0"] -["229","304","1-1","4.5","████","0","0","234","304","4500","0","false","黑体","0"] -["230","305","1-1","4.5","█████","0","0","239","305","4500","0","false","黑体","0"] -["275","303","1-1","4.5",")","90","0","275","303","500","0","false","黑体","0"] -["296","285","1-1","4.5",")","90","0","296","285","500","0","false","黑体","0"] -["254","285","1-1","4.5",")","90","0","254","285","500","0","false","黑体","0"] -["221","325","1-1","4.5","█","90","0","221","325","500","0","false","黑体","0"] -["308","312","1-1","4.5","█","0","0","308","312","500","0","false","黑体","0"] -["301","295","1-1","4.5","█","0","0","301","295","500","0","false","黑体","0"] -["313","324","1-1","4.5","█","0","0","313","324","500","0","false","黑体","0"] -["239","318","1-1","4.5","███","0","0","239","318","500","0","false","黑体","0"] -["241","318","1-1","4.5","███","0","0","241","318","500","0","false","黑体","0"] -["231","332","1-1","4.5","████","0","0","231","332","500","0","false","黑体","0"] -["228","332","1-1","4.5","████","0","0","228","332","500","0","false","黑体","0"] -["234","346","1-1","4.5","████","0","0","234","346","500","0","false","黑体","0"] -["230","346","1-1","4.5","████","0","0","230","346","500","0","false","黑体","0"] -["224","314","1-1","4.5","█","0","0","228","314","4500","0","false","黑体","0"] -["226","325","1-1","4.5","█","0","0","229","325","4500","0","false","黑体","0"] -["287","314","1-1","4.5","█","0","0","291","314","4500","0","false","黑体","0"] -["284","327","1-1","4.5","█","0","0","287","327","4500","0","false","黑体","0"] -["233","360","1-1","4.5","████","0","0","233","360","500","0","false","黑体","0"] -["231","360","1-1","4.5","████","0","0","231","360","500","0","false","黑体","0"] -["226","340","1-1","4.5","█","0","0","226","340","500","0","false","黑体","0"] -["223","351","1-1","4.5","█","0","0","223","351","500","0","false","黑体","0"] -["221","362","1-1","4.5","█","0","0","221","362","500","0","false","黑体","0"] -["278","348","1-1","4.5","█","0","0","278","348","500","0","false","黑体","0"] -["288","353","1-1","4.5","█","0","0","288","353","500","0","false","黑体","0"] -["294","363","1-1","4.5","█","0","0","294","363","500","0","false","黑体","0"] -["212","375","1-1","4.5","███████","0","0","212","375","500","0","false","黑体","0"] -["219","370","1-1","4.5","██████","0","0","219","370","500","0","false","黑体","0"] -["221","370","1-1","4.5","██████","0","0","221","370","500","0","false","黑体","0"] -["238","233","1-1","4.5","█","0","0","238","233","500","0","false","黑体","0"] -["245","233","1-1","4.5","███","0","0","245","233","500","0","false","黑体","0"] -["243","233","1-1","4.5","███","0","0","243","233","500","0","false","黑体","0"] -["234","236","1-1","10","◆","73","76","234","236","500","0","false","黑体","0"] -["292","248","1-1","10","◆","286","70","292","248","500","0","false","黑体","0"] -["294","244","1-1","10","█","9","0","294","244","500","0","true","黑体","0"] -["193","255","1-1","10","█","346","0","193","255","500","0","true","黑体","0"] -["222","268","1-1","10","█","59","50","222","268","500","0","true","黑体","0"] -["293","294","1-1","10","█","300","48","293","294","500","0","true","黑体","0"] -["228","253","1-1","10","███","0","0","228","253","500","0","false","黑体","0"] -["232","253","1-1","10","█/n","0","0","232","253","500","0","false","黑体","0"] -["218","267","1-1","10","█","0","0","218","267","500","0","false","黑体","0"] -["279","267","1-1","10","█","0","0","279","267","500","0","false","黑体","0"] -["248","248","1-1","10","██","0","0","248","248","500","0","false","黑体","0"] -["248","248","1-1","10"," █","0","0","248","248","500","0","false","黑体","0"] -["224","249","1-1","10","█","0","0","224","249","500","0","false","黑体","0"] -["228","240","1-1","10","████","0","0","228","240","500","0","false","黑体","0"] -["228","240","1-1","10"," ████","0","0","228","240","500","0","false","黑体","0"] -["212","269","1-1","10","█","0","0","212","269","500","0","false","黑体","0"] -["216","260","1-1","10","█","0","0","216","260","500","0","false","黑体","0"] -["207","283","1-1","10","█","0","0","207","283","500","0","false","黑体","0"] -["290","249","1-1","10","█","0","0","290","249","500","0","false","黑体","0"] -["295","257","1-1","10","█","0","0","295","257","500","0","false","黑体","0"] -["301","266","1-1","10","█","0","0","301","266","500","0","false","黑体","0"] -["307","275","1-1","10","█","0","0","307","275","500","0","false","黑体","0"] -["225","296","1-1","10","█/n","0","0","225","296","500","0","false","黑体","0"] -["287","290","1-1","10","█","0","0","287","290","500","0","false","黑体","0"] -["229","293","1-1","10","█","0","0","229","293","500","0","false","黑体","0"] -["249","256","1-1","10","██","0","0","249","256","500","0","false","黑体","0"] -["248","256","1-1","10","██","0","0","248","256","500","0","false","黑体","0"] -["211","298","1-1","10","█","0","0","211","298","500","0","false","黑体","0"] -["300","293","1-1","10","█","0","0","300","293","500","0","false","黑体","0"] -["305","283","1-1","10","█","0","0","305","283","500","0","false","黑体","0"] -["232","247","1-1","10","█","0","0","232","247","500","0","false","黑体","0"] -["285","246","1-1","10","█","0","0","285","246","500","0","false","黑体","0"] -["279","244","1-1","10","█","0","0","279","244","500","0","false","黑体","0"] -["306","280","1-1","10","█","0","0","306","280","500","0","false","黑体","0"] -["300","267","1-1","10","█","0","0","300","267","500","0","false","黑体","0"] -["254","267","1-1","10","█","0","0","254","267","500","0","false","黑体","0"] -["259","267","1-1","10","█","0","0","259","267","500","0","false","黑体","0"] -["210","295","1-1","10","█","0","0","210","295","500","0","false","黑体","0"] -["210","308","1-1","10","█","0","0","210","308","500","0","false","黑体","0"] -["300","306","1-1","10","█","0","0","300","306","500","0","false","黑体","0"] -["299","308","1-1","10","█","0","0","299","308","500","0","false","黑体","0"] -["295","311","1-1","10","█","0","0","295","311","500","0","false","黑体","0"] -["300","321","1-1","10","█","0","0","300","321","500","0","false","黑体","0"] -["208","316","1-1","10","█","0","0","208","316","500","0","false","黑体","0"] -["243","236","1-1","10","█","0","0","243","236","500","0","false","黑体","0"] -["276","235","1-1","10","█","0","0","276","235","500","0","false","黑体","0"] -["210","293","1-1","10","█","0","0","210","293","500","0","false","黑体","0"] -["298","302","1-1","10","█","0","0","298","302","500","0","false","黑体","0"] -["215","302","1-1","10","█","0","0","215","302","500","0","false","黑体","0"] -["220","311","1-1","10","█","0","0","220","311","500","0","false","黑体","0"] -["289","308","1-1","10","█","0","0","289","308","500","0","false","黑体","0"] -["232","304","1-1","5","████","0","0","227","304","4500","0","false","黑体","0"] -["234","304","1-1","5","████","0","0","229","304","4500","0","false","黑体","0"] -["239","305","1-1","5","█████","0","0","230","305","4500","0","false","黑体","0"] -["275","303","1-1","10",")","90","0","275","303","500","0","false","黑体","0"] -["296","285","1-1","10",")","90","0","296","285","500","0","false","黑体","0"] -["254","285","1-1","10",")","90","0","254","285","500","0","false","黑体","0"] -["221","325","1-1","10","█","90","0","221","325","500","0","false","黑体","0"] -["308","312","1-1","10","█","0","0","308","312","500","0","false","黑体","0"] -["301","295","1-1","10","█","0","0","301","295","500","0","false","黑体","0"] -["313","324","1-1","10","█","0","0","313","324","500","0","false","黑体","0"] -["239","318","1-1","10","███","0","0","239","318","500","0","false","黑体","0"] -["241","318","1-1","10","███","0","0","241","318","500","0","false","黑体","0"] -["231","332","1-1","10","████","0","0","231","332","500","0","false","黑体","0"] -["228","332","1-1","10","████","0","0","228","332","500","0","false","黑体","0"] -["234","346","1-1","10","████","0","0","234","346","500","0","false","黑体","0"] -["230","346","1-1","10","████","0","0","230","346","500","0","false","黑体","0"] -["228","314","1-1","5","█","0","0","224","314","4500","0","false","黑体","0"] -["229","325","1-1","5","█","0","0","226","325","4500","0","false","黑体","0"] -["291","314","1-1","5","█","0","0","287","314","4500","0","false","黑体","0"] -["287","327","1-1","5","█","0","0","284","327","4500","0","false","黑体","0"] -["233","360","1-1","10","████","0","0","233","360","500","0","false","黑体","0"] -["231","360","1-1","10","████","0","0","231","360","500","0","false","黑体","0"] -["226","340","1-1","10","█","0","0","226","340","500","0","false","黑体","0"] -["223","351","1-1","10","█","0","0","223","351","500","0","false","黑体","0"] -["221","362","1-1","10","█","0","0","221","362","500","0","false","黑体","0"] -["278","348","1-1","10","█","0","0","278","348","500","0","false","黑体","0"] -["288","353","1-1","10","█","0","0","288","353","500","0","false","黑体","0"] -["294","363","1-1","10","█","0","0","294","363","500","0","false","黑体","0"] -["212","375","1-1","10","███████","0","0","212","375","500","0","false","黑体","0"] -["219","370","1-1","10","██████","0","0","219","370","500","0","false","黑体","0"] -["221","370","1-1","10","██████","0","0","221","370","500","0","false","黑体","0"] -["238","233","1-1","10","█","0","0","238","233","500","0","false","黑体","0"] -["245","233","1-1","10","███","0","0","245","233","500","0","false","黑体","0"] -["243","233","1-1","10","███","0","0","243","233","500","0","false","黑体","0"] -["227","304","1-1","5.5","████","0","0","232","304","5500","0","false","黑体","0"] -["229","304","1-1","5.5","████","0","0","234","304","5500","0","false","黑体","0"] -["230","305","1-1","5.5","█████","0","0","239","305","4500","0","false","黑体","0"] -["275","303","1-1","5.5",")","90","0","275","303","500","0","false","黑体","0"] -["224","314","1-1","5.5","█","0","0","228","314","5500","0","false","黑体","0"] -["226","325","1-1","5.5","█","0","0","229","325","5500","0","false","黑体","0"] -["287","314","1-1","5.5","█","0","0","291","314","5500","0","false","黑体","0"] -["284","327","1-1","5.5","█","0","0","287","327","5500","0","false","黑体","0"] -["234","236","1-1","10","◆","73","76","234","236","500","0","false","黑体","0"] -["292","248","1-1","10","◆","286","70","292","248","500","0","false","黑体","0"] -["294","244","1-1","10","█","9","0","294","244","500","0","true","黑体","0"] -["193","255","1-1","10","█","346","0","193","255","500","0","true","黑体","0"] -["222","268","1-1","10","█","59","50","222","268","500","0","true","黑体","0"] -["293","294","1-1","10","█","300","48","293","294","500","0","true","黑体","0"] -["228","253","1-1","10","███","0","0","228","253","500","0","false","黑体","0"] -["232","253","1-1","10","█/n","0","0","232","253","500","0","false","黑体","0"] -["218","267","1-1","10","█","0","0","218","267","500","0","false","黑体","0"] -["279","267","1-1","10","█","0","0","279","267","500","0","false","黑体","0"] -["248","248","1-1","10","██","0","0","248","248","500","0","false","黑体","0"] -["248","248","1-1","10"," █","0","0","248","248","500","0","false","黑体","0"] -["224","249","1-1","10","█","0","0","224","249","500","0","false","黑体","0"] -["228","240","1-1","10","████","0","0","228","240","500","0","false","黑体","0"] -["228","240","1-1","10"," ████","0","0","228","240","500","0","false","黑体","0"] -["212","269","1-1","10","█","0","0","212","269","500","0","false","黑体","0"] -["216","260","1-1","10","█","0","0","216","260","500","0","false","黑体","0"] -["207","283","1-1","10","█","0","0","207","283","500","0","false","黑体","0"] -["290","249","1-1","10","█","0","0","290","249","500","0","false","黑体","0"] -["295","257","1-1","10","█","0","0","295","257","500","0","false","黑体","0"] -["301","266","1-1","10","█","0","0","301","266","500","0","false","黑体","0"] -["307","275","1-1","10","█","0","0","307","275","500","0","false","黑体","0"] -["225","296","1-1","10","█/n","0","0","225","296","500","0","false","黑体","0"] -["287","290","1-1","10","█","0","0","287","290","500","0","false","黑体","0"] -["229","293","1-1","10","█","0","0","229","293","500","0","false","黑体","0"] -["249","256","1-1","10","██","0","0","249","256","500","0","false","黑体","0"] -["248","256","1-1","10","██","0","0","248","256","500","0","false","黑体","0"] -["211","298","1-1","10","█","0","0","211","298","500","0","false","黑体","0"] -["300","293","1-1","10","█","0","0","300","293","500","0","false","黑体","0"] -["305","283","1-1","10","█","0","0","305","283","500","0","false","黑体","0"] -["232","247","1-1","10","█","0","0","232","247","500","0","false","黑体","0"] -["285","246","1-1","10","█","0","0","285","246","500","0","false","黑体","0"] -["279","244","1-1","10","█","0","0","279","244","500","0","false","黑体","0"] -["306","280","1-1","10","█","0","0","306","280","500","0","false","黑体","0"] -["300","267","1-1","10","█","0","0","300","267","500","0","false","黑体","0"] -["254","267","1-1","10","█","0","0","254","267","500","0","false","黑体","0"] -["259","267","1-1","10","█","0","0","259","267","500","0","false","黑体","0"] -["210","295","1-1","10","█","0","0","210","295","500","0","false","黑体","0"] -["210","308","1-1","10","█","0","0","210","308","500","0","false","黑体","0"] -["300","306","1-1","10","█","0","0","300","306","500","0","false","黑体","0"] -["299","308","1-1","10","█","0","0","299","308","500","0","false","黑体","0"] -["295","311","1-1","10","█","0","0","295","311","500","0","false","黑体","0"] -["300","321","1-1","10","█","0","0","300","321","500","0","false","黑体","0"] -["208","316","1-1","10","█","0","0","208","316","500","0","false","黑体","0"] -["243","236","1-1","10","█","0","0","243","236","500","0","false","黑体","0"] -["276","235","1-1","10","█","0","0","276","235","500","0","false","黑体","0"] -["210","293","1-1","10","█","0","0","210","293","500","0","false","黑体","0"] -["298","302","1-1","10","█","0","0","298","302","500","0","false","黑体","0"] -["215","302","1-1","10","█","0","0","215","302","500","0","false","黑体","0"] -["220","311","1-1","10","█","0","0","220","311","500","0","false","黑体","0"] -["289","308","1-1","10","█","0","0","289","308","500","0","false","黑体","0"] -["227","304","1-1","5","████","0","0","232","304","4500","0","false","黑体","0"] -["229","304","1-1","5","████","0","0","234","304","4500","0","false","黑体","0"] -["230","305","1-1","5","█████","0","0","239","305","4500","0","false","黑体","0"] -["275","303","1-1","10",")","90","0","275","303","500","0","false","黑体","0"] -["296","285","1-1","10",")","90","0","296","285","500","0","false","黑体","0"] -["254","285","1-1","10",")","90","0","254","285","500","0","false","黑体","0"] -["221","325","1-1","10","█","90","0","221","325","500","0","false","黑体","0"] -["308","312","1-1","10","█","0","0","308","312","500","0","false","黑体","0"] -["301","295","1-1","10","█","0","0","301","295","500","0","false","黑体","0"] -["313","324","1-1","10","█","0","0","313","324","500","0","false","黑体","0"] -["239","318","1-1","10","███","0","0","239","318","500","0","false","黑体","0"] -["241","318","1-1","10","███","0","0","241","318","500","0","false","黑体","0"] -["231","332","1-1","10","████","0","0","231","332","500","0","false","黑体","0"] -["228","332","1-1","10","████","0","0","228","332","500","0","false","黑体","0"] -["234","346","1-1","10","████","0","0","234","346","500","0","false","黑体","0"] -["230","346","1-1","10","████","0","0","230","346","500","0","false","黑体","0"] -["224","314","1-1","5","█","0","0","228","314","4500","0","false","黑体","0"] -["226","325","1-1","5","█","0","0","229","325","4500","0","false","黑体","0"] -["287","314","1-1","5","█","0","0","291","314","4500","0","false","黑体","0"] -["284","327","1-1","5","█","0","0","287","327","4500","0","false","黑体","0"] -["233","360","1-1","10","████","0","0","233","360","500","0","false","黑体","0"] -["231","360","1-1","10","████","0","0","231","360","500","0","false","黑体","0"] -["226","340","1-1","10","█","0","0","226","340","500","0","false","黑体","0"] -["223","351","1-1","10","█","0","0","223","351","500","0","false","黑体","0"] -["221","362","1-1","10","█","0","0","221","362","500","0","false","黑体","0"] -["278","348","1-1","10","█","0","0","278","348","500","0","false","黑体","0"] -["288","353","1-1","10","█","0","0","288","353","500","0","false","黑体","0"] -["294","363","1-1","10","█","0","0","294","363","500","0","false","黑体","0"] -["212","375","1-1","10","███████","0","0","212","375","500","0","false","黑体","0"] -["219","370","1-1","10","██████","0","0","219","370","500","0","false","黑体","0"] -["221","370","1-1","10","██████","0","0","221","370","500","0","false","黑体","0"] -["238","233","1-1","10","█","0","0","238","233","500","0","false","黑体","0"] -["245","233","1-1","10","███","0","0","245","233","500","0","false","黑体","0"] -["243","233","1-1","10","███","0","0","243","233","500","0","false","黑体","0"] -["232","304","1-1","5.5","████","0","0","227","304","5500","0","false","黑体","0"] -["234","304","1-1","5.5","████","0","0","229","304","5500","0","false","黑体","0"] -["239","305","1-1","5.5","█████","0","0","230","305","5500","0","false","黑体","0"] -["275","303","1-1","5.5",")","90","0","275","303","500","0","false","黑体","0"] -["228","314","1-1","5.5","█","0","0","224","314","5500","0","false","黑体","0"] -["229","325","1-1","5.5","█","0","0","226","325","5500","0","false","黑体","0"] -["291","314","1-1","5.5","█","0","0","287","314","5500","0","false","黑体","0"] -["287","327","1-1","5.5","█","0","0","284","327","5500","0","false","黑体","0"] -["234","236","1-1","3","◆","73","76","234","236","500","0","false","黑体","0"] -["292","248","1-1","3","◆","286","70","292","248","500","0","false","黑体","0"] -["294","244","1-1","3","█","9","0","294","244","500","0","true","黑体","0"] -["193","255","1-1","3","█","346","0","193","255","500","0","true","黑体","0"] -["222","268","1-1","3","█","59","50","222","268","500","0","true","黑体","0"] -["293","294","1-1","3","█","300","48","293","294","500","0","true","黑体","0"] -["228","253","1-1","3","███","0","0","228","253","500","0","false","黑体","0"] -["232","253","1-1","3","█/n","0","0","232","253","500","0","false","黑体","0"] -["218","267","1-1","3","█","0","0","218","267","500","0","false","黑体","0"] -["279","267","1-1","3","█","0","0","279","267","500","0","false","黑体","0"] -["248","248","1-1","3","██","0","0","248","248","500","0","false","黑体","0"] -["248","248","1-1","3"," █","0","0","248","248","500","0","false","黑体","0"] -["224","249","1-1","3","█","0","0","224","249","500","0","false","黑体","0"] -["228","240","1-1","3","████","0","0","228","240","500","0","false","黑体","0"] -["228","240","1-1","3"," ████","0","0","228","240","500","0","false","黑体","0"] -["212","269","1-1","3","█","0","0","212","269","500","0","false","黑体","0"] -["216","260","1-1","3","█","0","0","216","260","500","0","false","黑体","0"] -["207","283","1-1","3","█","0","0","207","283","500","0","false","黑体","0"] -["290","249","1-1","3","█","0","0","290","249","500","0","false","黑体","0"] -["295","257","1-1","3","█","0","0","295","257","500","0","false","黑体","0"] -["301","266","1-1","3","█","0","0","301","266","500","0","false","黑体","0"] -["307","275","1-1","3","█","0","0","307","275","500","0","false","黑体","0"] -["225","296","1-1","3","█/n","0","0","225","296","500","0","false","黑体","0"] -["287","290","1-1","3","█","0","0","287","290","500","0","false","黑体","0"] -["229","293","1-1","3","█","0","0","229","293","500","0","false","黑体","0"] -["249","256","1-1","3","██","0","0","249","256","500","0","false","黑体","0"] -["248","256","1-1","3","██","0","0","248","256","500","0","false","黑体","0"] -["211","298","1-1","3","█","0","0","211","298","500","0","false","黑体","0"] -["300","293","1-1","3","█","0","0","300","293","500","0","false","黑体","0"] -["305","283","1-1","3","█","0","0","305","283","500","0","false","黑体","0"] -["232","247","1-1","3","█","0","0","232","247","500","0","false","黑体","0"] -["285","246","1-1","3","█","0","0","285","246","500","0","false","黑体","0"] -["279","244","1-1","3","█","0","0","279","244","500","0","false","黑体","0"] -["306","280","1-1","3","█","0","0","306","280","500","0","false","黑体","0"] -["300","267","1-1","3","█","0","0","300","267","500","0","false","黑体","0"] -["254","267","1-1","3","█","0","0","254","267","500","0","false","黑体","0"] -["259","267","1-1","3","█","0","0","259","267","500","0","false","黑体","0"] -["210","295","1-1","3","█","0","0","210","295","500","0","false","黑体","0"] -["210","308","1-1","3","█","0","0","210","308","500","0","false","黑体","0"] -["300","306","1-1","3","█","0","0","300","306","500","0","false","黑体","0"] -["299","308","1-1","3","█","0","0","299","308","500","0","false","黑体","0"] -["295","311","1-1","3","█","0","0","295","311","500","0","false","黑体","0"] -["300","321","1-1","3","█","0","0","300","321","500","0","false","黑体","0"] -["208","316","1-1","3","█","0","0","208","316","500","0","false","黑体","0"] -["243","236","1-1","3","█","0","0","243","236","500","0","false","黑体","0"] -["276","235","1-1","3","█","0","0","276","235","500","0","false","黑体","0"] -["210","293","1-1","3","█","0","0","210","293","500","0","false","黑体","0"] -["298","302","1-1","3","█","0","0","298","302","500","0","false","黑体","0"] -["215","302","1-1","3","█","0","0","215","302","500","0","false","黑体","0"] -["220","311","1-1","3","█","0","0","220","311","500","0","false","黑体","0"] -["289","308","1-1","3","█","0","0","289","308","500","0","false","黑体","0"] -["232","304","1-1","3","████","0","0","230","304","500","0","false","黑体","0"] -["234","304","1-1","3","████","0","0","232","304","500","0","false","黑体","0"] -["239","305","1-1","3","█████","0","0","237","305","500","0","false","黑体","0"] -["275","303","1-1","3",")","90","0","275","303","500","0","false","黑体","0"] -["296","285","1-1","3",")","90","0","296","285","500","0","false","黑体","0"] -["254","285","1-1","3",")","90","0","254","285","500","0","false","黑体","0"] -["221","325","1-1","3","█","90","0","221","325","500","0","false","黑体","0"] -["308","312","1-1","3","█","0","0","308","312","500","0","false","黑体","0"] -["301","295","1-1","3","█","0","0","301","295","500","0","false","黑体","0"] -["313","324","1-1","3","█","0","0","313","324","500","0","false","黑体","0"] -["239","318","1-1","3","███","0","0","239","318","500","0","false","黑体","0"] -["241","318","1-1","3","███","0","0","241","318","500","0","false","黑体","0"] -["231","332","1-1","3","████","0","0","231","332","500","0","false","黑体","0"] -["228","332","1-1","3","████","0","0","228","332","500","0","false","黑体","0"] -["234","346","1-1","3","████","0","0","234","346","500","0","false","黑体","0"] -["230","346","1-1","3","████","0","0","230","346","500","0","false","黑体","0"] -["228","314","1-1","3","█","0","0","226","314","500","0","false","黑体","0"] -["229","325","1-1","3","█","0","0","227","325","500","0","false","黑体","0"] -["291","314","1-1","3","█","0","0","289","314","500","0","false","黑体","0"] -["287","327","1-1","3","█","0","0","286","327","4500","0","false","黑体","0"] -["233","360","1-1","3","████","0","0","233","360","500","0","false","黑体","0"] -["231","360","1-1","3","████","0","0","231","360","500","0","false","黑体","0"] -["226","340","1-1","3","█","0","0","226","340","500","0","false","黑体","0"] -["223","351","1-1","3","█","0","0","223","351","500","0","false","黑体","0"] -["221","362","1-1","3","█","0","0","221","362","500","0","false","黑体","0"] -["278","348","1-1","3","█","0","0","278","348","500","0","false","黑体","0"] -["288","353","1-1","3","█","0","0","288","353","500","0","false","黑体","0"] -["294","363","1-1","3","█","0","0","294","363","500","0","false","黑体","0"] -["212","375","1-1","3","███████","0","0","212","375","500","0","false","黑体","0"] -["219","370","1-1","3","██████","0","0","219","370","500","0","false","黑体","0"] -["221","370","1-1","3","██████","0","0","221","370","500","0","false","黑体","0"] -["238","233","1-1","3","█","0","0","238","233","500","0","false","黑体","0"] -["245","233","1-1","3","███","0","0","245","233","500","0","false","黑体","0"] -["243","233","1-1","3","███","0","0","243","233","500","0","false","黑体","0"] -["310","308","1-0","4.5","~♪","5","0","346","310","4500","0","true","宋体","0"] -["310","298","1-0","4.5","~♩","360","0","355","294","4500","0","true","宋体","0"] -["300","296","1-0","4.5","~♫","352","0","343","273","4500","0","true","宋体","0"] -["198","310","1-0","4.5","♭~","348","0","128","339","4500","0","true","宋体","0"] -["189","293","1-0","4.5","♬~","5","0","150","271","4500","0","true","宋体","0"] -["302","260","1-0","4.5","~♪","330","0","355","230","4500","0","true","宋体","0"] -["302","260","1-0","4.5","~♪","330","0","355","230","4500","0","true","宋体","0"] -["302","260","1-0","4.5","~♪","330","0","355","230","4500","0","true","宋体","0"] -["302","260","1-0","4.5","~♪","330","0","355","230","4500","0","true","宋体","0"] -["302","260","1-0","4.5","~♪","330","0","355","230","4500","0","true","宋体","0"] -["302","260","1-0","4.5","~♪","330","0","355","230","4500","0","true","宋体","0"] -["302","260","1-0","4.5","~♪","330","0","355","230","4500","0","true","宋体","0"] -["302","260","1-0","4.5","~♪","330","0","355","230","4500","0","true","宋体","0"] -["302","260","1-0","4.5","~♪","330","0","355","230","4500","0","true","宋体","0"] -["302","260","1-0","4.5","~♪","330","0","355","230","4500","0","true","宋体","0"] -["286","253","1-0","4.5","~♫","320","0","341","205","4500","0","true","宋体","0"] -["286","253","1-0","4.5","~♫","320","0","341","205","4500","0","true","宋体","0"] -["286","253","1-0","4.5","~♫","320","0","341","205","4500","0","true","宋体","0"] -["286","253","1-0","4.5","~♫","320","0","341","205","4500","0","true","宋体","0"] -["286","253","1-0","4.5","~♫","320","0","341","205","4500","0","true","宋体","0"] -["286","253","1-0","4.5","~♫","320","0","341","205","4500","0","true","宋体","0"] -["286","253","1-0","4.5","~♫","320","0","341","205","4500","0","true","宋体","0"] -["286","253","1-0","4.5","~♫","320","0","341","205","4500","0","true","宋体","0"] -["286","253","1-0","6","~♫","320","0","341","205","6000","0","true","宋体","0"] -["198","310","1-0","4.5","♭~","348","0","128","339","4500","0","true","宋体","0"] -["198","310","1-0","4.5","♭~","348","0","128","339","4500","0","true","宋体","0"] -["198","310","1-0","4.5","♭~","348","0","128","339","4500","0","true","宋体","0"] -["198","310","1-0","4.5","♭~","348","0","128","339","4500","0","true","宋体","0"] -["198","310","1-0","4.5","♭~","348","0","128","339","4500","0","true","宋体","0"] -["189","293","1-0","4.5","♬~","5","0","150","271","4500","0","true","宋体","0"] -["189","293","1-0","4.5","♬~","5","0","150","271","4500","0","true","宋体","0"] -["189","293","1-0","4.5","♬~","5","0","150","271","4500","0","true","宋体","0"] -["189","293","1-0","4.5","♬~","5","0","150","271","4500","0","true","宋体","0"] -["189","293","1-0","4.5","♬~","5","0","150","271","4500","0","true","宋体","0"] -["300","296","1-0","4.5","~♫","352","0","343","273","4500","0","true","宋体","0"] -["300","296","1-0","4.5","~♫","352","0","343","273","4500","0","true","宋体","0"] -["300","296","1-0","4.5","~♫","352","0","343","273","4500","0","true","宋体","0"] -["300","296","1-0","4.5","~♫","352","0","343","273","4500","0","true","宋体","0"] -["310","298","1-0","4.5","~♩","360","0","355","294","4500","0","true","宋体","0"] -["310","298","1-0","4.5","~♩","360","0","355","294","4500","0","true","宋体","0"] -["310","298","1-0","4.5","~♩","360","0","355","294","4500","0","true","宋体","0"] -["310","298","1-0","4.5","~♩","360","0","355","294","4500","0","true","宋体","0"] -["310","308","1-0","4.5","~♪","5","0","346","310","4500","0","true","宋体","0"] -["310","308","1-0","4.5","~♪","5","0","346","310","4500","0","true","宋体","0"] -["341","7","0-1","2","我呢","0","0","241","7","2000","0","true","黑体","0"] -["241","7","1-0","2","我呢","0","0","141","7","2000","0","true","黑体","0"] -["252","30","0-1","2","看见了这世上最美丽的光 ","0","0","152","30","2000","0","true","黑体","0"] -["152","30","1-0","2","看见了这世上最美丽的光 ","0","0","52","30","2000","0","true","黑体","0"] -["278","6","0-1","2","将那花朵抱在胸前","0","0","178","6","2000","0","true","黑体","0"] -["178","6","1-0","2","将那花朵抱在胸前","0","0","78","6","2000","0","true","黑体","0"] -["262","30","0-1","2","包括Laurant的份一起","0","0","162","30","2000","0","true","黑体","0"] -["162","30","1-0","2","包括Laurant的份一起","0","0","62","30","2000","0","true","黑体","0"] -["315","55","0-1","2","继续歌唱","0","0","215","55","2000","0","true","黑体","0"] -["215","55","1-0","2","继续歌唱","0","0","115","55","2000","0","true","黑体","0"] -["167","110","0-1","2","“那里有Roman在吗?”","0","0","167","110","500","0","true","黑体","0"] -["167","110","1-0","2","“那里有Roman在吗?”","0","0","167","110","500","0","true","黑体","0"] -["241","323","0-1","2","春之追忆","0","0","241","323","500","0","true","黑体","0"] -["241","323","1-0","2.5","春之追忆","0","0","241","323","500","0","true","黑体","0"] -["259","352","0-1","2","美丽的音色","0","0","259","352","500","0","true","黑体","0"] -["259","352","1-0","2.5","美丽的音色","0","0","259","352","500","0","true","黑体","0"] -["188","10","0-1","2","咏唱的少女(Monica)","0","0","188","10","500","0","true","黑体","0"] -["188","10","1-0","2.5","咏唱的少女(Monica)","0","0","188","10","500","0","true","黑体","0"] -["223","40","0-1","2","鸟儿的鸣叫","0","0","223","40","500","0","true","黑体","0"] -["223","40","1-0","2.5","鸟儿的鸣叫","0","0","223","40","500","0","true","黑体","0"] -["271","294","1-0","4.5","指针的前进 →","0","0","321","294","4500","0","true","黑体","0"] -["263","333","0-1","1.1","字幕By:Black★Rock.Shooter/n     感谢观看~","0","0","263","333","500","0","true","黑体","0"] -["260","330","0-1","1.1","字幕By:Black★Rock.Shooter/n     感谢观看~","0","0","260","330","500","0","true","黑体","0"] -["263","333","1-1","4.5","字幕By:Black★Rock.Shooter/n     感谢观看~","0","0","263","333","500","0","true","黑体","0"] -["260","330","1-1","4.5","字幕By:Black★Rock.Shooter/n     感谢观看~","0","0","260","330","500","0","true","黑体","0"] -["105","304","0-1","4.5","美丽之物","0","0","105","304","500","0","true","黑体","0"] -["315","335","0-1","4.5","为了将它采撷","0","0","315","335","500","0","true","黑体","0"] -["33","76","0-1","2","生命(人)才降诞到世上","350","20","33","76","500","0","true","黑体","0"] -["33","76","1-0","2.5","生命(人)才降诞到世上","350","20","33","76","500","0","true","黑体","0"] -["129","353","0-1","2","你所拥有的短暂的季节(Saison)","0","0","129","353","500","0","true","黑体","0"] -["129","353","1-1","2","你所拥有的短暂的季节(Saison)","0","0","129","353","500","0","true","黑体","0"] -["129","353","1-0","2","你所拥有的短暂的季节(Saison)","0","0","129","353","500","0","true","黑体","0"] -["273","358","0-1","4.5","永远不忘","0","0","273","358","500","0","true","黑体","0"] -["100","358","1-0","4.5","蝉鸣如时雨","0","0","100","358","500","0","true","黑体","0"] - \ No newline at end of file diff --git a/src/danmaku/test/av39444.xml b/src/danmaku/test/av39444.xml deleted file mode 100644 index 164cf04..0000000 --- a/src/danmaku/test/av39444.xml +++ /dev/null @@ -1,5882 +0,0 @@ -chat.bilibili.com669260100000k-v -赤い赤い赤い赤い赤い赤い赤い赤い 赤い赤い赤い赤い - -二小姐求舔足 -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰 -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -二小姐 -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -好甜 -好甜 -好甜 -好红 -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -好甜赤い赤い赤い赤い赤い赤い -赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い -赤い赤い赤い赤い - -赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -芙兰朵露芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰 -芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰 -芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰 -芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰 -赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -芙兰芙兰芙兰杜兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰 -芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰 -芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -甘い甘い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -赤い 赤い 赤い殺してあげる -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -好甜 好红 好甜 好红 -好甜好红好甜好红 -赤い甘い赤い甘い赤い甘い赤い甘い赤い甘い赤い甘い赤い甘い赤い甘い -赤い甘い赤い甘い赤い甘い赤い甘い赤い甘い -赤い甘い赤い甘い赤い甘い赤い甘い -赤い甘い赤い甘い赤い甘い赤い甘い赤い甘い赤い甘い赤い甘い -赤い甘い赤い甘い赤い甘い赤い甘い赤い甘い赤い甘い -赤い甘い赤い甘い赤い甘い赤い甘い赤い甘い赤い甘い -赤い甘い赤い甘い赤い甘い赤い甘い赤い甘い赤い甘い -赤い甘い赤い甘い赤い甘い赤い甘い赤い甘い赤い甘い -赤い甘い赤い甘い赤い甘い赤い甘い赤い甘い赤い甘い芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰 -芙兰芙兰芙兰芙兰 -好甜 好红 好甜 好红 好甜 好红 好甜 好红 啊啊啊~ -芙兰朵露!!!! -二小姐 -二小姐 -コカキコォ -好红 好甜 好红 好甜 -甘い -芙兰 -甘甘甘甘 -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -二小姐!!!芙~兰~朵~露~ -好红 -在我的身体里 -把只言片语 -一句又一句 -一遍遍 -无穷无尽地重复 -歌唱 -赤い赤い赤い赤い赤い赤い赤赤い赤い赤い赤い赤い赤い赤 -赤い赤い赤い赤い赤い赤い赤 -赤い赤い赤い赤い赤い赤い赤 -赤い赤い赤い赤い赤い赤い赤 赤い赤い赤い赤い赤い赤い赤赤い赤い赤い赤い赤い赤い赤 -好红 -芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰 -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -芙兰芙兰芙兰芙兰芙兰 -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -甘い 赤い甘い 赤い甘い 赤い甘い 赤い甘い 赤い甘い 赤い甘い 赤い甘い 赤い甘い 赤い甘い 赤い -甘い 赤い甘い 赤い甘い 赤い甘い 赤い甘い 赤い甘い 赤い甘い 赤い甘い 赤い甘い 赤い甘い 赤い甘い 赤い甘い 赤い甘い 赤い甘い 赤い -甘い 赤い甘い 赤い甘い 赤い甘い 赤い甘い 赤い甘い 赤い甘い 赤い甘い 赤い甘い 赤い甘い 赤い甘い 赤い甘い 赤い -赤い赤い赤い赤い赤い赤い赤 -赤い赤い赤い赤い赤い赤い赤 -赤い赤い赤い赤い赤い赤い赤 -赤い赤い赤い赤い赤い赤い赤 -甘い 赤い甘い 赤い甘い 赤い甘い 赤い甘い 赤い甘い 赤い甘い 赤い甘い 赤い甘い 赤い甘い 赤い甘い 赤い甘い 赤い甘い 赤い甘い 赤い甘い 赤い甘い 赤い甘い 赤い甘い 赤い甘い 赤い甘い 赤い甘い 赤い甘い 赤い -甘い 赤い甘い 赤い甘い 赤い甘い 赤い甘い 赤い甘い 赤い甘い 赤い甘い 赤い甘い 赤い甘い 赤い甘い 赤い -甘い 赤い甘い 赤い甘い 赤い甘い 赤い甘い 赤い甘い 赤い甘い 赤い甘い 赤い甘い 赤い甘い 赤い甘い 赤い甘い 赤い -甘い 赤い甘い 赤い甘い 赤い甘い 赤い甘い 赤い甘い 赤い甘い 赤い - 赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰 -赤い -芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰 -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い好甜 -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤 -芙兰芙兰芙兰芙兰芙兰芙兰芙兰 -芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰 -赤は赤くて赤くて赤くて赤くて赤くて赤くて赤くて赤く赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -俺を殺しに来たよ -俺を殺しに来たよ,俺を殺しに来たよ,俺を殺しに来たよ,俺を殺しに来たよ -俺を殺しに来たよ,俺を殺しに来たよ,俺を殺しに来たよ -殺して殺して殺して殺すしっとりとした真っ赤な瞳 -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -この想いは伝えられないの? -甘甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘いい甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -甘甘甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘いい甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘いい甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -甘い -甘い -甘い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い甘い甘い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い - 赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い - 甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い - 甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -赤い赤い赤い赤い赤い赤い赤 -赤い赤い赤い赤い赤い赤い赤 -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤いい赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤いい赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤赤い赤赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤いい赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤いい赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤いい赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤いい赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤いい赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤いい赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤いい赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤いい赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤いい赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い -赤い 赤い 赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤いい赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤いい -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い 赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤いい赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -二小姐二小姐二小姐二小姐二小姐二小姐二小姐二小姐二小姐二小姐二小姐二小姐二小姐二小姐二小姐二小姐二小姐二小姐二小姐二小姐二小姐二小姐二小姐二小姐二小姐二小姐二小姐二小姐二小姐二小姐二小姐二小姐 -二小姐求舔足二小姐求舔足二小姐求舔足二小姐求舔足二小姐求舔足二小姐求舔足二小姐求舔足二小姐求舔足二小姐求舔足二小姐求舔足二小姐求舔足二小姐求舔足二小姐求二小姐求舔足二小姐求舔足二小姐求舔足二小姐求舔足 -芙兰芙兰芙兰芙兰芙芙兰芙兰芙兰 -好红 -殺してあげる -殺してあげる殺してあげる殺してあげる殺してあげる殺してあげる殺してあげる殺してあげる -好红 -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤 -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤 -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤 -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -俺を殺しに来だよ! -なんですか -だれかいますか -芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰 -甘い 赤い甘い 赤い甘い 赤い甘い 赤い甘い 赤い甘い 赤い甘い 赤い甘い 赤い甘い 赤い甘い 赤い甘い 赤い -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰 -好甜 好红芙兰芙兰芙兰 -芙兰朵露 -芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰 -芙兰朵露 -芙兰朵露 -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -芙兰芙兰芙兰芙兰芙兰芙兰芙 -芙兰芙兰芙兰芙兰芙兰芙兰 -その想い断たれたのかな? -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -キルキルキルキル殺す -芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰 -芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰 -芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰 -芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰 -芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰 -芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰 -甘い甘い甘い甘い甘い甘い甘い甘い -私がおまえを殺し 私がおまえを殺し 私がおまえを殺し -赤い -芙兰朵露芙兰朵露芙兰朵露芙兰朵露芙兰朵露芙兰朵露芙兰朵露芙兰朵露芙兰朵露芙兰朵露 -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -芙兰芙兰芙兰芙兰芙兰芙兰 -芙兰朵露芙兰朵露芙兰朵露芙兰朵露芙兰朵露芙兰朵露芙兰朵露芙兰朵露芙兰朵露芙兰朵露芙兰朵露芙兰朵露 -することができます、 -テストの下で、 -好甜禁忌「カゴメカゴメ」 -禁忌「クランベリートラップ」 -禁弾「スターボウブレイク」 -殺してあげる!!!! -甘い     甘い     甘い     甘い     甘い     甘い     甘い     甘い     甘い     甘い     甘い     甘い     甘い     甘い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰 -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ 赤ぃ -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -殺しけあげる! -甘い -二小姐二小姐二小姐二小姐二小姐二小姐二小姐二小姐二小姐二小姐二小姐二小姐二小姐二小姐二小姐二小姐二小姐二小姐二小姐二小姐 -芙蘭朵露芙蘭朵露芙蘭朵露芙蘭朵露芙蘭朵露芙蘭朵露出芙蘭朵露芙蘭朵露芙蘭朵露芙蘭朵露芙蘭朵露芙蘭朵露芙蘭朵露芙蘭朵露芙蘭朵露芙蘭朵露芙蘭朵露芙蘭朵露芙蘭朵露赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -芙兰朵露芙兰朵露芙兰朵露芙兰朵露 -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い -亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い -亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い -亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い -亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い -亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い -亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い -亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い -亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い -亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い -亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦 -亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦 -亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い -亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦 -亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い -亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い -亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦 -亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い -亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い -亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い -赤い甘い赤い甘い -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤 -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤 -殺してあげる -あなたを殺した! -二小姐 二小姐 -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -カカオキクキァガキオゥェキァカェォィカァゥェキガゥァキォィコクオガゥキガキォキガキキオガオェキガェキガオケギセセセクスシススギセサセゴギスギセズソザシジシススセサズサズソセセササオカェィカカィェコォカォカオコカェカィォィァカカオキキィキガゥオキォィオイオァェェィェァィォェァェォェオォカォォィオカクォ赤い赤い赤い赤い赤い赤い赤い赤い赤い 赤い赤い赤い赤い赤い赤い赤 赤い赤い赤い赤い赤い赤い赤い赤い赤い 赤い赤い赤い赤い赤い赤い赤い赤い赤い い赤い -芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰 -レッド -甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い甘い甘い甘い甘い -芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰 -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰 -芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰朵露芙兰朵露芙兰朵露芙兰朵露芙兰朵露芙兰朵露 -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -甘ぃ -芙兰朵露芙兰朵露芙兰朵露芙兰朵露芙兰朵露芙兰朵露芙兰朵露芙兰朵露芙兰朵露芙兰朵露芙兰朵露芙兰朵露芙兰朵露芙兰朵露芙兰朵露芙兰朵露芙兰朵露芙兰朵露芙兰朵露芙兰朵露芙兰朵露芙兰朵露芙兰朵露芙兰朵露芙兰朵露 -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -芙兰朵露芙兰朵露芙兰朵露芙兰朵露芙兰朵露芙兰朵露芙兰朵露芙兰朵露芙兰朵露芙兰朵露芙兰朵露芙兰朵露 -芙兰朵露芙兰朵露芙兰朵露芙兰朵露芙兰朵露芙兰朵露 -芙兰朵露芙兰朵露芙兰朵露芙兰朵露芙兰朵露芙兰朵露 -芙兰朵露芙兰朵露芙兰朵露芙兰朵露芙兰朵露芙兰朵露 -芙兰朵露芙兰朵露芙兰朵露芙兰朵露芙兰朵露芙兰朵露 -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -甘い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -红い红い红い红い红い红い红い红い红い红い红い红い红い红い红い红い -红い红い红い红い红い红い红い红い红い红い红い红い红い红い红い红い红い红い红い红い红い红い红い红い -红い红い红い红い红い红い红い红い红い红い红い红い红い红い红い红い红い红い红い红い红い红い红い红い -红い红い红い红い红い红い红い红い红い红い红い红い红い红い红い红い红い红い红い红い红い红い红い红い -红い红い红い红い红い红い红い红い红い红い红い红い -红い红い红い红い红い红い红い红い红い红い红い红い红い红い红い红い红い红い红い红い红い红い红い红い红い红い红い红い红い红い红い红い红い红い红い红い -赤い -ゆき甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰 -芙兰芙兰芙兰芙兰芙兰芙兰芙兰 -二小姐二小姐二小姐二小姐二小姐二小姐二小姐 -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙 -芙兰芙兰芙兰 -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -甘く甘い甘い甘い甘い甘い甘い甘い甘い甘い甘さ -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -殺してあげる殺してあげる殺してあげる殺してあげる殺してあげる殺してあげる殺殺してあげる殺してあげるしてあげる殺してあげる -殺してあげる殺してあげる殺してあげる殺してあげる殺し殺してあげる殺してあげるてあげる殺してあげる -二小姐二小姐二小姐二小姐二小姐二小姐 -芙兰朵露!芙兰朵露!芙兰朵露!芙兰朵露!芙兰朵露! -い赤い甘 -い赤い甘 -い赤い甘 -い赤い甘 -い赤い甘 -い赤い甘 -い赤い甘 -い赤い甘 -い赤い甘 -い赤い甘 -い赤い甘 -い赤い甘 -い赤い甘 -い赤い甘 -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -甘い甘い甘甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘いい -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -赤い -殺す赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤いい赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤いい赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤いい赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤いい赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤いい赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤赤い赤赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤 -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤いい赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤いい赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤いい赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い - い赤 い赤 い赤 い赤 い赤 い赤 い赤 い赤 い赤 い赤 い赤 い赤 い赤 い赤 - い赤 い赤 い赤 い赤 い赤 い赤 い赤 い赤 い赤 い赤 い赤 い赤 い赤 い赤 い赤 い赤 い赤 い赤 い赤 い赤 い赤 い赤 い赤 い赤 い赤 い赤 い赤 い赤 い赤 い赤 い赤 い赤 い赤 -良い甘い甘い良い甘い甘い良い甘い甘い良い甘い甘い良い甘い甘い良い甘い良い甘い良い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -甘い甘い甘い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -甘い甘い甘甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘いい -甘い甘い甘甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘いい甘い甘い甘甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘いい -甘い甘い甘甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘いい。v -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤 -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤 -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤いい赤い赤い赤い赤い赤赤い赤い赤い赤い赤いい赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤いい赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘いい甘い甘い甘い甘い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い!!甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い!!甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い -赤い赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ赤ぃぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ赤ぃぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ赤ぃぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ赤ぃぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ赤ぃぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ赤ぃぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ -好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红 -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ赤ぃぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ -好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红 -甘い -赤い -赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ赤ぃぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤いい赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤赤い赤赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰 -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い -願いは破滅しましたか? -好红 -好甜 -好红 -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い -芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰 -赤い赤い赤い赤い赤い赤赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い亦い -赤い赤い赤い赤い赤い赤い赤い赤い赤い -芙兰芙兰芙兰芙兰 -好红 -好甜 -好甜 -好甜 -好红 -芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰 -芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰 -您那底下深沉的瞳孔,正是吾辈追求的高尚 -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -好红好红好红好红好红好红好红好红好红好红 -赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红 -好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红 -何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も -何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も -何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も -何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も何度も -好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红 -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ赤ぃぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ -好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红 -好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红 -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ赤ぃぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ赤ぃぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ赤ぃぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ赤ぃぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ赤ぃぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ赤ぃぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ赤ぃぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ -好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红 -好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红 -好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红好甜好红 -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ赤ぃぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ赤ぃぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ赤ぃぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -赤い赤い赤い赤い赤い赤い赤い赤い赤い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -赤い赤い赤い赤い赤い赤い赤い赤い赤い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い赤い赤い赤い赤い赤い赤い赤い赤い赤い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -赤い赤い赤い赤い赤い赤い赤い赤い赤い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い赤い赤い赤い赤い赤い赤い赤い赤い赤い甘い甘い甘い甘い甘い甘い甘い甘い -赤い赤い赤い赤い赤い赤い赤い赤い赤い甘い甘い甘い甘い甘い甘い -赤い赤い赤い赤い赤い赤い赤い赤い赤い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い赤い赤い赤い赤い赤い赤い赤い赤い赤い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い赤い赤い赤い赤い赤い赤い赤い赤い赤い甘い甘い -赤い赤い赤い赤い赤い赤い赤い赤い赤い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い赤い赤い赤い赤い赤い赤い赤い赤い赤い甘い甘い甘い甘い -赤い赤い赤い赤い赤い赤い赤い赤い赤い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い赤い赤い赤い赤い赤い赤い赤い赤い赤い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い甘い甘い甘い赤い赤い赤い赤い赤い赤い赤い赤い赤い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い -甘い甘い甘い甘い甘い甘い甘い -私の中の私が -ひとつの単語を -ひとつの単語を -何度も何度も何度も -何度も何度も何度も -繰り返し 繰り返し 繰り返し 繰り返し -繰り返し 繰り返し 繰り返し 繰り返し歌う -歌う -掴む左手が甘くて 震える右手が甘くて -掴む左手が甘くて 震える右手が甘くて -笑う口が裂けても それがあなたを殺し -笑う口が裂けても それがあなたを殺し -緋色月下、狂咲ノ絶 -好红 好甜赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ赤ぃぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ -芙兰朵露芙兰朵露芙兰朵露芙兰朵露芙兰朵露芙兰朵露 -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ赤ぃぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い甘い甘い甘い甘い -殺してあげる - - 赤 赤い,甘い, 赤い,甘い, 赤い,甘い, 赤い,甘い, 赤い,甘い, 赤い,甘い, 赤い,甘い,赤い,甘い, 赤い,甘い, 赤い,甘い, 赤い,甘い, 赤い,甘い, 赤い,甘い, 赤い,甘い, - 赤 赤い,甘い, 赤い,甘い, 赤い,甘い, 赤い,甘い, 赤い,甘い, 赤い,甘い, 赤い,甘い,赤い,甘い, 赤い,甘い, 赤い,甘い, 赤い,甘い, 赤い,甘い, 赤い,甘い, 赤い,甘い, - 赤 赤い,甘い, 赤い,甘い, 赤い,甘い, 赤い,甘い, 赤い,甘い, 赤い,甘い, 赤い,甘い,赤い,甘い, 赤い,甘い, 赤い,甘い, 赤い,甘い, 赤い,甘い, 赤い,甘い, 赤い,甘い, - 赤 赤い,甘い, 赤い,甘い, 赤い,甘い, 赤い,甘い, 赤い,甘い, 赤い,甘い, 赤い,甘い,赤い,甘い, 赤い,甘い, 赤い,甘い, 赤い,甘い, 赤い,甘い, 赤い,甘い, 赤い,甘い, 赤 赤い,甘い, 赤い,甘い, 赤い,甘い, 赤い,甘い, 赤い,甘い, 赤い,甘い, 赤い,甘い,赤い,甘い, 赤い,甘い, 赤い,甘い, 赤い,甘い, 赤い,甘い, 赤い,甘い, 赤い,甘い, -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -芙兰芙兰芙兰芙兰 -赤赤赤赤赤赤赤赤 -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤 -赤ぃ赤 -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -好甜 -甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い -芙兰 -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い -良い赤 -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -好甜好甜 -芙兰 -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い 赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い 赤い赤い赤い赤い赤い赤い 赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い -赤い赤いい赤い赤い赤い赤い赤い 赤 -赤い赤い赤い赤い赤い赤い 赤い赤い赤い赤い赤い赤い 赤 -い赤い赤い赤い赤い赤い 赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰 -芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰 -芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰 -芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰 -芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰 -芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰 -芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰 -芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰 -杀戮好红 -赤い -赤い赤い赤い赤い赤い赤い芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰 -红红红宫红 - -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -好红赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰芙兰赤い - -赤赤赤赤赤赤赤赤赤赤赤赤赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -来晚了抱歉 -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ殺殺殺殺殺殺殺殺 - -赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤赤好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红 -好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红红好红 -好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红红好红 -好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红红好红 -好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红好红红好红 -啊啊啊啊 -吃瓜 -我擦 -zaaa -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ赤ぃぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ赤ぃぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ赤ぃぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ赤ぃぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ赤ぃぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ赤ぃぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ赤ぃぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ赤ぃぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ赤ぃぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ -1 -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -2 -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ1 -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -1 -1 -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ -赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ赤ぃぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤ぃ赤赤ぃ赤ぃ赤ぃ赤ぃ -刚刚 -姐姐 -ۗۚۗۖۗۗۚ۟ۥٌَُُُُُٜ۪ۖ۟۟ۚۗ۟ۖۗۛٛٞۚۚۙۚۚۥ۟۟۟۟ۚۚ۟ۛۥۛۚۚۚ۟۟ۡۥۛۛ۟ۙۙۚۥۚۙۙۙۚۥۛۛۚۚۙۙۦُُ۟۟ۖۖۖٛ۟ۗۖۚۥٌُٞۖۛۚ۟۟۟۟ۡۛۛۡ۟ۗۚۗۖۗۗۚ۟ۥٌَُُُُُٜ۪ۖ۟۟ۚۗ۟ۖۗۛٛٞۚۚۙۚۚۥ۟۟۟۟ۚۚ۟ۛۥۛۚۚۚ۟۟ۡۥۛۛ۟ۙۙۚۥۚۙۙۙۚۥۛۛۚۚۙۙۦُُ۟۟ۖۖۖٛ۟ۗۖۚۥٌُٞۖۛۚ۟۟۟۟ۡۛۛۡ۟芙兰芙兰芙兰 -二妹 -二妹 嗷嗷嗷芙兰芙兰芙兰芙兰 -二妹 -绿绿绿绿绿绿绿绿绿绿绿绿绿绿绿绿绿绿绿绿绿绿绿绿 -绿绿绿绿绿绿绿绿绿绿绿绿绿绿绿绿绿绿绿绿绿绿绿绿 -绿绿绿绿绿绿 -你们不要过来啊啊啊啊啊啊啊啊 -UP主辛苦了 -发生了什么(゚o゚;赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -aaaaaaaaaaaaa -["200","312","1-1","0.4","「この想い届かないのかな?」"] -["228","301","1-1","0.4","「この想い届かないのかな?」"] -["193","318","1-1","0.4","「この想い届かないのかな?」"] -["189","319","1-1","0.4","「この想い届かないのかな?」"] -["60","345","1-1","0.4","「その瞳には谁が映るのかな?」"] -["120","340","1-1","0.4","「その瞳には谁が映るのかな?」"] -["200","330","1-1","0.4","「その瞳には谁が映るのかな?」"] -["243","347","1-1","0.4","「その瞳には谁が映るのかな?」"]["216","348","1-1","0.4","「その瞳には谁が映るのかな?」"]["154","333","1-1","0.4","「その瞳には谁が映るのかな?」"] -["160","348","1-1","0.6","「心壊れているのかな?」"] -["30","348","1-1","0.6","「心壊れてい るのかな?」"]["50","325","1-1","0.6","「壊れたら戻ら ないのかな?」"] -["120","283","1-1","0.6","「壊れたら戻ら ないのかな?」"] -["120","265","1-1","0.6"," 「壊れたら戻らないのかな?」"] -["93","304","1-1","2.5","「そうして时を刻むの?」","350","0"] -["110","325","1-1","2.5","「そうして时を刻むの?」","350","0"] -["130","345","1-1","2.5","「そうして时を刻むの?」","350","0"] -["14","65","0.8-0.8","6","█████████████ ████████","3","0"]["14","65","1-1","6","あなたのその全てが欲しくて 欲しくて震えてる","3","0"] -["14","65","1-1","6","▃▃▃▃▃▃▃▃▃▃▃▃▃ ▃▃▃▃▃▃▃▃","3","0"]["35","300","0.8-0.8","6","███████████  ██████████████"]["35","300","0.8-0.8","6","(この気持ち気付くいて どうして気付いてくれないの)"] -["14","65","1-1","5.9","あ ","3","0"]["35","300","0.8-0.8","5.9","( "] -["14","65","1-1","5.7"," な ","3","0"] -["35","300","0.8-0.8","5.7"," こ "] -["14","65","1-1","5.5","  た ","3","0"] -["35","300","0.8-0.8","5.5","( の "] -["14","65","1-1","5.3","   の ","3","0"] -["35","300","0.8-0.8","5.3","(  気 "] -["14","65","1-1","5.1","    そ ","3","0"] -["35","300","0.8-0.8","5.1","(   持 "] -["14","65","1-1","4.9","     の ","3","0"] -["35","300","0.8-0.8","4.9","(    ち "] -["14","65","1-1","4.7","      全 ","3","0"] -["35","300","0.8-0.8","4.7","(     気 "] -["14","65","1-1","4.5","       て ","3","0"] -["35","300","0.8-0.8","4.5","(      付 "] -["14","65","1-1","4.3","        が ","3","0"] -["35","300","0.8-0.8","4.3","(       く "] -["14","65","1-1","4.1","         欲 ","3","0"]["35","300","0.8-0.8","4.1","(        い "]["14","65","1-1","3.9","          し ","3","0"]["35","300","0.8-0.8","3.9","(         て"]["14","65","1-1","3.7","           く","3","0"]["14","65","1-1","2.8","              欲 ","3","0"] -["36","300","0.8-0.8","2.8","(            ど"] -["37","300","0.8-0.8","2.6","(             う "] -["14","65","1-1","2.4","                く ","3","0"] -["37","300","0.8-0.8","2.4","(              し "] -["14","65","1-1","2.2","                 て ","3","0"] -["37","300","0.8-0.8","2.2","(               て "] -["14","65","1-1","2","                  震 ","3","0"] -["37","300","0.8-0.8","2","(                気 "] -["14","65","1-1","1.8","                   え ","3","0"] -["37","300","0.8-0.8","1.8","(                 付 "] -["14","65","1-1","1.6","                    て ","3","0"] -["37","300","0.8-0.8","1.6","(                  い "]["14","65","1-1","1.4","                     る","3","0"] -["37","300","0.8-0.8","1.4","(                   て "] -["37","300","0.8-0.8","1.2","(                    く "] -["37","300","0.8-0.8","1","(                     れ "] -["37","300","0.8-0.8","0.8","(                      な "] -["37","300","0.8-0.8","0.6","(                       い "] -["37","300","0.8-0.8","0.4","(                        の)"]["110","27","0.7-0.7","3","そ/nの/n肌/nを/n秽/nし/n尽/nく/nし","336","0"] -["80","311","0.9-0.9","3","こ","336","0"]["130","277","0.9-0.9","3","気","345","0"] -["161","268","0.9-0.9","3","持","355","0"] -["220","265","0.9-0.9","3","壊","15","0"] -["453","268","0.9-0.9","3","た"] -["477","277","0.9-0.9","3","て"] -["80","300","0.8-0.8","3","ど","336","0"] -["100","275","0.8-0.8","3","こ","336","0"]["160","255","0.8-0.8","3","辿","350","0"] -["218","255","0.8-0.8","3","着"] -["263","274","0.8-0.8","3","く"] -["431","254","0.8-0.8","3","の"] -["477","265","0.8-0.8","3","し","10","0"] -["500","280","0.8-0.8","3","ょ","10","0"]["525","327","0.8-0.8","3","か","20","0"] -["83","290","0.6-0.6","3.0","爱","338","0"] -["105","272","0.5-0.5","3.0","漏","338","0"] -["160","245","0.5-0.5","3.0","て","350","0"] -["190","236","0.5-0.5","3.0","行"] -["215","245","0.5-0.5","3.0","く"] -["260","260","0.5-0.5","3.0","わ"] -["30","156","0.8-0.8","3","爱で抚でて揺さ振られて","338","0"] -["80","274","0.5-0.5","3.0","止","337","0"]["98","254","0.5-0.5","3.0","め","337","0"] -["120","240","0.5-0.5","3.0","る","337","0"] -["152","230","0.5-0.5","3.0","こ","350","0"] -["430","227","0.5-0.5","3.0","は"] -["453","230","0.5-0.5","3.0","で"] -["475","243","0.5-0.5","3.0","き"] -["496","260","0.5-0.5","3.0","な"] -["518","280","0.5-0.5","3.0","い","15","0"] -["430","227","0.5-0.5","3.0","は"] -["0","376","1.00-1.00","1.40","MMMMMMMMMMMMMMM@ . ;@MMMMMMMMMMMMMM. @MMMMMMMMMMMMMMQi M7 QMMMMMMMMMMMMMMMM"] -["0","199","1-1","1.4","MMMMMMMAi oMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM 7MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMS .;EMMMMMM"] -["0","243","1.00-1.00","1.40","Z MMMA MMMMMME BMMMMMMM@. 7MMMMM MMMMMS .$MMMMMMM# 2MMMMMM 2MMM "] -["0","228","1-1","1.4","v MMMMU MMMMMMMM MMMMMMMM YMMMM "] -["0","368","1.00-1.00","1.40","MMMMMMMMMMMMMMMM#7. :bMMX i@MMMMMMMMMMMM MMMMMMMMMMMMMI. BMMMMMMMMMMMMMMMM"]["0","236","1.00-1.00","1.40","8 .MMM. ZMMMMMMM, .UZAbE0E#M2 M@B9EY 28b#@M. UM$QQbb9EQ. .MMMMMMM0 .MMMt "] -["0","250","1.00-1.00","1.40","A 9MMMM MMMMMMM MMMMMMM iMMMMM .MMMMM7 #MMMMMM #MMMMMM MMMM@ "] -["0","265","1.00-1.00","1.40","YEMMMMMv MMMMMMM :MMMM@ cMM7 @MMA UMMMMi MMMMMMM :MMMMM@"] -["0","191","1.00-1.00","1.40","c MMv v$MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMi $MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM0i SMM,"] -["0","273","1.00-1.00","1.40","MMMMMMM@ BMMMMMMMi MMMMMM MMMMM9 MMMMMM MMMMMM MMMMMMMA bMMMMMM"] -["0","258","1.00-1.00","1.40","i MMMMM 7MMMMMM MMMMM 0MM8 vMMMM MMMMM: MMMMMMv MMMMM."] -["0","88","1-1","1.4","MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMn #MM MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"] -["0","125","1-1","1.4","MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM.M. :W MMM MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"] -["0","22","1-1","1.4","MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM: .MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"] -["0","44","1-1","1.4","MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM @MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"] -["0","280","1-1","1.4","MMMMMMMM .MMMMMMMMB UMMMMMMb :MMMMM EMMMMMn .MMMMMMz YMMMMMMMM MMMMMMM"] -["0","354","1-1","1.4","MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM6 ,BMMMMMM@ 2MMMMMMb. ,@MMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"] -["0","110","1-1","1.4","MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM: :,nE 8MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"] -["0","287","1-1","1.4","MMMMMMMM. MMMMMMMMMM MMMMMMMM MMMMMU MMMMMM oMMMMMMM BMMMMMMMM@ MMMMMMM"] -["0","346","1-1","1.4","MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMQ 8MMMMM MMMM@i tMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"] -["0","37","1-1","1.4","MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM: ;MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"] -["0","66","1-1","1.4","MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM ; :MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"] -["0","133","1-1","1.4","MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM#c , Y:c;cY. @MMMMMM. CMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"] -["0","118","1-1","1.4","MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM0IM vMMM .MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"] -["0","361","1-1","1.4","MMMMMMMMMMMMMMMMMMMMMMMMMMMMMM1 SMMMMMMMMM$ ZMMMMMMMMMo. 7C.. ..7A$MMMMMMMMMMMMMMMMMMM"] -["0","184","1-1","1.4","C YBMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMME :MMMMMMMMMMMMtMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM9i "] -["0","52","1-1","1.4","MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM7 oMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"] -["0","339","1-1","1.4","MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM#. MMMM. .MMM: :$MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"] -["0","331","1-1","1.4","MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM: MMM. ,MM iMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"] -["0","324","1-1","1.4","MMMMMMMMMMMMMMMMMMMMMM@$MMMMMMMMMMMMMc@MMc M# . nMMM .MMMMMMMMMMMMS.MMMMMMMMMMMMMMMMMMMMM"] -["0","15","1-1","1.4","MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMA 0MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"] -["0","302","1-1","1.4","MMMMMMMM#.MMMMMMMMMMM2 .MMMMMMMMM@ @MMMMB :MMMMMM MMMMMMMMM: .MMMMMMMMMMM oMMMMMMM"] -["0","29","1-1","1.4","MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM .MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"] -["0","7","1-1","1.4","MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM 2M1X ,MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"] -["0","103","1-1","1.4","MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM MM MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"] -["0","0","1-1","1.4","MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM7 MMMMM#: ;MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"] -["0","155","1-1","1.4","MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM0MMMM .MMMX#MMMMMMMMMMM@ AMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"] -["0","169","1-1","1.4","MMEMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWc C@MMMMMMMMMMMMMM7S . @MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM@oB"] -["0","309","1-1","1.4","MMMMMMMMMMMMMMMMMMMMMM iMMMMMMMMMMMC #MMMMM: MMMMMMM @MMMMMMMMMMc $MMMMMMMMMMMMMMMMMMMM"] -["0","317","1-1","1.4","MMMMMMMMMMMMMMMMMMMMMM: MMMMMMMMMMMMM MMMME: ; v$MMMMc MMMMMMMMMMMM MMMMMMMMMMMMMMMMMMMMM"] -["0","295","1-1","1.4","MMMMMMMMY cMMMMMMMMMM $MMMMMMMMi iMMMMM BMMMMMz MMMMMMMM@ MMMMMMMMMM, :MMMMMMM"] -["0","59","1-1","1.4","MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMA @MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"] -["0","140","1-1","1.4","MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM7 c .t :. , 8:QMMMMMMM@ #MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"] -["0","74","1-1","1.4","MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM7 cM $MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"] -["0","81","1-1","1.4","MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM@. MM. vMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"] -["0","147","1-1","1.4","MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM2tE WM.MM. 2Mb;@ :MMMMMMMMMMMi MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"] -["0","96","1-1","1.4","MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMME UM. MMM ;MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"] -["0","0","1-1","1.3","/n█████████████████████/n█████████████████████/n█████████████████████/n█████████████████████/n█████████████████████/n█████████████████████/n/n/n/n/n/n/n/n"] -["0","0","1-1","1.3","█████████████████████/n/n/n/n/n/n/n/n/n/n/n█████████████████████/n█████████████████████/n█████████████████████"]["0","0","1-1","1.3"," .2YYcYcYvYvYvYvYcYvYvYvYcYvYvYcYccvYcYv7t; "] -["0","7","1-1","1.3"," YMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM :zQ0Bt "] -["0","29","1-1","1.3"," #MBWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMi "] -["0","15","1-1","1.3"," AMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM .MMMMMMMMM, "] -["0","22","1-1","1.3"," v@MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM "] -["0","37","1-1","1.3"," iMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMU "] -["0","44","1-1","1.3"," 0MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMn "] -["0","52","1-1","1.3"," tMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMc "] -["0","66","1-1","1.3"," UMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM, "] -["0","74","1-1","1.3"," @MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM@MMMMMMMMMM# "] -["0","81","1-1","1.3"," XMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM..MMMMMMMMMMM "] -["0","88","1-1","1.3"," .9MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMi :MMMMMMMMMMM. "] -["0","96","1-1","1.3"," cI.nMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM. vMMMMMMMMMMM. "] -["0","103","1-1","1.3"," @MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM. EMMMMMMMMMc Y7 "] -["0","110","1-1","1.3"," 1 WMMMMMMMMM1;@MMMMMMMMMMMMMMMMMMMMMMMM @MMMMMMMMM@ ;WMMMMMi "] -["0","118","1-1","1.3"," 1MMb. iMMMMMMMMMM; .n@MMMMMMMMMMMMMMMMMMMMM@; MMMMMMMMMMM S@MMMMMMMMMM@ "] -["0","125","1-1","1.3"," ,#MMMMMMMQ; @MMMMMMMMMM6i.;6W@MMMMMMMMMMMMMMMQ .; .MMMMMMMMMMM; ,$MMMMMMMM7 bMMM."] -["0","133","1-1","1.3","MMMMMEMMMMMMMM8, EMM2MMMMMMMMMMM@MMMMMMMMMMMMMMWWM :MMMMMMMMMMMW :$MMMMMMMQ; 7M."]["0","140","1-1","1.3","MM#: :bMMMMMMMM@; .v:.M#YIMMMMMMMMQWMMMMQMMMMYz $ iMMMMMMMMMMMM .bMMMMM1vMMM, M."] -["0","147","1-1","1.3","MX .MMM$MMMMM@v .@ IMMMMMMMM.I7MM MQ@# MMMMMMMMMMM@ .IMMMM@; tMMMMM. M."] -["0","155","1-1","1.3","Mi .#MMM: :bMMMM@; MMMMMMMMMMME.Q @#. #MMMMMMMMMM ,S@MMMMM8 CMMMMMMM MM "] -["0","177","1-1","1.3","MMM @MMMMMMMME #MM#iCoSSAU0EB0@MMMMMMMM1 .zMMMMMMMMMMMMMMMMMMMMMMMMM0: bMMMMcEMMMMMMMMX 1MMMMMI MMMMMMMMM MM "] -["0","184","1-1","1.3","MMM. #MMMMMMMM7 tMMMMMt ;MMM iMMMMz .MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMc ZMMMM 7MMMMM#MM ,MMMMMMM; ,MMMMMMM$ M;"] -["0","191","1-1","1.3","MMz .MMMMMMMM :MMMMMMMv ,MMMMMC #MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM#@MMME nMMMMMM@0 MMMMMMMMM. QMMMMMMX . "] -["0","199","1-1","1.3","M$ MMMMMMMi MMMMMMMMM MMMMMMMc .MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMc .MMMMMMMM :MMMMMMMMM7 MMMMMM. "] -["0","206","1-1","1.3","M IMMMMM@ ,MMMMMMMMM, QMMMMMMMM AMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMb.MMMMMMMMM6 @MMMMMMMM tMMMMM "] -["0","214","1-1","1.3"," .MMMMM. MMMMMMMMW MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMz .MMMMMMM@ MMMMM "] -["0","221","1-1","1.3"," MMMM@ cMMMMMMM: 9MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM BMMMMMMn ,MMMb "] -["0","228","1-1","1.3"," EMMM. MMMMMMM MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMc .MMMMMM WMMi "] -["0","236","1-1","1.3"," .MM@ BMMMMM: QMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM bMMMMM MM. "] -["0","243","1-1","1.3"," MM. ,MMMMM MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMQXMMMMM6 MMMMz 8M "] -["0","250","1-1","1.3"," @@ MMMM7 EMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM@MMMMM WMMM. "] -["0","258","1-1","1.3"," UMMM MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM6 MMM "] -["0","265","1-1","1.3"," .MMC BMM@0MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM77MMM QM@ "] -["0","273","1-1","1.3"," MM MMoXMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMo MM$ MY "] -["0","280","1-1","1.3"," :Y MMUMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM.bM. "] -["0","287","1-1","1.3"," YMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM1 "] -["0","295","1-1","1.3"," MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM "] -["0","302","1-1","1.3"," AMMMMMMMMM@QMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM0 "] -["0","309","1-1","1.3"," MMMMMMMMMM..MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM "] -["0","317","1-1","1.3"," ,MMMMMMMMMM :MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM; "] -["0","324","1-1","1.3"," QMMMMMMMMM. ;MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM#,MMMMMMMMM@ "] -["0","331","1-1","1.3"," MMMMMMMMM@ iMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM$ #MMMMMMMMM "] -["0","339","1-1","1.3"," tMMMMMMMMM .MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMU MMMMMMMMME. "] -["0","346","1-1","1.3"," Y#MMMMMMMMMMS MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM7 oMMMMMMMMMMM$i .;7. "] -["0","361","1-1","1.3"," MMMMM2 .bMMMMMMMMMMMMMMM iMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM MMMMMMMMMMMMMMMMZ. .MMMb. "] -["0","368","1-1","1.3"," EMMM# tMMMMMMMMMMMMMMMMM. YMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM MMMMMMMMMMMMMMMMMMU. cMMMMS "] -["0","377","1-1","1.3",",.vcYcv,tMMMMMMM7 XMMMMMMMMMMMMMMMMMMS @MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM. ;@MMMMMMMMMMMMMMMMMM0: tMMMMMMMM90Q@@@Q$ "] -/n/n/n/n/n/n/n/n 君/n 屠/n る/n 此/n ノ/n 色/n 彩/n/n/n/n/n/n/n -/n/n/n/n/n/n/n/n  君/n  屠/n  る/n  此/n  ノ/n  色/n  彩/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n   君/n   屠/n   る/n   此/n   ノ/n   色/n   彩/n/n/n/n/n/n/n -/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n  ★/n/n/n -/n/n/n/n/n/n/n/n    君/n    屠/n    る/n    此/n    ノ/n    色/n    彩/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n  ★/n/n/n -/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n   つ/n/n/n/n -/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n     を/n/n/n/n/n/n  -/n/n/n/n/n/n/n/n/n/n/n/n/n/n      色/n/n/n/n/n/n/n -/n/n/n/n/n/n/n/n/n/n/n/n/n       き/n/n/n/n/n/n/n/n -/n/n/n/n/n/n/n/n/n/n/n/n        深/n/n/n/n/n/n/n/n/n -/n/n/n/n/n/n/n/n/n/n/n         く/n/n/n/n/n/n/n/n/n/n -/n/n/n/n/n/n/n/n/n/n          甘/n/n/n/n/n/n/n/n/n/n/n -/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n  ★/n/n/n -/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n   つ/n/n/n/n -/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n    放/n/n/n/n/n -/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n     を/n/n/n/n/n/n  -/n/n/n/n/n/n/n/n/n/n/n/n/n/n      色/n/n/n/n/n/n/n -/n/n/n/n/n/n/n/n/n/n/n/n/n       き/n/n/n/n/n/n/n/n -/n/n/n/n/n/n/n/n/n/n/n/n        深/n/n/n/n/n/n/n/n/n -/n/n/n/n/n/n/n/n/n/n/n         く/n/n/n/n/n/n/n/n/n/n -/n/n/n/n/n/n/n/n/n/n          甘/n/n/n/n/n/n/n/n/n/n/n -/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n   つ/n/n/n/n -/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n    放/n/n/n/n/n -/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n     を/n/n/n/n/n/n  -/n/n/n/n/n/n/n/n/n/n/n/n/n/n      色/n/n/n/n/n/n/n -/n/n/n/n/n/n/n/n/n/n/n/n/n       き/n/n/n/n/n/n/n/n -/n/n/n/n/n/n/n/n/n/n/n/n        深/n/n/n/n/n/n/n/n/n -/n/n/n/n/n/n/n/n/n/n/n         く/n/n/n/n/n/n/n/n/n/n -/n/n/n/n/n/n/n/n喉/n を/n 枯/n ら/n し/n 叫/n う/n 音/n 色/n/n/n/n/n/n -/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n  ★/n/n/n -/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n   つ/n/n/n/n -/n/n/n/n/n/n/n/n/n/n          甘/n/n/n/n/n/n/n/n/n/n/n -/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n    放/n/n/n/n/n -/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n     を/n/n/n/n/n/n  -/n/n/n/n/n/n/n/n/n/n/n/n/n/n      色/n/n/n/n/n/n/n -/n/n/n/n/n/n/n/n/n/n/n/n/n       き/n/n/n/n/n/n/n/n -/n/n/n/n/n/n/n/n/n/n/n/n        深/n/n/n/n/n/n/n/n/n -/n/n/n/n/n/n/n/n/n/n/n         く/n/n/n/n/n/n/n/n/n/n -/n/n/n/n/n/n/n/n/n/n          甘/n/n/n/n/n/n/n/n/n/n/n -/n/n/n/n/n/n/n音   /n/n/n/n/n/n/n/n/n/n/n/n/n/n -/n/n/n/n/n/nう    /n/n/n/n/n/n/n/n/n/n/n/n/n/n/n -/n/n/n/n/n叫     /n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n -/n/n/n/nし      /n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n -/n/n/nら       /n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n -/n/n枯        /n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n -/nを         /n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n -喉          /n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n -/n/n/n/n/n/nう    /n/n/n/n/n/n/n/n/n/n/n/n/n/n/n -/n/n/n/n/n叫     /n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n -/n/n/n/nし      /n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n -/n/n/nら       /n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n -/n/n枯        /n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n -/nを         /n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n -/n/n/n/n/n/n/n音   /n/n/n/n/n/n/n/n/n/n/n/n/n/n -/n/n/n/n/n/nう    /n/n/n/n/n/n/n/n/n/n/n/n/n/n/n -/n/n/n/n/n叫     /n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n -/n/n/n/nし      /n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n -/n/n/nら       /n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n -/n/n枯        /n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n -/nを         /n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n -/n/n/n/n/n/n/n音   /n/n/n/n/n/n/n/n/n/n/n/n/n/n -/n/n/n/n/n/nう    /n/n/n/n/n/n/n/n/n/n/n/n/n/n/n -/n/n/n/n/n叫     /n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n -/n/n/n/nし      /n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n -/n/n/nら       /n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n -/n/n枯        /n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n -/nを         /n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n -/n/n/n赤キ雨に彩られたら/n/n/n/n/n/n/n/n/n -/n/n/n 赤キ雨に彩られたら/n/n/n/n/n/n/n/n/n/n/n/n/n -/n/n/n   赤キ雨に彩られたら/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n -/n/n    赤キ雨に彩られたら/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n -/n/n/n/n/n/n/n/n      绮丽な舞台の出来上がり/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n -/n/n/n/n/n/n     绮丽な舞台の出来上がり/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n -/n/n/n   绮丽な舞台の出来上がり/n/n/n/n/n/n/n/n/n/n/n/n/n -/n 绮丽な舞台の出来上がり/n/n/n/n/n/n/n/n/n/n/n -["169","145","0.1-1.00","3.5","私独り其処で踊る"] -["13","130","1.00-1.00","0.3","私独り其処で踊る"] -喉          /n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n -喉          /n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n -喉          /n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n -["92","345","1.0-1.0","2.5","█"] -["15","275","1.0-1.0","2.5","█"] -["20","330","1.00-1.00","0.2","そ "] -["20","330","1.00-1.00","0.5","  の  "] -["20","330","1.00-1.00","0.8","    愿  "]["20","330","1.00-1.00","1.1","      い  "]["20","330","1.00-1.00","1.4","        溃  "]["20","330","1.00-1.00","1.7","          え  "]["20","330","1.00-1.00","2","            た  "]["20","330","1.00-1.00","2.3","              の  "] -["20","330","1.00-1.00","2.6","                か  "] -["20","330","1.00-1.00","2.9","                  な?"] -["16","327","1.00-1.00","3.1","そ  "] -["16","327","1.00-1.00","2.8","  の  "] -["14","327","1.00-1.00","2.5","    愿  "]["8","327","1.00-1.00","2.2","      い  "] -["8","327","1.00-1.00","1.9","        溃  "] -["2","327","1.00-1.00","1.3","            た  "] -["7","327","1.00-1.00","1","             の"] -["12","327","1.00-1.00","0.7"," か "] -["10","327","1.00-1.00","0.4"," な?"] -["10","327","1-1","0.3","その愿い溃えたのかな?"] -["10","327","1-1","0.3","その愿い溃えたのかな?"] -["58","344","1-1","0.3","その愿い溃えたのかな?"] -["66","324","1-1","0.3","その愿い溃えたのかな?"] -["46","364","1-1","0.3","その愿い溃えたのかな?"] -["46","364","1-1","0.3","その愿い溃えたのかな?"] -["56","340","1-1","0.3","その愿い溃えたのかな?"]["0","307","1-1","0.3","その愿い溃えたのかな?"] -["0","307","1-1","0.3","その愿い溃えたのかな?"] -["20","330","1.00-1.00","0.1","そ  "] -["20","330","1.00-1.00","0.4","  の  "] -["20","330","1.00-1.00","0.7","    想  "] -["20","330","1.00-1.00","1","      い  "] -["20","330","1.00-1.00","1.6","          た "] -["20","330","1.00-1.00","1.3","        断  "] -["20","330","1.00-1.00","1.9","            れ  "] -["20","330","1.00-1.00","2.2","              た  "] -["20","330","1.00-1.00","2.5","                 の  "] -["20","330","1.00-1.00","2.8","                   か  "] -["20","330","1.00-1.00","3.1","                      な ?"] -["16","327","1.00-1.00","3.3","そ "] -["15","327","1.00-1.00","3"," の "] -["13","327","1.00-1.00","2.7","   想  "] -["17","327","1.00-1.00","2.4"," い "] -["17","327","1.00-1.00","2.1"," 断 "] -["13","327","1.00-1.00","1.8"," た "] -["18","327","1.00-1.00","1.5"," れ "]["18","327","1.00-1.00","1.2"," た "]["20","327","1.00-1.00","0.9"," の "]["15","327","1.00-1.00","0.6"," か "] -["0","327","1-1","0.3","その想い断たれたのかな?"]["18","327","1.00-1.00","0.3"," な ?"] -["0","327","1-1","0.3","その想い断たれたのかな?"] -["0","327","1-1","0.3","その想い断たれたのかな?"] -["10","307","1-1","0.3","その想い断たれたのかな?"] -["40","349","1-1","0.3","その想い断たれたのかな?"] -["40","349","1-1","0.3","その想い断たれたのかな?"] -["10","307","1-1","0.3","その想い断たれたのかな?"] -["0","307","1-1","0.3","その想い断たれたのかな?"] -["40","349","1-1","0.3","その想い断たれたのかな?"] -["10","307","1-1","0.3","その想い断たれたのかな?"] -["0","307","1-1","0.3","その想い断たれたのかな?"]["264","0","1.00-1.00","3.3","そ/n/nの/n/n希/n/n望/n/n绝/n/nえ/n/nた/n/nの/n/nか/n/nな/n/n?"] -["261","0","1-1","0.3","そ"]["261","6","1-1","0.3","/nの"] -["261","12","1-1","0.3","/n/n希"] -["261","19","1-1","0.3","/n/n/n望"] -["261","29","1-1","0.3","/n/n/n/n绝"] -["261","12","1-1","0.3","/n/n/n/n/n/nえ"] -["261","248","1-1","0.3","の"]["261","285","1-1","0.3","か"] -["261","295","1-1","0.3","/nな"] -["135","140","1.00-1.00","3","その瞳焼かれたのかな?"] -["135","197","0.5-0.5","3","その瞳焼かれたのかな?","180","180"] -["128","155","0.5-1","2.8","その肌は秽されたのかな?"] -["20","145","1-0.5","0.2","そして谁もいなくなる?"] -["2","327","1.00-1.00","1.6","          え  "] -["276","172","0.1-1","0.4","「この想い届かないのかな?」","340","0"] -["271","320","1-1","0.4","「この想い届かないのかな?」","355","0"] -["15","74","1.00-0.1","3","其ノ生を引き裂かれて"] -["267","150","0.1-1","3"," どこにも本当の","180","180"] -["58","139","0.1-1","3","私/nな/nん/nて/nい/nな/nい/nの/nだ/nか/nら"] -["163","128","1.00-0.1","3","其ノ生の华散らして","35","0"] -["301","265","0.1-1","3","儚い命だわ","35","0"] -["295","310","1-0.1","3","极彩に咲き我が粮"] -["402","345","0.1-1","3","美しく爱おしい"] -["10","158","0.1-1","3","其ノ生がお前ならば"]["10","158","1-1","3","其ノ生がお前ならば"] -["10","158","1-0.1","0.1","其ノ生がお前ならば","355","0"] -["10","158","1-0.1","0.2","其ノ生がお前ならば","350","0"] -["10","158","1-0.1","0.3","其ノ生がお前ならば","345","0"] -["10","158","1-0.1","0.4","其ノ生がお前ならば","340","0"] -["10","158","1-0.1","0.5","其ノ生がお前ならば","335","0"] -["10","158","1-0.1","0.6","其ノ生がお前ならば","330","0"] -["10","158","1-0.1","0.8","其ノ生がお前ならば","325","0"] -["10","158","1-0.1","1","其ノ生がお前ならば","320","0"] -["10","158","1-0.1","1.2","其ノ生がお前ならば","315","0"] -["10","158","1-0.1","1.4","其ノ生がお前ならば","310","0"] -["10","158","1-0.1","1.6","其ノ生がお前ならば","305","0"] -["10","158","1-0.1","1.8","其ノ生がお前ならば","300","0"] -["10","158","1-0.1","3","其ノ生がお前ならば","295","0"] -["10","158","1-0.1","2","其ノ生がお前ならば","290","0"] -["10","158","1-0.1","2.2","其ノ生がお前ならば","285","0"] -["10","158","1-0.1","2.4","其ノ生がお前ならば","280","0"] -["10","158","1-0.1","2.6","其ノ生がお前ならば","275","0"] -["10","158","1-0.1","2.8","其ノ生がお前ならば","270","0"] -["10","158","1-0.1","3","其ノ生がお前ならば","265","0"] -["10","178","1-0.1","3","★☆永远に私のもの"] -["10","178","1-0.1","3","★☆永远に私のもの","5","0"] -["10","178","1-0.1","3","★☆永远に私のもの","10","0"] -["10","178","1-0.1","3","★☆永远に私のもの","15","0"] -["10","178","1-0.1","3","★☆永远に私のもの","20","0"] -["10","178","1-0.1","3","★☆永远に私のもの","25","0"] -["10","178","1-0.1","3","★☆永远に私のもの","30","0"] -["10","178","1-0.1","3","★☆永远に私のもの","35","0"] -["10","178","1-0.1","3","★☆永远に私のもの","40","0"] -["10","178","1-0.1","3","★☆永远に私のもの","45","0"] -["10","178","1-0.1","3","★☆永远に私のもの","50","0"] -["10","178","1-0.1","3","★☆永远に私のもの","55","0"] -["10","178","1-0.1","3","★☆永远に私のもの","60","0"]["10","178","1-0.1","3","★☆永远に私のもの","65","0"]["10","178","1-0.1","3","★☆永远に私のもの","70","0"]["10","178","1-0.1","3","★☆永远に私のもの","75","0"]["10","178","1-0.1","3","★☆永远に私のもの","80","0"]["10","178","1-0.1","3","★☆永远に私のもの","85","0"]["10","178","1-0.1","3","★☆永远に私のもの","90","0"]["249","2","1.00-0.1","6","其/nノ/n四/n肢/nを/n贽/nと/n捧/nげ","355","0"] -["288","200","1.00-0.1","6","★☆ずっと私の傍に","85","0"] -["231","5","1.00-0.1","5.7","其/nノ/n四/n肢/nを/n贽/nと/n捧/nげ","350","0"] -["288","200","1.00-0.1","5.7","★☆ずっと私の傍に","80","0"] -["215","10","1.00-0.1","5.4","其/nノ/n四/n肢/nを/n贽/nと/n捧/nげ","345","0"] -["288","200","1.00-0.1","5.4","★☆ずっと私の傍に","75","0"] -["198","16","1.00-0.1","5.1","其/nノ/n四/n肢/nを/n贽/nと/n捧/nげ","340","0"] -["288","200","1.00-0.1","5.1","★☆ずっと私の傍に","70","0"] -["288","200","1.00-0.1","4.8","★☆ずっと私の傍に","65","0"] -["166","30","1.00-0.1","4.5","其/nノ/n四/n肢/nを/n贽/nと/n捧/nげ","330","0"] -["151","40","1.00-0.1","4.2","其/nノ/n四/n肢/nを/n贽/nと/n捧/nげ","325","0"] -["288","200","1.00-0.1","4.2","★☆ずっと私の傍に","55","0"] -["137","51","1.00-0.1","3.9","其/nノ/n四/n肢/nを/n贽/nと/n捧/nげ","320","0"] -["288","200","1.00-0.1","3.9","★☆ずっと私の傍に","50","0"] -["125","64","1.00-0.1","3.6","其/nノ/n四/n肢/nを/n贽/nと/n捧/nげ","315","0"] -["288","200","1.00-0.1","3.6","★☆ずっと私の傍に","45","0"] -["115","78","1.00-0.1","3.3","其/nノ/n四/n肢/nを/n贽/nと/n捧/nげ","310","0"]["288","200","1.00-0.1","3.3","★☆ずっと私の傍に","40","0"]["275","190","1.00-0.1","3","★我が足元の死尸となれ","305","0"]["275","190","1.00-0.1","2.8","☆我が足元の死尸となれ","310","0"] -["261","212","1.00-0.1","2.8","もう行かさないから","180","0"] -["275","190","1.00-0.1","2.6","☆我が足元の死尸となれ","315","0"] -["275","190","1.00-0.1","2.4","☆我が足元の死尸となれ","320","0"] -["261","212","1.00-0.1","2.4","もう行かさないから","170","0"] -["275","190","1.00-0.1","2.2","☆我が足元の死尸となれ","325","0"] -["261","212","1.00-0.1","2.2","もう行かさないから","165","0"] -["261","212","1.00-0.1","2","もう行かさないから","160","0"] -["275","190","1.00-0.1","2","☆我が足元の死尸となれ","330","0"] -["275","190","1.00-0.1","1.8","☆我が足元の死尸となれ","335","0"] -["261","212","1.00-0.1","1.8","もう行かさないから","155","0"] -["275","190","1.00-0.1","1.6","☆我が足元の死尸となれ","340","0"] -["261","212","1.00-0.1","1.6","もう行かさないから","150","0"] -["275","190","1.00-0.1","1.4","☆我が足元の死尸となれ","345","0"] -["261","212","1.00-0.1","1.4","もう行かさないから","145","0"] -["275","190","1.00-0.1","1.2","☆我が足元の死尸となれ","350","0"] -["261","212","1.00-0.1","1.2","もう行かさないから","140","0"] -["275","190","1.00-0.1","1","☆我が足元の死尸となれ","355","0"] -["261","212","1.00-0.1","1","もう行かさないから","135","0"] -["261","212","1.00-0.1","0.8","もう行かさないから","130","0"] -["261","212","1.00-0.1","0.6","もう行かさないから","125","0"] -["261","212","1.00-0.1","0.4","もう行かさないから","120","0"] -["171","145","1.00-0.8","0.8","赤 い"]["120","167","1.00-0.8","0.8","嬉しくなっても"] -["221","150","1.00-1.00","1.5","あなた"] -["221","150","1.00-0.1","1.5","あなた"] -["221","150","1.00-0.1","1.5","甘い"] -["221","150","1.00-0.1","1.5","甘い"] -["240","150","1.00-0.1","1.5","赤い"] -["240","150","1.00-0.1","1.5","赤い"] -["151","280","1.00-1.00","2.0","杀してあげる!","10","0"] -["140","357","0.1-0.7","2.0","杀してあげる!","188","180"] -["108","98","0.9-0.9","6.0","ノ"] -["94","130","0.9-0.9","6.0","生"] -["90","175","0.9-0.9","6.0","が"] -["90","215","0.8-0.8","6.0","お"] -["100","255","0.8-0.8","6.0","前","340","0"]["117","290","0.8-0.8","6.0","な","340","0"] -["139","310","0.8-0.8","6.0","ら","340","0"] -["167","330","0.8-0.8","6.0","ば","340","0"] -["420","60","0.8-0.8","6.0","喰","15","0"] -["460","150","0.8-0.8","6.0","い"] -["451","116","0.8-0.8","6.0","ら","15","0"] -["465","195","0.8-0.8","6.0","尽"] -["456","250","0.8-0.8","6.0","く"] -["417","319","0.8-0.8","6.0","て"] -["350","356","0.8-0.8","6.0","肉"] -["320","360","0.8-0.8","6.0","に"] -["294","360","0.8-0.8","6.0","す"] -["163","86","0.7-0.7","6.0","永","350","0"]["183","80","0.7-0.7","6.0","远","350","0"] -["245","75","0.7-0.7","6.0","の","355","0"] -["265","75","0.7-0.7","6.0","も","355","0"] -["285","78","0.7-0.7","6.0","の","355","0"] -["325","82","0.7-0.7","6.0","な","10","0"] -["362","153","0.7-0.7","6.0","い","10","0"] -["330","125","0.7-0.7","6.0","か","10","0"] -["310","120","0.7-0.7","6.0","し","5","0"] -["293","115","0.7-0.7","6.0","る","5","0"]["48","310","1.00-1.00","3.0","其ノ四肢を贽と捧げ"] -["173","352","0.7-0.7","3.0","ずっと私の傍に"] -["270","310","0.7-0.7","3.0","我が足元の死尸となれ"] -["426","59","1.00-0.1","1","私の中の","55","0"] -["50","131","1.00-0.1","1","私の中の"] -["343","120","1.00-0.1","1","私の中の","60","0"] -["346","250","1.00-0.1","1","私が","10","0"] -["90","209","1.00-0.1","1","ひとつ","320","0"] -["69","225","1.00-0.1","0.8","ひとつ","320","0"] -["24","87","1.00-0.1","0.8","何/n度/nも","5","0"]["44","124","1.00-0.1","0.8","何/n度/nも","350","0"]["50","217","1.00-0.1","0.8","何/n度/nも"] -["29","127","1.00-0.1","0.8","何/n度/nも"] -["106","330","1.00-0.1","0.8","无尽蔵に"] -["480","50","1.00-0.1","0.8","缲り返し","45","0"] -["396","144","1.00-0.1","0.8","缲り返し","45","0"] -["352","216","1.00-0.1","0.8","缲り返し","60","0"] -["383","300","1.00-0.1","0.8","缲り返し"] -["370","318","1.00-0.1","0.8","缲り返し","10","0"] -["382","360","1.00-0.1","0.8","缲り返し"] -["119","248","1.00-0.1","0.8","缲り返し"]["113","262","1.00-0.1","0.8","歌う","330","0"] -["251","228","1-0.1","3","震える右手","330","0"] -["382","160","1-0.1","3","が"] -["380","185","1-0.1","3","甘/nく/nて"] -["133","263","1-0.1","0.3","缲り返し","330","0"] -["380","22","1-0.1","3","つ/nか/nむ/n左/n手/nが/n甘/nく/nて"] -["288","82","1.00-0.1","0.8","甘くて"] -["391","71","1.00-0.1","0.8","甘くて","330","0"] -["283","310","1.00-0.1","0.8","笑う口が裂けても","330","0"] -["260","138","1.00-0.1","0.8","楽しくて","10","0"] -["236","211","1.00-0.1","0.8","楽しくて","20","0"]["95","178","1.00-0.1","0.8","脳髄を","20","0"] -["267","268","1.00-0.1","0.8","震えて"] -["221","258","1.00-0.1","0.4","震えて"] -["203","222","1.00-0.1","0.4","楽しくて"] -["103","320","1.00-0.1","0.8","楽しくて"] -/n/n/n/n/n/nこの気持ち気付いて… -/n/n/n/n/n/n/n/n/n/n/n/n狂気に満ちてゆくわ -/n/n/n/n/n/n/n/nこの気持ち壊れたて -/n/n/n/n/n/n/n/n/n/n/n爱漏れて行くわ -["30","330","1.00-0.1","1","どうして気付いてくれないの"] -["30","330","1-0.1","3","ど "] -["30","330","1-0.1","3"," う "]["30","330","1-0.1","3","  し "] -["30","330","1-0.1","3","   て "] -["30","330","1-0.1","3","    気 "] -["30","330","1-0.1","3","      い "] -["30","330","1-0.1","3","       て "] -["30","330","1-0.1","3","        く "] -["30","330","1-0.1","3","         れ "] -["30","330","1-0.1","3","          な "]["30","330","1-0.1","3","           い "]["30","330","1-0.1","3","            の"]["30","330","1.00-0.1","3","どうすれば止まるの"] -["30","330","1-0.1","3","ど "] -["30","330","1-0.1","3"," う "] -["30","330","1-0.1","3","   れ "] -["30","330","1-0.1","3","    ば "] -["30","330","1-0.1","3","     止 "]["30","330","1-0.1","3","      ま "] -["30","330","1-0.1","3","       る "] -["30","330","1-0.1","3","        の"] -["14","260","1.00-1.00","3","どこへ辿り着くのでしょうか"] -["14","260","1.00-0.1","1","ど "] -["14","260","1.00-0.1","1","  へ "] -["14","260","1.00-0.1","1","    り "] -["14","260","1.00-0.1","1","     着 "] -["14","260","1.00-0.1","1","      く "]["14","260","1.00-0.1","1","       の "] -["14","260","1.00-0.1","1","        で "] -["14","260","1.00-0.1","1","         し "] -["14","260","1.00-0.1","1","          ょ "] -["14","260","1.00-0.1","1","           う "] -["14","260","1.00-0.1","1","            か"] -["14","260","1.00-0.1","1","/n/n/n/n/n/n/n/n/n/n/n/n爱漏れて行くわ/n/n/n/n/n/n/n"] -/n/n/n/n/n/n/n/n/n/n/n/n爱漏れて行くわ/n/n/n/n/n/n/n -甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い -甘い /n甘い /n甘い /n甘い /n甘い /n甘い /n甘い /n甘い /n甘い /n甘い /n甘い /n甘い /n甘い /n甘い -甘い  /n甘い  /n甘い  /n甘い  /n甘い  /n甘い  /n甘い  /n甘い  /n甘い  /n甘い  /n甘い  /n甘い  /n甘い  /n甘い -甘い   /n甘い   /n甘い   /n甘い   /n甘い   /n甘い   /n甘い   /n甘い   /n甘い   /n甘い   /n甘い   /n甘い   /n甘い   /n甘い  -甘い    /n甘い    /n甘い    /n甘い    /n甘い    /n甘い    /n甘い    /n甘い    /n甘い    /n甘い    /n甘い    /n甘い    /n甘い    /n甘い  -赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い -赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い -赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い -赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い -赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い -赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い -赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い -赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い -赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い -赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い -赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い -赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い -赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い -赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い -赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い -赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い -赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い -赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い -赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い -赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い -赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い -赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い -赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い -赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い -赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い -赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い -赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い -赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い -赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い -赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い -赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い -赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い -赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い -赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い -赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い -赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い -赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い -赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い -赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い -赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い -赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い -赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い -赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い -赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い -赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い -赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い -赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い -赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い -赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い -赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い -赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い -赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い -赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い -赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い -赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い -赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い -赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い -赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い -赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い -赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い -赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い -赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い -赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い -赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い -赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い -赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い -赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い -赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い -赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い -赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い -赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い -赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い -赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い -赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い -赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い -赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い -赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い -赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い -赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い -赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い -赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い -赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い -赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い -赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い -赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い -赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い -赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い -赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い -赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い -赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い -赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い -赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い -赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い -赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い -赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い -赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い -赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い -赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い -赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い -赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い -赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い -赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い -赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い -赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い -赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い -赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い -赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い -赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い -赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い -赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い -赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い -赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い -赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い -赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い -赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い -赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い -赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い -赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い -赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い -赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い -赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い -赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い -赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い -赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い -赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い -赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い -赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い -赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い -赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い -赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い -赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い -赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い -赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い -赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い -赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い -赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い -赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い -赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い -赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い -赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い -赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い -赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い -赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い -赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い -赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い -赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い -赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い -赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い -赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い -赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い -赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い -赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い -赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い -赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い -赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い -赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い -赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い -赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い -赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い -赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い -赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い -赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い -赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い -赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い -赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い -赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い -赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い -赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い -赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い -赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い -赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い -赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い -赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い -赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い -赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い -赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い -赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い -赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い -赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い -赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い -赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い -赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い -赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い -赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い -赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い -赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い -赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い -赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い -赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い -赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い -赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い -赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い -赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い -赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い/n赤い赤い赤い -赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い/n赤い赤い赤い赤い -赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い -赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い -赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い/n赤い赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い/n赤い赤い -赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い赤い/n赤い赤い赤い赤い赤い赤い -甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い -甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い -甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い -甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い -甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い -甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い -甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い -甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い -甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い -甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い -甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い -甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い -甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い -甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い -甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い -甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い -甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い -甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い -甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い -甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い -甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い -甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い -甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い -甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い -甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い -甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い -甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い -甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い -甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い -甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い -甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い -甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い -甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い -甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い -甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い -甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い -甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い -甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い -甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い -甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い -甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い -甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い -甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い -甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い -甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い -甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い -甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い -甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い -甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い -甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い -甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い -甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い -甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い -甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い -甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い -甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い/n甘い -甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い -甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い -甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い -甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い -甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い -甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い -甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い -甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い/n甘い甘い -甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い -甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い -甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い -甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い/n甘い甘い甘い -甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い -甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い -甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い -甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い/n甘い甘い甘い甘い -["73","355","1.00-0.4","3.5","濕潤的深紅眼眸"] -["73","355","1.00-0.4","3.5","艷色的裙尾搖擺"] -["73","355","1.00-0.4","3.5","幼小的臉頰浸染朱紅"] -["40","355","1.00-0.4","3.5","這份思念無法傳達嗎?"] -["40","365","1.00-0.4","3.5","這樣做能刻畫下時間嗎?"] -["30","337","1.00-0.4","3.5","我想要你的全部 想到渾身顫抖"] -["121","367","1.00-0.4","3.5","(希望你察覺我的心情 為什麼沒有察覺到呢?)"] -["121","367","1.00-0.4","3","(希望你察覺我的心情 為什麼沒有察覺到呢?)"] -["76","346","1.00-0.4","3","我想要你的全部 想到渾身顫抖"] -["76","346","1.00-1","4","就讓那甜美的心跳 竭盡氣力就此停止吧/n(漸漸地被陷入瘋狂 無論怎樣也無法抑止)"] -["76","346","1.00-1","4","盡情弄髒那片肌膚 污辱你的只有我/n(心情都被毀壞 接著該如何是好)"]["76","346","1.00-1","5","快點平息這份愛撫和操弄你的衝動吧/n(愛就要滿溢出來 無法停止…)"] -["20","346","1.00-1","5","聲嘶力竭歌唱的音色"] -["20","346","1.00-1","3","█████████"] -["20","346","1.00-1","3","旋律化作赤色的虹"] -["20","346","1.00-1","3","旋律化作赤色的虹"] -["20","346","1.00-1","3","那是屠殺你的色彩"] -["20","346","1.00-1","3","又甜又深的顏色"] -["20","346","1.00-1","3","聲嘶力竭歌唱的音色"] -["20","346","1.00-1","3","血色的雨濺遍四處"] -["20","346","1.00-1","3","編織成華麗的舞臺"] -["20","346","1.00-1","3","我獨自在上面跳舞"]["20","355","1.00-1","3","願望破滅了嗎?"] -["20","355","1.00-1","3","思念斷絕了嗎?"] -["20","355","1.00-1","3","希望磨滅了嗎?"] -["20","355","1.00-1","3","目光燃燒了嗎?"] -["20","355","1.00-1","3","肌膚污穢了嗎?"] -["20","355","1.00-1","3","這之後剩下的還有誰?"] -["20","345","1.00-1","5.5","就算把這具身體撕裂  只有赤銀煙霧擴散/n(因為哪裡都不存在真正的我)"] -["20","345","1.00-1","5.5","讓那具身體四分五裂 綻放鮮艷的色彩即我的糧食/n(真是虛幻的生命 美麗又可愛)"] -["20","345","1.00-1","5.5"," 如果那具身體就是你 我會將肉都吃得乾乾凈凈/n (你只能永遠成為我的東西)"] -["20","345","1.00-1","5.5","把四肢作為供物 讓你成為我腳邊的死屍/n(一直在我身邊 再也不放你走)"] -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い /n/n甘い 甘い 甘い 甘い 甘い 甘い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -[450,298,"1-0.15",4,"永远に私のものになるしかない",0,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",4,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",8,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",12,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",16,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",24,0,450,298,500,0,true,"黑体",1][450,298,"1-0.15",4,"永远に私のものになるしかない",20,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",36,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",40,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",28,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",32,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",48,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",44,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",52,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",60,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",56,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",68,0,450,298,500,0,true,"黑体",1][450,298,"1-0.15",4,"永远に私のものになるしかない",72,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",64,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",80,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",88,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",84,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",76,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",92,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",100,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",104,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",96,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",112,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",108,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",116,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",124,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",120,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",136,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",128,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",140,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",132,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",156,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",152,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",144,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",148,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",160,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",168,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",164,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",180,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",184,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",176,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",172,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",192,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",188,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",196,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",200,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",204,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",208,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",212,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",228,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",224,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",216,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",220,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",232,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",236,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",248,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",240,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",244,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",256,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",264,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",260,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",252,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",272,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",268,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",276,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",284,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",280,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",288,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",292,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",304,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",296,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",300,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",308,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",312,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",320,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",324,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",316,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",336,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",328,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",340,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",332,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",352,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",344,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",348,0,450,298,500,0,true,"黑体",1] -[450,298,"1-0.15",4,"永远に私のものになるしかない",356,0,450,298,500,0,true,"黑体",1] -[0.01,0,"1-1",6,"この想い届かないのかな",0,0,0.01,0,4200,500,false,"楷体",1] -[0.01,0.09,"1-1",6,"この想い届かないのかな",0,0,0.01,0,4200,500,false,"楷体",1] -[0.01,0.27,"1-1",6,"この想い届かないのかな",0,0,0.01,0,4200,500,false,"楷体",1][0.01,0.18,"1-1",6,"この想い届かないのかな",0,0,0.01,0,4200,500,false,"楷体",1] -[0.01,0.36,"1-1",6,"この想い届かないのかな",0,0,0.01,0,4200,500,false,"楷体",1] -[0.01,0.45,"1-1",6,"この想い届かないのかな",0,0,0.01,0,4200,500,false,"楷体",1] -[0.01,0.63,"1-1",6,"この想い届かないのかな",0,0,0.01,0,4200,500,false,"楷体",1] -[0.01,0.54,"1-1",6,"この想い届かないのかな",0,0,0.01,0,4200,500,false,"楷体",1] -[0.01,0.72,"1-1",6,"この想い届かないのかな",0,0,0.01,0,4200,500,false,"楷体",1][0.01,0.81,"1-1",6,"この想い届かないのかな",0,0,0.01,0,4200,500,false,"楷体",1] -[0.01,0.9,"1-1",6,"この想い届かないのかな",0,0,0.01,0,4200,500,false,"楷体",1] -[0.01,0.99,"1-1",6,"この想い届かないのかな",0,0,0.01,0,4200,500,false,"楷体",1] -[0.01,0.99,"1-1",6,"この想い届かないのかな",0,0,0.01,0.99,4200,500,false,"楷体",1] -[0.01,0.9,"1-1",6,"この想い届かないのかな",0,0,0.01,0.99,4200,500,false,"楷体",1] -[0.01,0.72,"1-1",6,"この想い届かないのかな",0,0,0.01,0.99,4200,500,false,"楷体",1] -[0.01,0.81,"1-1",6,"この想い届かないのかな",0,0,0.01,0.99,4200,500,false,"楷体",1] -[0.01,0.63,"1-1",6,"この想い届かないのかな",0,0,0.01,0.99,4200,500,false,"楷体",1] -[0.01,0.54,"1-1",6,"この想い届かないのかな",0,0,0.01,0.99,4200,500,false,"楷体",1] -[0.01,0.45,"1-1",6,"この想い届かないのかな",0,0,0.01,0.99,4200,500,false,"楷体",1] -[0.01,0.36,"1-1",6,"この想い届かないのかな",0,0,0.01,0.99,4200,500,false,"楷体",1][0.01,0.27,"1-1",6,"この想い届かないのかな",0,0,0.01,0.99,4200,500,false,"楷体",1] -[0.01,0.18,"1-1",6,"この想い届かないのかな",0,0,0.01,0.99,4200,500,false,"楷体",1] -[0.01,0.09,"1-1",6,"この想い届かないのかな",0,0,0.01,0.99,4200,500,false,"楷体",1] -[0.01,0,"1-1",6,"この想い届かないのかな",0,0,0.01,0.99,4200,500,false,"楷体",1] -[0.01,0.09,"1-0",5,"この想い届かないのかな",0,0,0.01,0,4200,500,false,"楷体",1] -[0.01,0,"1-0",5,"この想い届かないのかな",0,0,0.01,0,4200,500,false,"楷体",1] -[0.01,0.27,"1-0",5,"この想い届かないのかな",0,0,0.01,0,4200,500,false,"楷体",1][0.01,0.18,"1-0",5,"この想い届かないのかな",0,0,0.01,0,4200,500,false,"楷体",1] -[0.01,0.36,"1-0",5,"この想い届かないのかな",0,0,0.01,0,4200,500,false,"楷体",1] -[0.01,0.45,"1-0",5,"この想い届かないのかな",0,0,0.01,0,4200,500,false,"楷体",1] -[0.01,0.72,"1-0",5,"この想い届かないのかな",0,0,0.01,0,4200,500,false,"楷体",1] -[0.01,0.63,"1-0",5,"この想い届かないのかな",0,0,0.01,0,4200,500,false,"楷体",1] -[0.01,0.54,"1-0",5,"この想い届かないのかな",0,0,0.01,0,4200,500,false,"楷体",1] -[0.01,0.81,"1-0",5,"この想い届かないのかな",0,0,0.01,0,4200,500,false,"楷体",1] -[0.01,0.9,"1-0",5,"この想い届かないのかな",0,0,0.01,0,4200,500,false,"楷体",1][0.01,0.99,"1-0",5,"この想い届かないのかな",0,0,0.01,0,4200,500,false,"楷体",1] -[0.01,0.99,"1-0",5,"この想い届かないのかな",0,0,0.01,0.99,4200,500,false,"楷体",1] -[0.01,0.9,"1-0",5,"この想い届かないのかな",0,0,0.01,0.99,4200,500,false,"楷体",1] -[0.01,0.72,"1-0",5,"この想い届かないのかな",0,0,0.01,0.99,4200,500,false,"楷体",1] -[0.01,0.81,"1-0",5,"この想い届かないのかな",0,0,0.01,0.99,4200,500,false,"楷体",1] -[0.01,0.63,"1-0",5,"この想い届かないのかな",0,0,0.01,0.99,4200,500,false,"楷体",1] -[0.01,0.45,"1-0",5,"この想い届かないのかな",0,0,0.01,0.99,4200,500,false,"楷体",1][0.01,0.54,"1-0",5,"この想い届かないのかな",0,0,0.01,0.99,4200,500,false,"楷体",1] -[0.01,0.36,"1-0",5,"この想い届かないのかな",0,0,0.01,0.99,4200,500,false,"楷体",1] -[0.01,0.27,"1-0",5,"この想い届かないのかな",0,0,0.01,0.99,4200,500,false,"楷体",1] -[0.01,0.09,"1-0",5,"この想い届かないのかな",0,0,0.01,0.99,4200,500,false,"楷体",1] -[0.01,0.18,"1-0",5,"この想い届かないのかな",0,0,0.01,0.99,4200,500,false,"楷体",1] -[0.01,0,"1-0",5,"この想い届かないのかな",0,0,0.01,0.99,4200,500,false,"楷体",1][0.4,0,"1-1",10,"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM",0,0,0,0,6000,1500,false,"黑体",1][0.4,0.01,"1-1",10,"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM",0,0,0,0.01,6000,1500,false,"黑体",1][0.4,0.02,"1-1",10,"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM",0,0,0,0.02,6000,1500,false,"黑体",1][0.4,0.03,"1-1",10,"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM",0,0,0,0.03,6000,1500,false,"黑体",1][0.4,0.04,"1-1",10,"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMo~ ;#MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM",0,0,0,0.04,6000,1500,false,"黑体",1] -[0.4,0.05,"1-1",10,"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM 8MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM",0,0,0,0.05,6000,1500,false,"黑体",1] -[0.4,0.06,"1-1",10,"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM #MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM",0,0,0,0.06,6000,1500,false,"黑体",1] -[0.4,0.07,"1-1",10,"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM8 :MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM",0,0,0,0.07,6000,1500,false,"黑体",1] -[0.4,0.08,"1-1",10,"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM .M; oMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM",0,0,0,0.08,6000,1500,false,"黑体",1][0.4,0.09,"1-1",10,"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM #MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM",0,0,0,0.09,6000,1500,false,"黑体",1][0.4,0.1,"1-1",10,"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM# MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM",0,0,0,0.1,6000,1500,false,"黑体",1][0.4,0.11,"1-1",10,"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM: MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM",0,0,0,0.11,6000,1500,false,"黑体",1][0.4,0.12,"1-1",10,"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM: MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM",0,0,0,0.12,6000,1500,false,"黑体",1][0.4,0.13,"1-1",10,"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM= MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM",0,0,0,0.13,6000,1500,false,"黑体",1][0.4,0.14,"1-1",10,"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM IMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM",0,0,0,0.14,6000,1500,false,"黑体",1][0.4,0.15,"1-1",10,"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM",0,0,0,0.15,6000,1500,false,"黑体",1][0.4,0.16,"1-1",10,"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM",0,0,0,0.16,6000,1500,false,"黑体",1][0.4,0.17,"1-1",10,"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMO IMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM",0,0,0,0.17,6000,1500,false,"黑体",1][0.4,0.18,"1-1",10,"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM+ M MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM",0,0,0,0.18,6000,1500,false,"黑体",1][0.4,0.19,"1-1",10,"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM #M: ;MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM",0,0,0,0.19,6000,1500,false,"黑体",1][0.4,0.2,"1-1",10,"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMME #MMI 8MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM",0,0,0,0.2,6000,1500,false,"黑体",1][0.4,0.21,"1-1",10,"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMoiMM MMMI MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM",0,0,0,0.21,6000,1500,false,"黑体",1][0.4,0.22,"1-1",10,"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM` =. oMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM",0,0,0,0.22,6000,1500,false,"黑体",1][0.4,0.23,"1-1",10,"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM M MM MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM",0,0,0,0.23,6000,1500,false,"黑体",1][0.4,0.24,"1-1",10,"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM M M: MMM MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM",0,0,0,0.24,6000,1500,false,"黑体",1][0.06,0.59,"1-1",3.5,"「この想い届かないのかな?」",0,0,0.06,0.59,500,0,true,"黑体",1] -[0.06,0.59,"1-1",3.5,"「こ",0,0,0.06,0.59,500,0,true,"黑体",1] -[0.06,0.59,"1-0.99",2.7,"「この想",0,0,0.06,0.59,500,0,true,"黑体",1] -[0.06,0.59,"1-0.98",1.7,"「この想い届",0,0,0.06,0.59,500,0,true,"黑体",1] -[0.06,0.59,"1-0.98",1.9,"「この想い",0,0,0.06,0.59,500,0,true,"黑体",1][0.06,0.59,"1-0.98",1.3,"「この想い届か",0,0,0.06,0.59,500,0,true,"黑体",1] -[0.06,0.59,"1-0.98",0.7,"「この想い届かないの",0,0,0.06,0.59,500,0,true,"黑体",1] -[0.06,0.59,"1-0.98",1.1,"「この想い届かない",0,0,0.06,0.59,500,0,true,"黑体",1] -[0.06,0.59,"1-0.98",0.6,"「この想い届かないのか",0,0,0.06,0.59,500,0,true,"黑体",1] -[0.06,0.59,"1-0.98",0.3,"「この想い届かないのかな?」",0,0,0.06,0.59,500,0,true,"黑体",1] -[0.15,0.15,"1-0",1,"私の中の",0,0,0.15,0.15,500,0,1,"SimHei",true] -[0.15,0.15,"1-0",1,"私の中の",0,0,0.15,0.15,500,0,1,"SimHei",true] -[0.6,0.67,"1-0",1,"私の中の",0,0,0.6,0.67,500,0,1,"SimHei",true] -[0.6,0.67,"1-0",0.5,"私の中の",0,0,0.6,0.67,500,0,1,"SimHei",true] -[0.15,0.6,"1-0",1,"私の中の",0,0,0.15,0.6,500,0,1,"SimHei",true] -[0.65,0.15,"1-0",1,"私が",0,0,0.65,0.15,500,0,1,"SimHei",true] -[0.65,0.15,"1-0",0.5,"私が",0,0,0.65,0.15,500,0,1,"SimHei",true] -[0.15,0.6,"1-0",1,"私の中の",0,0,0.15,0.6,500,0,1,"SimHei",true][84,128,"1-0",1,"ひとつの",0,0,84,128,500,0,1,"SimHei",true] -[90,128,"1-0",0.5,"ひとつの",0,0,90,128,500,0,1,"SimHei",true] -[196,128,"1-0",1,"のつとひ",0,0,196,128,500,0,1,"SimHei",true] -[196,128,"1-0",0.5,"のつとひ",0,0,196,128,500,0,1,"SimHei",true] -[168,168,"1-1",1,"単语を",0,0,168,168,500,0,1,"SimHei",true] -[163,168,"1-0",1,"単语を",0,0,168,168,500,0,1,"SimHei",true] -[181,167,"1-0",1,"単语を",0,0,168,168,500,0,1,"SimHei",true] -[163,164,"1-0",0.5,"単语を",0,0,163,164,500,0,1,"SimHei",true] -[162,161,"1-0",0.5,"単语を",0,0,162,161,500,0,1,"SimHei",true] -[642,170,"1-0",1,"何\n度\nも",0,0,642,170,500,0,1,"SimHei",true] -[734,200,"1-0",1,"何\n度\nも",350,0,734,200,500,0,1,"SimHei",true] -[709,226,"1-0",1,"何\n度\nも",15,0,709,226,500,0,1,"SimHei",true][747,298,"1-0",1,"何度も",10,0,747,298,500,0,1,"SimHei",true] -[706,205,"1-0",1,"何\n度\nも",0,0,706,205,500,0,1,"SimHei",true] -[733,361,"1-0",1,"何度も",170,0,733,361,500,0,1,"SimHei",true] -[129,254,"1-0",1.5,"无尽蔵に",0,0,129,254,500,0,1,"SimHei",true] -[42,257,"1-0",0.5,"无尽蔵に",0,0,132,255,400,0,1,"SimHei",true] -[42,257,"1-0",0.5,"无尽蔵に",0,0,132,255,500,0,1,"SimHei",true] -[42,257,"1-0",0.5,"无尽蔵に",0,0,132,255,600,0,1,"SimHei",true] -[42,257,"1-0",0.5,"无尽蔵に",0,0,132,255,700,0,1,"SimHei",true] -[42,257,"1-0",0.5,"无尽蔵に",0,0,132,255,800,0,1,"SimHei",true] -[42,257,"1-0",0.5,"无尽蔵に",0,0,132,255,900,0,1,"SimHei",true] -[42,257,"1-0",0.5,"无尽蔵に",0,0,132,255,1000,0,1,"SimHei",true][42,257,"1-0",0.5,"无尽蔵に",0,0,132,255,200,0,1,"SimHei",true] -[42,257,"1-0",0.5,"无尽蔵に",0,0,132,255,100,0,1,"SimHei",true] -[42,257,"1-0",0.6,"无尽蔵に",0,0,132,255,300,0,1,"SimHei",true] -[132,254,"1-0",0.5,"无尽蔵に",0,0,132,254,1000,0,1,"SimHei",true] -[132,254,"1-0",0.5,"无尽蔵に",0,0,132,254,1000,0,1,"SimHei",true] -好红 -好红 -好红 -好红 -好 红 -好红好红 -好甜 -好甜好甜 -好甜 -好甜好甜 -好甜 -好甜好甜 -好 甜 -好甜 好甜 -好红 -好红 -好红 好红 好红 -好红 好红 -好红好红 -好红 -好甜 好甜好红好红 -好红 -好红 好红好红 好红 -赤い 赤い 赤い -赤い 赤い 赤い -赤い 赤い 赤い -赤い 赤い 赤い -赤い 赤い 赤い -赤い 赤い 赤い -赤い 赤い 赤い -赤い 赤い 赤い赤い 赤い 赤い -赤い 赤い 赤い赤い 赤い 赤い -赤い 赤い 赤い赤い 赤い 赤い赤い 赤い 赤い赤い 赤い 赤い -赤い 赤い 赤い赤い 赤い 赤い -赤い 赤い 赤い赤い 赤い 赤い -赤い 赤い 赤い赤い 赤い 赤い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -赤い 赤い 赤い赤い 赤い 赤い赤い 赤い 赤い赤い 赤い 赤い -赤い 赤い 赤い赤い 赤い 赤い赤い 赤い 赤い赤い 赤い 赤い -赤い 赤い 赤い赤い 赤い 赤い赤い 赤い 赤い赤い 赤い 赤い -赤い 赤い 赤い赤い 赤い 赤い赤い 赤い 赤い赤い 赤い 赤い -赤い 赤い 赤い赤い 赤い 赤い赤い 赤い 赤い赤い 赤い 赤い -赤い 赤い 赤い赤い 赤い 赤い赤い 赤い 赤い赤い 赤い 赤い -赤い 赤い 赤い赤い 赤い 赤い赤い 赤い 赤い赤い 赤い 赤い -甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い -私の中の私が 私の中の私が -ひとつの単语を -何度も何度も何度も -私の中の私が 私の中の私が -无尽蔵に -缲り返し 缲り返し 缲り返し 缲り返し -掴む左手が甘くて 震える右手が甘くて -歌う -笑う口が裂けても それがあなたを杀し -楽しくて 脳髄を烧くように -楽しくて楽しくて 震えて 楽しくて -甘い 甘い 甘い 甘い 甘い 甘い 甘い -赤い 赤い 赤い 赤い -赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い -[0,0,"1-1",4,"甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n",0,0,0,0,500,0,1,"SimHei",true][0,149,"1-1",4,"甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n",0,0,0,149,500,0,1,"SimHei",true] -[0,300,"1-1",4,"甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n",0,0,0,300,500,0,1,"SimHei",true] -[0,451,"1-1",4,"甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n",0,0,0,451,500,0,1,"SimHei",true] -[467,199,"1-1",4,"甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n",0,0,467,199,500,0,1,"SimHei",true] -[467,393,"1-1",4,"甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n",0,0,467,393,500,0,1,"SimHei",true] -[366,3,"1-1",4,"甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n",0,0,366,3,500,0,1,"SimHei",true] -[0,446,"1-1",4,"赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n",0,0,0,446,500,0,1,"SimHei",true] -[293,0,"1-1",4,"赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n",0,0,293,0,500,0,1,"SimHei",true] -[293,150,"1-1",4,"赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n",0,0,293,150,500,0,1,"SimHei",true] -[293,298,"1-1",4,"赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n",0,0,293,298,500,0,1,"SimHei",true] -[0,0,"1-1",4,"赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n",0,0,0,0,500,0,1,"SimHei",true] -[0,148,"1-1",4,"赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n",0,0,0,148,500,0,1,"SimHei",true] -[0,297,"1-1",4,"赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n",0,0,0,297,500,0,1,"SimHei",true] -[580,0,"1-1",4,"赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n",0,0,580,0,500,0,1,"SimHei",true] -[580,299,"1-1",4,"赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n",0,0,580,299,500,0,1,"SimHei",true] -[293,445,"1-1",4,"赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n",0,0,293,445,500,0,1,"SimHei",true] -[580,447,"1-1",4,"赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n",0,0,580,447,500,0,1,"SimHei",true] -[562,416,"0.5-0.5",4,"赤い",0,0,562,416,500,0,1,"SimHei",true][354,77,"0.5-0.5",4,"赤い",0,0,354,77,500,0,1,"SimHei",true] -[29,326,"0.5-0.5",4,"赤い",0,0,29,326,500,0,1,"SimHei",true] -[749,443,"0.5-0.5",4,"赤い",0,0,749,443,500,0,1,"SimHei",true] -[569,232,"0.5-0.5",4,"赤い",0,0,569,232,500,0,1,"SimHei",true] -[62,135,"0.5-0.5",4,"赤い",0,0,62,135,500,0,1,"SimHei",true] -[731,296,"1-1",4,"甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n",0,0,731,296,500,0,1,"SimHei",true] -[0,0,"1-1",4,"甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n",0,0,0,0,500,0,1,"SimHei",true] -[0,301,"1-1",4,"甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n",0,0,0,301,500,0,1,"SimHei",true] -[731,0,"1-1",4,"甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n",0,0,731,0,500,0,1,"SimHei",true] -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い赤い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い 甘い -[0.25,0.11,"1-1",2,"/n/n/n 杀",0,0,0.25,0.11,500,0,true,"黑体",1] -[0.25,0.11,"1-1",2,"/n/n/n 杀/n/n/n",0,0,0.25,0.11,500,0,false,"黑体",1] -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い /n/n赤い 赤い 赤い 赤い 赤い 赤い -[0.4,0.25,"1-1",10,"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMO MMMMMMMM MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM",0,0,0,0.25,6000,1500,false,"黑体",1] -[0.4,0.26,"1-1",10,"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM + ;MMMMMMMM MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM",0,0,0,0.26,6000,1500,false,"黑体",1] -[0.4,0.27,"1-1",10,"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM i M .EM MMMMMMMMMM iMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM",0,0,0,0.27,6000,1500,false,"黑体",1] -[0.4,0.28,"1-1",10,"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM M MMM MMM# +IMMMMMMMMMM= MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM",0,0,0,0.28,6000,1500,false,"黑体",1] -[0.4,0.29,"1-1",10,"MMMMMMMMMMMMMMMMMMMMMMMM~ ~MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM+O#i.MMM8 MMMM MMMMMMMMMMMMM .MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM",0,0,0,0.29,6000,1500,false,"黑体",1] -[0.4,0.3,"1-1",10,"MMMMMMMMMMMMMMMMMMMMMM ;MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMME~ :MMMMMMMMMMMMMMMMMM MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM",0,0,0,0.3,6000,1500,false,"黑体",1] -[0.4,0.31,"1-1",10,"MMMMMMMMMMMMMMMMMMMM `oMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMI ;MMMMMMMMMMMMMM 8MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMi:MMMMMMMMMMMMMMMMMMMMMMMMM",0,0,0,0.31,6000,1500,false,"黑体",1] -[0.4,0.31,"1-1",10,"MMMMMMMMMMMMMMMMME +MMO iMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM8 IMMMMMMMMMMM8M OMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMEI #MMMMMMMMMMMMMMMMMMMMMM",0,0,0,0.31,6000,1500,false,"黑体",1] -[0.4,0.32,"1-1",10,"MMMMMMMMMMMMMMM; ;MMMMMMMM+ 8MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM 8MMMMMMMMMMM+EI MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM8~ MMMMMMMMMMMMMMMMMMMMM",0,0,0,0.32,6000,1500,false,"黑体",1] -[0.4,0.33,"1-1",10,"MMMMMMMMMMMM8 OMMMMMMMMMMM =MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM# MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM; ~#M8 MMMMMMMMMMMMMMMMMMM",0,0,0,0.33,6000,1500,false,"黑体",1] -[0.4,0.34,"1-1",10,"MMMMMMMMME ~MMMMMMMMM MMi 8MMMMMMMMMMMMMMMMMMMMMMMMMMMMMM OMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM; ;#MMMMMMM. MMMMMMMMMMMMMMMMM",0,0,0,0.34,6000,1500,false,"黑体",1] -[0.4,0.35,"1-1",10,"MMMMMMO `M .MMMMMMi iMMM8 =EMMMMMMMMMMMo: =#MMMo MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMo .. .MMMMMMMMMMM EMMMMMMMMMMMMMM",0,0,0,0.35,6000,1500,false,"黑体",1] -[0.4,0.36,"1-1",10,"MMMMM 8MMM MMMMM =MMMMM+ IO=` :OMMMMMMMMMMMMM8; ;MMM oMMMMMMMM+ ;MMMMMMMMMMMM",0,0,0,0.36,6000,1500,false,"黑体",1] -[0.4,0.37,"1-1",10,"MMMM# oMMMMM. MMMM IMMMMMM~ ``..` iMMMM= MMMMMM MI 8MMMMMMMMM",0,0,0,0.37,6000,1500,false,"黑体",1] -[0.4,0.38,"1-1",10,"MMMMM ;MMMMMMMM~ MMMMM IMMMMM =MMMMMMMM. =MMMMMM MMMMMMM MMMM: MMI OMMMMMM",0,0,0,0.38,6000,1500,false,"黑体",1] -[0.4,0.39,"1-1",10,"MMMMM +MMMMMMM~ MMMMMM IMMMMM MMMMMM MMMMM MMMMMM MMMMMMMMM. IMMMMMM MMM8 MMMM# MMMMMM",0,0,0,0.39,6000,1500,false,"黑体",1] -[0.4,0.4,"1-1",10,"MMMMM MMMMMMM; MMMMMMM =MMMM: ~MMMM =MMMMM 8MMMM~ #MMMMMM MMMMMM MMMM~ MMMMMMME MMMMMM",0,0,0,0.4,6000,1500,false,"黑体",1] -[0.4,0.41,"1-1",10,"MMMMM +MMMMMMi MMMMMMMM =MMMM~ :MMMM MMM .MMM~ MMMM8 IMMMMO MMMMM MMMMMMMM8; MMMMMM",0,0,0,0.41,6000,1500,false,"黑体",1] -[0.4,0.42,"1-1",10,"MMMMM MMMMMMMo MMMMMMMMM =MMMMM iMMMM MMMM~ OMM MMMMO MMMMi IMMMMMM ;MMMMMMM. MMMMMM",0,0,0,0.42,6000,1500,false,"黑体",1] -[0.4,0.43,"1-1",10,"MMMM# MMMMMMMM# MMMMMMMMMM =MMMMMM MMMMM8 MMMMMM MMMMM` MMMMO MMMMM; OMMMMMMM MMMMMMM` MMMMMM",0,0,0,0.43,6000,1500,false,"黑体",1] -[0.4,0.44,"1-1",10,"MMMMM MMMMMMMMME MMMMMMMMMMM =MMMMMMM MMMMMM MMMMM= EMMMMM MMMMM8 MMMMMM: #MMMMMMMM MMMMMMM8 MMMMMM",0,0,0,0.44,6000,1500,false,"黑体",1] -[0.4,0.45,"1-1",10,"MMMMM: MMMMMMMMMMM MMMMMMMMMMMM IMMMMMMMM MMMMMMO =MMMMM MMMMM MMMMMME MMMMMMM: MMMMMMMMMO .MMMMMMMM MMMMMM",0,0,0,0.45,6000,1500,false,"黑体",1] -[0.4,0.46,"1-1",10,"MMMMMM= MMMMMMMMMMMM MMMMMMMMMMMMM OMMMMMMMM8 MMMMMMM MMMMM MMMMM: IMMMMMMM MMMMMMMM. MMMMMMMMMM; #MMMMMMMMM MMMMMMM",0,0,0,0.46,6000,1500,false,"黑体",1] -[0.4,0.47,"1-1",10,"MMMMMMM8 MMMMMMMMMMMMM:MMMMMMMMMMMMMMMMMMMMMMMMI MMMMMMMO MMMMM .MMMMM MMMMMMM# MMMMMMMMM+.MMMMMMMMMMM MMMMMMMMMM8 oMMMMMMMM",0,0,0,0.47,6000,1500,false,"黑体",1] -[0.4,0.48,"1-1",10,"MMMMMMMMM MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM MMMMMMMM MMMMMI MMMMM MMMMMMMM# MMMMMMMMMMMMMMMMMMMMMMMI:MMMMMMMMMMM MMMMMMMMMM",0,0,0,0.48,6000,1500,false,"黑体",1] -[0.4,0.49,"1-1",10,"MMMMMMMMMM MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM MMMMMMMMO MMMMM ; MMMMM; EMMMMMMMMM MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM MMMMMMMMMMMM",0,0,0,0.49,6000,1500,false,"黑体",1] -[0.4,0.5,"1-1",10,"MMMMMMMMMMM MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM oMMMMMMMMM iMM M M` ~MMM ~MMMMMMMMMM ~MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM8 ~MMMMMMMMMMMMM",0,0,0,0.5,6000,1500,false,"黑体",1] -[0.4,0.51,"1-1",10,"MMMMMMMMMMMM MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM = MM. MM ~MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM MMMMMMMMMMMMMMM",0,0,0,0.51,6000,1500,false,"黑体",1][0.4,0.52,"1-1",10,"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMME MMM MMM ~MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM +MMMMMMMMMMMMMMMM",0,0,0,0.52,6000,1500,false,"黑体",1][0.4,0.53,"1-1",10,"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM MMMMMMM MMMMM8 iMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM",0,0,0,0.53,6000,1500,false,"黑体",1][0.4,0.54,"1-1",10,"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM= EMMMMMMMMM MMMMMMMM# +MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM",0,0,0,0.54,6000,1500,false,"黑体",1] -[0.4,0.55,"1-1",10,"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM#8EMMMMMM EMMMMMMMMMMMM +MMMMMMMMMMMM= iMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM",0,0,0,0.55,6000,1500,false,"黑体",1] -[0.4,0.56,"1-1",10,"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM ` EMMMMMMMMMMMMMMM MMMMMMMMMMMMMMM#. MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM",0,0,0,0.56,6000,1500,false,"黑体",1] -[0.4,0.57,"1-1",10,"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM Io MMMMMMMMMMMMMMMMMM MMMMMMMMMMMMMMMMMMO MMiEMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM",0,0,0,0.57,6000,1500,false,"黑体",1] -[0.4,0.58,"1-1",10,"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM; .MMMMMMMMMMMMMMMMMMMM MMMMMMMMMMMMMMMMMMMMMo OMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM",0,0,0,0.58,6000,1500,false,"黑体",1] -[0.4,0.59,"1-1",10,"MMMMMMMMMMMMMMMMMMMMMMMMMMM;:=#MMMM#o; ;MMMMMMMMMMMMMMMMMMMMMM MMMMMMMMMMMMMMMMMMMMMMMMM8. `EMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM",0,0,0,0.59,6000,1500,false,"黑体",1] -[0.4,0.6,"1-1",10,"MMMMMMMMMMMMMMMMMMMMMMMMMMM= ;EMMMMMMMMMMMMMMMMMMMMMMMMM MMMMMMMMMMMMMMMMMMMMMMMMMMMM; `;=++MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM",0,0,0,0.6,6000,1500,false,"黑体",1] -[0.4,0.61,"1-1",10,"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMM8. IMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM~ ;MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMi iMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM",0,0,0,0.61,6000,1500,false,"黑体",1][0.4,0.62,"1-1",10,"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMO=;` I#MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM# MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM8i. ~oMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM",0,0,0,0.62,6000,1500,false,"黑体",1] -[0.4,0.63,"1-1",10,"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM",0,0,0,0.63,6000,1500,false,"黑体",1] -[0.4,0.64,"1-1",10,"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMO EMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM",0,0,0,0.64,6000,1500,false,"黑体",1][0.4,0.65,"1-1",10,"MMME#M88M8MMoMOOM8MMME#OEMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM",0,0,0,0.65,6000,1500,false,"黑体",1][0.4,0.66,"1-1",10,"#MMMMMMMMMMMM#MM#MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMO EMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM",0,0,0,0.66,6000,1500,false,"黑体",1][0.4,0.67,"1-1",10,"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM",0,0,0,0.67,6000,1500,false,"黑体",1][0.4,0.68,"1-1",10,"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM/n",0,0,0,0.68,6000,1500,false,"黑体",1][89,206,"1-0",1,"「刻む伤は愈えないのかな?」",0,0,89,206,500,0,0,"SimHei",true] -[89,206,"1-0",1,"「刻む伤は愈えないのかな?」",0,0,89,206,500,0,0,"SimHei",true] -[89,206,"1-0",1,"「刻む伤は愈えないのかな?」",0,0,89,206,500,0,0,"SimHei",true] -[0.99,0,"1-1",2.2,"喉\nを\n枯\nら\nし\n叫\nウ\n音\n色 ",0,0,0,0,2200,0,0,"SimHei",true] -[0.99,0,"1-1",2,"喉\nを\n枯\nら\nし\n叫\nウ\n音\n色 ",0,0,0,0,2000,0,0,"SimHei",true] -[0.99,0,"1-1",2.1,"喉\nを\n枯\nら\nし\n叫\nウ\n音\n色 ",0,0,0,0,2100,0,0,"SimHei",true] -[0.99,0,"1-1",2.3,"喉\nを\n枯\nら\nし\n叫\nウ\n音\n色 ",0,0,0,0,2300,0,0,"SimHei",true] -[0.99,0,"1-1",2.4,"喉\nを\n枯\nら\nし\n叫\nウ\n音\n色 ",0,0,0,0,2400,0,0,"SimHei",true] -[0,0,"1-1",2,"旋\n律\nは\n朱\nの\n虹\nと\nな\nり\n\n\n\n",0,0,0.99,0,2000,0,0,"SimHei",true] -[0,0,"1-1",2.2,"旋\n律\nは\n朱\nの\n虹\nと\nな\nり\n\n\n\n",0,0,0.99,0,2200,0,0,"SimHei",true] -[0,0,"1-1",2.4,"旋\n律\nは\n朱\nの\n虹\nと\nな\nり\n\n\n\n",0,0,0.99,0,2400,0,0,"SimHei",true] -[0,0,"1-1",2.1,"旋\n律\nは\n朱\nの\n虹\nと\nな\nり\n\n\n\n",0,0,0.99,0,2100,0,0,"SimHei",true] -[0,0,"1-1",2.3,"旋\n律\nは\n朱\nの\n虹\nと\nな\nり\n\n\n\n",0,0,0.99,0,2300,0,0,"SimHei",true] -[0.99,0.8,"1-1",2,"君屠る此ノ色彩",0,0,0,0.8,2000,0,0,"SimHei",true][0.99,0.2,"1-1",2,"君屠る此ノ色彩",0,0,0,0.2,2000,0,0,"SimHei",true] -[0.99,0.6,"1-1",2,"君屠る此ノ色彩",0,0,0,0.6,2000,0,0,"SimHei",true] -[0.99,0,"1-1",2,"君屠る此ノ色彩",0,0,0,0,2000,0,0,"SimHei",true] -[0.99,0.4,"1-1",2,"君屠る此ノ色彩",0,0,0,0.4,2000,0,0,"SimHei",true] -[0,0.1,"1-1",3,"喉を枯らし叫ウ音色",0,0,0.99,0.1,3000,0,0,"SimHei",true] -[0,0.5,"1-1",3,"喉を枯らし叫ウ音色",0,0,0.99,0.5,3000,0,0,"SimHei",true] -[0,0.9,"1-1",3,"喉を枯らし叫ウ音色",0,0,0.99,0.9,3000,0,0,"SimHei",true] -[0,0.3,"1-1",3,"喉を枯らし叫ウ音色",0,0,0.99,0.3,3000,0,0,"SimHei",true] -[0,0.7,"1-1",3,"喉を枯らし叫ウ音色",0,0,0.99,0.7,3000,0,0,"SimHei",true] -[0.39,0.3,"1-0",3,"彩られたら",0,0,0.39,0.3,3000,0,0,"SimHei",true] -[0.28,0.23,"1-0",3,"赤キ雨に",0,0,0.28,0.23,3000,0,0,"SimHei",true][0.09,0.75,"1-0",2,"声嘶力竭歌唱的音色 ",0,0,0.09,0.75,3000,0,1,"SimHei",true] -[0.05,0.68,"1-0",2,"重复着一个单语",0,0,0.05,0.68,3000,0,1,"SimHei",true] -[0.05,0.68,"1-0",2,"我之中的我",0,0,0.05,0.68,3000,0,1,"SimHei",true] -[0.05,0.68,"1-0",2,"多少遍多少遍多少遍",0,0,0.05,0.68,3000,0,1,"SimHei",true] -[0.05,0.68,"1-0",2,"重复着 重复着 重复着",0,0,0.05,0.68,3000,0,1,"SimHei",true] -[0.04,0.68,"1-0",2,"紧握的左手异常甜蜜",0,0,0.04,0.68,3000,0,1,"SimHei",true] -[0.05,0.68,"1-0",2,"不断重复",0,0,0.05,0.68,3000,0,1,"SimHei",true][0.05,0.68,"1-0",2,"歌唱",0,0,0.05,0.68,3000,0,1,"SimHei",true] -[0.04,0.68,"1-0",2,"随后便给予你杀戮",0,0,0.04,0.68,3000,0,1,"SimHei",true] -[0.04,0.68,"1-0",2,"挥舞的右手无比甘甜",0,0,0.04,0.68,3000,0,1,"SimHei",true] -[0.04,0.68,"1-0",2,"笑着的嘴巴已然开裂",0,0,0.04,0.68,3000,0,1,"SimHei",true] -[0.04,0.68,"1-0",2,"好快乐 刻录在你的脑髓之中",0,0,0.04,0.68,3000,0,1,"SimHei",true] -[0.04,0.68,"1-0",2,"快乐到颤抖 好快乐",0,0,0.04,0.68,3000,0,1,"SimHei",true] -[0.99,0.25,"1-1",3,"狂気満ちて行くわ",0,0,0,0.25,3000,0,1,"SimHei",true][0.99,0.6,"1-1",3,"この気持ち気付いて",0,0,0,0.6,3000,0,1,"SimHei",true] -[0,0.4,"1-1",3,"狂気満ちて行くわ",0,0,0.99,0.4,3000,0,1,"SimHei",true] -[0,0.8,"1-1",3,"爱溢れて行くわ",0,0,0.99,0.8,3000,0,1,"SimHei",true] -[0.99,0.25,"1-1",3,"止めることはできない",0,0,0,0.25,3000,0,1,"SimHei",true] -[0,0.1,"1-1",3,"希望你能察觉到我的心情",0,0,0.99,0.1,3000,0,1,"SimHei",true] -[0,0.5,"1-1",3,"希望你能察觉到我的心情",0,0,0.99,0.5,3000,0,1,"SimHei",true] -[0.99,0.3,"1-1",3,"渐渐地陷入疯狂",0,0,0,0.3,3000,0,1,"SimHei",true] -[0.99,0.7,"1-1",3,"爱就要满溢出来",0,0,0,0.7,3000,0,1,"SimHei",true] -[0,0.9,"1-1",3," 再也无法停止",0,0,0.99,0.9,3000,0,1,"SimHei",true]深く朱き潤む瞳 甘い色のスカート揺らす -幼き頬 朱を滲ませ 疼きに足を崩される -「その瞳には誰が映るのかな?」 -「心壊れているのかな?」 -「この想い届かないのかな?」 -「刻む傷は癒えないのかな?」あなたのその全てが欲しくて 欲しくて震えてる (この気持ち気付いて どうして気付いてくれないの) -「そうして時を刻むの?」愛で撫でて揺さ振られて この衝動殺してよ (愛漏れて行くわ 止めることはできない…) -喉を枯らし叫ウ音色 旋律は朱の虹となり甘美なその鼓動を うだち尽くして止めようか (狂気に満ちてゆくわ どうすれば止まるの) -その肌を穢し尽くし 辱めるのは私だけ (この気持ち壊れたて どこへ辿り着くのでしょうか) -喉を枯らし叫ウ音色 赤キ雨に彩られたら -綺麗な舞台の出来上がり 私独り其処で踊る -「その願い潰えたのかな?」 -君屠る此ノ色彩 甘く深き色を放つ -「その想い断たれたのかな?」 -「その希望絶えたのかな?」 -「その瞳焼かれたのかな?」「そして誰もいなくなる?」 -其ノ生を引き裂かれて赤銀を吐き消し飛べ(どこにも本当の 私なんていないのだから -其ノ生の華散らして 極彩に咲き我が糧 (儚い命だわ 美しく愛おしい)其ノ生がお前ならば 喰らい尽くして血肉にす (永遠に私のものになるしかない) -「その肌は穢されたかな?」 -私の中の私が 私の中の私が(この気持ち気付いて…) ひとつの単語を -其ノ四肢を贄と捧げ 我が足元の死屍となれ (ずっと私の傍に もう行かさないから…)無盡蔵に(狂気満ちて行くわ…)繰り返し 繰り返し 繰り返し 繰り返し 歌う (この気持ち気付いて…) -掴む左手が甘くて 震える右手が甘くて -何度も何度も何度も -笑う口が裂けても それがあなたを殺し -楽しくて 脳髄を焼くように(愛溢れて行くわ 止めることはできない…) -楽しくて 震えて 楽しくて 楽しくて -この気持ち気付いて どうして気付いてくれないの -この気持ち気付いて どこへ辿り着くのでしょうか -狂気満ちて行くわ どうすれば止まるの -『殺してあげる』 -其ノ四肢を贄と捧げ 我が足元の死屍となれ (ずっと私の傍に あなたといきたいの…)好了,完美结束!!! -[0.05,0.67,"1-0",3,"「那双眼眸映出的是谁?」",0,0,0.05,0.67,3000,0,1,"SimSun",true] -[0.05,0.67,"1-0",5,"「那双眼眸映出的是谁?」",0,0,0.05,0.67,3000,0,1,"SimSun",true] -[0.05,0.67,"1-0",5,"「心已经被弄坏了吗?」",0,0,0.05,0.67,3000,0,1,"SimSun",true] -[0.05,0.67,"1-0",5,"「受的伤害无法痊愈吗?」",0,0,0.05,0.67,3000,0,1,"SimSun",true] -[0.05,0.67,"1-0",5,"「这样做能刻画下时间吗?」",0,0,0.05,0.67,3000,0,1,"SimSun",true] -[0.05,0.67,"1-0",5,"「弄坏后就不能恢复吗?」",0,0,0.05,0.67,3000,0,1,"SimSun",true] -[0,0,"1-0",4.5,"●",0,0,0,0,500,0,0,"SimHei",true,"M585,267L585,267"] -[0,0,"1-0",4.5,"●",0,0,0,0,500,0,0,"SimHei",true,"M587,261L587,261"] -[0,0,"1-0",4.5,"●",0,0,0,0,500,0,0,"SimHei",true,"M617,283L617,283"] -[0,0,"1-0",4.5,"●",0,0,0,0,500,0,0,"SimHei",true,"M651,268L651,268"] -[0,0,"1-0",4.5,"●",0,0,0,0,500,0,0,"SimHei",true,"M583,248L583,248"] -[0,0,"1-0",4.5,"●",0,0,0,0,500,0,0,"SimHei",true,"M611,364L611,364"][0,0,"1-0",4.5,".",0,0,0,0,500,0,0,"SimHei",true,"M585,272L585,272"] -[0,0,"1-0",4.5,".",0,0,0,0,500,0,0,"SimHei",true,"M568,252L568,252"] -[0,0,"1-0",4.5,"●",0,0,0,0,500,0,0,"SimHei",true,"M684,315L684,315"] -[0,0,"1-0",4.5,"●",0,0,0,0,500,0,0,"SimHei",true,"M662,409L662,409"] -[0,0,"1-0",4.5,"●",0,0,0,0,500,0,0,"SimHei",true,"M625,413L625,413"] -[0,0,"1-0",4.5,"●",0,0,0,0,500,0,0,"SimHei",true,"M610,422L610,422"][0,0,"1-0",4.5,".",0,0,0,0,500,0,0,"SimHei",true,"M682,359L682,359"] -[0,0,"1-0",4.5,".",0,0,0,0,500,0,0,"SimHei",true,"M702,400L702,400"] -[169,387,"1-0",1,"缲り返し",0,0,169,387,500,0,1,"SimHei",true] -[172,386,"1-0",1,"缲り返し",0,0,257,386,100,0,1,"SimHei",true] -[172,386,"1-0",0.9,"缲り返し",0,0,257,386,600,0,1,"SimHei",true] -[172,386,"1-0",1,"缲り返し",0,0,257,386,700,0,1,"SimHei",true] -[172,386,"1-0",1,"缲り返し",0,0,257,386,800,0,1,"SimHei",true] -[172,386,"1-0",1,"缲り返し",0,0,257,386,900,0,1,"SimHei",true] -[172,386,"1-0",1,"缲り返し",0,0,257,386,1000,0,1,"SimHei",true][172,386,"1-0",1,"缲り返し",0,0,257,386,200,0,1,"SimHei",true] -[172,386,"1-0",1,"缲り返し",0,0,257,386,300,0,1,"SimHei",true] -[172,386,"1-0",1,"缲り返し",0,0,257,386,400,0,1,"SimHei",true] -[172,386,"1-0",1,"缲り返し",0,0,257,386,500,0,1,"SimHei",true] -[427,169,"1-0",0.5,"缲り返し",0,0,427,169,1000,0,1,"SimHei",true] -[427,169,"1-0",0.5,"缲り返し",0,0,427,184,100,0,1,"SimHei",true] -[427,169,"1-0",0.5,"缲り返し",0,0,427,184,200,0,1,"SimHei",true] -[427,169,"1-0",0.5,"缲り返し",0,0,427,184,300,0,1,"SimHei",true] -[427,169,"1-0",0.5,"缲り返し",0,0,427,184,400,0,1,"SimHei",true] -[427,169,"1-0",0.5,"缲り返し",0,0,427,184,500,0,1,"SimHei",true] -[170,255,"1-0",0.5,"缲り返し",0,0,170,255,500,0,1,"SimHei",true][170,255,"1-0",0.5,"缲り返し",0,0,170,245,100,0,1,"SimHei",true] -[170,255,"1-0",0.5,"缲り返し",0,0,170,245,200,0,1,"SimHei",true] -[170,255,"1-0",0.5,"缲り返し",0,0,170,245,300,0,1,"SimHei",true] -[170,255,"1-0",0.6,"缲り返し",0,0,170,245,400,0,1,"SimHei",true] -[170,255,"1-0",0.5,"缲り返し",0,0,170,245,500,0,1,"SimHei",true] -[472,73,"1-0",0.5,"缲り返し",0,0,472,84,100,0,1,"SimHei",true] -[472,73,"1-0",0.5,"缲り返し",0,0,472,84,200,0,1,"SimHei",true] -[472,73,"1-0",0.5,"缲り返し",0,0,472,84,300,0,1,"SimHei",true] -[472,84,"1-0",0.5,"缲り返し",0,0,472,84,500,0,1,"SimHei",true] -[472,73,"1-0",0.5,"缲り返し",0,0,472,84,400,0,1,"SimHei",true][472,73,"1-0",0.5,"缲り返し",0,0,472,84,500,0,1,"SimHei",true] -[472,92,"1-0",0.5,"缲り返し",0,0,472,84,500,0,1,"SimHei",true] -[472,92,"1-0",0.5,"缲り返し",0,0,472,84,400,0,1,"SimHei",true] -[472,92,"1-0",0.5,"缲り返し",0,0,472,84,300,0,1,"SimHei",true] -[472,92,"1-0",0.5,"缲り返し",0,0,472,84,200,0,1,"SimHei",true] -[472,92,"1-0",0.4,"缲り返し",0,0,472,84,100,0,1,"SimHei",true] -[195,212,"0-1",0.7,"缲り返し",0,0,215,213,200,0,1,"SimHei",true] -[195,212,"0-1",0.7,"缲り返し",0,0,215,213,300,0,1,"SimHei",true] -[195,212,"0-1",0.7,"缲り返し",0,0,215,213,400,0,1,"SimHei",true] -[195,212,"0-1",0.7,"缲り返し",0,0,215,213,500,0,1,"SimHei",true][195,212,"0-1",0.7,"缲り返し",0,0,215,213,600,0,1,"SimHei",true] -[215,213,"1-0",0.7,"缲り返し",0,0,215,213,100,0,1,"SimHei",true] -[235,214,"0-1",0.7,"缲り返し",0,0,215,213,600,0,1,"SimHei",true] -[235,214,"0-1",0.7,"缲り返し",0,0,215,213,500,0,1,"SimHei",true] -[235,214,"0-1",0.7,"缲り返し",0,0,215,213,400,0,1,"SimHei",true] -[235,214,"0-1",0.6,"缲り返し",0,0,215,213,300,0,1,"SimHei",true] -[235,214,"0-1",0.7,"缲り返し",0,0,215,213,200,0,1,"SimHei",true] -[235,214,"0-1",0.7,"缲り返し",0,0,215,213,700,0,1,"SimHei",true] -[195,212,"0-1",0.7,"缲り返し",0,0,215,213,700,0,1,"SimHei",true][213,216,"1-0",0.7,"缲\nり\n返\nし",0,0,213,216,200,0,1,"SimHei",true] -[200,215,"0.5-0",0.6,"缲\nり\n返\nし",0,0,213,216,200,0,1,"SimHei",true] -[200,215,"0.5-0",0.6,"缲\nり\n返\nし",0,0,213,216,300,0,1,"SimHei",true] -[200,215,"0.5-0",0.6,"缲\nり\n返\nし",0,0,213,216,400,0,1,"SimHei",true] -[229,214,"0.5-0",0.6,"缲\nり\n返\nし",0,0,213,216,400,0,1,"SimHei",true] -[229,214,"0.5-0",0.6,"缲\nり\n返\nし",0,0,213,216,300,0,1,"SimHei",true][229,214,"0.5-0",0.6,"缲\nり\n返\nし",0,0,213,216,200,0,1,"SimHei",true] -[473,213,"1-0",0.8,"缲\nり\n返\nし",0,0,473,213,200,0,1,"SimHei",true] -[473,204,"0.5-0",0.8,"缲\nり\n返\nし",0,0,473,213,500,0,1,"SimHei",true] -[473,204,"0.5-0",0.8,"缲\nり\n返\nし",0,0,473,213,600,0,1,"SimHei",true] -[473,204,"0.5-0",0.8,"缲\nり\n返\nし",0,0,473,213,700,0,1,"SimHei",true] -[472,224,"0.5-0",0.8,"缲\nり\n返\nし",0,0,473,213,600,0,1,"SimHei",true][472,224,"0.5-0",0.8,"缲\nり\n返\nし",0,0,473,213,700,0,1,"SimHei",true] -[472,224,"0.5-0",0.8,"缲\nり\n返\nし",0,0,473,213,500,0,1,"SimHei",true] -[430,342,"1-0",0.5,"歌う",0,0,430,342,500,0,1,"SimHei",true] -[430,342,"1-0",0.5,"歌う",0,0,430,342,500,0,1,"SimHei",true] -[476,209,"1-0",0.1,"缲\nり\n返\nし",0,0,476,209,500,0,1,"SimHei",true] -[474,217,"1-0",0.1,"缲\nり\n返\nし",0,0,474,217,500,0,1,"SimHei",true] -[470,214,"1-0",0.1,"缲\nり\n返\nし",0,0,470,214,500,0,1,"SimHei",true][425,338,"1-0",0.1,"歌う",0,0,425,338,500,0,1,"SimHei",true] -[425,333,"1-0",0.1,"歌う",0,0,425,333,500,0,1,"SimHei",true] -[431,332,"1-0",0.1,"歌う",0,0,431,332,500,0,1,"SimHei",true] -[299,361,"0-1",0.5,"缲\nり\n返\nし",0,0,299,361,500,0,1,"SimHei",true] -[301,367,"0-1",0.5,"缲\nり\n返\nし",0,0,299,361,500,0,1,"SimHei",true] -[301,359,"0-1",0.7,"缲\nり\n返\nし",0,0,301,359,500,0,1,"SimHei",true] -[300,357,"0-1",0.5,"缲\nり\n返\nし",0,0,299,361,500,0,1,"SimHei",true][393,173,"1-1",3,"甘\nく\nて",0,0,393,173,500,0,1,"SimHei",true] -[185,168,"1-1",3,"掴む左手が",340,0,185,168,500,0,1,"SimHei",true] -[83,336,"1-0",1,"甘くて",350,0,83,336,500,0,0,"SimHei",true] -[83,336,"1-0",1,"甘くて",350,0,83,336,500,0,0,"SimHei",true] -[546,93,"1-0",1,"甘くて",20,0,546,93,500,0,0,"SimHei",true] -[546,93,"1-0",1,"甘くて",20,0,546,93,500,0,0,"SimHei",true] -[343,299,"1-1",2,"笑う口が裂けても",0,0,343,299,500,0,1,"SimHei",true] -[462,332,"1-1",2,"⌒",180,0,462,332,500,0,1,"SimHei",true] -[528,298,"1-1",2,"▏",0,0,528,298,500,0,0,"SimHei",true] -[515,298,"1-1",2,"▏",0,0,515,298,500,0,0,"SimHei",true] -[388,342,"1-1",1,"それがあなたを し",0,0,388,342,500,0,0,"SimHei",true] -[388,342,"1-1",1," █",0,0,388,342,500,0,0,"SimHei",true][87,85,"1-1",1,"楽しくて",20,0,87,85,500,0,1,"SimHei",true] -[171,171,"1-1",1,"楽しくて",20,0,171,171,500,0,1,"SimHei",true] -[388,342,"1-1",1," 杀",0,0,388,342,500,0,0,"SimHei",true] -[174,341,"1-1",1,"脳髄を焼",340,0,174,341,500,0,1,"SimHei",true] -[300,297,"1-1",1,"くように",0,0,300,297,500,0,1,"SimHei",true] -[451,381,"1-1",0.5,"楽しくて ",0,0,451,381,500,0,1,"SimHei",true] -[513,106,"1-1",1,"楽しくて ",40,0,513,106,500,0,1,"SimHei",true] -[118,163,"1-0",0.5,"震えて",10,0,118,163,500,0,1,"SimHei",true] -[118,163,"1-0",0.5,"震えて",10,0,118,163,500,0,1,"SimHei",true] -[118,163,"1-0",0.5,"震えて",10,0,118,163,500,0,1,"SimHei",true][118,163,"1-0",0.5,"震えて",10,0,118,163,500,0,1,"SimHei",true] -[118,163,"1-0",0.5,"震えて",10,0,118,163,500,0,1,"SimHei",true] -[118,163,"1-0",0.5,"震えて",10,0,118,163,500,0,1,"SimHei",true] -[118,163,"1-0",0.5,"震えて",10,0,118,163,500,0,1,"SimHei",true] -[118,163,"1-0",0.6,"震えて",10,0,118,163,500,0,1,"SimHei",true] -[118,163,"1-0",0.6,"震えて",10,0,118,163,500,0,1,"SimHei",true] -[513,257,"1-0",1,"楽しくて",350,0,513,257,500,0,1,"SimHei",true] -[67,75,"1-0",0.5,"楽しくて",350,0,67,75,500,0,1,"SimHei",true] -[429,279,"1-0",0.5,"楽しくて",20,0,429,279,500,0,1,"SimHei",true] -[353,411,"1-0",0.5,"楽しくて",70,0,353,411,500,0,1,"SimHei",true][636,134,"1-0",0.5,"楽しくて",40,0,636,134,500,0,1,"SimHei",true] -[76,450,"1-0",0.5,"楽しくて",320,0,76,450,500,0,1,"SimHei",true] -[180,186,"0.5-0",0.5,"楽しくて",0,0,180,186,500,0,1,"SimHei",true] -[566,457,"1-0",0.5,"楽しくて",320,0,566,457,500,0,1,"SimHei",true] -[177,226,"1-1",2,"赤い 赤い \n 赤い 赤い",350,0,177,226,500,0,1,"SimHei",true] -[442,238,"1-1",2,"赤い \n 赤い ",20,0,442,238,500,0,1,"SimHei",true] -[288,302,"1-1",2,"赤い 赤い \n \n 赤い ",355,0,288,302,500,0,1,"SimHei",true] -[484,168,"1-1",2,"赤い 赤い \n 赤い \n 赤い ",20,0,484,168,500,0,1,"SimHei",true] -[183,325,"1-1",2,"赤い 赤い \n 赤い ",0,0,183,325,500,0,1,"SimHei",true][485,382,"1-1",2,"赤い 赤い \n \n \n \n",340,0,485,382,500,0,1,"SimHei",true] -[691,247,"1-1",2,"赤い ",340,0,691,247,500,0,1,"SimHei",true] -[365,258,"1-1",2,"赤い ",20,0,365,258,500,0,1,"SimHei",true] -[225,273,"1-1",2,"赤い ",20,0,225,273,500,0,1,"SimHei",true] -[280,339,"1-1",2,"赤い ",10,0,280,339,500,0,1,"SimHei",true] -[373,315,"1-1",2,"赤い ",10,0,373,315,500,0,1,"SimHei",true] -[658,275,"1-1",2,"赤い ",350,0,658,275,500,0,1,"SimHei",true][511,245,"1-1",2,"赤い ",350,0,511,245,500,0,1,"SimHei",true] -[588,284,"1-1",2,"赤い ",10,0,588,284,500,0,1,"SimHei",true] -[215,212,"1-1",2,"赤い\n赤い\n赤い \n赤い",0,0,215,212,500,0,1,"SimHei",true] -[280,188,"1-1",2,"赤い\n赤い\n赤い \n赤い",0,0,280,188,500,0,1,"SimHei",true] -[173,232,"1-1",2,"赤い\n赤い\n赤い \n赤い",0,0,173,232,500,0,1,"SimHei",true] -[428,88,"1-1",2,"赤い\n赤い\n赤い \n赤い",0,0,428,88,500,0,1,"SimHei",true] -[551,110,"1-1",2,"赤い\n赤い\n赤い \n赤い",0,0,551,110,500,0,1,"SimHei",true] -[343,170,"1-1",2,"赤い\n赤い\n赤い \n赤い",0,0,343,170,500,0,1,"SimHei",true] -[807,181,"1-1",2,"赤い\n赤い\n赤い \n赤い",0,0,807,181,500,0,1,"SimHei",true] -[644,140,"1-1",2,"赤い\n赤い\n赤い \n赤い",0,0,644,140,500,0,1,"SimHei",true] -[731,163,"1-1",2,"赤い\n赤い\n赤い \n赤い",0,0,731,163,500,0,1,"SimHei",true][0,4,"1-1",3,"甘い \n甘い \n甘い\n甘い\n甘い\n甘い \n甘い\n甘い \n甘い\n甘い\n甘い\n甘い \n甘い",0,0,0,4,500,0,1,"SimHei",true] -[93,2,"1-1",3,"甘い \n甘い \n甘い\n甘い\n甘い\n甘い \n甘い \n甘い\n甘い\n甘い\n",0,0,93,2,500,0,1,"SimHei",true] -[170,0,"1-1",3,"甘い \n甘い \n甘い\n甘い\n甘い\n甘い\n甘い\n甘い\n\n",0,0,170,0,500,0,1,"SimHei",true] -[291,0,"1-1",3,"甘い \n甘い \n甘い\n甘い\n甘い\n甘い \n甘い\n甘い \n甘い\n甘い\n甘い\n甘い \n甘い\n甘い\n甘い\n甘い \n甘い",0,0,291,0,500,0,1,"SimHei",true] -[0.95,0,"1-1",3,"甘い \n甘い \n甘い\n甘い\n甘い\n甘い \n甘い\n甘い \n甘い\n甘い\n甘い\n甘い \n甘い",0,0,0.95,0,500,0,1,"SimHei",true] -[0.85,0,"1-1",3,"甘い \n甘い \n甘い\n甘い\n甘い\n甘い\n甘い\n甘い\n\n",0,0,0.85,0,500,0,1,"SimHei",true] -[0.76,0,"1-1",3,"甘い \n甘い \n甘い\n甘い\n甘い\n甘い\n甘い\n甘い\n甘い \n甘い \n甘い\n甘い\n甘い\n甘い\n甘い\n甘い\n\n",0,0,0.76,0,500,0,1,"SimHei",true] -[375,0,"1-1",3,"甘い \n甘い \n甘い\n甘い\n甘い\n甘い\n甘い \n甘い\n甘い\n甘い\n甘い\n甘い\n甘い\n\n",0,0,375,0,500,0,1,"SimHei",true] -[513,2,"1-1",3,"甘い \n甘い \n甘い\n甘い\n甘い\n甘い \n甘い \n甘い\n甘い\n甘い\n",0,0,513,2,500,0,1,"SimHei",true][206,0,"1-1",2,"甘い \n甘い \n甘い\n甘い\n甘い\n甘い \n甘い \n甘い\n甘い\n甘い\n",0,0,206,0,500,0,1,"SimHei",true] -[326,0,"1-1",2,"甘い \n甘い \n甘い\n甘い\n甘い\n甘い\n甘い\n甘い\n甘い \n甘い \n甘い\n甘い\n甘い\n甘い\n甘い\n甘い\n\n",0,0,326,0,500,0,1,"SimHei",true] -[55,3,"1-1",2,"甘い \n甘い \n甘い\n甘い\n甘い\n甘い \n甘い \n甘い\n甘い\n甘い\n",0,0,55,3,500,0,1,"SimHei",true] -[495,0,"1-1",2,"甘い \n甘い \n甘い\n甘い\n甘い\n甘い \n甘い\n甘い \n甘い\n甘い\n甘い\n甘い \n甘い",0,0,495,0,500,0,1,"SimHei",true] -[600,0,"1-1",2,"甘い \n甘い \n甘い\n甘い\n甘い\n甘い \n甘い\n甘い \n甘い\n甘い\n甘い\n甘い \n甘い",0,0,600,0,500,0,1,"SimHei",true] -[526,4,"1-1",2,"甘い \n甘い \n甘い\n甘い\n甘い\n甘い \n甘い\n甘い \n甘い\n甘い\n甘い\n甘い \n甘い\n甘い \n甘い \n甘い\n甘い\n甘い\n甘い \n甘い\n甘い \n甘い\n甘い\n甘い\n甘い \n甘い",0,0,526,4,500,0,1,"SimHei",true][720,5,"1-1",2,"甘い \n甘い \n甘い\n甘い\n甘い\n甘い \n甘い\n甘い \n甘い\n甘い\n甘い\n甘い \n甘い",0,0,720,5,500,0,1,"SimHei",true] -[0,0,"1-1",3,"甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n",0,0,0,0,500,0,1,"SimHei",true] -[0,197,"1-1",3,"甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n",0,0,0,197,500,0,1,"SimHei",true] -[0,393,"1-1",3,"甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n",0,0,0,393,500,0,1,"SimHei",true] -[488,0,"1-1",3,"甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n",0,0,488,0,500,0,1,"SimHei",true] -[591,197,"1-1",3,"甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n",0,0,591,197,500,0,1,"SimHei",true] -[528,400,"1-1",3,"甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n",0,0,528,400,500,0,1,"SimHei",true] -[0,0,"1-1",3,"甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n",0,0,0,0,500,0,1,"SimHei",true][0,394,"1-1",3,"甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n",0,0,0,394,500,0,1,"SimHei",true] -[197,228,"1-1",0.5,"嬉しくなっても",0,0,197,228,500,0,1,"SimHei",true] -[0,0,"1-1",3,"赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n",0,0,0,0,500,0,1,"SimHei",true] -[0,299,"1-1",3,"赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n",0,0,0,299,500,0,1,"SimHei",true] -[570,0,"1-1",3,"赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n",0,0,570,0,500,0,1,"SimHei",true] -[572,299,"1-1",3,"赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n",0,0,572,299,500,0,1,"SimHei",true] -[648,347,"1-1",3,"赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n",0,0,648,347,500,0,1,"SimHei",true] -[0,0,"1-1",3,"赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n",0,0,0,0,500,0,1,"SimHei",true] -[0,344,"1-1",3,"赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n",0,0,0,344,500,0,1,"SimHei",true] -[645,0,"1-1",3,"赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n",0,0,645,0,500,0,1,"SimHei",true] -[1,0,"1-1",3,"赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n",0,0,1,0,500,0,1,"SimHei",true] -[0,198,"1-1",3,"赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n",0,0,0,198,500,0,1,"SimHei",true] -[386,0,"1-1",3,"赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n",0,0,386,0,500,0,1,"SimHei",true] -[386,197,"1-1",3,"赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n",0,0,386,197,500,0,1,"SimHei",true] -[384,395,"1-1",3,"赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n",0,0,384,395,500,0,1,"SimHei",true] -[0,396,"1-1",3,"赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n",0,0,0,396,500,0,1,"SimHei",true] -[127,342,"1-1",1,"あなた",0,0,127,342,500,0,1,"SimHei",true] -[265,205,"1-1",1,"甘い",0,0,265,205,500,0,1,"SimHei",true] -[0,0,"0.5-0",1.2,"甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n",0,0,0,0,500,0,1,"SimHei",true] -[0,0.87,"0.5-0",1.2,"甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n",0,0,0,0.87,500,0,1,"SimHei",true] -[0.11,0.14,"0.5-0",1.2,"甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n",0,0,0.11,0.14,500,0,1,"SimHei",true] -[0.12,0.82,"0.5-0",1.2,"甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n",0,0,0.12,0.82,500,0,1,"SimHei",true] -[0,0.47,"1-0",1.2,"甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n",0,0,0,0.47,500,0,1,"SimHei",true] -[0.09,0,"1-0",1.2,"甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n",0,0,0.09,0,500,0,1,"SimHei",true] -[0,0.7,"1-0",1.2,"甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n",0,0,0,0.7,500,0,1,"SimHei",true] -[0.63,0.34,"1-0",1.2,"甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n",0,0,0.63,0.34,500,0,1,"SimHei",true] -[0.06,0.2,"1-0",1.2,"甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n",0,0,0.06,0.2,500,0,1,"SimHei",true] -[0.42,0.41,"1-0",1.2,"甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n",0,0,0.42,0.41,500,0,1,"SimHei",true] -[0.68,0.08,"1-0",1.1,"甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n",0,0,0.68,0.08,500,0,1,"SimHei",true] -[0.7,0.82,"1-0",1.2,"甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n",0,0,0.7,0.82,500,0,1,"SimHei",true] -[0,0.08,"1-0",1.2,"甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n",0,0,0,0.08,500,0,1,"SimHei",true] -[0.54,0.58,"1-0",1.2,"甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n",0,0,0.54,0.58,500,0,1,"SimHei",true] -[0.76,0.11,"1-0",1.2,"甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n",0,0,0.76,0.11,500,0,1,"SimHei",true] -[0.62,0.18,"1-0",1.2,"甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n",0,0,0.62,0.18,500,0,1,"SimHei",true] -[0.19,0.54,"1-0",1.1,"甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n",0,0,0.19,0.54,500,0,1,"SimHei",true] -[0.89,0.01,"1-0",1.2,"甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n",0,0,0.89,0.01,500,0,1,"SimHei",true] -[0,0.87,"1-0",1.2,"甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n",0,0,0,0.87,500,0,1,"SimHei",true] -[0,0,"1-0",1.2,"甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n",0,0,0,0,500,0,1,"SimHei",true] -[0,0.3,"1-0",1.2,"甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n",0,0,0,0.3,500,0,1,"SimHei",true] -[0,0.6,"1-0",1.2,"甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n甘い 甘い 甘い 甘い 甘い\n",0,0,0,0.6,500,0,1,"SimHei",true] -[0.3,0.37,"1-0",1,"赤い",0,0,0.3,0.37,500,0,1,"SimHei",true] -[0,0,"0.5-0",1.2,"赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い",0,0,0,0,500,0,1,"SimHei",true] -[0.02,0.59,"0.5-0",1.2,"赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い",0,0,0.02,0.59,500,0,1,"SimHei",true] -[0.51,0.02,"0.5-0",1.2,"赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い",0,0,0.51,0.02,500,0,1,"SimHei",true] -[0.16,0.16,"1-0",1.2,"赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い",0,0,0.16,0.16,500,0,1,"SimHei",true] -[0.65,0.62,"1-0",1.2,"赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い",0,0,0.65,0.62,500,0,1,"SimHei",true] -[0,0.87,"0.5-0",1.2,"赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い",0,0,0,0.87,500,0,1,"SimHei",true] -[0.64,0.01,"1-0",1.1,"赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い",0,0,0.64,0.01,500,0,1,"SimHei",true] -[0.01,0.46,"1-0",1.2,"赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い",0,0,0.01,0.46,500,0,1,"SimHei",true][0.03,0.02,"1-0",1.2,"赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い",0,0,0.03,0.02,500,0,1,"SimHei",true] -[0.63,0.34,"1-0",1.2,"赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い",0,0,0.63,0.34,500,0,1,"SimHei",true] -[0.14,0.15,"0.5-0",1.2,"赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い",0,0,0.14,0.15,500,0,1,"SimHei",true] -[0.75,0.86,"0.5-0",1.2,"赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い",0,0,0.75,0.86,500,0,1,"SimHei",true] -[0.57,0.64,"1-0",1.2,"赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い",0,0,0.57,0.64,500,0,1,"SimHei",true][0.57,0.32,"1-0",1.1,"赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い",0,0,0.57,0.32,500,0,1,"SimHei",true] -[0.59,0,"1-0",1.1,"赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い\n赤い 赤い 赤い 赤い",0,0,0.59,0,500,0,1,"SimHei",true] -[167,289,"1-1",3,"●",0,0,167,289,500,0,0,"SimHei",true] -[258,337,"1-0",1,"●",0,0,258,337,500,0,0,"SimHei",true] -[195,244,"1-0",1,"●",0,0,195,244,500,0,0,"SimHei",true] -[141,262,"1-1",1,"●",0,0,141,262,500,0,0,"SimHei",true] -[298,312,"1-0",1,"●",0,0,298,312,500,0,0,"SimHei",true] -[299,328,"1-0",1,"●",0,0,299,328,500,0,0,"SimHei",true][179,226,"1-1",1,"●",0,0,179,226,500,0,0,"SimHei",true] -[272,398,"1-0",1,"●",0,0,272,398,500,0,0,"SimHei",true] -[193,239,"1-0",1,"●",0,0,193,239,500,0,0,"SimHei",true] -[268,241,"1-0",1,"●",0,0,268,241,500,0,0,"SimHei",true] -[229,257,"1-0",1,"●",0,0,229,257,500,0,0,"SimHei",true] -[0,0,"1-0","2","●",0,0,0,0,500,0,0,"SimHei",true,"M204,452L204,452"] -[0,0,"1-0","2","●",0,0,0,0,500,0,0,"SimHei",true,"M195,437L195,437"] -[0,0,"1-0","2","●",0,0,0,0,500,0,0,"SimHei",true,"M282,536L282,536"] -[0,0,"1-0","2","●",0,0,0,0,500,0,0,"SimHei",true,"M222,424L222,424"] -[0,0,"1-0","2","●",0,0,0,0,500,0,0,"SimHei",true,"M178,334L178,334"][0,0,"1-0","2","●",0,0,0,0,500,0,0,"SimHei",true,"M276,340L276,340"] -[0,0,"1-0","2","●",0,0,0,0,500,0,0,"SimHei",true,"M327,252L327,252"] -[0,0,"1-0","2","●",0,0,0,0,500,0,0,"SimHei",true,"M424,288L424,288L424,288"] -[0,0,"1-0","2","●",0,0,0,0,500,0,0,"SimHei",true,"M306,361L306,361"] -[0,0,"1-0","2","●",0,0,0,0,500,0,0,"SimHei",true,"M407,185L407,185"][0,0,"1-0","2","●",0,0,0,0,500,0,0,"SimHei",true,"M461,202L461,202"] -[0,0,"1-0","2","●",0,0,0,0,500,0,0,"SimHei",true,"M591,301L591,301"] -[0,0,"1-0","2","●",0,0,0,0,500,0,0,"SimHei",true,"M546,230L546,230"] -[0,0,"1-0","2","●",0,0,0,0,500,0,0,"SimHei",true,"M394,311L394,311"] -[0,0,"1-0","2",".",0,0,0,0,500,0,0,"SimHei",true,"M394,311L394,311"] -[0,0,"1-0","2",".",0,0,0,0,500,0,0,"SimHei",true,"M522,202L522,202L522,202"][0,0,"1-0","2",".",0,0,0,0,500,0,0,"SimHei",true,"M334,296L334,296"] -[0,0,"0.5-0","2","●",0,0,0,0,500,0,0,"SimHei",true,"M429,346L429,346"] -[0,0,"0.5-0","2","●",0,0,0,0,500,0,0,"SimHei",true,"M544,290L544,290"] -[0,0,"0.5-0","2","●",0,0,0,0,500,0,0,"SimHei",true,"M277,276L277,276"] -[0,0,"0.5-0","2",".",0,0,0,0,500,0,0,"SimHei",true,"M490,322L490,322L490,322"] -[0,0,"0.5-0","2",".",0,0,0,0,500,0,0,"SimHei",true,"M218,258L218,258"][0,0.83,"1-0",5,"我之中的我",0,0,0,0.83,500,0,1,"SimHei",true] -[0,0.83,"1-0",2,"重复着一个单语",0,0,0,0.83,500,0,1,"SimHei",true] -[0,0.83,"1-0",2,"多少遍",0,0,0,0.83,500,0,1,"SimHei",true] -[0,0.83,"1-0",2,"不断重复",0,0,0,0.83,500,0,1,"SimHei",true] -[0,0.83,"1-0",3,"重复着",0,0,0,0.83,500,0,1,"SimHei",true] -[0,0.83,"1-0",3,"歌唱",0,0,0,0.83,500,0,1,"SimHei",true] -[0,0.83,"1-0",3,"紧握的左手异常甜蜜",0,0,0,0.83,500,0,1,"SimHei",true] -[0,0.83,"1-0",3,"挥舞的右手无比甘甜",0,0,0,0.83,500,0,1,"SimHei",true] -[0,0.83,"1-0",2,"笑着的嘴巴已然开裂",0,0,0,0.83,500,0,1,"SimHei",true] -[0,0.83,"1-0",2,"随后便给予你杀戮",0,0,0,0.83,500,0,1,"SimHei",true] -[0,0.83,"1-0",2,"好甜",0,0,0,0.83,500,0,1,"SimHei",true][0,0.83,"1-0",2,"要刻入脑髓",0,0,0,0.83,500,0,1,"SimHei",true] -[0,0.83,"1-0",2,"快乐到颤抖 ",0,0,0,0.83,500,0,1,"SimHei",true] -[0,0.83,"1-0",2,"快乐得",0,0,0,0.83,500,0,1,"SimHei",true] -[0,0.83,"1-0",2.1,"好快乐",0,0,0,0.83,500,0,1,"SimHei",true] -[0,0.83,"1-0",2.1,"快乐到颤抖 ",0,0,0,0.83,500,0,1,"SimHei",true] -[0,0.83,"1-0",3,"你…好甜…好..甜.....",0,0,0,0.83,500,0,1,"SimHei",true] -[0,0.83,"1-0",3,"好红…好..红.....",0,0,0,0.83,500,0,1,"SimHei",true] -[0,0.83,"1-0",3,"「让我杀了你!」",0,0,0,0.83,500,0,1,"SimHei",true] -[434,96,"1-1",1.2,"が手右るえ震\n",20,0,434,96,500,0,1,"SimHei",true]赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い -赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い 赤い -[574,0,"1-1",4,"赤い赤い赤い赤い赤い赤い\n赤い赤い赤い赤い赤い赤い\n赤い赤い赤い赤い赤い赤い\n赤い赤い赤い赤い赤い赤い\n赤い赤い赤い赤い赤い赤い\n",0,0,574,0,500,0,1,"SimHei",true] -[571,249,"1-1",4,"赤い赤い赤い赤い赤い赤い\n赤い赤い赤い赤い赤い赤い\n赤い赤い赤い赤い赤い赤い\n赤い赤い赤い赤い赤い赤い\n赤い赤い赤い赤い赤い赤い\n",0,0,571,249,500,0,1,"SimHei",true] -[568,496,"1-1",4,"赤い赤い赤い赤い赤い赤い\n赤い赤い赤い赤い赤い赤い\n赤い赤い赤い赤い赤い赤い\n赤い赤い赤い赤い赤い赤い\n赤い赤い赤い赤い赤い赤い\n",0,0,568,496,500,0,1,"SimHei",true] -科普一下:这个视频是关于东方project的同人作品《绯色月下、狂咲ノ绝》 -是日本同人游戏社团上海爱丽丝幻乐团所制作的一系列同人游戏、相关作品 -东方project 不是番 -[0.25,0.11,"1-1",7,"███████/n█ █/n█ █/n█ █/n█ █/n█ █/n███████",0,0,0.25,0.11,500,0,false,"黑体",1] -[0.25,0.11,"1-1",6,"/n █████/n █ █/n █ █/n █ █/n █████/n",0,0,0.25,0.11,500,0,false,"黑体",1] -[0.25,0.11,"1-1",5,"/n/n ███/n █ █/n ███",0,0,0.25,0.11,500,0,false,"黑体",1] -[0.25,0.11,"1-1",4,"/n/n/n █",0,0,0.25,0.11,500,0,false,"黑体",1] -[0.25,0.11,"1-1",4,"/n/n/n 让",0,0,0.25,0.11,500,0,false,"黑体",1] -[0.25,0.11,"1-1",2,"/n/n/n 我",0,0,0.25,0.11,500,0,false,"黑体",1][0.25,0.11,"1-1",2,"/n/n/n 了",0,0,0.25,0.11,500,0,false,"黑体",1] -[0.25,0.11,"1-1",2,"/n/n/n 你",0,0,0.25,0.11,500,0,false,"黑体",1] -[0.25,0.11,"1-0",0.5,"/n ╱/n ╱/n ╱/n ╱/n ╱",0,0,0.25,0.11,500,0,false,"黑体",1] -[0.25,0.11,"1-0",0.5,"/n ╱/n ╱/n ╱/n ╱/n ╱",0,0,0.25,0.11,500,0,false,"黑体",1] -[0.25,0.11,"1-1",4,"███████/n███████/n███████/n███████/n███████/n███████/n███████",0,0,0.25,0.11,500,0,false,"黑体",1] -[0.25,0.11,"1-1",4,"/n/n ◢",0,0,0.25,0.11,500,0,false,"黑体",1][0.25,0.11,"1-1",4,"/n/n 让",0,0,0.25,0.11,500,0,false,"黑体",1] -[0.25,0.11,"1-1",4,"/n/n/n/n 杀",0,0,0.25,0.11,500,0,false,"黑体",1] -[0.25,0.11,"1-1",4,"/n/n/n/n ◤",0,0,0.25,0.11,500,0,false,"黑体",1] -[0.25,0.11,"1-1",4,"/n/n 我",0,0,0.25,0.11,500,0,false,"黑体",1] -[0.25,0.11,"1-1",4,"/n/n 杀",0,0,0.25,0.11,500,0,false,"黑体",1] -[0.25,0.11,"1-1",4,"/n/n/n/n 了",0,0,0.25,0.11,500,0,false,"黑体",1] -[0.25,0.11,"1-1",4,"/n/n/n/n 你",0,0,0.25,0.11,500,0,false,"黑体",1] -[0,0,"1-1",6,"其/nノ/n生/nを/n引/nき/n裂/nか/nれ/nて/n/n赤/n银/nを/n吐/nき/n消/nし/n飞/nべ",0,0,0.99,0,10000,0,false,"楷体",1] -[0,0,"1-1",6,"其/nノ/n生/nを/n引/nき/n裂/nか/nれ/nて/n/n赤/n银/nを/n吐/nき/n消/nし/n飞/nべ",0,0,0.99,0,9100,0,false,"楷体",1][0,0,"1-1",6,"其/nノ/n生/nを/n引/nき/n裂/nか/nれ/nて/n/n赤/n银/nを/n吐/nき/n消/nし/n飞/nべ",0,0,0.99,0,7600,0,false,"楷体",1] -[0,0,"1-1",6,"其/nノ/n生/nを/n引/nき/n裂/nか/nれ/nて/n/n赤/n银/nを/n吐/nき/n消/nし/n飞/nべ",0,0,0.99,0,7000,0,false,"楷体",1] -[0,0,"1-1",6,"其/nノ/n生/nを/n引/nき/n裂/nか/nれ/nて/n/n赤/n银/nを/n吐/nき/n消/nし/n飞/nべ",0,0,0.99,0,6500,0,false,"楷体",1] -[0,0,"1-1",6,"其/nノ/n生/nを/n引/nき/n裂/nか/nれ/nて/n/n赤/n银/nを/n吐/nき/n消/nし/n飞/nべ",0,0,0.99,0,8300,0,false,"楷体",1] -[0.99,0,"1-1",6,"其/nノ/n生/nを/n引/nき/n裂/nか/nれ/nて/n/n赤/n银/nを/n吐/nき/n消/nし/n飞/nべ",0,0,0,0,10000,0,false,"楷体",1] -[0.99,0,"1-1",6,"其/nノ/n生/nを/n引/nき/n裂/nか/nれ/nて/n/n赤/n银/nを/n吐/nき/n消/nし/n飞/nべ",0,0,0,0,9100,0,false,"楷体",1] -[0.99,0,"1-1",6,"其/nノ/n生/nを/n引/nき/n裂/nか/nれ/nて/n/n赤/n银/nを/n吐/nき/n消/nし/n飞/nべ",0,0,0,0,7600,0,false,"楷体",1] -[0.99,0,"1-1",6,"其/nノ/n生/nを/n引/nき/n裂/nか/nれ/nて/n/n赤/n银/nを/n吐/nき/n消/nし/n飞/nべ",0,0,0,0,7000,0,false,"楷体",1] -[0.99,0,"1-1",6,"其/nノ/n生/nを/n引/nき/n裂/nか/nれ/nて/n/n赤/n银/nを/n吐/nき/n消/nし/n飞/nべ",0,0,0,0,6500,0,false,"楷体",1] -[0.99,0,"1-1",6,"其/nノ/n生/nを/n引/nき/n裂/nか/nれ/nて/n/n赤/n银/nを/n吐/nき/n消/nし/n飞/nべ",0,0,0,0,8300,0,false,"楷体",1] -[0.99,0,"1-1",6,"其/nノ/n生/nを/n引/nき/n裂/nか/nれ/nて/n/n赤/n银/nを/n吐/nき/n消/nし/n飞/nべ",0,0,0,0,6100,0,false,"楷体",1] -[0.5,0.5,"1-1",2," 杀してあげる",0,0,0.5,0.5,500,0,1,"SimSun",true] -[0.5,0.5,"1-1",2," 杀してあげる",10,0,0.5,0.5,500,0,1,"SimSun",true] -[0.5,0.5,"1-1",2," 杀してあげる",20,0,0.5,0.5,500,0,1,"SimSun",true] -[0.5,0.5,"1-1",2," 杀してあげる",30,0,0.5,0.5,500,0,1,"SimSun",true] -[0.5,0.5,"1-1",2," 杀してあげる",40,0,0.5,0.5,500,0,1,"SimSun",true] -[0.5,0.5,"1-1",2," 杀してあげる",50,0,0.5,0.5,500,0,1,"SimSun",true] -[0.5,0.5,"1-1",2," 杀してあげる",60,0,0.5,0.5,500,0,1,"SimSun",true][0.5,0.5,"1-1",2," 杀してあげる",70,0,0.5,0.5,500,0,1,"SimSun",true] -[0.5,0.5,"1-1",2," 杀してあげる",120,0,0.5,0.5,600,0,1,"SimSun",true] -[0.5,0.5,"1-1",2," 杀してあげる",80,0,0.5,0.5,500,0,1,"SimSun",true] -[0.5,0.5,"1-1",2," 杀してあげる",130,0,0.5,0.5,600,0,1,"SimSun",true] -[0.5,0.5,"1-1",2," 杀してあげる",140,0,0.5,0.5,600,0,1,"SimSun",true] -[0.5,0.5,"1-1",2," 杀してあげる",150,0,0.5,0.5,600,0,1,"SimSun",true][0.5,0.5,"1-1",2," 杀してあげる",160,0,0.5,0.5,600,0,1,"SimSun",true] -[0.5,0.5,"1-1",2," 杀してあげる",170,0,0.5,0.5,600,0,1,"SimSun",true] -[0.5,0.5,"1-1",2," 杀してあげる",90,0,0.5,0.5,600,0,1,"SimSun",true] -[0.5,0.5,"1-1",2," 杀してあげる",100,0,0.5,0.5,600,0,1,"SimSun",true] -[0.5,0.5,"1-1",2," 杀してあげる",110,0,0.5,0.5,600,0,1,"SimSun",true] -[0.5,0.5,"1-1",2," 杀してあげる",220,0,0.5,0.5,900,0,1,"SimSun",true][0.5,0.5,"1-1",2," 杀してあげる",180,0,0.5,0.5,700,0,1,"SimSun",true] -[0.5,0.5,"1-1",2," 杀してあげる",230,0,0.5,0.5,900,0,1,"SimSun",true] -[0.5,0.5,"1-1",2," 杀してあげる",240,0,0.5,0.5,900,0,1,"SimSun",true] -[0.5,0.5,"1-1",2," 杀してあげる",250,0,0.5,0.5,900,0,1,"SimSun",true] -[0.5,0.5,"1-1",2," 杀してあげる",260,0,0.5,0.5,900,0,1,"SimSun",true] -[0.5,0.5,"1-1",2," 杀してあげる",270,0,0.5,0.5,900,0,1,"SimSun",true][0.5,0.5,"1-1",2," 杀してあげる",280,0,0.5,0.5,900,0,1,"SimSun",true] -[0.5,0.5,"1-1",2," 杀してあげる",290,0,0.5,0.5,900,0,1,"SimSun",true] -[0.5,0.5,"1-1",2," 杀してあげる",300,0,0.5,0.5,900,0,1,"SimSun",true] -[0.5,0.5,"1-1",2," 杀してあげる",190,0,0.5,0.5,800,0,1,"SimSun",true] -[0.5,0.5,"1-1",2," 杀してあげる",310,0,0.5,0.5,900,0,1,"SimSun",true] -[0.5,0.5,"1-1",2," 杀してあげる",200,0,0.5,0.5,800,0,1,"SimSun",true][0.5,0.5,"1-1",2," 杀してあげる",210,0,0.5,0.5,800,0,1,"SimSun",true] -[0.5,0.5,"1-1",2," 杀してあげる",320,0,0.5,0.5,900,0,1,"SimSun",true] -[0.5,0.5,"1-1",2," 杀してあげる",330,0,0.5,0.5,900,0,1,"SimSun",true] -[0.5,0.5,"1-1",2," 杀してあげる",340,0,0.5,0.5,1100,0,1,"SimSun",true] -[0.5,0.5,"1-1",2," 杀してあげる",350,0,0.5,0.5,1100,0,1,"SimSun",true] -[256,41,"1-1",2,"深",0,0,256,41,1100,0,1,"FangSong",true] -[254,42,"1-1",2," く",0,0,254,42,1100,0,1,"FangSong",true][254,42,"1-1",2," 朱",0,0,254,42,1100,0,1,"FangSong",true] -[254,42,"1-1",2," 润",0,0,254,42,1100,0,1,"FangSong",true] -[254,42,"1-1",2," む",0,0,254,42,1100,0,1,"FangSong",true] -[254,42,"1-1",2," き",0,0,254,42,1100,0,1,"FangSong",true] -[254,42,"1-1",2," 瞳",0,0,254,42,1100,0,1,"FangSong",true] -[131,213,"1-1",3," 甘",0,0,131,213,500,0,1,"FangSong",true] -[190,232,"1-1",3," い",0,0,190,232,500,0,1,"FangSong",true] -[264,250,"1-1",3," 色",0,0,264,250,500,0,1,"FangSong",true] -[404,258,"1-1",3,"ス",355,0,404,258,500,0,1,"FangSong",true][463,239,"1-1",3,"カ",350,0,463,239,500,0,1,"FangSong",true] -[500,209,"1-1",3,"ー",345,0,500,209,500,0,1,"FangSong",true] -[334,259,"1-1",3," の",0,0,334,259,500,0,1,"FangSong",true] -[525,168,"1-1",3,"ト",10,0,525,168,500,0,1,"FangSong",true] -[532,119,"1-1",3,"揺",0,0,532,119,500,0,1,"FangSong",true] -[490,92,"1-1",3,"ら",0,0,490,92,500,0,1,"FangSong",true] -[443,83,"1-1",3,"す",0,0,443,83,500,0,1,"FangSong",true] -[354,406,"1-1",3.2,"幼き頬 朱を渗ませ",350,0,354,406,500,0,1,"FangSong",true] -[357,424,"1-1",3.2,"幼き頬 朱を渗ませ",10,0,357,424,500,0,1,"FangSong",true] -[128,214,"1-1",3.5,"疼",350,0,128,214,500,0,1,"FangSong",true] -[162,200,"1-1",3.5,"き",355,0,162,200,500,0,1,"FangSong",true][199,197,"1-1",3.5,"に",0,0,199,197,500,0,1,"FangSong",true] -[239,208,"1-1",3.5,"足",5,0,239,208,500,0,1,"FangSong",true] -[302,230,"1-1",3.5,"崩",20,0,302,230,500,0,1,"FangSong",true] -[273,219,"1-1",3.5,"を",10,0,273,219,500,0,1,"FangSong",true] -[331,243,"1-1",3.5,"さ",25,0,331,243,500,0,1,"FangSong",true] -[356,261,"1-1",3.5,"れ",30,0,356,261,500,0,1,"FangSong",true] -[380,281,"1-1",3.5,"る",35,0,380,281,500,0,1,"FangSong",true] -[122,81,"0-1",1,"甘\nい\n幻\n视\nに\n支\n配\nさ\nれ",0,0,122,81,500,0,1,"FangSong",true] -[122,81,"0-1",2,"甘\nい\n幻\n视\nに\n支\n配\nさ\nれ",0,0,122,81,500,0,1,"FangSong",true] -[170,169,"1-0",3,"奏\nで\nて\nは\n咲\nく\n五\n指\nの\n调\nべ",0,0,170,169,500,0,1,"FangSong",true] -[122,81,"1-1",2,"甘\nい\n幻\n视\nに\n支\n配\nさ\nれ",0,0,122,81,500,0,1,"FangSong",true] -[472,213,"1-0",0.5,"赤い红茶 滴る音",340,0,472,213,500,0,1,"FangSong",true] -[472,213,"1-0",0.5,"赤い红茶 滴る音",350,0,472,213,500,0,1,"FangSong",true] -[472,213,"1-0",0.5,"赤い红茶 滴る音",0,0,472,213,500,0,1,"FangSong",true] -[472,213,"1-0",0.5,"赤い红茶 滴る音",10,0,472,213,500,0,1,"FangSong",true] -[472,213,"1-0",0.5,"赤い红茶 滴る音",40,0,472,213,500,0,1,"FangSong",true] -[472,213,"1-0",0.5,"赤い红茶 滴る音",20,0,472,213,500,0,1,"FangSong",true][472,213,"1-0",0.5,"赤い红茶 滴る音",50,0,472,213,500,0,1,"FangSong",true] -[472,213,"1-0",0.5,"赤い红茶 滴る音",30,0,472,213,500,0,1,"FangSong",true] -[342,345,"1-1",3.5,"弄",350,0,342,345,500,0,1,"FangSong",true] -[394,335,"1-1",3.5,"ん",350,0,394,335,500,0,1,"FangSong",true] -[432,321,"1-1",3.5,"で",350,0,432,321,500,0,1,"FangSong",true] -[452,311,"1-1",3.5,"は",270,0,452,311,500,0,1,"FangSong",true] -[413,129,"1-0",0.5,"深く抉る",0,0,413,129,500,0,1,"FangSong",true] -[410,421,"1-0",0.5,"深く抉る",0,0,410,421,500,0,1,"FangSong",true] -[179,223,"1-0",0.5,"深く抉る",0,0,179,223,500,0,1,"FangSong",true] -[68,383,"1-0",0.5,"深く抉る",0,0,68,383,500,0,1,"FangSong",true] -[545,52,"1-0",0.5,"深く抉る",0,0,545,52,500,0,1,"FangSong",true] -[53,151,"1-0",0.5,"深く抉る",0,0,53,151,500,0,1,"FangSong",true] -[146,293,"1-0",0.5,"深く抉る",0,0,146,293,500,0,1,"FangSong",true] -[579,272,"1-0",0.5,"深く抉る",0,0,579,272,500,0,1,"FangSong",true] -[58,425,"1-0",3,"其ノ生を引き裂かれて",0,0,58,425,500,0,1,"SimSun",true][257,471,"1-0",3,"赤银を吐き消し飞べ",0,0,257,471,500,0,1,"SimSun",true] -[259,474,"1-0",3,"极彩に咲き我が粮",0,0,259,474,500,0,1,"SimSun",true] -[58,423,"1-0",3,"其ノ生の华散らして",0,0,58,423,500,0,1,"SimSun",true] -[15,63,"1-1",6.2,"█████████████████████\n",3,0,15,63,500,0,0,"SimHei",true] -[17,63,"1-1",6.2,"█████████████████████\n",3,0,17,63,500,0,0,"SimHei",true] -[15,63,"0.5-0.5",6.2,"甘美なその鼓动を うだち尽くして止めようか",3,0,15,63,500,0,0,"SimHei, \"Microsoft JhengHei\"",true] -[15,63,"1-1",1,"甘美",3,0,15,63,500,0,1,"SimHei",true] -[15,63,"1-1",1,"甘美な",3,0,15,63,500,0,1,"SimHei",true] -[15,63,"1-1",1,"甘美なそ",3,0,15,63,500,0,1,"SimHei",true] -[15,63,"1-1",1,"甘美なその",3,0,15,63,500,0,1,"SimHei",true] -[15,63,"1-1",1,"甘美なその鼓",3,0,15,63,500,0,1,"SimHei",true] -[15,63,"1-1",1,"甘",3,0,15,63,500,0,1,"SimHei",true] -[15,63,"1-1",1,"甘美なその鼓动",3,0,15,63,500,0,1,"SimHei",true] -[15,63,"1-1",1,"甘美なその鼓动を",3,0,15,63,500,0,1,"SimHei",true] -[15,63,"1-1",1,"甘美なその鼓动を う",3,0,15,63,500,0,1,"SimHei",true] -[15,63,"1-1",1,"甘美なその鼓动を うだ",3,0,15,63,500,0,1,"SimHei",true] -[15,63,"1-1",1,"甘美なその鼓动を うだち",3,0,15,63,500,0,1,"SimHei",true] -[15,63,"1-1",1,"甘美なその鼓动を うだち尽",3,0,15,63,500,0,1,"SimHei",true] -[15,63,"1-1",1,"甘美なその鼓动を うだち尽く",3,0,15,63,500,0,1,"SimHei",true] -[15,63,"1-1",1,"甘美なその鼓动を うだち尽くして",3,0,15,63,500,0,1,"SimHei",true] -[15,63,"1-1",0.5,"甘美なその鼓动を うだち尽くして止",3,0,15,63,500,0,1,"SimHei",true] -[15,63,"1-1",0.5,"甘美なその鼓动を うだち尽くして止め",3,0,15,63,500,0,1,"SimHei",true] -[15,63,"1-1",0.5,"甘美なその鼓动を うだち尽くして止めよ",3,0,15,63,500,0,1,"SimHei",true] -[15,63,"1-1",0.5,"甘美なその鼓动を うだち尽くして止めよう",3,0,15,63,500,0,1,"SimHei",true] -[15,63,"1-1",0.5,"甘美なその鼓动を うだち尽くして止めようか",3,0,15,63,500,0,1,"SimHei",true] -[15,63,"1-1",1,"甘美なその鼓动を うだち尽くし",3,0,15,63,500,0,1,"SimHei",true] -[15,63,"1-1",0.5,"甘美なその鼓动を うだち尽くして止めようか",3,0,15,63,600,0,1,"SimHei",true] -[15,63,"1-1",0.6,"甘美なその鼓动を うだち尽くして止めようか",3,0,15,63,600,0,1,"SimHei",true][34,299,"1-1",6.2,"██████████████████\n",0,0,34,299,600,0,1,"SimHei",true] -[34,299,"1-1",0.6,"██████████████████\n",0,0,34,299,600,0,1,"SimHei",true] -[34,299,"0.5-0.5",6.2,"狂気に満ちてゆくわどうすれば止まるの",0,0,34,299,600,0,1,"SimHei",true] -[34,299,"1-1",1,"狂気に",0,0,34,299,600,0,1,"SimHei",true] -[34,299,"1-1",1,"狂気に満",0,0,34,299,600,0,1,"SimHei",true] -[34,299,"1-1",1,"狂",0,0,34,299,600,0,1,"SimHei",true] -[34,299,"1-1",1,"狂気",0,0,34,299,600,0,1,"SimHei",true] -[34,299,"1-1",1,"狂気に満ちて",0,0,34,299,600,0,1,"SimHei",true][34,299,"1-1",1,"狂気に満ちてゆ",0,0,34,299,600,0,1,"SimHei",true] -[34,299,"1-1",1,"狂気に満ちてゆく",0,0,34,299,600,0,1,"SimHei",true] -[34,299,"1-1",1,"狂気に満ちてゆくわ",0,0,34,299,600,0,1,"SimHei",true] -[34,299,"1-1",1,"狂気に満ち",0,0,34,299,600,0,1,"SimHei",true] -[34,299,"1-1",1,"狂気に満ちてゆくわど",0,0,34,299,600,0,1,"SimHei",true] -[34,299,"1-1",1,"狂気に満ちてゆくわどう",0,0,34,299,600,0,1,"SimHei",true][34,299,"1-1",1,"狂気に満ちてゆくわどうす",0,0,34,299,600,0,1,"SimHei",true] -[34,299,"1-1",1,"狂気に満ちてゆくわどうすれ",0,0,34,299,600,0,1,"SimHei",true] -[34,299,"1-1",1,"狂気に満ちてゆくわどうすれば",0,0,34,299,600,0,1,"SimHei",true] -[34,299,"1-1",1,"狂気に満ちてゆくわどうすれば止",0,0,34,299,600,0,1,"SimHei",true] -[34,299,"1-1",1,"狂気に満ちてゆくわどうすれば止ま",0,0,34,299,600,0,1,"SimHei",true] -[34,299,"1-1",1,"狂気に満ちてゆくわどうすれば止まる",0,0,34,299,600,0,1,"SimHei",true] -[34,299,"1-1",1,"狂気に満ちてゆくわどうすれば止まるの",0,0,34,299,600,0,1,"SimHei",true] -[70,350,"1-1",3,"疼痛令双腿都不自由",0,0,70,350,500,0,1,"SimHei",true] -[70,350,"1-1",2.6,"被甜美的幻视支配",0,0,70,350,500,0,1,"SimHei",true][70,350,"1-1",2.6,"伸展五指弹奏的旋律",0,0,70,350,500,0,1,"SimHei",true] -[70,350,"1-1",2.6,"玩弄着的是深深的痛苦",0,0,70,350,500,0,1,"SimHei",true] -[70,350,"1-1",2.6,"赤色的红茶滴落的声音",0,0,70,350,500,0,1,"SimHei",true] -[51,427,"1-1",6,"希望你能察觉到我的心情 为什麽没有察觉到呢",0,0,51,427,500,0,1,"SimHei",true] -[51,427,"1-1",6,"渐渐地陷入疯狂 无论怎样也无法抑止",0,0,51,427,500,0,1,"SimHei",true] -[51,427,"1-1",6,"心情都被毁坏 接着该如何是好",0,0,51,427,500,0,1,"SimHei",true] -[51,427,"1-1",0.5,"你...",0,0,51,427,500,0,1,"SimHei",true] -[51,427,"1-1",1,"好甜",0,0,51,427,500,0,1,"SimHei",true] -[51,427,"1-1",1,"好甜",0,0,51,427,600,0,1,"SimHei",true] -[51,427,"1-1",1,"好红",0,0,51,427,600,0,1,"SimHei",true] -[51,427,"1-1",1,"好红",0,0,51,427,700,0,1,"SimHei",true] -[51,427,"1-1",6,"爱就要满溢出来 再也无法停止…",0,0,51,427,500,0,1,"SimHei",true] -[51,427,"1-1",1,"「让我杀了你!」",0,0,51,427,700,0,1,"SimHei",true] -[43,341,"1-1",6,"让那身体四分五裂 其艳之色即我食粮",0,0,43,341,700,0,1,"SimHei",true] -[41,343,"1-1",6,"就算把这身体撕裂 只有赤银烟雾飞溅",0,0,41,343,700,0,1,"SimHei",true] -[38,429,"1-1",6,"如果那具身体是你 我会将肉都吃得干干净净",0,0,38,429,700,0,1,"SimHei",true] -[38,429,"1-1",6,"把那肢体作为供物 让你成为我脚边的死尸",0,0,38,429,700,0,1,"SimHei",true] -[35,330,"1-1",2.9,"狂気満ちて行くわ",0,0,35,330,700,0,1,"SimHei",true][11,257,"1-1",2.9,"狂気満ちて行くわ",0,0,11,257,700,0,1,"SimHei",true] -[35,330,"1-1",2.7,"この気持ち気付いて",0,0,35,330,700,0,1,"SimHei",true] -[45,299,"1-1",6,"爱溢れて行くわ 止めることはできない",0,0,45,299,600,0,1,"SimHei",true] -[45,299,"1-1",2.9,"爱溢れて行くわ 止めることはできない",0,0,45,299,700,0,1,"SimHei",true] -[0,10,"1-1",10," rvrMO @BL :i::::,:.,...... . r@88MBB@BS UB@X .v75@: \n",0,0,0,10,500,0,1,"SimHei",true] -[0,20,"1-1",10," ,YuM@ ,@Br .:;:, ..::rrrii:i:::::, v@0PPGGOZMB@. vB@UO7 LjB. \n",0,0,0,20,500,0,1,"SimHei",true] -[0,30,"1-1",10," 20B 5E@B. :i:.. ::::::::i::::. . uBq008EZ00qGB@U i@@u,iMM0 Y@: \n",0,0,0,30,500,0,1,"SimHei",true] -[0,40,"1-1",10," G@ :2ZY1@B i: .i:::::::::::::: :@G8ZGEZNNPZEZB5 .B@J YrL@B MY \n",0,0,0,40,500,0,1,"SimHei",true] -[0,50,"1-1",10," :@. :LUBM,2@M v. ..,,,::::::,:,:::::iri,r@MMG8EZqqPqSkkZM@ B@B :O788 . \n",0,0,0,50,500,0,1,"SimHei",true] -[0,60,"1-1",10," :: X0BG B@u .J ....7uj:vkX2YvYLJ7r::::ii:,i@B@B@MMG8NNkqXEO@ .B@Br: .0G0 \n",0,0,0,60,500,0,1,"SimHei",true] -[0,0,"1-1",10," :rjJi@B. :@S :J1, U@@ r.rMB0 \n",0,0,0,0,500,0,1,"SimHei",true] -[0,80,"1-1",10," BM : .GB@7 r JEUi:,,,.:r ,; .:i::::::iL.rk5Y7 ,:.. .jBOZBM2 :@ .qB@B,, .7LY .@ \n",0,0,0,80,500,0,1,"SimHei",true] -[0,90,"1-1",10," :@ .: i::F@@q Fj .B. iiL2Jri: i7: r : :i:ii ii vL:i:.rY77iii :B@B MB@qZ0GB@B@E i. rk, \n",0,0,0,90,500,0,1,"SimHei",true] -[0,100,"1-1",10," . : uki FB@BZu1uJB@ .Z77LJv;7:i .i7 7..: .;i,: :: :7ir:7J77Lrii : uB@G8M@B@B@j i: P. \n",0,0,0,100,500,0,1,"SimHei",true] -[0,110,"1-1",10," ::Pr iB@@@B@B8Bv Fkv::rii..::: r,:v .::i i:i. :;7ir2JL;::;, : BBO8MB@BEi YL. ::u. \n",0,0,0,110,500,0,1,"SimHei",true] -[0,120,"1-1",10," i27 :X:X@@B@MNB1 7vLv:,;i.:,,;ir .:ir .vi; i,iri:.,::j.7u,::::L J@BS8@q: iYvFNu ,0; \n",0,0,0,120,500,0,1,"SimHei",true] -[0,70,"1-1",10," ZBZ .LB@r :.:75P1kXFuiir..rii::rYri71Li::,:.Fviir7UE@MOGGG@B iB@BL ,:: .B@ \n",0,0,0,70,500,0,1,"SimHei",true] -[0,150,"1-1",10," 7YBBi ,OB@OBBF P...,r:iLJF@BMSu7 iUi;0MB@k,,i:i:LF v.r i2 7@XXB@k:rqOqr iu@, \n",0,0,0,150,500,0,1,"SimHei",true] -[0,130,"1-1",10," Ev i7Pu, i0@0qB@ . iuk: i;iiir:. ,L, .Y:r,i ;i;rri::2 F::,ij: Y@O50@@, uivE@1 :, \n",0,0,0,130,500,0,1,"SimHei",true] -[0,160,"1-1",10," 7OB. ,75JvG@@GB. : .: ::.iGvM@BU.r,7J: 7B@Bv0:.rrjv:.,;,: vr .0@P8B@; i:vMBG ;@r \n",0,0,0,160,500,0,1,"SimHei",true][0,170,"1-1",10," 5B, jvqX1 F@BBM. .2,.i, : iB@. .,, BBq :77vr ri.: F .vuEBOG@BY .irE@: Nu \n",0,0,0,170,500,0,1,"SimHei",true] -[0,180,"1-1",10," Br rU@BL :@@@B8U: r7L:,iii: . r: . .: .i7i 7ir.iv , U 2M@B@B@BBMG ruB: \n",0,0,0,180,500,0,1,"SimHei",true] -[0,140,"1-1",10," J .7:OOq M@E8B@ 77i..;irL::i. v: :,L:i :: i::::,vr .7:::7j ;@NF0@MPF. rrPB: \n",0,0,0,140,500,0,1,"SimHei",true] -[0,210,"1-1",10," BS vJkqS rM@@@B@B@1: :.717rkLuuvL5ji: ,7Fuju7 ,r7i iL iOr iE@@@BBFEB ;JkO@M \n",0,0,0,210,500,0,1,"SimHei",true] -[0,220,"1-1",10," : UUOB@ .@M@B@B@@@Xirr.v7r r5YuSSZuu7riU1LG8.LOuB5.iB@B@B@@M. :0 iqU, .2q@j \n",0,0,0,220,500,0,1,"SimHei",true] -[0,230,"1-1",10," LOB@. vO :L5EB@@@B@jvYi:::L.,:7,iL.,..:,7:rO@B@B@B@O2: iqE. i :5ukOMJ 5B2 \n",0,0,0,230,500,0,1,"SimHei",true] -[0,240,"1-1",10," v@@ .rqqF5 iq..LO@B@G: . i .... ..:,:u@M. i7v: r LrYq@BS @O \n",0,0,0,240,500,0,1,"SimHei",true] -[0,190,"1-1",10," . :OBr @B@@@BMv. :1.7:iiiii:rr :J::L; .5:::ui. U .vG@BZXMB@B; 7XX: i@r \n",0,0,0,190,500,0,1,"SimHei",true][0,250,"1-1",10," N@ iLL8G@ .rv5 Nr iF ,,. ,. :i::::v: .i r:::i2Br .rUSO@. .0 \n",0,0,0,250,500,0,1,"SimHei",true] -[0,200,"1-1",10," jB7 .7X:i@B@B@BMu: :,:;:i:LvvJi . .7k: iv7 7r::..: 2ikM@BBOBM@B2. :iU@@B XF \n",0,0,0,200,500,0,1,"SimHei",true] -[0,260,"1-1",10," @i 2qB@M ,7riGY2 .r7: iBP :.. :i .;: . :7v 7i7iZ8@. r18B \n",0,0,0,260,500,0,1,"SimHei",true] -[0,280,"1-1",10," Z@S vi5XPr i: .GZM8MB@.Y25 .:F@u : rj :L7GN rBi \n",0,0,0,280,500,0,1,"SimHei",true] -[0,290,"1-1",10," @O 1u0Eu ri : 7MNEMB@u E@Bu :u7i,:. :,:; :JkO S7 \n",0,0,0,290,500,0,1,"SimHei",true] -[0,270,"1-1",10," FO@1 UiLOGM ui. .:BMU ..:. .7iL7 i ru i57ruOZ rZ@. \n",0,0,0,270,500,0,1,"SimHei",true] -[0,300,"1-1",10," :B UBPO@B@Bu:.i v8qGB@j::0EZOF: .:,i7rL7rUr5PGBr ,0@ \n",0,0,0,300,500,0,1,"SimHei",true] -[0,310,"1-1",10," Y@@LPr.:ZNr 5EGOq:.:.iMqNO@Bu .,7LvJ@B@1U1 :B \n",0,0,0,310,600,0,1,"SimHei",true] -[0,320,"1-1",10," @M i ,.:51OML. .:: OGPOGMB@8, v7 7q@. , \n",0,0,0,320,600,0,1,"SimHei",true] -[0,330,"1-1",10," : ,. ...: FOBM ...:.iZZZGZBBr ir: L. \n",0,0,0,330,600,0,1,"SimHei",true] -[0,350,"1-1",10," :v ,. r: LBMP;q@2PZui.7O0ZM7 .iiiii \n",0,0,0,350,700,0,1,"SimHei",true] -[0,340,"1-1",10," iui:: ,, ,..:BM8 v : i 5880OBi :rrr:,,:rY \n",0,0,0,340,700,0,1,"SimHei",true] -[0,360,"1-1",10," .i, . .ii...7BMEOBGk@M@BBMEGGZ@: ,,:.. .L. \n",0,0,0,360,700,0,1,"SimHei",true] -[0,390,"1-1",10," :iri::,: .Yii;ri:..JSYuj7:. ,vJUuSFSkS7Lvvi, J: \n",0,0,0,390,800,0,1,"SimHei",true] -[0,400,"1-1",10," :iri::,: .Yii;ri:..JSYuj7:. ,vJUuSFSkS7Lvvi, J: \n",0,0,0,400,800,0,1,"SimHei",true] -[0,370,"1-1",10," i;. .,iEB02qGM@@MM8OG80ZNGM :i, .7 \n",0,0,0,370,800,0,1,"SimHei",true] -[0,380,"1-1",10," .:r: .,. .8Mj .:UG8MM8MOMG88B87r: .::P \n",0,0,0,380,800,0,1,"SimHei",true] -[0,410,"1-1",10," :r:.. . .:.;u,::,.iuB8MMBB@@MPkLriYj1SPFF2, ,irLi,.,u \n",0,0,0,410,600,0,1,"SimHei",true] -[0,420,"1-1",10," L. ,,. ,rv7, .,. ,FBBGZEZEZ0G8MB@BBMMOOZGGBM. :ii:J \n",0,0,0,420,700,0,1,"SimHei",true] -[0,450,"1-1",10," :iXM@@BMOSkXG0E00qGE0NZZEFkFkFFNZ5Z5PPZBq ..,. .71: \n",0,0,0,450,700,0,1,"SimHei",true] -[0,460,"1-1",10," B@OOO8GZFkFq0ZE05ZGENq0GkSSXSkSGEZENFqZ8@@Li.. .:rYj;: \n",0,0,0,460,700,0,1,"SimHei",true] -[0,430,"1-1",10," ,L.:,.777 ,r5BMOEZ0EqE0ENqX0ZOZ8EEkqNNEBBq,, .u. \n",0,0,0,430,700,0,1,"SimHei",true] -[0,440,"1-1",10," Y,. YU :r18@B@qqEZNE0E08NE001kkPSk5kNPPkXZ@2 i:. .,: \n",0,0,0,440,700,0,1,"SimHei",true] -[0,470,"1-1",10," :BMNOMMMMOqkNqZ0ZXSZGNEqPSkSPSSkZ0E0ZkPFkkEO@@BF: ,r, \n",0,0,0,470,1000,0,1,"SimHei",true] -[0,480,"1-1",10," :B80Oi .:BqjZqqEZ0kFG0Z0Z0PSPkXSZEZ0ZNPSPSXFXqOO@BM, \n",0,0,0,480,1000,0,1,"SimHei",true] -[0,490,"1-1",10," BMu:8urM; L.LPSqNE2XENPNq0FkSXFX0NqNNS5XFXSkSFq0N8B@ ",0,0,0,490,300,0,1,"SimHei",true] -def text c1 { - content = "我之中的我 我之中的我" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 3s - fontFamily = "宋体" - -} -def text c2 { - content = "私の中の私が 私の中の私が" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 3s - fontFamily = "宋体" -} -def text c1 { - content = "重复着一个单语" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 1.50s - fontFamily = "宋体" - -} - -def text c2 { - content = "ひとつの単语を" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 1.50s - fontFamily = "宋体" - -} - -def text c1 { - content = "多少遍 多少遍 多少遍" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 1.5s - fontFamily = "宋体" - -} - -def text c2 { - content = "何度も 何度も 何度も" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 1.5s - fontFamily = "宋体" - -} - -def text c1 { - content = "不断重复" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 0.8s - fontFamily = "宋体" - -} - -def text c2 { - content = "无尽蔵に" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 0.8s - fontFamily = "宋体" - -} - -def text c1 { - content = "重复着 重复着 重复着 重复着" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 4.3s - fontFamily = "宋体" - -} - -def text c2 { - content = "缲り返し 缲り返し 缲り返し 缲り返し" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 4.3s - fontFamily = "宋体" - -} - -def text c1 { - content = "歌唱" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 1s - fontFamily = "宋体" - -} - -def text c2 { - content = "歌う" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 1s - fontFamily = "宋体" - -} - -def text c1 { - content = "紧握的左手异常甜蜜 挥舞的右手无比甘甜" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 3s - fontFamily = "宋体" - -} - -def text c2 { - content = "掴む左手が甘くて 震える右手が甘くて" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 3s - fontFamily = "宋体" - -} - -def text c1 { - content = "好甜 好甜" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 0.7s - fontFamily = "宋体" - -} - -def text c2 { - content = "甘くて 甘くて " - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 0.7s - fontFamily = "宋体" - -} - -def text c1 { - content = "笑着的嘴巴已然开裂 随后便给予你杀戮" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration =1.6s - fontFamily = "宋体" - -} - -def text c2 { - content = "笑う口が裂けても それがあなたを杀し" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 1.6s - fontFamily = "宋体" - -} - -def text c1 { - content = "好快乐 好快乐" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 0.8s - fontFamily = "宋体" - -} - -def text c2 { - content = "楽しくて 楽しくて " - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 0.8s - fontFamily = "宋体" - -} - -def text c1 { - content = "要刻入脑髓" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 0.6s - fontFamily = "宋体" - -} - -def text c2 { - content = "脳髄を烧くように" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 0.6s - fontFamily = "宋体" - -} - -def text c1 { - content = "好快乐 好快乐" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 1s - fontFamily = "宋体" - -} - -def text c2 { - content = "楽しくて 楽しくて" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 1s - fontFamily = "宋体" - -} - -def text c1 { - content = "快乐到颤抖" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 0.6s - fontFamily = "宋体" - -} - -def text c2 { - content = "震えて" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 0.6s - fontFamily = "宋体" - -} - -def text c1 { - content = "好快乐" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 2.6s - fontFamily = "宋体" - -} - -def text c2 { - content = "楽しくて" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 2.6s - fontFamily = "宋体" - -} - -def text c1 { - content = "好红 好红 好红 好红" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 2s - fontFamily = "宋体" - -} - -def text c2 { - content = "赤い 赤い 赤い 赤い" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 2s - fontFamily = "宋体" - -} - -def text c1 { - content = "好甜 好甜 好甜 好甜" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 3.5s - fontFamily = "宋体" - -} - -def text c2 { - content = "甘い 甘い 甘い 甘い " - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 3.5s - fontFamily = "宋体" - -} - -def text c1 { - content = "好红 好甜 好红 好甜" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 6s - fontFamily = "宋体" - -} - -def text c2 { - content = "赤い 甘い 赤い 甘い " - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 6s - fontFamily = "宋体" - -} - -def text c1 { - content = "你…" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 0.3s - fontFamily = "宋体" - -} - -def text c2 { - content = "あなた…" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 0.3s - fontFamily = "宋体" - -} - -def text c1 { - content = "好甜…" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 3s - fontFamily = "宋体" - -} - -def text c2 { - content = "甘い…" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 3s - fontFamily = "宋体" - -} -def text c1 { - content = "好红…" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 3s - fontFamily = "宋体" - -} - -def text c2 { - content = "赤い…" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 3s - fontFamily = "宋体" - -} -def text c1 { - content = "「让我杀了你!」" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 1s - fontFamily = "宋体" - -} - -def text c2 { - content = "『杀してあげる』" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 1s - fontFamily = "宋体" - -} -def text c1 { - content = "湿润的深红眼眸 鲜艳的裙摆摇荡" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 6s - fontFamily = "宋体" - -} - -def text c2 { - content = "深く朱き润む瞳 甘い色のスカート揺らす" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 6s - fontFamily = "宋体" - -} - -def text c1 { - content = "幼小的脸颊浸染朱红 疼痛令双腿都不自由" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 6.5s - fontFamily = "宋体" - -} - -def text c2 { - content = "幼き頬 朱を渗ませ 疼きに足を崩される" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 6.5s - fontFamily = "宋体" - -} - -def text c1 { - content = "被甜美的幻视支配 伸展五指弹奏的旋律" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 5.8s - fontFamily = "宋体" - -} - -def text c2 { - content = "甘い幻视に支配され 奏でては咲く五指の调べ" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 5.8s - fontFamily = "宋体" - -} - -def text c1 { - content = "赤色的红茶滴落的声音 玩弄着的是深深的痛苦" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 6.5s - fontFamily = "宋体" - -} - -def text c2 { - content = "赤い红茶 滴る音 弄んでは 深く抉る" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration =6.5s - fontFamily = "宋体" - -} - -def text c1 { - content = "「这份思念无法传达吗?」" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 3s - fontFamily = "宋体" - -} - -def text c2 { - content = "「この想い届かないのかな?」" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 3s - fontFamily = "宋体" - -} - -def text c1 { - content = "「那双眼眸映出的是谁?」" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 3s - fontFamily = "宋体" - -} - -def text c2 { - content = "「その瞳には谁が映るのかな?」" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 3s - fontFamily = "宋体" - -} - -def text c1 { - content = "「心已经被弄坏了吗?」" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 2.8s - fontFamily = "宋体" - -} - -def text c2 { - content = "「心壊れているのかな?」" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration =2.8s - fontFamily = "宋体" - -} - -def text c1 { - content = "「弄坏后就不能恢复吗?」" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 3s - fontFamily = "宋体" - -} - -def text c2 { - content = "「壊れたら戻らないのかな?」" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 3s - fontFamily = "宋体" - -} - -def text c1 { - content = "「受的伤害无法痊愈吗?」" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 3s - fontFamily = "宋体" - -} - -def text c2 { - content = "「刻む伤は愈えないのかな?」" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 3s - fontFamily = "宋体" - -} - -def text c1 { - content = "「这样做能刻画下时间吗?」" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 3s - fontFamily = "宋体" - -} - -def text c2 { - content = "「そうして时を刻むの?」" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 3s - fontFamily = "宋体" - -} - -def text c1 { - content = "我想要你的全部 想到浑身颤抖(希望你察觉我的心情 为什么没有察觉到呢?)" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 6s - fontFamily = "宋体" - -} - -def text c2 { - content = "あなたのその全てが欲しくて 欲しくて震えてる (この気持ち気付いて どうして気付いてくれないの)" - color = 0xFF0000 - fontSize =2% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 6s - fontFamily = "宋体" - -} - -def text c1 { - content = "就让那甜美的心跳 竭尽气力就此停止吧(渐渐地被陷入疯狂 无论怎样也无法抑止)" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 6s - fontFamily = "宋体" - -} - -def text c2 { - content = "甘美なその鼓动を うだち尽くして止めようか (狂気に満ちてゆくわ どうすれば止まるの)" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 6s - fontFamily = "宋体" - -} - -def text c1 { - content = "尽情弄脏那片肌肤 污辱你的只有我(心情都被毁坏 接着该如何是好)" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 6.5s - fontFamily = "宋体" - -} - -def text c2 { - content = "その肌を秽し尽くし 辱めるのは私だけ (この気持ち壊れたて どこへ辿り着くのでしょうか)" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 6.5s - fontFamily = "宋体" - -} - -def text c1 { - content = "快点平息这份爱抚 和操弄你的冲动吧(爱就要满溢出来 无法停止…)" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 6s - fontFamily = "宋体" - -} - -def text c2 { - content = "爱で抚でて揺さ振られて この冲动杀してよ (爱漏れて行くわ 止めることはできない…)" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 6s - fontFamily = "宋体" - -} - -def text c1 { - content = "声嘶力竭歌唱的音色 旋律化作朱红之虹" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 6.4s - fontFamily = "宋体" - -} - -def text c2 { - content = "喉を枯らし叫ウ音色 旋律は朱の虹となり" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 6.4s - fontFamily = "宋体" - -} - -def text c1 { - content = "那是屠杀你的色彩 绽放又甜又深之色" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 6s - fontFamily = "宋体" - -} - -def text c2 { - content = "君屠る此ノ色彩 甘く深き色を放つ" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 6s - fontFamily = "宋体" - -} - -def text c1 { - content = "声嘶力竭歌唱的颜色 赤色之雨飞溅四周" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 6.5s - fontFamily = "宋体" - -} - -def text c2 { - content = "喉を枯らし叫ウ音色 赤キ雨に彩られたら" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 6.5s - fontFamily = "宋体" - -} - -def text c1 { - content = "华丽的舞台业已构筑 我独自在其间起舞" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 6s - fontFamily = "宋体" - -} - -def text c2 { - content = "华丽的舞台业已构筑 我独自在其间起舞" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration =6s - fontFamily = "宋体" - -} - -def text c1 { - content = "「那份愿望破灭了吗?」" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 3s - fontFamily = "宋体" - -} - -def text c2 { - content = "「その愿い溃えたのかな?」" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 3s - fontFamily = "宋体" - -} - -def text c1 { - content = "「那份思念断绝了吗?」" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 3s - fontFamily = "宋体" - -} - -def text c2 { - content = "「その想い断たれたのかな?」" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 3s - fontFamily = "宋体" - -} - -def text c1 { - content = "「那份希望磨灭了吗?」" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 3s - fontFamily = "宋体" - -} - -def text c2 { - content = "「その希望绝えたのかな?」" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 3s - fontFamily = "宋体" - -} - -def text c1 { - content = "「那份目光燃烧了吗?」" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 3s - fontFamily = "宋体" - -} - -def text c2 { - content = "「その瞳烧かれたのかな?」" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 3s - fontFamily = "宋体" - -} - -def text c1 { - content = "「那份肌肤污秽了吗?」" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 3s - fontFamily = "宋体" - -} - -def text c2 { - content = "「その肌は秽されたかな?」" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 3s - fontFamily = "宋体" - -} - -def text c1 { - content = "「大家都已不见了吗?」" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 2.6s - fontFamily = "宋体" - -} - -def text c2 { - content = "「そして谁もいなくなる?」" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 2.6s - fontFamily = "宋体" - -} - -def text c1 { - content = "尽然把这身体撕裂 只有赤银烟雾飞溅(因为哪里都不存在真正的我)" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 6s - fontFamily = "宋体" - -} - -def text c2 { - content = "其ノ生を引き裂かれて 赤银を吐き消し飞べ (どこにも本当の 私なんていないのだから)" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 6s - fontFamily = "宋体" - -} - -def text c1 { - content = "让那身体四分五裂 其艳之色即我食粮(虚幻的生命 美丽又可爱)" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 6s - fontFamily = "宋体" - -} - -def text c2 { - content = "其ノ生の华散らして 极彩に咲き我が粮 (儚い命だわ 美しく爱おしい)" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 6s - fontFamily = "宋体" - -} - -def text c1 { - content = "如果那具身体是你 我会将肉都吃得干干净净(你只能永远成为我的东西)" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 6s - fontFamily = "宋体" - -} - -def text c2 { - content = "其ノ生がお前ならば 喰らい尽くして血肉にす (永远に私のものになるしかない)" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 6s - fontFamily = "宋体" - -} - -def text c1 { - content = "把那肢体作为供物 让你成为我脚边的死尸(一直在我身边 再也不放你走)" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 6s - fontFamily = "宋体" - -} - -def text c2 { - content = "其ノ四肢を贽と捧げ 我が足元の死尸となれ (ずっと私の傍に もう行かさないから…)" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 6s - fontFamily = "宋体" - -} - -def text c1 { - content = "重复着一个单语" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 1s - fontFamily = "宋体" - -} - -def text c2 { - content = "ひとつの単语を" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 1s - fontFamily = "宋体" - -} - -def text c1 { - content = "我之中的我 我之中的我(希望你能察觉到我的心情…)" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 2.7s - fontFamily = "宋体" - -} - -def text c2 { - content = "私の中の私が 私の中の私が(この気持ち気付いて…)" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 2.7s - fontFamily = "宋体" - -} -def text c1 { - content = "多少遍多少遍多少遍" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 1.4s - fontFamily = "宋体" - -} - -def text c2 { - content = "何度も何度も何度も" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 1.4s - fontFamily = "宋体" - -} - -def text c1 { - content = "不断重复(渐渐地陷入疯狂…)" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 1s - fontFamily = "宋体" - -} - -def text c2 { - content = "无尽蔵に(狂気満ちて行くわ…)" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 1s - fontFamily = "宋体" - -} - -def text c1 { - content ="重复着 重复着 重复着" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 4.1s - fontFamily = "宋体" - -} - -def text c2 { - content = "缲り返し 缲り返し 缲り返し 缲り返し" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 4.1s - fontFamily = "宋体" - -} - -def text c1 { - content = "歌唱" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 0.5s - fontFamily = "宋体" - -} - -def text c2 { - content = "歌う" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 0.5s - fontFamily = "宋体" - -} - -def text c1 { - content = "(希望你能察觉到我的心情…)" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 0.7s - fontFamily = "宋体" - -} - -def text c2 { - content = "(この気持ち気付いて…)" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 0.7s - fontFamily = "宋体" - -} - -def text c1 { - content = "紧握的左手异常甜蜜 挥舞的右手无比甘甜" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 3s - fontFamily = "宋体" - -} - -def text c2 { - content = "掴む左手が甘くて 震える右手が甘くて" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 3s - fontFamily = "宋体" - -} - -def text c1 { - content = "好甜…好甜… " - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 0.7s - fontFamily = "宋体" - -} - -def text c2 { - content = "甘くて 甘くて" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 0.7s - fontFamily = "宋体" - -} - -def text c1 { - content = "好快乐 好快乐" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 1s - fontFamily = "宋体" - -} - -def text c2 { - content = "楽しくて 楽しくて " - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 3s - fontFamily = "宋体" - -} - -def text c1 { - content = "笑着的嘴巴已然开裂 随后便给予你杀戮" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 1.2s - fontFamily = "宋体" - -} - -def text c2 { - content = "笑う口が裂けても それがあなたを杀し" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 1.2s - fontFamily = "宋体" - -} - -def text c1 { - content = "刻录在你的脑髓之中(爱就要满溢出来 再也无法停止…)" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 0.8s - fontFamily = "宋体" - -} - -def text c2 { - content = " 脳髄を烧くように(爱溢れて行くわ 止めることはできない…)" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 0.8s - fontFamily = "宋体" - -} - -def text c1 { - content = "好快乐 好快乐 " - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 0.8s - fontFamily = "宋体" - -} - -def text c2 { - content = "楽しくて 楽しくて" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 0.8s - fontFamily = "宋体" - -} - -def text c1 { - content = "快乐到颤抖" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 0.5s - fontFamily = "宋体" - -} - -def text c2 { - content = "震えて" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 0.5s - fontFamily = "宋体" - -} - -def text c1 { - content = "好快乐 好快乐 " - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 2s - fontFamily = "宋体" - -} - -def text c2 { - content = "楽しくて 楽しくて " - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 2s - fontFamily = "宋体" - -} - -def text c1 { - content = "希望你能察觉到我的心情 为什么没有察觉到呢" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 6s - fontFamily = "宋体" - -} - -def text c2 { - content = "この気持ち気付いて どうして気付いてくれないの" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 6s - fontFamily = "宋体" - -} - -def text c1 { - content = "渐渐地陷入疯狂 无论怎样也无法抑止" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 6s - fontFamily = "宋体" - -} - -def text c2 { - content = "狂気満ちて行くわ どうすれば止まるの" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 6s - fontFamily = "宋体" - -} - -def text c1 { - content = "心情都被毁坏 接着该如何是好" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 6.4s - fontFamily = "宋体" - -} - -def text c2 { - content = "この気持ち気付いて どこへ辿り着くのでしょうか" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 6.4s - fontFamily = "宋体" - -} - -def text c1 { - content = "爱就要满溢出来 再也无法停止…" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 6s - fontFamily = "宋体" - -} - -def text c2 { - content = "爱溢れて行くわ 止めることはできない…" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 6s - fontFamily = "宋体" - -} - -def text c1 { - content = "好甜… 好红…" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 12.5s - fontFamily = "宋体" - -} - -def text c2 { - content = "甘い 赤い……" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 12.5s - fontFamily = "宋体" - -} - -def text c1 { - content = "你…" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 0.5s - fontFamily = "宋体" - -} - -def text c2 { - content = "あなた…" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration =0.5s - fontFamily = "宋体" - -} - -def text c1 { - content = "好甜… 好甜… " - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 2.5s - fontFamily = "宋体" - -} - -def text c2 { - content = " 甘い… 甘い…" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 2.5s - fontFamily = "宋体" - -} - -def text c1 { - content = "赤い… 赤い…" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 2.5s - fontFamily = "宋体" - -} - -def text c2 { - content = "好红… 好红… " - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 2.5s - fontFamily = "宋体" - -} - -def text c1 { - content = "「让我杀了你!" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 1.2s - fontFamily = "宋体" - -} - -def text c2 { - content = "『杀してあげる』" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 1.2s - fontFamily = "宋体" - -} - -def text c1 { - content = "就算把这身体撕裂 只有赤银烟雾飞溅(因为想要我的世界永远保持美丽)" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 6s - fontFamily = "宋体" - -} - -def text c2 { - content = "其ノ生を引き裂かれて 赤银を吐き消し飞べ (私のセカイは 绮丽なままでいてほしいから)" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 6s - fontFamily = "宋体" - -} - -def text c1 { - content = "如果那具身体是你 我会将肉都吃得干干净净(对不起 这是我最后用来爱你的方式)" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 6s - fontFamily = "宋体" - -} - -def text c2 { - content = "其ノ生がお前ならば 喰らい尽くして血肉にす (ごめんなさい これが最后の爱し方だったから)" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 6s - fontFamily = "宋体" - -} - -def text c1 { - content = "让那身体四分五裂 其艳之色即我食粮(诸多回忆飞舞 成为我的东西吧)" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration =6s - fontFamily = "宋体" - -} - -def text c2 { - content = "其ノ生の华散らして 极彩に咲き我が粮 (想い出が舞うわ 私のものにさせて)" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 6s - fontFamily = "宋体" - -} - -def text c1 { - content = "把那肢体作为供物 让你成为我脚边的死尸(一直在我身边 我想和你在一起)" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 6s - fontFamily = "宋体" - -} - -def text c2 { - content = "其ノ四肢を贽と捧げ 我が足元の死尸となれ (ずっと私の傍に あなたといきたいの…)" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 6s - fontFamily = "宋体" - -} - -def text c1 { - content = "Bas字幕弹幕作者:本人猫癌晚期 UID:75549002" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 120s - fontFamily = "宋体" - -} -def text c2 { - content = "【弹幕祭应援】 緋色月下、狂咲ノ絶-1st Anniversary Remix" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 120s - fontFamily = "宋体" - -} - - -def text c1 { - content = "原曲/U.N.オーエンは彼女なのか?" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 95% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 12.1s - fontFamily = "宋体" - -} - -def text c2 { - content = " 緋色月下、狂咲ノ絶-1st Anniversary Remix" - color = 0xFF0000 - fontSize =3% - x = 50% - y = 5% - anchorX = 0.5 - anchorY = 0.5 - bold = 1 - textShadow = 1 - duration = 12.1s - fontFamily = "宋体" - -} - \ No newline at end of file diff --git a/src/danmaku/tsconfig.json b/src/danmaku/tsconfig.json deleted file mode 100644 index d220626..0000000 --- a/src/danmaku/tsconfig.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "extends": "../tsconfig-base.json", - "include": [ - "**/*", - "../utils", - ], - "references": [ - { - "path": "../utils" - } - ] -} \ No newline at end of file diff --git a/src/dash-player/index.js b/src/dash-player/index.js index 108c249..d358e07 100644 --- a/src/dash-player/index.js +++ b/src/dash-player/index.js @@ -4,9 +4,9 @@ * Copyright 2018 - 2024 bilibili, Inc. * Released in 2024-02-29T07:18:24.725Z * - * 从B站移植新版本时,须要手动冻结dashjs的MediaPlayer、FactoryMaker、Version三属性以免被页面污染 - * 参考:Object.defineProperties(s,{MediaPlayer:{value:n.a},FactoryMaker:{value:i.a},Version:{value:Object(o.a)()}}) + * 从B站移植新版本时,须要手动修改dashjs为局部变量避免污染页面环境 */ +var dashjs={}; export default (() => {function c(e){return c="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},c(e)}return function(e){var t={};function r(n){if(t[n])return t[n].exports;var i=t[n]={i:n,l:!1,exports:{}};return e[n].call(i.exports,i,i.exports,r),i.l=!0,i.exports}return r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==c(e)&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var i in e)r.d(n,i,function(t){return e[t]}.bind(null,i));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=16)}([function(e,t,r){"use strict";var n=function(){var e=void 0,t=[],r={},n={};function i(e,r){for(var n in t){var i=t[n];if(i.context===e&&i.name===r)return i.instance}return null}function o(e,t){return t[e]}function a(e,t,r){e in r&&(r[e]=t)}function s(t,r,n){var i=void 0,o=t.__dashjs_factory_name,a=r[o];if(a){var s=a.instance;if(!a.override)return s.apply({context:r,factory:e},n);for(var u in i=t.apply({context:r},n),s=s.apply({context:r,factory:e,parent:i},n))i.hasOwnProperty(u)&&(i[u]=s[u])}else i=t.apply({context:r},n);return i.getClassName=function(){return o},i}return e={extend:function(e,t,r,n){!n[e]&&t&&(n[e]={instance:t,override:r})},reset:function(){t.length=0},getSingletonInstance:i,setSingletonInstance:function(e,r,n){for(var i in t){var o=t[i];if(o.context===e&&o.name===r)return void(t[i].instance=n)}t.push({name:r,context:e,instance:n})},getSingletonFactory:function(e){var n=o(e.__dashjs_factory_name,r);return n||(n=function(r){var n=void 0;return void 0===r&&(r={}),{getInstance:function(){return n||(n=i(r,e.__dashjs_factory_name)),n||(n=s(e,r,arguments),t.push({name:e.__dashjs_factory_name,context:r,instance:n})),n}}},r[e.__dashjs_factory_name]=n),n},getSingletonFactoryByName:function(e){return o(e,r)},updateSingletonFactory:function(e,t){a(e,t,r)},getClassFactory:function(e){var t=o(e.__dashjs_factory_name,n);return t||(t=function(t){return void 0===t&&(t={}),{create:function(){return s(e,t,arguments)}}},n[e.__dashjs_factory_name]=t),t},getClassFactoryByName:function(e){return o(e,n)},updateClassFactory:function(e,t){a(e,t,n)}}}();t.a=n},function(e,t,r){var n={parseBuffer:function(e){return new i(e).parse()},addBoxProcessor:function(e,t){"string"==typeof e&&"function"==typeof t&&(o.prototype._boxProcessors[e]=t)},createFile:function(){return new i},createBox:function(e,t,r){var n=o.create(e);return t&&t.append(n,r),n},createFullBox:function(e,t,r){var i=n.createBox(e,t,r);return i.version=0,i.flags=0,i},Utils:{}};n.Utils.dataViewToString=function(e,t){var r=t||"utf-8";if("undefined"!=typeof TextDecoder)return new TextDecoder(r).decode(e);var n=[],i=0;if("utf-8"===r)for(;i>6),t.push(128|63&n)):n<65536?(t.push(224|n>>12),t.push(128|63&n>>6),t.push(128|63&n)):(t.push(240|n>>18),t.push(128|63&n>>12),t.push(128|63&n>>6),t.push(128|63&n))}return t},n.Utils.appendBox=function(e,t,r){if(t._offset=e._cursor.offset,t._root=e._root?e._root:e,t._raw=e._raw,t._parent=e,-1!==r)if(null!=r){var n,i=-1;if("number"==typeof r)i=r;else{if("string"==typeof r)n=r;else{if("object"!=c(r)||!r.type)return void e.boxes.push(t);n=r.type}for(var o=0;o>3,t},o.prototype._readUint=function(e){var t,r,n=null,i=this._cursor.offset-this._raw.byteOffset;switch(e){case 8:n=this._raw.getUint8(i);break;case 16:n=this._raw.getUint16(i);break;case 24:n=((t=this._raw.getUint16(i))<<8)+(r=this._raw.getUint8(i+2));break;case 32:n=this._raw.getUint32(i);break;case 64:t=this._raw.getUint32(i),r=this._raw.getUint32(i+4),n=t*Math.pow(2,32)+r}return this._cursor.offset+=e>>3,n},o.prototype._readString=function(e){for(var t="",r=0;r0?e:this._raw.byteLength-(this._cursor.offset-this._offset);if(t>0){var r=new Uint8Array(this._raw.buffer,this._cursor.offset,t);return this._cursor.offset+=t,r}return null},o.prototype._readUTF8String=function(){var e=this._raw.byteLength-(this._cursor.offset-this._offset),t=null;return e>0&&(t=new DataView(this._raw.buffer,this._cursor.offset,e),this._cursor.offset+=e),t?n.Utils.dataViewToString(t):t},o.prototype._parseBox=function(){if(this._parsing=!0,this._cursor.offset=this._offset,this._offset+8>this._raw.buffer.byteLength)this._root._incomplete=!0;else{switch(this._procField("size","uint",32),this._procField("type","string",4),1===this.size&&this._procField("largesize","uint",64),"uuid"===this.type&&this._procFieldArray("usertype",16,"uint",8),this.size){case 0:this._raw=new DataView(this._raw.buffer,this._offset,this._raw.byteLength-this._cursor.offset+8);break;case 1:this._offset+this.size>this._raw.buffer.byteLength?(this._incomplete=!0,this._root._incomplete=!0):this._raw=new DataView(this._raw.buffer,this._offset,this.largesize);break;default:this._offset+this.size>this._raw.buffer.byteLength?(this._incomplete=!0,this._root._incomplete=!0):this._raw=new DataView(this._raw.buffer,this._offset,this.size)}this._incomplete||(this._boxProcessors[this.type]&&this._boxProcessors[this.type].call(this),-1!==this._boxContainers.indexOf(this.type)?this._parseContainerBox():this._data=this._readData())}},o.prototype._parseFullBox=function(){this.version=this._readUint(8),this.flags=this._readUint(24)},o.prototype._parseContainerBox=function(){for(this.boxes=[];this._cursor.offset-this._raw.byteOffset>3}else this.size+=e>>3},o.prototype._writeUint=function(e,t){if(this._rawo){var r,n,i=this._cursor.offset-this._rawo.byteOffset;switch(e){case 8:this._rawo.setUint8(i,t);break;case 16:this._rawo.setUint16(i,t);break;case 24:r=(16776960&t)>>8,n=255&t,this._rawo.setUint16(i,r),this._rawo.setUint8(i+2,n);break;case 32:this._rawo.setUint32(i,t);break;case 64:n=t-(r=Math.floor(t/Math.pow(2,32)))*Math.pow(2,32),this._rawo.setUint32(i,r),this._rawo.setUint32(i+4,n)}this._cursor.offset+=e>>3}else this.size+=e>>3},o.prototype._writeString=function(e,t){for(var r=0;r>10&31),96+(this.language>>5&31),96+(31&this.language))),this._procField("pre_defined","uint",16)},o.prototype._boxProcessors.mehd=function(){this._procFullBox(),this._procField("fragment_duration","uint",1==this.version?64:32)},o.prototype._boxProcessors.mfhd=function(){this._procFullBox(),this._procField("sequence_number","uint",32)},o.prototype._boxProcessors.mfro=function(){this._procFullBox(),this._procField("mfra_size","uint",32)},o.prototype._boxProcessors.mp4a=o.prototype._boxProcessors.enca=function(){this._procFieldArray("reserved1",6,"uint",8),this._procField("data_reference_index","uint",16),this._procFieldArray("reserved2",2,"uint",32),this._procField("channelcount","uint",16),this._procField("samplesize","uint",16),this._procField("pre_defined","uint",16),this._procField("reserved3","uint",16),this._procField("samplerate","template",32),this._procField("esds","data",-1)},o.prototype._boxProcessors.mvhd=function(){this._procFullBox(),this._procField("creation_time","uint",1==this.version?64:32),this._procField("modification_time","uint",1==this.version?64:32),this._procField("timescale","uint",32),this._procField("duration","uint",1==this.version?64:32),this._procField("rate","template",32),this._procField("volume","template",16),this._procField("reserved1","uint",16),this._procFieldArray("reserved2",2,"uint",32),this._procFieldArray("matrix",9,"template",32),this._procFieldArray("pre_defined",6,"uint",32),this._procField("next_track_ID","uint",32)},o.prototype._boxProcessors.payl=function(){this._procField("cue_text","utf8")},o.prototype._boxProcessors.pssh=function(){this._procFullBox(),this._procFieldArray("SystemID",16,"uint",8),this._procField("DataSize","uint",32),this._procFieldArray("Data",this.DataSize,"uint",8)},o.prototype._boxProcessors.schm=function(){this._procFullBox(),this._procField("scheme_type","uint",32),this._procField("scheme_version","uint",32),1&this.flags&&this._procField("scheme_uri","string",-1)},o.prototype._boxProcessors.sdtp=function(){this._procFullBox();var e=-1;this._parsing&&(e=this._raw.byteLength-(this._cursor.offset-this._raw.byteOffset)),this._procFieldArray("sample_dependency_table",e,"uint",8)},o.prototype._boxProcessors.sidx=function(){this._procFullBox(),this._procField("reference_ID","uint",32),this._procField("timescale","uint",32),this._procField("earliest_presentation_time","uint",1==this.version?64:32),this._procField("first_offset","uint",1==this.version?64:32),this._procField("reserved","uint",16),this._procField("reference_count","uint",16),this._procEntries("references",this.reference_count,(function(e){this._parsing||(e.reference=(1&e.reference_type)<<31,e.reference|=2147483647&e.referenced_size,e.sap=(1&e.starts_with_SAP)<<31,e.sap|=(3&e.SAP_type)<<28,e.sap|=268435455&e.SAP_delta_time),this._procEntryField(e,"reference","uint",32),this._procEntryField(e,"subsegment_duration","uint",32),this._procEntryField(e,"sap","uint",32),this._parsing&&(e.reference_type=e.reference>>31&1,e.referenced_size=2147483647&e.reference,e.starts_with_SAP=e.sap>>31&1,e.SAP_type=e.sap>>28&7,e.SAP_delta_time=268435455&e.sap)}))},o.prototype._boxProcessors.smhd=function(){this._procFullBox(),this._procField("balance","uint",16),this._procField("reserved","uint",16)},o.prototype._boxProcessors.ssix=function(){this._procFullBox(),this._procField("subsegment_count","uint",32),this._procEntries("subsegments",this.subsegment_count,(function(e){this._procEntryField(e,"ranges_count","uint",32),this._procSubEntries(e,"ranges",e.ranges_count,(function(e){this._procEntryField(e,"level","uint",8),this._procEntryField(e,"range_size","uint",24)}))}))},o.prototype._boxProcessors.stsd=function(){this._procFullBox(),this._procField("entry_count","uint",32),this._procSubBoxes("entries",this.entry_count)},o.prototype._boxProcessors.subs=function(){this._procFullBox(), @@ -49,11 +49,11 @@ n.hasOwnProperty(Je.AVAILABILITY_TIME_COMPLETE)?d.availabilityTimeComplete="fals function a(e,t,n,i){return i?n&&t.timeShiftBufferDepth!=Number.POSITIVE_INFINITY?new Date(t.availabilityStartTime.getTime()+1e3*(e+t.timeShiftBufferDepth)):t.availabilityEndTime:n?new Date(t.availabilityStartTime.getTime()+1e3*(e-r)):t.availabilityStartTime}function s(e,t){return(e.getTime()-t.mpd.availabilityStartTime.getTime()+1e3*r)/1e3}function u(e){n||void 0!==e.offset&&(o(e.offset/1e3),n=!0)}function c(){r=0,n=!1,i=NaN}return{initialize:function(){c(),t.on(p.TIME_SYNCHRONIZATION_COMPLETED,u,this)},isTimeSyncCompleted:function(){return n},setTimeSyncCompleted:function(e){n=e},getClientTimeOffset:function(){return r},setClientTimeOffset:o,getExpectedLiveEdge:function(){return i},setExpectedLiveEdge:function(e){i=e},calcAvailabilityStartTimeFromPresentationTime:function(e,t,r){return a.call(this,e,t,r)},calcAvailabilityEndTimeFromPresentationTime:function(e,t,r){return a.call(this,e,t,r,!0)},calcPresentationTimeFromWallTime:s,calcPresentationTimeFromMediaTime:function(e,t){return e+(t.adaptation.period.start-t.presentationTimeOffset)},calcPeriodRelativeTimeFromMpdRelativeTime:function(e,t){return t-e.adaptation.period.start},calcMediaTimeFromPresentationTime:function(e,t){return e-t.adaptation.period.start+t.presentationTimeOffset},calcSegmentAvailabilityRange:function(e,t){var r=e.adaptation.period,i={start:r.start,end:r.start+r.duration};if(!t)return i;if(!n&&e.segmentAvailabilityRange)return e.segmentAvailabilityRange;var o=e.segmentDuration||(e.segments&&e.segments.length?e.segments[e.segments.length-1].duration:0),a=s(new Date,r),u=r.start+r.duration;i.start=Math.max(a-r.mpd.timeShiftBufferDepth,r.start);var c=void 0!==e.availabilityTimeOffset&&e.availabilityTimeOffset=u&&a-cte.min&&Math.abs(U.getDuration()-te.min)>=1&&M)try{var e=+("0."+((te.min+"").split(".")[1]||0)),t=e<.1?0:e>=.1?.1:+(e-.03).toFixed(2);t&&t>0&&!isNaN(t)&&(M.setMediaDuration(te.min-t),M.setAppendWindowEnd(te.min-t),k.setDeviationReset(!0))}catch(e){}}function ae(){return!!_&&!!G.getElement()}function se(){return Object(ti.a)()}function ue(){if(!T)throw t;return U.isPaused()}function ce(e){var t=[n.VIDEO,n.AUDIO,n.FRAGMENTED_TEXT];if(e)return-1!==t.indexOf(e)?_e().getCurrentBufferLevel(Ee(e))||NaN:(y("Warning - getBufferLength requested for invalid type"),NaN);var r=t.map((function(e){return Te(e).length>0?_e().getCurrentBufferLevel(Ee(e)):Number.MAX_VALUE})).reduce((function(e,t){return Math.min(e,t)}));return r===Number.MAX_VALUE?NaN:r}function le(e){var t=De(),r=U.getLiveDelay();if(!t)return 0;var n=t.range.start+e;return n>t.range.end-r&&(n=t.range.end-r),n}function de(e,r){if(!T)throw t;var n=be().currentTime;if(void 0!==e)n=M.getTimeRelativeToStreamId(n,e);else if(U.getIsDynamic()){var i=De();n=null===i?0:fe()-(i.range.end-i.time)}var o=k.getCachedDurationOffset("time");return r&&n-o>0&&(n-=o,n=Math.min(n,fe(void 0))),parseFloat(n.toFixed(5))}function fe(e){if(!T)throw t;var r=be().duration;if(U.getIsDynamic()){var n,i=De();if(!i)return 0;r=(n=i.range.end-i.range.start)a&&(r=o=t&&Z&&(k.setCachedDurationOffset("time",t),Z=!1,G.setDataOffsetAttr(t),f.trigger(p.APPENDED_NEXT_SOURCE_DURATION_CHANGED,{offset:t,totalTime:e.time,totalDuration:fe(),time:de(void 0,!0),duration:te.min||fe(!0)}))}function ke(e){try{if(e&&e.mediaType&&e.segments){ var t=e.mediaType,r=ee[t],n=e.segments,i=e.representation&&e.representation.index,o=!0,a=[],s={},u=0,c=0;if(r.total&&r.total===n.length&&(r.segments.forEach((function(e,t){e.duration&&e.duration!==n[t].duration&&t100&&a.push(t)})),a.length)){o=!1,s={old:r.qualityIndex,new:i};var l=G.getTime(),d=Math.floor(l/5);-1===a.indexOf(d)&&-1===a.indexOf(d+1)&&-1===a.indexOf(d+2)&&(o=!0)}var f=0,p=e.segments&&e.segments[e.segments.length-1];p&&(f=(p.duration+p.startTime)/p.timescale,te[t]=f,(!te.min||te.min>0&&te.min>f)&&(te.min=f));var h=0;n&&n.length&&(h=n[0].startTime/n[0].timescale,P.setStartOffsetTimeForType(t,h),u=parseInt(n[0].mediaRange.split("-")[0],10),c=parseInt(n[n.length-1].mediaRange.split("-")[1],10),J=Math.max(J,h)),te.min===f&&k.getCachedDurationOffset("duration")&&h>0&&(te.min-=h),k.setCachedDurationOffset("duration",te.min+Y);var y={total:n&&n.length,segments:n,isDurationAllEqual:o,qualityIndex:i,realDuration:f,startOffsetTime:h,rangeStart:u,rangeEnd:c};o||(y.notEqualIndexList=a,y.notEqualQualityIndex=s),ee[e.mediaType]=y,"function"==typeof re&&re(t,y)}}catch(e){throw new Error("Parse segment loaded error, "+e)}}function Le(){K=!0;var e=G.getBufferRange(),t=G.getElement().duration,r=e&&e.length&&e.end(e.length-1),n=G.getTime(),i=[t,r,n+ce("video"),n+ce("audio")],o=i.filter((function(e){return!isNaN(e)})),a=o.reduce((function(e,t){return e+t}))/o.length,s=Math.min.apply(null,o);if(y("onStreamBufferingCompleted",Y,i,k.getAppendingSourceState(),Q),Math.abs(s-a)<1&&(Y=parseFloat(s.toFixed(5)),Q.length)){var u=Q.splice(0,1)[0];y("Appending delay next source",u),Ue(u.manifest,u.preloadData)}}function xe(e){if(U.getEnded()&&k.getAppendingSourceState()){var t=e.sender.getType();if(ne[t]=e.bufferLevel,ne.audio>0&&ne.video>0){var r=G.getBufferRange(),n=r&&r.length&&r.end(r.length-1),i=U.getTime();n>i+.01&&(console.log("Playing next appending source after prev playback ended.",ne),U.seek(i+.01),U.play())}}if(k.getAppendingSourceState()&&z){for(var o=k.getCachedDurationOffset("offset"),a=G.getBufferRange(),s=U.getTime(),u=0,c=0;c=s&&(u=a.end(c));o>0&&u>o+1&&(k.setAppendingSourceState(!1),y("Appended Next Source, currentTime: "+s+" , buffered end: "+u),z=!1,f.trigger(p.APPENDED_NEXT_SOURCE,{time:s,ended:u}))}if(!X){var l=G.getBufferRange(),d=U.getTime(),h=l&&l.length&&l.start(0),m=k.getPlayAt();l&&l.length&&h>=J&&(X=!0,(m||d)3&&void 0!==arguments[3]&&arguments[3],i=new Date(1e3*e),o=i.toLocaleDateString(t),a=i.toLocaleTimeString(t,{hour12:r});return n?a+" "+o:a},getVersion:se,getDebug:function(){return h},getBufferLength:ce,getVideoContainer:function(){return G?G.getVideoContainer():null},getTTMLRenderingDiv:function(){return G?G.getTTMLRenderingDiv():null},getVideoElement:be,getSource:function(){if(!_)throw u;return _},setLiveDelayFragmentCount:function(e){k.setLiveDelayFragmentCount(e)},setLiveDelay:function(e){k.setLiveDelay(e)},getLiveDelay:function(){return k.getLiveDelay()},getCurrentLiveLatency:function(){if(!b)throw c;return T?U.getCurrentLiveLatency():NaN},useSuggestedPresentationDelay:function(e){k.setUseSuggestedPresentationDelay(e)},enableLastBitrateCaching:function(e,t){k.setLastBitrateCachingInfo(e,t)},enableLastMediaSettingsCaching:function(e,t){k.setLastMediaSettingsCachingInfo(e,t)},setMaxAllowedBitrateFor:function(e,t){A.setMaxAllowedBitrateFor(e,t)},getMaxAllowedBitrateFor:function(e){return A.getMaxAllowedBitrateFor(e)},getTopBitrateInfoFor:function(t){if(!S)throw e;return A.getTopBitrateInfoFor(t)},setMinAllowedBitrateFor:function(e,t){A.setMinAllowedBitrateFor(e,t)},getMinAllowedBitrateFor:function(e){return A.getMinAllowedBitrateFor(e)},setMaxAllowedRepresentationRatioFor:function(e,t){A.setMaxAllowedRepresentationRatioFor(e,t)},getMaxAllowedRepresentationRatioFor:function(e){return A.getMaxAllowedRepresentationRatioFor(e)},setAutoPlay:he,getAutoPlay:function(){return w},setScheduleWhilePaused:function(e){k.setScheduleWhilePaused(e)},getScheduleWhilePaused:function(){return k.getScheduleWhilePaused()},getDashMetrics:_e,getMetricsFor:Ee,getQualityFor:function(t){if(!S)throw e;return A.getQualityFor(t,M.getActiveStreamInfo())},setQualityFor:function(t,r){if(!S)throw e;A.setPlaybackQuality(t,M.getActiveStreamInfo(),r)},updatePortalSize:function(){A.setElementSize(),A.setWindowResizeEventCalled(!0)},getLimitBitrateByPortal:function(){return A.getLimitBitrateByPortal()},setLimitBitrateByPortal:function(e){A.setLimitBitrateByPortal(e)},getUsePixelRatioInLimitBitrateByPortal:function(){return A.getUsePixelRatioInLimitBitrateByPortal()}, setUsePixelRatioInLimitBitrateByPortal:function(e){A.setUsePixelRatioInLimitBitrateByPortal(e)},getBitrateInfoListFor:function(t){if(!S)throw e;var r=Oe();return r?r.getBitrateListFor(t):[]},setInitialBitrateFor:function(e,t){A.setInitialBitrateFor(e,t)},getInitialBitrateFor:function(t){if(!S)throw e;return A.getInitialBitrateFor(t)},setInitialRepresentationRatioFor:function(e,t){A.setInitialRepresentationRatioFor(e,t)},getInitialRepresentationRatioFor:function(e){return A.getInitialRepresentationRatioFor(e)},getStreamsFromManifest:function(t){if(!S)throw e;return C.getStreamsInfo(t)},getTracksFor:Te,getTracksForTypeFromManifest:function(t,r,n){if(!S)throw e;return(n=n||C.getStreamsInfo(r,1)[0])?C.getAllMediaInfoForType(n,t,r):[]},getCurrentTrackFor:function(t){if(!S)throw e;var r=M.getActiveStreamInfo();return r?P.getCurrentTrackFor(t,r):null},setInitialMediaSettingsFor:function(e,t){if(!b)throw c;P.setInitialSettings(e,t)},getInitialMediaSettingsFor:function(e){if(!b)throw c;return P.getInitialSettings(e)},setCurrentTrack:function(t){if(!S)throw e;P.setTrack(t)},getTrackSwitchModeFor:function(e){if(!b)throw c;return P.getSwitchMode(e)},setTrackSwitchModeFor:function(e,t){if(!b)throw c;P.setSwitchMode(e,t)},setSelectionModeForInitialTrack:function(e){if(!b)throw c;P.setSelectionModeForInitialTrack(e)},getSelectionModeForInitialTrack:function(){if(!b)throw c;return P.getSelectionModeForInitialTrack()},setFastSwitchEnabled:function(e){k.setFastSwitchEnabled(e)},getFastSwitchEnabled:function(){return k.getFastSwitchEnabled()},setMovingAverageMethod:function(e){e===n.MOVING_AVERAGE_SLIDING_WINDOW||e===n.MOVING_AVERAGE_EWMA?k.setMovingAverageMethod(e):y("Warning: Ignoring setMovingAverageMethod("+e+") - unknown value.")},getMovingAverageMethod:function(){return k.getMovingAverageMethod()},getAutoSwitchQualityFor:function(e){return A.getAutoSwitchBitrateFor(e)},setAutoSwitchQualityFor:function(e,t){A.setAutoSwitchBitrateFor(e,t)},setABRStrategy:function(e){e===n.ABR_STRATEGY_DYNAMIC||e===n.ABR_STRATEGY_BOLA||e===n.ABR_STRATEGY_THROUGHPUT?k.setABRStrategy(e):y("Warning: Ignoring setABRStrategy("+e+") - unknown value.")},getABRStrategy:function(){return k.getABRStrategy()},useDefaultABRRules:function(e){k.setUseDefaultABRRules(e)},addABRCustomRule:function(e,t,r){k.addABRCustomRule(e,t,r)},removeABRCustomRule:function(e){k.removeABRCustomRule(e)},removeAllABRCustomRule:function(){k.removeAllABRCustomRule()},setBandwidthSafetyFactor:function(e){k.setBandwidthSafetyFactor(e)},getBandwidthSafetyFactor:function(){return k.getBandwidthSafetyFactor()},getAverageThroughput:function(e){var t=A.getThroughputHistory();return t?t.getAverageThroughput(e):0},setAbandonLoadTimeout:function(e){k.setAbandonLoadTimeout(e)},retrieveManifest:function(e,t){var r=Re(),n=this;f.on(p.INTERNAL_MANIFEST_LOADED,(function e(i){i.error?t(null,i.error):t(i.manifest),f.off(p.INTERNAL_MANIFEST_LOADED,e,n),r.reset()}),n),Fe(l).getInstance().initialize(e),r.load(e,q)},addUTCTimingSource:ye,removeUTCTimingSource:me,clearDefaultUTCTimingSources:function(){k.setUTCTimingSources([])},restoreDefaultUTCTimingSources:ge,setBufferToKeep:function(e){k.setBufferToKeep(e)},setBufferAheadToKeep:function(e){k.setBufferAheadToKeep(e)},setBufferPruningInterval:function(e){k.setBufferPruningInterval(e)},setStableBufferTime:function(e){k.setStableBufferTime(e)},getStableBufferTime:function(){return k.getStableBufferTime()},setBufferTimeAtTopQuality:function(e){k.setBufferTimeAtTopQuality(e)},getBufferTimeAtTopQuality:function(){return k.getBufferTimeAtTopQuality()},setBufferTimeAtTopQualityLongForm:function(e){k.setBufferTimeAtTopQualityLongForm(e)},getBufferTimeAtTopQualityLongForm:function(){return k.getBufferTimeAtTopQualityLongForm()},setFragmentLoaderRetryAttempts:function(e){k.setFragmentRetryAttempts(e)},setFragmentLoaderRetryInterval:function(e){k.setFragmentRetryInterval(e)},setManifestLoaderRetryAttempts:function(e){k.setManifestRetryAttempts(e)},setManifestLoaderRetryInterval:function(e){k.setManifestRetryInterval(e)},setXHRWithCredentialsForType:function(e,t){k.setXHRWithCredentialsForType(e,t)},getXHRWithCredentialsForType:function(e){return k.getXHRWithCredentialsForType(e)},setJumpGaps:function(e){k.setJumpGaps(e)},getJumpGaps:function(){return k.getJumpGaps()},setSmallGapLimit:function(e){k.setSmallGapLimit(e)},getSmallGapLimit:function(){return k.getSmallGapLimit()},getLowLatencyEnabled:function(){return k.getLowLatencyEnabled()},setLowLatencyEnabled:function(e){return k.setLowLatencyEnabled(e)},setPreloadData:function(e){return k.setPreloadData(e)},setManifestUpdateRetryInterval:function(e){k.setManifestUpdateRetryInterval(e)},getManifestUpdateRetryInterval:function(){return k.getManifestUpdateRetryInterval()},setLongFormContentDurationThreshold:function(e){k.setLongFormContentDurationThreshold(e)},setSegmentOverlapToleranceTime:function(e){k.setSegmentOverlapToleranceTime(e)},setCacheLoadThresholdForType:function(e,t){k.setCacheLoadThresholdForType(e,t)},getProtectionController:function(){return Pe()},attachProtectionController:function(e){D=e},setProtectionData:function(e){E=e,M&&M.setProtectionData(E)},enableManifestDateHeaderTimeSource:function(e){k.setUseManifestDateHeaderTimeSource(e)},attachVideoContainer:function(e){if(!G.getElement())throw r;G.setVideoContainer(e)},attachTTMLRenderingDiv:function(e){if(!G.getElement())throw r;G.setTTMLRenderingDiv(e)},getUseDeadTimeLatencyForAbr:function(){return A.getUseDeadTimeLatency()},setUseDeadTimeLatencyForAbr:function(e){A.setUseDeadTimeLatency(e)},getThumbnail:function(e){if(e<0)return null;var t=U.getIsDynamic()?le(e):e,r=M.getStreamForTime(t);if(null===r)return null;var n=r.getThumbnailController(),i=r.getStreamInfo();if(!n||!i)return null;var o=M.getTimeRelativeToStreamId(t,r.getId());return n.get(o)},setDefaultQualityFor:function(e,t){A||(A=oe(l).getInstance()),A.setDefaultQualityFor(e,t)},getDefaultQualityFor:function(e){return A.getDefaultQualityFor(e)},getDroppedFramesInfo:function(){return A.getDroppedFramesInfo()},getCurrentSegmentInfoFor:function(e){return A.getCurrentSegmentInfoFor(e)},onInitSegmentsLoaded:function(e){re=e},getThroughputDict:function(e){var t=A.getThroughputHistory();return t?t.getDict(e):{}},getInitializeDate:Me,updateSource:function(e){if(!b)throw c;"string"==typeof e&&Fe(l).getInstance().initialize(e),ee.video={},ee.audio={},_=e,M.load(_,q)},switchSource:function(e){if(!b)throw c;"string"==typeof e&&Fe(l).getInstance().initialize(e),ee.video={},ee.audio={},te.min=0,te.video=0,te.audio=0,_=e,Y=0,G.setDataOffsetAttr(0),k.setCachedDurationOffset("offset",0),k.setCachedDurationOffset("time",0),k.setCachedDurationOffset("duration",0),k.setAppendingSourceState(!1),k.resetStartAndSeekTime(),Z=!1,Q=[],(S||T)&&Ae(),ae()&&Ce()},appendSource:Ue,setEndOfStreamState:function(e){k&&k.setEndOfStreamState(e)},setP2pPermission:function(e){e||k.addBufferingCount()},setP2pType:function(e,t){var r,n=!1;switch(e){case"yf-eg":case"yf-cg2":window.YFDashIO&&(n=!0);break;case"xl-eg":case"xl-cg2":window.xyvp&&(n=!0);break;case"ks-eg":case"ks-cg2":window.KSLoader&&(n=!0);break;case"bili-eg":case"bili-cg2":window.DIYSdk&&(n=!0);break;case"pear-eg":case"pear-cg2":window.PearDownloader&&(n=!0)}n?window.__DASH_P2P_TYPE__=e:t?(r=t,new Promise((function(e,t){var n=document.createElement("script");n.src=r,n.onload=function(){window.clearTimeout(W),e()},n.onerror=function(){window.clearTimeout(W),t(),f.trigger(p.PCDN_LOADED_ERROR,{code:o.DOWNLOAD_PCDN_SCRIPT_ERROR,url:r})},document.body.appendChild(n),W=window.setTimeout((function(){t(),f.trigger(p.PCDN_LOADED_ERROR,{code:o.DOWNLOAD_PCDN_SCRIPT_TIMEOUT,url:r})}),2e3)}))).then((function(){window.__DASH_P2P_TYPE__=e,y("[MediaPlayer] p2p Sdk Scripts Loaded")})).catch((function(){console.warn("load p2p sdk scripts failed")})):console.warn("lack p2p sdk scripts")},setUseNC:function(e){k&&k.setUseNC(e)},setUseP2P:function(e){k&&k.setUseP2P(e)},switchAudioTrack:function(e){return k.getAudioTrackIds().find((function(t){return t.id===e}))?(k.setAudioTrackId(e),f.trigger(p.SWITCH_AUDIO_TRACK_REQUEST,{id:e}),0):-1},getCurrentAudioTrackId:function(){return k.getAudioTrackId()}, -getAudioTrackList:function(){return k.getAudioTrackIds()},getRetryTimes:function(){return k.getRetryTimes()},getSpaceUtilLastSeekInSec:function(){return((new Date).getTime()-k.getLastStartSeekTime())/1e3},setPlayAt:function(e){return k.setPlayAt(e)},reset:function(){we(null),Se(null),E=null,z=!1,Z=!1,Y=0,Q=[],D&&(D.reset(),D=null),I&&(I.reset(),I=null),U&&(U.reset(),U=null),f.off(p.SEGMENTS_LOADED,ke,v),f.off(p.BUFFER_LEVEL_UPDATED,xe,v),f.off(p.PLAYBACK_ENDED,Le,this),f.off(p.STREAM_BUFFERING_COMPLETED,Le,this),f.off(p.PLAYBACK_TIME_UPDATED,Ne,this),f.off(p.PLAYBACK_METADATA_LOADED,ie,this)},setQualityMap:function(e){},setPlayerId:function(e){k.setPlayerId(e)}},function(){b=!1,T=!1,S=!1,w=!0,D=null,E=null,C=null,p.extend(hr),k=pr(l).getInstance(),G=Yn(l).getInstance(),Y=0,K=!1,Q=[],ne={video:0,audio:0},J=0,f.on(p.SEGMENTS_LOADED,ke,v),f.on(p.BUFFER_LEVEL_UPDATED,xe,v),f.on(p.PLAYBACK_ENDED,Le,this),f.on(p.STREAM_BUFFERING_COMPLETED,Le,this),f.on(p.PLAYBACK_TIME_UPDATED,Ne,this),f.on(p.PLAYBACK_METADATA_LOADED,ie,this)}(),v}Ai.__dashjs_factory_name="MediaPlayer";var Ri=s.a.getClassFactory(Ai);Ri.events=hr,s.a.updateClassFactory(Ai.__dashjs_factory_name,Ri),t.a=Ri},function(e,t,r){var n,i,o,u,c,l=function(e){for(var t=[],r=0;r>6),t.push(128|63&n)):n<65536?(t.push(224|n>>12),t.push(128|63&n>>6),t.push(128|63&n)):(t.push(240|n>>18),t.push(128|63&n>>12),t.push(128|63&n>>6),t.push(128|63&n))}return t},d=function(e){for(var t=[],r=0;r>18)),r.push(n.charAt(63&o>>12)),r.push(n.charAt(63&o>>6)),r.push(n.charAt(63&o))}return 2==e.length-t?(o=(e[t]<<16)+(e[t+1]<<8),r.push(n.charAt(63&o>>18)),r.push(n.charAt(63&o>>12)),r.push(n.charAt(63&o>>6)),r.push("=")):1==e.length-t&&(o=e[t]<<16,r.push(n.charAt(63&o>>18)),r.push(n.charAt(63&o>>12)),r.push("==")),r.join("")},o=function(){for(var e=[],t=0;t<64;++t)e[n.charCodeAt(t)]=t;return e["=".charCodeAt(0)]=0,e}(),u=function(e){for(var t=0,r=[],n=0|e.length/4;0>16),r.push(255&i>>8),r.push(255&i),t+=4}return r&&("="==e.charAt(t-2)?(r.pop(),r.pop()):"="==e.charAt(t-1)&&r.pop()),r},c={encode:function(e){for(var t=[],r=0;r>6),t.push(128|63&n)):n<65536?(t.push(224|n>>12),t.push(128|63&n>>6),t.push(128|63&n)):(t.push(240|n>>18),t.push(128|63&n>>12),t.push(128|63&n>>6),t.push(128|63&n))}return t},d=function(e){for(var t=[],r=0;r>18)),r.push(n.charAt(63&o>>12)),r.push(n.charAt(63&o>>6)),r.push(n.charAt(63&o))}return 2==e.length-t?(o=(e[t]<<16)+(e[t+1]<<8),r.push(n.charAt(63&o>>18)),r.push(n.charAt(63&o>>12)),r.push(n.charAt(63&o>>6)),r.push("=")):1==e.length-t&&(o=e[t]<<16,r.push(n.charAt(63&o>>18)),r.push(n.charAt(63&o>>12)),r.push("==")),r.join("")},o=function(){for(var e=[],t=0;t<64;++t)e[n.charCodeAt(t)]=t;return e["=".charCodeAt(0)]=0,e}(),u=function(e){for(var t=0,r=[],n=0|e.length/4;0>16),r.push(255&i>>8),r.push(255&i),t+=4}return r&&("="==e.charAt(t-2)?(r.pop(),r.pop()):"="==e.charAt(t-1)&&r.pop()),r},c={encode:function(e){for(var t=[],r=0;r=0&&this.events[e].splice(r,1)}}else this.events[e]=[]},e.prototype.fire=function(e,t){if(e){var r=this.events[e];r&&r.length&&r.forEach((function(e){"function"==typeof e&&e.call(null,t)}))}},e.prototype.play=function(){try{return this.player.play()}catch(e){this.error({msg:"Call play function error",detail:e})}},e.prototype.pause=function(){try{this.player.pause()}catch(e){this.error({msg:"Call pause function error",detail:e})}},e.prototype.getCurrentTime=function(e){void 0===e&&(e=!0);try{return this.player.time(void 0,e)}catch(e){this.error({type:p.STRING.WARNING,msg:"Call getCurrentTime function error",detail:e})}return 0},e.prototype.getBufferedTime=function(){this.getCurrentTime();var e=this.video;try{e.buffered}catch(e){this.error({msg:"Call getBufferedTime function error",detail:e})}return 0},e.prototype.getDuration=function(e){void 0===e&&(e=!0);try{return this.player.duration(e)}catch(e){this.error({msg:"Call getDuration function error",detail:e})}},e.prototype.seek=function(e,t){var r=this;return void 0===t&&(t=!1),new Promise((function(n,i){if(isNaN(parseInt(""+e,10))||e<0)i({msg:"Seek time value error, time: "+e});else if(r.state.appendingSourceState)i({msg:"Call seek function error while appending source, time: "+e});else try{var o=r.getCurrentTime(),a=Date.now();r.changePlaybackState(y.BUFFERING.PLAYBACK.SEEKING);var s=r.getStartOffsetTime();eu-.6&&(e=u-.6),r.player.seek(e,t),r.player.on(p.EVENTS.PLAYBACK_SEEKED,(function i(){r.changePlaybackState(y.BUFFERING.PLAYBACK.PLAYING).player.off(p.EVENTS.PLAYBACK_SEEKED,i),n({cost:Math.floor(Date.now()-a),currentTime:o,seekedTime:e,isRelative:t})}))}catch(e){r.error({msg:"Call seek function error",detail:e})}}))},e.prototype.getCorePlayer=function(){return this.player},e.prototype.setVolume=function(e){if(isNaN(parseInt(""+e,10))||e<0||e>1)this.error({msg:"SetVolume value error, value: "+e});else try{this.player.setVolume(e)}catch(e){this.error({msg:"Call setVolume function error",detail:e})}},e.prototype.getVolume=function(){try{return this.player.getVolume()}catch(e){this.error({msg:"Call getVolume function error",detail:e})}},e.prototype.setPlaybackRate=function(e){try{this.player.setPlaybackRate(e)}catch(e){this.error({msg:"Call setPlaybackRate function error",detail:e})}},e.prototype.getPlaybackRate=function(){try{return this.player.getPlaybackRate()}catch(e){this.error({msg:"Call getPlaybackRate function error",detail:e})}},e.prototype.getAutoSwitchQualityFor=function(e){try{return this.player.getAutoSwitchQualityFor(e)}catch(e){this.error({msg:"Call getAutoSwitchQualityFor function error",detail:e})}},e.prototype.setAutoSwitchQualityFor=function(e,t,r){void 0===t&&(t=!1),void 0===r&&(r=!0);try{this.state.isAutoSwitch&&r&&(this.state.isAutoSwitch[e]=t),t||this.setMaxAllowedBitrateFor(e,0),this.player.setAutoSwitchQualityFor(e,t)}catch(e){this.error({msg:"Call setAutoSwitchQualityFor function error",detail:e})}return this},e.prototype.getAudioQuallityCanBeUsed=function(e){return e===p.DOLBY_AUDIO_QN.AUDIO||e===p.DOLBY_AUDIO_QN.AMTOS?this.checkAudioCodecCanUseByType(p.SPECIAL_AUDIO_TYPE.DOLBY)&&e||30280:e===p.FLAC_QN?this.checkAudioCodecCanUseByType(p.SPECIAL_AUDIO_TYPE.FLAC)&&e||30280:e},e.prototype.getQualityList=function(e){try{return this.player.getBitrateInfoListFor(e)}catch(e){this.error({msg:"Call getQualityList function error",detail:e})}},e.prototype.setDefaultQualityFor=function(e,t){void 0===t&&(t=0);var r=this.getQualityIndexFromQualityNumber(t,e);try{var n=this.state.mpd&&this.state.mpd[e];if(!n)return;var i=n.length;i&&(r=Math.min(r,i-1)),r>=0?(this.state.currentQualityIndex[e]=r,this.player.setDefaultQualityFor(e,r)):this.error({msg:"Call setDefaultQualityFor function error, quality: "+t})}catch(e){this.error({msg:"Call setDefaultQualityFor function error, quality: "+t+", qualityIndex = "+r,detail:e})}},e.prototype.setAutoSwitchTopQualityFor=function(e,t){var r=this;try{if(!this.state.initialized)return void this.on(p.EVENTS.STREAM_INITIALIZED,(function n(){r.off(p.EVENTS.STREAM_INITIALIZED,n),r.setAutoSwitchTopQualityFor(e,t)}));if(!t&&0!==t)return void this.setMaxAllowedBitrateFor(e,0);if(!this.state.isAutoSwitch||!this.state.isAutoSwitch[e])return;var n=this.getQualityIndexFromQualityNumber(t,e),i=this.getQualityList(e),o=i&&i[n],a=Math.floor(o.bitrate/1e3)+1;this.state.autoSwitchTopQualityMap[e]=n,this.setMaxAllowedBitrateFor(e,a)}catch(e){this.error({msg:"Call setAutoSwitchTopQualityFor function error",detail:e})}},e.prototype.setQualityFor=function(e,t,r){var n=this;return void 0===r&&(r=20),new Promise((function(i,o){if(isNaN(parseInt(""+t,10))||t<0)o({msg:"Set quality value error, quality: "+t,code:p.ERROR_CODE.SET_QUALITY_VALUE_ERR});else if(n.detectQualityTypeIsLegal(e))o({msg:"Set quality type error, type: "+e,code:p.ERROR_CODE.SET_QUALITY_TYPE_ERR});else if(n.state.appendingSourceState)o({msg:"Reject setQualityFor by appending next source.",code:p.ERROR_CODE.SET_QUALITY_APENDING_ERR});else if("audio"===e&&n.state.enableMultiAudioTracks)o({msg:"MultiAudioTrack disable change Qn",code:p.ERROR_CODE.SET_QUALITY_MULTI_TRACK});else if("audio"===e&&n.getAutoSwitchQualityFor("video"))o({msg:"abr disable change Qn",code:p.ERROR_CODE.SET_QUALITY_ABR});else try{var a=t;if((t=n.getQualityIndexFromQualityNumber(t,e))===n.state.currentQualityIndex[e])return void o({msg:"Set quality equal current quality, quality: "+t,code:p.ERROR_CODE.SET_QUALITY_SAME_VALUE,isChange:a!==n.getQualityNumberFromQualityIndex(t)&&t!==a});n.player.setQualityFor(e,t);var s=Date.now(),u=function(e){-1!==n.timer.qnChangeTimeOutFlag[e]&&(clearTimeout(n.timer.qnChangeTimeOutFlag[e]),n.timer.qnChangeTimeOutFlag[e]=-1),n.qnSwitchingInfo[e]={}};u(e);var c=1e3*r;n.timer.qnChangeTimeOutFlag[e]=window.setTimeout((function(){n.player.off(p.EVENTS.QUALITY_CHANGE_RENDERED,l),o({msg:"Call setQualityFor timeout",code:p.ERROR_CODE.SET_QUALITY_TIME_OUT}),u(e);var t=n.state.currentQualityIndex[e];n.player.setQualityFor(e,t)}),c);var l=function r(c){if(c.mediaType===e){var l=c.newQuality;if(u(c.mediaType),n.player.off(p.EVENTS.QUALITY_CHANGE_RENDERED,r),l===t){var d=n.getQualityNumberFromQualityIndex(l,e);l!==a&&a!==d?(o({msg:"Quality rendered and quality is not "+a+" but "+d,code:p.ERROR_CODE.SET_QUALITY_RENDER_CHANGED,newQuality:l,newQnNumber:d}),n.fire(p.EVENTS.QUALITY_CHANGE_ABNORMAL,{type:p.EVENTS.QUALITY_CHANGE_ABNORMAL,mediaType:e,defaultQuality:a,currentIndex:l,qualityNumber:d})):i(n.getQualityChangedData(c,s))}}};n.qnSwitchingInfo[e]={switching:!0,oQn:a,qn:t,reject:o,resolve:i,listener:l},n.player.on(p.EVENTS.QUALITY_CHANGE_RENDERED,l)}catch(e){o({msg:"Call setQualityFor function error",code:p.ERROR_CODE.SET_QUALITY_ERR,detail:e})}}))},e.prototype.getQualityFor=function(e){try{return this.player.getQualityFor(e)}catch(e){ this.error({msg:"Call getQualityFor function error",detail:e})}},e.prototype.setStableBufferTime=function(e){void 0===e&&(e=60);try{this.player.setStableBufferTime(e),this.player.setBufferTimeAtTopQuality(e),this.player.setBufferTimeAtTopQualityLongForm(e),this.player.setBufferAheadToKeep(e+10)}catch(e){this.error({msg:"Call setStableBufferTime function error",detail:e})}},e.prototype.getStableBufferTime=function(){try{return this.player.getStableBufferTime()}catch(e){this.error({msg:"Call getStableBufferTime function error",detail:e})}},e.prototype.getBufferLength=function(e){try{return this.player.getBufferLength(e)}catch(e){this.error({msg:"Call getBufferLength function error",detail:e})}},e.prototype.getCurrentPlayURLFor=function(e){try{return this.player.getCurrentSegmentInfoFor(e).url||""}catch(t){this.error({msg:"Call getCurrentPlayURLFor function error, type: "+e+", qualityIndex: "+this.state.currentQualityIndex[e],detail:t})}},e.prototype.getVideoInfo=function(){return{mediaInfo:this.state.mediaInfo,statisticsInfo:this.state.statisticsInfo}},e.prototype.getCurrentCodecID=function(e){void 0===e&&(e=p.STRING.VIDEO);var t=0;return e===p.STRING.VIDEO&&this.state&&(t=p.VIDEO_CODEC_ID.AVC,this.state.enableAV1&&(t=p.VIDEO_CODEC_ID.AV1),this.state.enableHEVC&&(t=p.VIDEO_CODEC_ID.HEVC)),t},e.prototype.getAverageConnectionSpeed=function(e){void 0===e&&(e=p.STRING.VIDEO);try{return Number(this.player.getAverageThroughput(e))||0}catch(e){this.error({msg:"Call getAverageConnectionSpeed function error",detail:e})}},e.prototype.switchSource=function(e,t,r,n){var i=this;this.onDashJSLog({type:"switchsource",message:"start switchsource"+this.id}),"function"==typeof this.switchSourcePromise.reject&&this.switchSourcePromise.reject({code:p.ERROR_CODE.SWITCH_SOURCE_ABORT,msg:"abort switch source"}),this.player.off(p.EVENTS.STREAM_INITIALIZED,this.onSiwtchSourceInitialized,this);var o=n||{},a=o.playAt,s=o.keepTransition,u=o.autoPlay;if(window.clearTimeout(this.timer.removeSwitchSourceTransition),window.clearTimeout(this.timer.switchSourceTimer),s||this.onVideoSwitchSourceEnded(),t&&("string"!=typeof t.videoid||"string"!=typeof t.audioid?this.error({type:p.STRING.WARNING,msg:"Preload data videoid/audioid value must be string type.",detail:t}):this.player&&this.player.setPreloadData(t)),this.player&&this.player.setPlayAt(a||0),this.state&&(this.state.playAt=a),r){if(!this.getTransitionCanvas()||!s){var c=document.createElement("canvas"),l=this.video.offsetWidth,d=this.video.offsetHeight,f=this.video.videoWidth,h=this.video.videoHeight,y=f/h,m=l/d;c.width=f,c.height=h,c.style.zIndex="10",c.style.position="absolute",c.style.top="50%",c.style.left="50%",c.style.transform="translate(-50%, -50%)",c.className=p.STRING.SWITCH_SOURCE_TRANSITION_ELEMENT_CLASS_NAME;var g=c.getContext("2d"),v=void 0,_=void 0;m>=y?(_=d,v=d/h*f):(v=l,_=l/f*h),c.style.width=(v/l*100).toFixed(2)+"%",c.style.height=(_/d*100).toFixed(2)+"%",g.fillRect(0,0,c.width,c.height),g.drawImage(this.video,0,0,c.width,c.height),this.video.parentNode.appendChild(c),this.video.style.visibility="hidden",c=null,g=null}this.timer.removeSwitchSourceTransition=window.setTimeout((function(){s||i.onVideoSwitchSourceEnded()}),1e4),this.video.removeEventListener("canplay",this.onVideoSwitchSourceEnded),this.video.addEventListener("canplay",this.onVideoSwitchSourceEnded)}return new Promise((function(t,r){try{e=i.filterManifest({manifest:e,state:i.state}).manifest,i.state.mpd=e,i.player?(["video","audio"].forEach((function(e){var t=i.qnSwitchingInfo[e];t&&t.switching?(clearTimeout(i.timer.qnChangeTimeOutFlag[e]),i.setDefaultQualityFor(e,t.qn),i.player.off(p.EVENTS.QUALITY_CHANGE_RENDERED,t.listener)):i.setDefaultQualityFor(e,i.state.currentQualityIndex[e])})),i.state.defaultVideoQuality=i.state.currentQualityIndex.video,i.state.defaultAudioQuality=i.state.currentQualityIndex.audio,i.bindSwitchSourceEvents(t,r,1e4,u),i.player.switchSource(e)):r({code:p.ERROR_CODE.SWITCH_SOURCE_FAILED,msg:"switch source failed"})}catch(e){r({code:p.ERROR_CODE.SWITCH_SOURCE_ERR,msg:e})}}))},e.prototype.updateSource=function(e){var t=this;return new Promise((function(r,n){try{e=t.filterManifest({manifest:e,state:t.state}).manifest,t.player.updateSource(e),t.timer.updateSourceTimer=window.setTimeout((function(){n({msg:"Update Source Timeout"})}),5e3),t.player.on(p.EVENTS.STREAM_INITIALIZED,(function e(){t.player.off(p.EVENTS.STREAM_INITIALIZED,e),clearTimeout(t.timer.updateSourceTimer),r(null)}))}catch(e){n({msg:"Call updateSource function error, "+JSON.stringify(e),detail:e})}}))},e.prototype.appendSource=function(e,t){var r=this,n=this.state.appendingSourceState,i=this.state;return new Promise((function(o,a){n&&a("Dash Player is Appending Next Source!"),r.setAutoSwitchQualityFor("video",!1,!1),r.setAutoSwitchQualityFor("audio",!1,!1);var s=Date.now();t&&("string"!=typeof t.videoid||"string"!=typeof t.audioid?r.error({type:p.STRING.WARNING,msg:"Preload data videoid/audioid value must be string type.",detail:t}):r.player&&r.player.setPreloadData(t)),r.timer.appendSourceTimer=window.setTimeout((function(){r.state.appendingSourceState=!1,a({msg:"Append source time out."})}),5e3),r.state.appendingSourceState=!0;try{if(r.player){for(var u in r.state.currentQualityIndex)r.state.currentQualityIndex[u]>=0&&r.setDefaultQualityFor(u,r.state.currentQualityIndex[u]);e=r.filterManifest({manifest:e,state:r.state}).manifest,r.player.setEndOfStreamState(!1),r.player.appendSource(e),r.player.on(p.EVENTS.APPENDED_NEXT_SOURCE,(function e(t){clearTimeout(r.timer.appendSourceTimer),r.player.off(p.EVENTS.APPENDED_NEXT_SOURCE,e),o({cost:Date.now()-s})})),r.player.on(p.EVENTS.APPENDED_NEXT_SOURCE_DURATION_CHANGED,(function e(t){r.player.off(p.EVENTS.APPENDED_NEXT_SOURCE_DURATION_CHANGED,e),r.state.appendingSourceState=!1,i.autoSwitchTopQualityMap.video>=0&&r.setAutoSwitchTopQualityFor("video",i.autoSwitchTopQualityMap.video),r.setAutoSwitchQualityFor("video",i.isAutoSwitch[p.STRING.VIDEO],!1),r.setAutoSwitchQualityFor("audio",i.isAutoSwitch[p.STRING.AUDIO],!1)}))}else clearTimeout(r.timer.appendSourceTimer),r.state.appendingSourceState=!1,a({msg:"Missing dash player instance!"})}catch(e){clearTimeout(r.timer.appendSourceTimer),r.state.appendingSourceState=!1,a({msg:"Append Source Error",detail:e})}}))},e.prototype.getNextFragmentHistoryInfo=function(){var e="";try{var t=function(e){return e?1:0};if(this.state.getNextFragmentHistory){var r=function(r){var i=n.state.getNextFragmentHistory[r],o="";i.forEach((function(e){var r,n,i=[t(e.final),e.currentTime,e.bufferLevel.toFixed(3),(r=[e.bufferResetInProgress,e.isReplacement,e.switchTrack,e.hasTopQualityChanged,e.lastInitQuality,e.bufferLevelRule],n="",r.length&&r.forEach((function(e){n+=""+t(e)})),parseInt(n,2))];o+=i.join(",")+";"})),e+=r+":"+o+"!"},n=this;for(var i in this.state.getNextFragmentHistory)r(i)}}catch(e){}return e},e.prototype.isLoadingFragment=function(e){if(e)return!!this.state.fragmentLoadingInfo[e]},e.prototype.getNetworkActivity=function(){return this.components.networkActivity&&this.components.networkActivity.getNetworkActivity()},e.prototype.getNetworkActivityHistory=function(e){return void 0===e&&(e=0),this.components.networkActivity&&this.components.networkActivity.getNetworkActivityHistory(e)},e.prototype.setEndOfStreamState=function(e){void 0===e&&(e=!1);try{return this.player.setEndOfStreamState(e)}catch(e){this.error({msg:"Call setEndOfStreamState function error",detail:e})}},e.prototype.getLogHistory=function(){var e=this.state&&this.state.logHistory||[],t=e.join("\n");return{length:e.length,log:t,size:function(e){for(var t=[],r=[e],n=0;r.length;){var i=r.pop();if("boolean"==typeof i)n+=4;else if("string"==typeof i)n+=2*i.length;else if("number"==typeof i)n+=8;else if("object"==c(i)&&-1===t.indexOf(i))for(var o in t.push(i),i)r.push(i[o])}return n}(t)}},e.prototype.getPerformanceInfo=function(e){void 0===e&&(e=!1);var t=this.getDroppedFramesInfo();return this.components.performanceInfo.getInfo(t,e)},e.prototype.getDisableLoaderType=function(){ -return this.components.ncDisableStrategy.isUseNC?this.components.ncDisableStrategy.isUseP2P?p.DISABLE_LOADER_TYPE.NONE:p.DISABLE_LOADER_TYPE.DISABLE_RP:p.DISABLE_LOADER_TYPE.DISABLE_NC},e.prototype.getBufferingInfo=function(e){var t;void 0===e&&(e=!0);var r={type:-1};try{var i=this.state.currentQualityIndex.video,o=this.state.currentQualityIndex.audio;r=this.components.bufferingInfo&&this.components.bufferingInfo.getInfo({videoBufferLength:this.getBufferLength("video"),audioBufferLength:this.getBufferLength("audio"),videoCurrentIndex:i,audioCurrentIndex:o,videoCurrentQn:this.getQualityNumberFromQualityIndex(i,p.STRING.VIDEO),audioCurrentQn:this.getQualityNumberFromQualityIndex(o,p.STRING.AUDIO),videoSpeed:this.getAverageConnectionSpeed(p.STRING.VIDEO),audioSpeed:this.getAverageConnectionSpeed(p.STRING.AUDIO),networkActivity:this.getNetworkActivityHistory(5),playbackState:this.state.playbackState,latestPlayurlErrorHTTPCode:this.state.latestPlayurlErrorHTTPCode,fragmentLoadingInfo:this.state.fragmentLoadingInfo,networkWarningHistory:this.state.networkWarningHistory,startOffsetTime:this.state.startOffsetTime,retryTimes:this.player.getRetryTimes(),seekSpace:this.player.getSpaceUtilLastSeekInSec()}),r=n(n({},r),this.getPerformanceInfo(!0)),null===(t=this.components)||void 0===t||t.bufferingStrategy.push(r)}catch(e){throw new Error(e.message)}return e&&this.onDashJSLog({type:"videoBuffering",message:"[videoBuffering]:"+JSON.stringify(r)}),r},e.prototype.getCDNInfo=function(e){if(void 0===e&&(e=!1),this.state){var t=n({},this.state.cdnInfo);return e&&(this.state.cdnInfo={cdn:0,p2p:0,nc:0,pdn:0,ncBr:0,business:0,mcdn:0,bcache:0,vcache:0,ncFail:0,ncGotFail:0}),t}return{}},e.prototype.getCDNInfoDetail=function(e){if(void 0===e&&(e=!1),this.state){var t=n({},this.state.cdnInfoDetail);return e&&(this.state.cdnInfoDetail={}),t}return{}},e.prototype.getRequestedInfoDetail=function(e){if(void 0===e&&(e=!1),this.state){var t=n({},this.state.requestedInfoDetail);return e&&(this.state.requestedInfoDetail={}),t}return{}},e.prototype.getNCInfo=function(e){if(void 0===e&&(e=!1),this.state){var t={};for(var r in this.state.NCInfo)t[r]=n({},this.state.NCInfo[r]);return e&&(this.state.NCInfo={}),t}return{}},e.prototype.switchAudioTrack=function(e){var t=this;return new Promise((function(r,n){try{var i=-1,o="";if(t.state.enableMultiAudioTracks?e===t.getCurrentAudioTrackId()&&(i=p.ERROR_CODE.SWITCH_AUDIO_TRACK_ID_NOT_CHANGED,o="the id which is going to be switched is equal to current"):(i=p.ERROR_CODE.SWITCH_AUDIO_TRACK_NOT_SUPPORT,o="not support multiple audio tracks"),-1!==i)return void n({code:i,msg:o});0!==t.player.switchAudioTrack(e)?n({code:i=p.ERROR_CODE.SWITCH_AUDIO_TRACK_ID_NOT_EXIST,msg:o="the id which is going to be switched is not exist"}):t.once(p.EVENTS.SWITCH_AUDIO_TRACK_RENDERED,(function(e){r(e)}))}catch(e){n({code:p.ERROR_CODE.SWITCH_AUDIO_TRACK_ERR,msg:"switch aduio track failed, "+(e&&e.message)})}}))},e.prototype.getCurrentAudioTrackId=function(){return this.player.getCurrentAudioTrackId()},e.prototype.getAudioTrackList=function(){try{return this.player.getAudioTrackList()}catch(e){return[]}},e.prototype.destroy=function(){try{this.player.off(p.EVENTS.FRAGMENT_LOADING_STARTED,this.onFragmentLoading,this),this.player.off(p.EVENTS.FRAGMENT_LOADING_PROGRESS,this.onFragmentLoading,this),this.player.off(p.EVENTS.FRAGMENT_LOADING_COMPLETED,this.onFragmentLoading,this),this.player.off(p.EVENTS.SWITCH_AUDIO_TRACK_REQUEST,this.onAudioTrackSwitchRequest,this),this.player.off(p.EVENTS.SWITCH_AUDIO_TRACK_RENDERED,this.onAudioTrackSwitchRendered,this),this.player.off(p.EVENTS.LOG,this.onDashJSLog,this),this.player.off(p.EVENTS.PREDICT_SEEK,this.onPredictSeek,this),this.player.off(p.EVENTS.JUMP_MEDIA_GAP,this.onJumpMediaGap,this),this.player.off(p.EVENTS.STREAM_INITIALIZED,this.onSiwtchSourceInitialized,this),this.player&&this.player.reset(),this.onVideoSwitchSourceEnded(),this.switchSourcePromise=null,this.clearAllTimer(),this.events={},this.config=null,this.state=null,this.video=null,this.player=null,this.switchStartTime=null,this.destroyAllComponents(),this.externalMediaLoader=null,this.qnSwitchingInfo=null}catch(e){this.error({msg:"Call destroy function error, "+(e&&e.message),detail:e})}},e.prototype.destroyAllComponents=function(){this.components.networkActivity.destroy(),this.components.networkActivity=null,this.components.bufferingInfo.destroy(),this.components.bufferingInfo=null,this.components.bufferingStrategy.destroy(),this.components.bufferingStrategy=null,this.components.performanceInfo.destroy(),this.components.performanceInfo=null,this.components.ncDisableStrategy.destroy(),this.components.ncDisableStrategy=null,this.components=null},e.prototype.clearAllTimer=function(){var e=this;this.timer&&(Object.keys(this.timer).forEach((function(t){"qnChangeTimeOutFlag"===t?(clearTimeout(e.timer.qnChangeTimeOutFlag.video),clearTimeout(e.timer.qnChangeTimeOutFlag.audio)):clearTimeout(e.timer[t])})),this.timer=null)},e.prototype.mixin=function(e){if(e&&e instanceof Object)for(var t in e)e.hasOwnProperty(t)&&this.state.hasOwnProperty(t)&&(this.state[t]=e[t]);this.state&&this.state.isAutoSwitch||(this.state.isAutoSwitch={video:!1,audio:!1})},e.prototype.loadDashPlayerScript=function(){var e=this,t=this.config.scriptSrc;return new Promise((function(r,n){try{var i=document.createElement("script");i.src=t,i.onload=function(){e.state.onScriptLoading=!1,clearTimeout(e.timer.loadScript),r()},i.onerror=function(r){e.state.onScriptLoading=!1,clearTimeout(e.timer.loadScript),n({msg:"Load dash player script error, src: "+t,detail:r})},document.body.appendChild(i),e.state.onScriptLoading=!0,e.timer.loadScript=window.setTimeout((function(){n({msg:"Call loadDashPlayerScript function timeout"})}),e.config.scriptMaxLoadTime)}catch(e){n({msg:"Call loadDashPlayerScript function error",detail:e})}}))},e.prototype.createPlayer=function(t,r){var n,i=window.dashjs,o=this.state;this.state.defaultVideoQuality=this.getQualityIndexFromQualityNumber(o.defaultVideoQuality,p.STRING.VIDEO),this.state.defaultAudioQuality=this.getQualityIndexFromQualityNumber(o.defaultAudioQuality,p.STRING.AUDIO);try{var a=i.MediaPlayer().create();a.setPlayerId(this.id),o&&o.protectionDataSet&&"function"==typeof a.setProtectionData&&a.setProtectionData(o.protectionDataSet),this.player=a,this.setDebugLog(),this.bindEvents(t),this.state.logHistory.push("Dash player log history, "+JSON.stringify(e.CONFIG)),o.preloadData&&a.setPreloadData(o.preloadData),o.playAt&&a.setPlayAt(o.playAt),a.initialize(this.video,o.mpd,o.isAutoPlay,o.enforceSSLStream,null===(n=this.config)||void 0===n?void 0:n.hid,this.externalMediaLoader,o.ignoreEmeEncryptedEvent,o.enableMultiAudioTracks),a.setABRStrategy(o.abrStrategy||p.STRING.ABR_THROUGHPUT),a.setAutoSwitchQualityFor(p.STRING.VIDEO,o.isAutoSwitch[p.STRING.VIDEO]),a.setAutoSwitchQualityFor(p.STRING.AUDIO,o.isAutoSwitch[p.STRING.AUDIO]),a.setFastSwitchEnabled(!0),a.enableLastBitrateCaching(!1),a.setBufferPruningInterval(20),a.setUseNC(this.components.ncDisableStrategy.isUseNC),a.setUseP2P(this.components.ncDisableStrategy.isUseP2P),o.mpd&&o.mpd.interactive&&a.setEndOfStreamState(!1),a.setJumpGaps(!0),this.setStableBufferTime(this.state.stableBufferTime)}catch(e){r({msg:"Call createPlayer function error",detail:e})}},e.prototype.bindEvents=function(e){var t=this,r=this.player;r.on(p.EVENTS.STREAM_INITIALIZED,(function n(){r.off(p.EVENTS.STREAM_INITIALIZED,n,t),t.state.initialized=!0;var i=t.player.getBitrateInfoListFor("video");e&&e(i),t.fire(p.EVENTS.STREAM_INITIALIZED),t.videoInfoSchedule(),t.detectDefaultQualityChangeAbnormal()}),this),r.on(p.EVENTS.MANIFEST_LOADED,this.parseManifestInfo,this),r.on(p.EVENTS.GET_NEXT_FRAGMENT,this.getNextFragmentInfo,this),r.on(p.EVENTS.FRAGMENT_LOADING_STARTED,this.onFragmentLoading,this),r.on(p.EVENTS.FRAGMENT_LOADING_PROGRESS,this.onFragmentLoading,this),r.on(p.EVENTS.FRAGMENT_LOADING_COMPLETED,this.onFragmentLoading,this),r.on(p.EVENTS.SWITCH_AUDIO_TRACK_REQUEST,this.onAudioTrackSwitchRequest,this),r.on(p.EVENTS.SWITCH_AUDIO_TRACK_RENDERED,this.onAudioTrackSwitchRendered,this), -r.on(p.EVENTS.FRAGMENT_LOADED_ERROR,this.onFragmentLoadingError,this),r.on(p.EVENTS.REQUEST_RETRYED,this.onFragmentLoadingError,this),r.on(p.EVENTS.LOG,this.onDashJSLog,this),r.on(p.EVENTS.PREDICT_SEEK,this.onPredictSeek,this),r.on(p.EVENTS.JUMP_MEDIA_GAP,this.onJumpMediaGap,this),r.onInitSegmentsLoaded(this.onInitSegmentsLoaded.bind(this)),r.on(p.EVENTS.DELAY_TO_APPEND_TO_BUFFER,(function(e){})),r.on(p.EVENTS.START_OFFSET_TIME_WARNING,(function(e){var r=e&&e.time;r&&r>t.state.startOffsetTime&&(t.error({type:p.STRING.WARNING,code:p.ERROR.START_OFFSET_TIME_WARNING,msg:"Start offset time is less than buffer.start(0), "+t.state.startOffsetTime+" < "+r}),t.state.startOffsetTime=r+.2)})),r.on(p.EVENTS.PLAYBACK_METADATA_LOADED,(function e(){r.off(p.EVENTS.PLAYBACK_METADATA_LOADED,e),t.timer.delayStartOffsetHackTimer=window.setTimeout((function(){if(t.video){var e=t.video.buffered,n=e.length&&e.start(0),i=t.video.currentTime;i=400){var c=0;if(a){var l=a.match(/expires=\d+/g),h=a.match(/deadline=\d+/g);c=l&&parseInt(l[0].substr(8),10)||h&&parseInt(h[0].substr(9),10)}c&&this.state.latestPlayurlExpireTimeStamp===c&&this.state.latestPlayurlErrorHTTPCode===s&&(u=!1),this.state.latestPlayurlExpireTimeStamp=c,this.state.latestPlayurlErrorHTTPCode=s}u&&this.error({code:r,msg:s+" downloadError - "+a,detail:e});break;case p.ERROR.MANIFEST_ERROR:case p.ERROR.MANIFEST_TYPE_ERROR:case p.ERROR.MANIFEST_RESOLVE_ERROR:this.error({code:r,msg:n+" - "+i,detail:e});break;case p.ERROR.MEDIA_ERROR:case p.ERROR.MEDIA_ABORTED_ERROR:case p.ERROR.MEDIA_NETWORK_ERROR:case p.ERROR.MEDIA_SUPPORTED_ERROR:case p.ERROR.MEDIA_ENCRYPTED_ERROR:case p.ERROR.MEDIA_UNKNOWN_ERROR:case p.ERROR.MEDIA_DECODE_ERROR:!function(){try{if(r===p.ERROR.MEDIA_DECODE_ERROR&&"audio"===o){var e=t.getQualityNumberFromQualityIndex(t.getQualityFor("audio"),"audio");f.default.saveDecodeErrorMediaQualityNumber("audio",e,p.STRING.BILIBILI_DECODE_ERROR_OBJ)}var n=t.state,i=n.enableAV1,a=n.enableHEVC,s=n.decodeErrorLocalConfig,u=i?"AV1":a?"HEVC":"AVC";if("AVC"===u)return;if(!o||"audio"!==o){var c="enable"+u+"Error";f.default.setSessionStorage(c,"1")}if(s&&s[u]){var l=s[u],h=l.occurTimes,y=l.disableDuration,m=d.getLocalDecodeErrorInfo(u)||{occurTimes:0},g=m.occurTimes,v=m.reactivateTime;if(++g>=h&&!v){var _=y||6048e5;v=(new Date).getTime()+_}d.setLocalDecodeErrorInfo(u,{occurTimes:g,reactivateTime:v})}}catch(e){}}();var y=e.event,m=o?"[DecodeErrorType]"+o+";":n+" - "+y;this.error({code:r,msg:m,detail:e});break;default:this.error({msg:"Unknown error.",detail:e}),console.error(e)}}catch(e){this.error({msg:"Unknown error.",detail:e})}},e.prototype.detectDashJSExists=function(){return!(!window.dashjs||!window.dashjs.MediaPlayer)},e.prototype.detectQualityTypeIsLegal=function(e){return!(e===p.STRING.VIDEO||e===p.STRING.AUDIO)},e.prototype.parseManifestInfo=function(e){var t=this;try{var r=e.data;if(this.state.manifest=r,r&&r.Period){var n={baseUri:r.baseUri,representation:{video:[],audio:[]}},i={video:{},audio:{}},o=r.Period.AdaptationSet_asArray;o.length&&o.forEach((function(e){var r=e.Representation_asArray;if(r){var o="";r instanceof Array||(r=[r]),r.length&&(r.sort((function(e,t){return e.bandwidth-t.bandwidth})),r.forEach((function(r,a){var s=r.frameRate,u=r.mimeType||e.mimeType;try{if(s&&(""+s).indexOf("/")>-1){var c=s.split("/");s=(s=parseInt(c[0],10)/parseInt(c[1],10)).toFixed(3)}}catch(e){t.error({msg:"Parse fps error, fps: "+s,detail:e})}var l={id:r.id,baseURL:r.BaseURL,width:r.width,height:r.height,bandwidth:r.bandwidth,codecs:r.codecs,fps:s,mimeType:u,sar:r.sar,startWithSAP:r.startWithSAP,audioSamplingRate:r.audioSamplingRate} +return this.components.ncDisableStrategy.isUseNC?this.components.ncDisableStrategy.isUseP2P?p.DISABLE_LOADER_TYPE.NONE:p.DISABLE_LOADER_TYPE.DISABLE_RP:p.DISABLE_LOADER_TYPE.DISABLE_NC},e.prototype.getBufferingInfo=function(e){var t;void 0===e&&(e=!0);var r={type:-1};try{var i=this.state.currentQualityIndex.video,o=this.state.currentQualityIndex.audio;r=this.components.bufferingInfo&&this.components.bufferingInfo.getInfo({videoBufferLength:this.getBufferLength("video"),audioBufferLength:this.getBufferLength("audio"),videoCurrentIndex:i,audioCurrentIndex:o,videoCurrentQn:this.getQualityNumberFromQualityIndex(i,p.STRING.VIDEO),audioCurrentQn:this.getQualityNumberFromQualityIndex(o,p.STRING.AUDIO),videoSpeed:this.getAverageConnectionSpeed(p.STRING.VIDEO),audioSpeed:this.getAverageConnectionSpeed(p.STRING.AUDIO),networkActivity:this.getNetworkActivityHistory(5),playbackState:this.state.playbackState,latestPlayurlErrorHTTPCode:this.state.latestPlayurlErrorHTTPCode,fragmentLoadingInfo:this.state.fragmentLoadingInfo,networkWarningHistory:this.state.networkWarningHistory,startOffsetTime:this.state.startOffsetTime,retryTimes:this.player.getRetryTimes(),seekSpace:this.player.getSpaceUtilLastSeekInSec()}),r=n(n({},r),this.getPerformanceInfo(!0)),null===(t=this.components)||void 0===t||t.bufferingStrategy.push(r)}catch(e){throw new Error(e.message)}return e&&this.onDashJSLog({type:"videoBuffering",message:"[videoBuffering]:"+JSON.stringify(r)}),r},e.prototype.getCDNInfo=function(e){if(void 0===e&&(e=!1),this.state){var t=n({},this.state.cdnInfo);return e&&(this.state.cdnInfo={cdn:0,p2p:0,nc:0,pdn:0,ncBr:0,business:0,mcdn:0,bcache:0,vcache:0,ncFail:0,ncGotFail:0}),t}return{}},e.prototype.getCDNInfoDetail=function(e){if(void 0===e&&(e=!1),this.state){var t=n({},this.state.cdnInfoDetail);return e&&(this.state.cdnInfoDetail={}),t}return{}},e.prototype.getRequestedInfoDetail=function(e){if(void 0===e&&(e=!1),this.state){var t=n({},this.state.requestedInfoDetail);return e&&(this.state.requestedInfoDetail={}),t}return{}},e.prototype.getNCInfo=function(e){if(void 0===e&&(e=!1),this.state){var t={};for(var r in this.state.NCInfo)t[r]=n({},this.state.NCInfo[r]);return e&&(this.state.NCInfo={}),t}return{}},e.prototype.switchAudioTrack=function(e){var t=this;return new Promise((function(r,n){try{var i=-1,o="";if(t.state.enableMultiAudioTracks?e===t.getCurrentAudioTrackId()&&(i=p.ERROR_CODE.SWITCH_AUDIO_TRACK_ID_NOT_CHANGED,o="the id which is going to be switched is equal to current"):(i=p.ERROR_CODE.SWITCH_AUDIO_TRACK_NOT_SUPPORT,o="not support multiple audio tracks"),-1!==i)return void n({code:i,msg:o});0!==t.player.switchAudioTrack(e)?n({code:i=p.ERROR_CODE.SWITCH_AUDIO_TRACK_ID_NOT_EXIST,msg:o="the id which is going to be switched is not exist"}):t.once(p.EVENTS.SWITCH_AUDIO_TRACK_RENDERED,(function(e){r(e)}))}catch(e){n({code:p.ERROR_CODE.SWITCH_AUDIO_TRACK_ERR,msg:"switch aduio track failed, "+(e&&e.message)})}}))},e.prototype.getCurrentAudioTrackId=function(){return this.player.getCurrentAudioTrackId()},e.prototype.getAudioTrackList=function(){try{return this.player.getAudioTrackList()}catch(e){return[]}},e.prototype.destroy=function(){try{this.player.off(p.EVENTS.FRAGMENT_LOADING_STARTED,this.onFragmentLoading,this),this.player.off(p.EVENTS.FRAGMENT_LOADING_PROGRESS,this.onFragmentLoading,this),this.player.off(p.EVENTS.FRAGMENT_LOADING_COMPLETED,this.onFragmentLoading,this),this.player.off(p.EVENTS.SWITCH_AUDIO_TRACK_REQUEST,this.onAudioTrackSwitchRequest,this),this.player.off(p.EVENTS.SWITCH_AUDIO_TRACK_RENDERED,this.onAudioTrackSwitchRendered,this),this.player.off(p.EVENTS.LOG,this.onDashJSLog,this),this.player.off(p.EVENTS.PREDICT_SEEK,this.onPredictSeek,this),this.player.off(p.EVENTS.JUMP_MEDIA_GAP,this.onJumpMediaGap,this),this.player.off(p.EVENTS.STREAM_INITIALIZED,this.onSiwtchSourceInitialized,this),this.player&&this.player.reset(),this.onVideoSwitchSourceEnded(),this.switchSourcePromise=null,this.clearAllTimer(),this.events={},this.config=null,this.state=null,this.video=null,this.player=null,this.switchStartTime=null,this.destroyAllComponents(),this.externalMediaLoader=null,this.qnSwitchingInfo=null}catch(e){this.error({msg:"Call destroy function error, "+(e&&e.message),detail:e})}},e.prototype.destroyAllComponents=function(){this.components.networkActivity.destroy(),this.components.networkActivity=null,this.components.bufferingInfo.destroy(),this.components.bufferingInfo=null,this.components.bufferingStrategy.destroy(),this.components.bufferingStrategy=null,this.components.performanceInfo.destroy(),this.components.performanceInfo=null,this.components.ncDisableStrategy.destroy(),this.components.ncDisableStrategy=null,this.components=null},e.prototype.clearAllTimer=function(){var e=this;this.timer&&(Object.keys(this.timer).forEach((function(t){"qnChangeTimeOutFlag"===t?(clearTimeout(e.timer.qnChangeTimeOutFlag.video),clearTimeout(e.timer.qnChangeTimeOutFlag.audio)):clearTimeout(e.timer[t])})),this.timer=null)},e.prototype.mixin=function(e){if(e&&e instanceof Object)for(var t in e)e.hasOwnProperty(t)&&this.state.hasOwnProperty(t)&&(this.state[t]=e[t]);this.state&&this.state.isAutoSwitch||(this.state.isAutoSwitch={video:!1,audio:!1})},e.prototype.loadDashPlayerScript=function(){var e=this,t=this.config.scriptSrc;return new Promise((function(r,n){try{var i=document.createElement("script");i.src=t,i.onload=function(){e.state.onScriptLoading=!1,clearTimeout(e.timer.loadScript),r()},i.onerror=function(r){e.state.onScriptLoading=!1,clearTimeout(e.timer.loadScript),n({msg:"Load dash player script error, src: "+t,detail:r})},document.body.appendChild(i),e.state.onScriptLoading=!0,e.timer.loadScript=window.setTimeout((function(){n({msg:"Call loadDashPlayerScript function timeout"})}),e.config.scriptMaxLoadTime)}catch(e){n({msg:"Call loadDashPlayerScript function error",detail:e})}}))},e.prototype.createPlayer=function(t,r){var n,i=dashjs,o=this.state;this.state.defaultVideoQuality=this.getQualityIndexFromQualityNumber(o.defaultVideoQuality,p.STRING.VIDEO),this.state.defaultAudioQuality=this.getQualityIndexFromQualityNumber(o.defaultAudioQuality,p.STRING.AUDIO);try{var a=i.MediaPlayer().create();a.setPlayerId(this.id),o&&o.protectionDataSet&&"function"==typeof a.setProtectionData&&a.setProtectionData(o.protectionDataSet),this.player=a,this.setDebugLog(),this.bindEvents(t),this.state.logHistory.push("Dash player log history, "+JSON.stringify(e.CONFIG)),o.preloadData&&a.setPreloadData(o.preloadData),o.playAt&&a.setPlayAt(o.playAt),a.initialize(this.video,o.mpd,o.isAutoPlay,o.enforceSSLStream,null===(n=this.config)||void 0===n?void 0:n.hid,this.externalMediaLoader,o.ignoreEmeEncryptedEvent,o.enableMultiAudioTracks),a.setABRStrategy(o.abrStrategy||p.STRING.ABR_THROUGHPUT),a.setAutoSwitchQualityFor(p.STRING.VIDEO,o.isAutoSwitch[p.STRING.VIDEO]),a.setAutoSwitchQualityFor(p.STRING.AUDIO,o.isAutoSwitch[p.STRING.AUDIO]),a.setFastSwitchEnabled(!0),a.enableLastBitrateCaching(!1),a.setBufferPruningInterval(20),a.setUseNC(this.components.ncDisableStrategy.isUseNC),a.setUseP2P(this.components.ncDisableStrategy.isUseP2P),o.mpd&&o.mpd.interactive&&a.setEndOfStreamState(!1),a.setJumpGaps(!0),this.setStableBufferTime(this.state.stableBufferTime)}catch(e){r({msg:"Call createPlayer function error",detail:e})}},e.prototype.bindEvents=function(e){var t=this,r=this.player;r.on(p.EVENTS.STREAM_INITIALIZED,(function n(){r.off(p.EVENTS.STREAM_INITIALIZED,n,t),t.state.initialized=!0;var i=t.player.getBitrateInfoListFor("video");e&&e(i),t.fire(p.EVENTS.STREAM_INITIALIZED),t.videoInfoSchedule(),t.detectDefaultQualityChangeAbnormal()}),this),r.on(p.EVENTS.MANIFEST_LOADED,this.parseManifestInfo,this),r.on(p.EVENTS.GET_NEXT_FRAGMENT,this.getNextFragmentInfo,this),r.on(p.EVENTS.FRAGMENT_LOADING_STARTED,this.onFragmentLoading,this),r.on(p.EVENTS.FRAGMENT_LOADING_PROGRESS,this.onFragmentLoading,this),r.on(p.EVENTS.FRAGMENT_LOADING_COMPLETED,this.onFragmentLoading,this),r.on(p.EVENTS.SWITCH_AUDIO_TRACK_REQUEST,this.onAudioTrackSwitchRequest,this),r.on(p.EVENTS.SWITCH_AUDIO_TRACK_RENDERED,this.onAudioTrackSwitchRendered,this), +r.on(p.EVENTS.FRAGMENT_LOADED_ERROR,this.onFragmentLoadingError,this),r.on(p.EVENTS.REQUEST_RETRYED,this.onFragmentLoadingError,this),r.on(p.EVENTS.LOG,this.onDashJSLog,this),r.on(p.EVENTS.PREDICT_SEEK,this.onPredictSeek,this),r.on(p.EVENTS.JUMP_MEDIA_GAP,this.onJumpMediaGap,this),r.onInitSegmentsLoaded(this.onInitSegmentsLoaded.bind(this)),r.on(p.EVENTS.DELAY_TO_APPEND_TO_BUFFER,(function(e){})),r.on(p.EVENTS.START_OFFSET_TIME_WARNING,(function(e){var r=e&&e.time;r&&r>t.state.startOffsetTime&&(t.error({type:p.STRING.WARNING,code:p.ERROR.START_OFFSET_TIME_WARNING,msg:"Start offset time is less than buffer.start(0), "+t.state.startOffsetTime+" < "+r}),t.state.startOffsetTime=r+.2)})),r.on(p.EVENTS.PLAYBACK_METADATA_LOADED,(function e(){r.off(p.EVENTS.PLAYBACK_METADATA_LOADED,e),t.timer.delayStartOffsetHackTimer=window.setTimeout((function(){if(t.video){var e=t.video.buffered,n=e.length&&e.start(0),i=t.video.currentTime;i=400){var c=0;if(a){var l=a.match(/expires=\d+/g),h=a.match(/deadline=\d+/g);c=l&&parseInt(l[0].substr(8),10)||h&&parseInt(h[0].substr(9),10)}c&&this.state.latestPlayurlExpireTimeStamp===c&&this.state.latestPlayurlErrorHTTPCode===s&&(u=!1),this.state.latestPlayurlExpireTimeStamp=c,this.state.latestPlayurlErrorHTTPCode=s}u&&this.error({code:r,msg:s+" downloadError - "+a,detail:e});break;case p.ERROR.MANIFEST_ERROR:case p.ERROR.MANIFEST_TYPE_ERROR:case p.ERROR.MANIFEST_RESOLVE_ERROR:this.error({code:r,msg:n+" - "+i,detail:e});break;case p.ERROR.MEDIA_ERROR:case p.ERROR.MEDIA_ABORTED_ERROR:case p.ERROR.MEDIA_NETWORK_ERROR:case p.ERROR.MEDIA_SUPPORTED_ERROR:case p.ERROR.MEDIA_ENCRYPTED_ERROR:case p.ERROR.MEDIA_UNKNOWN_ERROR:case p.ERROR.MEDIA_DECODE_ERROR:!function(){try{if(r===p.ERROR.MEDIA_DECODE_ERROR&&"audio"===o){var e=t.getQualityNumberFromQualityIndex(t.getQualityFor("audio"),"audio");f.default.saveDecodeErrorMediaQualityNumber("audio",e,p.STRING.BILIBILI_DECODE_ERROR_OBJ)}var n=t.state,i=n.enableAV1,a=n.enableHEVC,s=n.decodeErrorLocalConfig,u=i?"AV1":a?"HEVC":"AVC";if("AVC"===u)return;if(!o||"audio"!==o){var c="enable"+u+"Error";f.default.setSessionStorage(c,"1")}if(s&&s[u]){var l=s[u],h=l.occurTimes,y=l.disableDuration,m=d.getLocalDecodeErrorInfo(u)||{occurTimes:0},g=m.occurTimes,v=m.reactivateTime;if(++g>=h&&!v){var _=y||6048e5;v=(new Date).getTime()+_}d.setLocalDecodeErrorInfo(u,{occurTimes:g,reactivateTime:v})}}catch(e){}}();var y=e.event,m=o?"[DecodeErrorType]"+o+";":n+" - "+y;this.error({code:r,msg:m,detail:e});break;default:this.error({msg:"Unknown error.",detail:e}),console.error(e)}}catch(e){this.error({msg:"Unknown error.",detail:e})}},e.prototype.detectDashJSExists=function(){return!(!dashjs||!dashjs.MediaPlayer)},e.prototype.detectQualityTypeIsLegal=function(e){return!(e===p.STRING.VIDEO||e===p.STRING.AUDIO)},e.prototype.parseManifestInfo=function(e){var t=this;try{var r=e.data;if(this.state.manifest=r,r&&r.Period){var n={baseUri:r.baseUri,representation:{video:[],audio:[]}},i={video:{},audio:{}},o=r.Period.AdaptationSet_asArray;o.length&&o.forEach((function(e){var r=e.Representation_asArray;if(r){var o="";r instanceof Array||(r=[r]),r.length&&(r.sort((function(e,t){return e.bandwidth-t.bandwidth})),r.forEach((function(r,a){var s=r.frameRate,u=r.mimeType||e.mimeType;try{if(s&&(""+s).indexOf("/")>-1){var c=s.split("/");s=(s=parseInt(c[0],10)/parseInt(c[1],10)).toFixed(3)}}catch(e){t.error({msg:"Parse fps error, fps: "+s,detail:e})}var l={id:r.id,baseURL:r.BaseURL,width:r.width,height:r.height,bandwidth:r.bandwidth,codecs:r.codecs,fps:s,mimeType:u,sar:r.sar,startWithSAP:r.startWithSAP,audioSamplingRate:r.audioSamplingRate} ;u.indexOf(p.STRING.VIDEO)>-1?o=p.STRING.VIDEO:u.indexOf(p.STRING.AUDIO)>-1?o=p.STRING.AUDIO:t.error({msg:"Manifest missing mimeType",detail:r}),o&&(function(e){var t,r,n,i=e.width,o=e.height,a=e.id;return t=function(e,t,r,n){return e+';codecs="'+t+'"'+(r&&n?';width="'+r+'";height="'+n+'"':";")}(e.mimeType,e.codecs,i,o),(null===(n=(r=window.MediaSource).isTypeSupported)||void 0===n?void 0:n.call(r,t))&&function(e,t,r){return v.getCurrentBrowserInfo().type!==v.BROWSER_TYPE.Firefox||e!==p.VIDEO_QN.QN_4K||!(!t||!r)&&t<=4096&&r<=2160}(+a,i,o)}(l)||t.isUsingWasmPlayer())?(n.representation[o].push(l),i[o][l.id]=a):t.onDashJSLog({type:"FilterCodecNotSupportedQn",message:"abandon qn: "+l.id})}))),t.state.maxQualityIndex[o]=r.length-1}})),this.state.manifestInfo=n,this.state.qualityNumberMap=i,this.state.setTheDefaultVideoQualityState||(this.state.setTheDefaultVideoQualityState=!0,this.setDefaultQualityFor("video",this.state.defaultVideoQuality),this.setDefaultQualityFor("audio",this.state.defaultAudioQuality))}}catch(t){this.error({msg:"Parse dash player manifest error",detail:e})}},e.prototype.getQualityIndexFromQualityNumber=function(e,t){void 0===t&&(t=p.STRING.VIDEO);var r=0;"audio"===t&&(e=this.qualityNumberDowngrading("audio",e)),t===p.STRING.AUDIO&&[p.DOLBY_AUDIO_QN.AMTOS,p.DOLBY_AUDIO_QN.AUDIO,p.FLAC_QN].includes(e)&&(e=this.getAudioQuallityCanBeUsed(e));try{var n=this.state.qualityNumberMap[t];e=parseInt(""+e,10);var i=this.state.maxQualityIndex[t];if(n&&n[e]>=0&&e>=this.config.minQualityNumberMappingNumber){var o=n[e];o>=0&&(r=o)}else if(-1===i||e>=0&&e<=i)r=e;else if(n){for(var a in n)if(n.hasOwnProperty(a)){var s=parseInt(a,10);s "+r.new+", segment index list: "+t.notEqualIndexList+" } "})}var n=this.state.segmentsInfoMap,i=this.state.appendingSourceState;if(n&&n.video&&n.audio&&!i){var o=n.video.realDuration,a=n.audio.realDuration,s=Math.abs(o-a);if(s>3){var u=this.getQualityNumberFromQualityIndex(n.video.qualityIndex,p.STRING.VIDEO),c=this.getQualityNumberFromQualityIndex(n.audio.qualityIndex,p.STRING.AUDIO),l=n.video.segments.length,d=n.audio.segments.length;this.error({type:p.STRING.WARNING,code:p.ERROR.DURATION_DEVIATION_IS_TOO_LARGE,msg:"Video / audio duration deviation is too large, deviation: "+s+"s. Video qn:"+u+", segments:"+l+", duration:"+o+". Audio qn:"+c+", segments:"+d+", duration:"+a})}}if(this.state.startOffsetTimeh&&(this.player.seek(this.state.startOffsetTime+.2),this.error({type:p.STRING.WARNING,code:p.ERROR.START_OFFSET_TIME_WARNING,msg:e+" start offset time is greater than 0, "+this.state.startOffsetTime}))}}catch(e){this.error({msg:"Call onInitSegmentsLoaded function error",detail:e})}},e.prototype.setMaxAllowedBitrateFor=function(e,t){try{this.player.setMaxAllowedBitrateFor(e,t)}catch(e){this.error({msg:"Call setMaxAllowedBitrateFor function error",detail:e})}},e.prototype.videoInfoSchedule=function(){var e=this,t={},r={};try{var n=this.state,i=n.manifestInfo,o=n.currentQualityIndex,a=i.representation[p.STRING.VIDEO][o[p.STRING.VIDEO]]||{},s=i.representation[p.STRING.AUDIO][o[p.STRING.AUDIO]]||{},u=a.mimeType+';codecs="'+a.codecs+'"',c=this.getCurrentSegmentInfoFor(p.STRING.VIDEO),l=s.mimeType+';codecs="'+s.codecs+'"',d=this.getCurrentSegmentInfoFor(p.STRING.AUDIO),f=this.getDroppedFramesInfo(),h=this.getCurrentCodecID(p.STRING.VIDEO);t={width:a.width,height:a.height,sar:a.sar,mimeType:u+", "+l,fps:a.fps,videoDataRate:a.bandwidth,videoCodec:a.codecs,videoCodecID:h,audioDataRate:s.bandwidth,audioCodec:s.codecs,audioCodecID:0,audioSampleRate:s.audioSamplingRate||0,audioChannelCount:0},r={playerType:"DashPlayer",speed:Math.round(this.getAverageConnectionSpeed(p.STRING.VIDEO)),videoConnectionSpeed:Math.round(this.getAverageConnectionSpeed(p.STRING.VIDEO)),audioConnectionSpeed:Math.round(this.getAverageConnectionSpeed(p.STRING.AUDIO)),networkActivity:this.components.networkActivity&&this.components.networkActivity.getNetworkActivity(),videoBufferHealth:this.getBufferLength("video")||0,audioBufferHealth:this.getBufferLength("audio")||0,droppedFrames:f.droppedFrames||0,decodedFrames:f.totalFrames||0,audioURL:d.url||"",audioCurrentSegmentIndex:d.index||0,audioCurrentSegmentStartTime:d.startTime||0,audioCurrentSegmentDuration:d.duration||0,audioTotalSegmentCount:n.segmentsInfoMap[p.STRING.AUDIO].total||0,videoURL:c.url||"",videoCurrentSegmentIndex:c.index||0,videoCurrentSegmentStartTime:c.startTime||0,videoCurrentSegmentDuration:c.duration||0,videoTotalSegmentCount:n.segmentsInfoMap[p.STRING.VIDEO].total||0},this.state.mediaInfo=t,this.state.statisticsInfo=r,this.fire(p.EVENTS.VIDEO_INFO,{mediaInfo:t,statisticsInfo:r})}catch(e){}clearTimeout(this.timer&&this.timer.videoInfoSchedule),this.config&&this.config.videoInfoScheduleTime&&this.timer&&(this.timer.videoInfoSchedule=window.setTimeout((function(){e.videoInfoSchedule()}),this.config.videoInfoScheduleTime))},e.prototype.getQualityChangedData=function(e,t){var r={};try{var n=e.mediaType,i=e.type,o=e.newQuality,a=e.oldQuality,s=this.state.isAutoSwitch[n];r={mediaType:n,type:i,newQuality:o,newQualityNumber:this.getQualityNumberFromQualityIndex(o,n),oldQuality:a,oldQualityNumber:this.getQualityNumberFromQualityIndex(a,n),currentTime:this.getCurrentTime(),isAutoSwitch:s,dict:this.player.getThroughputDict("video")},p.EVENTS.QUALITY_CHANGE_RENDERED===i?((t=t||this.state.autoSwitchStartTime||0)&&(r.cost=Math.floor(Date.now()-t)),this.state.autoSwitchStartTime=0):p.EVENTS.QUALITY_CHANGE_REQUESTED===i&&(this.state.autoSwitchStartTime=Date.now())}catch(e){this.error({msg:"Call getQualityChangedData error",detail:e,type:p.STRING.WARNING})}return r},e.prototype.getTransitionCanvas=function(){try{if(this.video&&this.video.parentNode)return this.video.parentNode.querySelector("."+p.STRING.SWITCH_SOURCE_TRANSITION_ELEMENT_CLASS_NAME)}catch(e){return null}},e.prototype.onVideoSwitchSourceEnded=function(e){try{this.switchSourcePromise={},window.clearTimeout(this.timer.removeSwitchSourceTransition),window.clearTimeout(this.timer.switchSourceTimer),this.video&&(this.video.style.visibility="",this.video.removeEventListener("canplay",this.onVideoSwitchSourceEnded));var t=void 0;if(e&&e.target){var r=e.target.parentElement;t=r&&r.querySelectorAll("."+p.STRING.SWITCH_SOURCE_TRANSITION_ELEMENT_CLASS_NAME) }else t=document.querySelectorAll("."+p.STRING.SWITCH_SOURCE_TRANSITION_ELEMENT_CLASS_NAME);t&&t.length&&t.forEach((function(e){e&&e.parentElement.removeChild(e)}))}catch(e){console.error("Remove transition canvas error.",e)}},e.prototype.getStartOffsetTime=function(){var e=0;try{var t=this.state.segmentsInfoMap;t&&(e=Math.max(t.video.startOffsetTime||0,t.audio.startOffsetTime||0))}catch(e){}return e},e.prototype.setDebugLog=function(){try{var e=location.href;e&&e.indexOf("debug=1")>0&&this.player.getDebug().setLogToBrowserConsole(!0)}catch(e){}},e.prototype.getNextFragmentInfo=function(e){if(e&&e.mediaType){var t=e.mediaType;this.state.getNextFragmentHistory||(this.state.getNextFragmentHistory={video:[],audio:[]});var r=!1,n=this.state.getNextFragmentHistory[t].length;if(n){var i=this.state.getNextFragmentHistory[t][this.state.getNextFragmentHistory[t].length-1];for(var o in i)if(i[o]!==e[o]){r=!0;break}}!r&&n||(n>=30&&this.state.getNextFragmentHistory[e.mediaType].splice(0,1),this.state.getNextFragmentHistory[e.mediaType].push(e))}},e.prototype.onFragmentLoading=function(e){var t,r;if(e&&e.type){var n=e.type,i=e.request,o=this.covertFragmentInfoObject(i);if(o.loadingType=n,n===p.EVENTS.FRAGMENT_LOADING_STARTED)this.state.fragmentLoadingInfo[o.mediaType]=o;else if(n===p.EVENTS.FRAGMENT_LOADING_COMPLETED){var a=this.state.fragmentLoadingInfo[o.mediaType];o.isError=!!e.error;var s=e.response&&e.response.byteLength||0,u=Number(i.firstByteDate)-Number(i.requestStartDate);u=u?Math.max(u,1):1;var c=Number(i.requestEndDate)-Number(i.firstByteDate);c=c?Math.max(c,1):1;var l=Number(i.requestEndDate)-Number(i.requestStartDate);l=l?Math.max(l,1):1;var d=Math.round(8*s/l);o.latencyTime=u,o.downloadTime=c,o.totalTime=l,o.downloadBytes=s,o.connectionSpeed=d,o.data=e.response,o.totalSize=this.state.segmentsInfoMap[o.mediaType].rangeEnd+1;var f=!0;for(var h in a)if(a[h]!==o[h]&&"loadingType"!==h&&!isNaN(a[h])){f=!1;break}f&&(this.state.fragmentLoadingInfo[o.mediaType]=null,this.fire(p.EVENTS.FRAGMENT_LOADING_COMPLETED,o)),this.bpNCSaveData(o);var y=i.mediaType;"video"!==y&&"audio"!==y||this.recordRequestedInfoDetail(y,{code:200,qn:null==o?void 0:o.qn,index:null==o?void 0:o.index,rType:null===(t=null==i?void 0:i.loadInfo)||void 0===t?void 0:t.rType,protocol:null===(r=null==i?void 0:i.loadInfo)||void 0===r?void 0:r.protocol,currentTime:this.getCurrentTime(),buffered:this.getBufferLength(y),now:Math.round(null===performance||void 0===performance?void 0:performance.now()),latencyTime:u,downloadTime:c,totalTime:l,connectionSpeed:d,totalSize:s,retryTimes:null==i?void 0:i.retry,url:null==i?void 0:i.url})}else this.state.fragmentLoadingInfo&&this.state.fragmentLoadingInfo[o.mediaType]&&!this.state.fragmentLoadingInfo[o.mediaType].loaderType&&(this.state.fragmentLoadingInfo[o.mediaType].loaderType=o.loaderType),this.components.networkActivity&&this.components.networkActivity.onFragmentLoadingProgess(e)}},e.prototype.onFragmentLoadingError=function(e){if(e){if(e.type===p.EVENTS.REQUEST_RETRYED&&(null==e?void 0:e.code)<4e3)return;var t=null==e?void 0:e.mediaType;"video"!==t&&"audio"!==t||this.recordRequestedInfoDetail(t,{code:null==e?void 0:e.code,qn:(null==e?void 0:e.id)||(null==e?void 0:e.qn),index:null==e?void 0:e.index,rType:e.rType,retryTimes:e.retryTimes,currentTime:this.getCurrentTime(),buffered:this.getBufferLength(t),now:Math.round(null===performance||void 0===performance?void 0:performance.now()),latencyTime:null==e?void 0:e.timeout,downloadTime:(null==e?void 0:e.downloadTime)||(null==e?void 0:e.timeout),totalTime:(null==e?void 0:e.totalTime)||(null==e?void 0:e.timeout),totalSize:null==e?void 0:e.received,url:null==e?void 0:e.url})}},e.prototype.recordRequestedInfoDetail=function(e,t){var r,n,i,o;if(t&&this.state){(null===(r=this.state)||void 0===r?void 0:r.requestedInfoDetail[e])||(this.state.requestedInfoDetail[e]={latestHost:"",list:[]}),this.state.requestedInfoDetail[e].list.length>1e3&&this.state.requestedInfoDetail[e].list.shift();var a="-",s="443";try{var u=((null==t?void 0:t.url)||"").match(/^(?:https?:\/\/)?([^\/\n:]+)(?::(\d+))?/);u[2]&&(s=u[2]);var c=null===(n=u[1])||void 0===n?void 0:n.split(".");a=c.splice(0,c.length-2).join("."),"443"!==s&&(a+=":"+s);var l=this.state.requestedInfoDetail[e].latestHost;l&&l===a||(this.state.requestedInfoDetail[e].latestHost=a)}catch(e){}var d=t.qn||t.representationId;if(d){var p={id:this.state.requestedInfoRecordIndex++,co:t.code,qn:parseInt(d.toString().substr(-3),10),in:t.index,rt:(t.rType||"-").substr(0,1),qr:(t.protocol||"-").substr(0,1),ct:f.default.toFixed(t.currentTime||0,2),bf:f.default.toFixed(t.buffered||0,2),lt:t.latencyTime||0,dt:t.downloadTime||0,cs:t.connectionSpeed||0,ts:t.totalSize||0,ry:t.retryTimes||0,ht:a};null===(o=null===(i=this.state)||void 0===i?void 0:i.requestedInfoDetail[e])||void 0===o||o.list.push([p.id,p.co,p.qn,p.in,{"-":0,X:1,N:2,P:3}[p.rt]||0,{"-":0,T:1,Q:2}[p.qr]||0,p.ct,p.bf,p.lt,p.dt,p.cs,p.ts,p.ry,p.ht])}}},e.prototype.bpNCSaveData=function(e){if(e&&e.url&&e.range&&e.data){var t=this.getBPNC();if(t&&"function"==typeof t.allowDPSaveDataByNC){var r=(""+e.url).split("?")[0].split("/"),n=r[r.length-1].split(".")[0].split("-").pop();if(n){var i=parseInt(n,10);if(!isNaN(i)&&t.allowDPSaveDataByNC({qn:i})){var o={url:e.url,range:e.range,data:e.data,totalSize:e.totalSize,token:t.getToken()};t.saveData(o)}}}}},e.prototype.getBPNC=function(){var e,t="bpNC";return(null===(e=this.config)||void 0===e?void 0:e.hid)&&(t+="_"+this.config.hid),window[t]},e.prototype.covertFragmentInfoObject=function(e){var t={};try{t={type:e.type,mediaType:e.mediaType,startTime:e.startTime,url:e.url,index:e.index,id:e.representationId,qn:this.getQualityNumberFromQualityIndex(e.quality,e.mediaType),range:e.range,duration:e.duration,isError:!1},e.startLoadFragmentTime?t.startDownloadTime=new Date(e.startLoadFragmentTime).getTime():t.startDownloadTime=(new Date).getTime(),e.loadInfo&&(t.loaderType=e.loadInfo.rType)}catch(e){}return t},e.prototype.onDashJSLog=function(e){this.state.logHistory||(this.state.logHistory=[]),this.state.logHistory.length>=2e4&&this.state.logHistory.shift(),e&&this.state.logHistory.push(e.message)},e.prototype.onPredictSeek=function(e){this.fire(p.EVENTS.PREDICT_SEEK,e)},e.prototype.onJumpMediaGap=function(e){this.fire(p.EVENTS.JUMP_MEDIA_GAP,e)},e.prototype.onDashCDNLoadInfo=function(e){var t,r,n,i,o,a,s,u,c;if(null==e?void 0:e.loadInfo){var l=e.loadInfo,d=null==e?void 0:e.mediaInfo.id;for(var f in d&&!this.state.cdnInfoDetail[d]&&(this.state.cdnInfoDetail[d]={cdn:0,p2p:0,nc:0,ncBr:0,pdn:0,business:0,mcdn:0,bcache:0,vcache:0,ncFail:0,ncGotFail:0}),this.state.cdnInfo)l[f]&&(this.state.cdnInfo[f]+=l[f],d&&(this.state.cdnInfoDetail[d][f]+=l[f]));if("XHR"===l.rType&&l.url&&l.cdn){var h=l.url;h.indexOf("upos-")>-1?(this.state.cdnInfo.business+=l.cdn,d&&(this.state.cdnInfoDetail[d].business+=l.cdn)):h.indexOf("os=mcdn")>-1?(this.state.cdnInfo.mcdn+=l.cdn,d&&(this.state.cdnInfoDetail[d].mcdn+=l.cdn)):h.indexOf("os=bcache")>-1?(this.state.cdnInfo.bcache+=l.cdn,d&&(this.state.cdnInfoDetail[d].bcache+=l.cdn)):h.indexOf("os=vcache")>-1&&(this.state.cdnInfo.vcache+=l.cdn,d&&(this.state.cdnInfoDetail[d].vcache+=l.cdn))}this.fire(p.EVENTS.CDN_LOAD_INFO,l)}if((null===(t=null==e?void 0:e.NCInfo)||void 0===t?void 0:t.responseData)&&this.state.NCInfo){var y=null==e?void 0:e.NCInfo,m=(d=y.qn,this.state.NCInfo[d]||{total:0,response:0,expect:0});if(9999===y.code){if(m.total++,y.range){var g=y.range.split("-"),v=parseInt(g[0],10),_=parseInt(g[1],10);_&&(m.expect+=_-v+1||0)}return void(this.state.NCInfo[d]=m)}if(m[y.code]||(m[y.code]={}),4999===y.code)return m[y.code].count=(null===(r=m[y.code])||void 0===r?void 0:r.count)+1||1,(null===(n=this.video)||void 0===n?void 0:n.seeking)&&(m[y.code].seeking=m[y.code].seeking+1||1),void(this.state.NCInfo[d]=m);var E=y.responseData,b=E.gotDataByte,S=E.getDataTime,T=E.usePeerSids,w=E.retryCount,A=E.originRange,R=E.gotDataByteFromBr;T=T||[],b=b||0,S=S||0,R=R||0,w=w||0,A&&A.hasOwnProperty("start")&&A.hasOwnProperty("end")||(A={start:0,end:0});var P=Math.abs(A.end-A.start)+1,D=(null==T?void 0:T.length)||0;if(m.response++, m[y.code].count=(null===(i=m[y.code])||void 0===i?void 0:i.count)+1||1,m[y.code].bytes=(null===(o=m[y.code])||void 0===o?void 0:o.bytes)+b||b,m[y.code].time=(null===(a=m[y.code])||void 0===a?void 0:a.time)+S||S,m[y.code].peers=(null===(s=m[y.code])||void 0===s?void 0:s.peers)+D||D,m[y.code].retry=(null===(u=m[y.code])||void 0===u?void 0:u.retry)+w||w,m[y.code].expect=(null===(c=m[y.code])||void 0===c?void 0:c.expect)+P||P,4003!==y.code&&40003!==y.code||0!==b||(m[y.code].abnormal=m[y.code].abnormal+1||1),2e3===y.code||24e3===y.code){var I="peers_"+D;m[y.code][I]=m[y.code][I]+1||1}this.state.NCInfo[d]=m}},e.prototype.changePlaybackState=function(e){return this.state.playbackState=e,this},e.prototype.saveNetworkWarningInfo=function(e){this.state.networkWarningHistory&&(this.state.networkWarningHistory||(this.state.networkWarningHistory=[]),this.state.networkWarningHistory.length>=100&&this.state.networkWarningHistory.shift(),this.state.networkWarningHistory.push(e))},e.prototype.detectDefaultQualityChangeAbnormal=function(){var e=this,t=this.state,r=t.defaultVideoQuality,n=t.defaultAudioQuality,i=t.currentQualityIndex,o=i.video,a=i.audio;return[{type:p.STRING.VIDEO,defaultQuality:r,currentIndex:o},{type:p.STRING.AUDIO,defaultQuality:n,currentIndex:a}].forEach((function(t){var r=e.getQualityNumberFromQualityIndex(t.currentIndex,t.type),n=t.defaultQuality,i=t.currentIndex;n>=0&&i>=0&&-1===[i,r].indexOf(n)&&e.fire(p.EVENTS.QUALITY_CHANGE_ABNORMAL,{type:p.EVENTS.QUALITY_CHANGE_ABNORMAL,mediaType:t.type,defaultQuality:n,currentIndex:i,qualityNumber:r})})),this},e.prototype.isUsingWasmPlayer=function(){return window.__ENABLE_WASM_PLAYER__&&this.video instanceof window.BwpElement},e.prototype.filterHEVCSpecialVideoUrl=function(e){var t=this;if("string"!=typeof e&&(null==e?void 0:e.video)){var r=this.state.defaultVideoQuality,n=[p.VIDEO_QN.QN_8K,p.VIDEO_QN.QN_DOLBY_VISION,p.VIDEO_QN.QN_HDR10],i=p.VIDEO_CODEC_ID.HEVC;((null==e?void 0:e.video)||[]).filter((function(e){return e.codecid===i})).length<=0||n.includes(r)||(e.video=e.video.filter((function(e){if(!n.includes(e.id))return!0;var r=t.state.filterVideoInfo,i=p.FILTER_VIDEO_URL_REASON.FILTER_SPECIAL_VIDEO;return void 0===r[i]&&(r[i]=[]),r[i].push(JSON.parse(JSON.stringify(e))),t.onDashJSLog({type:"filterSpecialVideo",message:"filter hevc special video urls"}),!1})))}},e.prototype.filterPdnUrl=function(t){if("string"!=typeof t&&t){var r=t.video,n=t.audio,i=function(e){return"string"==typeof e&&(e.includes("traceid")||e.includes(":8082"))},o=this.shouldFilterPdnUrl(),a=JSON.parse(e.getFawkesConfigValue(p.STRING.DASH_CONFIG,"pdn_disable_qn_list")),s=JSON.parse(e.getFawkesConfigValue(p.STRING.DASH_CONFIG,"pdn_disable_codecid_list")),u=!1,c=function(e){e&&e.length&&e.forEach((function(e){(i(e.baseUrl)||i(e.base_url))&&(o||a.includes(e.id)&&f.default.isInSpareTime()||s.includes(e.codecid)&&f.default.isInSpareTime())&&(u=!0,e.baseUrl&&e.backupUrl&&e.backupUrl.length&&(e.baseUrl=e.backupUrl.shift()),e.base_url&&e.backup_url&&e.backup_url.length&&(e.base_url=e.backup_url.shift()))}))};c(r),c(n),u&&this.onDashJSLog({type:"filterPdn",message:"["+Math.round(performance.now())+"] filter pdn, "+o+", qn list: "+a+", codec list: "+s})}},e.prototype.filterManifest=function(e){var t=e.manifest;e.log=this.onDashJSLog.bind(this),void 0===e.enableAV1&&(e.enableAV1=this.state.enableAV1),void 0===e.enableHEVC&&(e.enableHEVC=this.state.enableHEVC),t&&!t.video&&(this.onDashJSLog({type:"audio-only",message:"playing in audio-only"}),"string"!=typeof t&&(t.video=[]));var r=p.VIDEO_CODEC_ID.AVC;t&&t.video&&t.video instanceof Array&&(this.externalMediaLoader||(this.filterHEVCSpecialVideoUrl(e.manifest),r=g.applyCodecStrategy(e),t.video=t.video.filter((function(e){return e.codecid===r}))),t=this.adjustManifestHack(t));try{this.filterPdnUrl(t)}catch(e){}return{codecId:r,manifest:t}},e.prototype.shouldFilterPdnUrl=function(){try{var t=e.getFawkesConfigValue(p.STRING.DASH_CONFIG,"pdn_mid_black_list"),r=t&&JSON.parse(t)||[],n=f.default.getCookie("DedeUserID"),i=""===n?0:Number(n);if(Math.random()>Number(e.getFawkesConfigValue(p.STRING.DASH_CONFIG,p.STRING.PDN_ENABLE)))return!0;if(r.includes(i))return!0}catch(e){return!1}return!1},e.prototype.adjustManifestHack=function(e){var t;if((null===(t=null==e?void 0:e.video)||void 0===t?void 0:t.length)>1){var r=e.video,n=Math.max.apply(Math,r.map((function(e){return e.bandwidth}))),i=Math.min.apply(Math,r.map((function(e){return e.bandwidth})));if(Math.abs(n-i)<1e4){this.state&&this.state.logHistory.push("Adjust manifest hack, max: "+n+", min: "+i+", delta: "+Math.abs(n-i));for(var o=r.length-1;o>0;o--)r[o-1].bandwidth=Math.max(r[o-1].bandwidth,r[o].bandwidth+1e4)}}return e},e.prototype.processSpecialAudioItem=function(e,t){var r;void 0===t&&(t=30280),e.audio;var n=e.flac,i=e.dolby;if(n&&n.audio&&d.isFLACTypeSupported()&&t===p.FLAC_QN)return n.audio.codecs=n.audio.codecs.toLocaleLowerCase(),void(e.audio=[n.audio]);i&&(null===(r=null==i?void 0:i.audio)||void 0===r?void 0:r.length)&&d.isEC3DolbyATMOSTypeSupported()&&[p.DOLBY_AUDIO_QN.AMTOS,p.DOLBY_AUDIO_QN.AUDIO].includes(t)&&(e.audio=s(i.audio))},e.prototype.checkAudioCodecCanUseByType=function(e){var t=this.state.mpd&&this.state.mpd[p.STRING.AUDIO]?this.state.mpd[p.STRING.AUDIO]:this.state.manifest&&this.state.manifest.audio?this.state.manifest.audio:void 0;if(!t||!t.length)return!1;switch(e){case p.SPECIAL_AUDIO_TYPE.DOLBY:if(d.isEC3DolbyATMOSTypeSupported())return-1!==t.findIndex((function(e){return e.id===p.DOLBY_AUDIO_QN.AUDIO||e.id===p.DOLBY_AUDIO_QN.AMTOS}));break;case p.SPECIAL_AUDIO_TYPE.FLAC:if(d.isFLACTypeSupported())return-1!==t.findIndex((function(e){return e.id===p.FLAC_QN}))}return!1},e.prototype.qualityNumberDowngrading=function(e,t){var r=f.default.getDecodeErrorMediaQualityNumberListForType(e,p.STRING.BILIBILI_DECODE_ERROR_OBJ),n=Object.keys(this.state.qualityNumberMap[e]).map(Number).reverse(),i=0;if(r.length>0){for(;r.includes(t)&&i(s.shouldUseHardwareDecodingAV1()?f:h)&&(u=!1);var y=p();switch(y){case i.USER_CODEC_PREFER_TYPE.AV1:u=!0;break;case i.USER_CODEC_PREFER_TYPE.DISABLE:case i.USER_CODEC_PREFER_TYPE.HEVC:u=!1}var m=s.getLocalDecodeErrorInfo("AV1"),g=(new Date).getTime();m&&(m.reactivateTime||0)>g&&(u=!1),l.includes(d)&&(u=!0),s.isBrowserSupportAV1(r)||(u=!1);var v=!!+a.default.getSessionStorage("enableAV1Error");v&&(u=!1),n&&n({type:"applyAV1Strategy",message:"[1] Apply AV1 strategy, enable: "+u+", preferType: "+y+", hardwareLimit: "+f+", softwareLimit: "+h+", prevEnableAV1Error: "+v})}return u}(m,n,I,v,_))return b;var O=!!window.__ENABLE_WASM_PLAYER__;return(1===A||O)&&P&&function(e,t,r,n){var c=e;if(c){var l=navigator&&navigator.userAgent&&navigator.userAgent.toLowerCase(),y=l.indexOf("edge/")>-1,m=t&&t.defaultVideoQuality||n,g=d.SOFTWARE,v=d.HARDWARE,_=d.WASM,E=o.getCurrentBrowserInfo(),b=E.type,S=E.version,T=b===o.BROWSER_TYPE.Chrome&&S>108&&s.getCPULogicCount()>=8?v:g,w=m>T&&!y&&l.indexOf("chrome/")>0,A=m>v,R=b===o.BROWSER_TYPE.ChromiumEdge,P=!!+a.default.getSessionStorage("enableHEVCError"),D=parseInt(a.default.getLocalStorage("bilibili_player_codec_prefer_strategy"),10);window.__ENABLE_WASM_PLAYER__?m>_&&(c=!1):(w||A)&&(c=!1);var I=p();i.USER_CODEC_PREFER_TYPE.HEVC===I?c=!0:i.USER_CODEC_PREFER_TYPE.DISABLE===I||i.USER_CODEC_PREFER_TYPE.AV1===I?(c=!1,h().then((function(){a.default.setLocalStorage(u,""+i.USER_CODEC_PREFER_TYPE.RECOMMEND)})).catch((function(){}))):(h().catch((function(){})),i.USER_CODEC_PREFER_TYPE.DISABLE===D&&(c=!1));var O=s.getLocalDecodeErrorInfo("HEVC"),C=(new Date).getTime();O&&(O.reactivateTime||0)>C&&(c=!1),f.includes(m)&&(c=!0),(!window.__ENABLE_WASM_PLAYER__&&R&&"Mac OS"!==s.getOS()||P)&&(c=!1),r&&r({type:"applyHEVCStrategy",message:"[1] Apply HEVC strategy, enable: "+c+", preferType: "+I+", hardwareLimit: "+v+", softwareLimit: "+T+", wasm: "+_+", prevEnableHEVCError: "+P+", strategyType: "+D})}return c}(g,n,v,_)?E:S},t.getUserCodecPreferType=p},function(e,t,r){"use strict";var n=this&&this.__assign||function(){return(n=Object.assign||function(e){for(var t,r=1,n=arguments.length;r0){var t=this.video.buffered.end(this.video.buffered.length-1),r=this.video.currentTime,n=Math.floor(1e3*(t-r))/1e3;this.swichingInfo.bufferLen=n}},e.prototype.getInfo=function(e){var r={type:-1};if(!e)return r;try{var i=this.video,o=i.currentTime,a=i.duration,s=i.readyState,u=i.buffered,c=u&&u.length,l=i.seeking,d=i.playbackRate,f=t.BUFFERING.TYPE.UNKNOWN,p=e.playbackState||t.BUFFERING.PLAYBACK.PLAYING;if(!l||p!==t.BUFFERING.PLAYBACK.PLAYING&&p!==t.BUFFERING.PLAYBACK.SWITCHING_QUALITY||(p=t.BUFFERING.PLAYBACK.SEEKING),r={type:f,isSeeking:l,cTime:o,duration:a,rState:s,nRanges:c,vLength:e.videoBufferLength,aLength:e.audioBufferLength,activity:e.networkActivity,vSpeed:Math.round(e.videoSpeed),aSpeed:Math.round(e.audioSpeed),playbackRate:d,vQn:e.videoCurrentQn,aQn:e.audioCurrentQn,retryTimes:e.retryTimes,seekSpace:e.seekSpace},p===t.BUFFERING.PLAYBACK.SEEKING)f=0===c?t.BUFFERING.TYPE.SEEKING_OUTSIDE_BUFFERED:t.BUFFERING.TYPE.SEEKING_INSIDE_BUFFERED;else{var h=this.checkGapInfo(),y=h.nativeBufferLength;if(r.nLength=y,r.cRange=h.start+"-"+h.end,h.hasGap)f=h.type,r.gap=h.gapStart+"-"+h.gapEnd,r.gapLength=h.gapLength;else if(y<1){var m=this.checkLackOfBufferInfo(e);f=m.type,r.vCode=m.videoErrorCode,r.aCode=m.audioErrorCode}else f=s>=3?t.BUFFERING.TYPE.PERFORMANCE:t.BUFFERING.TYPE.PERFORMANCE_UNKNOWN}var g=this.checkNativeErrorInfo();g.type&&(f=g.type,r.nCode=g.code);var v=e.fragmentLoadingInfo;if(v&&v.video){var _=v.video;r.vFragment=_.id+"-"+_.index,e.latestPlayurlErrorHTTPCode&&(r.hCode=e.latestPlayurlErrorHTTPCode)}v&&v.audio&&(_=v.audio,r.aFragment=_.id+"-"+_.index,e.latestPlayurlErrorHTTPCode&&(r.hCode=e.latestPlayurlErrorHTTPCode)),r.type=""+p+(f||"00")}catch(e){throw new Error(e.message)}return this.swichingInfo&&(r=n(n({},r),this.swichingInfo)),this.downloadInfo&&(r=n(n({},r),this.downloadInfo)),r},e.prototype.checkGapInfo=function(){var e=this.video,r=e.currentTime,n=e.buffered,i=n&&n.length,o=0,a=!1,s=0,u=0,c={start:0,end:0},l=0,d=t.BUFFERING.TYPE.UNKNOWN;if(i&&i>1)for(var f=0;f=r&&(o=Math.floor(1e3*(h-r))/1e3,c={start:p,end:h},d===t.BUFFERING.TYPE.GAP_BEFORE_CURRENT?u=p:s=h),p>r&&p-r<=1&&d!==t.BUFFERING.TYPE.GAP_BEFORE_CURRENT&&(a=!0,d=t.BUFFERING.TYPE.GAP_AFTER_CURRENT,u=u?Math.min(u,p):p,s=c.end),r-h<3&&r-h>=0&&(a=!0,d=t.BUFFERING.TYPE.GAP_BEFORE_CURRENT,s=h,u=c.start)}else 1===i&&(h=n.end(0),c={start:n.start(0),end:h},o=Math.floor(1e3*(h-r))/1e3);return a&&(l=Math.round(1e3*(u-s))/1e3),{type:d,start:c.start,end:c.end,nativeBufferLength:o,hasGap:a,gapStart:s,gapEnd:u,gapLength:l}},e.prototype.checkLackOfBufferInfo=function(e){var r=t.BUFFERING.TYPE.UNKNOWN,n=this.video,i=n.currentTime,o=n.buffered,a=n.duration,s=n.readyState,u=0,c=0;if(o&&o.length)if(a&&a-i<1)r=t.BUFFERING.TYPE.INTERNAL_ENDING;else{var l=this.checkNetworkInfo(e);r=l.type,u=l.videoErrorCode,c=l.audioErrorCode}else r=i<=e.startOffsetTime&&s<=1?t.BUFFERING.TYPE.INTERNAL_STARTING:(e.networkActivity.length&&e.networkActivity.reduce((function(e,t){return e+t})))<100?t.BUFFERING.TYPE.NETWORK_POOR:e.videoBufferLength?e.audioBufferLength?t.BUFFERING.TYPE.INTERNAL_UNNORMAL_RANGES:t.BUFFERING.TYPE.INTERNAL_AUDIO_RANGES:t.BUFFERING.TYPE.INTERNAL_VIDEO_RANGES;return{type:r,videoErrorCode:u,audioErrorCode:c}},e.prototype.checkNetworkInfo=function(e){var r=this,n=t.BUFFERING.TYPE.UNKNOWN,o=this.video.currentTime,a=e.videoBufferLength||0,s=e.audioBufferLength||0,u=e.networkWarningHistory,c=this.getCurrentNetworkWarningInfo({mediaType:i.STRING.VIDEO,currentQn:e.videoCurrentQn,networkWarningHistory:u,warningTime:o+a,bufferLength:a},1),l=this.getCurrentNetworkWarningInfo({mediaType:i.STRING.AUDIO,currentQn:e.audioCurrentQn,networkWarningHistory:u,warningTime:o+s,bufferLength:s},1),d=c.code,f=l.code,p=this.getBufferingTypeByPoorNetwork(d),h=this.getBufferingTypeByPoorNetwork(f);if(p||h)n=p||"00",h&&(n=""+n+h);else if(this.video.playbackRate>1)n=t.BUFFERING.TYPE.NETWORK_POOR_UNDER_PLAYBACKRATE;else{var y=e.fragmentLoadingInfo,m=function(e){if(!e)return!1;var t=e.startDownloadTime,n=e.loaderType,i=e.qn,o=e.index,a=((new Date).getTime()-t)/1e3;return r.downloadInfo||(r.downloadInfo={}),r.downloadInfo[e.mediaType]={downloadTime:a,loaderType:n,qn:i,index:o},a>5};if(y){var g=y.video,v=y.audio,_=m(g),E=m(v) ;n=_||E?t.BUFFERING.TYPE.NETWORK_DOWNLOAD_TOO_LONG:t.BUFFERING.TYPE.NETWORK}else n=t.BUFFERING.TYPE.NETWORK}return{type:n,videoErrorCode:d,audioErrorCode:f,videoBufferLength:a,audioBufferLength:s}},e.prototype.checkNativeErrorInfo=function(){var e=this.video.error,r=e&&e.code||0,n=t.BUFFERING.TYPE.UNKNOWN;if(e&&r)switch(r){case 1:n=t.BUFFERING.TYPE.MEDIA_ERR_ABORTED;break;case 2:n=t.BUFFERING.TYPE.MEDIA_ERR_NETWORK;break;case 3:n=t.BUFFERING.TYPE.MEDIA_ERR_DECODE;break;case 4:n=t.BUFFERING.TYPE.MEDIA_ERR_SRC_NOT_SUPPORTED;break;default:n=t.BUFFERING.TYPE.MEDIA}return{type:n,code:r}},e.prototype.getCurrentNetworkWarningInfo=function(e,t){if(void 0===t&&(t=1),!e)return{code:0};var r=e.mediaType,n={code:0,type:"",mediaType:e.mediaType},i=e.networkWarningHistory,o=e.warningTime,a=e.bufferLength,s=e.currentQn;if(i.length&&a=o&&e.id===s.toString()}));u.length&&(n=u[u.length-1])}catch(e){throw new Error("getCurrentNetworkWarningInfo error, "+JSON.stringify(e))}}return n},e.prototype.getBufferingTypeByPoorNetwork=function(e){var r=t.BUFFERING.TYPE.UNKNOWN;if(e)switch(e){case i.ERROR.CONNECT_FRAGMENT_ERROR_TIMEOUT:r=t.BUFFERING.TYPE.NETWORK_CONNECT_TIMEOUT;break;case i.ERROR.DOWNLOAD_FRAGMENT_ERROR_TIMEOUT:r=t.BUFFERING.TYPE.NETWORK_DOWNLOAD_TIMEOUT;break;case i.ERROR.DOWNLOAD_FRAGMENT_ERROR:r=t.BUFFERING.TYPE.NETWORK_DOWNLOAD_ERROR}return r},e}();t.default=o},function(e,t,r){"use strict";var n=Array.isArray,i=Object.keys,o=Object.prototype.hasOwnProperty;e.exports=function e(t,r){if(t===r)return!0;var a,s,u,c=n(t),l=n(r);if(c&&l){if((s=t.length)!=r.length)return!1;for(a=0;a=r&&console.log(this.time+" ["+e+"] "+t)}},d=function(e){for(var t=[],r=0;ri&&(l.log("ERROR","Too large cursor position "+this.pos),this.pos=i)},moveCursor:function(e){var t=this.pos+e;if(e>1)for(var r=this.pos+1;r=144&&this.backSpace();var t=r(e);this.pos>=i?l.log("ERROR","Cannot insert "+e.toString(16)+" ("+t+") at position "+this.pos+". Skipping it!"):(this.chars[this.pos].setChar(t,this.currPenState),this.moveCursor(1))},clearFromPos:function(e){var t;for(t=e;t0&&(r=e?"["+t.join(" | ")+"]":t.join("\n")),r},getTextAndFormat:function(){return this.rows}};var m=function(e,t){this.chNr=e,this.outputFilter=t,this.mode=null,this.verbose=0,this.displayedMemory=new y,this.nonDisplayedMemory=new y,this.lastOutputScreen=new y,this.currRollUpRow=this.displayedMemory.rows[14],this.writeScreen=this.displayedMemory,this.mode=null,this.cueStartTime=null};m.prototype={modes:["MODE_ROLL-UP","MODE_POP-ON","MODE_PAINT-ON","MODE_TEXT"],reset:function(){this.mode=null,this.displayedMemory.reset(),this.nonDisplayedMemory.reset(),this.lastOutputScreen.reset(),this.currRollUpRow=this.displayedMemory.rows[14],this.writeScreen=this.displayedMemory,this.mode=null,this.cueStartTime=null,this.lastCueEndTime=null},getHandler:function(){return this.outputFilter},setHandler:function(e){this.outputFilter=e},setPAC:function(e){this.writeScreen.setPAC(e)},setBkgData:function(e){this.writeScreen.setBkgData(e)},setMode:function(e){e!==this.mode&&(this.mode=e,l.log("INFO","MODE="+e),"MODE_POP-ON"==this.mode?this.writeScreen=this.nonDisplayedMemory:(this.writeScreen=this.displayedMemory,this.writeScreen.reset()),"MODE_ROLL-UP"!==this.mode&&(this.displayedMemory.nrRollUpRows=null,this.nonDisplayedMemory.nrRollUpRows=null),this.mode=e)},insertChars:function(e){for(var t=0;t=46,t.italics)t.foreground="white";else{var r=Math.floor(e/2)-16;t.foreground=["white","green","blue","cyan","red","yellow","magenta"][r]}l.log("INFO","MIDROW: "+JSON.stringify(t)),this.writeScreen.setPen(t)},outputDataUpdate:function(){var e=l.time;null!==e&&this.outputFilter&&(this.outputFilter.updateData&&this.outputFilter.updateData(e,this.displayedMemory),null!==this.cueStartTime||this.displayedMemory.isEmpty()?this.displayedMemory.equals(this.lastOutputScreen)||(this.outputFilter.newCue&&this.outputFilter.newCue(this.cueStartTime,e,this.lastOutputScreen),this.cueStartTime=this.displayedMemory.isEmpty()?null:e):this.cueStartTime=e,this.lastOutputScreen.copy(this.displayedMemory))},cueSplitAtTime:function(e){this.outputFilter&&(this.displayedMemory.isEmpty()||(this.outputFilter.newCue&&this.outputFilter.newCue(this.cueStartTime,e,this.displayedMemory),this.cueStartTime=e))}};var g=function(e,t,r){this.field=e||1,this.outputs=[t,r],this.channels=[new m(1,t),new m(2,r)],this.currChNr=-1,this.lastCmdA=null,this.lastCmdB=null,this.bufferedData=[],this.startTime=null,this.lastTime=null,this.dataCounters={padding:0,char:0,cmd:0,other:0}};g.prototype={getHandler:function(e){return this.channels[e].getHandler()},setHandler:function(e,t){this.channels[e].setHandler(t)},addData:function(e,t){var r,n,i,o=!1;this.lastTime=e,l.setTime(e);for(var a=0;a=16&&n<=31&&n===this.lastCmdA&&i===this.lastCmdB?(this.lastCmdA=null,this.lastCmdB=null,l.log("DEBUG","Repeated command ("+d([n,i])+") is dropped")):0!==n||0!==i?(l.log("DATA","["+d([t[a],t[a+1]])+"] -> ("+d([n,i])+")"),(r=this.parseCmd(n,i))||(r=this.parseMidrow(n,i)),r||(r=this.parsePAC(n,i)),r||(r=this.parseBackgroundAttributes(n,i)),r||(o=this.parseChars(n,i))&&(this.currChNr&&this.currChNr>=0?this.channels[this.currChNr-1].insertChars(o):l.log("WARNING","No channel found yet. TEXT-MODE?")),r?this.dataCounters.cmd+=2:o?this.dataCounters.char+=2:(this.dataCounters.other+=2,l.log("WARNING","Couldn't parse cleaned data "+d([n,i])+" orig: "+d([t[a],t[a+1]])))):this.dataCounters.padding+=2},parseCmd:function(e,t){var r;if(!((20===e||21===e||28===e||29===e)&&32<=t&&t<=47||(23===e||31===e)&&33<=t&&t<=35))return!1;r=20===e||21===e||23===e?1:2;var n=this.channels[r-1];return 20===e||21===e||28===e||29===e?32===t?n.cc_RCL():33===t?n.cc_BS():34===t?n.cc_AOF():35===t?n.cc_AON():36===t?n.cc_DER():37===t?n.cc_RU(2):38===t?n.cc_RU(3):39===t?n.cc_RU(4):40===t?n.cc_FON():41===t?n.cc_RDC():42===t?n.cc_TR():43===t?n.cc_RTD():44===t?n.cc_EDM():45===t?n.cc_CR():46===t?n.cc_ENM():47===t&&n.cc_EOC():n.cc_TO(t-32),this.lastCmdA=e,this.lastCmdB=t,this.currChNr=r,!0},parseMidrow:function(e,t){var r=null;if((17===e||25===e)&&32<=t&&t<=47){if((r=17===e?1:2)!==this.currChNr)return l.log("ERROR","Mismatch channel in midrow parsing"),!1;var n=this.channels[r-1];return n.insertChars([32]),n.cc_MIDROW(t),l.log("DEBUG","MIDROW ("+d([e,t])+")"),this.lastCmdA=e,this.lastCmdB=t,!0}return!1},parsePAC:function(e,t){var r,n;if(!((17<=e&&e<=23||25<=e&&e<=31)&&64<=t&&t<=127||(16===e||24===e)&&64<=t&&t<=95))return!1;r=e<=23?1:2,n=64<=t&&t<=95?1===r?o[e]:s[e]:1===r?a[e]:u[e];var i=this.interpretPAC(n,t);return this.channels[r-1].setPAC(i),this.lastCmdA=e,this.lastCmdB=t,this.currChNr=r,!0},interpretPAC:function(e,t){var r,n={color:null,italics:!1,indent:null,underline:!1,row:e};return r=t>95?t-96:t-64,n.underline=1==(1&r),r<=13?n.color=["white","green","blue","cyan","red","yellow","magenta","white"][Math.floor(r/2)]:r<=15?(n.italics=!0,n.color="white"):n.indent=4*Math.floor((r-16)/2),n},parseChars:function(e,t){var n,i=null,o=null,a=null;(e>=25?(i=2,a=e-8):(i=1,a=e),17<=a&&a<=19)?(n=17===a?t+80:18===a?t+112:t+144,l.log("INFO","Special char '"+r(n)+"' in channel "+i),o=[n],this.lastCmdA=e,this.lastCmdB=t):32<=e&&e<=127&&(o=0===t?[e]:[e,t],this.lastCmdA=null,this.lastCmdB=null);if(o){var s=d(o);l.log("DEBUG","Char codes = "+s.join(","))}return o},parseBackgroundAttributes:function(e,t){var r,n,i;return((16===e||24===e)&&32<=t&&t<=47||(23===e||31===e)&&45<=t&&t<=47)&&(r={},16===e||24===e?(n=Math.floor((t-32)/2),r.background=c[n],t%2==1&&(r.background=r.background+"_semi")):45===t?r.background="transparent":(r.foreground="black",47===t&&(r.underline=!0)),i=e<24?1:2,this.channels[i-1].setBkgData(r),this.lastCmdA=e,this.lastCmdB=t,!0)},reset:function(){for(var e=0;e=this.state.maxCacheLength&&this.state.networkActivityLoadedList.splice(0,1),r&&this.state.networkActivityLoadedList.push({time:n,loaded:Math.max(r,0)})},e.prototype.getNetworkActivity=function(){return this.state.networkActivityHistory.length?this.state.networkActivityHistory[this.state.networkActivityHistory.length-1]:0},e.prototype.getNetworkActivityHistory=function(e){return void 0===e&&(e=0),e?this.state.networkActivityHistory.slice(this.state.networkActivityHistory.length-e):this.state.networkActivityHistory},e.prototype.destroy=function(){clearTimeout(this.state&&this.state.networkActivityTimer),this.state.networkActivityLoadedList.length=0,this.state.networkActivityHistory.length=0,this.state=null},e.prototype.initalize=function(){this.runNewWorkActivityTimer()},e.prototype.runNewWorkActivityTimer=function(){var e=this;this.runNetworkActivity(),clearTimeout(this.state.networkActivityTimer),this.state.networkActivityTimer=window.setTimeout((function(){e.runNewWorkActivityTimer()}),this.state.runningInterval)},e.prototype.runNetworkActivity=function(){var e=this.state.runningInterval,t=Math.floor(performance.now()),r=this.state.networkActivityLatestTraceTime,n=this.state.networkActivityLoadedList.filter((function(n){return t-n.timer}));this.state.networkActivityLatestTraceTime=t;var i=0;n&&n.length&&n.forEach((function(e){return i+=e.loaded})),this.state.networkActivityHistory.length>=this.state.maxCacheLength&&this.state.networkActivityHistory.splice(0,1),this.state.networkActivityHistory.push(Math.max(i,0))},e}();t.default=n},function(e,t,r){"use strict";var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var i=r(2),o=r(13),a=n(r(4)),s=function(){function e(){this.bufferingHistory=[],this.state={enableHEVC:!1}}return e.prototype.destroy=function(){this.state=null,this.bufferingHistory.length=0},e.prototype.setHEVCEnableFlag=function(e){return void 0===e&&(e=!1),this.state.enableHEVC=e,this},e.prototype.push=function(e){this.bufferingHistory.length>=this.MAX_HISTORY_LENGTH&&this.bufferingHistory.shift(),e.timestamp=this.getNow(),this.bufferingHistory.push(e),this.optimize()},e.prototype.setOnOptimizingCallback=function(e){this.state.onOptimizingCallback=e},e.prototype.onOptimzing=function(e,t){"function"==typeof this.state.onOptimizingCallback&&this.state.onOptimizingCallback(e,t)},e.prototype.optimize=function(){var e=this.getNow(),t=this.bufferingHistory.filter((function(t){return e-t.timestamp<=2e4}));if(this.state.enableHEVC){var r=""+o.BUFFERING.PLAYBACK.PLAYING+o.BUFFERING.TYPE.PERFORMANCE,n=t.filter((function(e){return r===e.type}));n&&n.length>=10&&(this.state.enableHEVC=!1,this.disableHEVCFlag())}},e.prototype.disableHEVCFlag=function(){1!==parseInt(a.default.getLocalStorage("bilibili_player_codec_prefer_type"),10)&&(a.default.setLocalStorage("bilibili_player_codec_prefer_strategy","2"),this.onOptimzing(i.EVENTS.BUFFERING_OPTIMIZATION,{type:"hevcPerf"}))},e.prototype.getNow=function(){var e;return(null===(e=window.performance)||void 0===e?void 0:e.now())||Date.now()},e}();t.default=s},function(e,t,r){"use strict";var n=this&&this.__assign||function(){return(n=Object.assign||function(e){for(var t,r=1,n=arguments.length;r=this.state.maxCacheLength&&this.state.networkActivityLoadedList.splice(0,1),r&&this.state.networkActivityLoadedList.push({time:n,loaded:Math.max(r,0)})},e.prototype.getNetworkActivity=function(){return this.state.networkActivityHistory.length?this.state.networkActivityHistory[this.state.networkActivityHistory.length-1]:0},e.prototype.getNetworkActivityHistory=function(e){return void 0===e&&(e=0),e?this.state.networkActivityHistory.slice(this.state.networkActivityHistory.length-e):this.state.networkActivityHistory},e.prototype.destroy=function(){clearTimeout(this.state&&this.state.networkActivityTimer),this.state.networkActivityLoadedList.length=0,this.state.networkActivityHistory.length=0,this.state=null},e.prototype.initalize=function(){this.runNewWorkActivityTimer()},e.prototype.runNewWorkActivityTimer=function(){var e=this;this.runNetworkActivity(),clearTimeout(this.state.networkActivityTimer),this.state.networkActivityTimer=window.setTimeout((function(){e.runNewWorkActivityTimer()}),this.state.runningInterval)},e.prototype.runNetworkActivity=function(){var e=this.state.runningInterval,t=Math.floor(performance.now()),r=this.state.networkActivityLatestTraceTime,n=this.state.networkActivityLoadedList.filter((function(n){return t-n.timer}));this.state.networkActivityLatestTraceTime=t;var i=0;n&&n.length&&n.forEach((function(e){return i+=e.loaded})),this.state.networkActivityHistory.length>=this.state.maxCacheLength&&this.state.networkActivityHistory.splice(0,1),this.state.networkActivityHistory.push(Math.max(i,0))},e}();t.default=n},function(e,t,r){"use strict";var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var i=r(2),o=r(13),a=n(r(4)),s=function(){function e(){this.bufferingHistory=[],this.state={enableHEVC:!1}}return e.prototype.destroy=function(){this.state=null,this.bufferingHistory.length=0},e.prototype.setHEVCEnableFlag=function(e){return void 0===e&&(e=!1),this.state.enableHEVC=e,this},e.prototype.push=function(e){this.bufferingHistory.length>=this.MAX_HISTORY_LENGTH&&this.bufferingHistory.shift(),e.timestamp=this.getNow(),this.bufferingHistory.push(e),this.optimize()},e.prototype.setOnOptimizingCallback=function(e){this.state.onOptimizingCallback=e},e.prototype.onOptimzing=function(e,t){"function"==typeof this.state.onOptimizingCallback&&this.state.onOptimizingCallback(e,t)},e.prototype.optimize=function(){var e=this.getNow(),t=this.bufferingHistory.filter((function(t){return e-t.timestamp<=2e4}));if(this.state.enableHEVC){var r=""+o.BUFFERING.PLAYBACK.PLAYING+o.BUFFERING.TYPE.PERFORMANCE,n=t.filter((function(e){return r===e.type}));n&&n.length>=10&&(this.state.enableHEVC=!1,this.disableHEVCFlag())}},e.prototype.disableHEVCFlag=function(){1!==parseInt(a.default.getLocalStorage("bilibili_player_codec_prefer_type"),10)&&(a.default.setLocalStorage("bilibili_player_codec_prefer_strategy","2"),this.onOptimzing(i.EVENTS.BUFFERING_OPTIMIZATION,{type:"hevcPerf"}))},e.prototype.getNow=function(){var e;return(null===(e=window.performance)||void 0===e?void 0:e.now())||Date.now()},e}();t.default=s},function(e,t,r){"use strict";var n=this&&this.__assign||function(){return(n=Object.assign||function(e){for(var t,r=1,n=arguments.length;r0){var s=1e3/a;e.lows.length>=43200&&e.lows.shift(),e.lows.push(s)}t=n,o>=1e3&&(s=e.frameCount/o*1e3,e.frameCount=1,e.lastFPSTime=i,e.fpsArr.length>=7200&&e.fpsArr.shift(),e.fpsArr.push(Math.floor(s)||1)),e.rafId=requestAnimationFrame(r)}))},e.prototype.onChargingStateChange=function(){this.chargingState=+this.battery.charging},e.prototype.runBatteryStateDetect=function(){var e=this;"function"==typeof navigator.getBattery&&(this.onChargingStateChange=this.onChargingStateChange.bind(this),navigator.getBattery().then((function(t){e.battery=t,e.chargingState=+t.charging,t.addEventListener("chargingchange",e.onChargingStateChange)})))},e}();t.default=i},function(e,t,r){"use strict";var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var i=n(r(10)),o=r(2),a=function(){function e(e){this.player=e,this.ncGrayPercentage=Math.random(),this.p2pGrayPercentage=Math.random(),this.ncDisable=this.getDisableValue("nc_disable"),this.p2pDisable=this.getDisableValue("rp_disable")}return Object.defineProperty(e.prototype,"isUseNC",{get:function(){return this.ncGrayPercentage>=this.ncDisable},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"isUseP2P",{get:function(){return this.p2pGrayPercentage>=this.p2pDisable},enumerable:!1,configurable:!0}),e.prototype.getDisableValue=function(e){var t=0,r=i.default.getFawkesConfigValue(o.STRING.DASH_CONFIG,e);return""!==r&&(t=Number(r)),t},e.prototype.destroy=function(){this.player=null},e}();t.default=a},function(e,t,r){"use strict";r.r(t),r.d(t,"MediaPlayer",(function(){return n.a})),r.d(t,"Protection",(function(){return X}));var n=r(9),i=function(){function e(){!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e)}return e.findCencContentProtection=function(e){for(var t=null,r=0;r0&&(t+=4+16*r.getUint32(t)),t+=4,e.slice(t)},e.getPSSHForKeySystem=function(t,r){var n=e.parsePSSHList(r);return n.hasOwnProperty(t.uuid.toLowerCase())?n[t.uuid.toLowerCase()]:null},e.parseInitDataFromContentProtection=function(e,t){return"pssh"in e?t.decodeArray(e.pssh.__text).buffer:null},e.parsePSSHList=function(e){if(null===e)return[];for(var t=new DataView(e.buffer||e),r={},n=0;;){var i,o,a=void 0,s=n;if(n>=t.buffer.byteLength)break;if(i=n+t.getUint32(n),n+=4,1886614376===t.getUint32(n))if(n+=4,0===(o=t.getUint8(n))||1===o){n++,n+=3,a="";var u=void 0,c=void 0;for(u=0;u<4;u++)a+=1===(c=t.getUint8(n+u).toString(16)).length?"0"+c:c;for(n+=4,a+="-",u=0;u<2;u++)a+=1===(c=t.getUint8(n+u).toString(16)).length?"0"+c:c;for(n+=2,a+="-",u=0;u<2;u++)a+=1===(c=t.getUint8(n+u).toString(16)).length?"0"+c:c;for(n+=2,a+="-",u=0;u<2;u++)a+=1===(c=t.getUint8(n+u).toString(16)).length?"0"+c:c;for(n+=2,a+="-",u=0;u<6;u++)a+=1===(c=t.getUint8(n+u).toString(16)).length?"0"+c:c;n+=6,a=a.toLowerCase(),t.getUint32(n),n+=4,r[a]=t.buffer.slice(s,i),n=i}else n=i;else n=i}return r},e}(),o=function e(t,r){!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),this.contentType=t,this.robustness=r},a=function e(t,r,n,i,o){!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),this.initDataTypes=["cenc"],t&&t.length&&(this.audioCapabilities=t),r&&r.length&&(this.videoCapabilities=r),this.distinctiveIdentifier=n,this.persistentState=i,this.sessionTypes=o},s="function"==typeof Symbol&&"symbol"==c(Symbol.iterator)?function(e){return c(e)}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":c(e)};function u(e){var t,r=(e=e||{}).protectionKeyController,n=e.protectionModel,u=e.eventBus,c=e.events,l=e.log,d=e.BASE64,f=e.constants,p=e.ignoreEmeEncryptedEvent,h=void 0,y=void 0,m=void 0,g=void 0,v=void 0,_=void 0;function E(e,t){var o=i.getPSSHForKeySystem(_,e),a=T(_);if(o){for(var s=n.getAllInitData(),d=0;d0?t.audioRobustness:v,s=t&&t.videoRobustness&&t.videoRobustness.length>0?t.videoRobustness:v,u=A(e);return y.forEach((function(e){e.type===f.AUDIO?r.push(new o(e.codec,i)):e.type===f.VIDEO&&n.push(new o(e.codec,s))})),new a(r,n,"optional","temporary"===u?"optional":"required",[u])}function A(e){var t=T(e);return t&&t.sessionType?t.sessionType:g}function R(e,t){var i=this,o=[];e=e.sort((function(t,r){return(m&&m[t.ks.systemString]&&m[t.ks.systemString].priority>=0?m[t.ks.systemString].priority:e.length)-(m&&m[r.ks.systemString]&&m[r.ks.systemString].priority>=0?m[r.ks.systemString].priority:e.length)}));var a=void 0;if(_)for(a=0;a0&&n.setServerCertificate(d.decodeArray(s.serverCertificate).buffer) ;for(var l=0;l0&&R(t,!0)},createKeySession:E,loadKeySession:b,removeKeySession:function(e){n.removeKeySession(e)},closeKeySession:function(e){n.closeKeySession(e)},setServerCertificate:function(e){n.setServerCertificate(e)},setMediaElement:S,setSessionType:function(e){g=e},setRobustnessLevel:function(e){v=e},setProtectionData:function(e){m=e,r.setProtectionData(e)},getSupportedKeySystemsFromContentProtection:function(e){return r.getSupportedKeySystemsFromContentProtection(e)},getKeySystems:function(){return r?r.getKeySystems():[]},reset:function(){u.off(c.INTERNAL_KEY_MESSAGE,I,this),u.off(c.INTERNAL_KEY_STATUS_CHANGED,D,this),S(null),_=void 0,n&&(n.reset(),n=null),y=[]}},h=[],y=[],g="temporary",v="",t}u.__dashjs_factory_name="ProtectionController";var l=dashjs.FactoryMaker.getClassFactory(u),d=function e(t,r){!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),this.keyID=t,this.key=r},f=function(){function e(t,r){if(function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),r&&"persistent"!==r&&"temporary"!==r)throw new Error("Invalid ClearKey key set type! Must be one of 'persistent' or 'temporary'");this.keyPairs=t,this.type=r}return e.prototype.toJWK=function(){var e=void 0,t=this.keyPairs.length,r={keys:[]};for(e=0;e%CUSTOMDATA%'.replace("%CUSTOMDATA%",e),i=[],o=0;ofigure { - anchor-name: --bofqi-example; - - &:not(.mode-fullscreen-web) { - inline-size: 980px; - min-inline-size: 980px; - block-size: calc(980px / 16 * 9 + 68px); - - @media screen and (min-width:1400px) { - - & { - inline-size: 1160px; - block-size: calc(1160px / 16 * 9 + 68px); - } - } - - @media screen and (min-width:2500px) { - - & { - inline-size: 1920px; - block-size: calc(1920px / 16 * 9 + 68px); - } - } - } - } -} \ No newline at end of file diff --git a/src/docs/index.html b/src/docs/index.html deleted file mode 100644 index 1ae0d28..0000000 --- a/src/docs/index.html +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - Bilibili 2019 - - - - - - -
-

欢迎使用Bilibili 2019的播放器组件

-

您可以点击播放器右上角的【三点】加载本地视频、弹幕、字幕等文件,来作为本地播放器使用

-
    -
  • 视频文件目前只支持 mp4 容器,且只支持您的浏览器能支持的音视频编码格式
  • -
  • 弹幕文件支持 Bilibili 标准弹幕 xml 文件
  • -
  • 字幕文件支持标准 vtt 字幕文件(不支持内封字幕
  • -
-
- - - \ No newline at end of file diff --git a/src/docs/index.ts b/src/docs/index.ts deleted file mode 100644 index 6216ab1..0000000 --- a/src/docs/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { Player } from "../player"; -import '../utils/support'; - -document.body.appendChild(new Player()); \ No newline at end of file diff --git a/src/docs/tsconfig.json b/src/docs/tsconfig.json deleted file mode 100644 index af8dc37..0000000 --- a/src/docs/tsconfig.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "extends": "../tsconfig-base.json", - "include": [ - "**/*", - "../interface", - "../player" - ], - "references": [ - { - "path": "../interface" - }, - { - "path": "../player" - } - ] -} \ No newline at end of file diff --git a/src/flv.js/README.md b/src/flv.js/README.md deleted file mode 100644 index 857754a..0000000 --- a/src/flv.js/README.md +++ /dev/null @@ -1 +0,0 @@ -flv.js 视频核心组件,提取自B站[原生js文件](https://s1.hdslb.com/bfs/static/player/main/core.3447259f.js),与[开源版本](https://github.com/bilibili/flv.js)有所不同 \ No newline at end of file diff --git a/src/flv.js/config.js b/src/flv.js/config.js deleted file mode 100644 index 853afef..0000000 --- a/src/flv.js/config.js +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2016 Bilibili. All Rights Reserved. - * - * @author zheng qian - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -export const defaultConfig = { - enableWorker: false, - enableStashBuffer: true, - stashInitialSize: undefined, - - isLive: false, - - lazyLoad: true, - lazyLoadMaxDuration: 3 * 60, - lazyLoadRecoverDuration: 30, - deferLoadAfterSourceOpen: true, - - // autoCleanupSourceBuffer: default as false, leave unspecified - autoCleanupMaxBackwardDuration: 3 * 60, - autoCleanupMinBackwardDuration: 2 * 60, - - statisticsInfoReportInterval: 600, - - fixAudioTimestampGap: true, - - accurateSeek: false, - seekType: 'range', // [range, param, custom] - seekParamStart: 'bstart', - seekParamEnd: 'bend', - rangeLoadZeroStart: false, - customSeekHandler: undefined, - reuseRedirectedURL: false, - // referrerPolicy: leave as unspecified -}; - -export function createDefaultConfig() { - return Object.assign({}, defaultConfig); -} diff --git a/src/flv.js/core/features.js b/src/flv.js/core/features.js deleted file mode 100644 index 7a6c2be..0000000 --- a/src/flv.js/core/features.js +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2016 Bilibili. All Rights Reserved. - * - * @author zheng qian - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import IOController from '../io/io-controller.js'; -import { createDefaultConfig } from '../config.js'; - -class Features { - static supportMSEH264Playback() { - return window.MediaSource && window.MediaSource.isTypeSupported('video/mp4; codecs="avc1.42E01E,mp4a.40.2"'); - } - - static supportNetworkStreamIO() { - let ioctl = new IOController({}, createDefaultConfig()); - let loaderType = ioctl.loaderType; - ioctl.destroy(); - return loaderType == 'fetch-stream-loader' || loaderType == 'xhr-moz-chunked-loader'; - } - - static getNetworkLoaderTypeName() { - let ioctl = new IOController({}, createDefaultConfig()); - let loaderType = ioctl.loaderType; - ioctl.destroy(); - return loaderType; - } - - static supportNativeMediaPlayback(mimeType) { - if (Features.videoElement == undefined) { - Features.videoElement = window.document.createElement('video'); - } - let canPlay = Features.videoElement.canPlayType(mimeType); - return canPlay === 'probably' || canPlay == 'maybe'; - } - - static getFeatureList() { - let features = { - mseFlvPlayback: false, - mseLiveFlvPlayback: false, - networkStreamIO: false, - networkLoaderName: '', - nativeMP4H264Playback: false, - nativeWebmVP8Playback: false, - nativeWebmVP9Playback: false, - }; - - features.mseFlvPlayback = Features.supportMSEH264Playback(); - features.networkStreamIO = Features.supportNetworkStreamIO(); - features.networkLoaderName = Features.getNetworkLoaderTypeName(); - features.mseLiveFlvPlayback = features.mseFlvPlayback && features.networkStreamIO; - features.nativeMP4H264Playback = Features.supportNativeMediaPlayback( - 'video/mp4; codecs="avc1.42001E, mp4a.40.2"', - ); - features.nativeWebmVP8Playback = Features.supportNativeMediaPlayback('video/webm; codecs="vp8.0, vorbis"'); - features.nativeWebmVP9Playback = Features.supportNativeMediaPlayback('video/webm; codecs="vp9"'); - - return features; - } -} - -export default Features; diff --git a/src/flv.js/core/media-info.js b/src/flv.js/core/media-info.js deleted file mode 100644 index 70399bd..0000000 --- a/src/flv.js/core/media-info.js +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright (C) 2016 Bilibili. All Rights Reserved. - * - * @author zheng qian - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -class MediaInfo { - constructor() { - this.mimeType = null; - this.duration = null; - - this.hasAudio = null; - this.hasVideo = null; - this.audioCodec = null; - this.videoCodec = null; - this.audioDataRate = null; - this.videoDataRate = null; - - this.audioSampleRate = null; - this.audioChannelCount = null; - - this.width = null; - this.height = null; - this.fps = null; - this.profile = null; - this.level = null; - this.chromaFormat = null; - this.sarNum = null; - this.sarDen = null; - - this.metadata = null; - this.segments = null; // MediaInfo[] - this.segmentCount = null; - this.hasKeyframesIndex = null; - this.keyframesIndex = null; - } - - isComplete() { - let audioInfoComplete = - this.hasAudio === false || - (this.hasAudio === true && - this.audioCodec != null && - this.audioSampleRate != null && - this.audioChannelCount != null); - - let videoInfoComplete = - this.hasVideo === false || - (this.hasVideo === true && - this.videoCodec != null && - this.width != null && - this.height != null && - this.fps != null && - this.profile != null && - this.level != null && - this.chromaFormat != null && - this.sarNum != null && - this.sarDen != null); - - // keyframesIndex may not be present - return ( - this.mimeType != null && - this.duration != null && - this.metadata != null && - this.hasKeyframesIndex != null && - audioInfoComplete && - videoInfoComplete - ); - } - - isSeekable() { - return this.hasKeyframesIndex === true; - } - - getNearestKeyframe(milliseconds) { - if (this.keyframesIndex == null) { - return null; - } - - let table = this.keyframesIndex; - let keyframeIdx = this._search(table.times, milliseconds); - - return { - index: keyframeIdx, - milliseconds: table.times[keyframeIdx], - fileposition: table.filepositions[keyframeIdx], - }; - } - - _search(list, value) { - let idx = 0; - - let last = list.length - 1; - let mid = 0; - let lbound = 0; - let ubound = last; - - if (value < list[0]) { - idx = 0; - lbound = ubound + 1; // skip search - } - - while (lbound <= ubound) { - mid = lbound + Math.floor((ubound - lbound) / 2); - if (mid === last || (value >= list[mid] && value < list[mid + 1])) { - idx = mid; - break; - } else if (list[mid] < value) { - lbound = mid + 1; - } else { - ubound = mid - 1; - } - } - - return idx; - } -} - -export default MediaInfo; diff --git a/src/flv.js/core/media-segment-info.js b/src/flv.js/core/media-segment-info.js deleted file mode 100644 index 8e5d5cc..0000000 --- a/src/flv.js/core/media-segment-info.js +++ /dev/null @@ -1,229 +0,0 @@ -/* - * Copyright (C) 2016 Bilibili. All Rights Reserved. - * - * @author zheng qian - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// Represents an media sample (audio / video) -export class SampleInfo { - constructor(dts, pts, duration, originalDts, isSync) { - this.dts = dts; - this.pts = pts; - this.duration = duration; - this.originalDts = originalDts; - this.isSyncPoint = isSync; - this.fileposition = null; - } -} - -// Media Segment concept is defined in Media Source Extensions spec. -// Particularly in ISO BMFF format, an Media Segment contains a moof box followed by a mdat box. -export class MediaSegmentInfo { - constructor() { - this.beginDts = 0; - this.endDts = 0; - this.beginPts = 0; - this.endPts = 0; - this.originalBeginDts = 0; - this.originalEndDts = 0; - this.syncPoints = []; // SampleInfo[n], for video IDR frames only - this.firstSample = null; // SampleInfo - this.lastSample = null; // SampleInfo - } - - appendSyncPoint(sampleInfo) { - // also called Random Access Point - sampleInfo.isSyncPoint = true; - this.syncPoints.push(sampleInfo); - } -} - -// Ordered list for recording video IDR frames, sorted by originalDts -export class IDRSampleList { - constructor() { - this._list = []; - } - - clear() { - this._list = []; - } - - appendArray(syncPoints) { - let list = this._list; - - if (syncPoints.length === 0) { - return; - } - - if (list.length > 0 && syncPoints[0].originalDts < list[list.length - 1].originalDts) { - this.clear(); - } - - Array.prototype.push.apply(list, syncPoints); - } - - getLastSyncPointBeforeDts(dts) { - if (this._list.length == 0) { - return null; - } - - let list = this._list; - let idx = 0; - let last = list.length - 1; - let mid = 0; - let lbound = 0; - let ubound = last; - - if (dts < list[0].dts) { - idx = 0; - lbound = ubound + 1; - } - - while (lbound <= ubound) { - mid = lbound + Math.floor((ubound - lbound) / 2); - if (mid === last || (dts >= list[mid].dts && dts < list[mid + 1].dts)) { - idx = mid; - break; - } else if (list[mid].dts < dts) { - lbound = mid + 1; - } else { - ubound = mid - 1; - } - } - return this._list[idx]; - } -} - -// Data structure for recording information of media segments in single track. -export class MediaSegmentInfoList { - constructor(type) { - this._type = type; - this._list = []; - this._lastAppendLocation = -1; // cached last insert location - } - - get type() { - return this._type; - } - - get length() { - return this._list.length; - } - - isEmpty() { - return this._list.length === 0; - } - - clear() { - this._list = []; - this._lastAppendLocation = -1; - } - - _searchNearestSegmentBefore(originalBeginDts) { - let list = this._list; - if (list.length === 0) { - return -2; - } - let last = list.length - 1; - let mid = 0; - let lbound = 0; - let ubound = last; - - let idx = 0; - - if (originalBeginDts < list[0].originalBeginDts) { - idx = -1; - return idx; - } - - while (lbound <= ubound) { - mid = lbound + Math.floor((ubound - lbound) / 2); - if ( - mid === last || - (originalBeginDts > list[mid].lastSample.originalDts && - originalBeginDts < list[mid + 1].originalBeginDts) - ) { - idx = mid; - break; - } else if (list[mid].originalBeginDts < originalBeginDts) { - lbound = mid + 1; - } else { - ubound = mid - 1; - } - } - return idx; - } - - _searchNearestSegmentAfter(originalBeginDts) { - return this._searchNearestSegmentBefore(originalBeginDts) + 1; - } - - append(mediaSegmentInfo) { - let list = this._list; - let msi = mediaSegmentInfo; - let lastAppendIdx = this._lastAppendLocation; - let insertIdx = 0; - - if ( - lastAppendIdx !== -1 && - lastAppendIdx < list.length && - msi.originalBeginDts >= list[lastAppendIdx].lastSample.originalDts && - (lastAppendIdx === list.length - 1 || - (lastAppendIdx < list.length - 1 && msi.originalBeginDts < list[lastAppendIdx + 1].originalBeginDts)) - ) { - insertIdx = lastAppendIdx + 1; // use cached location idx - } else { - if (list.length > 0) { - insertIdx = this._searchNearestSegmentBefore(msi.originalBeginDts) + 1; - } - } - - this._lastAppendLocation = insertIdx; - this._list.splice(insertIdx, 0, msi); - } - - getLastSegmentBefore(originalBeginDts) { - let idx = this._searchNearestSegmentBefore(originalBeginDts); - if (idx >= 0) { - return this._list[idx]; - } else { - // -1 - return null; - } - } - - getLastSampleBefore(originalBeginDts) { - let segment = this.getLastSegmentBefore(originalBeginDts); - if (segment != null) { - return segment.lastSample; - } else { - return null; - } - } - - getLastSyncPointBefore(originalBeginDts) { - let segmentIdx = this._searchNearestSegmentBefore(originalBeginDts); - let syncPoints = this._list[segmentIdx].syncPoints; - while (syncPoints.length === 0 && segmentIdx > 0) { - segmentIdx--; - syncPoints = this._list[segmentIdx].syncPoints; - } - if (syncPoints.length > 0) { - return syncPoints[syncPoints.length - 1]; - } else { - return null; - } - } -} diff --git a/src/flv.js/core/mse-controller.js b/src/flv.js/core/mse-controller.js deleted file mode 100644 index f3af74b..0000000 --- a/src/flv.js/core/mse-controller.js +++ /dev/null @@ -1,547 +0,0 @@ -/* - * Copyright (C) 2016 Bilibili. All Rights Reserved. - * - * @author zheng qian - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import EventEmitter from 'events'; -import Log from '../utils/logger.js'; -import Browser from '../utils/browser.js'; -import MSEEvents from './mse-events.js'; -import { SampleInfo, IDRSampleList } from './media-segment-info.js'; -import { IllegalStateException } from '../utils/exception.js'; - -// Media Source Extensions controller -class MSEController { - constructor(config) { - this.TAG = 'MSEController'; - - this._config = config; - this._emitter = new EventEmitter(); - - if (this._config.isLive && this._config.autoCleanupSourceBuffer == undefined) { - // For live stream, do auto cleanup by default - this._config.autoCleanupSourceBuffer = true; - } - - this.e = { - onSourceOpen: this._onSourceOpen.bind(this), - onSourceEnded: this._onSourceEnded.bind(this), - onSourceClose: this._onSourceClose.bind(this), - onSourceBufferError: this._onSourceBufferError.bind(this), - onSourceBufferUpdateEnd: this._onSourceBufferUpdateEnd.bind(this), - }; - - this._mediaSource = null; - this._mediaSourceObjectURL = null; - this._mediaElement = null; - - this._isBufferFull = false; - this._hasPendingEos = false; - - this._requireSetMediaDuration = false; - this._pendingMediaDuration = 0; - - this._pendingSourceBufferInit = []; - this._mimeTypes = { - video: null, - audio: null, - }; - this._sourceBuffers = { - video: null, - audio: null, - }; - this._lastInitSegments = { - video: null, - audio: null, - }; - this._pendingSegments = { - video: [], - audio: [], - }; - this._pendingRemoveRanges = { - video: [], - audio: [], - }; - this._idrList = new IDRSampleList(); - } - - destroy() { - if (this._mediaElement || this._mediaSource) { - this.detachMediaElement(); - } - this.e = null; - this._emitter.removeAllListeners(); - this._emitter = null; - } - - on(event, listener) { - this._emitter.addListener(event, listener); - } - - off(event, listener) { - this._emitter.removeListener(event, listener); - } - - attachMediaElement(mediaElement) { - if (this._mediaSource) { - throw new IllegalStateException('MediaSource has been attached to an HTMLMediaElement!'); - } - let ms = (this._mediaSource = new window.MediaSource()); - ms.addEventListener('sourceopen', this.e.onSourceOpen); - ms.addEventListener('sourceended', this.e.onSourceEnded); - ms.addEventListener('sourceclose', this.e.onSourceClose); - - this._mediaElement = mediaElement; - this._mediaSourceObjectURL = window.URL.createObjectURL(this._mediaSource); - mediaElement.src = this._mediaSourceObjectURL; - } - - detachMediaElement() { - if (this._mediaSource) { - let ms = this._mediaSource; - for (let type in this._sourceBuffers) { - // pending segments should be discard - let ps = this._pendingSegments[type]; - ps.splice(0, ps.length); - this._pendingSegments[type] = null; - this._pendingRemoveRanges[type] = null; - this._lastInitSegments[type] = null; - - // remove all sourcebuffers - let sb = this._sourceBuffers[type]; - if (sb) { - if (ms.readyState !== 'closed') { - ms.removeSourceBuffer(sb); - sb.removeEventListener('error', this.e.onSourceBufferError); - sb.removeEventListener('updateend', this.e.onSourceBufferUpdateEnd); - } - this._mimeTypes[type] = null; - this._sourceBuffers[type] = null; - } - } - if (ms.readyState === 'open') { - try { - ms.endOfStream(); - } catch (error) { - Log.e(this.TAG, error.message); - } - } - ms.removeEventListener('sourceopen', this.e.onSourceOpen); - ms.removeEventListener('sourceended', this.e.onSourceEnded); - ms.removeEventListener('sourceclose', this.e.onSourceClose); - this._pendingSourceBufferInit = []; - this._isBufferFull = false; - this._idrList.clear(); - this._mediaSource = null; - } - - if (this._mediaElement) { - this._mediaElement.src = ''; - this._mediaElement.removeAttribute('src'); - this._mediaElement = null; - } - if (this._mediaSourceObjectURL) { - window.URL.revokeObjectURL(this._mediaSourceObjectURL); - this._mediaSourceObjectURL = null; - } - } - - appendInitSegment(initSegment, deferred) { - if (!this._mediaSource || this._mediaSource.readyState !== 'open') { - // sourcebuffer creation requires mediaSource.readyState === 'open' - // so we defer the sourcebuffer creation, until sourceopen event triggered - this._pendingSourceBufferInit.push(initSegment); - // make sure that this InitSegment is in the front of pending segments queue - this._pendingSegments[initSegment.type].push(initSegment); - return; - } - - let is = initSegment; - let mimeType = `${is.container}`; - if (is.codec && is.codec.length > 0) { - mimeType += `;codecs=${is.codec}`; - } - - let firstInitSegment = false; - - Log.v(this.TAG, 'Received Initialization Segment, mimeType: ' + mimeType); - this._lastInitSegments[is.type] = is; - - if (mimeType !== this._mimeTypes[is.type]) { - if (!this._mimeTypes[is.type]) { - // empty, first chance create sourcebuffer - firstInitSegment = true; - try { - let sb = (this._sourceBuffers[is.type] = this._mediaSource.addSourceBuffer(mimeType)); - sb.addEventListener('error', this.e.onSourceBufferError); - sb.addEventListener('updateend', this.e.onSourceBufferUpdateEnd); - } catch (error) { - Log.e(this.TAG, error.message); - this._emitter.emit(MSEEvents.ERROR, { code: error.code, msg: error.message }); - return; - } - } else { - Log.v( - this.TAG, - `Notice: ${is.type} mimeType changed, origin: ${this._mimeTypes[is.type]}, target: ${mimeType}`, - ); - } - this._mimeTypes[is.type] = mimeType; - } - - if (!deferred) { - // deferred means this InitSegment has been pushed to pendingSegments queue - this._pendingSegments[is.type].push(is); - } - if (!firstInitSegment) { - // append immediately only if init segment in subsequence - if (this._sourceBuffers[is.type] && !this._sourceBuffers[is.type].updating) { - this._doAppendSegments(); - } - } - if (Browser.safari && is.container === 'audio/mpeg' && is.mediaDuration > 0) { - // 'audio/mpeg' track under Safari may cause MediaElement's duration to be NaN - // Manually correct MediaSource.duration to make progress bar seekable, and report right duration - this._requireSetMediaDuration = true; - this._pendingMediaDuration = is.mediaDuration / 1000; // in seconds - this._updateMediaSourceDuration(); - } - } - - appendMediaSegment(mediaSegment) { - let ms = mediaSegment; - this._pendingSegments[ms.type].push(ms); - - if (this._config.autoCleanupSourceBuffer && this._needCleanupSourceBuffer()) { - this._doCleanupSourceBuffer(); - } - - let sb = this._sourceBuffers[ms.type]; - if (sb && !sb.updating && !this._hasPendingRemoveRanges()) { - this._doAppendSegments(); - } - } - - seek(seconds) { - // remove all appended buffers - for (let type in this._sourceBuffers) { - if (!this._sourceBuffers[type]) { - continue; - } - - // abort current buffer append algorithm - let sb = this._sourceBuffers[type]; - if (this._mediaSource.readyState === 'open') { - try { - // If range removal algorithm is running, InvalidStateError will be throwed - // Ignore it. - sb.abort(); - } catch (error) { - Log.e(this.TAG, error.message); - } - } - - // IDRList should be clear - this._idrList.clear(); - - // pending segments should be discard - let ps = this._pendingSegments[type]; - ps.splice(0, ps.length); - - if (this._mediaSource.readyState === 'closed') { - // Parent MediaSource object has been detached from HTMLMediaElement - continue; - } - - // record ranges to be remove from SourceBuffer - for (let i = 0; i < sb.buffered.length; i++) { - let start = sb.buffered.start(i); - let end = sb.buffered.end(i); - this._pendingRemoveRanges[type].push({ start, end }); - } - - // if sb is not updating, let's remove ranges now! - if (!sb.updating) { - this._doRemoveRanges(); - } - - // Safari 10 may get InvalidStateError in the later appendBuffer() after SourceBuffer.remove() call - // Internal parser's state may be invalid at this time. Re-append last InitSegment to workaround. - // Related issue: https://bugs.webkit.org/show_bug.cgi?id=159230 - if (Browser.safari) { - let lastInitSegment = this._lastInitSegments[type]; - if (lastInitSegment) { - this._pendingSegments[type].push(lastInitSegment); - if (!sb.updating) { - this._doAppendSegments(); - } - } - } - } - } - - endOfStream() { - let ms = this._mediaSource; - let sb = this._sourceBuffers; - if (!ms || ms.readyState !== 'open') { - if (ms && ms.readyState === 'closed' && this._hasPendingSegments()) { - // If MediaSource hasn't turned into open state, and there're pending segments - // Mark pending endOfStream, defer call until all pending segments appended complete - this._hasPendingEos = true; - } - return; - } - - if (window['__FLV_P2P_TYPE__'] && this._hasPendingSegments()) { - this._doAppendSegments(); - this._hasPendingEos = true; - return; - } - - if ((sb.video && sb.video.updating) || (sb.audio && sb.audio.updating)) { - // If any sourcebuffer is updating, defer endOfStream operation - // See _onSourceBufferUpdateEnd() - this._hasPendingEos = true; - } else { - this._hasPendingEos = false; - // Notify media data loading complete - // This is helpful for correcting total duration to match last media segment - // Otherwise MediaElement's ended event may not be triggered - ms.endOfStream(); - } - } - - getNearestKeyframe(dts) { - return this._idrList.getLastSyncPointBeforeDts(dts); - } - - _needCleanupSourceBuffer() { - if (!this._config.autoCleanupSourceBuffer) { - return false; - } - - let currentTime = this._mediaElement.currentTime; - - for (let type in this._sourceBuffers) { - let sb = this._sourceBuffers[type]; - if (sb) { - let buffered = sb.buffered; - if (buffered.length >= 1) { - if (currentTime - buffered.start(0) >= this._config.autoCleanupMaxBackwardDuration) { - return true; - } - } - } - } - - return false; - } - - _doCleanupSourceBuffer() { - let currentTime = this._mediaElement.currentTime; - - for (let type in this._sourceBuffers) { - let sb = this._sourceBuffers[type]; - if (sb) { - let buffered = sb.buffered; - let doRemove = false; - - for (let i = 0; i < buffered.length; i++) { - let start = buffered.start(i); - let end = buffered.end(i); - - if (start <= currentTime && currentTime < end + 3) { - // padding 3 seconds - if (currentTime - start >= this._config.autoCleanupMaxBackwardDuration) { - doRemove = true; - let removeEnd = currentTime - this._config.autoCleanupMinBackwardDuration; - this._pendingRemoveRanges[type].push({ start: start, end: removeEnd }); - } - } else if (end < currentTime) { - doRemove = true; - this._pendingRemoveRanges[type].push({ start: start, end: end }); - } - } - - if (doRemove && !sb.updating) { - this._doRemoveRanges(); - } - } - } - } - - _updateMediaSourceDuration() { - let sb = this._sourceBuffers; - if (this._mediaElement.readyState === 0 || this._mediaSource.readyState !== 'open') { - return; - } - if ((sb.video && sb.video.updating) || (sb.audio && sb.audio.updating)) { - return; - } - - let current = this._mediaSource.duration; - let target = this._pendingMediaDuration; - - if (target > 0 && (isNaN(current) || target > current)) { - Log.v(this.TAG, `Update MediaSource duration from ${current} to ${target}`); - this._mediaSource.duration = target; - } - - this._requireSetMediaDuration = false; - this._pendingMediaDuration = 0; - } - - _doRemoveRanges() { - for (let type in this._pendingRemoveRanges) { - if (!this._sourceBuffers[type] || this._sourceBuffers[type].updating) { - continue; - } - let sb = this._sourceBuffers[type]; - let ranges = this._pendingRemoveRanges[type]; - while (ranges.length && !sb.updating) { - let range = ranges.shift(); - sb.remove(range.start, range.end); - } - } - } - - _doAppendSegments() { - let pendingSegments = this._pendingSegments; - - for (let type in pendingSegments) { - if (!this._sourceBuffers[type] || this._sourceBuffers[type].updating) { - continue; - } - - if (pendingSegments[type].length > 0) { - let segment = pendingSegments[type].shift(); - - if (segment.timestampOffset) { - // For MPEG audio stream in MSE, if unbuffered-seeking occurred - // We need explicitly set timestampOffset to the desired point in timeline for mpeg SourceBuffer. - let currentOffset = this._sourceBuffers[type].timestampOffset; - let targetOffset = segment.timestampOffset / 1000; // in seconds - - let delta = Math.abs(currentOffset - targetOffset); - if (delta > 0.1) { - // If time delta > 100ms - Log.v(this.TAG, `Update MPEG audio timestampOffset from ${currentOffset} to ${targetOffset}`); - this._sourceBuffers[type].timestampOffset = targetOffset; - } - delete segment.timestampOffset; - } - - if (!segment.data || segment.data.byteLength === 0) { - // Ignore empty buffer - continue; - } - - try { - this._sourceBuffers[type].appendBuffer(segment.data); - this._isBufferFull = false; - if (type === 'video' && segment.hasOwnProperty('info')) { - this._idrList.appendArray(segment.info.syncPoints); - } - } catch (error) { - this._pendingSegments[type].unshift(segment); - if (error.code === 22) { - // QuotaExceededError - /* Notice that FireFox may not throw QuotaExceededError if SourceBuffer is full - * Currently we can only do lazy-load to avoid SourceBuffer become scattered. - * SourceBuffer eviction policy may be changed in future version of FireFox. - * - * Related issues: - * https://bugzilla.mozilla.org/show_bug.cgi?id=1279885 - * https://bugzilla.mozilla.org/show_bug.cgi?id=1280023 - */ - - // report buffer full, abort network IO - if (!this._isBufferFull) { - this._emitter.emit(MSEEvents.BUFFER_FULL); - } - this._isBufferFull = true; - } else { - Log.e(this.TAG, error.message); - this._emitter.emit(MSEEvents.ERROR, { code: error.code, msg: error.message }); - } - } - } - } - } - - _onSourceOpen() { - Log.v(this.TAG, 'MediaSource onSourceOpen'); - this._mediaSource.removeEventListener('sourceopen', this.e.onSourceOpen); - // deferred sourcebuffer creation / initialization - if (this._pendingSourceBufferInit.length > 0) { - let pendings = this._pendingSourceBufferInit; - while (pendings.length) { - let segment = pendings.shift(); - this.appendInitSegment(segment, true); - } - } - // there may be some pending media segments, append them - if (this._hasPendingSegments()) { - this._doAppendSegments(); - } - this._emitter.emit(MSEEvents.SOURCE_OPEN); - } - - _onSourceEnded() { - // fired on endOfStream - Log.v(this.TAG, 'MediaSource onSourceEnded'); - } - - _onSourceClose() { - // fired on detaching from media element - Log.v(this.TAG, 'MediaSource onSourceClose'); - if (this._mediaSource && this.e != null) { - this._mediaSource.removeEventListener('sourceopen', this.e.onSourceOpen); - this._mediaSource.removeEventListener('sourceended', this.e.onSourceEnded); - this._mediaSource.removeEventListener('sourceclose', this.e.onSourceClose); - } - } - - _hasPendingSegments() { - let ps = this._pendingSegments; - return ps.video.length > 0 || ps.audio.length > 0; - } - - _hasPendingRemoveRanges() { - let prr = this._pendingRemoveRanges; - return prr.video.length > 0 || prr.audio.length > 0; - } - - _onSourceBufferUpdateEnd() { - if (this._requireSetMediaDuration) { - this._updateMediaSourceDuration(); - } else if (this._hasPendingRemoveRanges()) { - this._doRemoveRanges(); - } else if (this._hasPendingSegments()) { - this._doAppendSegments(); - } else if (this._hasPendingEos) { - this.endOfStream(); - } - this._emitter.emit(MSEEvents.UPDATE_END); - } - - _onSourceBufferError(e) { - Log.e(this.TAG, `SourceBuffer Error: ${e}`); - // this error might not always be fatal, just ignore it - } -} - -export default MSEController; diff --git a/src/flv.js/core/mse-events.js b/src/flv.js/core/mse-events.js deleted file mode 100644 index d7c66b5..0000000 --- a/src/flv.js/core/mse-events.js +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2016 Bilibili. All Rights Reserved. - * - * @author zheng qian - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -const MSEEvents = { - ERROR: 'error', - SOURCE_OPEN: 'source_open', - UPDATE_END: 'update_end', - BUFFER_FULL: 'buffer_full', -}; - -export default MSEEvents; diff --git a/src/flv.js/core/transmuxer.js b/src/flv.js/core/transmuxer.js deleted file mode 100644 index 66668b5..0000000 --- a/src/flv.js/core/transmuxer.js +++ /dev/null @@ -1,247 +0,0 @@ -/* - * Copyright (C) 2016 Bilibili. All Rights Reserved. - * - * @author zheng qian - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import EventEmitter from 'events'; -import Log from '../utils/logger.js'; -import LoggingControl from '../utils/logging-control.js'; -import TransmuxingController from './transmuxing-controller.js'; -import TransmuxingEvents from './transmuxing-events.js'; -import TransmuxingWorker from './transmuxing-worker.js'; -import MediaInfo from './media-info.js'; -import { OTHER_EVENTS_POLYMER } from '../player/player-events.js'; - -class Transmuxer { - constructor(mediaDataSource, config, mediaElement) { - this.TAG = 'Transmuxer'; - this._emitter = new EventEmitter(); - this._mediaElement = mediaElement; - - if (config.enableWorker && typeof Worker !== 'undefined') { - try { - let work = require('webworkify'); - this._worker = work(TransmuxingWorker); - this._workerDestroying = false; - this._worker.addEventListener('message', this._onWorkerMessage.bind(this)); - this._worker.postMessage({ cmd: 'init', param: [mediaDataSource, config, this._mediaElement] }); - this.e = { - onLoggingConfigChanged: this._onLoggingConfigChanged.bind(this), - }; - LoggingControl.registerListener(this.e.onLoggingConfigChanged); - this._worker.postMessage({ cmd: 'logging_config', param: LoggingControl.getConfig() }); - } catch (error) { - Log.e(this.TAG, 'Error while initialize transmuxing worker, fallback to inline transmuxing'); - this._worker = null; - this._controller = new TransmuxingController(mediaDataSource, config, this._mediaElement); - } - } else { - this._controller = new TransmuxingController(mediaDataSource, config, this._mediaElement); - } - - if (this._controller) { - let ctl = this._controller; - ctl.on(TransmuxingEvents.IO_ERROR, this._onIOError.bind(this)); - ctl.on(TransmuxingEvents.DEMUX_ERROR, this._onDemuxError.bind(this)); - ctl.on(TransmuxingEvents.INIT_SEGMENT, this._onInitSegment.bind(this)); - ctl.on(TransmuxingEvents.MEDIA_SEGMENT, this._onMediaSegment.bind(this)); - ctl.on(TransmuxingEvents.LOADING_COMPLETE, this._onLoadingComplete.bind(this)); - ctl.on(TransmuxingEvents.RECOVERED_EARLY_EOF, this._onRecoveredEarlyEof.bind(this)); - ctl.on(TransmuxingEvents.MEDIA_INFO, this._onMediaInfo.bind(this)); - ctl.on(TransmuxingEvents.STATISTICS_INFO, this._onStatisticsInfo.bind(this)); - ctl.on(TransmuxingEvents.RECOMMEND_SEEKPOINT, this._onRecommendSeekpoint.bind(this)); - ctl.on(OTHER_EVENTS_POLYMER, (obj) => this._emitter.emit(OTHER_EVENTS_POLYMER, obj)); - } - } - - destroy() { - if (this._worker) { - if (!this._workerDestroying) { - this._workerDestroying = true; - this._worker.postMessage({ cmd: 'destroy' }); - LoggingControl.removeListener(this.e.onLoggingConfigChanged); - this.e = null; - } - } else { - this._controller.destroy(); - this._controller = null; - } - this._emitter.removeAllListeners(); - this._emitter = null; - } - - on(event, listener) { - this._emitter.addListener(event, listener); - } - - off(event, listener) { - this._emitter.removeListener(event, listener); - } - - hasWorker() { - return this._worker != null; - } - - open() { - if (this._worker) { - this._worker.postMessage({ cmd: 'start' }); - } else { - this._controller.start(); - } - } - - close() { - if (this._worker) { - this._worker.postMessage({ cmd: 'stop' }); - } else { - this._controller.stop(); - } - } - - seek(milliseconds) { - if (this._worker) { - this._worker.postMessage({ cmd: 'seek', param: milliseconds }); - } else { - this._controller.seek(milliseconds); - } - } - - pause() { - if (this._worker) { - this._worker.postMessage({ cmd: 'pause' }); - } else { - this._controller.pause(); - } - } - - resume() { - if (this._worker) { - this._worker.postMessage({ cmd: 'resume' }); - } else { - this._controller.resume(); - } - } - - _onInitSegment(type, initSegment) { - // do async invoke - Promise.resolve().then(() => { - this._emitter.emit(TransmuxingEvents.INIT_SEGMENT, type, initSegment); - }); - } - - _onMediaSegment(type, mediaSegment) { - Promise.resolve().then(() => { - this._emitter.emit(TransmuxingEvents.MEDIA_SEGMENT, type, mediaSegment); - }); - } - - _onLoadingComplete(from, to, requestUrl) { - Promise.resolve().then(() => { - this._emitter.emit(TransmuxingEvents.LOADING_COMPLETE, from, to, requestUrl); - }); - } - - _onRecoveredEarlyEof() { - Promise.resolve().then(() => { - this._emitter.emit(TransmuxingEvents.RECOVERED_EARLY_EOF); - }); - } - - _onMediaInfo(mediaInfo) { - Promise.resolve().then(() => { - this._emitter.emit(TransmuxingEvents.MEDIA_INFO, mediaInfo); - }); - } - - _onStatisticsInfo(statisticsInfo) { - Promise.resolve().then(() => { - this._emitter.emit(TransmuxingEvents.STATISTICS_INFO, statisticsInfo); - }); - } - - _onIOError(type, info) { - Promise.resolve().then(() => { - this._emitter.emit(TransmuxingEvents.IO_ERROR, type, info); - }); - } - - _onDemuxError(type, info) { - Promise.resolve().then(() => { - this._emitter.emit(TransmuxingEvents.DEMUX_ERROR, type, info); - }); - } - - _onRecommendSeekpoint(milliseconds) { - Promise.resolve().then(() => { - this._emitter.emit(TransmuxingEvents.RECOMMEND_SEEKPOINT, milliseconds); - }); - } - - _onLoggingConfigChanged(config) { - if (this._worker) { - this._worker.postMessage({ cmd: 'logging_config', param: config }); - } - } - - _onWorkerMessage(e) { - let message = e.data; - let data = message.data; - - if (message.msg === 'destroyed' || this._workerDestroying) { - this._workerDestroying = false; - this._worker.terminate(); - this._worker = null; - return; - } - - switch (message.msg) { - case OTHER_EVENTS_POLYMER: - this._emitter.emit(OTHER_EVENTS_POLYMER, data); - break; - case TransmuxingEvents.INIT_SEGMENT: - case TransmuxingEvents.MEDIA_SEGMENT: - this._emitter.emit(message.msg, data.type, data.data); - break; - case TransmuxingEvents.LOADING_COMPLETE: - this._emitter.emit(message.msg, data.from, data.to, data.requestUrl); - break; - case TransmuxingEvents.RECOVERED_EARLY_EOF: - this._emitter.emit(message.msg); - break; - case TransmuxingEvents.MEDIA_INFO: - Object.setPrototypeOf(data, MediaInfo.prototype); - this._emitter.emit(message.msg, data); - break; - case TransmuxingEvents.STATISTICS_INFO: - this._emitter.emit(message.msg, data); - break; - case TransmuxingEvents.IO_ERROR: - case TransmuxingEvents.DEMUX_ERROR: - this._emitter.emit(message.msg, data.type, data.info); - break; - case TransmuxingEvents.RECOMMEND_SEEKPOINT: - this._emitter.emit(message.msg, data); - break; - case 'logcat_callback': - Log.emitter.emit('log', data.type, data.logcat); - break; - default: - break; - } - } -} - -export default Transmuxer; diff --git a/src/flv.js/core/transmuxing-controller.js b/src/flv.js/core/transmuxing-controller.js deleted file mode 100644 index 5bf15b6..0000000 --- a/src/flv.js/core/transmuxing-controller.js +++ /dev/null @@ -1,466 +0,0 @@ -/* - * Copyright (C) 2016 Bilibili. All Rights Reserved. - * - * @author zheng qian - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import EventEmitter from 'events'; -import Log from '../utils/logger.js'; -import Browser from '../utils/browser.js'; -import MediaInfo from './media-info.js'; -import FLVDemuxer from '../demux/flv-demuxer.js'; -import MP4Remuxer from '../remux/mp4-remuxer.js'; -import DemuxErrors from '../demux/demux-errors.js'; -import IOController from '../io/io-controller.js'; -import TransmuxingEvents from './transmuxing-events.js'; -import { LoaderStatus, LoaderErrors } from '../io/loader.js'; -import { OTHER_EVENTS_POLYMER } from '../player/player-events.js'; - -// Transmuxing (IO, Demuxing, Remuxing) controller, with multipart support -class TransmuxingController { - constructor(mediaDataSource, config, mediaElement) { - this.TAG = 'TransmuxingController'; - this._emitter = new EventEmitter(); - - this._config = config; - this._mediaElement = mediaElement; - - // treat single part media as multipart media, which has only one segment - if (!mediaDataSource.segments) { - mediaDataSource.segments = [ - { - duration: mediaDataSource.duration, - filesize: mediaDataSource.filesize, - url: mediaDataSource.url, - }, - ]; - } - - // fill in default IO params if not exists - if (typeof mediaDataSource.cors !== 'boolean') { - mediaDataSource.cors = true; - } - if (typeof mediaDataSource.withCredentials !== 'boolean') { - mediaDataSource.withCredentials = false; - } - - this._mediaDataSource = mediaDataSource; - this._currentSegmentIndex = 0; - let totalDuration = 0; - - this._mediaDataSource.segments.forEach((segment) => { - // timestampBase for each segment, and calculate total duration - segment.timestampBase = totalDuration; - totalDuration += segment.duration; - // params needed by IOController - segment.cors = mediaDataSource.cors; - segment.withCredentials = mediaDataSource.withCredentials; - // referrer policy control, if exist - if (config.referrerPolicy) { - segment.referrerPolicy = config.referrerPolicy; - } - }); - - if (!isNaN(totalDuration) && this._mediaDataSource.duration !== totalDuration) { - this._mediaDataSource.duration = totalDuration; - } - - this._mediaInfo = null; - this._demuxer = null; - this._remuxer = null; - this._ioctl = null; - - this._pendingSeekTime = null; - this._pendingResolveSeekPoint = null; - - this._statisticsReporter = null; - } - - destroy() { - this._mediaInfo = null; - this._mediaDataSource = null; - - if (this._statisticsReporter) { - this._disableStatisticsReporter(); - } - if (this._ioctl) { - this._ioctl.destroy(); - this._ioctl = null; - } - if (this._demuxer) { - this._demuxer.destroy(); - this._demuxer = null; - } - if (this._remuxer) { - this._remuxer.destroy(); - this._remuxer = null; - } - - this._emitter.removeAllListeners(); - this._emitter = null; - } - - on(event, listener) { - this._emitter.addListener(event, listener); - } - - off(event, listener) { - this._emitter.removeListener(event, listener); - } - - start() { - this._loadSegment(0); - this._enableStatisticsReporter(); - } - - _loadSegment(segmentIndex, optionalFrom, bNewSlice) { - this._currentSegmentIndex = segmentIndex; - let dataSource = this._mediaDataSource.segments[segmentIndex]; - - let ioctl = (this._ioctl = new IOController( - dataSource, - this._config, - segmentIndex, - this._mediaElement, - bNewSlice, - )); - ioctl.onError = this._onIOException.bind(this); - ioctl.onSeeked = this._onIOSeeked.bind(this); - ioctl.onComplete = this._onIOComplete.bind(this); - ioctl.onRedirect = this._onIORedirect.bind(this); - ioctl.onRecoveredEarlyEof = this._onIORecoveredEarlyEof.bind(this); - ioctl.on(OTHER_EVENTS_POLYMER, (obj) => this._emitter.emit(OTHER_EVENTS_POLYMER, obj)); - - if (optionalFrom) { - this._demuxer.bindDataSource(this._ioctl); - } else { - ioctl.onDataArrival = this._onInitChunkArrival.bind(this); - } - - ioctl.open(optionalFrom); - } - - stop() { - this._internalAbort(); - this._disableStatisticsReporter(); - } - - _internalAbort() { - if (this._ioctl) { - this._ioctl.destroy(); - this._ioctl = null; - } - } - - pause() { - // take a rest - if (this._ioctl && this._ioctl.isWorking()) { - this._ioctl.pause(); - this._disableStatisticsReporter(); - } - } - - resume() { - if (this._ioctl && this._ioctl.isPaused()) { - this._ioctl.resume(); - this._enableStatisticsReporter(); - } - } - - seek(milliseconds) { - if (this._mediaInfo == null || !this._mediaInfo.isSeekable()) { - return; - } - - let targetSegmentIndex = this._searchSegmentIndexContains(milliseconds); - - if (targetSegmentIndex === this._currentSegmentIndex) { - // intra-segment seeking - let segmentInfo = this._mediaInfo.segments[targetSegmentIndex]; - - if (segmentInfo == undefined) { - // current segment loading started, but mediainfo hasn't received yet - // wait for the metadata loaded, then seek to expected position - this._pendingSeekTime = milliseconds; - } else { - let keyframe = segmentInfo.getNearestKeyframe(milliseconds); - this._remuxer.seek(keyframe.milliseconds); - this._ioctl.seek(keyframe.fileposition); - // Will be resolved in _onRemuxerMediaSegmentArrival() - this._pendingResolveSeekPoint = keyframe.milliseconds; - } - } else { - // cross-segment seeking - let targetSegmentInfo = this._mediaInfo.segments[targetSegmentIndex]; - - if (targetSegmentInfo == undefined) { - // target segment hasn't been loaded. We need metadata then seek to expected time - this._pendingSeekTime = milliseconds; - this._internalAbort(); - this._remuxer.seek(); - this._remuxer.insertDiscontinuity(); - // change slice - this._loadSegment(targetSegmentIndex, undefined, true); - // Here we wait for the metadata loaded, then seek to expected position - } else { - // We have target segment's metadata, direct seek to target position - let keyframe = targetSegmentInfo.getNearestKeyframe(milliseconds); - this._internalAbort(); - this._remuxer.seek(milliseconds); - this._remuxer.insertDiscontinuity(); - this._demuxer.resetMediaInfo(); - this._demuxer.timestampBase = this._mediaDataSource.segments[targetSegmentIndex].timestampBase; - this._loadSegment(targetSegmentIndex, keyframe.fileposition); - this._pendingResolveSeekPoint = keyframe.milliseconds; - this._reportSegmentMediaInfo(targetSegmentIndex); - } - } - - this._enableStatisticsReporter(); - } - - _searchSegmentIndexContains(milliseconds) { - let segments = this._mediaDataSource.segments; - let idx = segments.length - 1; - - for (let i = 0; i < segments.length; i++) { - if (milliseconds < segments[i].timestampBase) { - idx = i - 1; - break; - } - } - return idx; - } - - _onInitChunkArrival(data, byteStart) { - let probeData = null; - let consumed = 0; - - if (byteStart > 0) { - // IOController seeked immediately after opened, byteStart > 0 callback may received - this._demuxer.bindDataSource(this._ioctl); - this._demuxer.timestampBase = this._mediaDataSource.segments[this._currentSegmentIndex].timestampBase; - - consumed = this._demuxer.parseChunks(data, byteStart); - } else if ((probeData = FLVDemuxer.probe(data)).match) { - // Always create new FLVDemuxer - this._demuxer = new FLVDemuxer(probeData, this._config); - - if (!this._remuxer) { - this._remuxer = new MP4Remuxer(this._config); - } - - let mds = this._mediaDataSource; - if (mds.duration != undefined && !isNaN(mds.duration)) { - this._demuxer.overridedDuration = mds.duration; - } - if (typeof mds.hasAudio === 'boolean') { - this._demuxer.overridedHasAudio = mds.hasAudio; - } - if (typeof mds.hasVideo === 'boolean') { - this._demuxer.overridedHasVideo = mds.hasVideo; - } - - this._demuxer.timestampBase = mds.segments[this._currentSegmentIndex].timestampBase; - - this._demuxer.onError = this._onDemuxException.bind(this); - this._demuxer.onMediaInfo = this._onMediaInfo.bind(this); - this._demuxer.onFlvFrameDecoded = this._onFlvFrameDecoded.bind(this); - this._demuxer.onCtsWarning = this._onCtsWarning.bind(this); - this._demuxer.onFLVDataAbnormal = this._onFLVDataAbnormal.bind(this); - - this._remuxer.bindDataSource(this._demuxer.bindDataSource(this._ioctl)); - - this._remuxer.onInitSegment = this._onRemuxerInitSegmentArrival.bind(this); - this._remuxer.onMediaSegment = this._onRemuxerMediaSegmentArrival.bind(this); - - consumed = this._demuxer.parseChunks(data, byteStart); - } else { - probeData = null; - Log.e(this.TAG, 'Non-FLV, Unsupported media type!'); - Promise.resolve().then(() => { - this._internalAbort(); - }); - this._emitter.emit( - TransmuxingEvents.DEMUX_ERROR, - DemuxErrors.FORMAT_UNSUPPORTED, - 'Non-FLV, Unsupported media type', - ); - - consumed = 0; - } - - return consumed; - } - - _onFlvFrameDecoded(eventName, timestamp) { - this._emitter.emit(OTHER_EVENTS_POLYMER, { - eventName: eventName, - eventParams: [timestamp], - }); - } - - _onCtsWarning(eventName, params) { - this._emitter.emit(OTHER_EVENTS_POLYMER, { - eventName: eventName, - eventParams: [params], - }); - } - - _onFLVDataAbnormal(eventName, params) { - this._emitter.emit(OTHER_EVENTS_POLYMER, { - eventName: eventName, - eventParams: [params], - }); - } - - _onMediaInfo(mediaInfo) { - if (this._mediaInfo == null) { - // Store first segment's mediainfo as global mediaInfo - this._mediaInfo = Object.assign({}, mediaInfo); - this._mediaInfo.keyframesIndex = null; - this._mediaInfo.segments = []; - this._mediaInfo.segmentCount = this._mediaDataSource.segments.length; - Object.setPrototypeOf(this._mediaInfo, MediaInfo.prototype); - } - - let segmentInfo = Object.assign({}, mediaInfo); - Object.setPrototypeOf(segmentInfo, MediaInfo.prototype); - this._mediaInfo.segments[this._currentSegmentIndex] = segmentInfo; - - // notify mediaInfo update - this._reportSegmentMediaInfo(this._currentSegmentIndex); - - if (this._pendingSeekTime != null) { - Promise.resolve().then(() => { - let target = this._pendingSeekTime; - this._pendingSeekTime = null; - this.seek(target); - }); - } - } - - _onIOSeeked() { - this._remuxer.insertDiscontinuity(); - } - - _onIOComplete(extraData, from, to, requestUrl) { - let segmentIndex = extraData; - let nextSegmentIndex = segmentIndex + 1; - - if (nextSegmentIndex < this._mediaDataSource.segments.length) { - this._internalAbort(); - // change slice - this._loadSegment(nextSegmentIndex, undefined, true); - } else { - this._emitter.emit(TransmuxingEvents.LOADING_COMPLETE, from, to, requestUrl); - this._disableStatisticsReporter(); - } - } - - _onIORedirect(redirectedURL) { - let segmentIndex = this._ioctl.extraData; - this._mediaDataSource.segments[segmentIndex].redirectedURL = redirectedURL; - } - - _onIORecoveredEarlyEof() { - this._emitter.emit(TransmuxingEvents.RECOVERED_EARLY_EOF); - } - - _onIOException(type, info) { - Log.e(this.TAG, `IOException: type = ${type}, code = ${info.code}, msg = ${info.msg}`); - this._emitter.emit(TransmuxingEvents.IO_ERROR, type, info); - this._disableStatisticsReporter(); - } - - _onDemuxException(type, info) { - Log.e(this.TAG, `DemuxException: type = ${type}, info = ${info}`); - this._emitter.emit(TransmuxingEvents.DEMUX_ERROR, type, info); - } - - _onRemuxerInitSegmentArrival(type, initSegment) { - this._emitter.emit(TransmuxingEvents.INIT_SEGMENT, type, initSegment); - } - - _onRemuxerMediaSegmentArrival(type, mediaSegment) { - if (this._pendingSeekTime != null) { - // Media segments after new-segment cross-seeking should be dropped. - return; - } - this._emitter.emit(TransmuxingEvents.MEDIA_SEGMENT, type, mediaSegment); - - // Resolve pending seekPoint - if (this._pendingResolveSeekPoint != null && type === 'video') { - let syncPoints = mediaSegment.info.syncPoints; - let seekpoint = this._pendingResolveSeekPoint; - this._pendingResolveSeekPoint = null; - - // Safari: Pass PTS for recommend_seekpoint - if (Browser.safari && syncPoints.length > 0 && syncPoints[0].originalDts === seekpoint) { - seekpoint = syncPoints[0].pts; - } - // else: use original DTS (keyframe.milliseconds) - - this._emitter.emit(TransmuxingEvents.RECOMMEND_SEEKPOINT, seekpoint); - } - } - - _enableStatisticsReporter() { - if (this._statisticsReporter == null) { - this._statisticsReporter = self.setInterval( - this._reportStatisticsInfo.bind(this), - this._config.statisticsInfoReportInterval, - ); - } - } - - _disableStatisticsReporter() { - if (this._statisticsReporter) { - self.clearInterval(this._statisticsReporter); - this._statisticsReporter = null; - } - } - - _reportSegmentMediaInfo(segmentIndex) { - let segmentInfo = this._mediaInfo.segments[segmentIndex]; - let exportInfo = Object.assign({}, segmentInfo); - - exportInfo.duration = this._mediaInfo.duration; - exportInfo.segmentCount = this._mediaInfo.segmentCount; - delete exportInfo.segments; - delete exportInfo.keyframesIndex; - - this._emitter.emit(TransmuxingEvents.MEDIA_INFO, exportInfo); - } - - _reportStatisticsInfo() { - let info = {}; - - info.url = this._ioctl.currentURL; - info.hasRedirect = this._ioctl.hasRedirect; - if (info.hasRedirect) { - info.redirectedURL = this._ioctl.currentRedirectedURL; - } - - info.speed = this._ioctl.currentSpeed; - info.loaderType = this._ioctl.loaderType; - info.currentSegmentIndex = this._currentSegmentIndex; - info.totalSegmentCount = this._mediaDataSource.segments.length; - - this._emitter.emit(TransmuxingEvents.STATISTICS_INFO, info); - } -} - -export default TransmuxingController; diff --git a/src/flv.js/core/transmuxing-events.js b/src/flv.js/core/transmuxing-events.js deleted file mode 100644 index f87b1a1..0000000 --- a/src/flv.js/core/transmuxing-events.js +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2016 Bilibili. All Rights Reserved. - * - * @author zheng qian - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -const TransmuxingEvents = { - IO_ERROR: 'io_error', - DEMUX_ERROR: 'demux_error', - INIT_SEGMENT: 'init_segment', - MEDIA_SEGMENT: 'media_segment', - LOADING_COMPLETE: 'loading_complete', - RECOVERED_EARLY_EOF: 'recovered_early_eof', - MEDIA_INFO: 'media_info', - STATISTICS_INFO: 'statistics_info', - RECOMMEND_SEEKPOINT: 'recommend_seekpoint', -}; - -export default TransmuxingEvents; diff --git a/src/flv.js/core/transmuxing-worker.js b/src/flv.js/core/transmuxing-worker.js deleted file mode 100644 index 3d12c1e..0000000 --- a/src/flv.js/core/transmuxing-worker.js +++ /dev/null @@ -1,199 +0,0 @@ -/* - * Copyright (C) 2016 Bilibili. All Rights Reserved. - * - * @author zheng qian - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import Log from '../utils/logger.js'; -import LoggingControl from '../utils/logging-control.js'; -import Polyfill from '../utils/polyfill.js'; -import TransmuxingController from './transmuxing-controller.js'; -import TransmuxingEvents from './transmuxing-events.js'; -import { OTHER_EVENTS_POLYMER } from '../player/player-events.js'; - -/* post message to worker: - data: { - cmd: string - param: any - } - - receive message from worker: - data: { - msg: string, - data: any - } - */ - -let TransmuxingWorker = function (self) { - let TAG = 'TransmuxingWorker'; - let controller = null; - let logcatListener = onLogcatCallback.bind(this); - - Polyfill.install(); - - self.addEventListener('message', function (e) { - switch (e.data.cmd) { - case 'init': - controller = new TransmuxingController(e.data.param[0], e.data.param[1], e.data.param[2]); - controller.on(TransmuxingEvents.IO_ERROR, onIOError.bind(this)); - controller.on(TransmuxingEvents.DEMUX_ERROR, onDemuxError.bind(this)); - controller.on(TransmuxingEvents.INIT_SEGMENT, onInitSegment.bind(this)); - controller.on(TransmuxingEvents.MEDIA_SEGMENT, onMediaSegment.bind(this)); - controller.on(TransmuxingEvents.LOADING_COMPLETE, onLoadingComplete.bind(this)); - controller.on(TransmuxingEvents.RECOVERED_EARLY_EOF, onRecoveredEarlyEof.bind(this)); - controller.on(TransmuxingEvents.MEDIA_INFO, onMediaInfo.bind(this)); - controller.on(TransmuxingEvents.STATISTICS_INFO, onStatisticsInfo.bind(this)); - controller.on(TransmuxingEvents.RECOMMEND_SEEKPOINT, onRecommendSeekpoint.bind(this)); - controller.on(OTHER_EVENTS_POLYMER, onOtherEventsPolymer.bind(this)); - break; - case 'destroy': - if (controller) { - controller.destroy(); - controller = null; - } - self.postMessage({ msg: 'destroyed' }); - break; - case 'start': - controller.start(); - break; - case 'stop': - controller.stop(); - break; - case 'seek': - controller.seek(e.data.param); - break; - case 'pause': - controller.pause(); - break; - case 'resume': - controller.resume(); - break; - case 'logging_config': { - let config = e.data.param; - LoggingControl.applyConfig(config); - - if (config.enableCallback === true) { - LoggingControl.addLogListener(logcatListener); - } else { - LoggingControl.removeLogListener(logcatListener); - } - break; - } - } - }); - - function onOtherEventsPolymer(obj) { - self.postMessage({ - msg: OTHER_EVENTS_POLYMER, - data: obj, - }); - } - - function onInitSegment(type, initSegment) { - let obj = { - msg: TransmuxingEvents.INIT_SEGMENT, - data: { - type: type, - data: initSegment, - }, - }; - self.postMessage(obj, [initSegment.data]); // data: ArrayBuffer - } - - function onMediaSegment(type, mediaSegment) { - let obj = { - msg: TransmuxingEvents.MEDIA_SEGMENT, - data: { - type: type, - data: mediaSegment, - }, - }; - self.postMessage(obj, [mediaSegment.data]); // data: ArrayBuffer - } - - function onLoadingComplete(from, to, requestUrl) { - let obj = { - msg: TransmuxingEvents.LOADING_COMPLETE, - data: { - requestUrl: requestUrl, - from: from, - to: to, - }, - }; - self.postMessage(obj); - } - - function onRecoveredEarlyEof() { - let obj = { - msg: TransmuxingEvents.RECOVERED_EARLY_EOF, - }; - self.postMessage(obj); - } - - function onMediaInfo(mediaInfo) { - let obj = { - msg: TransmuxingEvents.MEDIA_INFO, - data: mediaInfo, - }; - self.postMessage(obj); - } - - function onStatisticsInfo(statInfo) { - let obj = { - msg: TransmuxingEvents.STATISTICS_INFO, - data: statInfo, - }; - self.postMessage(obj); - } - - function onIOError(type, info) { - self.postMessage({ - msg: TransmuxingEvents.IO_ERROR, - data: { - type: type, - info: info, - }, - }); - } - - function onDemuxError(type, info) { - self.postMessage({ - msg: TransmuxingEvents.DEMUX_ERROR, - data: { - type: type, - info: info, - }, - }); - } - - function onRecommendSeekpoint(milliseconds) { - self.postMessage({ - msg: TransmuxingEvents.RECOMMEND_SEEKPOINT, - data: milliseconds, - }); - } - - function onLogcatCallback(type, str) { - self.postMessage({ - msg: 'logcat_callback', - data: { - type: type, - logcat: str, - }, - }); - } -}; - -export default TransmuxingWorker; diff --git a/src/flv.js/demux/amf-parser.js b/src/flv.js/demux/amf-parser.js deleted file mode 100644 index e129141..0000000 --- a/src/flv.js/demux/amf-parser.js +++ /dev/null @@ -1,248 +0,0 @@ -/* - * Copyright (C) 2016 Bilibili. All Rights Reserved. - * - * @author zheng qian - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import Log from '../utils/logger.js'; -import decodeUTF8 from '../utils/utf8-conv.js'; -import { IllegalStateException } from '../utils/exception.js'; - -let le = (function () { - let buf = new ArrayBuffer(2); - new DataView(buf).setInt16(0, 256, true); // little-endian write - return new Int16Array(buf)[0] === 256; // platform-spec read, if equal then LE -})(); - -class AMF { - static parseScriptData(arrayBuffer, dataOffset, dataSize) { - let data = {}; - - try { - let name = AMF.parseValue(arrayBuffer, dataOffset, dataSize); - let value = AMF.parseValue(arrayBuffer, dataOffset + name.size, dataSize - name.size); - - data[name.data] = value.data; - } catch (e) { - Log.e('AMF', e.toString()); - } - - return data; - } - - static parseObject(arrayBuffer, dataOffset, dataSize) { - if (dataSize < 3) { - throw new IllegalStateException('Data not enough when parse ScriptDataObject'); - } - let name = AMF.parseString(arrayBuffer, dataOffset, dataSize); - let value = AMF.parseValue(arrayBuffer, dataOffset + name.size, dataSize - name.size); - let isObjectEnd = value.objectEnd; - - return { - data: { - name: name.data, - value: value.data, - }, - size: name.size + value.size, - objectEnd: isObjectEnd, - }; - } - - static parseVariable(arrayBuffer, dataOffset, dataSize) { - return AMF.parseObject(arrayBuffer, dataOffset, dataSize); - } - - static parseString(arrayBuffer, dataOffset, dataSize) { - if (dataSize < 2) { - throw new IllegalStateException('Data not enough when parse String'); - } - let v = new DataView(arrayBuffer, dataOffset, dataSize); - let length = v.getUint16(0, !le); - - let str; - if (length > 0) { - str = decodeUTF8(new Uint8Array(arrayBuffer, dataOffset + 2, length)); - } else { - str = ''; - } - - return { - data: str, - size: 2 + length, - }; - } - - static parseLongString(arrayBuffer, dataOffset, dataSize) { - if (dataSize < 4) { - throw new IllegalStateException('Data not enough when parse LongString'); - } - let v = new DataView(arrayBuffer, dataOffset, dataSize); - let length = v.getUint32(0, !le); - - let str; - if (length > 0) { - str = decodeUTF8(new Uint8Array(arrayBuffer, dataOffset + 4, length)); - } else { - str = ''; - } - - return { - data: str, - size: 4 + length, - }; - } - - static parseDate(arrayBuffer, dataOffset, dataSize) { - if (dataSize < 10) { - throw new IllegalStateException('Data size invalid when parse Date'); - } - let v = new DataView(arrayBuffer, dataOffset, dataSize); - let timestamp = v.getFloat64(0, !le); - let localTimeOffset = v.getInt16(8, !le); - timestamp += localTimeOffset * 60 * 1000; // get UTC time - - return { - data: new Date(timestamp), - size: 8 + 2, - }; - } - - static parseValue(arrayBuffer, dataOffset, dataSize) { - if (dataSize < 1) { - throw new IllegalStateException('Data not enough when parse Value'); - } - - let v = new DataView(arrayBuffer, dataOffset, dataSize); - - let offset = 1; - let type = v.getUint8(0); - let value; - let objectEnd = false; - - try { - switch (type) { - case 0: // Number(Double) type - value = v.getFloat64(1, !le); - offset += 8; - break; - case 1: { - // Boolean type - let b = v.getUint8(1); - value = b ? true : false; - offset += 1; - break; - } - case 2: { - // String type - let amfstr = AMF.parseString(arrayBuffer, dataOffset + 1, dataSize - 1); - value = amfstr.data; - offset += amfstr.size; - break; - } - case 3: { - // Object(s) type - value = {}; - let terminal = 0; // workaround for malformed Objects which has missing ScriptDataObjectEnd - if ((v.getUint32(dataSize - 4, !le) & 0x00ffffff) === 9) { - terminal = 3; - } - while (offset < dataSize - 4) { - // 4 === type(UI8) + ScriptDataObjectEnd(UI24) - let amfobj = AMF.parseObject(arrayBuffer, dataOffset + offset, dataSize - offset - terminal); - if (amfobj.objectEnd) break; - value[amfobj.data.name] = amfobj.data.value; - offset += amfobj.size; - } - if (offset <= dataSize - 3) { - let marker = v.getUint32(offset - 1, !le) & 0x00ffffff; - if (marker === 9) { - offset += 3; - } - } - break; - } - case 8: { - // ECMA array type (Mixed array) - value = {}; - offset += 4; // ECMAArrayLength(UI32) - let terminal = 0; // workaround for malformed MixedArrays which has missing ScriptDataObjectEnd - if ((v.getUint32(dataSize - 4, !le) & 0x00ffffff) === 9) { - terminal = 3; - } - while (offset < dataSize - 8) { - // 8 === type(UI8) + ECMAArrayLength(UI32) + ScriptDataVariableEnd(UI24) - let amfvar = AMF.parseVariable(arrayBuffer, dataOffset + offset, dataSize - offset - terminal); - if (amfvar.objectEnd) break; - value[amfvar.data.name] = amfvar.data.value; - offset += amfvar.size; - } - if (offset <= dataSize - 3) { - let marker = v.getUint32(offset - 1, !le) & 0x00ffffff; - if (marker === 9) { - offset += 3; - } - } - break; - } - case 9: // ScriptDataObjectEnd - value = undefined; - offset = 1; - objectEnd = true; - break; - case 10: { - // Strict array type - // ScriptDataValue[n]. NOTE: according to video_file_format_spec_v10_1.pdf - value = []; - let strictArrayLength = v.getUint32(1, !le); - offset += 4; - for (let i = 0; i < strictArrayLength; i++) { - let val = AMF.parseValue(arrayBuffer, dataOffset + offset, dataSize - offset); - value.push(val.data); - offset += val.size; - } - break; - } - case 11: { - // Date type - let date = AMF.parseDate(arrayBuffer, dataOffset + 1, dataSize - 1); - value = date.data; - offset += date.size; - break; - } - case 12: { - // Long string type - let amfLongStr = AMF.parseString(arrayBuffer, dataOffset + 1, dataSize - 1); - value = amfLongStr.data; - offset += amfLongStr.size; - break; - } - default: - // ignore and skip - offset = dataSize; - Log.w('AMF', 'Unsupported AMF value type ' + type); - } - } catch (e) { - Log.e('AMF', e.toString()); - } - - return { - data: value, - size: offset, - objectEnd: objectEnd, - }; - } -} - -export default AMF; diff --git a/src/flv.js/demux/demux-errors.js b/src/flv.js/demux/demux-errors.js deleted file mode 100644 index 12cfd68..0000000 --- a/src/flv.js/demux/demux-errors.js +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2016 Bilibili. All Rights Reserved. - * - * @author zheng qian - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -const DemuxErrors = { - OK: 'OK', - FORMAT_ERROR: 'FormatError', - FORMAT_UNSUPPORTED: 'FormatUnsupported', - CODEC_UNSUPPORTED: 'CodecUnsupported', -}; - -export default DemuxErrors; diff --git a/src/flv.js/demux/exp-golomb.js b/src/flv.js/demux/exp-golomb.js deleted file mode 100644 index c27719b..0000000 --- a/src/flv.js/demux/exp-golomb.js +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (C) 2016 Bilibili. All Rights Reserved. - * - * @author zheng qian - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { IllegalStateException, InvalidArgumentException } from '../utils/exception.js'; - -// Exponential-Golomb buffer decoder -class ExpGolomb { - constructor(uint8array) { - this.TAG = 'ExpGolomb'; - - this._buffer = uint8array; - this._buffer_index = 0; - this._total_bytes = uint8array.byteLength; - this._total_bits = uint8array.byteLength * 8; - this._current_word = 0; - this._current_word_bits_left = 0; - } - - destroy() { - this._buffer = null; - } - - _fillCurrentWord() { - let buffer_bytes_left = this._total_bytes - this._buffer_index; - if (buffer_bytes_left <= 0) - throw new IllegalStateException('ExpGolomb: _fillCurrentWord() but no bytes available'); - - let bytes_read = Math.min(4, buffer_bytes_left); - let word = new Uint8Array(4); - word.set(this._buffer.subarray(this._buffer_index, this._buffer_index + bytes_read)); - this._current_word = new DataView(word.buffer).getUint32(0, false); - - this._buffer_index += bytes_read; - this._current_word_bits_left = bytes_read * 8; - } - - readBits(bits) { - if (bits > 32) throw new InvalidArgumentException('ExpGolomb: readBits() bits exceeded max 32bits!'); - - if (bits <= this._current_word_bits_left) { - let result = this._current_word >>> (32 - bits); - this._current_word <<= bits; - this._current_word_bits_left -= bits; - return result; - } - - let result = this._current_word_bits_left ? this._current_word : 0; - result = result >>> (32 - this._current_word_bits_left); - let bits_need_left = bits - this._current_word_bits_left; - - this._fillCurrentWord(); - let bits_read_next = Math.min(bits_need_left, this._current_word_bits_left); - - let result2 = this._current_word >>> (32 - bits_read_next); - this._current_word <<= bits_read_next; - this._current_word_bits_left -= bits_read_next; - - result = (result << bits_read_next) | result2; - return result; - } - - readBool() { - return this.readBits(1) === 1; - } - - readByte() { - return this.readBits(8); - } - - _skipLeadingZero() { - let zero_count; - for (zero_count = 0; zero_count < this._current_word_bits_left; zero_count++) { - if (0 !== (this._current_word & (0x80000000 >>> zero_count))) { - this._current_word <<= zero_count; - this._current_word_bits_left -= zero_count; - return zero_count; - } - } - this._fillCurrentWord(); - return zero_count + this._skipLeadingZero(); - } - - readUEG() { - // unsigned exponential golomb - let leading_zeros = this._skipLeadingZero(); - return this.readBits(leading_zeros + 1) - 1; - } - - readSEG() { - // signed exponential golomb - let value = this.readUEG(); - if (value & 0x01) { - return (value + 1) >>> 1; - } else { - return -1 * (value >>> 1); - } - } -} - -export default ExpGolomb; diff --git a/src/flv.js/demux/flv-demuxer.js b/src/flv.js/demux/flv-demuxer.js deleted file mode 100644 index b76a21a..0000000 --- a/src/flv.js/demux/flv-demuxer.js +++ /dev/null @@ -1,1202 +0,0 @@ -/* - * Copyright (C) 2016 Bilibili. All Rights Reserved. - * - * @author zheng qian - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import Log from '../utils/logger.js'; -import AMF from './amf-parser.js'; -import SPSParser from './sps-parser.js'; -import DemuxErrors from './demux-errors.js'; -import MediaInfo from '../core/media-info.js'; -import { IllegalStateException } from '../utils/exception.js'; -import PlayerEvents from '../player/player-events.js'; -import Utils from '../utils/utils.js'; -import Browser from '../utils/browser.js'; - -function Swap16(src) { - return ((src >>> 8) & 0xff) | ((src & 0xff) << 8); -} - -function Swap32(src) { - return ( - ((src & 0xff000000) >>> 24) | - ((src & 0x00ff0000) >>> 8) | - ((src & 0x0000ff00) << 8) | - ((src & 0x000000ff) << 24) - ); -} - -function ReadBig32(array, index) { - return (array[index] << 24) | (array[index + 1] << 16) | (array[index + 2] << 8) | array[index + 3]; -} - -class FLVDemuxer { - constructor(probeData, config) { - this.TAG = 'FLVDemuxer'; - - this._config = config; - - this._onError = null; - this._onMediaInfo = null; - this._onTrackMetadata = null; - this._onDataAvailable = null; - this._onFlvFrameDecoded = null; - this._onCtsWarning = null; - this._onFLVDataAbnormal = null; - - this._dataOffset = probeData.dataOffset; - this._firstParse = true; - this._dispatch = false; - - this._hasAudio = probeData.hasAudioTrack; - this._hasVideo = probeData.hasVideoTrack; - - this._hasAudioFlagOverrided = false; - this._hasVideoFlagOverrided = false; - - this._audioInitialMetadataDispatched = false; - this._videoInitialMetadataDispatched = false; - - this._ctsWarning = false; - this._flvDataAbnormal = false; - this._hasKeyframe = false; - - this._mediaInfo = new MediaInfo(); - this._mediaInfo.hasAudio = this._hasAudio; - this._mediaInfo.hasVideo = this._hasVideo; - this._metadata = null; - this._audioMetadata = null; - this._videoMetadata = null; - - this._naluLengthSize = 4; - this._timestampBase = 0; // int32, in milliseconds - this._timescale = 1000; - this._duration = 0; // int32, in milliseconds - this._durationOverrided = false; - this._referenceFrameRate = { - fixed: true, - fps: 23.976, - fps_num: 23976, - fps_den: 1000, - }; - - this._flvSoundRateTable = [5500, 11025, 22050, 44100, 48000]; - - this._mpegSamplingRates = [ - 96000, - 88200, - 64000, - 48000, - 44100, - 32000, - 24000, - 22050, - 16000, - 12000, - 11025, - 8000, - 7350, - ]; - - this._mpegAudioV10SampleRateTable = [44100, 48000, 32000, 0]; - this._mpegAudioV20SampleRateTable = [22050, 24000, 16000, 0]; - this._mpegAudioV25SampleRateTable = [11025, 12000, 8000, 0]; - - this._mpegAudioL1BitRateTable = [0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, -1]; - this._mpegAudioL2BitRateTable = [0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, -1]; - this._mpegAudioL3BitRateTable = [0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, -1]; - - this._videoTrack = { type: 'video', id: 1, sequenceNumber: 0, samples: [], length: 0 }; - this._audioTrack = { type: 'audio', id: 2, sequenceNumber: 0, samples: [], length: 0 }; - - this._littleEndian = (function () { - let buf = new ArrayBuffer(2); - new DataView(buf).setInt16(0, 256, true); // little-endian write - return new Int16Array(buf)[0] === 256; // platform-spec read, if equal then LE - })(); - } - - destroy() { - this._mediaInfo = null; - this._metadata = null; - this._audioMetadata = null; - this._videoMetadata = null; - this._videoTrack = null; - this._audioTrack = null; - - this._onError = null; - this._onMediaInfo = null; - this._onTrackMetadata = null; - this._onDataAvailable = null; - this._onFlvFrameDecoded = null; - this._onCtsWarning = null; - this._onFLVDataAbnormal = null; - - this._ctsWarning = false; - this._flvDataAbnormal = false; - this._hasKeyframe = false; - } - - static probe(buffer) { - let data = new Uint8Array(buffer); - let mismatch = { match: false }; - - if (data[0] !== 0x46 || data[1] !== 0x4c || data[2] !== 0x56 || data[3] !== 0x01) { - return mismatch; - } - - let hasAudio = (data[4] & 4) >>> 2 !== 0; - let hasVideo = (data[4] & 1) !== 0; - - let offset = ReadBig32(data, 5); - - if (offset < 9) { - return mismatch; - } - - return { - match: true, - consumed: offset, - dataOffset: offset, - hasAudioTrack: hasAudio, - hasVideoTrack: hasVideo, - }; - } - - bindDataSource(loader) { - loader.onDataArrival = this.parseChunks.bind(this); - return this; - } - - // prototype: function(type: string, metadata: any): void - get onTrackMetadata() { - return this._onTrackMetadata; - } - - set onTrackMetadata(callback) { - this._onTrackMetadata = callback; - } - - // prototype: function(mediaInfo: MediaInfo): void - get onMediaInfo() { - return this._onMediaInfo; - } - - set onMediaInfo(callback) { - this._onMediaInfo = callback; - } - - // prototype: function(type: number, info: string): void - get onError() { - return this._onError; - } - - set onError(callback) { - this._onError = callback; - } - - // prototype: function(videoTrack: any, audioTrack: any): void - get onDataAvailable() { - return this._onDataAvailable; - } - - set onDataAvailable(callback) { - this._onDataAvailable = callback; - } - - get onFlvFrameDecoded() { - return this._onFlvFrameDecoded; - } - - set onFlvFrameDecoded(callback) { - this._onFlvFrameDecoded = callback; - } - - get onCtsWarning() { - return this._onCtsWarning; - } - - set onCtsWarning(callback) { - this._onCtsWarning = callback; - } - - get onFLVDataAbnormal() { - return this._onFLVDataAbnormal; - } - - set onFLVDataAbnormal(callback) { - this._onFLVDataAbnormal = callback; - } - - // timestamp base for output samples, must be in milliseconds - get timestampBase() { - return this._timestampBase; - } - - set timestampBase(base) { - this._timestampBase = base; - } - - get overridedDuration() { - return this._duration; - } - - // Force-override media duration. Must be in milliseconds, int32 - set overridedDuration(duration) { - this._durationOverrided = true; - this._duration = duration; - this._mediaInfo.duration = duration; - } - - // Force-override audio track present flag, boolean - set overridedHasAudio(hasAudio) { - this._hasAudioFlagOverrided = true; - this._hasAudio = hasAudio; - this._mediaInfo.hasAudio = hasAudio; - } - - // Force-override video track present flag, boolean - set overridedHasVideo(hasVideo) { - this._hasVideoFlagOverrided = true; - this._hasVideo = hasVideo; - this._mediaInfo.hasVideo = hasVideo; - } - - resetMediaInfo() { - this._mediaInfo = new MediaInfo(); - } - - _isInitialMetadataDispatched() { - if (this._hasAudio && this._hasVideo) { - // both audio & video - return this._audioInitialMetadataDispatched && this._videoInitialMetadataDispatched; - } - if (this._hasAudio && !this._hasVideo) { - // audio only - return this._audioInitialMetadataDispatched; - } - if (!this._hasAudio && this._hasVideo) { - // video only - return this._videoInitialMetadataDispatched; - } - return false; - } - - // function parseChunks(chunk: ArrayBuffer, byteStart: number): number; - parseChunks(chunk, byteStart) { - if (!this._onError || !this._onMediaInfo || !this._onTrackMetadata || !this._onDataAvailable) { - throw new IllegalStateException( - 'Flv: onError & onMediaInfo & onTrackMetadata & onDataAvailable callback must be specified', - ); - } - - let offset = 0; - let le = this._littleEndian; - - if (byteStart === 0) { - // buffer with FLV header - if (chunk.byteLength > 13) { - let probeData = FLVDemuxer.probe(chunk); - offset = probeData.dataOffset; - } else { - return 0; - } - } - - if (this._firstParse) { - // handle PreviousTagSize0 before Tag1 - this._firstParse = false; - if (byteStart + offset !== this._dataOffset) { - Log.w(this.TAG, 'First time parsing but chunk byteStart invalid!'); - } - - let v = new DataView(chunk, offset); - let prevTagSize0 = v.getUint32(0, !le); - if (prevTagSize0 !== 0) { - Log.w(this.TAG, 'PrevTagSize0 !== 0 !!!'); - } - offset += 4; - } - - while (offset < chunk.byteLength) { - this._dispatch = true; - - let v = new DataView(chunk, offset); - - if (offset + 11 + 4 > chunk.byteLength) { - // data not enough for parsing an flv tag - break; - } - - let tagType = v.getUint8(0); - let dataSize = v.getUint32(0, !le) & 0x00ffffff; - - if (offset + 11 + dataSize + 4 > chunk.byteLength) { - // data not enough for parsing actual data body - break; - } - - if (tagType !== 8 && tagType !== 9 && tagType !== 18) { - Log.w(this.TAG, `Unsupported tag type ${tagType}, skipped`); - // consume the whole tag (skip it) - offset += 11 + dataSize + 4; - continue; - } - - let ts2 = v.getUint8(4); - let ts1 = v.getUint8(5); - let ts0 = v.getUint8(6); - let ts3 = v.getUint8(7); - - let timestamp = ts0 | (ts1 << 8) | (ts2 << 16) | (ts3 << 24); - - let streamId = v.getUint32(7, !le) & 0x00ffffff; - if (streamId !== 0) { - Log.w(this.TAG, 'Meet tag which has StreamID != 0!'); - } - - let dataOffset = offset + 11; - - switch (tagType) { - case 8: // Audio - this._parseAudioData(chunk, dataOffset, dataSize, timestamp); - break; - case 9: // Video - this._parseVideoData(chunk, dataOffset, dataSize, timestamp, byteStart + offset); - break; - case 18: // ScriptDataObject - this._parseScriptData(chunk, dataOffset, dataSize); - break; - } - - let prevTagSize = v.getUint32(11 + dataSize, !le); - if (prevTagSize !== 11 + dataSize) { - Log.w(this.TAG, `Invalid PrevTagSize ${prevTagSize}`); - } - - offset += 11 + dataSize + 4; // tagBody + dataSize + prevTagSize - } - - // dispatch parsed frames to consumer (typically, the remuxer) - if (this._isInitialMetadataDispatched()) { - if (this._dispatch && (this._audioTrack.length || this._videoTrack.length)) { - this._onDataAvailable(this._audioTrack, this._videoTrack); - } - } - - return offset; // consumed bytes, just equals latest offset index - } - - _parseScriptData(arrayBuffer, dataOffset, dataSize) { - let scriptData = AMF.parseScriptData(arrayBuffer, dataOffset, dataSize); - - if (scriptData.hasOwnProperty('onMetaData')) { - if (scriptData.onMetaData == null || typeof scriptData.onMetaData !== 'object') { - Log.w(this.TAG, 'Invalid onMetaData structure!'); - return; - } - if (this._metadata) { - Log.w(this.TAG, 'Found another onMetaData tag!'); - } - this._metadata = scriptData; - let onMetaData = this._metadata.onMetaData; - - if (typeof onMetaData.hasAudio === 'boolean') { - // hasAudio - if (this._hasAudioFlagOverrided === false) { - this._hasAudio = onMetaData.hasAudio; - this._mediaInfo.hasAudio = this._hasAudio; - } - } - if (typeof onMetaData.hasVideo === 'boolean') { - // hasVideo - if (this._hasVideoFlagOverrided === false) { - this._hasVideo = onMetaData.hasVideo; - this._mediaInfo.hasVideo = this._hasVideo; - } - } - if (typeof onMetaData.audiodatarate === 'number') { - // audiodatarate - this._mediaInfo.audioDataRate = onMetaData.audiodatarate; - } - if (typeof onMetaData.videodatarate === 'number') { - // videodatarate - this._mediaInfo.videoDataRate = onMetaData.videodatarate; - } - if (typeof onMetaData.width === 'number') { - // width - this._mediaInfo.width = onMetaData.width; - } - if (typeof onMetaData.height === 'number') { - // height - this._mediaInfo.height = onMetaData.height; - } - if (typeof onMetaData.duration === 'number') { - // duration - if (!this._durationOverrided) { - let duration = Math.floor(onMetaData.duration * this._timescale); - this._duration = duration; - this._mediaInfo.duration = duration; - } - } else { - this._mediaInfo.duration = 0; - } - if (typeof onMetaData.framerate === 'number') { - // framerate - let fps_num = Math.floor(onMetaData.framerate * 1000); - if (fps_num > 0) { - let fps = fps_num / 1000; - this._referenceFrameRate.fixed = true; - this._referenceFrameRate.fps = fps; - this._referenceFrameRate.fps_num = fps_num; - this._referenceFrameRate.fps_den = 1000; - this._mediaInfo.fps = fps; - } - } - if (typeof onMetaData.keyframes === 'object') { - // keyframes - this._mediaInfo.hasKeyframesIndex = true; - let keyframes = onMetaData.keyframes; - this._mediaInfo.keyframesIndex = this._parseKeyframesIndex(keyframes); - onMetaData.keyframes = null; // keyframes has been extracted, remove it - } else { - this._mediaInfo.hasKeyframesIndex = false; - } - this._dispatch = false; - this._mediaInfo.metadata = onMetaData; - Log.v(this.TAG, 'Parsed onMetaData'); - if (this._mediaInfo.isComplete()) { - this._onMediaInfo(this._mediaInfo); - } - } - } - - _parseKeyframesIndex(keyframes) { - let times = []; - let filepositions = []; - - // ignore first keyframe which is actually AVC Sequence Header (AVCDecoderConfigurationRecord) - for (let i = 1; i < keyframes.times.length; i++) { - let time = this._timestampBase + Math.floor(keyframes.times[i] * 1000); - times.push(time); - filepositions.push(keyframes.filepositions[i]); - } - - return { - times: times, - filepositions: filepositions, - }; - } - - _parseAudioData(arrayBuffer, dataOffset, dataSize, tagTimestamp) { - if (dataSize <= 1) { - Log.w(this.TAG, 'Flv: Invalid audio packet, missing SoundData payload!'); - return; - } - - if (this._hasAudioFlagOverrided === true && this._hasAudio === false) { - // If hasAudio: false indicated explicitly in MediaDataSource, - // Ignore all the audio packets - return; - } - - let le = this._littleEndian; - let v = new DataView(arrayBuffer, dataOffset, dataSize); - - let soundSpec = v.getUint8(0); - - let soundFormat = soundSpec >>> 4; - if (soundFormat !== 2 && soundFormat !== 10) { - // MP3 or AAC - this._onError(DemuxErrors.CODEC_UNSUPPORTED, 'Flv: Unsupported audio codec idx: ' + soundFormat); - return; - } - - let soundRate = 0; - let soundRateIndex = (soundSpec & 12) >>> 2; - if (soundRateIndex >= 0 && soundRateIndex <= 4) { - soundRate = this._flvSoundRateTable[soundRateIndex]; - } else { - this._onError(DemuxErrors.FORMAT_ERROR, 'Flv: Invalid audio sample rate idx: ' + soundRateIndex); - return; - } - - let soundSize = (soundSpec & 2) >>> 1; // unused - let soundType = soundSpec & 1; - - let meta = this._audioMetadata; - let track = this._audioTrack; - - if (!meta) { - if (this._hasAudio === false && this._hasAudioFlagOverrided === false) { - this._hasAudio = true; - this._mediaInfo.hasAudio = true; - } - - // initial metadata - meta = this._audioMetadata = {}; - meta.type = 'audio'; - meta.id = track.id; - meta.timescale = this._timescale; - meta.duration = this._duration; - meta.audioSampleRate = soundRate; - meta.channelCount = soundType === 0 ? 1 : 2; - } - - if (soundFormat === 10) { - // AAC - let aacData = this._parseAACAudioData(arrayBuffer, dataOffset + 1, dataSize - 1); - if (aacData == undefined) { - return; - } - - if (aacData.packetType === 0) { - // AAC sequence header (AudioSpecificConfig) - if (meta.config) { - Log.w(this.TAG, 'Found another AudioSpecificConfig!'); - } - let misc = aacData.data; - meta.audioSampleRate = misc.samplingRate; - meta.channelCount = misc.channelCount; - meta.codec = misc.codec; - meta.originalCodec = misc.originalCodec; - meta.config = misc.config; - // The decode result of an aac sample is 1024 PCM samples - meta.refSampleDuration = (1024 / meta.audioSampleRate) * meta.timescale; - Log.v(this.TAG, 'Parsed AudioSpecificConfig'); - - if (this._isInitialMetadataDispatched()) { - // Non-initial metadata, force dispatch (or flush) parsed frames to remuxer - if (this._dispatch && (this._audioTrack.length || this._videoTrack.length)) { - this._onDataAvailable(this._audioTrack, this._videoTrack); - } - } else { - this._audioInitialMetadataDispatched = true; - } - // then notify new metadata - this._dispatch = false; - this._onTrackMetadata('audio', meta); - - let mi = this._mediaInfo; - mi.audioCodec = meta.originalCodec; - mi.audioSampleRate = meta.audioSampleRate; - mi.audioChannelCount = meta.channelCount; - if (mi.hasVideo) { - if (mi.videoCodec != null) { - mi.mimeType = 'video/x-flv; codecs="' + mi.videoCodec + ',' + mi.audioCodec + '"'; - } - } else { - mi.mimeType = 'video/x-flv; codecs="' + mi.audioCodec + '"'; - } - if (mi.isComplete()) { - this._onMediaInfo(mi); - } - } else if (aacData.packetType === 1) { - // AAC raw frame data - let dts = this._timestampBase + tagTimestamp; - let aacSample = { unit: aacData.data, dts: dts, pts: dts }; - track.samples.push(aacSample); - track.length += aacData.data.length; - } else { - Log.e(this.TAG, `Flv: Unsupported AAC data type ${aacData.packetType}`); - } - } else if (soundFormat === 2) { - // MP3 - if (!meta.codec) { - // We need metadata for mp3 audio track, extract info from frame header - let misc = this._parseMP3AudioData(arrayBuffer, dataOffset + 1, dataSize - 1, true); - if (misc == undefined) { - return; - } - meta.audioSampleRate = misc.samplingRate; - meta.channelCount = misc.channelCount; - meta.codec = misc.codec; - meta.originalCodec = misc.originalCodec; - // The decode result of an mp3 sample is 1152 PCM samples - meta.refSampleDuration = (1152 / meta.audioSampleRate) * meta.timescale; - Log.v(this.TAG, 'Parsed MPEG Audio Frame Header'); - - this._audioInitialMetadataDispatched = true; - this._onTrackMetadata('audio', meta); - - let mi = this._mediaInfo; - mi.audioCodec = meta.codec; - mi.audioSampleRate = meta.audioSampleRate; - mi.audioChannelCount = meta.channelCount; - mi.audioDataRate = misc.bitRate; - if (mi.hasVideo) { - if (mi.videoCodec != null) { - mi.mimeType = 'video/x-flv; codecs="' + mi.videoCodec + ',' + mi.audioCodec + '"'; - } - } else { - mi.mimeType = 'video/x-flv; codecs="' + mi.audioCodec + '"'; - } - if (mi.isComplete()) { - this._onMediaInfo(mi); - } - } - - // This packet is always a valid audio packet, extract it - let data = this._parseMP3AudioData(arrayBuffer, dataOffset + 1, dataSize - 1, false); - if (data == undefined) { - return; - } - let dts = this._timestampBase + tagTimestamp; - let mp3Sample = { unit: data, dts: dts, pts: dts }; - track.samples.push(mp3Sample); - track.length += data.length; - } - - if (this._onFlvFrameDecoded) { - this._onFlvFrameDecoded(PlayerEvents.AUDIO_FRAME_DECODED, Date.now()); - } - } - - _parseAACAudioData(arrayBuffer, dataOffset, dataSize) { - if (dataSize <= 1) { - Log.w(this.TAG, 'Flv: Invalid AAC packet, missing AACPacketType or/and Data!'); - return; - } - - let result = {}; - let array = new Uint8Array(arrayBuffer, dataOffset, dataSize); - - result.packetType = array[0]; - - if (array[0] === 0) { - result.data = this._parseAACAudioSpecificConfig(arrayBuffer, dataOffset + 1, dataSize - 1); - } else { - result.data = array.subarray(1); - } - - return result; - } - - _parseAACAudioSpecificConfig(arrayBuffer, dataOffset, dataSize) { - let array = new Uint8Array(arrayBuffer, dataOffset, dataSize); - let config = null; - - /* Audio Object Type: - 0: Null - 1: AAC Main - 2: AAC LC - 3: AAC SSR (Scalable Sample Rate) - 4: AAC LTP (Long Term Prediction) - 5: HE-AAC / SBR (Spectral Band Replication) - 6: AAC Scalable - */ - - let audioObjectType = 0; - let originalAudioObjectType = 0; - let audioExtensionObjectType = null; - let samplingIndex = 0; - let extensionSamplingIndex = null; - - // 5 bits - audioObjectType = originalAudioObjectType = array[0] >>> 3; - // 4 bits - samplingIndex = ((array[0] & 0x07) << 1) | (array[1] >>> 7); - if (samplingIndex < 0 || samplingIndex >= this._mpegSamplingRates.length) { - this._onError(DemuxErrors.FORMAT_ERROR, 'Flv: AAC invalid sampling frequency index!'); - return; - } - - let samplingFrequence = this._mpegSamplingRates[samplingIndex]; - - // 4 bits - let channelConfig = (array[1] & 0x78) >>> 3; - if (channelConfig < 0 || channelConfig >= 8) { - this._onError(DemuxErrors.FORMAT_ERROR, 'Flv: AAC invalid channel configuration'); - return; - } - - if (audioObjectType === 5) { - // HE-AAC? - // 4 bits - extensionSamplingIndex = ((array[1] & 0x07) << 1) | (array[2] >>> 7); - // 5 bits - audioExtensionObjectType = (array[2] & 0x7c) >>> 2; - } - - // workarounds for various browsers - let userAgent = self.navigator.userAgent.toLowerCase(); - - if (userAgent.indexOf('firefox') !== -1) { - // firefox: use SBR (HE-AAC) if freq less than 24kHz - if (samplingIndex >= 6) { - audioObjectType = 5; - config = new Array(4); - extensionSamplingIndex = samplingIndex - 3; - } else { - // use LC-AAC - audioObjectType = 2; - config = new Array(2); - extensionSamplingIndex = samplingIndex; - } - } else if (userAgent.indexOf('android') !== -1) { - // android: always use LC-AAC - audioObjectType = 2; - config = new Array(2); - extensionSamplingIndex = samplingIndex; - } else { - // for other browsers, e.g. chrome... - // Always use HE-AAC to make it easier to switch aac codec profile - audioObjectType = 5; - extensionSamplingIndex = samplingIndex; - config = new Array(4); - - if (samplingIndex >= 6) { - extensionSamplingIndex = samplingIndex - 3; - } else if (channelConfig === 1) { - // Mono channel - audioObjectType = 2; - config = new Array(2); - extensionSamplingIndex = samplingIndex; - } - } - - config[0] = audioObjectType << 3; - config[0] |= (samplingIndex & 0x0f) >>> 1; - config[1] = (samplingIndex & 0x0f) << 7; - config[1] |= (channelConfig & 0x0f) << 3; - if (audioObjectType === 5) { - config[1] |= (extensionSamplingIndex & 0x0f) >>> 1; - config[2] = (extensionSamplingIndex & 0x01) << 7; - // extended audio object type: force to 2 (LC-AAC) - config[2] |= 2 << 2; - config[3] = 0; - } - - return { - config: config, - samplingRate: samplingFrequence, - channelCount: channelConfig, - codec: 'mp4a.40.' + audioObjectType, - originalCodec: 'mp4a.40.' + originalAudioObjectType, - }; - } - - _parseMP3AudioData(arrayBuffer, dataOffset, dataSize, requestHeader) { - if (dataSize < 4) { - Log.w(this.TAG, 'Flv: Invalid MP3 packet, header missing!'); - return; - } - - let le = this._littleEndian; - let array = new Uint8Array(arrayBuffer, dataOffset, dataSize); - let result = null; - - if (requestHeader) { - if (array[0] !== 0xff) { - return; - } - let ver = (array[1] >>> 3) & 0x03; - let layer = (array[1] & 0x06) >> 1; - - let bitrate_index = (array[2] & 0xf0) >>> 4; - let sampling_freq_index = (array[2] & 0x0c) >>> 2; - - let channel_mode = (array[3] >>> 6) & 0x03; - let channel_count = channel_mode !== 3 ? 2 : 1; - - let sample_rate = 0; - let bit_rate = 0; - let object_type = 34; // Layer-3, listed in MPEG-4 Audio Object Types - - let codec = 'mp3'; - - switch (ver) { - case 0: // MPEG 2.5 - sample_rate = this._mpegAudioV25SampleRateTable[sampling_freq_index]; - break; - case 2: // MPEG 2 - sample_rate = this._mpegAudioV20SampleRateTable[sampling_freq_index]; - break; - case 3: // MPEG 1 - sample_rate = this._mpegAudioV10SampleRateTable[sampling_freq_index]; - break; - } - - switch (layer) { - case 1: // Layer 3 - object_type = 34; - if (bitrate_index < this._mpegAudioL3BitRateTable.length) { - bit_rate = this._mpegAudioL3BitRateTable[bitrate_index]; - } - break; - case 2: // Layer 2 - object_type = 33; - if (bitrate_index < this._mpegAudioL2BitRateTable.length) { - bit_rate = this._mpegAudioL2BitRateTable[bitrate_index]; - } - break; - case 3: // Layer 1 - object_type = 32; - if (bitrate_index < this._mpegAudioL1BitRateTable.length) { - bit_rate = this._mpegAudioL1BitRateTable[bitrate_index]; - } - break; - } - - result = { - bitRate: bit_rate, - samplingRate: sample_rate, - channelCount: channel_count, - codec: codec, - originalCodec: codec, - }; - } else { - result = array; - } - - return result; - } - - _parseVideoData(arrayBuffer, dataOffset, dataSize, tagTimestamp, tagPosition) { - if (dataSize <= 1) { - Log.w(this.TAG, 'Flv: Invalid video packet, missing VideoData payload!'); - return; - } - - if (this._hasVideoFlagOverrided === true && this._hasVideo === false) { - // If hasVideo: false indicated explicitly in MediaDataSource, - // Ignore all the video packets - return; - } - - let spec = new Uint8Array(arrayBuffer, dataOffset, dataSize)[0]; - - let frameType = (spec & 240) >>> 4; - let codecId = spec & 15; - - if (codecId !== 7) { - this._onError(DemuxErrors.CODEC_UNSUPPORTED, `Flv: Unsupported codec in video frame: ${codecId}`); - return; - } - - this._parseAVCVideoPacket(arrayBuffer, dataOffset + 1, dataSize - 1, tagTimestamp, tagPosition, frameType); - - if (this._onFlvFrameDecoded) { - this._onFlvFrameDecoded(PlayerEvents.VIDEO_FRAME_DECODED, Date.now()); - } - } - - _parseAVCVideoPacket(arrayBuffer, dataOffset, dataSize, tagTimestamp, tagPosition, frameType) { - if (dataSize < 4) { - Log.w(this.TAG, 'Flv: Invalid AVC packet, missing AVCPacketType or/and CompositionTime'); - return; - } - - let le = this._littleEndian; - let v = new DataView(arrayBuffer, dataOffset, dataSize); - - let packetType = v.getUint8(0); - let cts_unsigned = v.getUint32(0, !le) & 0x00ffffff; - let cts = (cts_unsigned << 8) >> 8; // convert to 24-bit signed int - //cts warning - if (this._ctsWarning === false && (cts > 500 || cts < 0)) { - if (this._onCtsWarning) { - this._ctsWarning = true; - let params = { cts: cts, dts: this.timestampBase + tagTimestamp }; - this._onCtsWarning(PlayerEvents.CTS_WARNING, params); - } - } - - if (packetType === 0) { - // AVCDecoderConfigurationRecord - this._parseAVCDecoderConfigurationRecord(arrayBuffer, dataOffset + 4, dataSize - 4); - } else if (packetType === 1) { - // One or more Nalus - this._parseAVCVideoData( - arrayBuffer, - dataOffset + 4, - dataSize - 4, - tagTimestamp, - tagPosition, - frameType, - cts, - ); - } else if (packetType === 2) { - // empty, AVC end of sequence - } else { - this._onError(DemuxErrors.FORMAT_ERROR, `Flv: Invalid video packet type ${packetType}`); - return; - } - } - - _parseAVCDecoderConfigurationRecord(arrayBuffer, dataOffset, dataSize) { - if (dataSize < 7) { - Log.w(this.TAG, 'Flv: Invalid AVCDecoderConfigurationRecord, lack of data!'); - return; - } - - let meta = this._videoMetadata; - let track = this._videoTrack; - let le = this._littleEndian; - let v = new DataView(arrayBuffer, dataOffset, dataSize); - - if (!meta) { - if (this._hasVideo === false && this._hasVideoFlagOverrided === false) { - this._hasVideo = true; - this._mediaInfo.hasVideo = true; - } - - meta = this._videoMetadata = {}; - meta.type = 'video'; - meta.id = track.id; - meta.timescale = this._timescale; - meta.duration = this._duration; - } else { - if (typeof meta.avcc !== 'undefined') { - Log.w(this.TAG, 'Found another AVCDecoderConfigurationRecord!'); - } - } - - let version = v.getUint8(0); // configurationVersion - let avcProfile = v.getUint8(1); // avcProfileIndication - let profileCompatibility = v.getUint8(2); // profile_compatibility - let avcLevel = v.getUint8(3); // AVCLevelIndication - - if (version !== 1 || avcProfile === 0) { - this._onError(DemuxErrors.FORMAT_ERROR, 'Flv: Invalid AVCDecoderConfigurationRecord'); - return; - } - - this._naluLengthSize = (v.getUint8(4) & 3) + 1; // lengthSizeMinusOne - // if (this._naluLengthSize !== 3 && this._naluLengthSize !== 4) { // holy shit!!! - // this._onError(DemuxErrors.FORMAT_ERROR, `Flv: Strange NaluLengthSizeMinusOne: ${this._naluLengthSize - 1}`); - // return; - // } - - let spsCount = v.getUint8(5) & 31; // numOfSequenceParameterSets - if (spsCount === 0) { - this._onError(DemuxErrors.FORMAT_ERROR, 'Flv: Invalid AVCDecoderConfigurationRecord: No SPS'); - return; - } else if (spsCount > 1) { - Log.w(this.TAG, `Flv: Strange AVCDecoderConfigurationRecord: SPS Count = ${spsCount}`); - } - - let offset = 6; - - for (let i = 0; i < spsCount; i++) { - let len = v.getUint16(offset, !le); // sequenceParameterSetLength - offset += 2; - - if (len === 0) { - continue; - } - - // Notice: Nalu without startcode header (00 00 00 01) - let sps = new Uint8Array(arrayBuffer, dataOffset + offset, len); - offset += len; - - let config = SPSParser.parseSPS(sps); - if (i !== 0) { - // ignore other sps's config - continue; - } - - meta.codecWidth = config.codec_size.width; - meta.codecHeight = config.codec_size.height; - meta.presentWidth = config.present_size.width; - meta.presentHeight = config.present_size.height; - - meta.profile = config.profile_string; - meta.level = config.level_string; - meta.bitDepth = config.bit_depth; - meta.chromaFormat = config.chroma_format; - meta.sarRatio = config.sar_ratio; - meta.frameRate = config.frame_rate; - - if ( - config.frame_rate.fixed === false || - config.frame_rate.fps_num === 0 || - config.frame_rate.fps_den === 0 - ) { - meta.frameRate = this._referenceFrameRate; - } - - let fps_den = meta.frameRate.fps_den; - let fps_num = meta.frameRate.fps_num; - meta.refSampleDuration = meta.timescale * (fps_den / fps_num); - - let codecArray = sps.subarray(1, 4); - let codecString = 'avc1.'; - for (let j = 0; j < 3; j++) { - let h = codecArray[j].toString(16); - if (h.length < 2) { - h = '0' + h; - } - codecString += h; - } - meta.codec = codecString; - - let mi = this._mediaInfo; - mi.width = meta.codecWidth; - mi.height = meta.codecHeight; - mi.fps = meta.frameRate.fps; - mi.profile = meta.profile; - mi.level = meta.level; - mi.chromaFormat = config.chroma_format_string; - mi.sarNum = meta.sarRatio.width; - mi.sarDen = meta.sarRatio.height; - mi.videoCodec = codecString; - - if (mi.hasAudio) { - if (mi.audioCodec != null) { - mi.mimeType = 'video/x-flv; codecs="' + mi.videoCodec + ',' + mi.audioCodec + '"'; - } - } else { - mi.mimeType = 'video/x-flv; codecs="' + mi.videoCodec + '"'; - } - if (mi.isComplete()) { - this._onMediaInfo(mi); - } - } - - let ppsCount = v.getUint8(offset); // numOfPictureParameterSets - if (ppsCount === 0) { - this._onError(DemuxErrors.FORMAT_ERROR, 'Flv: Invalid AVCDecoderConfigurationRecord: No PPS'); - return; - } else if (ppsCount > 1) { - Log.w(this.TAG, `Flv: Strange AVCDecoderConfigurationRecord: PPS Count = ${ppsCount}`); - } - - offset++; - - for (let i = 0; i < ppsCount; i++) { - let len = v.getUint16(offset, !le); // pictureParameterSetLength - offset += 2; - - if (len === 0) { - continue; - } - - // pps is useless for extracting video information - offset += len; - } - - meta.avcc = new Uint8Array(dataSize); - meta.avcc.set(new Uint8Array(arrayBuffer, dataOffset, dataSize), 0); - Log.v(this.TAG, 'Parsed AVCDecoderConfigurationRecord'); - - if (this._isInitialMetadataDispatched()) { - // flush parsed frames - if (this._dispatch && (this._audioTrack.length || this._videoTrack.length)) { - this._onDataAvailable(this._audioTrack, this._videoTrack); - } - } else { - this._videoInitialMetadataDispatched = true; - } - // notify new metadata - this._dispatch = false; - this._onTrackMetadata('video', meta); - } - - _parseAVCVideoData(arrayBuffer, dataOffset, dataSize, tagTimestamp, tagPosition, frameType, cts) { - let le = this._littleEndian; - let v = new DataView(arrayBuffer, dataOffset, dataSize); - - let units = [], - length = 0; - - let offset = 0; - const lengthSize = this._naluLengthSize; - let dts = this._timestampBase + tagTimestamp; - let keyframe = frameType === 1; // from FLV Frame Type constants - - while (offset < dataSize) { - if (offset + 4 >= dataSize) { - Log.w(this.TAG, `Malformed Nalu near timestamp ${dts}, offset = ${offset}, dataSize = ${dataSize}`); - break; // data not enough for next Nalu - } - // Nalu with length-header (AVC1) - let naluSize = v.getUint32(offset, !le); // Big-Endian read - if (lengthSize < 4) { - naluSize >>>= 8 * (4 - lengthSize); - } - if (naluSize > dataSize - lengthSize) { - Log.w(this.TAG, `Malformed Nalus near timestamp ${dts}, NaluSize > DataSize!`); - return; - } - - let unitType = v.getUint8(offset + lengthSize) & 0x1f; - - if (unitType === 5) { - // IDR - keyframe = true; - } - - let data = new Uint8Array(arrayBuffer, dataOffset + offset, lengthSize + naluSize); - // workaround for safari if NaluLengthSizeMinusOne less than 4 - if (Browser.safari && lengthSize < 4) { - const naluSizeComplement = new Uint8Array(4 - lengthSize); - data = Utils.concatUint8Array(naluSizeComplement, data); - } - let unit = { type: unitType, data: data }; - units.push(unit); - length += data.byteLength; - - offset += lengthSize + naluSize; - } - - if (units.length) { - let track = this._videoTrack; - let avcSample = { - units: units, - length: length, - isKeyframe: keyframe, - dts: dts, - cts: cts, - pts: dts + cts, - }; - if (keyframe) { - avcSample.fileposition = tagPosition; - } - track.samples.push(avcSample); - track.length += length; - - if (!this._flvDataAbnormal && this._onFLVDataAbnormal) { - if (!this._hasKeyframe && frameType === 1) { - for (let i = 0; i < units.length; i++) { - if (units[i].type === 5) { - break; - } - if (i === units.length - 1) { - this._flvDataAbnormal = true; - let params = { type: 1, dts: dts }; - this._onFLVDataAbnormal(PlayerEvents.FLV_DATA_ABNORMAL, params); - } - } - } else if (frameType === 2 && units.length > 1) { - for (let j = 0; j < units.length; j++) { - if (units[j].type === 5) { - this._flvDataAbnormal = true; - let params = { type: 2, dts: dts }; - this._onFLVDataAbnormal(PlayerEvents.FLV_DATA_ABNORMAL, params); - break; - } - } - } - } - if (keyframe) { - this._hasKeyframe = true; - } - } - } -} - -export default FLVDemuxer; diff --git a/src/flv.js/demux/sps-parser.js b/src/flv.js/demux/sps-parser.js deleted file mode 100644 index 5ef506d..0000000 --- a/src/flv.js/demux/sps-parser.js +++ /dev/null @@ -1,300 +0,0 @@ -/* - * Copyright (C) 2016 Bilibili. All Rights Reserved. - * - * @author zheng qian - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import ExpGolomb from './exp-golomb.js'; - -class SPSParser { - static _ebsp2rbsp(uint8array) { - let src = uint8array; - let src_length = src.byteLength; - let dst = new Uint8Array(src_length); - let dst_idx = 0; - - for (let i = 0; i < src_length; i++) { - if (i >= 2) { - // Unescape: Skip 0x03 after 00 00 - if (src[i] === 0x03 && src[i - 1] === 0x00 && src[i - 2] === 0x00) { - continue; - } - } - dst[dst_idx] = src[i]; - dst_idx++; - } - - return new Uint8Array(dst.buffer, 0, dst_idx); - } - - static parseSPS(uint8array) { - let rbsp = SPSParser._ebsp2rbsp(uint8array); - let gb = new ExpGolomb(rbsp); - - gb.readByte(); - let profile_idc = gb.readByte(); // profile_idc - gb.readByte(); // constraint_set_flags[5] + reserved_zero[3] - let level_idc = gb.readByte(); // level_idc - gb.readUEG(); // seq_parameter_set_id - - let profile_string = SPSParser.getProfileString(profile_idc); - let level_string = SPSParser.getLevelString(level_idc); - let chroma_format_idc = 1; - let chroma_format = 420; - let chroma_format_table = [0, 420, 422, 444]; - let bit_depth = 8; - - if ( - profile_idc === 100 || - profile_idc === 110 || - profile_idc === 122 || - profile_idc === 244 || - profile_idc === 44 || - profile_idc === 83 || - profile_idc === 86 || - profile_idc === 118 || - profile_idc === 128 || - profile_idc === 138 || - profile_idc === 144 - ) { - chroma_format_idc = gb.readUEG(); - if (chroma_format_idc === 3) { - gb.readBits(1); // separate_colour_plane_flag - } - if (chroma_format_idc <= 3) { - chroma_format = chroma_format_table[chroma_format_idc]; - } - - bit_depth = gb.readUEG() + 8; // bit_depth_luma_minus8 - gb.readUEG(); // bit_depth_chroma_minus8 - gb.readBits(1); // qpprime_y_zero_transform_bypass_flag - if (gb.readBool()) { - // seq_scaling_matrix_present_flag - let scaling_list_count = chroma_format_idc !== 3 ? 8 : 12; - for (let i = 0; i < scaling_list_count; i++) { - if (gb.readBool()) { - // seq_scaling_list_present_flag - if (i < 6) { - SPSParser._skipScalingList(gb, 16); - } else { - SPSParser._skipScalingList(gb, 64); - } - } - } - } - } - gb.readUEG(); // log2_max_frame_num_minus4 - let pic_order_cnt_type = gb.readUEG(); - if (pic_order_cnt_type === 0) { - gb.readUEG(); // log2_max_pic_order_cnt_lsb_minus_4 - } else if (pic_order_cnt_type === 1) { - gb.readBits(1); // delta_pic_order_always_zero_flag - gb.readSEG(); // offset_for_non_ref_pic - gb.readSEG(); // offset_for_top_to_bottom_field - let num_ref_frames_in_pic_order_cnt_cycle = gb.readUEG(); - for (let i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; i++) { - gb.readSEG(); // offset_for_ref_frame - } - } - gb.readUEG(); // max_num_ref_frames - gb.readBits(1); // gaps_in_frame_num_value_allowed_flag - - let pic_width_in_mbs_minus1 = gb.readUEG(); - let pic_height_in_map_units_minus1 = gb.readUEG(); - - let frame_mbs_only_flag = gb.readBits(1); - if (frame_mbs_only_flag === 0) { - gb.readBits(1); // mb_adaptive_frame_field_flag - } - gb.readBits(1); // direct_8x8_inference_flag - - let frame_crop_left_offset = 0; - let frame_crop_right_offset = 0; - let frame_crop_top_offset = 0; - let frame_crop_bottom_offset = 0; - - let frame_cropping_flag = gb.readBool(); - if (frame_cropping_flag) { - frame_crop_left_offset = gb.readUEG(); - frame_crop_right_offset = gb.readUEG(); - frame_crop_top_offset = gb.readUEG(); - frame_crop_bottom_offset = gb.readUEG(); - } - - let sar_width = 1, - sar_height = 1; - let fps = 0, - fps_fixed = true, - fps_num = 0, - fps_den = 0; - - let vui_parameters_present_flag = gb.readBool(); - if (vui_parameters_present_flag) { - if (gb.readBool()) { - // aspect_ratio_info_present_flag - let aspect_ratio_idc = gb.readByte(); - let sar_w_table = [1, 12, 10, 16, 40, 24, 20, 32, 80, 18, 15, 64, 160, 4, 3, 2]; - let sar_h_table = [1, 11, 11, 11, 33, 11, 11, 11, 33, 11, 11, 33, 99, 3, 2, 1]; - - if (aspect_ratio_idc > 0 && aspect_ratio_idc < 16) { - sar_width = sar_w_table[aspect_ratio_idc - 1]; - sar_height = sar_h_table[aspect_ratio_idc - 1]; - } else if (aspect_ratio_idc === 255) { - sar_width = (gb.readByte() << 8) | gb.readByte(); - sar_height = (gb.readByte() << 8) | gb.readByte(); - } - } - - if (gb.readBool()) { - // overscan_info_present_flag - gb.readBool(); // overscan_appropriate_flag - } - if (gb.readBool()) { - // video_signal_type_present_flag - gb.readBits(4); // video_format & video_full_range_flag - if (gb.readBool()) { - // colour_description_present_flag - gb.readBits(24); // colour_primaries & transfer_characteristics & matrix_coefficients - } - } - if (gb.readBool()) { - // chroma_loc_info_present_flag - gb.readUEG(); // chroma_sample_loc_type_top_field - gb.readUEG(); // chroma_sample_loc_type_bottom_field - } - if (gb.readBool()) { - // timing_info_present_flag - let num_units_in_tick = gb.readBits(32); - let time_scale = gb.readBits(32); - fps_fixed = gb.readBool(); // fixed_frame_rate_flag - - fps_num = time_scale; - fps_den = num_units_in_tick * 2; - fps = fps_num / fps_den; - } - } - - let sarScale = 1; - if (sar_width !== 1 || sar_height !== 1) { - sarScale = sar_width / sar_height; - } - - let crop_unit_x = 0, - crop_unit_y = 0; - if (chroma_format_idc === 0) { - crop_unit_x = 1; - crop_unit_y = 2 - frame_mbs_only_flag; - } else { - let sub_wc = chroma_format_idc === 3 ? 1 : 2; - let sub_hc = chroma_format_idc === 1 ? 2 : 1; - crop_unit_x = sub_wc; - crop_unit_y = sub_hc * (2 - frame_mbs_only_flag); - } - - let codec_width = (pic_width_in_mbs_minus1 + 1) * 16; - let codec_height = (2 - frame_mbs_only_flag) * ((pic_height_in_map_units_minus1 + 1) * 16); - - codec_width -= (frame_crop_left_offset + frame_crop_right_offset) * crop_unit_x; - codec_height -= (frame_crop_top_offset + frame_crop_bottom_offset) * crop_unit_y; - - let present_width = Math.ceil(codec_width * sarScale); - - gb.destroy(); - gb = null; - - return { - profile_string: profile_string, // baseline, high, high10, ... - level_string: level_string, // 3, 3.1, 4, 4.1, 5, 5.1, ... - bit_depth: bit_depth, // 8bit, 10bit, ... - chroma_format: chroma_format, // 4:2:0, 4:2:2, ... - chroma_format_string: SPSParser.getChromaFormatString(chroma_format), - - frame_rate: { - fixed: fps_fixed, - fps: fps, - fps_den: fps_den, - fps_num: fps_num, - }, - - sar_ratio: { - width: sar_width, - height: sar_height, - }, - - codec_size: { - width: codec_width, - height: codec_height, - }, - - present_size: { - width: present_width, - height: codec_height, - }, - }; - } - - static _skipScalingList(gb, count) { - let last_scale = 8, - next_scale = 8; - let delta_scale = 0; - for (let i = 0; i < count; i++) { - if (next_scale !== 0) { - delta_scale = gb.readSEG(); - next_scale = (last_scale + delta_scale + 256) % 256; - } - last_scale = next_scale === 0 ? last_scale : next_scale; - } - } - - static getProfileString(profile_idc) { - switch (profile_idc) { - case 66: - return 'Baseline'; - case 77: - return 'Main'; - case 88: - return 'Extended'; - case 100: - return 'High'; - case 110: - return 'High10'; - case 122: - return 'High422'; - case 244: - return 'High444'; - default: - return 'Unknown'; - } - } - - static getLevelString(level_idc) { - return (level_idc / 10).toFixed(1); - } - - static getChromaFormatString(chroma) { - switch (chroma) { - case 420: - return '4:2:0'; - case 422: - return '4:2:2'; - case 444: - return '4:4:4'; - default: - return 'Unknown'; - } - } -} - -export default SPSParser; diff --git a/src/flv.js/index.d.ts b/src/flv.js/index.d.ts deleted file mode 100644 index 18f19f9..0000000 --- a/src/flv.js/index.d.ts +++ /dev/null @@ -1,448 +0,0 @@ -/* - * Copyright (C) 2016 Bilibili. All Rights Reserved. - * - * @author zheng qian - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// flv.js TypeScript definition file - -declare namespace flvjs { - interface MediaSegment { - duration: number; - filesize?: number; - url: string; - } - - interface MediaDataSource { - type: string; - isLive?: boolean; - cors?: boolean; - withCredentials?: boolean; - - hasAudio?: boolean; - hasVideo?: boolean; - - duration?: number; - filesize?: number; - url?: string; - - segments?: MediaSegment[]; - } - - interface Config { - /** - * @desc Enable separated thread for transmuxing (unstable for now) - * @defaultvalue false - */ - enableWorker?: boolean; - /** - * @desc Enable IO stash buffer. Set to false if you need realtime (minimal latency) for live stream - * playback, but may stalled if there's network jittering. - * @defaultvalue true - */ - enableStashBuffer?: boolean; - /** - * @desc Indicates IO stash buffer initial size. Default is `384KB`. Indicate a suitable size can - * improve video load/seek time. - */ - stashInitialSize?: number; - - /** - * @desc Same to `isLive` in **MediaDataSource**, ignored if has been set in MediaDataSource structure. - * @defaultvalue false - */ - isLive?: boolean; - - /** - * @desc Abort the http connection if there's enough data for playback. - * @defaultvalue true - */ - lazyLoad?: boolean; - /** - * @desc Indicates how many seconds of data to be kept for `lazyLoad`. - * @defaultvalue 3 * 60 - */ - lazyLoadMaxDuration?: number; - /** - * @desc Indicates the `lazyLoad` recover time boundary in seconds. - * @defaultvalue 30 - */ - lazyLoadRecoverDuration?: number; - /** - * @desc Do load after MediaSource `sourceopen` event triggered. On Chrome, tabs which - * be opened in background may not trigger `sourceopen` event until switched to that tab. - * @defaultvalue true - */ - deferLoadAfterSourceOpen?: boolean; - - /** - * @desc Do auto cleanup for SourceBuffer - * @defaultvalue false (from docs) - */ - autoCleanupSourceBuffer?: boolean; - /** - * @desc When backward buffer duration exceeded this value (in seconds), do auto cleanup for SourceBuffer - * @defaultvalue 3 * 60 - */ - autoCleanupMaxBackwardDuration?: number; - /** - * @desc Indicates the duration in seconds to reserve for backward buffer when doing auto cleanup. - * @defaultvalue 2 * 60 - */ - autoCleanupMinBackwardDuration?: number; - - /** - * @defaultvalue 600 - */ - statisticsInfoReportInterval?: number; - - /** - * @desc Fill silent audio frames to avoid a/v unsync when detect large audio timestamp gap. - * @defaultvalue true - */ - fixAudioTimestampGap?: boolean; - - /** - * @desc Accurate seek to any frame, not limited to video IDR frame, but may a bit slower. - * Available on Chrome > 50, FireFox and Safari. - * @defaultvalue false - */ - accurateSeek?: boolean; - /** - * @desc 'range' use range request to seek, or 'param' add params into url to indicate request range. - * @defaultvalue 'range' - */ - seekType?: 'range' | 'param' | 'custom'; - /** - * @desc Indicates seek start parameter name for seekType = 'param' - * @defaultvalue 'bstart' - */ - seekParamStart?: string; - /** - * @desc Indicates seek end parameter name for seekType = 'param' - * @defaultvalue 'bend' - */ - seekParamEnd?: string; - /** - * @desc Send Range: bytes=0- for first time load if use Range seek - * @defaultvalue false - */ - rangeLoadZeroStart?: boolean; - /** - * @desc Indicates a custom seek handler - * @desc Should implement `SeekHandler` interface - */ - customSeekHandler?: CustomSeekHandlerConstructor; - /** - * @desc Reuse 301/302 redirected url for subsequence request like seek, reconnect, etc. - * @defaultvalue false - */ - reuseRedirectedURL?: boolean; - /** - * @desc Indicates the Referrer Policy when using FetchStreamLoader - * @defaultvalue 'no-referrer-when-downgrade' (from docs) - */ - referrerPolicy?: ReferrerPolicy; - /** - * @desc Indicates additional headers that will be added to request - */ - headers?: { - [k: string]: string - } - /** - * @desc Should implement `BaseLoader` interface - */ - customLoader?: CustomLoaderConstructor; - } - - interface CustomSeekHandlerConstructor { - new(): SeekHandler; - } - - interface SeekHandler { - getConfig(sourceURL: string, range: Range): SeekConfig; - removeURLParameters(url: string): string; - } - - interface SeekConfig { - url: string; - headers: Headers | object; - } - - interface BaseLoaderConstructor { - new(typeName: string): BaseLoader; - } - - interface BaseLoader { - _status: number; - _needStash: boolean; - - destroy(): void; - isWorking(): boolean; - readonly type: string; - readonly status: number; - readonly needStashBuffer: boolean; - onContentLengthKnown: (contentLength: number) => void; - onURLRedirect: (redirectedURL: string) => void; - onDataArrival: (chunk: ArrayBuffer, byteStart: number, receivedLength?: number) => void; - onError: (errorType: LoaderErrors, errorInfo: LoaderErrorMessage) => void; - onComplete: (rangeFrom: number, rangeTo: number) => void; - open(dataSource: MediaSegment, range: Range): void; - abort(): void; - } - - interface CustomLoaderConstructor { - new(seekHandler: SeekHandler, config: Config): BaseLoader; - } - - interface Range { - from: number; - to: number; - } - - interface LoaderStatus { - readonly kIdle: 0; - readonly kConnecting: 1; - readonly kBuffering: 2; - readonly kError: 3; - readonly kComplete: 4; - } - - interface LoaderErrors { - readonly OK: 'OK'; - readonly EXCEPTION: 'Exception'; - readonly HTTP_STATUS_CODE_INVALID: 'HttpStatusCodeInvalid'; - readonly CONNECTING_TIMEOUT: 'ConnectingTimeout'; - readonly EARLY_EOF: 'EarlyEof'; - readonly UNRECOVERABLE_EARLY_EOF: 'UnrecoverableEarlyEof'; - } - - interface LoaderErrorMessage { - code: number; - msg: string; - } - - interface FeatureList { - mseFlvPlayback: boolean; - mseLiveFlvPlayback: boolean; - networkStreamIO: boolean; - networkLoaderName: string; - nativeMP4H264Playback: boolean; - nativeWebmVP8Playback: boolean; - nativeWebmVP9Playback: boolean; - } - - interface PlayerConstructor { - new(mediaDataSource: MediaDataSource, config?: Config): Player; - } - - interface Player { - destroy(): void; - on(event: string, listener: (...args: any[]) => void): void; - off(event: string, listener: (...args: any[]) => void): void; - attachMediaElement(mediaElement: HTMLMediaElement): void; - detachMediaElement(): void; - load(): void; - unload(): void; - play(): Promise | void; - pause(): void; - seek(offset: number): void; - type: 'NativePlayer' | 'FlvPlayer'; - buffered: TimeRanges; - duration: number; - volume: number; - muted: boolean; - currentTime: number; - /** - * @deprecated FlvPlayer/NativePlayer have its own `mediaInfo` field. - * @desc Keep it for backwards compatibility - * @since 1.4 - */ - mediaInfo: NativePlayerMediaInfo | FlvPlayerMediaInfo; - /** - * @deprecated FlvPlayer/NativePlayer have its own `statisticsInfo` field. - * @desc Keep it for backwards compatibility - * @since 1.4 - */ - statisticsInfo: NativePlayerStatisticsInfo | FlvPlayerStatisticsInfo; - - createdTime: number; - one(event: string, listener: (...args: any[]) => void): void; - _transmuxer: { - _controller: { - _ioctl: { - _speedSampler: { - averageKBps: number; - lastSecondKBps: number; - } - } - } - }; - _emitter: { - removeAllListeners: (v: string) => void; - } - } - - interface NativePlayerStatisticsInfo { - playerType: 'NativePlayer'; - url: string; - redirectedURL?: string; - decodedFrames?: number; - droppedFrames?: number; - } - - interface FlvPlayerReportStatisticsInfo { - url: string; - hasRedirect: boolean; - redirectedURL?: string; - speed: number; // KB/s - loaderType: string; - currentSegmentIndex: number; - totalSegmentCount: number; - } - - interface FlvPlayerStatisticsInfo extends Partial { - playerType: 'FlvPlayer'; - redirectedURL?: string; - decodedFrames?: number; - droppedFrames?: number; - } - - interface NativePlayerMediaInfo { - mimeType: string; - duration?: number; - width?: number; - height?: number; - } - - interface FlvPlayerMediaInfo extends NativePlayerMediaInfo { - audioCodec?: string; - videoCodec?: string; - audioDataRate?: number; - videoDataRate?: number; - hasAudio?: boolean; - hasVideo?: boolean; - chromaFormat?: string; - fps?: number; - - [k: string]: any; - } - - interface FlvPlayer extends Player { - mediaInfo: FlvPlayerMediaInfo; - statisticsInfo: FlvPlayerStatisticsInfo; - } - - interface NativePlayer extends Player { - mediaInfo: NativePlayerMediaInfo; - statisticsInfo: NativePlayerStatisticsInfo; - } - - interface LoggingControlConfig { - forceGlobalTag: boolean; - globalTag: string; - enableAll: boolean; - enableDebug: boolean; - enableVerbose: boolean; - enableInfo: boolean; - enableWarn: boolean; - enableError: boolean; - } - - interface LoggingControl extends LoggingControlConfig { - getConfig(): LoggingControlConfig; - applyConfig(config: Partial): void; - addLogListener(listener: (...args: any[]) => void): void; - removeLogListener(listener: (...args: any[]) => void): void; - } - - interface Events { - ERROR: string; - LOADING_COMPLETE: string; - RECOVERED_EARLY_EOF: string; - MEDIA_INFO: string; - METADATA_ARRIVED: string; - SCRIPTDATA_ARRIVED: string; - STATISTICS_INFO: string; - - HTTP_REQUEST_ENDED: string; - P2P_REQUEST_ENDED: string; - HTTP_HEADER_RECEIVED: string; - AUDIO_FRAME_DECODED: string; - VIDEO_FRAME_DECODED: string; - CTS_WARNING: string; - FLV_DATA_ABNORMAL: string; - LOADING_STARTED: string; - } - - interface ErrorTypes { - NETWORK_ERROR: string; - MEDIA_ERROR: string; - OTHER_ERROR: string; - } - - interface ErrorDetails { - NETWORK_EXCEPTION: string; - NETWORK_STATUS_CODE_INVALID: string; - NETWORK_TIMEOUT: string; - NETWORK_UNRECOVERABLE_EARLY_EOF: string; - - MEDIA_MSE_ERROR: string; - - MEDIA_FORMAT_ERROR: string; - MEDIA_FORMAT_UNSUPPORTED: string; - MEDIA_CODEC_UNSUPPORTED: string; - - ABNORMAL_SEGMENT_BYTELENGTH: string; - } -} - -declare var flvjs: { - createPlayer(mediaDataSource: flvjs.MediaDataSource, config?: flvjs.Config): flvjs.Player; - isSupported(): boolean; - getFeatureList(): flvjs.FeatureList; - - /** - * @deprecated Use `FlvJs.BaseLoaderConstructor` instead. - * Because it's not available on `flvjs` variable. - * @desc implement interface `BaseLoader` - * @since 1.4 - */ - BaseLoader: flvjs.BaseLoaderConstructor; - /** - * @deprecated Use `FlvJs.BaseLoaderConstructor` instead. - * Because it's not available on `flvjs` variable. - * @since 1.4 - */ - LoaderStatus: flvjs.LoaderStatus; - /** - * @deprecated Use `FlvJs.BaseLoaderConstructor` instead. - * Because it's not available on `flvjs` variable. - * @since 1.4 - */ - LoaderErrors: flvjs.LoaderErrors; - - readonly version: string; - - readonly Events: Readonly; - readonly ErrorTypes: Readonly; - readonly ErrorDetails: Readonly; - - readonly FlvPlayer: flvjs.PlayerConstructor; - readonly NativePlayer: flvjs.PlayerConstructor; - readonly LoggingControl: flvjs.LoggingControl; -}; - -export default flvjs; \ No newline at end of file diff --git a/src/flv.js/index.js b/src/flv.js/index.js deleted file mode 100644 index 9f520da..0000000 --- a/src/flv.js/index.js +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (C) 2016 Bilibili. All Rights Reserved. - * - * @author zheng qian - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import Polyfill from './utils/polyfill.js'; -import Features from './core/features.js'; -import FlvPlayer from './player/flv-player.js'; -import NativePlayer from './player/native-player.js'; -import PlayerEvents from './player/player-events.js'; -import { ErrorTypes, ErrorDetails } from './player/player-errors.js'; -import LoggingControl from './utils/logging-control.js'; -import { InvalidArgumentException } from './utils/exception.js'; - -// here are all the interfaces - -// install polyfills -Polyfill.install(); - -// factory method -function createPlayer(mediaDataSource, optionalConfig) { - let mds = mediaDataSource; - if (mds == null || typeof mds !== 'object') { - throw new InvalidArgumentException('MediaDataSource must be an javascript object!'); - } - - if (!mds.hasOwnProperty('type')) { - throw new InvalidArgumentException('MediaDataSource must has type field to indicate video file type!'); - } - - switch (mds.type) { - case 'flv': - return new FlvPlayer(mds, optionalConfig); - default: - return new NativePlayer(mds, optionalConfig); - } -} - -// feature detection -function isSupported() { - return Features.supportMSEH264Playback(); -} - -function getFeatureList() { - return Features.getFeatureList(); -} - -// interfaces -let flvjs = {}; - -flvjs.createPlayer = createPlayer; -flvjs.isSupported = isSupported; -flvjs.getFeatureList = getFeatureList; - -flvjs.Events = PlayerEvents; -flvjs.ErrorTypes = ErrorTypes; -flvjs.ErrorDetails = ErrorDetails; - -flvjs.FlvPlayer = FlvPlayer; -flvjs.NativePlayer = NativePlayer; -flvjs.LoggingControl = LoggingControl; - -Object.defineProperty(flvjs, 'version', { - enumerable: true, - get: function () { - // replaced by browserify-versionify transform - return '__VERSION__'; - }, -}); - -export default flvjs; diff --git a/src/flv.js/io/fetch-stream-loader.js b/src/flv.js/io/fetch-stream-loader.js deleted file mode 100644 index e1753e1..0000000 --- a/src/flv.js/io/fetch-stream-loader.js +++ /dev/null @@ -1,271 +0,0 @@ -/* - * Copyright (C) 2016 Bilibili. All Rights Reserved. - * - * @author zheng qian - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import Log from '../utils/logger.js'; -import Browser from '../utils/browser.js'; -import { BaseLoader, LoaderStatus, LoaderErrors } from './loader.js'; -import { RuntimeException } from '../utils/exception.js'; - -let urlIndex = 0; - -/* fetch + stream IO loader. Currently working on chrome 43+. - * fetch provides a better alternative http API to XMLHttpRequest - * - * fetch spec https://fetch.spec.whatwg.org/ - * stream spec https://streams.spec.whatwg.org/ - */ -class FetchStreamLoader extends BaseLoader { - static isSupported() { - try { - // fetch + stream is broken on Microsoft Edge. Disable before build 15048. - // see https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/8196907/ - // Fixed in Jan 10, 2017. Build 15048+ removed from blacklist. - let isWorkWellEdge = Browser.msedge && Browser.version.minor >= 15048; - let browserNotBlacklisted = Browser.msedge ? isWorkWellEdge : true; - return self.fetch && self.ReadableStream && browserNotBlacklisted; - } catch (e) { - return false; - } - } - - constructor(seekHandler, config) { - super('fetch-stream-loader'); - this.TAG = 'FetchStreamLoader'; - - this._seekHandler = seekHandler; - this._config = config; - this._needStash = true; - - this._requestAbort = false; - this._contentLength = null; - this._receivedLength = 0; - } - - destroy() { - if (this.isWorking()) { - this.abort(); - } - super.destroy(); - } - - open(dataSource, range, remainingAttempts = 3) { - this._dataSource = dataSource; - this._range = range; - - let sourceURL = dataSource.url; - - // 使用备线 - if (dataSource.backupURL && dataSource.backupURL.length > 0 && urlIndex) { - const index = urlIndex % (dataSource.backupURL.length + 1); - if (index !== 0) { - sourceURL = dataSource.backupURL[index - 1]; - } - } - - if (this._config.reuseRedirectedURL && dataSource.redirectedURL != undefined) { - sourceURL = dataSource.redirectedURL; - } - - let seekConfig = this._seekHandler.getConfig(sourceURL, range); - - let headers = new self.Headers(); - - if (typeof seekConfig.headers === 'object') { - let configHeaders = seekConfig.headers; - for (let key in configHeaders) { - if (configHeaders.hasOwnProperty(key)) { - headers.append(key, configHeaders[key]); - } - } - } - - let params = { - method: 'GET', - headers: headers, - mode: 'cors', - cache: 'default', - // The default policy of Fetch API in the whatwg standard - // Safari incorrectly indicates 'no-referrer' as default policy, fuck it - referrerPolicy: 'no-referrer-when-downgrade', - }; - - // cors is enabled by default - if (dataSource.cors === false) { - // no-cors means 'disregard cors policy', which can only be used in ServiceWorker - params.mode = 'same-origin'; - } - - // withCredentials is disabled by default - if (dataSource.withCredentials) { - params.credentials = 'include'; - } - - // referrerPolicy from config - if (dataSource.referrerPolicy) { - params.referrerPolicy = dataSource.referrerPolicy; - } - - this.status = LoaderStatus.kConnecting; - self.fetch(seekConfig.url, params) - .then((res) => { - if (this._requestAbort) { - this._requestAbort = false; - return; - } - if (this.onHeaderReceived) { - this.onHeaderReceived(res.headers); - } - if (res.ok && res.status >= 200 && res.status <= 299) { - if (res.url !== seekConfig.url) { - if (this._onURLRedirect) { - let redirectedURL = this._seekHandler.removeURLParameters(res.url); - this._onURLRedirect(redirectedURL); - } - } - - let lengthHeader = res.headers.get('Content-Length'); - if (lengthHeader != null) { - this._contentLength = parseInt(lengthHeader); - if (this._contentLength !== 0) { - if (this._onContentLengthKnown) { - this._onContentLengthKnown(this._contentLength); - } - } - } - - return this._pump.call(this, res.body.getReader()); - } else { - if (remainingAttempts > 0) { - urlIndex++; - this.open(dataSource, range, remainingAttempts - 1); - } else { - this.status = LoaderStatus.kError; - if (this._onError) { - this._onError(LoaderErrors.HTTP_STATUS_CODE_INVALID, { - code: res.status, - msg: res.statusText, - }); - } else { - throw new RuntimeException( - 'FetchStreamLoader: Http code invalid, ' + res.status + ' ' + res.statusText, - ); - } - } - } - }) - .catch((e) => { - if (remainingAttempts > 0) { - urlIndex++; - this.open(dataSource, range, remainingAttempts - 1); - } else { - this.status = LoaderStatus.kError; - if (this._onError) { - this._onError(LoaderErrors.EXCEPTION, { code: -1, msg: e.message }); - } else { - throw e; - } - } - }); - if (this.onStarted) { - this.onStarted(Date.now()); - } - } - - abort() { - this._requestAbort = true; - this.status = LoaderStatus.kComplete; - } - - _pump(reader) { - // ReadableStreamReader - return reader - .read() - .then((result) => { - if (result.done) { - // First check received length - if (this._contentLength !== null && this._receivedLength < this._contentLength) { - // Report Early-EOF - this.status = LoaderStatus.kError; - let type = LoaderErrors.EARLY_EOF; - let info = { code: -1, msg: 'Fetch stream meet Early-EOF' }; - if (this._onError) { - this._onError(type, info); - } else { - throw new RuntimeException(info.msg); - } - } else { - // OK. Download complete - this.status = LoaderStatus.kComplete; - if (this._onComplete) { - this._onComplete(this._range.from, this._range.from + this._receivedLength - 1); - } - } - } else { - if (this._requestAbort === true) { - this._requestAbort = false; - return reader.cancel(); - } - - this.status = LoaderStatus.kBuffering; - - let chunk = result.value.buffer; - let byteStart = this._range.from + this._receivedLength; - this._receivedLength += chunk.byteLength; - - if (this._onDataArrival) { - this._onDataArrival(chunk, byteStart, this._receivedLength); - } - - this._pump(reader); - } - }) - .catch((e) => { - if (e.code === 11 && Browser.msedge) { - // InvalidStateError on Microsoft Edge - // Workaround: Edge may throw InvalidStateError after ReadableStreamReader.cancel() call - // Ignore the unknown exception. - // Related issue: https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/11265202/ - return; - } - - this.status = LoaderStatus.kError; - let type = 0; - let info = null; - - if ( - (e.code === 19 || e.message === 'network error') && // NETWORK_ERR - (this._contentLength === null || - (this._contentLength !== null && this._receivedLength < this._contentLength)) - ) { - type = LoaderErrors.EARLY_EOF; - info = { code: e.code, msg: 'Fetch stream meet Early-EOF' }; - } else { - type = LoaderErrors.EXCEPTION; - info = { code: e.code, msg: e.message }; - } - - if (this._onError) { - this._onError(type, info); - } else { - throw new RuntimeException(info.msg); - } - }); - } -} - -export default FetchStreamLoader; diff --git a/src/flv.js/io/io-controller.js b/src/flv.js/io/io-controller.js deleted file mode 100644 index a46bf12..0000000 --- a/src/flv.js/io/io-controller.js +++ /dev/null @@ -1,800 +0,0 @@ -/* - * Copyright (C) 2016 Bilibili. All Rights Reserved. - * - * @author zheng qian - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import EventEmitter from 'events'; -import Log from '../utils/logger.js'; -import SpeedSampler from './speed-sampler.js'; -import { LoaderStatus, LoaderErrors, LoaderEvents } from './loader.js'; -import FetchStreamLoader from './fetch-stream-loader.js'; -import MozChunkedLoader from './xhr-moz-chunked-loader.js'; -// import MSStreamLoader from './xhr-msstream-loader.js'; -import RangeLoader from './xhr-range-loader.js'; -import YFLoader from './yf-loader.js'; -import XYVODLoader from './xyvod-loader'; -import WebSocketLoader from './websocket-loader.js'; -import RangeSeekHandler from './range-seek-handler.js'; -import ParamSeekHandler from './param-seek-handler.js'; -import { RuntimeException, IllegalStateException, InvalidArgumentException } from '../utils/exception.js'; -import PlayerEvents, { OTHER_EVENTS_POLYMER } from '../player/player-events.js'; - -/** - * DataSource: { - * url: string, - * filesize: number, - * cors: boolean, - * withCredentials: boolean - * } - * - */ - -// Manage IO Loaders -class IOController { - constructor(dataSource, config, extraData, mediaElement, bNewSlice) { - this.TAG = 'IOController'; - - this._config = config; - this._extraData = extraData; - this._mediaElement = mediaElement; - this._emitter = new EventEmitter(); - - this._stashInitialSize = 1024 * 384; // default initial size: 384KB - if (config.stashInitialSize != undefined && config.stashInitialSize > 0) { - // apply from config - this._stashInitialSize = config.stashInitialSize; - } - - this._stashUsed = 0; - this._stashSize = this._stashInitialSize; - this._bufferSize = 1024 * 1024 * 3; // initial size: 3MB - this._stashBuffer = new ArrayBuffer(this._bufferSize); - this._stashByteStart = 0; - this._enableStash = true; - if (config.enableStashBuffer === false) { - this._enableStash = false; - } - - this._loader = null; - this._loaderClass = null; - this._seekHandler = null; - - this._dataSource = dataSource; - this._isWebSocketURL = /wss?:\/\/(.+?)/.test(dataSource.url); - this._refTotalLength = dataSource.filesize ? dataSource.filesize : null; - this._totalLength = this._refTotalLength; - this._fullRequestFlag = false; - this._currentRange = null; - this._redirectedURL = null; - - this._speedNormalized = 0; - this._speedSampler = new SpeedSampler(); - this._speedNormalizeList = [64, 128, 256, 384, 512, 768, 1024, 1536, 2048, 3072, 4096]; - - this._isEarlyEofReconnecting = false; - - this._paused = false; - this._resumeFrom = 0; - this._requestStartTime = null; - - this._onDataArrival = null; - this._onSeeked = null; - this._onError = null; - this._onComplete = null; - this._onRedirect = null; - this._onRecoveredEarlyEof = null; - this._bNewSlice = bNewSlice; - - this._selectSeekHandler(); - this._selectLoader(); - this._createLoader(bNewSlice); - this._onPerRequestReceivedBytesEvent(); - } - - destroy() { - if (this._loader.isWorking()) { - this._loader.abort(); - } - this._loader.destroy(); - this._loader = null; - this._loaderClass = null; - this._backupLoaderClass = null; - this._dataSource = null; - this._stashBuffer = null; - this._stashUsed = this._stashSize = this._bufferSize = this._stashByteStart = 0; - this._currentRange = null; - - this._isEarlyEofReconnecting = false; - - this._onDataArrival = null; - this._onSeeked = null; - this._onError = null; - this._onComplete = null; - this._onRedirect = null; - this._onRecoveredEarlyEof = null; - - this._requestStartTime = null; - this._emitter.removeAllListeners(); - this._emitter = null; - this._extraData = null; - this._speedSampler.destroy(); - this._speedSampler = null; - } - - isWorking() { - return this._loader && this._loader.isWorking() && !this._paused; - } - - isPaused() { - return this._paused; - } - - get status() { - return this._loader.status; - } - - get extraData() { - return this._extraData; - } - - set extraData(data) { - this._extraData = data; - } - - // prototype: function onDataArrival(chunks: ArrayBuffer, byteStart: number): number - get onDataArrival() { - return this._onDataArrival; - } - - set onDataArrival(callback) { - this._onDataArrival = callback; - } - - get onSeeked() { - return this._onSeeked; - } - - set onSeeked(callback) { - this._onSeeked = callback; - } - - // prototype: function onError(type: number, info: {code: number, msg: string}): void - get onError() { - return this._onError; - } - - set onError(callback) { - this._onError = callback; - } - - get onComplete() { - return this._onComplete; - } - - set onComplete(callback) { - this._onComplete = callback; - } - - get onRedirect() { - return this._onRedirect; - } - - set onRedirect(callback) { - this._onRedirect = callback; - } - - get onRecoveredEarlyEof() { - return this._onRecoveredEarlyEof; - } - - set onRecoveredEarlyEof(callback) { - this._onRecoveredEarlyEof = callback; - } - - get currentURL() { - return this._dataSource.url; - } - - get hasRedirect() { - return this._redirectedURL != null || this._dataSource.redirectedURL != undefined; - } - - get currentRedirectedURL() { - return this._redirectedURL || this._dataSource.redirectedURL; - } - - // in KB/s - get currentSpeed() { - if (this._loaderClass === RangeLoader) { - // SpeedSampler is inaccuracy if loader is RangeLoader - return this._loader.currentSpeed; - } - return this._speedSampler.lastSecondKBps; - } - - get loaderType() { - return this._loader.type; - } - - _selectSeekHandler() { - let config = this._config; - - if (config.seekType === 'range') { - this._seekHandler = new RangeSeekHandler(this._config.rangeLoadZeroStart); - } else if (config.seekType === 'param') { - let paramStart = config.seekParamStart || 'bstart'; - let paramEnd = config.seekParamEnd || 'bend'; - - this._seekHandler = new ParamSeekHandler(paramStart, paramEnd); - } else if (config.seekType === 'custom') { - if (typeof config.customSeekHandler !== 'function') { - throw new InvalidArgumentException( - 'Custom seekType specified in config but invalid customSeekHandler!', - ); - } - this._seekHandler = new config.customSeekHandler(); - } else { - throw new InvalidArgumentException(`Invalid seekType in config: ${config.seekType}`); - } - } - - _selectLoader() { - this._loaderClass = null; - this._config.p2pType = window['__FLV_P2P_TYPE__']; - if (this._config.p2pType) { - switch (this._config.p2pType) { - case 'yf-eg': - this._loaderClass = YFLoader; - break; - case 'xl-eg': - this._loaderClass = XYVODLoader; - break; - default: - break; - } - if (this._loaderClass !== null && FetchStreamLoader.isSupported()) { - this._backupLoaderClass = FetchStreamLoader; - return; - } - } - if (this._isWebSocketURL) { - this._loaderClass = WebSocketLoader; - } else if (FetchStreamLoader.isSupported()) { - this._loaderClass = FetchStreamLoader; - } else if (MozChunkedLoader.isSupported()) { - this._loaderClass = MozChunkedLoader; - } else if (RangeLoader.isSupported()) { - this._loaderClass = RangeLoader; - } else { - throw new RuntimeException("Your browser doesn't support xhr with arraybuffer responseType!"); - } - } - - _createLoader(bNewSlice) { - this._loader = new this._loaderClass(this._seekHandler, this._config, this._mediaElement, bNewSlice); - if (this._loader.needStashBuffer === false) { - this._enableStash = false; - } - if (this.loaderType == 'yf-loader' || this.loaderType == 'xyvod-loader') { - let backupLoader = new this._backupLoaderClass( - this._seekHandler, - this._config, - this._mediaElement, - bNewSlice, - ); - backupLoader.onHeaderReceived = this._onHeaderReceived.bind(this); - backupLoader.onContentLengthKnown = this._onContentLengthKnown.bind(this); - backupLoader.onURLRedirect = this._onURLRedirect.bind(this); - backupLoader.onError = this._onLoaderError.bind(this); - this._loader.setBackupLoader(backupLoader); - } - this._loader.onHeaderReceived = this._onHeaderReceived.bind(this); - this._loader.onContentLengthKnown = this._onContentLengthKnown.bind(this); - this._loader.onURLRedirect = this._onURLRedirect.bind(this); - this._loader.onDataArrival = this._onLoaderChunkArrival.bind(this); - this._loader.onStarted = this._onLoaderStarted.bind(this); - this._loader.onComplete = this._onLoaderComplete.bind(this); - this._loader.onError = this._onLoaderError.bind(this); - this._loader.onNeedStashBuffer = this._onNeedStashBuffer.bind(this); - this._loader.on(LoaderEvents.STATUS_CHANGE, this._onLoaderStatusChange.bind(this)); - } - - _onPerRequestReceivedBytesEvent() { - this._speedSampler.on(PlayerEvents.HTTP_REQUEST_ENDED, (totalBytes) => { - this._emitter.emit(OTHER_EVENTS_POLYMER, { - eventName: PlayerEvents.HTTP_REQUEST_ENDED, - eventParams: [totalBytes, this._dataSource.url], - }); - }); - this._speedSampler.on(PlayerEvents.P2P_REQUEST_ENDED, (cdnLoadedBytes, p2pLoadedBytes, backupLoadedBytes) => { - // Log.w(this.TAG, `Load Info: cdnLoadedBytes=${cdnLoadedBytes}, p2pLoadedBytes=${p2pLoadedBytes}`); - this._emitter.emit(OTHER_EVENTS_POLYMER, { - eventName: PlayerEvents.P2P_REQUEST_ENDED, - eventParams: [ - cdnLoadedBytes, - p2pLoadedBytes, - backupLoadedBytes, - this._dataSource.url, - this._config.p2pType, - ], - }); - }); - } - - open(optionalFrom) { - this._emitter.emit(OTHER_EVENTS_POLYMER, { - eventName: PlayerEvents.CREATE_LOADER, - eventParams: [this.loaderType], - }); - this._currentRange = { from: 0, to: -1 }; - if (optionalFrom) { - this._currentRange.from = optionalFrom; - } - - if (!optionalFrom) { - this._fullRequestFlag = true; - } - - this._updateLoaderStashSize(); - this._loader.open(this._dataSource, Object.assign({}, this._currentRange)); - } - - abort() { - this._loader.abort(); - - if (this._paused) { - this._paused = false; - this._resumeFrom = 0; - } - } - - pause() { - if (this.isWorking()) { - this._loader.abort(); - - if (this._stashUsed !== 0) { - this._resumeFrom = this._stashByteStart; - this._currentRange.to = this._stashByteStart - 1; - } else { - this._resumeFrom = this._currentRange.to + 1; - } - this._stashUsed = 0; - this._stashByteStart = 0; - this._paused = true; - } - } - - resume() { - if (this._paused) { - this._paused = false; - let bytes = this._resumeFrom; - this._resumeFrom = 0; - this._internalSeek(bytes, true, true); - } - } - - seek(bytes) { - this._paused = false; - this._stashUsed = 0; - this._stashByteStart = 0; - this._internalSeek(bytes, true); - } - - /** - * When seeking request is from media seeking, unconsumed stash data should be dropped - * However, stash data shouldn't be dropped if seeking requested from http reconnection - * - * @dropUnconsumed: Ignore and discard all unconsumed data in stash buffer - */ - _internalSeek(bytes, dropUnconsumed, resume) { - if (this._loader.isWorking()) { - this._loader.abort(); - } - - // dispatch & flush stash buffer before seek - this._flushStashBuffer(dropUnconsumed); - - let requestRange = { from: bytes, to: -1 }; - this._currentRange = { from: requestRange.from, to: -1 }; - - if (resume && this.loaderType === 'yf-loader' && this._loader.useP2pIO()) { - this._loader.resume(requestRange); - if (this._onSeeked) { - this._onSeeked(); - } - return; - } - // p2p - let noResource = resume && this._loader.noResource ? true : false; - - this._loader.destroy(); - this._loader = null; - - this._stashSize = this._stashInitialSize; - this._selectLoader(); - this._createLoader(); - - this._updateLoaderStashSize(); - if (noResource && this._loader.noResource !== undefined) { - this._loader.noResource = true; - } - - this._loader.open(this._dataSource, requestRange); - - if (this._onSeeked) { - this._onSeeked(); - } - } - - updateUrl(url) { - if (!url || typeof url !== 'string' || url.length === 0) { - throw new InvalidArgumentException('Url must be a non-empty string!'); - } - - this._dataSource.url = url; - - // TODO: replace with new url - } - - _expandBuffer(expectedBytes) { - let bufferNewSize = this._stashSize; - while (bufferNewSize + 1024 * 1024 * 1 < expectedBytes) { - bufferNewSize *= 2; - } - - bufferNewSize += 1024 * 1024 * 1; // bufferSize = stashSize + 1MB - if (bufferNewSize === this._bufferSize) { - return; - } - - let newBuffer = new ArrayBuffer(bufferNewSize); - - if (this._stashUsed > 0) { - // copy existing data into new buffer - let stashOldArray = new Uint8Array(this._stashBuffer, 0, this._stashUsed); - let stashNewArray = new Uint8Array(newBuffer, 0, bufferNewSize); - stashNewArray.set(stashOldArray, 0); - } - - this._stashBuffer = newBuffer; - this._bufferSize = bufferNewSize; - } - - _normalizeSpeed(input) { - let list = this._speedNormalizeList; - let last = list.length - 1; - let mid = 0; - let lbound = 0; - let ubound = last; - - if (input < list[0]) { - return list[0]; - } - - // binary search - while (lbound <= ubound) { - mid = lbound + Math.floor((ubound - lbound) / 2); - if (mid === last || (input >= list[mid] && input < list[mid + 1])) { - return list[mid]; - } else if (list[mid] < input) { - lbound = mid + 1; - } else { - ubound = mid - 1; - } - } - } - - _adjustStashSize(normalized) { - let stashSizeKB = 0; - - if (this._config.isLive) { - // live stream: always use single normalized speed for size of stashSizeKB - stashSizeKB = normalized; - } else { - if (normalized < 512) { - stashSizeKB = normalized; - } else if (normalized >= 512 && normalized <= 1024) { - stashSizeKB = Math.floor(normalized * 1.5); - } else { - stashSizeKB = normalized * 2; - } - } - - if (this.loaderType === 'yf-loader') { - let timeRange = this._mediaElement.buffered; - if (timeRange.length < 1 || timeRange.end(0) - this._mediaElement.currentTime <= 10) { - if (stashSizeKB > 512) { - stashSizeKB = 512; - } - } - if (stashSizeKB > 2048) { - stashSizeKB = 2048; - } - } - - if (stashSizeKB > 8192) { - stashSizeKB = 8192; - } - - let bufferSize = stashSizeKB * 1024 + 1024 * 1024 * 1; // stashSize + 1MB - if (this._bufferSize < bufferSize) { - this._expandBuffer(bufferSize); - } - this._stashSize = stashSizeKB * 1024; - //Log.w(this.TAG, `adjust stash size, new size: ${this._stashSize}`); - this._updateLoaderStashSize(); - } - - _dispatchChunks(chunks, byteStart) { - this._currentRange.to = byteStart + chunks.byteLength - 1; - return this._onDataArrival(chunks, byteStart); - } - - _onURLRedirect(redirectedURL) { - this._redirectedURL = redirectedURL; - if (this._onRedirect) { - this._onRedirect(redirectedURL); - } - } - - _onHeaderReceived(headers) { - this._emitter.emit(OTHER_EVENTS_POLYMER, { - eventName: PlayerEvents.HTTP_HEADER_RECEIVED, - eventParams: [Date.now() - this._requestStartTime, this._dataSource.url, headers], - }); - } - - _onContentLengthKnown(contentLength) { - if (contentLength && this._fullRequestFlag) { - this._totalLength = contentLength; - this._fullRequestFlag = false; - } - } - - _onLoaderChunkArrival(chunk, byteStart, receivedLength, p2pLoadInfo) { - if (!this._onDataArrival) { - throw new IllegalStateException('IOController: No existing consumer (onDataArrival) callback!'); - } - if (this._paused) { - return; - } - if (this._isEarlyEofReconnecting) { - // Auto-reconnect for EarlyEof succeed, notify to upper-layer by callback - this._isEarlyEofReconnecting = false; - if (this._onRecoveredEarlyEof) { - this._onRecoveredEarlyEof(); - } - } - - this._speedSampler.addBytes(chunk.byteLength, p2pLoadInfo); - - // adjust stash buffer size according to network speed dynamically - let KBps = this._speedSampler.lastSecondKBps; - if (KBps !== 0) { - let normalized = this._normalizeSpeed(KBps); - if (this._speedNormalized !== normalized) { - this._speedNormalized = normalized; - this._adjustStashSize(normalized); - } - } - - if (!this._enableStash) { - // disable stash - if (this._stashUsed === 0) { - // dispatch chunk directly to consumer; - // check ret value (consumed bytes) and stash unconsumed to stashBuffer - let consumed = this._dispatchChunks(chunk, byteStart); - if (consumed < chunk.byteLength) { - // unconsumed data remain. - let remain = chunk.byteLength - consumed; - if (remain > this._bufferSize) { - this._expandBuffer(remain); - } - let stashArray = new Uint8Array(this._stashBuffer, 0, this._bufferSize); - stashArray.set(new Uint8Array(chunk, consumed), 0); - this._stashUsed += remain; - this._stashByteStart = byteStart + consumed; - } - } else { - // else: Merge chunk into stashBuffer, and dispatch stashBuffer to consumer. - if (this._stashUsed + chunk.byteLength > this._bufferSize) { - this._expandBuffer(this._stashUsed + chunk.byteLength); - } - let stashArray = new Uint8Array(this._stashBuffer, 0, this._bufferSize); - stashArray.set(new Uint8Array(chunk), this._stashUsed); - this._stashUsed += chunk.byteLength; - let consumed = this._dispatchChunks(this._stashBuffer.slice(0, this._stashUsed), this._stashByteStart); - if (consumed < this._stashUsed && consumed > 0) { - // unconsumed data remain - let remainArray = new Uint8Array(this._stashBuffer, consumed); - stashArray.set(remainArray, 0); - } - this._stashUsed -= consumed; - this._stashByteStart += consumed; - } - } else { - // enable stash - if (this._stashUsed === 0 && this._stashByteStart === 0) { - // seeked? or init chunk? - // This is the first chunk after seek action - this._stashByteStart = byteStart; - } - if (this._stashUsed + chunk.byteLength <= this._stashSize) { - // just stash - let stashArray = new Uint8Array(this._stashBuffer, 0, this._stashSize); - stashArray.set(new Uint8Array(chunk), this._stashUsed); - this._stashUsed += chunk.byteLength; - } else { - // stashUsed + chunkSize > stashSize, size limit exceeded - let stashArray = new Uint8Array(this._stashBuffer, 0, this._bufferSize); - if (this._stashUsed > 0) { - // There're stash datas in buffer - // dispatch the whole stashBuffer, and stash remain data - // then append chunk to stashBuffer (stash) - let buffer = this._stashBuffer.slice(0, this._stashUsed); - let consumed = this._dispatchChunks(buffer, this._stashByteStart); - if (consumed < buffer.byteLength) { - if (consumed > 0) { - let remainArray = new Uint8Array(buffer, consumed); - stashArray.set(remainArray, 0); - this._stashUsed = remainArray.byteLength; - this._stashByteStart += consumed; - } - } else { - this._stashUsed = 0; - this._stashByteStart += consumed; - } - if (this._stashUsed + chunk.byteLength > this._bufferSize) { - this._expandBuffer(this._stashUsed + chunk.byteLength); - stashArray = new Uint8Array(this._stashBuffer, 0, this._bufferSize); - } - stashArray.set(new Uint8Array(chunk), this._stashUsed); - this._stashUsed += chunk.byteLength; - } else { - // stash buffer empty, but chunkSize > stashSize (oh, holy shit) - // dispatch chunk directly and stash remain data - let consumed = this._dispatchChunks(chunk, byteStart); - if (consumed < chunk.byteLength) { - let remain = chunk.byteLength - consumed; - if (remain > this._bufferSize) { - this._expandBuffer(remain); - stashArray = new Uint8Array(this._stashBuffer, 0, this._bufferSize); - } - stashArray.set(new Uint8Array(chunk, consumed), 0); - this._stashUsed += remain; - this._stashByteStart = byteStart + consumed; - } - } - } - } - } - - _flushStashBuffer(dropUnconsumed) { - if (this._stashUsed > 0) { - let buffer = this._stashBuffer.slice(0, this._stashUsed); - let consumed = this._dispatchChunks(buffer, this._stashByteStart); - let remain = buffer.byteLength - consumed; - - if (consumed < buffer.byteLength) { - if (dropUnconsumed) { - Log.w(this.TAG, `${remain} bytes unconsumed data remain when flush buffer, dropped`); - } else { - if (consumed > 0) { - let stashArray = new Uint8Array(this._stashBuffer, 0, this._bufferSize); - let remainArray = new Uint8Array(buffer, consumed); - stashArray.set(remainArray, 0); - this._stashUsed = remainArray.byteLength; - this._stashByteStart += consumed; - } - return 0; - } - } - this._stashUsed = 0; - this._stashByteStart = 0; - return remain; - } - return 0; - } - - _onLoaderStarted(timestamp) { - this._emitter.emit(OTHER_EVENTS_POLYMER, { - eventName: PlayerEvents.LOADING_STARTED, - eventParams: [timestamp], - }); - } - - _onLoaderComplete(from, to) { - // Force-flush stash buffer, and drop unconsumed data - this._flushStashBuffer(true); - - if (this._onComplete) { - this._onComplete(this._extraData, from, to, this._dataSource && this._dataSource.url); - } - } - - _onLoaderError(type, data) { - Log.e(this.TAG, `Loader error, code = ${data.code}, msg = ${data.msg}`); - - this._flushStashBuffer(false); - - if (this._isEarlyEofReconnecting) { - // Auto-reconnect for EarlyEof failed, throw UnrecoverableEarlyEof error to upper-layer - this._isEarlyEofReconnecting = false; - type = LoaderErrors.UNRECOVERABLE_EARLY_EOF; - } - - switch (type) { - case LoaderErrors.EARLY_EOF: { - if (!this._config.isLive) { - // Do internal http reconnect if not live stream - if (this._totalLength) { - let nextFrom = this._currentRange.to + 1; - if (nextFrom < this._totalLength) { - Log.w(this.TAG, 'Connection lost, trying reconnect...'); - this._isEarlyEofReconnecting = true; - this._internalSeek(nextFrom, false); - } - return; - } - // else: We don't know totalLength, throw UnrecoverableEarlyEof - } - // live stream: throw UnrecoverableEarlyEof error to upper-layer - type = LoaderErrors.UNRECOVERABLE_EARLY_EOF; - break; - } - case LoaderErrors.UNRECOVERABLE_EARLY_EOF: - case LoaderErrors.CONNECTING_TIMEOUT: - case LoaderErrors.HTTP_STATUS_CODE_INVALID: - case LoaderErrors.EXCEPTION: - break; - } - - if (this._onError) { - this._onError(type, data); - } else { - throw new RuntimeException('IOException: ' + data.msg); - } - } - - _onLoaderStatusChange(prevStatus, status) { - if (status === LoaderStatus.kError || status === LoaderStatus.kComplete) { - this._speedSampler.reset(); - } - if (status === LoaderStatus.kConnecting) { - this._requestStartTime = Date.now(); - } - } - - _onNeedStashBuffer(bNeed) { - this._enableStash = bNeed; - } - - _updateLoaderStashSize() { - if (this._loader && this._loader.setStashSize) { - this._loader.setStashSize(this._stashSize); - } - } - - on(event, listener) { - this._emitter.addListener(event, listener); - } - - off(event, listener) { - this._emitter.removeListener(event, listener); - } -} - -export default IOController; diff --git a/src/flv.js/io/loader.js b/src/flv.js/io/loader.js deleted file mode 100644 index fca0aed..0000000 --- a/src/flv.js/io/loader.js +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright (C) 2016 Bilibili. All Rights Reserved. - * - * @author zheng qian - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import EventEmitter from 'events'; -import Log from '../utils/logger.js'; -import { NotImplementedException } from '../utils/exception.js'; - -export const LoaderStatus = { - kIdle: 0, - kConnecting: 1, - kBuffering: 2, - kError: 3, - kComplete: 4, -}; - -export const LoaderErrors = { - OK: 'OK', - EXCEPTION: 'Exception', - HTTP_STATUS_CODE_INVALID: 'HttpStatusCodeInvalid', - CONNECTING_TIMEOUT: 'ConnectingTimeout', - EARLY_EOF: 'EarlyEof', - UNRECOVERABLE_EARLY_EOF: 'UnrecoverableEarlyEof', -}; - -export const LoaderEvents = { - STATUS_CHANGE: 'StatusChange', -}; - -/* Loader has callbacks which have following prototypes: - * function onContentLengthKnown(contentLength: number): void - * function onURLRedirect(url: string): void - * function onDataArrival(chunk: ArrayBuffer, byteStart: number, receivedLength: number): void - * function onError(errorType: number, errorInfo: {code: number, msg: string}): void - * function onComplete(rangeFrom: number, rangeTo: number): void - */ -export class BaseLoader { - constructor(typeName) { - this._type = typeName || 'undefined'; - /** @private */ - this._status = LoaderStatus.kIdle; - this._needStash = false; - // callbacks - this._onHeaderReceived = null; - this._onContentLengthKnown = null; - this._onURLRedirect = null; - this._onDataArrival = null; - this._onError = null; - this._onStarted = null; - this._onComplete = null; - this._emitter = new EventEmitter(); - } - - destroy() { - this.status = LoaderStatus.kIdle; - this._onHeaderReceived = null; - this._onContentLengthKnown = null; - this._onURLRedirect = null; - this._onDataArrival = null; - this._onError = null; - this._onStarted = null; - this._onComplete = null; - this._emitter.removeAllListeners(); - this._emitter = null; - } - - isWorking() { - return this.status === LoaderStatus.kConnecting || this.status === LoaderStatus.kBuffering; - } - - get type() { - return this._type; - } - - get status() { - return this._status; - } - - set status(val) { - if (this._status !== val) { - if (this._emitter) { - this._emitter.emit(LoaderEvents.STATUS_CHANGE, this._status, (this._status = val)); - } else { - // The `_status` maybe set to a certain kind of status after invoking `destroy` function. - // In this case, setting `_status` should be ignore because instance has been destroyed. - this._status = val; - Log.w('StatusChange', 'Status has been set, but instance had been destroyed'); - } - } - } - - get needStashBuffer() { - return this._needStash; - } - - get onHeaderReceived() { - return this._onHeaderReceived; - } - - set onHeaderReceived(callback) { - this._onHeaderReceived = callback; - } - - get onContentLengthKnown() { - return this._onContentLengthKnown; - } - - set onContentLengthKnown(callback) { - this._onContentLengthKnown = callback; - } - - get onURLRedirect() { - return this._onURLRedirect; - } - - set onURLRedirect(callback) { - this._onURLRedirect = callback; - } - - get onDataArrival() { - return this._onDataArrival; - } - - set onDataArrival(callback) { - this._onDataArrival = callback; - } - - get onError() { - return this._onError; - } - - set onError(callback) { - this._onError = callback; - } - - get onStarted() { - return this._onStarted; - } - - set onStarted(callback) { - this._onStarted = callback; - } - - get onComplete() { - return this._onComplete; - } - - set onComplete(callback) { - this._onComplete = callback; - } - - // pure virtual - open(dataSource, range) { - throw new NotImplementedException('Unimplemented abstract function!'); - } - - abort() { - throw new NotImplementedException('Unimplemented abstract function!'); - } - - on(event, listener) { - this._emitter.addListener(event, listener); - } - - off(event, listener) { - this._emitter.removeListener(event, listener); - } -} diff --git a/src/flv.js/io/param-seek-handler.js b/src/flv.js/io/param-seek-handler.js deleted file mode 100644 index 8d45478..0000000 --- a/src/flv.js/io/param-seek-handler.js +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (C) 2016 Bilibili. All Rights Reserved. - * - * @author zheng qian - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -class ParamSeekHandler { - constructor(paramStart, paramEnd) { - this._startName = paramStart; - this._endName = paramEnd; - } - - getConfig(baseUrl, range) { - let url = baseUrl; - - if (range.from !== 0 || range.to !== -1) { - let needAnd = true; - if (url.indexOf('?') === -1) { - url += '?'; - needAnd = false; - } - - if (needAnd) { - url += '&'; - } - - url += `${this._startName}=${range.from.toString()}`; - - if (range.to !== -1) { - url += `&${this._endName}=${range.to.toString()}`; - } - } - - return { - url: url, - headers: {}, - }; - } - - removeURLParameters(seekedURL) { - let baseURL = seekedURL.split('?')[0]; - let params = undefined; - - let queryIndex = seekedURL.indexOf('?'); - if (queryIndex !== -1) { - params = seekedURL.substring(queryIndex + 1); - } - - let resultParams = ''; - - if (params != undefined && params.length > 0) { - let pairs = params.split('&'); - - for (let i = 0; i < pairs.length; i++) { - let pair = pairs[i].split('='); - let requireAnd = i > 0; - - if (pair[0] !== this._startName && pair[0] !== this._endName) { - if (requireAnd) { - resultParams += '&'; - } - resultParams += pairs[i]; - } - } - } - - return resultParams.length === 0 ? baseURL : baseURL + '?' + resultParams; - } -} - -export default ParamSeekHandler; diff --git a/src/flv.js/io/range-seek-handler.js b/src/flv.js/io/range-seek-handler.js deleted file mode 100644 index 407d190..0000000 --- a/src/flv.js/io/range-seek-handler.js +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2016 Bilibili. All Rights Reserved. - * - * @author zheng qian - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -class RangeSeekHandler { - constructor(zeroStart) { - this._zeroStart = zeroStart || false; - } - - getConfig(url, range) { - let headers = {}; - - if (range.from !== 0 || range.to !== -1) { - let param; - if (range.to !== -1) { - param = `bytes=${range.from.toString()}-${range.to.toString()}`; - } else { - param = `bytes=${range.from.toString()}-`; - } - headers['Range'] = param; - } else if (this._zeroStart) { - headers['Range'] = 'bytes=0-'; - } - - return { - url: url, - headers: headers, - }; - } - - removeURLParameters(seekedURL) { - return seekedURL; - } -} - -export default RangeSeekHandler; diff --git a/src/flv.js/io/speed-sampler.js b/src/flv.js/io/speed-sampler.js deleted file mode 100644 index ffedc3d..0000000 --- a/src/flv.js/io/speed-sampler.js +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (C) 2016 Bilibili. All Rights Reserved. - * - * @author zheng qian - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import EventEmitter from 'events'; -import PlayerEvents from '../player/player-events.js'; - -// Utility class to calculate realtime network I/O speed -class SpeedSampler { - constructor() { - // milliseconds - this._firstCheckpoint = 0; - this._lastCheckpoint = 0; - this._intervalBytes = 0; - this._totalBytes = 0; - this._p2pLoadedBytes = 0; - this._CDNLoadedBytes = 0; - this._backupLoaderLoadedBytes = 0; - this._lastSecondBytes = 0; - this._emitter = new EventEmitter(); - - // compatibility detection - if (self.performance && self.performance.now) { - this._now = self.performance.now.bind(self.performance); - } else { - this._now = Date.now; - } - } - - destroy() { - this._emitter.removeAllListeners(); - this._emitter = null; - } - - reset() { - this._emitter.emit(PlayerEvents.HTTP_REQUEST_ENDED, this._totalBytes); - if (this._CDNLoadedBytes + this._p2pLoadedBytes + this._backupLoaderLoadedBytes > 0) - this._emitter.emit( - PlayerEvents.P2P_REQUEST_ENDED, - this._CDNLoadedBytes, - this._p2pLoadedBytes, - this._backupLoaderLoadedBytes, - ); - this._firstCheckpoint = this._lastCheckpoint = 0; - this._totalBytes = this._intervalBytes = 0; - this._lastSecondBytes = 0; - this._p2pLoadedBytes = 0; - this._CDNLoadedBytes = 0; - this._backupLoaderLoadedBytes = 0; - } - - addBytes(bytes, p2pLoadInfo) { - if (this._firstCheckpoint === 0) { - this._firstCheckpoint = this._now(); - this._lastCheckpoint = this._firstCheckpoint; - this._intervalBytes += bytes; - this._totalBytes += bytes; - } else if (this._now() - this._lastCheckpoint < 1000) { - this._intervalBytes += bytes; - this._totalBytes += bytes; - } else { - // duration >= 1000 - this._lastSecondBytes = this._intervalBytes; - this._intervalBytes = bytes; - this._totalBytes += bytes; - this._lastCheckpoint = this._now(); - } - if (p2pLoadInfo) { - this._CDNLoadedBytes += p2pLoadInfo.cdn ? p2pLoadInfo.cdn : 0; - this._p2pLoadedBytes += p2pLoadInfo.p2p ? p2pLoadInfo.p2p : 0; - this._backupLoaderLoadedBytes += p2pLoadInfo.backup ? p2pLoadInfo.backup : 0; - } - } - - get currentKBps() { - this.addBytes(0); - - let durationSeconds = (this._now() - this._lastCheckpoint) / 1000; - if (durationSeconds == 0) durationSeconds = 1; - return this._intervalBytes / durationSeconds / 1024; - } - - get lastSecondKBps() { - this.addBytes(0); - - if (this._lastSecondBytes !== 0) { - return this._lastSecondBytes / 1024; - } else { - // lastSecondBytes === 0 - if (this._now() - this._lastCheckpoint >= 500) { - // if time interval since last checkpoint has exceeded 500ms - // the speed is nearly accurate - return this.currentKBps; - } else { - // We don't know - return 0; - } - } - } - - get averageKBps() { - let durationSeconds = (this._now() - this._firstCheckpoint) / 1000; - return this._totalBytes / durationSeconds / 1024; - } - - on(event, listener) { - this._emitter.addListener(event, listener); - } - - off(event, listener) { - this._emitter.removeListener(event, listener); - } -} - -export default SpeedSampler; diff --git a/src/flv.js/io/websocket-loader.js b/src/flv.js/io/websocket-loader.js deleted file mode 100644 index 185b0cb..0000000 --- a/src/flv.js/io/websocket-loader.js +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright (C) 2016 Bilibili. All Rights Reserved. - * - * @author zheng qian - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import Log from '../utils/logger.js'; -import { BaseLoader, LoaderStatus, LoaderErrors } from './loader.js'; -import { RuntimeException } from '../utils/exception.js'; - -// For FLV over WebSocket live stream -class WebSocketLoader extends BaseLoader { - static isSupported() { - try { - return typeof self.WebSocket !== 'undefined'; - } catch (e) { - return false; - } - } - - constructor() { - super('websocket-loader'); - this.TAG = 'WebSocketLoader'; - - this._needStash = true; - - this._ws = null; - this._requestAbort = false; - this._receivedLength = 0; - } - - destroy() { - if (this._ws) { - this.abort(); - } - super.destroy(); - } - - open(dataSource) { - try { - let ws = (this._ws = new self.WebSocket(dataSource.url)); - ws.binaryType = 'arraybuffer'; - ws.onopen = this._onWebSocketOpen.bind(this); - ws.onclose = this._onWebSocketClose.bind(this); - ws.onmessage = this._onWebSocketMessage.bind(this); - ws.onerror = this._onWebSocketError.bind(this); - - this.status = LoaderStatus.kConnecting; - } catch (e) { - this.status = LoaderStatus.kError; - - let info = { code: e.code, msg: e.message }; - - if (this._onError) { - this._onError(LoaderErrors.EXCEPTION, info); - } else { - throw new RuntimeException(info.msg); - } - } - if (this.onStarted) { - this.onStarted(Date.now()); - } - } - - abort() { - let ws = this._ws; - if (ws && (ws.readyState === 0 || ws.readyState === 1)) { - // CONNECTING || OPEN - this._requestAbort = true; - ws.close(); - } - - this._ws = null; - this.status = LoaderStatus.kComplete; - } - - _onWebSocketOpen(e) { - this.status = LoaderStatus.kBuffering; - } - - _onWebSocketClose(e) { - if (this._requestAbort === true) { - this._requestAbort = false; - return; - } - - this.status = LoaderStatus.kComplete; - - if (this._onComplete) { - this._onComplete(0, this._receivedLength - 1); - } - } - - _onWebSocketMessage(e) { - if (e.data instanceof ArrayBuffer) { - this._dispatchArrayBuffer(e.data); - } else if (e.data instanceof Blob) { - let reader = new FileReader(); - reader.onload = () => { - this._dispatchArrayBuffer(reader.result); - }; - reader.readAsArrayBuffer(e.data); - } else { - this.status = LoaderStatus.kError; - let info = { code: -1, msg: 'Unsupported WebSocket message type: ' + e.data.constructor.name }; - - if (this._onError) { - this._onError(LoaderErrors.EXCEPTION, info); - } else { - throw new RuntimeException(info.msg); - } - } - } - - _dispatchArrayBuffer(arraybuffer) { - let chunk = arraybuffer; - let byteStart = this._receivedLength; - this._receivedLength += chunk.byteLength; - - if (this._onDataArrival) { - this._onDataArrival(chunk, byteStart, this._receivedLength); - } - } - - _onWebSocketError(e) { - this.status = LoaderStatus.kError; - - let info = { - code: e.code, - msg: e.message, - }; - - if (this._onError) { - this._onError(LoaderErrors.EXCEPTION, info); - } else { - throw new RuntimeException(info.msg); - } - } -} - -export default WebSocketLoader; diff --git a/src/flv.js/io/xhr-moz-chunked-loader.js b/src/flv.js/io/xhr-moz-chunked-loader.js deleted file mode 100644 index fb6185e..0000000 --- a/src/flv.js/io/xhr-moz-chunked-loader.js +++ /dev/null @@ -1,207 +0,0 @@ -/* - * Copyright (C) 2016 Bilibili. All Rights Reserved. - * - * @author zheng qian - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import Log from '../utils/logger.js'; -import { BaseLoader, LoaderStatus, LoaderErrors } from './loader.js'; -import { RuntimeException } from '../utils/exception.js'; - -// For FireFox browser which supports `xhr.responseType = 'moz-chunked-arraybuffer'` -class MozChunkedLoader extends BaseLoader { - static isSupported() { - try { - let xhr = new XMLHttpRequest(); - // Firefox 37- requires .open() to be called before setting responseType - xhr.open('GET', 'https://example.com', true); - xhr.responseType = 'moz-chunked-arraybuffer'; - return xhr.responseType === 'moz-chunked-arraybuffer'; - } catch (e) { - Log.w('MozChunkedLoader', e.message); - return false; - } - } - - constructor(seekHandler, config) { - super('xhr-moz-chunked-loader'); - this.TAG = 'MozChunkedLoader'; - - this._seekHandler = seekHandler; - this._config = config; - this._needStash = true; - - this._xhr = null; - this._requestAbort = false; - this._contentLength = null; - this._receivedLength = 0; - } - - destroy() { - if (this.isWorking()) { - this.abort(); - } - if (this._xhr) { - this._xhr.onreadystatechange = null; - this._xhr.onprogress = null; - this._xhr.onloadend = null; - this._xhr.onerror = null; - this._xhr = null; - } - super.destroy(); - } - - open(dataSource, range) { - this._dataSource = dataSource; - this._range = range; - - let sourceURL = dataSource.url; - if (this._config.reuseRedirectedURL && dataSource.redirectedURL != undefined) { - sourceURL = dataSource.redirectedURL; - } - - let seekConfig = this._seekHandler.getConfig(sourceURL, range); - this._requestURL = seekConfig.url; - - let xhr = (this._xhr = new XMLHttpRequest()); - xhr.open('GET', seekConfig.url, true); - xhr.responseType = 'moz-chunked-arraybuffer'; - xhr.onreadystatechange = this._onReadyStateChange.bind(this); - xhr.onprogress = this._onProgress.bind(this); - xhr.onloadend = this._onLoadEnd.bind(this); - xhr.onerror = this._onXhrError.bind(this); - - // cors is auto detected and enabled by xhr - - // withCredentials is disabled by default - if (dataSource.withCredentials && xhr['withCredentials']) { - xhr.withCredentials = true; - } - - if (typeof seekConfig.headers === 'object') { - let headers = seekConfig.headers; - - for (let key in headers) { - if (headers.hasOwnProperty(key)) { - xhr.setRequestHeader(key, headers[key]); - } - } - } - - this.status = LoaderStatus.kConnecting; - xhr.send(); - if (this.onStarted) { - this.onStarted(Date.now()); - } - } - - abort() { - this._requestAbort = true; - if (this._xhr) { - this._xhr.abort(); - } - this.status = LoaderStatus.kComplete; - } - - _onReadyStateChange(e) { - let xhr = e.target; - - if (xhr.readyState === 2) { - // HEADERS_RECEIVED - if (this.onHeaderReceived) { - this.onHeaderReceived(xhr.getAllResponseHeaders()); - } - if (xhr.responseURL != undefined && xhr.responseURL !== this._requestURL) { - if (this._onURLRedirect) { - let redirectedURL = this._seekHandler.removeURLParameters(xhr.responseURL); - this._onURLRedirect(redirectedURL); - } - } - - if (xhr.status !== 0 && (xhr.status < 200 || xhr.status > 299)) { - this.status = LoaderStatus.kError; - if (this._onError) { - this._onError(LoaderErrors.HTTP_STATUS_CODE_INVALID, { code: xhr.status, msg: xhr.statusText }); - } else { - throw new RuntimeException( - 'MozChunkedLoader: Http code invalid, ' + xhr.status + ' ' + xhr.statusText, - ); - } - } else { - this.status = LoaderStatus.kBuffering; - } - } - } - - _onProgress(e) { - if (this.status === LoaderStatus.kError) { - // Ignore error response - return; - } - - if (this._contentLength === null) { - if (e.total !== null && e.total !== 0) { - this._contentLength = e.total; - if (this._onContentLengthKnown) { - this._onContentLengthKnown(this._contentLength); - } - } - } - - let chunk = e.target.response; - let byteStart = this._range.from + this._receivedLength; - this._receivedLength += chunk.byteLength; - - if (this._onDataArrival) { - this._onDataArrival(chunk, byteStart, this._receivedLength); - } - } - - _onLoadEnd(e) { - if (this._requestAbort === true) { - this._requestAbort = false; - return; - } else if (this.status === LoaderStatus.kError) { - return; - } - - this.status = LoaderStatus.kComplete; - if (this._onComplete) { - this._onComplete(this._range.from, this._range.from + this._receivedLength - 1); - } - } - - _onXhrError(e) { - this.status = LoaderStatus.kError; - let type = 0; - let info = null; - - if (this._contentLength && e.loaded < this._contentLength) { - type = LoaderErrors.EARLY_EOF; - info = { code: -1, msg: 'Moz-Chunked stream meet Early-Eof' }; - } else { - type = LoaderErrors.EXCEPTION; - info = { code: -1, msg: e.constructor.name + ' ' + e.type }; - } - - if (this._onError) { - this._onError(type, info); - } else { - throw new RuntimeException(info.msg); - } - } -} - -export default MozChunkedLoader; diff --git a/src/flv.js/io/xhr-range-loader.js b/src/flv.js/io/xhr-range-loader.js deleted file mode 100644 index 6c0dd7d..0000000 --- a/src/flv.js/io/xhr-range-loader.js +++ /dev/null @@ -1,360 +0,0 @@ -/* - * Copyright (C) 2016 Bilibili. All Rights Reserved. - * - * @author zheng qian - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import Log from '../utils/logger.js'; -import SpeedSampler from './speed-sampler.js'; -import { BaseLoader, LoaderStatus, LoaderErrors } from './loader.js'; -import { RuntimeException } from '../utils/exception.js'; - -// Universal IO Loader, implemented by adding Range header in xhr's request header -class RangeLoader extends BaseLoader { - static isSupported() { - try { - let xhr = new XMLHttpRequest(); - xhr.open('GET', 'https://example.com', true); - xhr.responseType = 'arraybuffer'; - return xhr.responseType === 'arraybuffer'; - } catch (e) { - Log.w('RangeLoader', e.message); - return false; - } - } - - constructor(seekHandler, config) { - super('xhr-range-loader'); - this.TAG = 'RangeLoader'; - - this._seekHandler = seekHandler; - this._config = config; - this._needStash = false; - - this._chunkSizeKBList = [128, 256, 384, 512, 768, 1024, 1536, 2048, 3072, 4096, 5120, 6144, 7168, 8192]; - this._currentChunkSizeKB = 384; - this._currentSpeedNormalized = 0; - this._zeroSpeedChunkCount = 0; - - this._xhr = null; - this._speedSampler = new SpeedSampler(); - - this._requestAbort = false; - this._waitForTotalLength = false; - this._totalLengthReceived = false; - - this._currentRequestURL = null; - this._currentRedirectedURL = null; - this._currentRequestRange = null; - this._totalLength = null; // size of the entire file - this._contentLength = null; // Content-Length of entire request range - this._receivedLength = 0; // total received bytes - this._lastTimeLoaded = 0; // received bytes of current request sub-range - } - - destroy() { - if (this.isWorking()) { - this.abort(); - } - if (this._xhr) { - this._xhr.onreadystatechange = null; - this._xhr.onprogress = null; - this._xhr.onload = null; - this._xhr.onerror = null; - this._xhr = null; - } - super.destroy(); - this._speedSampler.destroy(); - } - - get currentSpeed() { - return this._speedSampler.lastSecondKBps; - } - - open(dataSource, range) { - this._dataSource = dataSource; - this._range = range; - this.status = LoaderStatus.kConnecting; - - let useRefTotalLength = false; - if (this._dataSource.filesize != undefined && this._dataSource.filesize !== 0) { - useRefTotalLength = true; - this._totalLength = this._dataSource.filesize; - } - - if (!this._totalLengthReceived && !useRefTotalLength) { - // We need total filesize - this._waitForTotalLength = true; - this._internalOpen(this._dataSource, { from: 0, to: -1 }); - } else { - // We have filesize, start loading - this._openSubRange(); - } - if (this.onStarted) { - this.onStarted(Date.now()); - } - } - - _openSubRange() { - let chunkSize = this._currentChunkSizeKB * 1024; - - let from = this._range.from + this._receivedLength; - let to = from + chunkSize; - - if (this._contentLength != null) { - if (to - this._range.from >= this._contentLength) { - to = this._range.from + this._contentLength - 1; - } - } - - this._currentRequestRange = { from, to }; - this._internalOpen(this._dataSource, this._currentRequestRange); - } - - _internalOpen(dataSource, range) { - this._lastTimeLoaded = 0; - - let sourceURL = dataSource.url; - if (this._config.reuseRedirectedURL) { - if (this._currentRedirectedURL != undefined) { - sourceURL = this._currentRedirectedURL; - } else if (dataSource.redirectedURL != undefined) { - sourceURL = dataSource.redirectedURL; - } - } - - let seekConfig = this._seekHandler.getConfig(sourceURL, range); - this._currentRequestURL = seekConfig.url; - - let xhr = (this._xhr = new XMLHttpRequest()); - xhr.open('GET', seekConfig.url, true); - xhr.responseType = 'arraybuffer'; - xhr.onreadystatechange = this._onReadyStateChange.bind(this); - xhr.onprogress = this._onProgress.bind(this); - xhr.onload = this._onLoad.bind(this); - xhr.onerror = this._onXhrError.bind(this); - - if (dataSource.withCredentials && xhr['withCredentials']) { - xhr.withCredentials = true; - } - - if (typeof seekConfig.headers === 'object') { - let headers = seekConfig.headers; - - for (let key in headers) { - if (headers.hasOwnProperty(key)) { - xhr.setRequestHeader(key, headers[key]); - } - } - } - - xhr.send(); - } - - abort() { - this._requestAbort = true; - this._internalAbort(); - this.status = LoaderStatus.kComplete; - } - - _internalAbort() { - if (this._xhr) { - this._xhr.onreadystatechange = null; - this._xhr.onprogress = null; - this._xhr.onload = null; - this._xhr.onerror = null; - this._xhr.abort(); - this._xhr = null; - } - } - - _onReadyStateChange(e) { - let xhr = e.target; - - if (xhr.readyState === 2) { - // HEADERS_RECEIVED - if (this.onHeaderReceived) { - this.onHeaderReceived(xhr.getAllResponseHeaders()); - } - if (xhr.responseURL != undefined) { - // if the browser support this property - let redirectedURL = this._seekHandler.removeURLParameters(xhr.responseURL); - if (xhr.responseURL !== this._currentRequestURL && redirectedURL !== this._currentRedirectedURL) { - this._currentRedirectedURL = redirectedURL; - if (this._onURLRedirect) { - this._onURLRedirect(redirectedURL); - } - } - } - - if (xhr.status >= 200 && xhr.status <= 299) { - if (this._waitForTotalLength) { - return; - } - this.status = LoaderStatus.kBuffering; - } else { - this.status = LoaderStatus.kError; - if (this._onError) { - this._onError(LoaderErrors.HTTP_STATUS_CODE_INVALID, { code: xhr.status, msg: xhr.statusText }); - } else { - throw new RuntimeException('RangeLoader: Http code invalid, ' + xhr.status + ' ' + xhr.statusText); - } - } - } - } - - _onProgress(e) { - if (this.status === LoaderStatus.kError) { - // Ignore error response - return; - } - - if (this._contentLength === null) { - let openNextRange = false; - - if (this._waitForTotalLength) { - this._waitForTotalLength = false; - this._totalLengthReceived = true; - openNextRange = true; - - let total = e.total; - this._internalAbort(); - if ((total != null) & (total !== 0)) { - this._totalLength = total; - } - } - - // calculate currrent request range's contentLength - if (this._range.to === -1) { - this._contentLength = this._totalLength - this._range.from; - } else { - // to !== -1 - this._contentLength = this._range.to - this._range.from + 1; - } - - if (openNextRange) { - this._openSubRange(); - return; - } - if (this._onContentLengthKnown) { - this._onContentLengthKnown(this._contentLength); - } - } - - let delta = e.loaded - this._lastTimeLoaded; - this._lastTimeLoaded = e.loaded; - this._speedSampler.addBytes(delta); - } - - _normalizeSpeed(input) { - let list = this._chunkSizeKBList; - let last = list.length - 1; - let mid = 0; - let lbound = 0; - let ubound = last; - - if (input < list[0]) { - return list[0]; - } - - while (lbound <= ubound) { - mid = lbound + Math.floor((ubound - lbound) / 2); - if (mid === last || (input >= list[mid] && input < list[mid + 1])) { - return list[mid]; - } else if (list[mid] < input) { - lbound = mid + 1; - } else { - ubound = mid - 1; - } - } - } - - _onLoad(e) { - if (this.status === LoaderStatus.kError) { - // Ignore error response - return; - } - - if (this._waitForTotalLength) { - this._waitForTotalLength = false; - return; - } - - this._lastTimeLoaded = 0; - let KBps = this._speedSampler.lastSecondKBps; - if (KBps === 0) { - this._zeroSpeedChunkCount++; - if (this._zeroSpeedChunkCount >= 3) { - // Try get currentKBps after 3 chunks - KBps = this._speedSampler.currentKBps; - } - } - - if (KBps !== 0) { - let normalized = this._normalizeSpeed(KBps); - if (this._currentSpeedNormalized !== normalized) { - this._currentSpeedNormalized = normalized; - this._currentChunkSizeKB = normalized; - } - } - - let chunk = e.target.response; - let byteStart = this._range.from + this._receivedLength; - this._receivedLength += chunk.byteLength; - - let reportComplete = false; - - if (this._contentLength != null && this._receivedLength < this._contentLength) { - // continue load next chunk - this._openSubRange(); - } else { - reportComplete = true; - } - - // dispatch received chunk - if (this._onDataArrival) { - this._onDataArrival(chunk, byteStart, this._receivedLength); - } - - if (reportComplete) { - this.status = LoaderStatus.kComplete; - if (this._onComplete) { - this._onComplete(this._range.from, this._range.from + this._receivedLength - 1); - } - } - } - - _onXhrError(e) { - this.status = LoaderStatus.kError; - let type = 0; - let info = null; - - if (this._contentLength && this._receivedLength > 0 && this._receivedLength < this._contentLength) { - type = LoaderErrors.EARLY_EOF; - info = { code: -1, msg: 'RangeLoader meet Early-Eof' }; - } else { - type = LoaderErrors.EXCEPTION; - info = { code: -1, msg: e.constructor.name + ' ' + e.type }; - } - - if (this._onError) { - this._onError(type, info); - } else { - throw new RuntimeException(info.msg); - } - } -} - -export default RangeLoader; diff --git a/src/flv.js/io/xyvod-loader.js b/src/flv.js/io/xyvod-loader.js deleted file mode 100644 index e89c732..0000000 --- a/src/flv.js/io/xyvod-loader.js +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright (C) 2017 Shenzhen Onething Technologies Co., Ltd. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import Log from '../utils/logger.js'; -import { BaseLoader, LoaderStatus, LoaderErrors } from './loader.js'; -import { RuntimeException } from '../utils/exception.js'; - -class XYVODLoader extends BaseLoader { - static isSupported() { - try { - // fetch + stream is broken on Microsoft Edge. Disable before build 15048. - // see https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/8196907/ - // Fixed in Jan 10, 2017. Build 15048+ removed from blacklist. - let isWorkWellEdge = Browser.msedge && Browser.version.minor >= 15048; - let browserNotBlacklisted = Browser.msedge ? isWorkWellEdge : true; - return self.fetch && self.ReadableStream && browserNotBlacklisted; - } catch (e) { - return false; - } - } - - constructor(seekHandler, config, mediaElement) { - super('xyvod-loader'); - - //console.log('---player constructor'); - this.TAG = 'XLLoader'; - this._seekHandler = seekHandler; - this._config = config; - this._needStash = true; - this._mediaElement = mediaElement; - this._dataSource = null; - - this._receivedLength = 0; - - this._xyvod = null; - this._loadInfo = { cdnBytes: 0, p2pBytes: 0 }; - this._backupLoader = null; - } - - setBackupLoader(backupLoader) { - this._backupLoader = backupLoader; - this._backupLoader.onComplete = this._onBackupLoaderComplete.bind(this); - this._backupLoader.onDataArrival = this._onBackupLoaderLoad.bind(this); - } - - destroy() { - this.abort(); - // console.log('---player destroy'); - this._mediaElement = null; - this._destroyP2pIO(); - this._dataSource = null; - if (this._backupLoader) { - this._backupLoader.destroy(); - this._backupLoader = null; - } - this._loadInfo = { cdnBytes: 0, p2pBytes: 0 }; - super.destroy(); - } - - open(dataSource, range) { - // console.log('---player open'); - this._range = range; - this._receivedLength = 0; - this.status = LoaderStatus.kConnecting; - this._dataSource = dataSource; - - if (window.xyflv) { - let { XYVOD, XYVODEvent } = window.xyflv; - if (this._xyvod === null) { - // console.log('---before new XYVOD'); - this._xyvod = new XYVOD({ - url: dataSource.url, - media: this._mediaElement, - }); - this._xyvod.on(XYVODEvent.DATA, ({ buffer, offset }) => { - this._receivedLength += buffer.byteLength; - let loadinfo = this._xyvod.info; - if (this._onDataArrival) { - // console.log('---arrival, offset:', offset); - this._onDataArrival(buffer, offset, this._receivedLength, { - cdn: loadinfo.cdnBytes - this._loadInfo.cdnBytes, - p2p: loadinfo.p2pBytes - this._loadInfo.p2pBytes, - }); - this._loadInfo = loadinfo; - } - }); - this._xyvod.on(XYVODEvent.END, () => { - this.status = LoaderStatus.kComplete; - if (this._onComplete) { - this._onComplete(this._range.from, this._range.from + this._receivedLength - 1); - } - }); - this._xyvod.on(XYVODEvent.ERROR, (error) => { - this._onLoaderError(); - }); - } - try { - this._xyvod.open(this._range.from); - } catch (err) { - Log.e(this.TAG, `xyvod-loader error:${err}`); - this._backupLoader.open(dataSource, range); - } - this.status = LoaderStatus.kBuffering; - } else { - this._backupLoader.open(dataSource, range); - } - if (this.onStarted) { - this.onStarted(Date.now()); - } - } - - abort() { - if (this.isWorking()) { - if (this._xyvod) this._xyvod.close(); - this._backupLoader.abort(); - this.status = LoaderStatus.kComplete; - } - } - - _onBackupLoaderLoad(chunk, byteStart, receivedLength) { - this._receivedLength += chunk.byteLength; - if (this._onDataArrival) { - this._onDataArrival(chunk, byteStart, this._receivedLength, { backup: chunk.byteLength }); - } - } - - _onBackupLoaderComplete(from, to) { - if (this._status === LoaderStatus.kError) { - // Ignore error response - return; - } - if (this._range && this._status != LoaderStatus.kComplete) { - this.status = LoaderStatus.kComplete; - if (this._onComplete) { - this._onComplete(this._range.from, this._range.from + this._receivedLength - 1); - } - } - } - - _onLoaderError(e) { - Log.w(this.TAG, 'XYIOError: code-' + String(e)); - this._switchIO(); - } - - _switchIO() { - this._destroyP2pIO(); - if (this.onNeedStashBuffer) { - this.onNeedStashBuffer(this._backupLoader.needStashBuffer); - } - let backupRange = Object.assign({}, this._range); - backupRange.from = this._range.from + this._receivedLength; - this._backupLoader.open(this._dataSource, backupRange); - } - - _destroyP2pIO() { - if (this._xyvod) { - this._xyvod.dispose(); - this._xyvod = null; - } - } -} - -export default XYVODLoader; diff --git a/src/flv.js/io/yf-loader.js b/src/flv.js/io/yf-loader.js deleted file mode 100644 index d530dce..0000000 --- a/src/flv.js/io/yf-loader.js +++ /dev/null @@ -1,261 +0,0 @@ -/* - * Copyright (C) 2016 Bilibili. All Rights Reserved. - * - * @author zheng qian - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import Log from '../utils/logger.js'; -import { BaseLoader, LoaderStatus, LoaderErrors } from './loader.js'; -import { RuntimeException } from '../utils/exception.js'; -// Quick Fix -// Be Careful: We don't provide any node.js modules -// You MUST install these modules manually -import { Buffer } from 'buffer'; -//import YFCloudIO from '@bilibili-player/component.yfcloudio'; - -// For FLV over WebSocket live stream -class YFLoader extends BaseLoader { - static isSupported() { - return true; - } - - constructor(seekHandler, config, mediaElement, bNewSlice) { - super('yf-loader'); - this.TAG = 'YFLoader'; - this._requestAbort = false; - this._receivedLength = 0; - this._needStash = false; - this._p2pIO = null; - this._dataSource = null; - this._mediaElement = mediaElement; - this._bNewSlice = bNewSlice ? bNewSlice : false; - this._loadInfo = { cdnloaded: 0, scdnloaded: 0 }; - this._stashSize = 0; - this._backupLoader = null; - this._noResource = false; - } - - setBackupLoader(backupLoader) { - this._backupLoader = backupLoader; - this._backupLoader.onComplete = this._onBackupLoaderComplete.bind(this); - this._backupLoader.onDataArrival = this._onBackupLoaderLoad.bind(this); - } - - destroy() { - this.abort(); - this._destroyP2pIO(); - this._backupLoader.destroy(); - this._backupLoader = null; - this._dataSource = null; - this._mediaElement = null; - this._loadInfo = { cdnloaded: 0, scdnloaded: 0 }; - this._stashSize = 0; - this._noResource = false; - this._bNewSlice = false; - super.destroy(); - } - - open(dataSource, range) { - this._dataSource = dataSource; - try { - this._range = range; - // seek - if (this._p2pIO) { - this._destroyP2pIO(); - } - if (window.YFCloudIO && !this._noResource) { - this._createP2pIO(); - //this._p2pConfig.domain = dataSource.url.match(/\/?([a-zA-Z0-9.-]+)\//)[1]; - this._p2pConfig.domain = 'upos-hz-mirrorks3u.acgvideo.com'; - this._p2pIO.init(this._mediaElement, dataSource.url, this._p2pConfig); - this._p2pIO.load(range, this._bNewSlice, this._stashSize); - } else { - if (this.onNeedStashBuffer) { - this.onNeedStashBuffer(this._backupLoader.needStashBuffer); - } - this._backupLoader.open(this._dataSource, range); - } - this.status = LoaderStatus.kConnecting; - if (this.onStarted) { - this.onStarted(Date.now()); - } - } catch (e) { - this.status = LoaderStatus.kError; - let info = { code: e.code, msg: e.message }; - if (this._onError) { - this._onError(LoaderErrors.EXCEPTION, info); - } else { - throw new RuntimeException(info.msg); - } - } - } - - abort() { - let p2pIO = this._p2pIO; - if (p2pIO && this._requestAbort == false) { - // CONNECTING - p2pIO.pause(); - } - this._backupLoader.abort(); - this._requestAbort = true; - // this._loadInfo = { cdnloaded: 0, scdnloaded: 0 }; - this.status = LoaderStatus.kComplete; - } - - resume(range) { - if (this._p2pIO) { - this._requestAbort = false; - this._p2pIO.seek(range); - this.status = LoaderStatus.kConnecting; - if (this.onStarted) { - this.onStarted(Date.now()); - } - } - } - - setStashSize(stashSize) { - if (this._stashSize === stashSize) { - return; - } - this._stashSize = stashSize; - if (this._p2pIO) { - this._p2pIO.setThreshold(this._stashSize); - } - } - - get noResource() { - return this._noResource; - } - - set noResource(noResource) { - this._noResource = noResource; - } - - useP2pIO() { - return this._p2pIO != null; - } - - _switchIO() { - this._destroyP2pIO(); - if (this.onNeedStashBuffer) { - this.onNeedStashBuffer(this._backupLoader.needStashBuffer); - } - let backupRange = Object.assign({}, this._range); - backupRange.from = this._range.from + this._receivedLength; - this._backupLoader.open(this._dataSource, backupRange); - } - - _createP2pIO() { - // Log.w(this.TAG, 'create p2pio'); - let YFCloudIO = window.YFCloudIO; - this._p2pIO = new YFCloudIO(); - this._p2pIO.onLoadError = this._onLoaderError.bind(this); - this._p2pIO.onDataArrive = this._onLoad.bind(this); - this._p2pIO.onIOComplete = this._onLoaderComplete.bind(this); - this._p2pIO.noResources = this._onNoResources.bind(this); - if (this.onURLRedirect) { - this._p2pIO.onURLRedirect = this.onURLRedirect.bind(this); - } - this._p2pConfig = { - limitBufferLength: 150, - taccesskey: 'b0c2a0fef8cf47608ec06008e089b87f3d4a965b', - tokenid: 'b048975020d226f7bb64eff579a5751f6738367c', - }; - if (this.onNeedStashBuffer) { - this.onNeedStashBuffer(false); - } - } - - _destroyP2pIO() { - if (this._p2pIO) { - // Log.w(this.TAG, 'destroy p2pio'); - this._p2pIO.pause(); - this._p2pIO.destroy(); - this._p2pIO = null; - this._p2pConfig = null; - } - } - - _onLoad(chunk, byteStart) { - if (this._status === LoaderStatus.kError) { - // Ignore error response - return; - } - if (this._p2pIO == null) { - return; - } - let data = Buffer.from(chunk).buffer; - this._receivedLength += data.byteLength; - let loadinfo = this._p2pIO.loadedInfo(); - //Log.v(this.TAG, 'Load Info:' + JSON.stringify(loadinfo)); - if (this._onDataArrival) { - this._onDataArrival(data, byteStart, this._receivedLength, { - cdn: loadinfo.cdnloaded - this._loadInfo.cdnloaded, - p2p: loadinfo.scdnloaded - this._loadInfo.scdnloaded, - backup: 0, - }); - this._loadInfo = loadinfo; - } - if (this._noResource && !this._bNewSlice) { - this._switchIO(); - } else if (this._bNewSlice === true) { - this._bNewSlice = false; - } - } - - _onBackupLoaderLoad(chunk, byteStart, receivedLength) { - this._receivedLength += chunk.byteLength; - if (this._onDataArrival) { - this._onDataArrival(chunk, byteStart, this._receivedLength, { cdn: 0, p2p: 0, backup: chunk.byteLength }); - } - } - - _onLoaderComplete() { - if (this._status === LoaderStatus.kError) { - // Ignore error response - return; - } - if (this._range && this._status != LoaderStatus.kComplete) { - this.status = LoaderStatus.kComplete; - if (this._onComplete) { - this._onComplete(this._range.from, this._range.from + this._receivedLength - 1); - } - } - } - - _onBackupLoaderComplete(from, to) { - if (this._status === LoaderStatus.kError) { - // Ignore error response - return; - } - if (this._range && this._status != LoaderStatus.kComplete) { - this.status = LoaderStatus.kComplete; - if (this._onComplete) { - this._onComplete(this._range.from, this._range.from + this._receivedLength - 1); - } - } - } - - _onLoaderError(e) { - Log.w(this.TAG, 'YFIOError: code-' + String(e)); - this._switchIO(); - } - - _onNoResources() { - this._noResource = true; - } -} - -export default YFLoader; diff --git a/src/flv.js/player/flv-player.js b/src/flv.js/player/flv-player.js deleted file mode 100644 index 110f86f..0000000 --- a/src/flv.js/player/flv-player.js +++ /dev/null @@ -1,688 +0,0 @@ -/* - * Copyright (C) 2016 Bilibili. All Rights Reserved. - * - * @author zheng qian - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import EventEmitter from 'events'; -import Log from '../utils/logger.js'; -import Browser from '../utils/browser.js'; -import PlayerEvents, { OTHER_EVENTS_POLYMER } from './player-events.js'; -import Transmuxer from '../core/transmuxer.js'; -import TransmuxingEvents from '../core/transmuxing-events.js'; -import MSEController from '../core/mse-controller.js'; -import MSEEvents from '../core/mse-events.js'; -import { ErrorTypes, ErrorDetails } from './player-errors.js'; -import { createDefaultConfig } from '../config.js'; -import { InvalidArgumentException, IllegalStateException } from '../utils/exception.js'; - -class FlvPlayer { - constructor(mediaDataSource, config) { - this.TAG = 'FlvPlayer'; - this._type = 'FlvPlayer'; - this._emitter = new EventEmitter(); - this._createdTime = Date.now(); - - this._config = createDefaultConfig(); - if (typeof config === 'object') { - Object.assign(this._config, config); - } - - if (mediaDataSource.type.toLowerCase() !== 'flv') { - throw new InvalidArgumentException('FlvPlayer requires an flv MediaDataSource input!'); - } - - if (mediaDataSource.isLive === true) { - this._config.isLive = true; - } - - this.e = { - onvLoadedMetadata: this._onvLoadedMetadata.bind(this), - onvSeeking: this._onvSeeking.bind(this), - onvCanPlay: this._onvCanPlay.bind(this), - onvStalled: this._onvStalled.bind(this), - onvProgress: this._onvProgress.bind(this), - }; - - if (self.performance && self.performance.now) { - this._now = self.performance.now.bind(self.performance); - } else { - this._now = Date.now; - } - - this._pendingSeekTime = null; // in seconds - this._requestSetTime = false; - this._seekpointRecord = null; - this._progressChecker = null; - - this._mediaDataSource = mediaDataSource; - this._mediaElement = null; - this._msectl = null; - this._transmuxer = null; - - this._mseSourceOpened = false; - this._hasPendingLoad = false; - this._receivedCanPlay = false; - - this._mediaInfo = null; - this._statisticsInfo = null; - let chromeNeedIDRFix = - Browser.chrome && - (Browser.version.major < 50 || (Browser.version.major === 50 && Browser.version.build < 2661)); - this._alwaysSeekKeyframe = chromeNeedIDRFix || Browser.msedge || Browser.msie ? true : false; - - if (this._alwaysSeekKeyframe) { - this._config.accurateSeek = false; - } - delete window['__FLV_P2P_TYPE__']; - } - - destroy() { - if (this._progressChecker != null) { - window.clearInterval(this._progressChecker); - this._progressChecker = null; - } - if (this._transmuxer) { - this.unload(); - } - if (this._mediaElement) { - this.detachMediaElement(); - } - this.e = null; - this._mediaDataSource = null; - - this._emitter.removeAllListeners(); - this._emitter = null; - } - - on(event, listener) { - if (event === PlayerEvents.MEDIA_INFO) { - if (this._mediaInfo != null) { - Promise.resolve().then(() => { - this._emitter.emit(PlayerEvents.MEDIA_INFO, this.mediaInfo); - }); - } - } else if (event === PlayerEvents.STATISTICS_INFO) { - if (this._statisticsInfo != null) { - Promise.resolve().then(() => { - this._emitter.emit(PlayerEvents.STATISTICS_INFO, this.statisticsInfo); - }); - } - } - this._emitter.addListener(event, listener); - } - - one(event, listener) { - this._emitter.once(event, listener); - } - - off(event, listener) { - this._emitter.removeListener(event, listener); - } - - attachMediaElement(mediaElement) { - this._mediaElement = mediaElement; - mediaElement.addEventListener('loadedmetadata', this.e.onvLoadedMetadata); - mediaElement.addEventListener('seeking', this.e.onvSeeking); - mediaElement.addEventListener('canplay', this.e.onvCanPlay); - mediaElement.addEventListener('stalled', this.e.onvStalled); - mediaElement.addEventListener('progress', this.e.onvProgress); - - this._msectl = new MSEController(this._config); - - this._msectl.on(MSEEvents.UPDATE_END, this._onmseUpdateEnd.bind(this)); - this._msectl.on(MSEEvents.BUFFER_FULL, this._onmseBufferFull.bind(this)); - this._msectl.on(MSEEvents.SOURCE_OPEN, () => { - this._mseSourceOpened = true; - if (this._hasPendingLoad) { - this._hasPendingLoad = false; - this.load(); - } - }); - this._msectl.on(MSEEvents.ERROR, (info) => { - this._emitter.emit(PlayerEvents.ERROR, ErrorTypes.MEDIA_ERROR, ErrorDetails.MEDIA_MSE_ERROR, info); - }); - - this._msectl.attachMediaElement(mediaElement); - - if (this._pendingSeekTime != null) { - try { - mediaElement.currentTime = this._pendingSeekTime; - this._pendingSeekTime = null; - } catch (e) { - // IE11 may throw InvalidStateError if readyState === 0 - // We can defer set currentTime operation after loadedmetadata - } - } - } - - detachMediaElement() { - if (this._mediaElement) { - this._msectl.detachMediaElement(); - this._mediaElement.removeEventListener('loadedmetadata', this.e.onvLoadedMetadata); - this._mediaElement.removeEventListener('seeking', this.e.onvSeeking); - this._mediaElement.removeEventListener('canplay', this.e.onvCanPlay); - this._mediaElement.removeEventListener('stalled', this.e.onvStalled); - this._mediaElement.removeEventListener('progress', this.e.onvProgress); - this._mediaElement = null; - } - if (this._msectl) { - this._msectl.destroy(); - this._msectl = null; - } - } - - load() { - if (!this._mediaElement) { - throw new IllegalStateException('HTMLMediaElement must be attached before load()!'); - } - if (this._transmuxer) { - throw new IllegalStateException('FlvPlayer.load() has been called, please call unload() first!'); - } - if (this._hasPendingLoad) { - return; - } - - if (this._config.deferLoadAfterSourceOpen && this._mseSourceOpened === false) { - this._hasPendingLoad = true; - return; - } - - if (this._mediaElement.readyState > 0) { - this._requestSetTime = true; - // IE11 may throw InvalidStateError if readyState === 0 - this._mediaElement.currentTime = 0; - } - - this._transmuxer = new Transmuxer(this._mediaDataSource, this._config, this._mediaElement); - - this._transmuxer.on(TransmuxingEvents.INIT_SEGMENT, (type, is) => { - this._msectl.appendInitSegment(is); - }); - this._transmuxer.on(TransmuxingEvents.MEDIA_SEGMENT, (type, ms) => { - this._msectl.appendMediaSegment(ms); - // lazyLoad check - if (this._config.lazyLoad && !this._config.isLive) { - let currentTime = this._mediaElement.currentTime; - if (ms.info.endDts >= (currentTime + this._config.lazyLoadMaxDuration) * 1000) { - if (this._progressChecker == null) { - Log.v(this.TAG, 'Maximum buffering duration exceeded, suspend transmuxing task'); - this._suspendTransmuxer(); - } - } - } - }); - this._transmuxer.on(TransmuxingEvents.LOADING_COMPLETE, (from, to, requestUrl) => { - this._msectl.endOfStream(); - this._emitter.emit(PlayerEvents.LOADING_COMPLETE, { - requestUrl: requestUrl, - metadata: this._mediaInfo ? this._mediaInfo.metadata : null, - from: from, - to: to, - }); - try { - const metadata = this._mediaInfo.metadata; - const filesize = metadata.filesize; - if (filesize && to && filesize !== to + 1) { - this._emitter.emit( - PlayerEvents.ERROR, - ErrorTypes.OTHER_ERROR, - ErrorDetails.ABNORMAL_SEGMENT_BYTELENGTH, - { code: -1, msg: 'Abnormal segment bytelength' }, - ); - } - } catch (e) { - // do nothing - } - }); - this._transmuxer.on(TransmuxingEvents.RECOVERED_EARLY_EOF, () => { - this._emitter.emit(PlayerEvents.RECOVERED_EARLY_EOF); - }); - this._transmuxer.on(TransmuxingEvents.IO_ERROR, (detail, info) => { - this._emitter.emit(PlayerEvents.ERROR, ErrorTypes.NETWORK_ERROR, detail, info); - }); - this._transmuxer.on(TransmuxingEvents.DEMUX_ERROR, (detail, info) => { - this._emitter.emit(PlayerEvents.ERROR, ErrorTypes.MEDIA_ERROR, detail, { code: -1, msg: info }); - }); - this._transmuxer.on(TransmuxingEvents.MEDIA_INFO, (mediaInfo) => { - this._mediaInfo = mediaInfo; - this._emitter.emit(PlayerEvents.MEDIA_INFO, Object.assign({}, mediaInfo)); - }); - this._transmuxer.on(TransmuxingEvents.STATISTICS_INFO, (statInfo) => { - this._statisticsInfo = this._fillStatisticsInfo(statInfo); - this._emitter.emit(PlayerEvents.STATISTICS_INFO, Object.assign({}, this._statisticsInfo)); - }); - this._transmuxer.on(TransmuxingEvents.RECOMMEND_SEEKPOINT, (milliseconds) => { - if (this._mediaElement && !this._config.accurateSeek) { - this._requestSetTime = true; - this._mediaElement.currentTime = milliseconds / 1000; - } - }); - this._transmuxer.on(OTHER_EVENTS_POLYMER, (obj) => { - this._emitter.emit(obj.eventName, ...obj.eventParams); - }); - - this._transmuxer.open(); - } - - unload() { - if (this._mediaElement) { - this._mediaElement.pause(); - } - if (this._msectl) { - this._msectl.seek(0); - } - if (this._transmuxer) { - this._transmuxer.close(); - this._transmuxer.destroy(); - this._transmuxer = null; - } - } - - play() { - return this._mediaElement.play(); - } - - pause() { - this._mediaElement.pause(); - } - - get type() { - return this._type; - } - - get buffered() { - return this._mediaElement.buffered; - } - - get duration() { - return this._mediaElement.duration; - } - - get volume() { - return this._mediaElement.volume; - } - - set volume(value) { - this._mediaElement.volume = value; - } - - get muted() { - return this._mediaElement.muted; - } - - set muted(muted) { - this._mediaElement.muted = muted; - } - - get currentTime() { - if (this._mediaElement) { - return this._mediaElement.currentTime; - } - return 0; - } - - set currentTime(seconds) { - if (this._mediaElement) { - this._internalSeek(seconds); - } else { - this._pendingSeekTime = seconds; - } - } - - get mediaInfo() { - return Object.assign({}, this._mediaInfo); - } - - get statisticsInfo() { - if (this._statisticsInfo == null) { - this._statisticsInfo = {}; - } - this._statisticsInfo = this._fillStatisticsInfo(this._statisticsInfo); - return Object.assign({}, this._statisticsInfo); - } - - get createdTime() { - return this._createdTime; - } - - setP2pType(type, url) { - let loaded = false; - switch (type) { - case 'yf-eg': - if (window.YFCloudIO) { - loaded = true; - } - this._config.p2pSdkSrc = url; - break; - case 'xl-eg': - if (window.xyflv) { - loaded = true; - } - this._config.p2pSdkSrc = url; - break; - default: - this._config.p2pSdkSrc = undefined; - break; - } - if (!loaded) { - if (this._config.p2pSdkSrc) { - this._loadP2pSdkScripts() - .then(() => { - Log.v(this.TAG, 'p2p Sdk Scripts Loaded'); - window['__FLV_P2P_TYPE__'] = type; - this._config.p2pType = type; - }) - .catch(() => { - Log.w(this.TAG, 'load p2p sdk scripts failed'); - }); - } - } else { - window['__FLV_P2P_TYPE__'] = type; - this._config.p2pType = type; - } - } - - _fillStatisticsInfo(statInfo) { - statInfo.playerType = this._type; - - if (!(this._mediaElement instanceof HTMLVideoElement)) { - return statInfo; - } - - let hasQualityInfo = true; - let decoded = 0; - let dropped = 0; - - if (this._mediaElement.getVideoPlaybackQuality) { - let quality = this._mediaElement.getVideoPlaybackQuality(); - decoded = quality.totalVideoFrames; - dropped = quality.droppedVideoFrames; - } else if (this._mediaElement.webkitDecodedFrameCount != undefined) { - decoded = this._mediaElement.webkitDecodedFrameCount; - dropped = this._mediaElement.webkitDroppedFrameCount; - } else { - hasQualityInfo = false; - } - - if (hasQualityInfo) { - statInfo.decodedFrames = decoded; - statInfo.droppedFrames = dropped; - } - - return statInfo; - } - - _onmseUpdateEnd() { - if (!this._config.lazyLoad || this._config.isLive) { - return; - } - - let buffered = this._mediaElement.buffered; - let currentTime = this._mediaElement.currentTime; - let currentRangeStart = 0; - let currentRangeEnd = 0; - - for (let i = 0; i < buffered.length; i++) { - let start = buffered.start(i); - let end = buffered.end(i); - if (start <= currentTime && currentTime < end) { - currentRangeStart = start; - currentRangeEnd = end; - break; - } - } - - if (currentRangeEnd >= currentTime + this._config.lazyLoadMaxDuration && this._progressChecker == null) { - Log.v(this.TAG, 'Maximum buffering duration exceeded, suspend transmuxing task'); - this._suspendTransmuxer(); - } - } - - _onmseBufferFull() { - Log.v(this.TAG, 'MSE SourceBuffer is full, suspend transmuxing task'); - if (this._progressChecker == null) { - this._suspendTransmuxer(); - } - } - - _suspendTransmuxer() { - if (this._transmuxer) { - this._transmuxer.pause(); - - if (this._progressChecker == null) { - this._progressChecker = window.setInterval(this._checkProgressAndResume.bind(this), 1000); - } - } - } - - _checkProgressAndResume() { - let currentTime = this._mediaElement.currentTime; - let buffered = this._mediaElement.buffered; - - let needResume = false; - - for (let i = 0; i < buffered.length; i++) { - let from = buffered.start(i); - let to = buffered.end(i); - if (currentTime >= from && currentTime < to) { - if (currentTime >= to - this._config.lazyLoadRecoverDuration) { - needResume = true; - } - break; - } - } - - if (needResume) { - window.clearInterval(this._progressChecker); - this._progressChecker = null; - if (needResume) { - Log.v(this.TAG, 'Continue loading from paused position'); - this._transmuxer.resume(); - } - } - } - - _isTimepointBuffered(seconds) { - let buffered = this._mediaElement.buffered; - - for (let i = 0; i < buffered.length; i++) { - let from = buffered.start(i); - let to = buffered.end(i); - if (seconds >= from && seconds < to) { - return true; - } - } - return false; - } - - _internalSeek(seconds) { - let directSeek = this._isTimepointBuffered(seconds); - - let directSeekBegin = false; - let directSeekBeginTime = 0; - - if (seconds < 1.0 && this._mediaElement.buffered.length > 0) { - let videoBeginTime = this._mediaElement.buffered.start(0); - if ((videoBeginTime < 1.0 && seconds < videoBeginTime) || Browser.safari) { - directSeekBegin = true; - // also workaround for Safari: Seek to 0 may cause video stuck, use 0.1 to avoid - directSeekBeginTime = Browser.safari ? 0.1 : videoBeginTime; - } - } - - if (directSeekBegin) { - // seek to video begin, set currentTime directly if beginPTS buffered - this._requestSetTime = true; - this._mediaElement.currentTime = directSeekBeginTime; - } else if (directSeek) { - // buffered position - if (!this._alwaysSeekKeyframe) { - this._requestSetTime = true; - this._mediaElement.currentTime = seconds; - } else { - let idr = this._msectl.getNearestKeyframe(Math.floor(seconds * 1000)); - this._requestSetTime = true; - if (idr != null) { - this._mediaElement.currentTime = idr.dts / 1000; - } else { - this._mediaElement.currentTime = seconds; - } - } - if (this._progressChecker != null) { - this._checkProgressAndResume(); - } - } else { - if (this._progressChecker != null) { - window.clearInterval(this._progressChecker); - this._progressChecker = null; - } - this._msectl.seek(seconds); - this._transmuxer.seek(Math.floor(seconds * 1000)); // in milliseconds - // no need to set mediaElement.currentTime if non-accurateSeek, - // just wait for the recommend_seekpoint callback - if (this._config.accurateSeek) { - this._requestSetTime = true; - this._mediaElement.currentTime = seconds; - } - } - } - - _checkAndApplyUnbufferedSeekpoint() { - if (this._seekpointRecord) { - if (this._seekpointRecord.recordTime <= this._now() - 100) { - let target = this._mediaElement.currentTime; - this._seekpointRecord = null; - if (!this._isTimepointBuffered(target)) { - if (this._progressChecker != null) { - window.clearTimeout(this._progressChecker); - this._progressChecker = null; - } - // .currentTime is consists with .buffered timestamp - // Chrome/Edge use DTS, while FireFox/Safari use PTS - this._msectl.seek(target); - this._transmuxer.seek(Math.floor(target * 1000)); - // set currentTime if accurateSeek, or wait for recommend_seekpoint callback - if (this._config.accurateSeek) { - this._requestSetTime = true; - this._mediaElement.currentTime = target; - } - } - } else { - window.setTimeout(this._checkAndApplyUnbufferedSeekpoint.bind(this), 50); - } - } - } - - _checkAndResumeStuckPlayback(stalled) { - let media = this._mediaElement; - if (stalled || !this._receivedCanPlay || media.readyState < 2) { - // HAVE_CURRENT_DATA - let buffered = media.buffered; - if (buffered.length > 0 && media.currentTime < buffered.start(0)) { - Log.w(this.TAG, `Playback seems stuck at ${media.currentTime}, seek to ${buffered.start(0)}`); - this._requestSetTime = true; - this._mediaElement.currentTime = buffered.start(0); - this._mediaElement.removeEventListener('progress', this.e.onvProgress); - } - } else { - // Playback didn't stuck, remove progress event listener - this._mediaElement.removeEventListener('progress', this.e.onvProgress); - } - } - - _onvLoadedMetadata(e) { - if (this._pendingSeekTime != null) { - this._mediaElement.currentTime = this._pendingSeekTime; - this._pendingSeekTime = null; - } - } - - _onvSeeking(e) { - // handle seeking request from browser's progress bar - let target = this._mediaElement.currentTime; - let buffered = this._mediaElement.buffered; - - if (this._requestSetTime) { - this._requestSetTime = false; - return; - } - - if (target < 1.0 && buffered.length > 0) { - // seek to video begin, set currentTime directly if beginPTS buffered - let videoBeginTime = buffered.start(0); - if ((videoBeginTime < 1.0 && target < videoBeginTime) || Browser.safari) { - this._requestSetTime = true; - // also workaround for Safari: Seek to 0 may cause video stuck, use 0.1 to avoid - this._mediaElement.currentTime = Browser.safari ? 0.1 : videoBeginTime; - return; - } - } - - if (this._isTimepointBuffered(target)) { - if (this._alwaysSeekKeyframe) { - let idr = this._msectl.getNearestKeyframe(Math.floor(target * 1000)); - if (idr != null) { - this._requestSetTime = true; - this._mediaElement.currentTime = idr.dts / 1000; - } - } - if (this._progressChecker != null) { - this._checkProgressAndResume(); - } - return; - } - - this._seekpointRecord = { - seekPoint: target, - recordTime: this._now(), - }; - window.setTimeout(this._checkAndApplyUnbufferedSeekpoint.bind(this), 50); - } - - _onvCanPlay(e) { - this._receivedCanPlay = true; - this._mediaElement.removeEventListener('canplay', this.e.onvCanPlay); - } - - _onvStalled(e) { - this._checkAndResumeStuckPlayback(true); - } - - _onvProgress(e) { - this._checkAndResumeStuckPlayback(); - } - - _loadP2pSdkScripts() { - return new Promise((resolve, reject) => { - const script = document.createElement('script'); - script.src = this._config.p2pSdkSrc; - script.onload = () => { - window.clearTimeout(this._p2pSdkSrcTimer); - resolve(); - }; - script.onerror = (err) => { - window.clearTimeout(this._p2pSdkSrcTimer); - reject(); - }; - document.body.appendChild(script); - this._p2pSdkSrcTimer = window.setTimeout(() => { - reject(); - }, 2000); - }); - } -} - -export default FlvPlayer; diff --git a/src/flv.js/player/native-player.js b/src/flv.js/player/native-player.js deleted file mode 100644 index f50f3c4..0000000 --- a/src/flv.js/player/native-player.js +++ /dev/null @@ -1,267 +0,0 @@ -/* - * Copyright (C) 2016 Bilibili. All Rights Reserved. - * - * @author zheng qian - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import EventEmitter from 'events'; -import PlayerEvents from './player-events.js'; -import { createDefaultConfig } from '../config.js'; -import { InvalidArgumentException, IllegalStateException } from '../utils/exception.js'; - -// Player wrapper for browser's native player (HTMLVideoElement) without MediaSource src. -class NativePlayer { - constructor(mediaDataSource, config) { - this.TAG = 'NativePlayer'; - this._type = 'NativePlayer'; - this._emitter = new EventEmitter(); - this._createdTime = Date.now(); - - this._config = createDefaultConfig(); - if (typeof config === 'object') { - Object.assign(this._config, config); - } - - if (mediaDataSource.type.toLowerCase() === 'flv') { - throw new InvalidArgumentException("NativePlayer does't support flv MediaDataSource input!"); - } - if (mediaDataSource.hasOwnProperty('segments')) { - throw new InvalidArgumentException( - `NativePlayer(${mediaDataSource.type}) doesn't support multipart playback!`, - ); - } - - this.e = { - onvLoadedMetadata: this._onvLoadedMetadata.bind(this), - }; - - this._pendingSeekTime = null; - this._statisticsReporter = null; - - this._mediaDataSource = mediaDataSource; - this._mediaElement = null; - } - - destroy() { - if (this._mediaElement) { - this.unload(); - this.detachMediaElement(); - } - this.e = null; - this._mediaDataSource = null; - this._emitter.removeAllListeners(); - this._emitter = null; - } - - on(event, listener) { - if (event === PlayerEvents.MEDIA_INFO) { - if (this._mediaElement != null && this._mediaElement.readyState !== 0) { - // HAVE_NOTHING - Promise.resolve().then(() => { - this._emitter.emit(PlayerEvents.MEDIA_INFO, this.mediaInfo); - }); - } - } else if (event === PlayerEvents.STATISTICS_INFO) { - if (this._mediaElement != null && this._mediaElement.readyState !== 0) { - Promise.resolve().then(() => { - this._emitter.emit(PlayerEvents.STATISTICS_INFO, this.statisticsInfo); - }); - } - } - this._emitter.addListener(event, listener); - } - - one(event, listener) { - this._emitter.once(event, listener); - } - - off(event, listener) { - this._emitter.removeListener(event, listener); - } - - attachMediaElement(mediaElement) { - this._mediaElement = mediaElement; - mediaElement.addEventListener('loadedmetadata', this.e.onvLoadedMetadata); - - if (this._pendingSeekTime != null) { - try { - mediaElement.currentTime = this._pendingSeekTime; - this._pendingSeekTime = null; - } catch (e) { - // IE11 may throw InvalidStateError if readyState === 0 - // Defer set currentTime operation after loadedmetadata - } - } - } - - detachMediaElement() { - if (this._mediaElement) { - this._mediaElement.src = ''; - this._mediaElement.removeAttribute('src'); - this._mediaElement.removeEventListener('loadedmetadata', this.e.onvLoadedMetadata); - this._mediaElement = null; - } - if (this._statisticsReporter != null) { - window.clearInterval(this._statisticsReporter); - this._statisticsReporter = null; - } - } - - load() { - if (!this._mediaElement) { - throw new IllegalStateException('HTMLMediaElement must be attached before load()!'); - } - this._mediaElement.src = this._mediaDataSource.url; - - if (this._mediaElement.readyState > 0) { - this._mediaElement.currentTime = 0; - } - - this._mediaElement.preload = 'auto'; - this._mediaElement.load(); - this._statisticsReporter = window.setInterval( - this._reportStatisticsInfo.bind(this), - this._config.statisticsInfoReportInterval, - ); - } - - unload() { - if (this._mediaElement) { - this._mediaElement.src = ''; - this._mediaElement.removeAttribute('src'); - } - if (this._statisticsReporter != null) { - window.clearInterval(this._statisticsReporter); - this._statisticsReporter = null; - } - } - - play() { - return this._mediaElement.play(); - } - - pause() { - this._mediaElement.pause(); - } - - get type() { - return this._type; - } - - get buffered() { - return this._mediaElement.buffered; - } - - get duration() { - return this._mediaElement.duration; - } - - get volume() { - return this._mediaElement.volume; - } - - set volume(value) { - this._mediaElement.volume = value; - } - - get muted() { - return this._mediaElement.muted; - } - - set muted(muted) { - this._mediaElement.muted = muted; - } - - get currentTime() { - if (this._mediaElement) { - return this._mediaElement.currentTime; - } - return 0; - } - - set currentTime(seconds) { - if (this._mediaElement) { - this._mediaElement.currentTime = seconds; - } else { - this._pendingSeekTime = seconds; - } - } - - get mediaInfo() { - let mediaPrefix = this._mediaElement instanceof HTMLAudioElement ? 'audio/' : 'video/'; - let info = { - mimeType: mediaPrefix + this._mediaDataSource.type, - }; - if (this._mediaElement) { - info.duration = Math.floor(this._mediaElement.duration * 1000); - if (this._mediaElement instanceof HTMLVideoElement) { - info.width = this._mediaElement.videoWidth; - info.height = this._mediaElement.videoHeight; - } - } - return info; - } - - get statisticsInfo() { - let info = { - playerType: this._type, - url: this._mediaDataSource.url, - }; - - if (!(this._mediaElement instanceof HTMLVideoElement)) { - return info; - } - - let hasQualityInfo = true; - let decoded = 0; - let dropped = 0; - - if (this._mediaElement.getVideoPlaybackQuality) { - let quality = this._mediaElement.getVideoPlaybackQuality(); - decoded = quality.totalVideoFrames; - dropped = quality.droppedVideoFrames; - } else if (this._mediaElement.webkitDecodedFrameCount != undefined) { - decoded = this._mediaElement.webkitDecodedFrameCount; - dropped = this._mediaElement.webkitDroppedFrameCount; - } else { - hasQualityInfo = false; - } - - if (hasQualityInfo) { - info.decodedFrames = decoded; - info.droppedFrames = dropped; - } - - return info; - } - - get createdTime() { - return this._createdTime; - } - - _onvLoadedMetadata(e) { - if (this._pendingSeekTime != null) { - this._mediaElement.currentTime = this._pendingSeekTime; - this._pendingSeekTime = null; - } - this._emitter.emit(PlayerEvents.MEDIA_INFO, this.mediaInfo); - } - - _reportStatisticsInfo() { - this._emitter.emit(PlayerEvents.STATISTICS_INFO, this.statisticsInfo); - } -} - -export default NativePlayer; diff --git a/src/flv.js/player/player-errors.js b/src/flv.js/player/player-errors.js deleted file mode 100644 index 79a2d85..0000000 --- a/src/flv.js/player/player-errors.js +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2016 Bilibili. All Rights Reserved. - * - * @author zheng qian - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { LoaderErrors } from '../io/loader.js'; -import DemuxErrors from '../demux/demux-errors.js'; - -export const ErrorTypes = { - NETWORK_ERROR: 'NetworkError', - MEDIA_ERROR: 'MediaError', - OTHER_ERROR: 'OtherError', -}; - -export const ErrorDetails = { - NETWORK_EXCEPTION: LoaderErrors.EXCEPTION, - NETWORK_STATUS_CODE_INVALID: LoaderErrors.HTTP_STATUS_CODE_INVALID, - NETWORK_TIMEOUT: LoaderErrors.CONNECTING_TIMEOUT, - NETWORK_UNRECOVERABLE_EARLY_EOF: LoaderErrors.UNRECOVERABLE_EARLY_EOF, - - MEDIA_MSE_ERROR: 'MediaMSEError', - - MEDIA_FORMAT_ERROR: DemuxErrors.FORMAT_ERROR, - MEDIA_FORMAT_UNSUPPORTED: DemuxErrors.FORMAT_UNSUPPORTED, - MEDIA_CODEC_UNSUPPORTED: DemuxErrors.CODEC_UNSUPPORTED, - - ABNORMAL_SEGMENT_BYTELENGTH: 'AbnormalSegmentBytelength', -}; diff --git a/src/flv.js/player/player-events.js b/src/flv.js/player/player-events.js deleted file mode 100644 index f87ea2b..0000000 --- a/src/flv.js/player/player-events.js +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2016 Bilibili. All Rights Reserved. - * - * @author zheng qian - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -const PlayerEvents = { - ERROR: 'error', - CREATE_LOADER: 'create_loader', - LOADING_STARTED: 'loading_started', - LOADING_COMPLETE: 'loading_complete', - RECOVERED_EARLY_EOF: 'recovered_early_eof', - MEDIA_INFO: 'media_info', - STATISTICS_INFO: 'statistics_info', - HTTP_REQUEST_ENDED: 'http_request_ended', - P2P_REQUEST_ENDED: 'p2p_request_ended', - HTTP_HEADER_RECEIVED: 'http_header_received', - AUDIO_FRAME_DECODED: 'audio_frame_decoded', - VIDEO_FRAME_DECODED: 'video_frame_decoded', - CTS_WARNING: 'cts_warning', - FLV_DATA_ABNORMAL: 'flv_data_abnormal', -}; - -/** - * @desc Internal. Delivery arbitrary events to player. - */ -export const OTHER_EVENTS_POLYMER = 'other_events_polymer'; - -export default PlayerEvents; diff --git a/src/flv.js/remux/aac-silent.js b/src/flv.js/remux/aac-silent.js deleted file mode 100644 index 21be3d2..0000000 --- a/src/flv.js/remux/aac-silent.js +++ /dev/null @@ -1,302 +0,0 @@ -/* - * Copyright (C) 2016 Bilibili. All Rights Reserved. - * - * This file is modified from dailymotion's hls.js library (hls.js/src/helper/aac.js) - * @author zheng qian - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -class AAC { - static getSilentFrame(codec, channelCount) { - if (codec === 'mp4a.40.2') { - // handle LC-AAC - if (channelCount === 1) { - return new Uint8Array([0x00, 0xc8, 0x00, 0x80, 0x23, 0x80]); - } else if (channelCount === 2) { - return new Uint8Array([0x21, 0x00, 0x49, 0x90, 0x02, 0x19, 0x00, 0x23, 0x80]); - } else if (channelCount === 3) { - return new Uint8Array([0x00, 0xc8, 0x00, 0x80, 0x20, 0x84, 0x01, 0x26, 0x40, 0x08, 0x64, 0x00, 0x8e]); - } else if (channelCount === 4) { - return new Uint8Array([ - 0x00, - 0xc8, - 0x00, - 0x80, - 0x20, - 0x84, - 0x01, - 0x26, - 0x40, - 0x08, - 0x64, - 0x00, - 0x80, - 0x2c, - 0x80, - 0x08, - 0x02, - 0x38, - ]); - } else if (channelCount === 5) { - return new Uint8Array([ - 0x00, - 0xc8, - 0x00, - 0x80, - 0x20, - 0x84, - 0x01, - 0x26, - 0x40, - 0x08, - 0x64, - 0x00, - 0x82, - 0x30, - 0x04, - 0x99, - 0x00, - 0x21, - 0x90, - 0x02, - 0x38, - ]); - } else if (channelCount === 6) { - return new Uint8Array([ - 0x00, - 0xc8, - 0x00, - 0x80, - 0x20, - 0x84, - 0x01, - 0x26, - 0x40, - 0x08, - 0x64, - 0x00, - 0x82, - 0x30, - 0x04, - 0x99, - 0x00, - 0x21, - 0x90, - 0x02, - 0x00, - 0xb2, - 0x00, - 0x20, - 0x08, - 0xe0, - ]); - } - } else { - // handle HE-AAC (mp4a.40.5 / mp4a.40.29) - if (channelCount === 1) { - // ffmpeg -y -f lavfi -i "aevalsrc=0:d=0.05" -c:a libfdk_aac -profile:a aac_he -b:a 4k output.aac && hexdump -v -e '16/1 "0x%x," "\n"' -v output.aac - return new Uint8Array([ - 0x1, - 0x40, - 0x22, - 0x80, - 0xa3, - 0x4e, - 0xe6, - 0x80, - 0xba, - 0x8, - 0x0, - 0x0, - 0x0, - 0x1c, - 0x6, - 0xf1, - 0xc1, - 0xa, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5e, - ]); - } else if (channelCount === 2) { - // ffmpeg -y -f lavfi -i "aevalsrc=0|0:d=0.05" -c:a libfdk_aac -profile:a aac_he_v2 -b:a 4k output.aac && hexdump -v -e '16/1 "0x%x," "\n"' -v output.aac - return new Uint8Array([ - 0x1, - 0x40, - 0x22, - 0x80, - 0xa3, - 0x5e, - 0xe6, - 0x80, - 0xba, - 0x8, - 0x0, - 0x0, - 0x0, - 0x0, - 0x95, - 0x0, - 0x6, - 0xf1, - 0xa1, - 0xa, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5e, - ]); - } else if (channelCount === 3) { - // ffmpeg -y -f lavfi -i "aevalsrc=0|0|0:d=0.05" -c:a libfdk_aac -profile:a aac_he_v2 -b:a 4k output.aac && hexdump -v -e '16/1 "0x%x," "\n"' -v output.aac - return new Uint8Array([ - 0x1, - 0x40, - 0x22, - 0x80, - 0xa3, - 0x5e, - 0xe6, - 0x80, - 0xba, - 0x8, - 0x0, - 0x0, - 0x0, - 0x0, - 0x95, - 0x0, - 0x6, - 0xf1, - 0xa1, - 0xa, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5a, - 0x5e, - ]); - } - } - return null; - } -} - -export default AAC; diff --git a/src/flv.js/remux/mp4-generator.js b/src/flv.js/remux/mp4-generator.js deleted file mode 100644 index a34b8f3..0000000 --- a/src/flv.js/remux/mp4-generator.js +++ /dev/null @@ -1,996 +0,0 @@ -/* - * Copyright (C) 2016 Bilibili. All Rights Reserved. - * - * This file is derived from dailymotion's hls.js library (hls.js/src/remux/mp4-generator.js) - * @author zheng qian - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// MP4 boxes generator for ISO BMFF (ISO Base Media File Format, defined in ISO/IEC 14496-12) -class MP4 { - static init() { - MP4.types = { - avc1: [], - avcC: [], - btrt: [], - dinf: [], - dref: [], - esds: [], - ftyp: [], - hdlr: [], - mdat: [], - mdhd: [], - mdia: [], - mfhd: [], - minf: [], - moof: [], - moov: [], - mp4a: [], - mvex: [], - mvhd: [], - sdtp: [], - stbl: [], - stco: [], - stsc: [], - stsd: [], - stsz: [], - stts: [], - tfdt: [], - tfhd: [], - traf: [], - trak: [], - trun: [], - trex: [], - tkhd: [], - vmhd: [], - smhd: [], - '.mp3': [], - }; - - for (let name in MP4.types) { - if (MP4.types.hasOwnProperty(name)) { - MP4.types[name] = [name.charCodeAt(0), name.charCodeAt(1), name.charCodeAt(2), name.charCodeAt(3)]; - } - } - - let constants = (MP4.constants = {}); - - constants.FTYP = new Uint8Array([ - 0x69, - 0x73, - 0x6f, - 0x6d, // major_brand: isom - 0x0, - 0x0, - 0x0, - 0x1, // minor_version: 0x01 - 0x69, - 0x73, - 0x6f, - 0x6d, // isom - 0x61, - 0x76, - 0x63, - 0x31, // avc1 - ]); - - constants.STSD_PREFIX = new Uint8Array([ - 0x00, - 0x00, - 0x00, - 0x00, // version(0) + flags - 0x00, - 0x00, - 0x00, - 0x01, // entry_count - ]); - - constants.STTS = new Uint8Array([ - 0x00, - 0x00, - 0x00, - 0x00, // version(0) + flags - 0x00, - 0x00, - 0x00, - 0x00, // entry_count - ]); - - constants.STSC = constants.STCO = constants.STTS; - - constants.STSZ = new Uint8Array([ - 0x00, - 0x00, - 0x00, - 0x00, // version(0) + flags - 0x00, - 0x00, - 0x00, - 0x00, // sample_size - 0x00, - 0x00, - 0x00, - 0x00, // sample_count - ]); - - constants.HDLR_VIDEO = new Uint8Array([ - 0x00, - 0x00, - 0x00, - 0x00, // version(0) + flags - 0x00, - 0x00, - 0x00, - 0x00, // pre_defined - 0x76, - 0x69, - 0x64, - 0x65, // handler_type: 'vide' - 0x00, - 0x00, - 0x00, - 0x00, // reserved: 3 * 4 bytes - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x56, - 0x69, - 0x64, - 0x65, - 0x6f, - 0x48, - 0x61, - 0x6e, - 0x64, - 0x6c, - 0x65, - 0x72, - 0x00, // name: VideoHandler - ]); - - constants.HDLR_AUDIO = new Uint8Array([ - 0x00, - 0x00, - 0x00, - 0x00, // version(0) + flags - 0x00, - 0x00, - 0x00, - 0x00, // pre_defined - 0x73, - 0x6f, - 0x75, - 0x6e, // handler_type: 'soun' - 0x00, - 0x00, - 0x00, - 0x00, // reserved: 3 * 4 bytes - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x53, - 0x6f, - 0x75, - 0x6e, - 0x64, - 0x48, - 0x61, - 0x6e, - 0x64, - 0x6c, - 0x65, - 0x72, - 0x00, // name: SoundHandler - ]); - - constants.DREF = new Uint8Array([ - 0x00, - 0x00, - 0x00, - 0x00, // version(0) + flags - 0x00, - 0x00, - 0x00, - 0x01, // entry_count - 0x00, - 0x00, - 0x00, - 0x0c, // entry_size - 0x75, - 0x72, - 0x6c, - 0x20, // type 'url ' - 0x00, - 0x00, - 0x00, - 0x01, // version(0) + flags - ]); - - // Sound media header - constants.SMHD = new Uint8Array([ - 0x00, - 0x00, - 0x00, - 0x00, // version(0) + flags - 0x00, - 0x00, - 0x00, - 0x00, // balance(2) + reserved(2) - ]); - - // video media header - constants.VMHD = new Uint8Array([ - 0x00, - 0x00, - 0x00, - 0x01, // version(0) + flags - 0x00, - 0x00, // graphicsmode: 2 bytes - 0x00, - 0x00, - 0x00, - 0x00, // opcolor: 3 * 2 bytes - 0x00, - 0x00, - ]); - } - - // Generate a box - static box(type) { - let size = 8; - let result = null; - let datas = Array.prototype.slice.call(arguments, 1); - let arrayCount = datas.length; - - for (let i = 0; i < arrayCount; i++) { - size += datas[i].byteLength; - } - - result = new Uint8Array(size); - result[0] = (size >>> 24) & 0xff; // size - result[1] = (size >>> 16) & 0xff; - result[2] = (size >>> 8) & 0xff; - result[3] = size & 0xff; - - result.set(type, 4); // type - - let offset = 8; - for (let i = 0; i < arrayCount; i++) { - // data body - result.set(datas[i], offset); - offset += datas[i].byteLength; - } - - return result; - } - - // emit ftyp & moov - static generateInitSegment(meta) { - let ftyp = MP4.box(MP4.types.ftyp, MP4.constants.FTYP); - let moov = MP4.moov(meta); - - let result = new Uint8Array(ftyp.byteLength + moov.byteLength); - result.set(ftyp, 0); - result.set(moov, ftyp.byteLength); - return result; - } - - // Movie metadata box - static moov(meta) { - let mvhd = MP4.mvhd(meta.timescale, meta.duration); - let trak = MP4.trak(meta); - let mvex = MP4.mvex(meta); - return MP4.box(MP4.types.moov, mvhd, trak, mvex); - } - - // Movie header box - static mvhd(timescale, duration) { - return MP4.box( - MP4.types.mvhd, - new Uint8Array([ - 0x00, - 0x00, - 0x00, - 0x00, // version(0) + flags - 0x00, - 0x00, - 0x00, - 0x00, // creation_time - 0x00, - 0x00, - 0x00, - 0x00, // modification_time - (timescale >>> 24) & 0xff, // timescale: 4 bytes - (timescale >>> 16) & 0xff, - (timescale >>> 8) & 0xff, - timescale & 0xff, - (duration >>> 24) & 0xff, // duration: 4 bytes - (duration >>> 16) & 0xff, - (duration >>> 8) & 0xff, - duration & 0xff, - 0x00, - 0x01, - 0x00, - 0x00, // Preferred rate: 1.0 - 0x01, - 0x00, - 0x00, - 0x00, // PreferredVolume(1.0, 2bytes) + reserved(2bytes) - 0x00, - 0x00, - 0x00, - 0x00, // reserved: 4 + 4 bytes - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x01, - 0x00, - 0x00, // ----begin composition matrix---- - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x01, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x40, - 0x00, - 0x00, - 0x00, // ----end composition matrix---- - 0x00, - 0x00, - 0x00, - 0x00, // ----begin pre_defined 6 * 4 bytes---- - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, // ----end pre_defined 6 * 4 bytes---- - 0xff, - 0xff, - 0xff, - 0xff, // next_track_ID - ]), - ); - } - - // Track box - static trak(meta) { - return MP4.box(MP4.types.trak, MP4.tkhd(meta), MP4.mdia(meta)); - } - - // Track header box - static tkhd(meta) { - let trackId = meta.id, - duration = meta.duration; - let width = meta.presentWidth, - height = meta.presentHeight; - - return MP4.box( - MP4.types.tkhd, - new Uint8Array([ - 0x00, - 0x00, - 0x00, - 0x07, // version(0) + flags - 0x00, - 0x00, - 0x00, - 0x00, // creation_time - 0x00, - 0x00, - 0x00, - 0x00, // modification_time - (trackId >>> 24) & 0xff, // track_ID: 4 bytes - (trackId >>> 16) & 0xff, - (trackId >>> 8) & 0xff, - trackId & 0xff, - 0x00, - 0x00, - 0x00, - 0x00, // reserved: 4 bytes - (duration >>> 24) & 0xff, // duration: 4 bytes - (duration >>> 16) & 0xff, - (duration >>> 8) & 0xff, - duration & 0xff, - 0x00, - 0x00, - 0x00, - 0x00, // reserved: 2 * 4 bytes - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, // layer(2bytes) + alternate_group(2bytes) - 0x00, - 0x00, - 0x00, - 0x00, // volume(2bytes) + reserved(2bytes) - 0x00, - 0x01, - 0x00, - 0x00, // ----begin composition matrix---- - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x01, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x40, - 0x00, - 0x00, - 0x00, // ----end composition matrix---- - (width >>> 8) & 0xff, // width and height - width & 0xff, - 0x00, - 0x00, - (height >>> 8) & 0xff, - height & 0xff, - 0x00, - 0x00, - ]), - ); - } - - // Media Box - static mdia(meta) { - return MP4.box(MP4.types.mdia, MP4.mdhd(meta), MP4.hdlr(meta), MP4.minf(meta)); - } - - // Media header box - static mdhd(meta) { - let timescale = meta.timescale; - let duration = meta.duration; - return MP4.box( - MP4.types.mdhd, - new Uint8Array([ - 0x00, - 0x00, - 0x00, - 0x00, // version(0) + flags - 0x00, - 0x00, - 0x00, - 0x00, // creation_time - 0x00, - 0x00, - 0x00, - 0x00, // modification_time - (timescale >>> 24) & 0xff, // timescale: 4 bytes - (timescale >>> 16) & 0xff, - (timescale >>> 8) & 0xff, - timescale & 0xff, - (duration >>> 24) & 0xff, // duration: 4 bytes - (duration >>> 16) & 0xff, - (duration >>> 8) & 0xff, - duration & 0xff, - 0x55, - 0xc4, // language: und (undetermined) - 0x00, - 0x00, // pre_defined = 0 - ]), - ); - } - - // Media handler reference box - static hdlr(meta) { - let data = null; - if (meta.type === 'audio') { - data = MP4.constants.HDLR_AUDIO; - } else { - data = MP4.constants.HDLR_VIDEO; - } - return MP4.box(MP4.types.hdlr, data); - } - - // Media infomation box - static minf(meta) { - let xmhd = null; - if (meta.type === 'audio') { - xmhd = MP4.box(MP4.types.smhd, MP4.constants.SMHD); - } else { - xmhd = MP4.box(MP4.types.vmhd, MP4.constants.VMHD); - } - return MP4.box(MP4.types.minf, xmhd, MP4.dinf(), MP4.stbl(meta)); - } - - // Data infomation box - static dinf() { - let result = MP4.box(MP4.types.dinf, MP4.box(MP4.types.dref, MP4.constants.DREF)); - return result; - } - - // Sample table box - static stbl(meta) { - let result = MP4.box( - MP4.types.stbl, // type: stbl - MP4.stsd(meta), // Sample Description Table - MP4.box(MP4.types.stts, MP4.constants.STTS), // Time-To-Sample - MP4.box(MP4.types.stsc, MP4.constants.STSC), // Sample-To-Chunk - MP4.box(MP4.types.stsz, MP4.constants.STSZ), // Sample size - MP4.box(MP4.types.stco, MP4.constants.STCO), // Chunk offset - ); - return result; - } - - // Sample description box - static stsd(meta) { - if (meta.type === 'audio') { - if (meta.codec === 'mp3') { - return MP4.box(MP4.types.stsd, MP4.constants.STSD_PREFIX, MP4.mp3(meta)); - } - // else: aac -> mp4a - return MP4.box(MP4.types.stsd, MP4.constants.STSD_PREFIX, MP4.mp4a(meta)); - } else { - return MP4.box(MP4.types.stsd, MP4.constants.STSD_PREFIX, MP4.avc1(meta)); - } - } - - static mp3(meta) { - let channelCount = meta.channelCount; - let sampleRate = meta.audioSampleRate; - - let data = new Uint8Array([ - 0x00, - 0x00, - 0x00, - 0x00, // reserved(4) - 0x00, - 0x00, - 0x00, - 0x01, // reserved(2) + data_reference_index(2) - 0x00, - 0x00, - 0x00, - 0x00, // reserved: 2 * 4 bytes - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - channelCount, // channelCount(2) - 0x00, - 0x10, // sampleSize(2) - 0x00, - 0x00, - 0x00, - 0x00, // reserved(4) - (sampleRate >>> 8) & 0xff, // Audio sample rate - sampleRate & 0xff, - 0x00, - 0x00, - ]); - - return MP4.box(MP4.types['.mp3'], data); - } - - static mp4a(meta) { - let channelCount = meta.channelCount; - let sampleRate = meta.audioSampleRate; - - let data = new Uint8Array([ - 0x00, - 0x00, - 0x00, - 0x00, // reserved(4) - 0x00, - 0x00, - 0x00, - 0x01, // reserved(2) + data_reference_index(2) - 0x00, - 0x00, - 0x00, - 0x00, // reserved: 2 * 4 bytes - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - channelCount, // channelCount(2) - 0x00, - 0x10, // sampleSize(2) - 0x00, - 0x00, - 0x00, - 0x00, // reserved(4) - (sampleRate >>> 8) & 0xff, // Audio sample rate - sampleRate & 0xff, - 0x00, - 0x00, - ]); - - return MP4.box(MP4.types.mp4a, data, MP4.esds(meta)); - } - - static esds(meta) { - let config = meta.config || []; - let configSize = config.length; - let data = new Uint8Array( - [ - 0x00, - 0x00, - 0x00, - 0x00, // version 0 + flags - - 0x03, // descriptor_type - 0x17 + configSize, // length3 - 0x00, - 0x01, // es_id - 0x00, // stream_priority - - 0x04, // descriptor_type - 0x0f + configSize, // length - 0x40, // codec: mpeg4_audio - 0x15, // stream_type: Audio - 0x00, - 0x00, - 0x00, // buffer_size - 0x00, - 0x00, - 0x00, - 0x00, // maxBitrate - 0x00, - 0x00, - 0x00, - 0x00, // avgBitrate - - 0x05, // descriptor_type - ] - .concat([configSize]) - .concat(config) - .concat([ - 0x06, - 0x01, - 0x02, // GASpecificConfig - ]), - ); - return MP4.box(MP4.types.esds, data); - } - - static avc1(meta) { - let avcc = meta.avcc; - let width = meta.codecWidth, - height = meta.codecHeight; - - let data = new Uint8Array([ - 0x00, - 0x00, - 0x00, - 0x00, // reserved(4) - 0x00, - 0x00, - 0x00, - 0x01, // reserved(2) + data_reference_index(2) - 0x00, - 0x00, - 0x00, - 0x00, // pre_defined(2) + reserved(2) - 0x00, - 0x00, - 0x00, - 0x00, // pre_defined: 3 * 4 bytes - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - (width >>> 8) & 0xff, // width: 2 bytes - width & 0xff, - (height >>> 8) & 0xff, // height: 2 bytes - height & 0xff, - 0x00, - 0x48, - 0x00, - 0x00, // horizresolution: 4 bytes - 0x00, - 0x48, - 0x00, - 0x00, // vertresolution: 4 bytes - 0x00, - 0x00, - 0x00, - 0x00, // reserved: 4 bytes - 0x00, - 0x01, // frame_count - 0x0a, // strlen - 0x78, - 0x71, - 0x71, - 0x2f, // compressorname: 32 bytes - 0x66, - 0x6c, - 0x76, - 0x2e, - 0x6a, - 0x73, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x18, // depth - 0xff, - 0xff, // pre_defined = -1 - ]); - return MP4.box(MP4.types.avc1, data, MP4.box(MP4.types.avcC, avcc)); - } - - // Movie Extends box - static mvex(meta) { - return MP4.box(MP4.types.mvex, MP4.trex(meta)); - } - - // Track Extends box - static trex(meta) { - let trackId = meta.id; - let data = new Uint8Array([ - 0x00, - 0x00, - 0x00, - 0x00, // version(0) + flags - (trackId >>> 24) & 0xff, // track_ID - (trackId >>> 16) & 0xff, - (trackId >>> 8) & 0xff, - trackId & 0xff, - 0x00, - 0x00, - 0x00, - 0x01, // default_sample_description_index - 0x00, - 0x00, - 0x00, - 0x00, // default_sample_duration - 0x00, - 0x00, - 0x00, - 0x00, // default_sample_size - 0x00, - 0x01, - 0x00, - 0x01, // default_sample_flags - ]); - return MP4.box(MP4.types.trex, data); - } - - // Movie fragment box - static moof(track, baseMediaDecodeTime) { - return MP4.box(MP4.types.moof, MP4.mfhd(track.sequenceNumber), MP4.traf(track, baseMediaDecodeTime)); - } - - static mfhd(sequenceNumber) { - let data = new Uint8Array([ - 0x00, - 0x00, - 0x00, - 0x00, - (sequenceNumber >>> 24) & 0xff, // sequence_number: int32 - (sequenceNumber >>> 16) & 0xff, - (sequenceNumber >>> 8) & 0xff, - sequenceNumber & 0xff, - ]); - return MP4.box(MP4.types.mfhd, data); - } - - // Track fragment box - static traf(track, baseMediaDecodeTime) { - let trackId = track.id; - - // Track fragment header box - let tfhd = MP4.box( - MP4.types.tfhd, - new Uint8Array([ - 0x00, - 0x00, - 0x00, - 0x00, // version(0) & flags - (trackId >>> 24) & 0xff, // track_ID - (trackId >>> 16) & 0xff, - (trackId >>> 8) & 0xff, - trackId & 0xff, - ]), - ); - // Track Fragment Decode Time - let tfdt = MP4.box( - MP4.types.tfdt, - new Uint8Array([ - 0x00, - 0x00, - 0x00, - 0x00, // version(0) & flags - (baseMediaDecodeTime >>> 24) & 0xff, // baseMediaDecodeTime: int32 - (baseMediaDecodeTime >>> 16) & 0xff, - (baseMediaDecodeTime >>> 8) & 0xff, - baseMediaDecodeTime & 0xff, - ]), - ); - let sdtp = MP4.sdtp(track); - let trun = MP4.trun(track, sdtp.byteLength + 16 + 16 + 8 + 16 + 8 + 8); - - return MP4.box(MP4.types.traf, tfhd, tfdt, trun, sdtp); - } - - // Sample Dependency Type box - static sdtp(track) { - let samples = track.samples || []; - let sampleCount = samples.length; - let data = new Uint8Array(4 + sampleCount); - // 0~4 bytes: version(0) & flags - for (let i = 0; i < sampleCount; i++) { - let flags = samples[i].flags; - data[i + 4] = - (flags.isLeading << 6) | // is_leading: 2 (bit) - (flags.dependsOn << 4) | // sample_depends_on - (flags.isDependedOn << 2) | // sample_is_depended_on - flags.hasRedundancy; // sample_has_redundancy - } - return MP4.box(MP4.types.sdtp, data); - } - - // Track fragment run box - static trun(track, offset) { - let samples = track.samples || []; - let sampleCount = samples.length; - let dataSize = 12 + 16 * sampleCount; - let data = new Uint8Array(dataSize); - offset += 8 + dataSize; - - data.set( - [ - 0x00, - 0x00, - 0x0f, - 0x01, // version(0) & flags - (sampleCount >>> 24) & 0xff, // sample_count - (sampleCount >>> 16) & 0xff, - (sampleCount >>> 8) & 0xff, - sampleCount & 0xff, - (offset >>> 24) & 0xff, // data_offset - (offset >>> 16) & 0xff, - (offset >>> 8) & 0xff, - offset & 0xff, - ], - 0, - ); - - for (let i = 0; i < sampleCount; i++) { - let duration = samples[i].duration; - let size = samples[i].size; - let flags = samples[i].flags; - let cts = samples[i].cts; - data.set( - [ - (duration >>> 24) & 0xff, // sample_duration - (duration >>> 16) & 0xff, - (duration >>> 8) & 0xff, - duration & 0xff, - (size >>> 24) & 0xff, // sample_size - (size >>> 16) & 0xff, - (size >>> 8) & 0xff, - size & 0xff, - (flags.isLeading << 2) | flags.dependsOn, // sample_flags - (flags.isDependedOn << 6) | (flags.hasRedundancy << 4) | flags.isNonSync, - 0x00, - 0x00, // sample_degradation_priority - (cts >>> 24) & 0xff, // sample_composition_time_offset - (cts >>> 16) & 0xff, - (cts >>> 8) & 0xff, - cts & 0xff, - ], - 12 + 16 * i, - ); - } - return MP4.box(MP4.types.trun, data); - } - - static mdat(data) { - return MP4.box(MP4.types.mdat, data); - } -} - -MP4.init(); - -export default MP4; diff --git a/src/flv.js/remux/mp4-remuxer.js b/src/flv.js/remux/mp4-remuxer.js deleted file mode 100644 index e3f2ac7..0000000 --- a/src/flv.js/remux/mp4-remuxer.js +++ /dev/null @@ -1,663 +0,0 @@ -/* - * Copyright (C) 2016 Bilibili. All Rights Reserved. - * - * @author zheng qian - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import Log from '../utils/logger.js'; -import MP4 from './mp4-generator.js'; -import AAC from './aac-silent.js'; -import Browser from '../utils/browser.js'; -import { SampleInfo, MediaSegmentInfo, MediaSegmentInfoList } from '../core/media-segment-info.js'; -import { IllegalStateException } from '../utils/exception.js'; - -// Fragmented mp4 remuxer -class MP4Remuxer { - constructor(config) { - this.TAG = 'MP4Remuxer'; - - this._config = config; - this._isLive = config.isLive === true ? true : false; - - this._dtsBase = -1; - this._dtsBaseInited = false; - this._audioDtsBase = Infinity; - this._videoDtsBase = Infinity; - this._audioNextDts = undefined; - this._videoNextDts = undefined; - - this._audioMeta = null; - this._videoMeta = null; - - this._audioSegmentInfoList = new MediaSegmentInfoList('audio'); - this._videoSegmentInfoList = new MediaSegmentInfoList('video'); - - this._onInitSegment = null; - this._onMediaSegment = null; - - // Workaround for chrome < 50: Always force first sample as a Random Access Point in media segment - // see https://bugs.chromium.org/p/chromium/issues/detail?id=229412 - this._forceFirstIDR = - Browser.chrome && - (Browser.version.major < 50 || (Browser.version.major === 50 && Browser.version.build < 2661)) - ? true - : false; - - // Workaround for IE11/Edge: Fill silent aac frame after keyframe-seeking - // Make audio beginDts equals with video beginDts, in order to fix seek freeze - this._fillSilentAfterSeek = Browser.msedge || Browser.msie; - - // While only FireFox supports 'audio/mp4, codecs="mp3"', use 'audio/mpeg' for chrome, safari, ... - this._mp3UseMpegAudio = !Browser.firefox; - - this._fillAudioTimestampGap = this._config.fixAudioTimestampGap; - } - - destroy() { - this._dtsBase = -1; - this._dtsBaseInited = false; - this._audioMeta = null; - this._videoMeta = null; - this._audioSegmentInfoList.clear(); - this._audioSegmentInfoList = null; - this._videoSegmentInfoList.clear(); - this._videoSegmentInfoList = null; - this._onInitSegment = null; - this._onMediaSegment = null; - } - - bindDataSource(producer) { - producer.onDataAvailable = this.remux.bind(this); - producer.onTrackMetadata = this._onTrackMetadataReceived.bind(this); - return this; - } - - /* prototype: function onInitSegment(type: string, initSegment: ArrayBuffer): void - InitSegment: { - type: string, - data: ArrayBuffer, - codec: string, - container: string - } - */ - get onInitSegment() { - return this._onInitSegment; - } - - set onInitSegment(callback) { - this._onInitSegment = callback; - } - - /* prototype: function onMediaSegment(type: string, mediaSegment: MediaSegment): void - MediaSegment: { - type: string, - data: ArrayBuffer, - sampleCount: int32 - info: MediaSegmentInfo - } - */ - get onMediaSegment() { - return this._onMediaSegment; - } - - set onMediaSegment(callback) { - this._onMediaSegment = callback; - } - - insertDiscontinuity() { - this._audioNextDts = this._videoNextDts = undefined; - } - - seek(originalDts) { - this._videoSegmentInfoList.clear(); - this._audioSegmentInfoList.clear(); - } - - remux(audioTrack, videoTrack) { - if (!this._onMediaSegment) { - throw new IllegalStateException('MP4Remuxer: onMediaSegment callback must be specificed!'); - } - if (!this._dtsBaseInited) { - this._calculateDtsBase(audioTrack, videoTrack); - } - this._remuxVideo(videoTrack); - this._remuxAudio(audioTrack); - } - - _onTrackMetadataReceived(type, metadata) { - let metabox = null; - - let container = 'mp4'; - let codec = metadata.codec; - - if (type === 'audio') { - this._audioMeta = metadata; - if (metadata.codec === 'mp3' && this._mp3UseMpegAudio) { - // 'audio/mpeg' for MP3 audio track - container = 'mpeg'; - codec = ''; - metabox = new Uint8Array(); - } else { - // 'audio/mp4, codecs="codec"' - metabox = MP4.generateInitSegment(metadata); - } - } else if (type === 'video') { - this._videoMeta = metadata; - metabox = MP4.generateInitSegment(metadata); - } else { - return; - } - - // dispatch metabox (Initialization Segment) - if (!this._onInitSegment) { - throw new IllegalStateException('MP4Remuxer: onInitSegment callback must be specified!'); - } - this._onInitSegment(type, { - type: type, - data: metabox.buffer, - codec: codec, - container: `${type}/${container}`, - mediaDuration: metadata.duration, // in timescale 1000 (milliseconds) - }); - } - - _calculateDtsBase(audioTrack, videoTrack) { - if (this._dtsBaseInited) { - return; - } - - if (audioTrack.samples && audioTrack.samples.length) { - this._audioDtsBase = audioTrack.samples[0].dts; - } - if (videoTrack.samples && videoTrack.samples.length) { - this._videoDtsBase = videoTrack.samples[0].dts; - } - - this._dtsBase = Math.min(this._audioDtsBase, this._videoDtsBase); - this._dtsBaseInited = true; - } - - _remuxAudio(audioTrack) { - if (this._audioMeta == null) { - return; - } - - let track = audioTrack; - let samples = track.samples; - let dtsCorrection = undefined; - let firstDts = -1, - lastDts = -1, - lastPts = -1; - let refSampleDuration = this._audioMeta.refSampleDuration; - - let mpegRawTrack = this._audioMeta.codec === 'mp3' && this._mp3UseMpegAudio; - let firstSegmentAfterSeek = this._dtsBaseInited && this._audioNextDts === undefined; - - let insertPrefixSilentFrame = false; - - if (!samples || samples.length === 0) { - return; - } - - let offset = 0; - let mdatbox = null; - let mdatBytes = 0; - - // calculate initial mdat size - if (mpegRawTrack) { - // for raw mpeg buffer - offset = 0; - mdatBytes = track.length; - } else { - // for fmp4 mdat box - offset = 8; // size + type - mdatBytes = 8 + track.length; - } - - let firstSampleOriginalDts = samples[0].dts - this._dtsBase; - - // calculate dtsCorrection - if (this._audioNextDts) { - dtsCorrection = firstSampleOriginalDts - this._audioNextDts; - } else { - // this._audioNextDts == undefined - if (this._audioSegmentInfoList.isEmpty()) { - dtsCorrection = 0; - if (this._fillSilentAfterSeek && !this._videoSegmentInfoList.isEmpty()) { - if (this._audioMeta.originalCodec !== 'mp3') { - insertPrefixSilentFrame = true; - } - } - } else { - let lastSample = this._audioSegmentInfoList.getLastSampleBefore(firstSampleOriginalDts); - if (lastSample != null) { - let distance = firstSampleOriginalDts - (lastSample.originalDts + lastSample.duration); - if (distance <= 3) { - distance = 0; - } - let expectedDts = lastSample.dts + lastSample.duration + distance; - dtsCorrection = firstSampleOriginalDts - expectedDts; - } else { - // lastSample == null, cannot found - dtsCorrection = 0; - } - } - } - - if (insertPrefixSilentFrame) { - // align audio segment beginDts to match with current video segment's beginDts - let firstSampleDts = firstSampleOriginalDts - dtsCorrection; - let videoSegment = this._videoSegmentInfoList.getLastSegmentBefore(firstSampleOriginalDts); - if (videoSegment != null && videoSegment.beginDts < firstSampleDts) { - let silentUnit = AAC.getSilentFrame(this._audioMeta.originalCodec, this._audioMeta.channelCount); - if (silentUnit) { - let dts = videoSegment.beginDts; - let silentFrameDuration = firstSampleDts - videoSegment.beginDts; - Log.v(this.TAG, `InsertPrefixSilentAudio: dts: ${dts}, duration: ${silentFrameDuration}`); - samples.unshift({ unit: silentUnit, dts: dts, pts: dts }); - mdatBytes += silentUnit.byteLength; - } // silentUnit == null: Cannot generate, skip - } else { - insertPrefixSilentFrame = false; - } - } - - let mp4Samples = []; - - // Correct dts for each sample, and calculate sample duration. Then output to mp4Samples - for (let i = 0; i < samples.length; i++) { - let sample = samples[i]; - let unit = sample.unit; - let originalDts = sample.dts - this._dtsBase; - let dts = originalDts - dtsCorrection; - - if (firstDts === -1) { - firstDts = dts; - } - - let sampleDuration = 0; - - if (i !== samples.length - 1) { - let nextDts = samples[i + 1].dts - this._dtsBase - dtsCorrection; - sampleDuration = nextDts - dts; - } else { - // the last sample - if (mp4Samples.length >= 1) { - // use second last sample duration - sampleDuration = mp4Samples[mp4Samples.length - 1].duration; - } else { - // the only one sample, use reference sample duration - sampleDuration = Math.floor(refSampleDuration); - } - } - - let needFillSilentFrames = false; - let silentFrames = null; - - // Silent frame generation, if large timestamp gap detected && config.fixAudioTimestampGap - if ( - sampleDuration > refSampleDuration * 1.5 && - this._audioMeta.codec !== 'mp3' && - this._fillAudioTimestampGap && - !Browser.safari - ) { - // We need to insert silent frames to fill timestamp gap - needFillSilentFrames = true; - let delta = Math.abs(sampleDuration - refSampleDuration); - let frameCount = Math.ceil(delta / refSampleDuration); - let currentDts = dts + refSampleDuration; // Notice: in float - - Log.w( - this.TAG, - 'Large audio timestamp gap detected, may cause AV sync to drift. ' + - 'Silent frames will be generated to avoid unsync.\n' + - `dts: ${dts + sampleDuration} ms, expected: ${dts + Math.round(refSampleDuration)} ms, ` + - `delta: ${Math.round(delta)} ms, generate: ${frameCount} frames`, - ); - - let silentUnit = AAC.getSilentFrame(this._audioMeta.originalCodec, this._audioMeta.channelCount); - if (silentUnit == null) { - Log.w( - this.TAG, - 'Unable to generate silent frame for ' + - `${this._audioMeta.originalCodec} with ${this._audioMeta.channelCount} channels, repeat last frame`, - ); - // Repeat last frame - silentUnit = unit; - } - silentFrames = []; - - for (let j = 0; j < frameCount; j++) { - let intDts = Math.round(currentDts); // round to integer - if (silentFrames.length > 0) { - // Set previous frame sample duration - let previousFrame = silentFrames[silentFrames.length - 1]; - previousFrame.duration = intDts - previousFrame.dts; - } - let frame = { - dts: intDts, - pts: intDts, - cts: 0, - unit: silentUnit, - size: silentUnit.byteLength, - duration: 0, // wait for next sample - originalDts: originalDts, - flags: { - isLeading: 0, - dependsOn: 1, - isDependedOn: 0, - hasRedundancy: 0, - }, - }; - silentFrames.push(frame); - mdatBytes += unit.byteLength; - currentDts += refSampleDuration; - } - - // last frame: align end time to next frame dts - let lastFrame = silentFrames[silentFrames.length - 1]; - lastFrame.duration = dts + sampleDuration - lastFrame.dts; - - // silentFrames.forEach((frame) => { - // Log.w(this.TAG, `SilentAudio: dts: ${frame.dts}, duration: ${frame.duration}`); - // }); - - // Set correct sample duration for current frame - sampleDuration = Math.round(refSampleDuration); - } - - mp4Samples.push({ - dts: dts, - pts: dts, - cts: 0, - unit: sample.unit, - size: sample.unit.byteLength, - duration: sampleDuration, - originalDts: originalDts, - flags: { - isLeading: 0, - dependsOn: 1, - isDependedOn: 0, - hasRedundancy: 0, - }, - }); - - if (needFillSilentFrames) { - // Silent frames should be inserted after wrong-duration frame - mp4Samples.push.apply(mp4Samples, silentFrames); - } - } - - // allocate mdatbox - if (mpegRawTrack) { - // allocate for raw mpeg buffer - mdatbox = new Uint8Array(mdatBytes); - } else { - // allocate for fmp4 mdat box - mdatbox = new Uint8Array(mdatBytes); - // size field - mdatbox[0] = (mdatBytes >>> 24) & 0xff; - mdatbox[1] = (mdatBytes >>> 16) & 0xff; - mdatbox[2] = (mdatBytes >>> 8) & 0xff; - mdatbox[3] = mdatBytes & 0xff; - // type field (fourCC) - mdatbox.set(MP4.types.mdat, 4); - } - - // Write samples into mdatbox - for (let i = 0; i < mp4Samples.length; i++) { - let unit = mp4Samples[i].unit; - mdatbox.set(unit, offset); - offset += unit.byteLength; - } - - let latest = mp4Samples[mp4Samples.length - 1]; - lastDts = latest.dts + latest.duration; - this._audioNextDts = lastDts; - - // fill media segment info & add to info list - let info = new MediaSegmentInfo(); - info.beginDts = firstDts; - info.endDts = lastDts; - info.beginPts = firstDts; - info.endPts = lastDts; - info.originalBeginDts = mp4Samples[0].originalDts; - info.originalEndDts = latest.originalDts + latest.duration; - info.firstSample = new SampleInfo( - mp4Samples[0].dts, - mp4Samples[0].pts, - mp4Samples[0].duration, - mp4Samples[0].originalDts, - false, - ); - info.lastSample = new SampleInfo(latest.dts, latest.pts, latest.duration, latest.originalDts, false); - if (!this._isLive) { - this._audioSegmentInfoList.append(info); - } - - track.samples = mp4Samples; - track.sequenceNumber++; - - let moofbox = null; - - if (mpegRawTrack) { - // Generate empty buffer, because useless for raw mpeg - moofbox = new Uint8Array(); - } else { - // Generate moof for fmp4 segment - moofbox = MP4.moof(track, firstDts); - } - - track.samples = []; - track.length = 0; - - let segment = { - type: 'audio', - data: this._mergeBoxes(moofbox, mdatbox).buffer, - sampleCount: mp4Samples.length, - info: info, - }; - - if (mpegRawTrack && firstSegmentAfterSeek) { - // For MPEG audio stream in MSE, if seeking occurred, before appending new buffer - // We need explicitly set timestampOffset to the desired point in timeline for mpeg SourceBuffer. - segment.timestampOffset = firstDts; - } - - this._onMediaSegment('audio', segment); - } - - _remuxVideo(videoTrack) { - if (this._videoMeta == null) { - return; - } - - let track = videoTrack; - let samples = track.samples; - let dtsCorrection = undefined; - let firstDts = -1, - lastDts = -1; - let firstPts = -1, - lastPts = -1; - - if (!samples || samples.length === 0) { - return; - } - - let offset = 8; - let mdatBytes = 8 + videoTrack.length; - let mdatbox = new Uint8Array(mdatBytes); - mdatbox[0] = (mdatBytes >>> 24) & 0xff; - mdatbox[1] = (mdatBytes >>> 16) & 0xff; - mdatbox[2] = (mdatBytes >>> 8) & 0xff; - mdatbox[3] = mdatBytes & 0xff; - mdatbox.set(MP4.types.mdat, 4); - - let firstSampleOriginalDts = samples[0].dts - this._dtsBase; - - // calculate dtsCorrection - if (this._videoNextDts) { - dtsCorrection = firstSampleOriginalDts - this._videoNextDts; - } else { - // this._videoNextDts == undefined - if (this._videoSegmentInfoList.isEmpty()) { - dtsCorrection = 0; - } else { - let lastSample = this._videoSegmentInfoList.getLastSampleBefore(firstSampleOriginalDts); - if (lastSample != null) { - let distance = firstSampleOriginalDts - (lastSample.originalDts + lastSample.duration); - if (distance <= 3) { - distance = 0; - } - let expectedDts = lastSample.dts + lastSample.duration + distance; - dtsCorrection = firstSampleOriginalDts - expectedDts; - } else { - // lastSample == null, cannot found - dtsCorrection = 0; - } - } - } - - let info = new MediaSegmentInfo(); - let mp4Samples = []; - - // Correct dts for each sample, and calculate sample duration. Then output to mp4Samples - for (let i = 0; i < samples.length; i++) { - let sample = samples[i]; - let originalDts = sample.dts - this._dtsBase; - let isKeyframe = sample.isKeyframe; - let dts = originalDts - dtsCorrection; - let cts = sample.cts; - let pts = dts + cts; - - if (firstDts === -1) { - firstDts = dts; - firstPts = pts; - } - - let sampleDuration = 0; - - if (i !== samples.length - 1) { - let nextDts = samples[i + 1].dts - this._dtsBase - dtsCorrection; - sampleDuration = nextDts - dts; - } else { - // the last sample - if (mp4Samples.length >= 1) { - // use second last sample duration - sampleDuration = mp4Samples[mp4Samples.length - 1].duration; - } else { - // the only one sample, use reference sample duration - sampleDuration = Math.floor(this._videoMeta.refSampleDuration); - } - } - - if (isKeyframe) { - let syncPoint = new SampleInfo(dts, pts, sampleDuration, sample.dts, true); - syncPoint.fileposition = sample.fileposition; - info.appendSyncPoint(syncPoint); - } - - mp4Samples.push({ - dts: dts, - pts: pts, - cts: cts, - units: sample.units, - size: sample.length, - isKeyframe: isKeyframe, - duration: sampleDuration, - originalDts: originalDts, - flags: { - isLeading: 0, - dependsOn: isKeyframe ? 2 : 1, - isDependedOn: isKeyframe ? 1 : 0, - hasRedundancy: 0, - isNonSync: isKeyframe ? 0 : 1, - }, - }); - } - - // Write samples into mdatbox - for (let i = 0; i < mp4Samples.length; i++) { - let units = mp4Samples[i].units; - while (units.length) { - let unit = units.shift(); - let data = unit.data; - mdatbox.set(data, offset); - offset += data.byteLength; - } - } - - let latest = mp4Samples[mp4Samples.length - 1]; - lastDts = latest.dts + latest.duration; - lastPts = latest.pts + latest.duration; - this._videoNextDts = lastDts; - - // fill media segment info & add to info list - info.beginDts = firstDts; - info.endDts = lastDts; - info.beginPts = firstPts; - info.endPts = lastPts; - info.originalBeginDts = mp4Samples[0].originalDts; - info.originalEndDts = latest.originalDts + latest.duration; - info.firstSample = new SampleInfo( - mp4Samples[0].dts, - mp4Samples[0].pts, - mp4Samples[0].duration, - mp4Samples[0].originalDts, - mp4Samples[0].isKeyframe, - ); - info.lastSample = new SampleInfo( - latest.dts, - latest.pts, - latest.duration, - latest.originalDts, - latest.isKeyframe, - ); - if (!this._isLive) { - this._videoSegmentInfoList.append(info); - } - - track.samples = mp4Samples; - track.sequenceNumber++; - - // workaround for chrome < 50: force first sample as a random access point - // see https://bugs.chromium.org/p/chromium/issues/detail?id=229412 - if (this._forceFirstIDR) { - let flags = mp4Samples[0].flags; - flags.dependsOn = 2; - flags.isNonSync = 0; - } - - let moofbox = MP4.moof(track, firstDts); - track.samples = []; - track.length = 0; - - this._onMediaSegment('video', { - type: 'video', - data: this._mergeBoxes(moofbox, mdatbox).buffer, - sampleCount: mp4Samples.length, - info: info, - }); - } - - _mergeBoxes(moof, mdat) { - let result = new Uint8Array(moof.byteLength + mdat.byteLength); - result.set(moof, 0); - result.set(mdat, moof.byteLength); - return result; - } -} - -export default MP4Remuxer; diff --git a/src/flv.js/tsconfig.json b/src/flv.js/tsconfig.json deleted file mode 100644 index c05aa28..0000000 --- a/src/flv.js/tsconfig.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "../tsconfig-base.json", - "include": [ - "**/*" - ], - "references": [] -} \ No newline at end of file diff --git a/src/flv.js/utils/browser.js b/src/flv.js/utils/browser.js deleted file mode 100644 index 75c9839..0000000 --- a/src/flv.js/utils/browser.js +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright (C) 2016 Bilibili. All Rights Reserved. - * - * @author zheng qian - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -let Browser = {}; - -function detect() { - // modified from jquery-browser-plugin - - let ua = self.navigator.userAgent.toLowerCase(); - - let match = - /(edge)\/([\w.]+)/.exec(ua) || - /(opr)[\/]([\w.]+)/.exec(ua) || - /(chrome)[ \/]([\w.]+)/.exec(ua) || - /(iemobile)[\/]([\w.]+)/.exec(ua) || - /(version)(applewebkit)[ \/]([\w.]+).*(safari)[ \/]([\w.]+)/.exec(ua) || - /(webkit)[ \/]([\w.]+).*(version)[ \/]([\w.]+).*(safari)[ \/]([\w.]+)/.exec(ua) || - /(webkit)[ \/]([\w.]+)/.exec(ua) || - /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) || - /(msie) ([\w.]+)/.exec(ua) || - (ua.indexOf('trident') >= 0 && /(rv)(?::| )([\w.]+)/.exec(ua)) || - (ua.indexOf('compatible') < 0 && /(firefox)[ \/]([\w.]+)/.exec(ua)) || - []; - - let platform_match = - /(ipad)/.exec(ua) || - /(ipod)/.exec(ua) || - /(windows phone)/.exec(ua) || - /(iphone)/.exec(ua) || - /(kindle)/.exec(ua) || - /(android)/.exec(ua) || - /(windows)/.exec(ua) || - /(mac)/.exec(ua) || - /(linux)/.exec(ua) || - /(cros)/.exec(ua) || - []; - - let matched = { - browser: match[5] || match[3] || match[1] || '', - version: match[2] || match[4] || '0', - majorVersion: match[4] || match[2] || '0', - platform: platform_match[0] || '', - }; - - let browser = {}; - if (matched.browser) { - browser[matched.browser] = true; - - let versionArray = matched.majorVersion.split('.'); - browser.version = { - major: parseInt(matched.majorVersion, 10), - string: matched.version, - }; - if (versionArray.length > 1) { - browser.version.minor = parseInt(versionArray[1], 10); - } - if (versionArray.length > 2) { - browser.version.build = parseInt(versionArray[2], 10); - } - } - - if (matched.platform) { - browser[matched.platform] = true; - } - - if (browser.chrome || browser.opr || browser.safari) { - browser.webkit = true; - } - - // MSIE. IE11 has 'rv' identifer - if (browser.rv || browser.iemobile) { - if (browser.rv) { - delete browser.rv; - } - let msie = 'msie'; - matched.browser = msie; - browser[msie] = true; - } - - // Microsoft Edge - if (browser.edge) { - delete browser.edge; - let msedge = 'msedge'; - matched.browser = msedge; - browser[msedge] = true; - } - - // Opera 15+ - if (browser.opr) { - let opera = 'opera'; - matched.browser = opera; - browser[opera] = true; - } - - // Stock android browsers are marked as Safari - if (browser.safari && browser.android) { - let android = 'android'; - matched.browser = android; - browser[android] = true; - } - - browser.name = matched.browser; - browser.platform = matched.platform; - - for (let key in Browser) { - if (Browser.hasOwnProperty(key)) { - delete Browser[key]; - } - } - Object.assign(Browser, browser); -} - -detect(); - -export default Browser; diff --git a/src/flv.js/utils/exception.js b/src/flv.js/utils/exception.js deleted file mode 100644 index e349bcf..0000000 --- a/src/flv.js/utils/exception.js +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2016 Bilibili. All Rights Reserved. - * - * @author zheng qian - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -export class RuntimeException { - constructor(message) { - this._message = message; - } - - get name() { - return 'RuntimeException'; - } - - get message() { - return this._message; - } - - toString() { - return this.name + ': ' + this.message; - } -} - -export class IllegalStateException extends RuntimeException { - constructor(message) { - super(message); - } - - get name() { - return 'IllegalStateException'; - } -} - -export class InvalidArgumentException extends RuntimeException { - constructor(message) { - super(message); - } - - get name() { - return 'InvalidArgumentException'; - } -} - -export class NotImplementedException extends RuntimeException { - constructor(message) { - super(message); - } - - get name() { - return 'NotImplementedException'; - } -} diff --git a/src/flv.js/utils/logger.js b/src/flv.js/utils/logger.js deleted file mode 100644 index e340464..0000000 --- a/src/flv.js/utils/logger.js +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (C) 2016 Bilibili. All Rights Reserved. - * - * @author zheng qian - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import EventEmitter from 'events'; - -class Log { - static e(tag, msg) { - if (!tag || Log.FORCE_GLOBAL_TAG) tag = Log.GLOBAL_TAG; - - let str = `[${tag}] > ${msg}`; - - if (Log.ENABLE_CALLBACK) { - Log.emitter.emit('log', 'error', str); - } - - if (!Log.ENABLE_ERROR) { - return; - } - - if (console.error) { - console.error(str); - } else if (console.warn) { - console.warn(str); - } else { - console.log(str); - } - } - - static i(tag, msg) { - if (!tag || Log.FORCE_GLOBAL_TAG) tag = Log.GLOBAL_TAG; - - let str = `[${tag}] > ${msg}`; - - if (Log.ENABLE_CALLBACK) { - Log.emitter.emit('log', 'info', str); - } - - if (!Log.ENABLE_INFO) { - return; - } - - if (console.info) { - console.info(str); - } else { - console.log(str); - } - } - - static w(tag, msg) { - if (!tag || Log.FORCE_GLOBAL_TAG) tag = Log.GLOBAL_TAG; - - let str = `[${tag}] > ${msg}`; - - if (Log.ENABLE_CALLBACK) { - Log.emitter.emit('log', 'warn', str); - } - - if (!Log.ENABLE_WARN) { - return; - } - - if (console.warn) { - console.warn(str); - } else { - console.log(str); - } - } - - static d(tag, msg) { - if (!tag || Log.FORCE_GLOBAL_TAG) tag = Log.GLOBAL_TAG; - - let str = `[${tag}] > ${msg}`; - - if (Log.ENABLE_CALLBACK) { - Log.emitter.emit('log', 'debug', str); - } - - if (!Log.ENABLE_DEBUG) { - return; - } - - if (console.debug) { - console.debug(str); - } else { - console.log(str); - } - } - - static v(tag, msg) { - if (!tag || Log.FORCE_GLOBAL_TAG) tag = Log.GLOBAL_TAG; - - let str = `[${tag}] > ${msg}`; - - if (Log.ENABLE_CALLBACK) { - Log.emitter.emit('log', 'verbose', str); - } - - if (!Log.ENABLE_VERBOSE) { - return; - } - - console.log(str); - } -} - -Log.GLOBAL_TAG = 'flv.js'; -Log.FORCE_GLOBAL_TAG = false; -Log.ENABLE_ERROR = true; -Log.ENABLE_INFO = true; -Log.ENABLE_WARN = true; -Log.ENABLE_DEBUG = true; -Log.ENABLE_VERBOSE = true; - -Log.ENABLE_CALLBACK = false; - -Log.emitter = new EventEmitter(); - -export default Log; diff --git a/src/flv.js/utils/logging-control.js b/src/flv.js/utils/logging-control.js deleted file mode 100644 index e666c9c..0000000 --- a/src/flv.js/utils/logging-control.js +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright (C) 2016 Bilibili. All Rights Reserved. - * - * @author zheng qian - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import EventEmitter from 'events'; -import Log from './logger.js'; - -class LoggingControl { - static get forceGlobalTag() { - return Log.FORCE_GLOBAL_TAG; - } - - static set forceGlobalTag(enable) { - Log.FORCE_GLOBAL_TAG = enable; - LoggingControl._notifyChange(); - } - - static get globalTag() { - return Log.GLOBAL_TAG; - } - - static set globalTag(tag) { - Log.GLOBAL_TAG = tag; - LoggingControl._notifyChange(); - } - - static get enableAll() { - return Log.ENABLE_VERBOSE && Log.ENABLE_DEBUG && Log.ENABLE_INFO && Log.ENABLE_WARN && Log.ENABLE_ERROR; - } - - static set enableAll(enable) { - Log.ENABLE_VERBOSE = enable; - Log.ENABLE_DEBUG = enable; - Log.ENABLE_INFO = enable; - Log.ENABLE_WARN = enable; - Log.ENABLE_ERROR = enable; - LoggingControl._notifyChange(); - } - - static get enableDebug() { - return Log.ENABLE_DEBUG; - } - - static set enableDebug(enable) { - Log.ENABLE_DEBUG = enable; - LoggingControl._notifyChange(); - } - - static get enableVerbose() { - return Log.ENABLE_VERBOSE; - } - - static set enableVerbose(enable) { - Log.ENABLE_VERBOSE = enable; - LoggingControl._notifyChange(); - } - - static get enableInfo() { - return Log.ENABLE_INFO; - } - - static set enableInfo(enable) { - Log.ENABLE_INFO = enable; - LoggingControl._notifyChange(); - } - - static get enableWarn() { - return Log.ENABLE_WARN; - } - - static set enableWarn(enable) { - Log.ENABLE_WARN = enable; - LoggingControl._notifyChange(); - } - - static get enableError() { - return Log.ENABLE_ERROR; - } - - static set enableError(enable) { - Log.ENABLE_ERROR = enable; - LoggingControl._notifyChange(); - } - - static getConfig() { - return { - globalTag: Log.GLOBAL_TAG, - forceGlobalTag: Log.FORCE_GLOBAL_TAG, - enableVerbose: Log.ENABLE_VERBOSE, - enableDebug: Log.ENABLE_DEBUG, - enableInfo: Log.ENABLE_INFO, - enableWarn: Log.ENABLE_WARN, - enableError: Log.ENABLE_ERROR, - enableCallback: Log.ENABLE_CALLBACK, - }; - } - - static applyConfig(config) { - Log.GLOBAL_TAG = config.globalTag; - Log.FORCE_GLOBAL_TAG = config.forceGlobalTag; - Log.ENABLE_VERBOSE = config.enableVerbose; - Log.ENABLE_DEBUG = config.enableDebug; - Log.ENABLE_INFO = config.enableInfo; - Log.ENABLE_WARN = config.enableWarn; - Log.ENABLE_ERROR = config.enableError; - Log.ENABLE_CALLBACK = config.enableCallback; - } - - static _notifyChange() { - let emitter = LoggingControl.emitter; - - if (emitter.listenerCount('change') > 0) { - let config = LoggingControl.getConfig(); - emitter.emit('change', config); - } - } - - static registerListener(listener) { - LoggingControl.emitter.addListener('change', listener); - } - - static removeListener(listener) { - LoggingControl.emitter.removeListener('change', listener); - } - - static addLogListener(listener) { - Log.emitter.addListener('log', listener); - if (Log.emitter.listenerCount('log') > 0) { - Log.ENABLE_CALLBACK = true; - LoggingControl._notifyChange(); - } - } - - static removeLogListener(listener) { - Log.emitter.removeListener('log', listener); - if (Log.emitter.listenerCount('log') === 0) { - Log.ENABLE_CALLBACK = false; - LoggingControl._notifyChange(); - } - } -} - -LoggingControl.emitter = new EventEmitter(); - -export default LoggingControl; diff --git a/src/flv.js/utils/polyfill.js b/src/flv.js/utils/polyfill.js deleted file mode 100644 index 7c8fc2b..0000000 --- a/src/flv.js/utils/polyfill.js +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) 2016 Bilibili. All Rights Reserved. - * - * @author zheng qian - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -class Polyfill { - static install() { - // ES6 Object.setPrototypeOf - Object.setPrototypeOf = - Object.setPrototypeOf || - function (obj, proto) { - obj.__proto__ = proto; - return obj; - }; - - // ES6 Object.assign - Object.assign = - Object.assign || - function (target) { - if (target === undefined || target === null) { - throw new TypeError('Cannot convert undefined or null to object'); - } - - let output = Object(target); - for (let i = 1; i < arguments.length; i++) { - let source = arguments[i]; - if (source !== undefined && source !== null) { - for (let key in source) { - if (source.hasOwnProperty(key)) { - output[key] = source[key]; - } - } - } - } - return output; - }; - - // ES6 Promise (missing support in IE11) - // if (typeof self.Promise !== 'function') { - // require('promise-polyfill/dist/polyfill'); - // } - } -} - -Polyfill.install(); - -export default Polyfill; diff --git a/src/flv.js/utils/utf8-conv.js b/src/flv.js/utils/utf8-conv.js deleted file mode 100644 index 84425e5..0000000 --- a/src/flv.js/utils/utf8-conv.js +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (C) 2016 Bilibili. All Rights Reserved. - * - * This file is derived from C++ project libWinTF8 (https://github.com/m13253/libWinTF8) - * @author zheng qian - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -function checkContinuation(uint8array, start, checkLength) { - let array = uint8array; - if (start + checkLength < array.length) { - while (checkLength--) { - if ((array[++start] & 0xc0) !== 0x80) return false; - } - return true; - } else { - return false; - } -} - -function decodeUTF8(uint8array) { - let out = []; - let input = uint8array; - let i = 0; - let length = uint8array.length; - - while (i < length) { - if (input[i] < 0x80) { - out.push(String.fromCharCode(input[i])); - ++i; - continue; - } else if (input[i] < 0xc0) { - // fallthrough - } else if (input[i] < 0xe0) { - if (checkContinuation(input, i, 1)) { - let ucs4 = ((input[i] & 0x1f) << 6) | (input[i + 1] & 0x3f); - if (ucs4 >= 0x80) { - out.push(String.fromCharCode(ucs4 & 0xffff)); - i += 2; - continue; - } - } - } else if (input[i] < 0xf0) { - if (checkContinuation(input, i, 2)) { - let ucs4 = ((input[i] & 0xf) << 12) | ((input[i + 1] & 0x3f) << 6) | (input[i + 2] & 0x3f); - if (ucs4 >= 0x800 && (ucs4 & 0xf800) !== 0xd800) { - out.push(String.fromCharCode(ucs4 & 0xffff)); - i += 3; - continue; - } - } - } else if (input[i] < 0xf8) { - if (checkContinuation(input, i, 3)) { - let ucs4 = - ((input[i] & 0x7) << 18) | - ((input[i + 1] & 0x3f) << 12) | - ((input[i + 2] & 0x3f) << 6) | - (input[i + 3] & 0x3f); - if (ucs4 > 0x10000 && ucs4 < 0x110000) { - ucs4 -= 0x10000; - out.push(String.fromCharCode((ucs4 >>> 10) | 0xd800)); - out.push(String.fromCharCode((ucs4 & 0x3ff) | 0xdc00)); - i += 4; - continue; - } - } - } - out.push(String.fromCharCode(0xfffd)); - ++i; - } - - return out.join(''); -} - -export default decodeUTF8; diff --git a/src/flv.js/utils/utils.js b/src/flv.js/utils/utils.js deleted file mode 100644 index 5daceb7..0000000 --- a/src/flv.js/utils/utils.js +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (C) 2016 Bilibili. All Rights Reserved. - * - * @author tqr - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -class Utils { - static cloneDeep(obj, deep) { - if (obj === null || typeof obj !== 'object') { - return obj; - } - let target; - - if (Utils.isTypedArray(obj) && typeof obj.slice === 'function') { - target = obj.slice(); - return target; - } - - target = Array.isArray(obj) ? [] : {}; - for (const name in obj) { - if (Object.prototype.hasOwnProperty.call(obj, name)) { - const value = obj[name]; - if (deep) { - if (typeof value === 'object') { - target[name] = this.cloneDeep(value, deep); - } else { - target[name] = value; - } - } else { - target[name] = value; - } - } - } - return target; - } - - static isTypedArray(obj) { - return !!(obj && obj.buffer instanceof ArrayBuffer && obj.BYTES_PER_ELEMENT); - } - - static downloadFile(fileName, contentOrPath) { - let aLink = document.createElement('a'), - isData = contentOrPath.slice && contentOrPath.slice(0, 5) === 'data:', - isPath = contentOrPath.lastIndexOf && contentOrPath.lastIndexOf('.') > -1; - - aLink.download = fileName; - aLink.style.display = 'none'; - - aLink.href = isPath || isData ? contentOrPath : URL.createObjectURL(new Blob([contentOrPath])); - document.body.appendChild(aLink); - aLink.click(); - document.body.removeChild(aLink); - } - - static concatArrayBuffer(buffer1, buffer2) { - if (!buffer1) { - return buffer2; - } else if (!buffer2) { - return buffer1; - } - - let tmp = new Uint8Array(buffer1.byteLength + buffer2.byteLength); - tmp.set(new Uint8Array(buffer1), 0); - tmp.set(new Uint8Array(buffer2), buffer1.byteLength); - return tmp.buffer; - } - - static concatUint8Array(array1, array2) { - if (!array1) { - return array2; - } else if (!array2) { - return array1; - } - - let tmp = new Uint8Array(array1.byteLength + array2.byteLength); - tmp.set(array1, 0); - tmp.set(array2, array1.byteLength); - return tmp; - } -} - -export default Utils; diff --git a/src/player/style/area/control/next.css b/src/index.ts similarity index 100% rename from src/player/style/area/control/next.css rename to src/index.ts diff --git a/src/interface/DirectoryPicker.d.ts b/src/interface/DirectoryPicker.d.ts new file mode 100644 index 0000000..7885e64 --- /dev/null +++ b/src/interface/DirectoryPicker.d.ts @@ -0,0 +1,15 @@ +/** + * 用于显示一个目录选择器,以允许用户选择一个目录。 + * + * @param options 选项对象 + */ +declare function showDirectoryPicker(options?: IOpenDirectoryPickerOptions): Promise; + +declare interface IOpenDirectoryPickerOptions { + /** 通过指定 ID,浏览器可以为不同的 ID 记住不同的目录。如果相同的 ID 用于另一个选择器,则该选择器将在同一目录中打开。 */ + id?: string; + /** 默认为 "read",用于只读访问,或 "readwrite" 用于读写访问。 */ + mode?: 'read' | 'readwrite'; + /** 一个 FileSystemHandle 对象或者代表某个众所周知的目录的字符串,用于指定选择器的起始目录。 */ + startIn?: FileSystemHandle | string; +} \ No newline at end of file diff --git a/src/interface/HTMLSelectListElement.d.ts b/src/interface/HTMLSelectListElement.d.ts deleted file mode 100644 index 43c73fb..0000000 --- a/src/interface/HTMLSelectListElement.d.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * @deprecated - * 关于可定制按热度排序 -`); - - private $interaction = Element.add('div', { class: 'interaction', 'data-num': '-', title: 'B站已不提供页码数,估算数据仅供参考!' }, this.$header); - - private $send = Element.add('div', { class: 'comment-send' }, this.$body); - - private $userFace = Element.add('div', { class: 'user-face' }, this.$send, ``); - - private $ipt = Element.add('textarea', { class: 'ipt-txt', cols: "80", rows: "5", placeholder: "发一条友善的评论" }, this.$send); - - private $submit = Element.add('button', { class: 'comment-submit' }, this.$send, '发表评论'); - - private $emote = Element.add('button', { class: 'comment-emote' }, this.$send, `${svg_emoji}表情`); - - private $emotePopover = Element.add('div', { class: 'emote-popover', popover: 'auto' }, this.$send); - - private $emoteTitle = Element.add('div', { class: 'emoji-title' }, this.$emotePopover); - - private $emoteWrap = Element.add('form', { class: 'emoji-wrap' }, this.$emotePopover); - - private $emoteTabs = Element.add('form', { class: 'emoji-tabs' }, this.$emotePopover); - - private $list = Element.add('div', { class: 'comment-list' }, this.$body); - - private $paging = Element.add('div', { class: 'comment-paging' }, this.$body); - - private $imagePopover = Element.add('div', { class: 'comment-popover', popover: 'auto' }, this); - - private $imageCon = Element.add('div', { class: 'image-con' }, this.$imagePopover); - - #oid: string | number = 0; - - get oid() { - return this.#oid; - } - - set oid(v) { - if (v !== this.#oid) { - this.identify(); - this.#oid = v; - this.init(); - } - } - - #pn = 1; - - get pn() { - return this.#pn; - } - - set pn(v) { - this.#pn = v; - this.init(); - } - - #sort: 0 | 2 = 2; - - get sort() { - return this.#sort; - } - - set sort(v) { - this.#sort = v; - this.init(); - } - - #type = 1; - - get type() { - return this.#type; - } - - set type(v) { - this.#type = v; - this.init(); - } - - seek_rpid?: number; - - #emote?: Awaited>; - - #aid = 0; - - #ssid = 0; - - #epid = 0; - - constructor() { - super(); - this.insertAdjacentHTML('beforeend', ``); - this.$ipt.required = true; - this.$emote.popoverTargetElement = this.$emotePopover; - - new IdCard(); - - this.$tabs.addEventListener('change', () => { - const d = new FormData(this.$tabs); - this.#pn = 1; - this.sort = <0>+[...d.values()][0]; - }); - this.$interaction.addEventListener('click', this.onPageClick); - this.$paging.addEventListener('click', this.onPageClick); - this.addEventListener('click', ({ target, screenX, screenY }) => { - if (target instanceof HTMLImageElement && target.classList.contains('topopover')) { - // 评论图片放大 - this.$imageCon.innerHTML = ``; - this.$imagePopover.style.transformOrigin = `${screenX}px ${screenY}px`; - this.$imagePopover.showPopover(); - } else if (target instanceof HTMLSpanElement && target.classList.contains('reply')) { - // 回复评论 - const { root, parent, uname } = target.dataset; - if (root && parent && uname) { - const id = crypto.randomUUID(); - target.style.setProperty('anchor-name', `--${id}`); - this.$send.style.setProperty('position-anchor', `--${id}`); - this.$send.popover = 'auto'; - this.$send.showPopover(); - - this.$ipt.placeholder = `回复 @${uname} :`; - this.$ipt.dataset.root = root; - this.$ipt.dataset.parent = parent; - } - } else if (target instanceof HTMLSpanElement && target.classList.contains('like-num')) { - const csrf = cookie.get('bili_jct'); - const { rpid } = target.dataset; - if (csrf && rpid) { - action(csrf, this.#oid, rpid, target.classList.contains('liked') ? 0 : 1, this.#type) - .then(d => { - if (d === 0) { - (target.lastChild).data = target.classList.toggle('liked') ? +(target.lastChild).data + 1 : +(target.lastChild).data - 1; - } - }) - } - } - }); - this.$imageCon.addEventListener('click', e => { - const { target } = e; - if (target === this.$imageCon) { - this.$imagePopover.hidePopover(); - } - }); - this.$send.addEventListener('toggle', () => { - if (!this.$send.matches(":popover-open")) { - this.$send.popover = null; - this.$send.style.insetInlineStart = ''; - this.$send.style.insetBlockStart = ''; - this.$send.style.removeProperty('position-anchor'); - this.$ipt.placeholder = `发一条友善的评论`; - this.$ipt.dataset.root = ''; - this.$ipt.dataset.parent = ''; - this.$ipt.value = ''; - } - }); - this.$submit.addEventListener('click', () => { - if (this.$ipt.checkValidity()) { - const csrf = cookie.get('bili_jct'); - const { root, parent } = this.$ipt.dataset; - if (csrf) { - replyAdd({ csrf, oid: this.#oid, message: root && parent ? this.$ipt.placeholder + this.$ipt.value : this.$ipt.value, type: this.#type, root, parent }) - .then(({ code, data }) => { - if (code === 0) { - if (root && parent) { - for (const d of this.$list.childNodes) { - if (d instanceof CommentItem && d.dataset.rpid === parent) { - d.$reply.insertAdjacentElement('afterbegin', new SubReply(data.reply, 0)); - break; - } - } - } else { - this.$list.insertAdjacentElement('afterbegin', new CommentItem(data.reply, 0)) - } - } - }) - .finally(() => { - this.$ipt.value = ''; - root && parent && this.$send.hidePopover(); - }) - } - } - }); - this.$emotePopover.addEventListener('toggle', () => { - if (this.$emotePopover.matches(":popover-open")) { - this.#emote || emoteWeb().then(d => { - this.#emote = d; - const id = crypto.randomUUID(); - this.$emoteTabs.innerHTML = https(d.map((d, i) => ``).join('')); - (this.$emoteTabs.firstElementChild)?.click(); - }); - } - }, { once: true }); - this.$emoteWrap.addEventListener('change', () => { - this.$ipt.focus(); - const d = new FormData(this.$emoteWrap); - this.$ipt.setRangeText([...d.values()][0], this.$ipt.selectionStart, this.$ipt.selectionEnd, 'end'); - }); - this.$emoteTabs.addEventListener('change', () => { - this.$ipt.focus(); - const d = new FormData(this.$emoteTabs); - const i = +[...d.values()][0]; - if (i >= 0 && this.#emote) { - const emote = this.#emote[i]; - this.$emoteTitle.textContent = emote.text; - const id = crypto.randomUUID(); - this.$emoteWrap.innerHTML = https(emote.emote.map(d => ``).join('')); - } - }); - - nav().then(d => { - if (d.isLogin) { - this.$userFace.innerHTML = https(`${d.pendant.image ? `` : ''}${d.officialVerify.desc ? `` : ''}`); - } else { - this.$ipt.placeholder = '请登录后评论'; - this.$ipt.disabled = true; - this.$submit.disabled = true; - this.$emote.disabled = true; - } - }) - } - - async navigate(router: ROUTER, url: URL | Location) { - url instanceof Location && (url = new URL(url.href)); - switch (router) { - case ROUTER.AV: { - const path = url.pathname.split('/'); - switch (true) { - case /^av\d+$/i.test(path[2]): { - this.#aid = +path[2].slice(2); - break; - } - case /^bv1[a-z0-9]{9}$/i.test(path[2]): { - this.#aid = +AV.fromBV(path[2]); - break; - } - } - if (this.#aid) { - this.oid = this.#aid; - } else { - console.error('解析av号出错~'); - } - break; - } - case ROUTER.BANGUMI: { - const path = url.pathname.split('/'); - switch (true) { - case /^ss\d+$/i.test(path[3]): { - this.#ssid = +path[3].slice(2); - break; - } - case /^ep\d+$/i.test(path[3]): { - this.#epid = +path[3].slice(2); - break; - } - } - if (this.#ssid || this.#epid) { - pgcAppSeason(this.#ssid ? { season_id: this.#ssid } : { ep_id: this.#epid }) - .then(async season => { - this.#ssid || (this.#ssid = season.season_id); - season.modules.forEach(d => { - switch (d.style) { - case "positive": - case "section": { - this.#epid || (this.#epid = d.data.episodes[0]?.ep_id); - if (this.#epid) { - const ep = d.data.episodes.find(d => d.ep_id === this.#epid); - if (ep) { - this.#aid = ep.aid; - } - } - break; - } - } - }); - if (!this.#aid && this.#ssid) { - const d = await pgcSection(this.#ssid); - const eps = d.main_section.episodes.concat(...d.section.map(d => d.episodes)); - const ep = this.#epid ? eps.find(d => d.id === this.#epid) : eps[0]; - if (ep) { - this.#aid = ep.aid; - } - } - this.#aid && (this.oid = this.#aid); - }); - } else { - console.error('解析Bangumi出错~'); - } - break; - } - case ROUTER.TOVIEW: { - const path = url.hash.split('/'); - switch (true) { - case /^av\d+$/i.test(path[1]): { - this.#aid = +path[1].slice(2); - break; - } - case /^bv1[a-z0-9]{9}$/i.test(path[1]): { - this.#aid = +AV.fromBV(path[1]); - break; - } - } - toviewWeb().then(toview => { - this.#aid || (toview.length && (this.#aid = toview[0].aid)); - if (this.#aid) { - this.oid = this.#aid; - } else { - console.error('解析稍后再看出错~'); - } - }) - break; - } - case ROUTER.MEDIALIST: { - const path = url.pathname.split('/'); - const ml = +path[2].slice(2); - if (ml) { - favResourceList(ml).then(({ medias }) => { - this.#aid = Number(url.searchParams.get('aid')) || medias[0].id; - if (this.#aid) { - this.oid = this.#aid; - } else { - console.error('解析播放列表出错~'); - } - }) - } else { - console.error('解析播放列表出错~'); - } - break; - } - } - } - - init( - oid?: number | string, - pn?: number, - sort?: 0 | 2, - type?: number, - seek_rpid?: number, - ) { - oid && (this.#oid = oid); - pn && (this.#pn = pn); - sort === undefined || (this.#sort = sort); - type && (this.#type = type); - seek_rpid && (this.seek_rpid = seek_rpid); - if (this.#oid) { - if (this.seek_rpid) { - const { seek_rpid } = this; - delete this.seek_rpid; - replyMain(this.oid, undefined, seek_rpid) - .then(d => { - if (d.replies) { - this.$list.replaceChildren(); - d.top.upper && d.replies.unshift(d.top.upper); - d.replies.forEach(k => { - this.$list.appendChild(new CommentItem(k, d.upper?.mid)); - }); - } - }).finally(() => { - document.querySelector(`[data-rpid="${seek_rpid}"]`)?.scrollIntoView({ behavior: 'smooth' }) - }) - } else { - reply(this.oid, this.pn, this.sort, this.type) - .then(d => { - if (d.replies) { - this.$list.replaceChildren(); - this.pn === 1 && d.upper.top && d.replies.unshift(d.upper.top); - d.replies.forEach(k => { - this.$list.appendChild(new CommentItem(k, d.upper?.mid)); - }); - this.$head.dataset.num = d.page.count; - this.interaction(Math.floor(d.page.count / d.page.size)); - } - }) - } - } - } - - private interaction(pages: number) { - this.$interaction.dataset.num = Math.floor(pages); - if (pages > 1) { - let html = ''; - if (this.pn > 1) { - html += '1'; - } - - if (this.pn - 3 > 1) { - html += `...`; - } - if (this.pn - 2 > 1) { - html += `${this.pn - 2}`; - } - if (this.pn - 1 > 1) { - html += `${this.pn - 1}`; - } - - html += `${this.pn}`; - - if (this.pn + 1 > 1 && this.pn + 1 <= pages) { - html += `${this.pn + 1}`; - } - if (this.pn + 2 > 1 && this.pn + 2 <= pages) { - html += `${this.pn + 2}`; - } - if (this.pn + 3 > 1 && this.pn + 3 <= pages) { - if (this.pn + 3 < pages) { - html += `...`; - } - - html += `${pages}`; - } - - if (this.pn < pages) { - html += ''; - } - - this.$interaction.innerHTML = html; - - this.$paging.innerHTML = html + `共${pages}页,跳至`; - this.$paging.querySelector('input')?.addEventListener('change', e => { - const input = e.target; - const i = Number(input.value); - input.value = ''; - i > 1 && (this.pn = i); - }); - } else { - this.$interaction.replaceChildren(); - this.$paging.replaceChildren(); - } - } - - private onPageClick = (e: MouseEvent) => { - const a = e.target; - if (a instanceof HTMLAnchorElement) { - const i = Number(a.dataset.value); - switch (i) { - case -1: { - this.pn > 1 && this.pn--; - break; - } - case 0: { - this.pn++; - break; - } - default: { - i > 0 && (this.pn = i); - break; - } - } - } - document.querySelector('.tabs-order')?.scrollIntoView(); - } - - private identify = () => { - this.#aid = 0; - this.#ssid = 0; - this.#epid = 0; - this.#oid = 0; - this.#pn = 1; - this.#sort = 2; - this.#type = 1; - (this.$tabs.firstChild!.firstChild).checked = true; - this.$list.replaceChildren(); - this.$paging.replaceChildren(); - } -} - - -//////////////////////////// 全局增强 //////////////////////////// -declare global { - /** 基于哈希消息认证码的一次性口令的密钥 */ - const __BILI_COMMENT_STYLE__: string; -} \ No newline at end of file diff --git a/src/main/bilibili/comment/initComment.ts b/src/main/bilibili/comment/initComment.ts deleted file mode 100644 index 118d303..0000000 --- a/src/main/bilibili/comment/initComment.ts +++ /dev/null @@ -1,10 +0,0 @@ -export interface InitComment { - allowForwardToDynamic?: boolean; - commentType?: string; - jumpReplyId?: number; - oid: string | number; - pageType: number; - scene?: string; - showMaxReplyCount?: number; - theme?: string; -} \ No newline at end of file diff --git a/src/main/bilibili/comment/style/card.css b/src/main/bilibili/comment/style/card.css deleted file mode 100644 index 4a3ea03..0000000 --- a/src/main/bilibili/comment/style/card.css +++ /dev/null @@ -1,166 +0,0 @@ -@scope { - :scope { - background-color: #fff; - box-shadow: 0 0 2px rgba(0, 0, 0, .3); - border-radius: 4px; - inline-size: 375px; - margin: 0; - border: 0; - padding: 0; - font-size: 12px; - inset-area: inline-end span-block-end; - position-try: - flip-block, - flip-inline, - flip-block flip-inline; - position-visibility: anchors-visible; - - &:popover-open, - &:hover { - display: flex; - flex-direction: column; - } - } -} - -.idc-theme-img { - border-start-start-radius: 4px; - border-start-end-radius: 4px; - display: block; - block-size: 120px; - background-size: cover; - background-position: 50%; -} - -.idc-info { - display: flex; - padding-block-start: 7px; - padding-block-end: 10px; - - >.up-cover-components { - flex-shrink: 0; - inline-size: 100px; - display: flex; - justify-content: center; - align-items: flex-start; - - >.avatar { - display: flex; - justify-content: center; - align-items: center; - position: relative; - - >.avatar-face { - inline-size: 60px; - aspect-ratio: 1; - border-radius: 50%; - } - - >.avatar-pendant { - position: absolute; - inline-size: 90px; - aspect-ratio: 1; - } - - &::after { - position: absolute; - padding: .75em; - inset-inline-end: 0; - inset-block-end: 0; - background-size: cover; - } - - &.personal::after { - content: ""; - background-image: url(//i0.hdslb.com/bfs/seed/jinkela/short/user-avatar/personal.svg); - } - - &.business::after { - content: ""; - background-image: url(//i0.hdslb.com/bfs/seed/jinkela/short/user-avatar/business.svg); - } - - &.small-vip::after { - content: ""; - background-image: url(//i0.hdslb.com/bfs/seed/jinkela/short/user-avatar/small-vip.svg); - } - - &.big-vip::after { - content: ""; - background-image: url(//i0.hdslb.com/bfs/seed/jinkela/short/user-avatar/big-vip.svg); - } - } - } - - >.idc-content { - flex: 1; - display: flex; - flex-direction: column; - row-gap: .5em; - color: #6d757a; - - >.idc-name { - display: flex; - align-items: center; - column-gap: .5em; - - >.idc-username { - color: #222; - text-decoration: none; - transition: color .2s ease, background-color .2s ease; - font-size: 14px; - font-weight: bold; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } - - >svg { - max-block-size: 1.5em; - aspect-ratio: 1; - } - - >img { - max-block-size: 1.5em; - } - } - - >.idc-meta { - display: flex; - align-items: center; - column-gap: 20px; - color: #222; - } - - >.idc-auth-description { - color: #222; - } - } -} - -.idc-action { - padding-block-end: 20px; - padding-inline: 20px; - display: flex; - align-items: center; - justify-content: center; - column-gap: 20px; - - >button { - inline-size: 80px; - block-size: 26px; - color: #666; - background-color: #fff; - border-color: #d9d9d9; - border: 1px solid #d9d9d9; - border-radius: 4px; - cursor: pointer; - transition: .2s; - - &:hover { - color: #00a1d6; - background-color: #fff; - border-color: #00a1d6; - } - } -} \ No newline at end of file diff --git a/src/main/bilibili/comment/style/card.d.css.ts b/src/main/bilibili/comment/style/card.d.css.ts deleted file mode 100644 index bc6e12f..0000000 --- a/src/main/bilibili/comment/style/card.d.css.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const css_card: string; -export default css_card; \ No newline at end of file diff --git a/src/main/bilibili/comment/style/index.css b/src/main/bilibili/comment/style/index.css deleted file mode 100644 index 0f6e5ac..0000000 --- a/src/main/bilibili/comment/style/index.css +++ /dev/null @@ -1,482 +0,0 @@ -@import url(./comment.css); - -@font-face { - font-family: 'fanscard'; - src: url('//s1.hdslb.com/bfs/static/jinkela/mall-h5/asserts/fansCard.ttf'); -} - -@scope { - :scope { - padding-block: 30px; - display: flex; - flex-direction: column; - overflow: hidden; - - &.bili-wrapper { - margin-inline: auto; - padding-inline-end: 290px; - inline-size: 980px; - min-inline-size: 980px; - - @media screen and (min-width:1400px) { - - & { - inline-size: 1160px; - } - } - - @media screen and (min-width:2500px) { - - & { - inline-size: 1920px; - } - } - } - - &, - div { - box-sizing: border-box; - } - - a { - outline: none; - text-decoration: none; - } - } -} - -.comment-head { - font-size: 18px; - line-height: 24px; - color: #222; - margin-block-end: 20px; - - &::before { - content: attr(data-num); - margin-inline-end: 10px; - } -} - -.comment { - display: flex; - flex-direction: column; - color: #222; - font-size: 12px; - - .comment-header { - margin-block-end: 24px; - border-block-end: 1px solid #e5e9ef; - display: flex; - justify-content: space-between; - align-items: center; - - .tabs-order { - font-size: 14px; - font-weight: bold; - display: flex; - align-items: center; - column-gap: 16px; - - >label { - cursor: pointer; - border-block-end: 1px solid transparent; - padding-block: 8px; - position: relative; - - &:has(input:checked) { - border-block-end: 1px solid #00a1d6; - color: #00a1d6; - - &::after { - content: ""; - position: absolute; - inset-inline-start: 50%; - inset-block-end: 0; - transform: translateX(-3px); - border-block-end: 3px solid #00a1d6; - border-block-start: 0; - border-inline-start: 3px solid rgba(0, 0, 0, 0); - border-inline-end: 3px solid rgba(0, 0, 0, 0); - box-sizing: border-box; - } - } - - >input { - appearance: none; - } - } - } - - .interaction { - display: flex; - align-items: center; - - &::before { - content: "共" attr(data-num) "页"; - margin-inline-end: 10px; - } - - >a { - margin-inline: 4px; - cursor: pointer; - - &:hover { - color: #00a1d6; - } - - &.on, - &.more { - pointer-events: none; - } - - &.on { - color: #00a1d6; - font-weight: bold; - } - } - } - } - - .comment-send { - margin-block: 10px; - padding-block: 15px; - display: flex; - align-items: center; - - .user-face { - margin-inline: 16px; - position: relative; - display: flex; - justify-content: center; - align-items: center; - - >.face { - inline-size: 48px; - aspect-ratio: 1; - border-radius: 50%; - } - - >.pendant { - position: absolute; - block-size: 72px; - inline-size: 72px; - } - - >.auth { - position: absolute; - inline-size: 20px; - block-size: 20px; - inset-inline-end: 0; - inset-block-start: 0; - background-image: url(//s1.hdslb.com/bfs/static/jinkela/videoplay/asserts/user-auth.png); - background-repeat: no-repeat; - z-index: 3; - - &.o-auth { - background-position: -4px -53px; - } - - &.p-auth { - background-position: -38px -53px; - } - } - } - - .ipt-txt { - box-sizing: border-box; - background-color: #f4f5f7; - border: 1px solid #e5e9ef; - border-radius: 4px; - color: #555; - padding-block: 5px; - padding-inline: 10px; - block-size: 65px; - outline: none; - resize: none; - anchor-name: --ipt-txt; - - &:focus { - background-color: #fff; - border-color: #00a1d6; - } - - &:disabled { - background-color: #e5e9ef; - } - } - - .comment-submit { - block-size: 64px; - inline-size: 70px; - margin-inline-start: 10px; - padding-block: 4px; - padding-inline: 15px; - font-size: 14px; - color: #fff; - border-radius: 4px; - cursor: pointer; - background-color: #00a1d6; - border: 1px solid #00a1d6; - box-sizing: border-box; - transition: 0.1s; - user-select: none; - outline: none; - - &:hover { - background-color: #00b5e5; - border-color: #00b5e5; - } - - &:disabled { - cursor: not-allowed; - color: #b8c0cc; - background-color: #e5e9ef; - border-color: #e5e9ef; - padding: 4px 10px; - - } - } - - .comment-emote { - position: absolute; - font-size: 12px; - background-color: inherit; - padding-inline: 1em; - padding-block: .3em; - inset-inline-start: anchor(--ipt-txt start); - inset-block-start: anchor(--ipt-txt end); - color: #99a2aa; - border: 1px solid #e5e9ef; - border-radius: 4px; - box-shadow: 0px 1px 10px 0 rgba(106, 115, 133, 0.25); - cursor: pointer; - margin-block-start: .5em; - display: flex; - justify-content: center; - align-items: center; - column-gap: .3em; - anchor-name: --comment-emote; - - >svg { - inline-size: 1.5em; - aspect-ratio: 1; - fill: #99a2aa; - } - - &:disabled { - cursor: not-allowed; - color: #b8c0cc; - background-color: #e5e9ef; - border-color: #e5e9ef; - } - - &:hover { - color: #6d757a; - - >svg { - fill: #6d757a; - } - } - } - - .emote-popover { - position: absolute; - inset-inline-start: anchor(--comment-emote start); - inset-block-start: anchor(--comment-emote end); - inline-size: 363px; - margin-block-start: .5em; - border: 1px solid #E5E9EF; - box-shadow: 0 11px 12px 0 rgba(106, 115, 133, 0.3); - border-radius: 8px; - color: #222; - - >.emoji-title { - line-height: 16px; - margin-block-start: 13px; - margin-inline: 15px; - color: #757575; - } - - >.emoji-wrap { - margin-block-start: 6px; - margin-inline: 11px; - block-size: 196px; - overflow-y: auto; - scrollbar-width: thin; - word-break: break-word; - display: flex; - flex-wrap: wrap; - gap: .1em; - - >label { - display: flex; - align-items: center; - justify-content: center; - padding: .3em; - border-radius: 4px; - transition: all 0.2s; - cursor: pointer; - - >img { - inline-size: 24px; - block-size: 24px; - } - - >input { - appearance: none; - } - - &:hover { - background-color: #ddd; - scale: 2; - } - } - } - - >.emoji-tabs { - block-size: 36px; - background-color: #f4f4f4; - display: flex; - overflow-x: auto; - scrollbar-width: thin; - - >label { - inline-size: 58px; - display: flex; - align-items: center; - justify-content: center; - transition: background-color 0.2s; - cursor: pointer; - - >img { - inline-size: 24px; - block-size: 24px; - } - - >input { - appearance: none; - } - - &:has(input:checked) { - background-color: #fff; - } - - &:hover { - background: #e7e7e7; - } - } - } - } - - &[popover] { - inset-area: block-end; - border: 0; - box-shadow: 0 0 20px 3px #0000005c; - border-radius: 10px; - padding-inline: 18px; - padding-block-end: 40px; - } - } - - .comment-list { - padding-block-start: 20px; - } - - .comment-paging { - inline-size: 100%; - position: relative; - - >a { - font-size: 14px; - block-size: 36px; - border-radius: 4px; - min-inline-size: 15px; - margin-inline: 2px; - padding-inline: 10px; - background-color: #fff; - border: solid 1px #ddd; - cursor: pointer; - transition: 0.2s all; - display: inline-flex; - align-items: center; - justify-content: center; - - &.prev, - &.next { - padding-inline: 15px; - } - - &.on, - &.more { - pointer-events: none; - } - - &.more { - border: 0; - } - - &:hover, - &.on { - background-color: #00a1d6; - color: #fff; - border: 1px solid #00a1d6; - } - } - - >.page-jump { - color: #99a2aa; - position: absolute; - inset-inline-end: 0; - - >input { - inline-size: 24px; - block-size: 24px; - margin-inline: 5px; - padding-inline: 10px; - border-radius: 4px; - font-size: 12px; - border: 1px solid #ddd; - text-align: center; - outline: none; - box-shadow: none; - } - } - - &:empty { - display: none; - } - } -} - -.comment-popover { - inline-size: 100vi; - block-size: 100vb; - border: 0; - background: rgba(24, 25, 28, 0.85); - transition: all .5s allow-discrete; - - @starting-style { - scale: 0; - } - - &:not(:popover-open):not(dialog[open]) { - scale: 0; - } - - >.image-con { - inline-size: 100%; - block-size: 100%; - padding-inline: 100px; - display: flex; - justify-content: center; - align-items: center; - overflow-y: auto; - scrollbar-width: none; - cursor: zoom-out; - - >img { - max-inline-size: 100%; - cursor: default; - } - } -} \ No newline at end of file diff --git a/src/main/bilibili/comment/style/sub-reply.css b/src/main/bilibili/comment/style/sub-reply.css deleted file mode 100644 index 9a80a79..0000000 --- a/src/main/bilibili/comment/style/sub-reply.css +++ /dev/null @@ -1,148 +0,0 @@ -.sub-comment-wrap { - padding-block: 10px; - display: flex; - - .reply-face { - flex-shrink: 0; - margin-inline: 8px; - - >img { - inline-size: 24px; - aspect-ratio: 1; - border-radius: 50%; - } - } - - .reply-con { - flex-grow: 1; - inline-size: calc(100% - 34px); - overflow: hidden; - - .reply-user { - font-weight: bold; - column-gap: 8px; - - - .name { - color: #6d757a; - text-wrap: nowrap; - vertical-align: middle; - } - - >img { - font-size: 12px; - block-size: 1em; - vertical-align: middle; - } - - .reply-is-up { - font-size: 9px; - border-radius: 1px; - background-color: #fb7299; - color: #fff; - border-color: #fb7299; - padding-inline: 2px; - font-weight: normal; - vertical-align: middle; - } - - .reply-fans { - font-weight: normal; - font-size: 10px; - display: inline-flex; - align-items: center; - vertical-align: middle; - - .reply-fans-name { - padding-inline: 2px; - border-width: 0.5px; - border-start-start-radius: 1px; - border-end-start-radius: 1px; - } - - .reply-fans-level { - border-width: 0.5px; - border-style: solid; - border-end-end-radius: 1px; - border-start-end-radius: 1px; - } - } - - .reply-nameplate>img { - font-size: 24px; - block-size: 1em; - vertical-align: middle; - } - - .reply-text { - display: inline; - font-size: 14px; - font-weight: normal; - white-space: pre-wrap; - margin-inline-start: 16px; - vertical-align: middle; - - >a { - color: #00a1d6; - } - - >img { - max-inline-size: 100%; - - &.emote { - inline-size: 20px; - aspect-ratio: 1; - vertical-align: text-bottom; - } - } - } - - } - - .reply-info { - color: #99a2aa; - margin-block-start: 6px; - font-size: 12px; - display: flex; - align-items: center; - column-gap: 20px; - - >span { - display: flex; - align-items: center; - - >svg { - inline-size: 12px; - aspect-ratio: 1; - fill: #99a2aa; - margin-inline-end: 5px; - } - - &.reply, - &.dialog { - padding: 5px; - border-radius: 4px; - cursor: pointer; - - &:hover { - color: #00a1d6; - background: #e5e9ef; - } - } - - - &.like-num { - cursor: pointer; - - &:hover>svg { - fill: #00a1d6; - } - } - - &:empty { - display: none; - } - } - } - } -} \ No newline at end of file diff --git a/src/main/bilibili/comment/sub-reply.ts b/src/main/bilibili/comment/sub-reply.ts deleted file mode 100644 index 3d5992b..0000000 --- a/src/main/bilibili/comment/sub-reply.ts +++ /dev/null @@ -1,157 +0,0 @@ -import { IReplies } from "../../../io/com/bilibili/api/x/v2/reply"; -import { cursor } from "../../../io/com/bilibili/api/x/v2/reply/dialog/cursor"; -import svg_like_number from "../../../player/assets/svg/like-number.svg"; -import { customElement } from "../../../utils/Decorator/customElement"; -import { Element } from "../../../utils/element"; -import { Format } from "../../../utils/fomat"; -import { https } from "../../../utils/https"; -import { LEVEL } from "./icon-level"; - -/** 评论楼中楼 */ -@customElement('div') -export class SubReply extends HTMLDivElement { - - /** - * 需要监听变动的属性。 - * 与实例方法`attributeChangedCallback`配合使用。 - * 此字符串序列定义了`attributeChangedCallback`回调时的第一个参数的可能值。 - */ - // static observedAttributes = []; - - /** - * 在属性更改、添加、移除或替换时调用。 - * 需要与静态属性`observedAttributes`配合使用。 - * 此回调的第一个参数在`observedAttributes`数组中定义。 - */ - // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} - - /** 初始化标记 */ - // #inited = false; - - /** 每当元素添加到文档中时调用。 */ - // connectedCallback() {} - - /** 每当元素从文档中移除时调用。 */ - // disconnectedCallback() {} - - /** 每当元素被移动到新文档中时调用。 */ - // adoptedCallback() {} - - constructor(private reply: IReplies, private upid: number) { - super(); - this.classList.add('sub-comment-wrap'); - this.dataset.rpid = reply.rpid_str; - - this.emote(); - this.jump_url(); - this.pictures(); - - this.innerHTML = ` -
-
- ${reply.member.uname} - - ${upid === reply.mid ? 'UP' : ''} - ${reply.member.fans_detail ? `${reply.member.fans_detail.medal_name}${reply.member.fans_detail.level}` : ''} - ${reply.member.nameplate?.image ? `` : ''} -

${reply.content.message}

-
-
- ${reply.floor ? `#${reply.floor}` : ''} - ${Format.eTime(reply.ctime)} - ${reply.reply_control.location || ''} - - ${reply.up_action?.like ? 'UP主觉得很赞' : ''} - ${reply.up_action?.reply ? 'UP主有回复' : ''} - ${reply.label?.content ? `${reply.label.content}` : ''} - 回复 - ${reply.dialog_str && reply.dialog_str !== reply.rpid_str ? `查看对话` : ''} -
-
`; - - this.addEventListener('click', e => { - const { target } = e; - if (target instanceof HTMLSpanElement) { - if (target.classList.contains('dialog')) { - // 处理查看对话 - const { oid, dialog, root } = target.dataset; - oid && dialog && root && cursor(oid, root, dialog).then(d => { - if (d.replies.length) { - const popover = Element.add('div', { class: "reply-box", 'data-loading': '加载更多……' }); - popover.popover = 'auto'; - d.replies.forEach(d => { - popover.appendChild(new SubReply(d, this.upid)); - }); - target.insertAdjacentElement('afterend', popover); - popover.addEventListener('toggle', () => { - popover.matches(":popover-open") || popover.remove(); - }); - popover.showPopover(); - if (popover.scrollHeight - popover.scrollTop < popover.clientHeight * 1.2) { - popover.dataset.loading = ''; - } else { - let { next } = d.cursor; const { max_floor } = d.dialog; - const scrollend = () => { - if (popover.scrollHeight - popover.scrollTop < popover.clientHeight * 1.2) { - if (next < max_floor) { - if (d.replies.length) { - cursor(oid, root, dialog, next).then(d => { - d.replies.forEach(d => { - popover.appendChild(new SubReply(d, this.upid)); - }); - next = d.cursor.next; - }) - } else { - popover.dataset.loading = '一滴也没有啦~'; - popover.removeEventListener('scrollend', scrollend); - } - } else { - popover.dataset.loading = '一滴也没有啦~'; - popover.removeEventListener('scrollend', scrollend); - } - } - } - popover.addEventListener('scrollend', scrollend); - } - } - }); - e.stopPropagation(); - } - } - }); - } - - /** 处理表情 */ - private emote() { - if (this.reply.content.emote) { - Object.entries(this.reply.content.emote).forEach(d => { - this.reply.content.message = this.reply.content.message.replaceAll(d[0], `${d[1].text}`) - }); - } - } - - /** 处理超链接 */ - private jump_url() { - this.at_name_to_mid_str(); - this.reply.content.message = Format.superLink(this.reply.content.message); - } - - /** 处理@ */ - private at_name_to_mid_str() { - if (this.reply.content.at_name_to_mid_str) { - Object.entries(this.reply.content.at_name_to_mid_str).forEach(d => { - this.reply.content.message = this.reply.content.message.replaceAll(`@${d[0]}`, `@${d[0]}`) - }) - } - } - - /** 处理图片 */ - private pictures() { - this.reply.content.pictures?.forEach(d => { - this.reply.content.message += ``; - }); - this.reply.content.rich_text?.note?.images?.forEach(d => { - this.reply.content.message += ``; - }); - } -} \ No newline at end of file diff --git a/src/main/bilibili/desc/index.ts b/src/main/bilibili/desc/index.ts deleted file mode 100644 index 50d58c1..0000000 --- a/src/main/bilibili/desc/index.ts +++ /dev/null @@ -1,324 +0,0 @@ -import { ROUTER } from ".."; -import { pgcAppSeason } from "../../../io/com/bilibili/api/pgc/view/v2/app/season"; -import { IEpisode, pgcSection } from "../../../io/com/bilibili/api/pgc/view/web/season/user/section"; -import { followAdd } from "../../../io/com/bilibili/api/pgc/web/follow/add"; -import { followDel } from "../../../io/com/bilibili/api/pgc/web/follow/del"; -import { cards, ICardsOut } from "../../../io/com/bilibili/api/x/article/cards"; -import { toviewWeb } from "../../../io/com/bilibili/api/x/v2/history/toview/web"; -import { favResourceList } from "../../../io/com/bilibili/api/x/v3/fav/resource/list"; -import { detail } from "../../../io/com/bilibili/api/x/web-interface/view/detail"; -import svg_heart from "../../../player/assets/svg/heart.svg"; -import { customElement } from "../../../utils/Decorator/customElement"; -import { AV } from "../../../utils/av"; -import { cookie } from "../../../utils/cookie"; -import { Element } from "../../../utils/element"; -import { Format } from "../../../utils/fomat"; -import { https } from "../../../utils/https"; - -/** 视频介绍 */ -@customElement('div') -export class Desc extends HTMLDivElement { - - /** - * 需要监听变动的属性。 - * 与实例方法`attributeChangedCallback`配合使用。 - * 此字符串序列定义了`attributeChangedCallback`回调时的第一个参数的可能值。 - */ - // static observedAttributes = []; - - /** - * 在属性更改、添加、移除或替换时调用。 - * 需要与静态属性`observedAttributes`配合使用。 - * 此回调的第一个参数在`observedAttributes`数组中定义。 - */ - // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} - - /** 初始化标记 */ - // #inited = false; - - /** 每当元素添加到文档中时调用。 */ - // connectedCallback() {} - - /** 每当元素从文档中移除时调用。 */ - // disconnectedCallback() {} - - /** 每当元素被移动到新文档中时调用。 */ - // adoptedCallback() {} - - private $desc = Element.add('div', { class: 'desc' }, this); - - private $tag = Element.add('div', { class: 'tag' }, this.$desc); - - private $m = Element.add('div', { class: 'm' }, this.$desc); - - private $cover = Element.add('a', { class: 'bangumi-cover', target: '_blank' }, this.$desc); - - private $info = Element.add('div', { class: 'bangumi-info', target: '_blank' }, this.$desc); - - private $title = Element.add('div', { class: 'bangumi-title' }, this.$info); - - private $a = Element.add('a', { target: '_blank' }, this.$title); - - private $follow = Element.add('button', { class: "btn-follow" }, this.$title, `${svg_heart}追番`); - - private $list = Element.add('div', { class: 'bangumi-list' }, this.$info); - - private $section = Element.add('form', { class: 'bangumi-section' }, this.$list); - - private $episode = Element.add('div', { class: 'bangumi-episode' }, this.$list); - - private $detail = Element.add('div', { class: 'bangumi-detail' }, this.$info); - - #aid = 0; - - #cid = 0; - - #ssid = 0; - - #epid = 0; - - constructor() { - super(); - this.insertAdjacentHTML('beforeend', ``); - - this.$section.addEventListener('change', () => { - const d = new FormData(this.$section); - const i = +[...d.values()][0]; - if (i) { - pgcSection(i).then(d => { - const eps = d.main_section.episodes.concat(...d.section.map(d => d.episodes)); - if (eps.length) { - this.$episode.replaceChildren(); - this.banggumiEpisode(eps); - } - }) - } - }); - this.$follow.addEventListener('click', () => { - const csrf = cookie.get('bili_jct'); - const { ssid } = this.$follow.dataset; - if (csrf && ssid) { - (this.$follow.classList.contains('followed') ? followDel(csrf, ssid) : followAdd(csrf, ssid)).then(({ toast }) => { - this.$follow.textContent = toast; - this.$follow.classList.toggle('followed'); - }); - } - }); - } - - async navigate(router: ROUTER, url: URL | Location) { - this.#aid = 0; - this.#cid = 0; - this.#ssid = 0; - this.#epid = 0; - url instanceof Location && (url = new URL(url.href)); - switch (router) { - case ROUTER.AV: { - const path = url.pathname.split('/'); - switch (true) { - case /^av\d+$/i.test(path[2]): { - this.#aid = +path[2].slice(2); - break; - } - case /^bv1[a-z0-9]{9}$/i.test(path[2]): { - this.#aid = +AV.fromBV(path[2]); - break; - } - } - if (this.#aid) { - Promise.allSettled([cards({ av: this.#aid }), detail(this.#aid)]) - .then(([cards, detail]) => { - const d = cards.status === "fulfilled" && cards.value; - const de = detail.status === "fulfilled" && detail.value; - if (d) { - const card = d[`av${this.#aid}`]; - if (de && de.View) { - this.update(de); - } else { - this.desc(card); - } - } - }); - } else { - console.error('解析av号出错~'); - } - break; - } - case ROUTER.BANGUMI: { - const path = url.pathname.split('/'); - switch (true) { - case /^ss\d+$/i.test(path[3]): { - this.#ssid = +path[3].slice(2); - break; - } - case /^ep\d+$/i.test(path[3]): { - this.#epid = +path[3].slice(2); - break; - } - } - if (this.#ssid || this.#epid) { - pgcAppSeason(this.#ssid ? { season_id: this.#ssid } : { ep_id: this.#epid }) - .then(season => { - this.#ssid || (this.#ssid = season.season_id); - season.modules.forEach(d => { - switch (d.style) { - case "positive": - case "section": { - this.#epid || (this.#epid = d.data.episodes[0]?.ep_id); - break; - } - } - }); - this.bangumi(season, this.#epid).finally(async () => { - if (this.#ssid && !this.#epid) { - const d = await pgcSection(this.#ssid); - const eps = d.main_section.episodes.concat(...d.section.map(d => d.episodes)); - const ep = this.#epid ? eps.find(d => d.id === this.#epid) : eps[0]; - if (ep) { - this.#epid = ep.id; - this.banggumiEpisode(eps, this.#epid); - } - } - }); - }); - } else { - console.error('解析Bangumi出错~'); - } - break; - } - case ROUTER.TOVIEW: { - const path = url.hash.split('/'); - switch (true) { - case /^av\d+$/i.test(path[1]): { - this.#aid = +path[1].slice(2); - break; - } - case /^bv1[a-z0-9]{9}$/i.test(path[1]): { - this.#aid = +AV.fromBV(path[1]); - break; - } - } - toviewWeb().then(toview => { - this.#aid || (toview.length && (this.#aid = toview[0].aid)); - if (this.#aid) { - Promise.allSettled([cards({ av: this.#aid }), detail(this.#aid)]) - .then(([cards, detail]) => { - const d = cards.status === "fulfilled" && cards.value; - const de = detail.status === "fulfilled" && detail.value; - if (d) { - const card = d[`av${this.#aid}`]; - if (de && de.View) { - this.update(de); - } else { - this.desc(card); - } - } - }); - } else { - console.error('解析稍后再看出错~'); - } - }) - break; - } - case ROUTER.MEDIALIST: { - const path = url.pathname.split('/'); - const ml = +path[2].slice(2); - if (ml) { - favResourceList(ml).then(({ medias }) => { - this.#aid = Number(url.searchParams.get('aid')) || medias[0].id; - if (this.#aid) { - Promise.allSettled([cards({ av: this.#aid }), detail(this.#aid)]) - .then(([cards, detail]) => { - const d = cards.status === "fulfilled" && cards.value; - const de = detail.status === "fulfilled" && detail.value; - if (d) { - const card = d[`av${this.#aid}`]; - if (de && de.View) { - this.update(de); - } else { - this.desc(card); - } - } - }); - } else { - console.error('解析播放列表出错~'); - } - }) - } else { - console.error('解析播放列表出错~'); - } - break; - } - } - } - - private update(data: Awaited>) { - this.desc(data.View); - let p = ''; - data.Tags.forEach(d => { - p += `${d.tag_name}` - }); - this.$tag.innerHTML = p; - } - - private desc(data: ICardsOut) { - this.identify(); - this.$m.innerHTML = Format.superLink(data.desc); - } - - private async bangumi(data: Awaited>, epid?: number) { - this.identify(); - this.$desc.classList.add('bangumi'); - this.$cover.href = this.$a.href = `//www.bilibili.com/bangumi/media/md${data.media_id}`; - this.$cover.innerHTML = ``; - this.$a.text = data.title; - this.$follow.dataset.ssid = data.season_id; - - const id = crypto.randomUUID(); - data.modules.forEach(d => { - switch (d.style) { - case "season": { - this.$section.innerHTML = d.data.seasons.map(d => { - return ``; - }).join(''); - break; - } - case "positive": - case "section": { - this.$episode.insertAdjacentHTML('beforeend', d.data.episodes.map(d => { - return `${d.long_title}${d.badge_info.text}` - }).join('')); - break; - } - } - }); - this.$detail.innerHTML = `

${data.styles.map(d => `${d.name}`).join('')}

-

${data.staff.info}

-

${data.actor.info}

-

${data.evaluate}

`; - } - - private async banggumiEpisode(eps: IEpisode[], epid?: number) { - this.$episode.insertAdjacentHTML('beforeend', eps.map(d => { - return `${d.long_title}${d.badge_info.text}` - }).join('')); - } - - private identify = () => { - this.$desc.classList.remove('bangumi'); - this.$tag.replaceChildren(); - this.$m.replaceChildren(); - this.$cover.replaceChildren(); - this.$a.replaceChildren(); - this.$section.replaceChildren(); - this.$episode.replaceChildren(); - this.$detail.replaceChildren(); - } -} - - -//////////////////////////// 全局增强 //////////////////////////// -declare global { - /** 基于哈希消息认证码的一次性口令的密钥 */ - const __BILI_DESC_STYLE__: string; -} \ No newline at end of file diff --git a/src/main/bilibili/desc/style/index.css b/src/main/bilibili/desc/style/index.css deleted file mode 100644 index d9423f0..0000000 --- a/src/main/bilibili/desc/style/index.css +++ /dev/null @@ -1,320 +0,0 @@ -@scope { - :scope { - display: flex; - flex-direction: column; - align-items: center; - font-size: 12px; - min-inline-size: 980px; - - &, - div { - box-sizing: border-box; - } - - a { - outline: none; - text-decoration: none; - } - } -} - -.desc { - padding-inline-end: 290px; - display: flex; - flex-direction: column; - inline-size: 980px; - - @media screen and (min-width:1400px) { - - & { - inline-size: 1160px; - } - } - - @media screen and (min-width:2500px) { - - & { - inline-size: 1920px; - } - } - - >.tag { - padding-block: 30px; - border-block-end: 1px solid #e5e9ef; - display: flex; - flex-wrap: wrap; - - >a { - color: #6d757a; - block-size: 22px; - margin-inline-end: 10px; - margin-block-end: 8px; - padding-inline: 10px; - border: 1px solid #e5e9ef; - border-radius: 20px; - transition: all .3s; - box-sizing: border-box; - cursor: pointer; - - &:hover { - border-color: #00a1d6; - color: #00a1d6; - } - } - - &:empty { - display: none; - } - } - - >.m { - padding-block: 30px; - border-block-end: 1px solid #e5e9ef; - white-space: pre-line; - color: #6d757a; - overflow: hidden; - - >a { - color: #00a1d6; - } - - &:empty { - display: none; - } - } - - &.bangumi { - padding-block-start: 24px; - padding-block-end: 20px; - padding-inline: 0; - border-block-end: 1px solid #e5e9ef; - flex-direction: row; - - >.tag, - >.m { - display: none; - } - - >.bangumi-cover { - flex-shrink: 0; - inline-size: 120px; - block-size: 160px; - border-radius: 4px; - overflow: hidden; - - >img { - inline-size: inherit; - block-size: inherit; - object-fit: cover; - } - } - - >.bangumi-info { - flex: 1; - margin-inline-start: 24px; - display: flex; - flex-direction: column; - overflow: hidden; - - >.bangumi-title { - margin-block-end: 30px; - display: flex; - justify-content: space-between; - - >a { - color: #222; - font-weight: 400; - font-size: 18px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } - - >button { - padding-inline: 1em; - background-color: #f36392; - border: 1px solid #f36392; - border-radius: 4px; - color: #fff; - font-size: 14px; - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - column-gap: .5em; - - >svg { - fill: #fff; - inline-size: 1em; - aspect-ratio: 1; - } - - &.followed { - color: #ccd0d7; - border-color: #e2e2e2; - background-color: #fff; - - >svg { - fill: #ccd0d7; - } - } - } - } - - >.bangumi-detail { - margin-block-start: 15px; - - >p { - >span { - margin-inline-end: 10px; - padding-inline: 4px; - border: 1px solid #6d757a; - border-radius: 3px; - } - - &:last-child { - color: #6d757a; - } - } - } - - >.bangumi-list { - display: flex; - flex-direction: column; - - >.bangumi-section { - block-size: 26px; - border-block-end: 1px solid #e5e9ef; - overflow-x: auto; - scrollbar-width: thin; - white-space: nowrap; - content-visibility: auto; - - >.season-item { - display: inline-block; - vertical-align: top; - margin-inline-end: 30px; - line-height: 1; - block-size: 24px; - border-block-end: 1px solid rgba(0, 0, 0, 0); - font-size: 12px; - cursor: pointer; - position: relative; - - >input { - appearance: none; - } - - &:has(input:checked) { - color: #00a1d6; - border-block-end-color: #00a1d6; - - &::after { - content: ""; - position: absolute; - inset-inline-start: 50%; - inset-block-end: 0; - transform: translateX(-3px); - border-block-end: 3px solid #00a1d6; - border-block-start: 0; - border-inline-start: 3px solid rgba(0, 0, 0, 0); - border-inline-end: 3px solid rgba(0, 0, 0, 0); - box-sizing: border-box; - } - - } - - &:last-child { - margin-inline-end: 0; - } - } - } - - >.bangumi-episode { - padding-block-start: 14px; - overflow-x: auto; - scrollbar-width: thin; - white-space: nowrap; - content-visibility: auto; - - >.episode-item { - inline-size: 118px; - block-size: 66px; - margin-inline-end: 10px; - border-radius: 4px; - padding: 8px; - background-color: #fff; - border: 1px solid #e5e9ef; - cursor: pointer; - box-sizing: border-box; - display: inline-flex; - flex-direction: column; - content-visibility: auto; - contain-intrinsic-inline-size: 118px; - contain-intrinsic-block-size: 66px; - position: relative; - - >span { - display: -webkit-box; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; - white-space: normal; - max-height: 32px; - color: #99a2aa; - word-break: break-word; - word-wrap: break-word; - overflow: hidden; - text-overflow: ellipsis; - - &.badge { - position: absolute; - padding-inline: 4px; - background-color: #999; - font-size: 12px; - inset-block-start: 0px; - inset-inline-end: 0px; - color: #fff; - border-radius: 9px; - } - } - - &.on, - &.on:hover { - background-color: #00a1d6; - border-color: #00a1d6; - - >span, - &::before { - color: #fff; - } - } - - &:hover { - border-color: #00a1d6; - color: #00a1d6; - } - - &::before { - content: attr(data-index); - color: #000; - font-weight: 400; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } - } - } - } - } - } - - &:not(.bangumi) { - >.bangumi-cover { - display: none; - } - - >.bangumi-info { - display: none; - } - } -} \ No newline at end of file diff --git a/src/main/bilibili/footer/index.ts b/src/main/bilibili/footer/index.ts deleted file mode 100644 index 83010d8..0000000 --- a/src/main/bilibili/footer/index.ts +++ /dev/null @@ -1,89 +0,0 @@ -import svg_icon_download from "../../../player/assets/svg/icon-download.svg"; -import svg_icon_wechat from "../../../player/assets/svg/icon-wechat.svg"; -import svg_icon_weibo from "../../../player/assets/svg/icon-weibo.svg"; -import { customElement } from "../../../utils/Decorator/customElement"; -import { Element } from "../../../utils/element"; - -/** 底栏 */ -@customElement('div') -export class Footer extends HTMLDivElement { - - /** - * 需要监听变动的属性。 - * 与实例方法`attributeChangedCallback`配合使用。 - * 此字符串序列定义了`attributeChangedCallback`回调时的第一个参数的可能值。 - */ - // static observedAttributes = []; - - /** - * 在属性更改、添加、移除或替换时调用。 - * 需要与静态属性`observedAttributes`配合使用。 - * 此回调的第一个参数在`observedAttributes`数组中定义。 - */ - // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} - - /** 初始化标记 */ - // #inited = false; - - /** 每当元素添加到文档中时调用。 */ - // connectedCallback() {} - - /** 每当元素从文档中移除时调用。 */ - // disconnectedCallback() {} - - /** 每当元素被移动到新文档中时调用。 */ - // adoptedCallback() {} - - private $cnt = Element.add('div', { class: 'footer-cnt' }, this); - - private $boston = Element.add('div', { class: 'boston-postcards' }, this.$cnt, ``); - - private $partner = Element.add('div', { class: 'partner' }, this.$cnt, `
- - -
-
-
-

广播电视节目制作经营许可证:(沪)字第1248号 | 网络文化经营许可证:沪网文[2016]2296-134号 | 信息网络传播视听节目许可证:0910417 | 互联网ICP备案:沪ICP备13002172号-3 沪ICP证:沪B2-20100043 | 违法不良信息举报邮箱:help@bilibili.com | 违法不良信息举报电话:4000233233转3 | 营业执照

-

上海互联网举报中心 |12345政务服务便民热线 | 沪公网安备 31011002002436号 |儿童色情信息举报专区 |扫黄打非举报

-

网上有害信息举报专区: 中国互联网违法和不良信息举报中心

-

亲爱的市民朋友,上海警方反诈劝阻电话“962110”系专门针对避免您财产被骗受损而设,请您一旦收到来电,立即接听。

-

公司名称:上海宽娱数码科技有限公司 | 公司地址:上海市杨浦区政立路485号 | 电话:021-25099888

-
`); - - constructor() { - super(); - this.insertAdjacentHTML('beforeend', ``); - } -} - - -//////////////////////////// 全局增强 //////////////////////////// -declare global { - /** 基于哈希消息认证码的一次性口令的密钥 */ - const __BILI_FOOTER_STYLE__: string; -} \ No newline at end of file diff --git a/src/main/bilibili/footer/style/index.css b/src/main/bilibili/footer/style/index.css deleted file mode 100644 index f6df9d1..0000000 --- a/src/main/bilibili/footer/style/index.css +++ /dev/null @@ -1,167 +0,0 @@ -@scope { - :scope { - inline-size: 100%; - min-inline-size: 980px; - padding-block-start: 20px; - color: #99a2aa; - font-size: 12px; - background-color: #f6f9fa; - display: flex; - justify-content: center; - align-items: center; - - &, - div { - box-sizing: border-box; - } - } -} - -.footer-cnt { - display: flex; - flex-direction: column; - align-items: center; - inline-size: 980px; - - @media screen and (min-width:1400px) { - - & { - inline-size: 1160px; - } - } - - @media screen and (min-width:2500px) { - - & { - inline-size: 1920px; - } - } - - .boston-postcards { - inline-size: 100%; - display: flex; - justify-content: center; - font-size: 14px; - row-gap: 30px; - - >div { - flex: 1; - display: flex; - flex-wrap: wrap; - align-content: flex-start; - box-sizing: border-box; - row-gap: 10px; - - >a { - - &:first-child { - inline-size: 100%; - } - - &:not(:first-child) { - min-inline-size: 100px; - } - - &.icon { - display: flex; - flex-direction: column; - align-items: center; - position: relative; - - >svg { - inline-size: 1em; - aspect-ratio: 1; - font-size: 56px; - } - - &:nth-child(2)>svg { - fill: #585f69; - } - - &:nth-child(3)>svg { - fill: #fe596c; - } - - &:last-child>svg { - fill: #42c86b; - } - - >img { - position: absolute; - background: #fff; - border: 1px solid #eee; - padding: 10px; - inset-block-end: calc(100% + 1em); - } - - &:not(:hover)>img { - display: none; - } - } - } - - &:not(:first-child) { - border-inline-start: solid 1px #e5e9ef; - padding-inline-start: 15px; - } - - } - } - - .partner { - inline-size: 100%; - display: flex; - - .pic-box { - display: flex; - flex-direction: column; - inline-size: 100px; - - .jvs-cert { - aspect-ratio: 66 / 25; - background-repeat: no-repeat; - background-size: contain; - animation-name: jvs-cert; - animation-play-state: running; - animation-duration: 6s; - animation-delay: 3s; - animation-iteration-count: infinite; - animation-direction: alternate-reverse; - } - } - - .content-box { - line-height: 2; - margin-inline-start: 15px; - - p { - margin-block-end: 5px; - } - } - - a { - color: inherit; - } - } - - a { - transition: color .2s; - outline: none; - text-decoration: none; - color: #222; - - &:hover { - color: #00a1d6; - } - } -} - -@keyframes jvs-cert { - from { - background-image: url(//backup.hdslb.com/bfs/mainfront/websafe.png); - } - - to { - background-image: url(//backup.hdslb.com/bfs/mainfront/confirm.png); - } -} \ No newline at end of file diff --git a/src/main/bilibili/header/BiliHeader.ts b/src/main/bilibili/header/BiliHeader.ts deleted file mode 100644 index 54d6ed8..0000000 --- a/src/main/bilibili/header/BiliHeader.ts +++ /dev/null @@ -1,11 +0,0 @@ -export interface BiliHeader { - components: unknown; - config: { - disableChannelEntry?: boolean; - disableSticky?: boolean; - forceVersion?: number; - headerType: 'mini' | 'medium'; - theme?: 'light'; - tokenSupport?: boolean; - } -} \ No newline at end of file diff --git a/src/main/bilibili/header/index.ts b/src/main/bilibili/header/index.ts deleted file mode 100644 index 6d6c92a..0000000 --- a/src/main/bilibili/header/index.ts +++ /dev/null @@ -1,451 +0,0 @@ -import { header, ISplitLayer } from "../../../io/com/bilibili/api/x/web-show/page/header"; -import svg_mobile from "../../../player/assets/svg/mobile.svg"; -import svg_tv from "../../../player/assets/svg/tv.svg"; -import { customElement } from "../../../utils/Decorator/customElement"; -import { Element } from "../../../utils/element"; -import { Format } from "../../../utils/fomat"; -import { https } from "../../../utils/https"; -import navGif from '../assets/nav-gift.json'; -import channelList from '../assets/channel-list.json'; -import { online } from "../../../io/com/bilibili/api/x/web-interface/online"; -import svg_icon_paihang from "../../../player/assets/svg/icon-paihang.svg"; -import svg_search from "../../../player/assets/svg/search.svg"; -import { searchDefault } from "../../../io/com/bilibili/api/x/web-interface/search/default"; -import { searchSuggest } from "../../../io/com/bilibili/search/s/main/suggest"; -import { cookie } from "../../../utils/cookie"; -import { nav } from "../../../io/com/bilibili/api/x/web-interface/nav"; -import svg_account from "../../../player/assets/svg/account.svg"; -import svg_member from "../../../player/assets/svg/member.svg"; -import svg_wallet from "../../../player/assets/svg/wallet.svg"; -import svg_live_center from "../../../player/assets/svg/live-center.svg"; -import svg_bml from "../../../player/assets/svg/bml.svg"; -import svg_cheese from "../../../player/assets/svg/cheese.svg"; -import { exit } from "../../../io/com/bilibili/passport/login/exit/v2"; -import { unread } from "../../../io/com/bilibili/api/x/msgfeed/unread"; -import { dynamic_new, IDynamicNewCard, IDynamicArticle } from "../../../io/com/bilibili/vc/api/dynamic_svr/v1/dynamic_svr/dynamic_new"; -import { dynamic_num } from "../../../io/com/bilibili/vc/api/dynamic_svr/v1/dynamic_svr/dynamic_num"; -import { toviewWeb } from "../../../io/com/bilibili/api/x/v2/history/toview/web"; -import { medialistRecent } from "../../../io/com/bilibili/api/medialist/gateway/coll/resource/recent"; -import { history } from "../../../io/com/bilibili/api/x/v2/history"; -import { ROUTER } from ".."; - -/** 顶栏 */ -@customElement('div') -export class Header extends HTMLDivElement { - - /** - * 需要监听变动的属性。 - * 与实例方法`attributeChangedCallback`配合使用。 - * 此字符串序列定义了`attributeChangedCallback`回调时的第一个参数的可能值。 - */ - // static observedAttributes = []; - - /** - * 在属性更改、添加、移除或替换时调用。 - * 需要与静态属性`observedAttributes`配合使用。 - * 此回调的第一个参数在`observedAttributes`数组中定义。 - */ - // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} - - /** 初始化标记 */ - // #inited = false; - - /** 每当元素添加到文档中时调用。 */ - // connectedCallback() {} - - /** 每当元素从文档中移除时调用。 */ - // disconnectedCallback() {} - - /** 每当元素被移动到新文档中时调用。 */ - // adoptedCallback() {} - - #host = this.attachShadow({ mode: 'closed' }); - - private $banner = this.#host.appendChild(Element.add('div', { class: 'head-banner' })); - - private $nav = this.#host.appendChild(Element.add('div', { class: 'nav-menu' })); - - private $wrapper = Element.add('div', { class: 'bili-wrapper nav-wrapper' }, this.$nav); - - private $left = Element.add('div', { class: 'nav-side' }, this.$wrapper, `${svg_tv}主站 -音频 -游戏中心 -直播 -会员购 -漫画 -${svg_mobile}下载APP`); - - private $right = Element.add('div', { class: 'nav-side' }, this.$wrapper, ` -大会员 -消息 -动态 -稍后再看 -收藏 -历史 -投稿`); - - private $primary = this.#host.appendChild(Element.add('div', { class: 'bili-wrapper primary-wrapper' })); - - private $menu = Element.add('a', { class: 'nav-primary' }, this.$primary); - - private $gift = Element.add('a', { class: 'nav-gif' }, this.$primary); - - private $content = this.#host.appendChild(Element.add('div', { class: 'search' }, undefined, `${svg_icon_paihang}排行榜`)); - - private $form = Element.add('form', { class: 'searchform' }, this.$content); - - private $search = Element.add('input', { class: 'search-keyword', type: 'search' }, this.$form); - - private $submit = Element.add('button', { class: 'search-submit', type: 'submit' }, this.$form, svg_search); - - private $datalist = Element.add('ul', { class: 'search-datalist' }, this.$content); - - private $profileWrap = Element.add('div', { class: 'profile-wrap' }, this.$right); - - private $messageWrap = Element.add('div', { class: 'message-wrap' }, this.$right, `回复我的 -@我的 -收到的赞 -系统通知 -我的消息`); - - private $dynamicWrap = Element.add('div', { class: 'dynamic-wrap' }, this.$right, `查看全部`); - - private $dynamicList = Element.add('ul', { class: 'dyn_list' }, this.$dynamicWrap, undefined, true); - - private $dynamicForm = Element.add('form', { class: 'dyn_menu' }, this.$dynamicWrap, undefined, true); - - private $watchlaterWrap = Element.add('div', { class: 'watchlater-wrap' }, this.$right, ``); - - private $watchlaterList = Element.add('div', { class: 'watchlater-list' }, this.$watchlaterWrap, undefined, true); - - private $favWrap = Element.add('div', { class: 'fav-wrap' }, this.$right, `查看更多`); - - private $favList = Element.add('div', { class: 'fav-list' }, this.$favWrap, undefined, true); - - private $historyWrap = Element.add('div', { class: 'history-wrap' }, this.$right, `查看更多`); - - private $historyList = Element.add('div', { class: 'history-list' }, this.$historyWrap, undefined, true); - - private $upLoadWrap = Element.add('div', { class: 'up-load-wrap' }, this.$right, `专栏投稿 -音频投稿 -视频投稿 -投稿管理`); - - #defaultSubmit?: string; - - #abortSignal?: AbortController; - - #composition = true; - - #isLogin = false; - - #mid = 0; - - set resource_id(v: number) { - // 获取banner横幅 - header(v).then(d => { - this.$banner.style.backgroundImage = `url(${https(d.pic)}@.webp)`; - try { - const animatedBannerConfig: ISplitLayer = JSON.parse(d.split_layer); - for (const { resources, scale, rotate, translate, blur, opacity } of animatedBannerConfig.layers) { - const div = Element.add('div', { class: 'layer' }, this.$banner); - for (const { id, src } of resources) { - const $scale = (scale?.initial || 0) + (scale?.offset || 0); - const $rotate = (rotate?.initial || 0) + (rotate?.offset || 0); - const $translate = [((translate?.initial?.[0] || 0) + (translate?.offset?.[0] || 0)) * ($scale || 1), (translate?.initial?.[1] || 0) + (translate?.offset?.[1] || 0) * ($scale || 1)]; - const $blur = blur?.wrap === 'alternate' ? Math.abs((blur.initial || 0) + (blur.offset || 0)) : Math.max(0, (blur?.initial || 0) + (blur?.offset || 0)); - const x = (opacity?.initial === undefined ? 1 : opacity.initial) + (opacity?.offset || 0); - let y = Math.abs(x % 1); - if (Math.abs(x % 2) >= 1) { - y = 1 - y; - } - if (/\.(webm|mp4)$/.test(src)) { - const video = Element.add('video', { src, id }, div); - video.muted = true; - video.autoplay = true; - video.loop = true; - video.playsInline = true; - - $rotate && (video.style.rotate = $rotate + 'deg'); - ($translate[0] || $translate[1]) && (video.style.translate = `calc(100cqb / 155 * (${$translate[0]})) calc(100cqb / 155 * (${$translate[1]}))`); - ($blur < 1e-4) || (video.style.filter = `blur(${$blur}px)`); - video.style.opacity = opacity?.wrap === 'alternate' ? y : Math.max(0, Math.min(1, x)); - } else { - const img = Element.add('img', { src, id }, div); - $rotate && (img.style.rotate = $rotate + 'deg'); - // ($translate[0] || $translate[1]) && (img.style.translate = `calc(100cqb / 155 * (${$translate[0]})) calc(100cqb / 155 * (${$translate[1]}))`); - ($blur < 1e-4) || (img.style.filter = `blur(${$blur}px)`); - img.style.opacity = opacity?.wrap === 'alternate' ? y : Math.max(0, Math.min(1, x)); - } - } - } - } catch { - console.error('animated_banner_config parse error'); - } - }); - // 随机推荐动图 - const gif = Format.subArray(navGif); - this.$gift.href = gif.links[0]; - this.$gift.innerHTML = ``; - this.$gift.title = gif.title; - // 获取各分区在线人数 - online().then(online => { - Array.from(this.$menu.querySelectorAll('.nav-item')).forEach(d => { - const tid = (d).dataset.tid; - if (tid && online[tid]) { - (d).dataset.online = online[tid] > 999 ? '999+' : online[tid]; - } - }) - }) - } - - constructor() { - super(); - this.$banner.insertAdjacentHTML('beforebegin', ``); - - const id = crypto.randomUUID(); - this.$search.setAttribute('list', id); - this.$datalist.id = id; - this.$dynamicForm.innerHTML = ``; - - channelList.forEach(d => { - const a = Element.add('a', { class: 'nav-item' }, this.$menu); - a.href = d.url; - a.text = d.name; - a.dataset.online = '...'; - if (d.position) { - a.classList.add('position'); - a.style.setProperty('--background-position', `${d.position[0]}px ${d.position[1]}px`); - } - d.tid && (a.dataset.tid = d.tid); - d.sub && a.insertAdjacentHTML('beforeend', `
${d.sub.map(d => `${d.name}`).join('')}
`); - }); - searchDefault().then(d => { - this.$search.placeholder = d.show_name || d.name; - this.#defaultSubmit = d.url; - }); - nav().then(d => { - // 请求用户数据 - if (d.isLogin) { - this.#isLogin = d.isLogin; - this.#mid = d.mid; - - // 请求新消息信息 - unread().then(({ reply, at, like, sys_msg }) => { - const all = reply + at + like + sys_msg; - reply && ((this.$messageWrap.children[0]).dataset.num = reply > 99 ? '99+' : reply); - at && ((this.$messageWrap.children[1]).dataset.num = at > 99 ? '99+' : at); - like && ((this.$messageWrap.children[2]).dataset.num = like > 99 ? '99+' : like); - sys_msg && ((this.$messageWrap.children[3]).dataset.num = sys_msg > 99 ? '99+' : sys_msg); - all && ((this.$right.querySelector('.nav-item.message')).dataset.num = all > 99 ? '99+' : all); - }); - // 请求新动态信息 - const id = localStorage.getItem(`bp_t_offset_${d.mid}`); - dynamic_num(d.mid, id || '0', 8, 64, 512).then(d => { - d && ((this.$right.querySelector('.nav-item.dynamic')).dataset.num = d > 99 ? '99+' : d); - }); - - // 绘制用户面板 - (this.$right.firstElementChild!.firstElementChild).src = `${d.face}@.webp`; - d.pendant.image && (this.$right.firstElementChild).insertAdjacentHTML('beforeend', ``); - d.officialVerify.desc ? (this.$right.firstElementChild).insertAdjacentHTML('beforeend', ``) : (d.vip.status && (this.$right.firstElementChild).insertAdjacentHTML('beforeend', ``)); - this.$profileWrap.innerHTML = `
${d.uname}
-
${d.vip.label.text}
- - - -
`; - } - }) - this.$form.addEventListener('compositionstart', () => { - this.#composition = false; - }); - this.$form.addEventListener('compositionend', () => { - this.#composition = true; - this.onInput(); - }); - this.$form.addEventListener('input', this.onInput); - this.$datalist.addEventListener('click', ({ target }) => { - if (target instanceof HTMLLIElement) { - this.$search.value = target.textContent!; - this.$submit.click(); - } - }); - this.$form.addEventListener('submit', () => { - if (this.$search.value) { - self.open(`//search.bilibili.com/all?keyword=${this.$search.value}`); - } else if (this.#defaultSubmit) { - self.open(this.#defaultSubmit); - } else { - self.open('//search.bilibili.com'); - } - }); - this.$profileWrap.addEventListener('click', ({ target }) => { - if (target instanceof HTMLButtonElement && target.classList.contains('exit')) { - const bili_jct = cookie.get('bili_jct'); - if (this.#isLogin && bili_jct) { - exit(bili_jct).then(d => { location.replace(d) }); - } - } - }); - new IntersectionObserver((entries, observer) => { - for (const entry of entries) { - if (entry.isIntersecting) { - observer.disconnect(); - this.dynamic_video(); - (this.$right.querySelector('.nav-item.dynamic')).removeAttribute('data-num'); - break; - } - } - }).observe(this.$dynamicWrap); - this.$dynamicForm.addEventListener('change', () => { - const d = new FormData(this.$dynamicForm); - const i = +[...d.values()][0]; - switch (i) { - case 0: { - this.dynamic_video(); - break; - } - case 2: { - this.dynamic_article(); - break; - } - default: { - this.$dynamicList.replaceChildren(); - } - } - }); - new IntersectionObserver((entries, observer) => { - for (const entry of entries) { - if (entry.isIntersecting) { - observer.disconnect(); - this.#mid && toviewWeb(6).then(d => { - this.$watchlaterList.innerHTML = d.map(d => `${d.title}`).join(''); - }) - break; - } - } - }).observe(this.$watchlaterWrap); - new IntersectionObserver((entries, observer) => { - for (const entry of entries) { - if (entry.isIntersecting) { - observer.disconnect(); - this.#mid && medialistRecent().then(d => { - this.$favList.innerHTML = d.map(d => `${d.title}`).join(''); - }) - break; - } - } - }).observe(this.$favWrap); - new IntersectionObserver((entries, observer) => { - for (const entry of entries) { - if (entry.isIntersecting) { - observer.disconnect(); - if (this.#mid) { - const t = new Date(); - const now = new Date(t.getFullYear(), t.getMonth(), t.getDate()).getTime(); - const pasts: string[] = []; - history().then(d => { - this.$historyList.innerHTML = d.map(d => { - const past = this.past(now - d.view_at * 1e3); - const label = pasts.includes(past); - label || pasts.push(past); - return `${label ? '' : `
${past}
`}
${d.title}
${d.progress ? d.progress > 0 ? Math.floor(d.progress / d.duration * 100) : '100' : '1'}%
` - }).join(''); - }) - } - break; - } - } - }).observe(this.$historyWrap); - } - - private onInput = () => { - if (this.#composition) { - if (this.$search.value) { - this.#abortSignal?.abort(); - this.#abortSignal = new AbortController(); - searchSuggest( - this.$search.value, - cookie.get('DedeUserID') || '0', - this.#abortSignal.signal, - ).then(d => { - this.$datalist.innerHTML = d.map(d => `
  • ${d.term}
  • `).join(''); - }) - } else { - this.$datalist.replaceChildren(); - } - } - } - - private dynamic_video() { - this.#mid && dynamic_new(this.#mid || 0, 8, 512).then(d => { - localStorage.setItem(`bp_t_offset_${this.#mid}`, d.max_dynamic_id); - this.$dynamicList.innerHTML = https(d.cards.map(d => { - const card: IDynamicNewCard = JSON.parse(d.card); - return `
  • ${card.owner.name}${card.title}
  • `; - }).join('')); - }); - } - - private dynamic_article() { - this.#mid && dynamic_new(this.#mid || 0, 64).then(d => { - localStorage.setItem(`bp_t_offset_${this.#mid}`, d.max_dynamic_id); - this.$dynamicList.innerHTML = https(d.cards.map(d => { - const card: IDynamicArticle = JSON.parse(d.card); - return `
  • ${card.author.name}${card.title}
  • `; - }).join('')); - }); - } - - private past(tsp: number) { - return tsp <= 0 ? "今日" : tsp > 0 && tsp <= 864e5 ? "昨日" : tsp > 864e5 && tsp <= 6048e5 ? "近1周" : tsp > 6048e5 && tsp <= 2592e6 ? "1周前" : tsp > 2592e6 && tsp <= 7776e6 ? "1个月前" : "last"; - } - - async navigate(router: ROUTER, url: URL | Location) { - this.identify(); - url instanceof Location && (url = new URL(url.href)); - switch (router) { - case ROUTER.AV: - case ROUTER.BANGUMI: - case ROUTER.HOME: - case ROUTER.TOVIEW: - case ROUTER.MEDIALIST: { - this.resource_id = 142; - break; - } - } - } - - private identify = () => { - this.classList.remove('mini'); - } -} - -//////////////////////////// 全局增强 //////////////////////////// -declare global { - const __BILI_HEADER_STYLE__: string; -} \ No newline at end of file diff --git a/src/main/bilibili/header/style/index.css b/src/main/bilibili/header/style/index.css deleted file mode 100644 index 69b38b1..0000000 --- a/src/main/bilibili/header/style/index.css +++ /dev/null @@ -1,1332 +0,0 @@ -@scope { - :scope { - display: flex; - flex-direction: column; - font-size: 12px; - min-inline-size: 980px; - - &, - div { - box-sizing: border-box; - } - - &.mini { - .head-banner { - block-size: 42px; - background-color: transparent; - background-image: unset !important; - - >div { - display: none; - } - } - - .nav-menu { - z-index: 99; - } - - .primary-wrapper { - display: none; - } - - .search { - display: none; - } - } - } -} - -ul { - margin: 0; - padding: 0; -} - -.head-banner { - background-size: cover; - background-position: center; - background-repeat: no-repeat; - background-color: #eee; - aspect-ratio: 32 / 3; - anchor-name: --head-banner; - container: banner / size; - position: relative; - overflow: hidden; - - >.layer { - position: absolute; - inset: 0; - inline-size: 100%; - block-size: 100%; - display: flex; - align-items: center; - justify-content: center; - - >img, - >video { - inline-size: 100%; - block-size: 100%; - } - } -} - -.nav-menu { - position: absolute; - inset-block-start: anchor(--head-banner start); - inset-inline-start: anchor(--head-banner start); - inset-inline-end: anchor(--head-banner end); - block-size: 42px; - color: #222; - background-color: hsla(0, 0%, 100%, .4); - box-shadow: 0 1px 2px rgba(0, 0, 0, .1); - /* backdrop-filter: blur(4px); */ - z-index: 1; - display: flex; - justify-content: center; - align-items: center; - - .nav-wrapper { - display: flex; - justify-content: space-between; - block-size: 100%; - - .nav-side { - display: flex; - - .nav-item { - display: flex; - justify-content: center; - align-items: center; - padding-inline: 7px; - color: #222; - text-decoration: none; - - &.home, - &.mobile { - >svg { - font-size: 16px; - fill: #00a1d6; - margin-inline: 3px; - } - } - - &.profile-info { - position: relative; - anchor-name: --profile-info; - display: flex; - justify-content: center; - align-items: center; - - >.face { - inline-size: 32px; - aspect-ratio: 1; - border-radius: 50%; - } - - >.pendant { - position: absolute; - block-size: 96px; - inline-size: 96px; - scale: .5; - } - - >.auth { - position: absolute; - inline-size: 20px; - block-size: 20px; - inset-inline-end: 1px; - inset-block-start: 24px; - background-image: url(//s1.hdslb.com/bfs/static/jinkela/videoplay/asserts/user-auth.png); - background-repeat: no-repeat; - z-index: 3; - scale: .5; - - &.o-auth { - background-position: -4px -53px; - } - - &.p-auth { - background-position: -38px -53px; - } - - &.vip { - background-position: -72px -52px; - } - } - - &:has(~.profile-wrap:not(:empty)):hover, - &:has(~.profile-wrap:hover) { - scale: 2; - translate: 0 16px; - transform-origin: center 0; - z-index: 2; - transition: .3s; - } - - &:not(:hover, :has(~.profile-wrap:hover)) { - - >.pendant, - >.auth, - ~.profile-wrap { - display: none; - } - - ~.profile-wrap { - opacity: 0; - scale: 0; - } - } - } - - &.message { - anchor-name: --nav-message; - position: relative; - - &[data-num]::after { - content: attr(data-num); - position: absolute; - color: #fff; - background-color: #f25d8e; - inset-block-start: 0; - inset-inline-end: 0; - border-radius: 10px; - padding-block: .1em; - padding-inline: .5em; - } - - &:not(:hover)~.message-wrap:not(:hover) { - display: none; - opacity: 0; - scale: 1 0; - } - } - - &.dynamic { - anchor-name: --nav-dynamic; - position: relative; - - &[data-num]::after { - content: attr(data-num); - position: absolute; - color: #fff; - background-color: #f25d8e; - inset-block-start: 0; - inset-inline-end: 0; - border-radius: 10px; - padding-block: .1em; - padding-inline: .5em; - } - - &:not(:hover)~.dynamic-wrap:not(:hover) { - display: none; - opacity: 0; - scale: 0; - } - } - - &.watchlater { - anchor-name: --nav-watchlater; - position: relative; - - &:not(:hover)~.watchlater-wrap:not(:hover) { - display: none; - opacity: 0; - scale: 0; - } - } - - &.fav { - anchor-name: --nav-fav; - position: relative; - - &:not(:hover)~.fav-wrap:not(:hover) { - display: none; - opacity: 0; - scale: 0; - } - } - - &.history { - anchor-name: --nav-history; - position: relative; - - &:not(:hover)~.history-wrap:not(:hover) { - display: none; - opacity: 0; - scale: 0; - } - } - - &.up-load { - inline-size: 68px; - background-color: #f45a8d; - border-end-start-radius: 6px; - border-end-end-radius: 6px; - font-size: 14px; - color: #fff; - box-sizing: border-box; - anchor-name: --nav-up-load; - - &:not(:hover)~.up-load-wrap:not(:hover) { - display: none; - opacity: 0; - scale: 0; - } - } - - >svg { - inline-size: 1em; - aspect-ratio: 1; - } - - &:not(.profile-info, .up-load):hover { - background-color: hsla(0, 0%, 100%, .3); - } - } - - >.profile-wrap { - position: absolute; - position-anchor: --profile-info; - inset-area: block-end span-all; - inline-size: 260px; - padding-block-start: calc(.75em + 50px); - background: #fff; - box-shadow: 0 2px 4px rgba(0, 0, 0, .16); - border-end-start-radius: 4px; - border-end-end-radius: 4px; - display: flex; - flex-direction: column; - align-items: center; - row-gap: .75em; - transform-origin: center 0; - transition: all .5s allow-discrete; - - @starting-style { - opacity: 0; - scale: 0; - } - - >.uname { - display: flex; - justify-content: center; - align-items: center; - font-weight: bold; - } - - >.vip-type { - display: flex; - justify-content: center; - align-items: center; - color: #fff; - background-color: #fb7299; - padding-block: 2px; - padding-inline: 3px; - border-radius: 3px; - cursor: pointer; - - &:empty { - display: none; - } - } - - >.btns-profile { - inline-size: 100%; - padding-inline: 20px; - display: flex; - justify-content: space-between; - align-items: center; - - >a { - color: #222; - display: flex; - align-items: center; - column-gap: .2em; - text-decoration: none; - - &.coin::before { - content: " "; - background-position: -343px -471px; - } - - &.bcoin::before { - content: " "; - background-position: -407px -471px; - } - - &.email { - &::before { - content: " "; - background-position: -279px -534px; - } - - &.verified::before { - background-position: -343px -534px; - } - } - - &.phone { - &::before { - content: " "; - background-position: -279px -599px; - } - - &.verified::before { - background-position: -343px -599px; - } - } - - &::before { - background-image: url(//static.hdslb.com/images/base/icons.png); - display: inline-block; - inline-size: 18px; - aspect-ratio: 1; - background-repeat: no-repeat; - } - } - } - - >.grade { - inline-size: 100%; - margin-block-start: 24px; - margin-block-end: 30px; - padding-inline: 20px; - display: flex; - justify-content: space-between; - align-items: center; - - >a { - color: #222; - text-decoration: none; - - >.bar { - position: relative; - inline-size: 170px; - block-size: 8px; - background: #eee; - display: flex; - align-items: center; - - >.lt { - position: absolute; - inline-size: 18px; - aspect-ratio: 1; - border-radius: 9px; - translate: -100%; - color: #fff; - background-color: #f3cb85; - display: flex; - justify-content: center; - align-items: center; - } - - >.rate { - inline-size: 0; - block-size: 8px; - background-color: #f3cb85; - } - - >.num { - position: absolute; - inset-inline-end: 0; - inset-block-end: -24px; - white-space: nowrap; - } - } - } - } - - >.member-menu { - padding-block: 10px; - padding-inline: 20px; - border-block-start: 1px solid #e5e9ef; - display: flex; - flex-wrap: wrap; - justify-content: space-between; - column-gap: 6em; - row-gap: 1em; - - >a { - color: #222; - text-decoration: none; - display: flex; - align-items: center; - column-gap: .5em; - - >svg { - font-size: 16px; - inline-size: 1em; - aspect-ratio: 1; - fill: #c4c4c4; - } - } - } - - >.member-bottom { - inline-size: 100%; - block-size: 30px; - padding-inline: 20px; - border-end-start-radius: 4px; - border-end-end-radius: 4px; - background-color: #f4f5f7; - display: flex; - flex-direction: row-reverse; - align-items: center; - - >button { - padding: 0; - border: 0; - color: #222; - background-color: transparent; - cursor: pointer; - display: flex; - justify-content: center; - align-items: center; - - &:hover { - color: #00a1d6; - } - } - } - - &:empty { - display: none; - } - } - - >.message-wrap { - position: absolute; - position-anchor: --nav-message; - inset-area: block-end span-all; - inline-size: 110px; - background: #fff; - box-shadow: 0 2px 4px rgba(0, 0, 0, .16); - border-end-start-radius: 4px; - border-end-end-radius: 4px; - display: flex; - flex-direction: column; - transform-origin: center 0; - transition: all .5s allow-discrete; - - @starting-style { - opacity: 0; - scale: 1 0; - } - - >a { - block-size: 42px; - text-decoration: none; - color: inherit; - display: flex; - justify-content: center; - align-items: center; - position: relative; - - &[data-num]::after { - content: attr(data-num); - position: absolute; - color: #fff; - background-color: #f25d8e; - inset-inline-end: 0; - border-radius: 10px; - padding-block: .1em; - padding-inline: .5em; - } - - &:hover { - color: #00a1d6; - background-color: #e5e9ef; - } - } - } - - >.dynamic-wrap { - position: absolute; - position-anchor: --nav-dynamic; - inset-area: block-end span-all; - inline-size: 380px; - background: #fff; - box-shadow: 0 2px 4px rgba(0, 0, 0, .16); - border-end-start-radius: 4px; - border-end-end-radius: 4px; - display: flex; - flex-direction: column; - transform-origin: center 0; - transition: all .5s allow-discrete; - - @starting-style { - opacity: 0; - scale: 0; - } - - >.dyn_menu { - padding-block-start: 16px; - padding-block-end: 10px; - display: flex; - justify-content: center; - align-items: center; - column-gap: 2em; - - >label { - block-size: 30px; - color: #666; - cursor: pointer; - display: flex; - align-items: center; - position: relative; - - >input { - appearance: none; - } - - &:hover { - color: #00a1d6; - } - - &:has(input:checked) { - color: #00a1d6; - border-block-end: 1px solid #00a1d6; - - &::after { - content: ""; - position: absolute; - inset-inline-start: 50%; - inset-block-end: 0; - transform: translateX(-3px); - border-block-end: 3px solid #00a1d6; - border-block-start: 0; - border-inline-start: 3px solid rgba(0, 0, 0, 0); - border-inline-end: 3px solid rgba(0, 0, 0, 0); - box-sizing: border-box; - } - } - } - } - - >.dyn_list { - display: flex; - flex-direction: column; - max-block-size: 300px; - row-gap: 1.5em; - overflow-y: auto; - scrollbar-width: thin; - transition: all .5s; - list-style: none; - - >li { - display: grid; - padding-inline: 12px; - grid-template-columns: repeat(6, 1fr); - grid-template-areas: - "cv up up up up up" - "cv tt tt tt tt tt"; - column-gap: 1em; - - >.cover { - grid-area: cv; - - >img { - inline-size: 100%; - block-size: 100%; - border-radius: 4px; - object-fit: cover; - } - } - - >.up { - grid-area: up; - color: #222; - text-decoration: none; - display: flex; - column-gap: 1.5em; - - &::after { - content: attr(data-action); - color: #99a2aa; - } - - &:hover { - color: #00a1d6; - } - } - - >.title { - grid-area: tt; - color: #00a1d6; - overflow: hidden; - text-overflow: ellipsis; - text-decoration: none; - white-space: nowrap; - - &:hover { - text-decoration: underline; - } - } - - &.article { - grid-template-areas: - "up up up up up cv" - "tt tt tt tt tt cv"; - } - } - - &:empty::before { - content: "暂时没有新动态了哦!"; - color: #99a2aa; - text-align: center; - } - - &:not(:empty):after { - content: "(´・ω・`) 点击下方按钮查看全部"; - color: #99a2aa; - text-align: center; - } - } - - >.read-more { - block-size: 30px; - margin-block: 10px; - margin-inline: 12px; - border-radius: 4px; - color: #222; - background-color: #e5e9ef; - border: 1px solid #e0e6ed; - outline: 0; - text-decoration: none; - white-space: nowrap; - display: flex; - justify-content: center; - align-items: center; - - &:hover { - background-color: #ccd0d7; - } - } - } - - >.watchlater-wrap { - position: absolute; - position-anchor: --nav-watchlater; - inset-area: block-end span-all; - inline-size: 320px; - background: #fff; - box-shadow: 0 2px 4px rgba(0, 0, 0, .16); - border-end-start-radius: 4px; - border-end-end-radius: 4px; - display: flex; - flex-direction: column; - transform-origin: center 0; - transition: all .5s allow-discrete; - - @starting-style { - opacity: 0; - scale: 0; - } - - >.watchlater-list { - padding-block-start: 12px; - display: flex; - flex-direction: column; - - >a { - display: list-item; - list-style-position: inside; - block-size: 28px; - line-height: 28px; - padding-inline: 12px; - font-size: 12px; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - color: #222; - text-decoration: none; - transition: color 0.2s; - - &:hover { - color: #00a1d6; - background-color: #e5e9ef; - } - } - - &:empty::before { - content: "暂时没有稍后再看哦!"; - color: #99a2aa; - text-align: center; - } - } - - >.btn { - padding-inline: 12px; - padding-block-start: 4px; - padding-block-end: 12px; - display: flex; - column-gap: 12px; - - >a { - flex: 1; - block-size: 22px; - border-radius: 4px; - color: #222222; - background-color: #e5e9ef; - border: 1px solid #e0e6ed; - display: flex; - justify-content: center; - align-items: center; - transition: color 0.2s; - text-decoration: none; - - &:hover { - background-color: #ccd0d7; - } - } - } - } - - >.fav-wrap { - position: absolute; - position-anchor: --nav-fav; - inset-area: block-end span-all; - inline-size: 320px; - background: #fff; - box-shadow: 0 2px 4px rgba(0, 0, 0, .16); - border-end-start-radius: 4px; - border-end-end-radius: 4px; - display: flex; - flex-direction: column; - transform-origin: center 0; - transition: all .5s allow-discrete; - - @starting-style { - opacity: 0; - scale: 0; - } - - >.fav-list { - padding-block-start: 12px; - display: flex; - flex-direction: column; - - >a { - display: list-item; - list-style-position: inside; - block-size: 28px; - line-height: 28px; - padding-inline: 12px; - font-size: 12px; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - color: #222; - text-decoration: none; - transition: color 0.2s; - - &:hover { - color: #00a1d6; - background-color: #e5e9ef; - } - } - - &:empty::before { - content: "暂时没有收藏哦!"; - color: #99a2aa; - text-align: center; - } - } - - >.read-more { - block-size: 30px; - margin-block: 10px; - margin-inline: 12px; - border-radius: 4px; - color: #222; - background-color: #e5e9ef; - border: 1px solid #e0e6ed; - outline: 0; - text-decoration: none; - white-space: nowrap; - display: flex; - justify-content: center; - align-items: center; - - &:hover { - background-color: #ccd0d7; - } - } - } - - >.history-wrap { - position: absolute; - position-anchor: --nav-history; - inset-area: block-end span-all; - inline-size: 400px; - background: #fff; - box-shadow: 0 2px 4px rgba(0, 0, 0, .16); - border-end-start-radius: 4px; - border-end-end-radius: 4px; - display: flex; - flex-direction: column; - transform-origin: center 0; - transition: all .5s allow-discrete; - - @starting-style { - opacity: 0; - scale: 0; - } - - >.history-list { - padding-block-start: 12px; - display: flex; - flex-direction: column; - - >.timeline { - color: #99a2aa; - margin-block: 10px; - border-block-start: 1px solid #e5e9ef; - display: flex; - align-items: center; - - >span { - position: absolute; - inset-inline-start: 0; - background-color: #fff; - padding-inline: 1em; - } - } - - >a { - display: list-item; - list-style-position: inside; - block-size: 28px; - line-height: 28px; - padding-inline: 12px; - font-size: 12px; - color: #222; - text-decoration: none; - transition: color 0.2s; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - - >.title { - inline-size: 240px; - vertical-align: middle; - display: inline-block; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - } - - >.state { - inline-size: calc(100% - 260px); - vertical-align: middle; - color: #99a2aa; - display: inline flex; - align-items: center; - justify-content: flex-end; - column-gap: 1em; - - >.device { - width: 20px; - height: 20px; - background: url(//static.hdslb.com/images/base/icons.png) no-repeat; - - &.pc { - background-position: -1367px -406px; - } - - &.phone { - background-position: -1367px -466px; - } - - &.pad { - background-position: -1367px -526px; - } - - &.tv { - background-position: -1430px -472px; - } - - &.unknown { - background-position: -1430px -407px; - } - } - } - - &:hover { - color: #00a1d6; - background-color: #e5e9ef; - } - } - - &:empty::before { - content: "暂时没有历史哦!"; - color: #99a2aa; - text-align: center; - } - } - - >.read-more { - block-size: 30px; - margin-block: 10px; - margin-inline: 12px; - border-radius: 4px; - color: #222; - background-color: #e5e9ef; - border: 1px solid #e0e6ed; - outline: 0; - text-decoration: none; - white-space: nowrap; - display: flex; - justify-content: center; - align-items: center; - - &:hover { - background-color: #ccd0d7; - } - } - } - - >.up-load-wrap { - position: absolute; - position-anchor: --nav-up-load; - inset-area: block-end span-inline-start; - background: #fff; - box-shadow: 0 2px 4px rgba(0, 0, 0, .16); - border-end-start-radius: 4px; - border-end-end-radius: 4px; - display: flex; - transform-origin: 100% 0; - transition: all .5s allow-discrete; - - @starting-style { - opacity: 0; - scale: 0; - } - - >a { - inline-size: 68px; - block-size: 64px; - transition: 0.2s; - color: #f25d8e; - background: #fff; - cursor: pointer; - text-decoration: none; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - - &:hover { - background: #e5e9ef; - - &::before { - translate: 0 -2px; - } - } - - - &::before { - content: ""; - padding: 10px; - background-image: url(//static.hdslb.com/images/base/icons.png); - transition: 0.2s; - } - - &.i-art::before { - background-position: -534px -919px; - } - - &.i-ap::before { - background-position: -534px -983px; - } - - &.i-vp::before { - background-position: -471px -919px; - } - - &.i-vm::before { - background-position: -471px -982px; - } - } - } - } - } -} - -.primary-wrapper { - block-size: 50px; - margin-inline: auto; - margin-block-end: 4px; - border-block-end: 1px solid #fff; - display: flex; - justify-content: center; - align-items: center; - anchor-name: --primary-wrapper; - - .nav-primary { - display: flex; - justify-content: space-between; - block-size: 42px; - flex-grow: 1; - - .nav-item { - block-size: 100%; - margin-inline: 5px; - color: #222; - position: relative; - display: flex; - flex-direction: column; - justify-content: flex-end; - align-items: center; - --background-position: 0; - - &::before { - content: attr(data-online); - padding-block: 1px; - padding-inline: 3px; - border-radius: 3px; - font-size: 9px; - color: #fff; - background-color: #ffafc9; - } - - &.position::before { - content: " "; - inline-size: 18px; - aspect-ratio: 1; - background: url(//static.hdslb.com/images/base/icons.png) no-repeat; - background-position: var(--background-position); - } - - .sub-item { - position: absolute; - inset-block-start: 100%; - inset-inline-start: 0; - color: #222; - background-color: #fff; - box-shadow: 0 2px 4px rgba(0, 0, 0, .16); - border-end-start-radius: 4px; - border-end-end-radius: 4px; - display: flex; - flex-direction: column; - z-index: 1; - transform-origin: center 0; - transition: all .3s allow-discrete; - - @starting-style { - opacity: 0; - scale: 1 0; - } - - >a { - min-inline-size: 120px; - block-size: 30px; - padding-block: 5px; - box-sizing: border-box; - display: flex; - align-items: center; - color: #222; - - &::before { - content: ">"; - color: #00a1d6; - padding-inline-start: 10px; - padding-inline-end: 5px; - transition: all .2s; - } - - &::after { - content: "<"; - color: #00a1d6; - padding-inline-start: 15px; - opacity: 0; - transition: all .2s; - } - - &:hover { - background-color: #e5e9ef; - - &::before { - padding-inline-start: 15px; - } - - &::after { - padding-inline-start: 5px; - opacity: 1; - } - } - } - } - - &:not(:hover) .sub-item { - display: none; - opacity: 0; - scale: 1 0; - } - } - - a { - text-decoration: none; - } - } - - .nav-gif { - display: flex; - block-size: 40px; - flex-shrink: 0; - - >img { - block-size: 100%; - } - } -} - -.search { - position: absolute; - inset-block-end: calc(anchor(--primary-wrapper start) + 20px); - inset-inline-end: anchor(--primary-wrapper end); - block-size: 32px; - display: flex; - column-gap: .2em; - - >.link-ranking { - inline-size: 68px; - background-color: rgba(255, 255, 255, 0.88); - border-radius: 4px; - transition: 0.2s background-color; - color: #f25d8e; - text-decoration: none; - display: flex; - align-items: center; - justify-content: center; - column-gap: .3em; - - >svg { - font-size: 18px; - inline-size: 1em; - aspect-ratio: 1; - fill: #f25d8e; - ; - } - - &:hover { - background-color: #fff; - } - } - - >.searchform { - border-radius: 4px; - background-color: rgba(255, 255, 255, 0.88); - transition: 0.2s background-color; - display: flex; - anchor-name: --searchform; - - >.search-keyword { - inline-size: 200px; - block-size: 100%; - padding-inline: 12px; - border: 0; - font-size: 12px; - color: #222; - background-color: transparent; - overflow: hidden; - box-shadow: none; - outline: none; - - &::-webkit-calendar-picker-indicator { - display: none !important; - } - } - - >.search-submit { - inline-size: 48px; - margin: 0; - border: 0; - padding: 0; - background-color: transparent; - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - - >svg { - font-size: 18px; - inline-size: 1em; - aspect-ratio: 1; - fill: #00a1d6; - } - - &:hover>svg { - fill: #f45a8d; - } - } - - &:hover { - background-color: #fff; - } - - &:not(:has(:focus))~.search-datalist:not(:hover) { - display: none; - opacity: 0; - scale: 1 0; - } - } - - >.search-datalist { - position: absolute; - position-anchor: --searchform; - inset-area: end center; - inline-size: anchor-size(--searchform inline); - margin-block-start: .2em; - border: 1px solid #e5e9ef; - border-radius: 4px; - background-color: #fff; - box-shadow: rgba(0, 0, 0, 0.16) 0px 2px 4px; - padding-block-end: .5em; - font-size: 12px; - z-index: 1; - list-style: none; - transform-origin: center 0; - transition: all .3s allow-discrete; - - @starting-style { - opacity: 0; - scale: 1 0; - } - - >li { - padding-block: .5em; - padding-inline: 1em; - cursor: pointer; - word-wrap: break-word; - word-break: break-all; - color: #222222; - - &:hover { - background-color: #e5e9ef; - } - } - - &:empty { - display: none; - opacity: 0; - scale: 1 0; - } - } -} - -.bili-wrapper { - inline-size: 980px; - - @media screen and (min-width:1400px) { - - & { - inline-size: 1160px; - } - } - - @media screen and (min-width:2500px) { - - & { - inline-size: 1920px; - } - } -} \ No newline at end of file diff --git a/src/main/bilibili/home/index.ts b/src/main/bilibili/home/index.ts deleted file mode 100644 index fb07c24..0000000 --- a/src/main/bilibili/home/index.ts +++ /dev/null @@ -1,373 +0,0 @@ -import { TYPE } from "../../../io/com/bilibili/api/pgc/web/timeline"; -import { add } from "../../../io/com/bilibili/api/x/v2/history/toview/add"; -import { del } from "../../../io/com/bilibili/api/x/v2/history/toview/del"; -import { REGION } from "../../../io/com/bilibili/api/x/web-interface/dynamic/region"; -import { rcmd } from "../../../io/com/bilibili/api/x/web-interface/index/top/rcmd"; -import { locs } from "../../../io/com/bilibili/api/x/web-show/res/locs"; -import { getList } from "../../../io/com/bilibili/live/api/xlive/web-interface/v1/webMain/getList"; -import { getMoreRecList } from "../../../io/com/bilibili/live/api/xlive/web-interface/v1/webMain/getMoreRecList"; -import svg_reflesh from "../../../player/assets/svg/reflesh.svg"; -import svg_sent from "../../../player/assets/svg/sent.svg"; -import { customElement } from "../../../utils/Decorator/customElement"; -import { AV } from "../../../utils/av"; -import { cookie } from "../../../utils/cookie"; -import { Element } from "../../../utils/element"; -import { Format } from "../../../utils/fomat"; -import { https } from "../../../utils/https"; -import { Ranking } from "./ranking"; -import { Timeline } from "./timeline"; -import { VideoInfo } from "./video-info"; - -/** 顶栏 */ -@customElement('div') -export class Home extends HTMLDivElement { - - /** - * 需要监听变动的属性。 - * 与实例方法`attributeChangedCallback`配合使用。 - * 此字符串序列定义了`attributeChangedCallback`回调时的第一个参数的可能值。 - */ - // static observedAttributes = []; - - /** - * 在属性更改、添加、移除或替换时调用。 - * 需要与静态属性`observedAttributes`配合使用。 - * 此回调的第一个参数在`observedAttributes`数组中定义。 - */ - // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} - - /** 初始化标记 */ - // #inited = false; - - /** 每当元素添加到文档中时调用。 */ - connectedCallback() { - document.body.appendChild(this.$form); - } - - /** 每当元素从文档中移除时调用。 */ - disconnectedCallback() { - this.$form.remove(); - } - - /** 每当元素被移动到新文档中时调用。 */ - // adoptedCallback() {} - - private $chief_recommend = Element.add('div', { class: 'chief-recommend' }, this); - - private $carousel_box = Element.add('a', { class: 'carousel-box', target: '_blank' }, this.$chief_recommend); - - private $trig = Element.add('form', { class: 'trig' }, this.$chief_recommend); - - private $recommend_module = Element.add('div', { class: 'recommend-module' }, this.$chief_recommend); - - private $recommend_flesh = Element.add('button', { class: 'recommend-flesh', title: '刷新' }, this.$chief_recommend, svg_reflesh); - - private $home_popularize = Element.add('div', { class: 'home-popularize' }, this); - - private $popularize = Element.add('div', { class: 'popularize' }, this.$home_popularize, '
    推广
    '); - - private $rank_list = Element.add('div', { class: 'rank_list' }, this.$home_popularize, '在线列表'); - - private rollTimer?: number; - - private $bili_live = Element.add('div', { class: 'bili-live' }, this); - - private $bili_live_l = Element.add('div', { class: 'l-con' }, this.$bili_live, `
    正在直播更多${svg_sent}
    `); - - private $bili_live_r = Element.add('div', { class: 'r-con' }, this.$bili_live); - - private $bili_live_r_tab = Element.add('form', { class: 'bili-tab' }, this.$bili_live_r); - - private $bili_live_r_box = Element.add('div', { class: 'tab-box' }, this.$bili_live_r); - - private $bili_douga = this.appendChild(new Ranking('动画', '/v/douga', REGION.DOUGA)); - - private $timeline_bangumi = this.appendChild(new Timeline('番剧', '/anime', TYPE.BANGUMI, REGION.BANGUMI)); - - private $bili_bangumi = this.appendChild(new Ranking('番剧动态', '/anime', REGION.BANGUMI)); - - private $timeline_guochuang = this.appendChild(new Timeline('国创', '/guochuang', TYPE.GUOCHUANG, REGION.GUOCHUANG)); - - private $bili_guochuang = this.appendChild(new Ranking('国产原创相关', '/guochuang', REGION.GUOCHUANG)); - - private $bili_music = this.appendChild(new Ranking('音乐', '/v/music', REGION.MUSCI)); - - private $bili_dance = this.appendChild(new Ranking('舞蹈', '/v/dance', REGION.DANCE)); - - private $bili_game = this.appendChild(new Ranking('游戏', '/v/game', REGION.GAME)); - - private $bili_knowledge = this.appendChild(new Ranking('知识', '/v/knowledge', REGION.KNOWLEDGE)); - - private $bili_technology = this.appendChild(new Ranking('科技', '/v/tech', REGION.TECHNOLOGY)); - - private $bili_life = this.appendChild(new Ranking('生活', '/v/life', REGION.LIFE)); - - private $bili_kichiku = this.appendChild(new Ranking('鬼畜', '/v/kichiku', REGION.KICHIKU)); - - private $bili_fashion = this.appendChild(new Ranking('时尚', '/v/fashion', REGION.FASHION)); - - private $bili_news = this.appendChild(new Ranking('资讯', '/v/information', REGION.NEWS)); - - private $bili_ent = this.appendChild(new Ranking('娱乐', '/v/ent', REGION.ENT)); - - private $bili_movie = this.appendChild(new Ranking('电影', '/movie', REGION.MOVIE)); - - private $bili_tv = this.appendChild(new Ranking('电视剧', '/tv', REGION.TV)); - - private $bili_cinephile = this.appendChild(new Ranking('影视', '/v/cinephile', REGION.CINEPHILE)); - - private $bili_documentary = this.appendChild(new Ranking('纪录片', '/documentary', REGION.DOCUMENTARY)); - - private $form = Element.add('form', { class: 'nav-form' }) - - constructor() { - super(); - this.insertAdjacentHTML('beforeend', ``); - - const id = crypto.randomUUID(); - this.$bili_live_r_tab.insertAdjacentHTML('afterbegin', ``); - this.$bili_douga.classList.add('bili-douga'); - this.$bili_bangumi.classList.add('no-icon'); - this.$bili_guochuang.classList.add('no-icon'); - this.$bili_music.classList.add('bili-music'); - this.$bili_dance.classList.add('bili-dance'); - this.$bili_game.classList.add('bili-game'); - this.$bili_knowledge.classList.add('bili-knowledge'); - this.$bili_technology.classList.add('bili-technology'); - this.$bili_life.classList.add('bili-life'); - this.$bili_kichiku.classList.add('bili-kichiku'); - this.$bili_fashion.classList.add('bili-fashion'); - this.$bili_news.classList.add('no-icon'); - this.$bili_ent.classList.add('bili-ent'); - this.$bili_movie.classList.add('bili-movie'); - this.$bili_tv.classList.add('bili-tv'); - this.$bili_cinephile.classList.add('bili-cinephile'); - this.$bili_documentary.classList.add('bili-documentary'); - this.$timeline_bangumi.classList.add('timeline-bangumi'); - this.$timeline_guochuang.classList.add('timeline-guochuang'); - this.$form.insertAdjacentHTML('afterbegin', ` - - - - - - - - - - - - - - - - -`) - - new VideoInfo(); - - locs(4694, 34).then(d => { - // 主推荐滚动图 - const chief = d[4694]; - if (chief && chief.length) { - this.$carousel_box.href = https(AV.fromStr(chief[0].url)); - this.$carousel_box.title = chief[0].name; - const $carousel: string[] = []; - const $trig: string[] = []; - const id = crypto.randomUUID(); - chief.forEach((d, i) => { - if (!d.ad_cb && !d.null_frame) { - $carousel.push(``); - $trig.push(``); - } - }) - this.$carousel_box.innerHTML = AV.fromStr(https($carousel.join(''))); - this.$trig.innerHTML = $trig.join(''); - } - const popularize = d[34]; - if (popularize && popularize.length) { - this.$popularize.lastElementChild!.innerHTML = https(AV.fromStr(popularize.map(d => (d.ad_cb || d.null_frame) ? '' : `

    ${d.name}

    ${d.archive ? `
    ${Format.fmSeconds(d.archive.duration)}
    ` : ''}
    `).join(''))); - } - }); - - this.rcmd(); - getList().then(({ recommend_room_list, online_total, ranking_list }) => { - this.$bili_live_l.firstElementChild!.insertAdjacentHTML('beforeend', `

    当前共有${online_total}个在线直播

    `); - this.$bili_live_l.lastElementChild!.innerHTML = https(recommend_room_list.map(d => `

    ${d.title}

    ${d.area_v2_parent_name}·${d.area_v2_name}

    ${d.uname}
    ${Format.carry(d.online)}
    `).join('')); - ranking_list && (this.$bili_live_r_box.innerHTML = AV.fromStr(https(ranking_list.map((d, i) => `
    ${i + 1}
    ${d.uname}${d.title}
    `).join('')))) - }) - - this.$carousel_box.addEventListener('pointerenter', this.carouselPointerEnter); - this.$carousel_box.addEventListener('pointerleave', this.carouselPointerLeave); - this.addEventListener('click', e => { - const { target } = e; - if (target instanceof HTMLDivElement && target.classList.contains('wl')) { - // 处理稍后再看 - const d = target.classList.toggle('wld'); - target.title = d ? '移除' : '稍后再看'; - const { aid } = target.dataset; - const csrf = cookie.get('bili_jct'); - if (aid && csrf) { - if (d) { - add(csrf, aid).catch(() => { - target.classList.remove('wld'); - target.title = '稍后再看'; - }) - } else { - del(csrf, aid).catch(() => { - target.classList.add('wld'); - target.title = '移除'; - }) - } - } else { - target.classList.remove('wld'); - target.title = '稍后再看'; - } - e.stopPropagation(); - e.preventDefault(); - } - }); - this.$trig.addEventListener('change', () => { - const d = new FormData(this.$trig); - const i = [...d.values()][0]; - const next = this.$carousel_box.querySelector(`[data-i="${i}"]`); - if (next instanceof HTMLElement) { - this.$carousel_box.querySelector(".show")?.classList.remove('show'); - next.classList.add('show'); - this.$carousel_box.href = next.dataset.href!; - this.$carousel_box.title = next.title; - } - }); - this.$recommend_flesh.addEventListener('click', () => { - this.$recommend_flesh.disabled = true; - this.rcmd().finally(() => { - this.$recommend_flesh.disabled = false; - }) - }); - this.$bili_live_l.addEventListener('click', e => { - const { target } = e; - if (target instanceof HTMLButtonElement && target.classList.contains('read-push')) { - target.disabled = true; - getMoreRecList().then(({ recommend_room_list }) => { - this.$bili_live_l.lastElementChild!.innerHTML = https(recommend_room_list.map(d => `

    ${d.title}

    ${d.area_v2_parent_name}·${d.area_v2_name}

    ${d.uname}
    ${Format.carry(d.online)}
    `).join('')); - }).finally(() => { - target.disabled = false; - }); - } - }); - this.$form.addEventListener('change', () => { - const d = new FormData(this.$form); - const i = +[...d.values()][0]; - switch (i) { - case 0: { - this.$bili_live.scrollIntoView({ behavior: 'smooth', block: 'center' }); - break; - } - case 1: { - this.$bili_douga.scrollIntoView({ behavior: 'smooth', block: 'center' }); - break; - } - case 2: { - this.$timeline_bangumi.scrollIntoView({ behavior: 'smooth', block: 'center' }); - break; - } - case 3: { - this.$timeline_guochuang.scrollIntoView({ behavior: 'smooth', block: 'center' }); - break; - } - case 4: { - this.$bili_music.scrollIntoView({ behavior: 'smooth', block: 'center' }); - break; - } - case 5: { - this.$bili_dance.scrollIntoView({ behavior: 'smooth', block: 'center' }); - break; - } - case 6: { - this.$bili_game.scrollIntoView({ behavior: 'smooth', block: 'center' }); - break; - } - case 7: { - this.$bili_knowledge.scrollIntoView({ behavior: 'smooth', block: 'center' }); - break; - } - case 8: { - this.$bili_technology.scrollIntoView({ behavior: 'smooth', block: 'center' }); - break; - } - case 9: { - this.$bili_life.scrollIntoView({ behavior: 'smooth', block: 'center' }); - break; - } - case 10: { - this.$bili_kichiku.scrollIntoView({ behavior: 'smooth', block: 'center' }); - break; - } - case 11: { - this.$bili_fashion.scrollIntoView({ behavior: 'smooth', block: 'center' }); - break; - } - case 12: { - this.$bili_news.scrollIntoView({ behavior: 'smooth', block: 'center' }); - break; - } - case 13: { - this.$bili_ent.scrollIntoView({ behavior: 'smooth', block: 'center' }); - break; - } - case 14: { - this.$bili_movie.scrollIntoView({ behavior: 'smooth', block: 'center' }); - break; - } - case 15: { - this.$bili_tv.scrollIntoView({ behavior: 'smooth', block: 'center' }); - break; - } - case 16: { - this.$bili_cinephile.scrollIntoView({ behavior: 'smooth', block: 'center' }); - break; - } - case 17: { - this.$bili_documentary.scrollIntoView({ behavior: 'smooth', block: 'center' }); - break; - } - } - }); - - this.carouselPointerLeave(); - } - - private carouselPointerLeave = () => { - this.rollTimer = setInterval(() => { - const now = this.$carousel_box.querySelector('.show'); - if (now) { - const next = now.nextSibling || this.$carousel_box.firstElementChild; - if (next instanceof HTMLElement && next !== now) { - next.classList.toggle('show'); - now.classList.toggle('show'); - this.$carousel_box.href = next.dataset.href!; - this.$carousel_box.title = next.title; - const trig = this.$trig.querySelector(`[value="${next.dataset.i}"]`); - trig && ((trig).checked = true); - } - } - }, 5e3); - } - - private carouselPointerEnter = () => { - clearInterval(this.rollTimer); - } - - private rcmd() { - return rcmd().then(d => { - // 主推荐视频位 - this.$recommend_module.innerHTML = https(AV.fromStr(d.map(d => `

    ${d.title}

    up主:${d.owner.name}

    播放:${Format.carry(d.stat.view)}

    `).join(''))) - }); - } -} - -//////////////////////////// 全局增强 //////////////////////////// -declare global { - /** 基于哈希消息认证码的一次性口令的密钥 */ - const __BILI_HOME_STYLE__: string; -} \ No newline at end of file diff --git a/src/main/bilibili/home/ranking.ts b/src/main/bilibili/home/ranking.ts deleted file mode 100644 index 8f1143c..0000000 --- a/src/main/bilibili/home/ranking.ts +++ /dev/null @@ -1,108 +0,0 @@ -import { REGION, region as dynamicRegion } from "../../../io/com/bilibili/api/x/web-interface/dynamic/region"; -import { newlist } from "../../../io/com/bilibili/api/x/web-interface/newlist"; -import { region as rankingRegion } from "../../../io/com/bilibili/api/x/web-interface/ranking/region"; -import svg_icon_danmaku from "../../../player/assets/svg/icon-danmaku.svg"; -import svg_icon_played from "../../../player/assets/svg/icon-played.svg"; -import svg_reflesh from "../../../player/assets/svg/reflesh.svg"; -import svg_sent from "../../../player/assets/svg/sent.svg"; -import { customElement } from "../../../utils/Decorator/customElement"; -import { AV } from "../../../utils/av"; -import { Element } from "../../../utils/element"; -import { Format } from "../../../utils/fomat"; -import { https } from "../../../utils/https"; - -/** 顶栏 */ -@customElement('div') -export class Ranking extends HTMLDivElement { - - /** - * 需要监听变动的属性。 - * 与实例方法`attributeChangedCallback`配合使用。 - * 此字符串序列定义了`attributeChangedCallback`回调时的第一个参数的可能值。 - */ - // static observedAttributes = []; - - /** - * 在属性更改、添加、移除或替换时调用。 - * 需要与静态属性`observedAttributes`配合使用。 - * 此回调的第一个参数在`observedAttributes`数组中定义。 - */ - // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} - - /** 初始化标记 */ - // #inited = false; - - /** 每当元素添加到文档中时调用。 */ - // connectedCallback() {} - - /** 每当元素从文档中移除时调用。 */ - // disconnectedCallback() {} - - /** 每当元素被移动到新文档中时调用。 */ - // adoptedCallback() {} - - private $lCon = Element.add('div', { class: 'l-con' }, this, `
    更多${svg_sent}
    `); - - private $rCon = Element.add('div', { class: 'r-con' }, this, `
    排行
    查看更多${svg_sent}`); - - private $lForm = Element.add('form', undefined, this.$lCon.firstElementChild); - - private newList = false; - - constructor( - name: string, - href: string, - private rid: REGION, - ) { - super(); - - this.classList.add('bili-ranking'); - - (this.$lCon.firstElementChild!.childNodes[1]).href = href; - (this.$lCon.firstElementChild!.childNodes[3]).href = href; - (this.$lCon.firstElementChild!.childNodes[1]).text = name; - (this.$rCon.childNodes[2]).href = `/ranking/all/${rid}/1/3/`; - - const id = crypto.randomUUID(); - this.$lForm.innerHTML = ``; - - this.$lCon.addEventListener('click', e => { - const { target } = e; - if (target instanceof HTMLButtonElement && target.classList.contains('read-push')) { - target.disabled = true; - this.fleshRegion().finally(() => { - target.disabled = false; - }); - } - }); - this.$lForm.addEventListener('change', () => { - const d = new FormData(this.$lForm); - const i = +[...d.values()][0]; - this.newList = Boolean(i); - this.fleshRegion(); - }); - - new IntersectionObserver((entries, observer) => { - for (const entry of entries) { - if (entry.isIntersecting) { - observer.disconnect(); - this.fleshRegion(); - this.rankRegion(); - break; - } - } - }).observe(this); - } - - private fleshRegion() { - return (this.newList ? newlist(this.rid) : dynamicRegion(this.rid)).then(d => { - this.$lCon.lastElementChild!.innerHTML = AV.fromStr(https(d.map(d => `

    ${d.title}

    ${svg_icon_played}${Format.carry(d.stat.view)}${svg_icon_danmaku}${Format.carry(d.stat.danmaku)}

    ${Format.fmSeconds(d.duration)}
    `).join(''))); - }); - } - - private rankRegion() { - rankingRegion(this.rid).then(d => { - d && ((this.$rCon.childNodes[1]).innerHTML = AV.fromStr(https(d.map((d, i) => `
    ${i + 1}
    ${i ? '' : ``}${d.title}${i ? '' : `
    `}
    `).join('')))); - }) - } -} \ No newline at end of file diff --git a/src/main/bilibili/home/style/index.css b/src/main/bilibili/home/style/index.css deleted file mode 100644 index 64d2970..0000000 --- a/src/main/bilibili/home/style/index.css +++ /dev/null @@ -1,768 +0,0 @@ -@import url(./ranking.css); -@import url(./timeline.css); - -@scope { - :scope { - display: flex; - flex-direction: column; - row-gap: 30px; - font-size: 12px; - inline-size: 980px; - min-inline-size: 980px; - container: home / inline-size; - margin-inline: auto; - - - @media screen and (min-width:1400px) { - - & { - inline-size: 1160px; - } - } - - @media screen and (min-width:2500px) { - - & { - inline-size: 1920px; - } - } - - &, - div { - box-sizing: border-box; - } - - a { - outline: none; - text-decoration: none; - } - } - - button { - cursor: pointer; - background-color: initial; - display: flex; - justify-content: center; - align-items: center; - transition: background-color .3s; - box-sizing: border-box; - margin: 0; - padding: 0; - border: 0; - user-select: none; - - &:disabled { - pointer-events: none; - - svg { - fill: #ccd0d7; - } - } - - &:hover { - background-color: #f4f5f7; - color: #6d757a; - } - } -} - -.chief-recommend { - block-size: calc(100cqi / 391 * 88); - display: flex; - column-gap: 20px; - - >.carousel-box { - flex: 2; - position: relative; - overflow: hidden; - display: flex; - anchor-name: --carousel; - - >img { - position: absolute; - inline-size: 100%; - block-size: 100%; - translate: 0; - display: block; - transition: all 0.7s allow-discrete; - - - &:not(.show) { - /* 隐藏样式:显示区域左侧外部 */ - translate: -100%; - display: none; - } - - @starting-style { - /* 初始样式:显示区域右侧外部 */ - translate: 40cqi; - } - } - - &::after { - content: attr(title); - position: absolute; - inset-inline-start: 1em; - inset-block-end: .75em; - color: #fff; - text-shadow: #000 1px 1px 2px, #000 0px 0px 1px; - font-size: 14px; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - } - } - - >.trig { - position: absolute; - inset-inline-end: anchor(--carousel end); - inset-block-end: anchor(--carousel end); - display: flex; - column-gap: 1em; - margin: 1em; - - >input { - cursor: pointer; - inline-size: 1.1em; - aspect-ratio: 1; - transition: all 0.7s; - } - } - - >.recommend-module { - flex: 3; - display: flex; - flex-wrap: wrap; - gap: 20px; - overflow: hidden; - anchor-name: --recommend-module; - - >a { - block-size: calc((100% - 20px) / 2); - aspect-ratio: 16 / 9; - position: relative; - - >img { - display: block; - inline-size: 100%; - block-size: 100%; - } - - >.mask { - position: absolute; - inset-inline-start: 1em; - inset-block-end: .75em; - color: #fff; - - >p { - overflow: hidden; - word-break: break-all; - word-wrap: break-word; - } - } - - &:not(:hover) { - - .wl, - .author, - .play { - display: none; - } - } - - &:hover { - >.mask { - inset: 0; - block-size: 100%; - padding: 1em; - background-color: rgba(0, 0, 0, .7); - display: flex; - flex-direction: column; - - >.title { - flex: 1; - } - } - - >.wl { - inline-size: 22px; - aspect-ratio: 1; - background-image: url(../../../../player/assets/image/watchlater.png); - position: absolute; - inset-inline-end: 1em; - inset-block-end: .75em; - } - - >.wl.wld { - content: url(../../../../player/assets/image/checked.png); - } - } - } - } - - >.recommend-flesh { - position: absolute; - inset-inline-start: anchor(--recommend-module end); - inset-block-start: anchor(--carousel start); - border: 1px solid #ccd0d7; - padding-block: 1em; - padding-inline: .5em; - border-radius: 4px; - - >svg { - font-size: 24px; - inline-size: 1em; - aspect-ratio: 1; - fill: #99a2aa; - transition: all .5s; - } - - &:hover>svg { - fill: #00a1d6; - } - - &:disabled>svg { - rotate: 1turn; - } - } -} - -.home-popularize { - display: flex; - justify-content: space-between; - - >.popularize { - display: flex; - flex-direction: column; - row-gap: 15px; - - .icon-promote { - background-position: -141px -75px; - } - - .storey-box { - max-block-size: calc(100cqi / 391 * 44); - } - } - - >.rank_list { - flex-shrink: 0; - inline-size: 260px; - - >a { - block-size: 34px; - border-radius: 4px; - display: flex; - justify-content: center; - align-items: center; - background-color: #e5e9ef; - white-space: nowrap; - color: #6d757a; - transition: .2s; - - &:hover { - color: #00a1d6; - } - } - } -} - -.headline { - font-size: 24px; - font-weight: 400; - display: flex; - align-items: center; - column-gap: .5em; - position: relative; - - >.icon-t { - inline-size: 40px; - aspect-ratio: 1; - vertical-align: middle; - display: inline-block; - background-image: url(//static.hdslb.com/images/base/icons.png); - } - - >.name { - color: inherit; - font-weight: initial; - - &:hover { - color: #00a1d6; - } - } - - >.online { - color: #99a2aa; - font-size: 12px; - font-weight: initial; - - >span { - color: #00a1d6; - } - } - - >.link-more, - >.read-push { - position: absolute; - inset-inline-end: 0; - font-size: 12px; - font-weight: initial; - background-color: #fff; - border: 1px solid #ccd0d7; - color: #555; - border-radius: 4px; - padding-inline: 1em; - padding-block: .25em; - display: flex; - justify-content: center; - align-items: center; - cursor: pointer; - box-sizing: border-box; - transition: all .2s; - - >svg { - inline-size: 1em; - aspect-ratio: 1; - fill: #555; - transition: all .5s; - pointer-events: none; - } - - &:hover { - background-color: #ccd0d7; - } - } - - >.link-more { - inline-size: 67px; - - &:hover { - column-gap: 5px; - } - } - - >.read-push { - inset-inline-end: 75px; - inline-size: fit-content; - column-gap: .5em; - - &:hover>svg { - rotate: 1turn; - } - } -} - -.storey-box { - max-block-size: calc(100cqi / 391 * 88); - display: flex; - justify-content: space-between; - flex-wrap: wrap; - gap: 20px; - overflow: hidden; - - >a { - flex-shrink: 0; - inline-size: calc(((100cqi / 391 * 88 - 20px) / 2 - 48px) / 9 * 16); - block-size: calc((100cqi / 391 * 88 - 20px) / 2); - display: flex; - flex-direction: column; - gap: 8px; - position: relative; - - >img { - display: block; - inline-size: 100%; - aspect-ratio: 16 / 9; - anchor-name: --storey-img; - } - - >p { - &.t { - block-size: 40px; - line-height: 20px; - transition: all .2s linear; - color: #222; - word-wrap: break-word; - word-break: break-all; - overflow: hidden; - } - - &.num { - inline-size: 100%; - position: absolute; - inset-block-end: 0; - block-size: 20px; - line-height: 20px; - color: #9ba3ab; - background-color: #fff; - transition: all .3s; - display: flex; - align-items: center; - - >span { - flex: 1; - display: flex; - gap: .5em; - overflow: hidden; - white-space: nowrap; - text-overflow: clip; - - >svg { - inline-size: 1em; - aspect-ratio: 1; - fill: #99a2aa; - } - } - } - } - - &:not(:hover) { - - >.mask, - >.wl, - >.duration, - >.online { - display: none; - } - } - - &:hover { - >p { - &.t { - color: #00a1d6; - } - - &.num { - inset-block-end: -2em; - opacity: 0; - } - } - - >.mask { - position: absolute; - inset-inline-start: anchor(--storey-img start); - inset-inline-end: anchor(--storey-img end); - inset-block-start: anchor(--storey-img start); - inset-block-end: anchor(--storey-img end); - background-color: rgba(0, 0, 0, .2); - } - - >.wl { - inline-size: 22px; - aspect-ratio: 1; - background-image: url(../../../../player/assets/image/watchlater.png); - position: absolute; - inset-inline-end: anchor(--storey-img end); - inset-block-end: anchor(--storey-img end); - margin-inline-end: 1em; - margin-block-end: .75em; - } - - >.duration { - position: absolute; - inset-inline-start: anchor(--storey-img start); - inset-block-end: anchor(--storey-img end); - color: #fff; - margin-inline-start: 1em; - margin-block-end: .75em; - } - - >.online { - position: absolute; - inset-inline-end: anchor(--storey-img end); - inset-block-end: anchor(--storey-img end); - color: #fff; - margin-inline-end: 1em; - margin-block-end: .75em; - display: flex; - align-items: center; - column-gap: 5px; - - &::before { - inline-size: 1em; - aspect-ratio: 1; - content: url(../../../../player/assets/image/online.png); - } - } - - >.wl.wld { - content: url(../../../../player/assets/image/checked.png); - } - } - } -} - -.bili-live { - display: flex; - justify-content: space-between; - column-gap: 20px; - max-block-size: calc(100cqi / 391 * 88 + 60px); - - >.l-con { - display: flex; - flex-direction: column; - row-gap: 15px; - - .icon-promote { - background-position: -141px -652px; - } - - } - - >.r-con { - flex-shrink: 0; - inline-size: 260px; - display: flex; - flex-direction: column; - row-gap: 20px; - - >.bili-tab { - flex-shrink: 0; - block-size: 24px; - display: flex; - column-gap: 12px; - - >label { - position: relative; - cursor: pointer; - padding-block-start: 1px; - padding-block-end: 2px; - border-block-end: 1px solid transparent; - transition: all .2s; - - >input { - appearance: none; - } - - &:has(input:checked) { - border-block-end: 1px solid #00a1d6; - color: #00a1d6; - - &::after { - content: ""; - position: absolute; - inset-inline-start: 50%; - inset-block-end: 0; - transform: translateX(-3px); - border-block-end: 3px solid #00a1d6; - border-block-start: 0; - border-inline-start: 3px solid rgba(0, 0, 0, 0); - border-inline-end: 3px solid rgba(0, 0, 0, 0); - box-sizing: border-box; - } - } - } - } - - >.tab-box { - flex: 1 0; - display: flex; - flex-direction: column; - overflow-y: auto; - scrollbar-width: none; - row-gap: 14px; - - >.r-item { - block-size: 40px; - cursor: pointer; - display: grid; - grid-template-columns: repeat(12, 1fr); - grid-template-areas: - "nm pv pv un un un un un un un un un" - "nm pv pv ut ut ut ut ut ut ut ut ut"; - - >.number { - grid-area: nm; - - >span { - color: #fff; - background-color: #b8c0cc; - border-radius: 4px; - padding-inline: 3px; - } - } - - >.preview { - grid-area: pv; - border-radius: 50%; - overflow: hidden; - - >img { - font-size: 40px; - inline-size: 1em; - aspect-ratio: 1; - } - } - - >.u-name { - grid-area: un; - color: #222; - display: flex; - justify-content: space-between; - align-items: center; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - - &::after { - content: url(../../../../player/assets/image/live-eye.png) attr(data-online); - color: #99a2aa; - display: flex; - align-items: center; - } - } - - >.u-title { - grid-area: ut; - color: #99a2aa; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - } - - a { - color: #99a2aa; - - &:hover { - color: #00a1d6; - } - } - - &:nth-child(1)>.number>span, - &:nth-child(2)>.number>span, - &:nth-child(3)>.number>span { - background-color: #f25d8e; - } - } - - &:empty { - align-items: center; - - &::before { - content: "没有数据(-_-#)"; - color: #99a2aa; - } - } - } - } -} - -.bili-douga>.l-con .icon-promote { - background-position: -141px -908px; -} - -.bili-music>.l-con .icon-promote { - background-position: -140px -266px; -} - -.bili-dance>.l-con .icon-promote { - background-position: -141px -461px; -} - -.bili-game>.l-con .icon-promote { - background-position: -141px -203px; -} - -.bili-knowledge>.l-con .icon-promote { - background-position: -141px -525px; -} - -.bili-technology>.l-con .icon-promote { - background-position: -140px -1741px; -} - -.bili-life>.l-con .icon-promote { - background-position: -137px -970px; -} - -.bili-kichiku>.l-con .icon-promote { - background-position: -141px -332px; -} - -.bili-fashion>.l-con .icon-promote { - background-position: -141px -718px; -} - -.bili-ent>.l-con .icon-promote { - background-position: -141px -1032px; -} - -.bili-movie>.l-con .icon-promote { - background-position: -141px -396px; -} - -.bili-tv>.l-con .icon-promote { - background-position: -141px -845px; -} - -.bili-cinephile>.l-con .icon-promote { - background-position: -140px -1356px; -} - -.bili-documentary>.l-con .icon-promote { - background-position: -140px -1292px; -} - -.timeline-bangumi>.l-con .icon-promote { - background-position: -141px -140px; -} - -.timeline-guochuang>.l-con .icon-promote { - background-position: -140px -1611px; -} - -.nav-form { - position: fixed; - inset-inline-end: calc((100vi - 980px) / 2); - inset-block-start: 50%; - padding-block-start: 6px; - font-size: 12px; - background-color: #f6f9fa; - border: 1px solid #e5e9ef; - overflow: hidden; - border-radius: 4px; - border-start-end-radius: 4px; - border-start-start-radius: 4px; - translate: 150% -50%; - display: flex; - flex-direction: column; - - @media screen and (min-width:1400px) { - - & { - inset-inline-end: calc((100vi - 1160px) / 2); - } - } - - @media screen and (min-width:2500px) { - - & { - inset-inline-end: calc((100vi - 1920px) / 2); - } - } - - >label { - block-size: 24px; - display: flex; - justify-content: center; - align-items: center; - transition: background-color .3s, color .3s; - cursor: pointer; - user-select: none; - - >input { - appearance: none; - } - - &:hover, - &:has(input:checked) { - background-color: #00a1d6; - color: #fff; - } - - &:has(input:checked) { - pointer-events: none; - } - } -} \ No newline at end of file diff --git a/src/main/bilibili/home/style/ranking.css b/src/main/bilibili/home/style/ranking.css deleted file mode 100644 index fad9aab..0000000 --- a/src/main/bilibili/home/style/ranking.css +++ /dev/null @@ -1,176 +0,0 @@ -.bili-ranking { - display: flex; - justify-content: space-between; - column-gap: 20px; - block-size: calc(100cqi / 391 * 88 + 60px); - - >.l-con { - flex: 1; - display: flex; - flex-direction: column; - row-gap: 15px; - - >.headline>form { - display: flex; - column-gap: 12px; - font-size: 12px; - - >label { - position: relative; - cursor: pointer; - padding-block-start: 1px; - padding-block-end: 2px; - border-block-end: 1px solid transparent; - transition: all .2s; - - >input { - appearance: none; - } - - &:has(input:checked) { - border-block-end: 1px solid #00a1d6; - color: #00a1d6; - - &::after { - content: ""; - position: absolute; - inset-inline-start: 50%; - inset-block-end: 0; - transform: translateX(-3px); - border-block-end: 3px solid #00a1d6; - border-block-start: 0; - border-inline-start: 3px solid rgba(0, 0, 0, 0); - border-inline-end: 3px solid rgba(0, 0, 0, 0); - box-sizing: border-box; - } - } - } - } - } - - >.r-con { - flex-shrink: 0; - inline-size: 260px; - display: flex; - flex-direction: column; - row-gap: 20px; - - >header { - flex-shrink: 0; - block-size: 24px; - font-size: 18px; - display: flex; - } - - >.tab-box { - flex: 1 0; - display: flex; - flex-direction: column; - overflow-y: auto; - scrollbar-width: none; - row-gap: 20px; - - >.item { - display: flex; - column-gap: .5em; - cursor: pointer; - position: relative; - - >.number>span { - color: #fff; - background-color: #b8c0cc; - border-radius: 4px; - padding-inline: 3px; - } - - >img { - inline-size: 80px; - aspect-ratio: 16 / 9; - anchor-name: --tab-img; - } - - >.ri { - color: #222; - display: -webkit-box; - -webkit-box-orient: vertical; - -webkit-line-clamp: 3; - overflow: hidden; - text-overflow: ellipsis; - } - - &:nth-child(1)>.number>span, - &:nth-child(2)>.number>span, - &:nth-child(3)>.number>span { - background-color: #f25d8e; - } - - a { - color: #99a2aa; - - &:hover { - color: #00a1d6; - } - } - - &:not(:has(>img))>.ri { - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - } - - &:not(:hover) .wl { - display: none; - } - - &:hover { - - >.wl { - inline-size: 22px; - aspect-ratio: 1; - background-image: url(../../../../player/assets/image/watchlater.png); - position: absolute; - inset-inline-end: anchor(--tab-img end); - inset-block-end: anchor(--tab-img end); - margin-inline-end: .3em; - margin-block-end: .3em; - } - - >.wl.wld { - content: url(../../../../player/assets/image/checked.png); - } - } - } - - &:empty { - align-items: center; - - &::before { - content: "没有数据(-_-#)"; - color: #99a2aa; - } - } - } - - >.more-link { - flex-shrink: 0; - block-size: 24px; - background-color: #e5e9ef; - border: 1px solid #e0e6ed; - color: #222; - border-radius: 4px; - transition: .2s; - display: flex; - justify-content: center; - align-items: center; - - >svg { - inline-size: 1em; - aspect-ratio: 1; - } - } - } - - &.no-icon .icon-t { - display: none; - } -} \ No newline at end of file diff --git a/src/main/bilibili/home/style/timeline.css b/src/main/bilibili/home/style/timeline.css deleted file mode 100644 index 252a250..0000000 --- a/src/main/bilibili/home/style/timeline.css +++ /dev/null @@ -1,264 +0,0 @@ -.bili-timeline { - display: flex; - justify-content: space-between; - column-gap: 20px; - block-size: calc(100cqi / 391 * 132 + 60px); - - >.l-con { - flex: 1; - - .bili-tab { - flex: 1; - display: flex; - - >label { - flex: 1 0; - border-block-end: 1px solid #e5e9ef; - font-size: 18px; - padding-block-end: .5em; - display: flex; - justify-content: center; - align-items: center; - position: relative; - cursor: pointer; - transition: all .2s; - - >input { - appearance: none; - } - - &:has(input:checked) { - border-block-end: 1px solid #00a1d6; - color: #00a1d6; - - &::after { - content: ""; - position: absolute; - inset-inline-start: 50%; - inset-block-end: 0; - transform: translateX(-3px); - border-block-end: 3px solid #00a1d6; - border-block-start: 0; - border-inline-start: 3px solid rgba(0, 0, 0, 0); - border-inline-end: 3px solid rgba(0, 0, 0, 0); - box-sizing: border-box; - } - } - } - } - - .c-clink { - border: 1px solid #f25d8e; - block-size: 34px; - color: #f25d8e; - font-size: 14px; - border-radius: 4px; - transition: .1s; - padding-inline: .5em; - cursor: pointer; - background-color: initial; - display: flex; - justify-content: center; - align-items: center; - box-sizing: border-box; - user-select: none; - - >svg { - inline-size: 1em; - aspect-ratio: 1; - fill: #f25d8e; - } - - &:hover { - color: #fff; - background-color: #f25d8e; - - >svg { - fill: #fff; - } - } - } - - .timing-box { - block-size: calc(100cqi / 391 * 132); - margin-block-start: 26px; - margin-block-end: 40px; - display: flex; - flex-wrap: wrap; - align-content: flex-start; - column-gap: 34px; - row-gap: 36px; - overflow-y: auto; - scrollbar-width: none; - - >.timing-card { - inline-size: calc((100cqi / 391* 88 - 20px) / 6 * 5); - block-size: calc((100cqi / 391* 88 - 20px) / 3); - border-radius: 4px; - display: grid; - grid-template-columns: repeat(5, 1fr); - grid-template-areas: - "pc pc t t t" - "pc pc t t t" - "pc pc t t t" - "pc pc ud ud ud"; - column-gap: 12px; - - >a { - color: #222; - transition: color .2s; - - &:hover { - color: #00a1d6; - } - } - - >.pic { - grid-area: pc; - aspect-ratio: 1; - overflow: hidden; - border-radius: 4px; - - >img { - inline-size: 100%; - block-size: 100%; - object-fit: none; - } - } - - >.t { - grid-area: t; - word-break: break-all; - word-wrap: break-word; - overflow: hidden; - text-overflow: ellipsis; - } - - >.update { - grid-area: ud; - color: #aaa; - white-space: nowrap; - display: flex; - align-items: center; - - >a { - color: #fff; - border-radius: 9px; - padding-inline: 4px; - text-overflow: ellipsis; - overflow: hidden; - background: #b8c0cc; - } - - &.published>a { - background: #ff8eb3; - } - } - } - - &:empty { - justify-content: center; - - &::before { - content: url(https://s1.hdslb.com/bfs/static/jinkela/home/asserts/bgm-nodata.png); - } - } - } - } - - >.r-con { - flex-shrink: 0; - inline-size: 260px; - display: flex; - flex-direction: column; - row-gap: 20px; - - >header { - flex-shrink: 0; - block-size: 24px; - font-size: 18px; - display: flex; - } - - >.tab-box { - flex: 1 0; - display: flex; - flex-direction: column; - overflow-y: auto; - scrollbar-width: none; - row-gap: 20px; - overflow-y: auto; - scrollbar-width: none; - - >.item { - display: flex; - column-gap: .5em; - cursor: pointer; - position: relative; - - >.number>span { - color: #fff; - background-color: #b8c0cc; - border-radius: 4px; - padding-inline: 3px; - } - - >.ri-title { - display: contents; - color: #222; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - - >.ri-total { - color: #99a2aa; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - } - } - - &:nth-child(1)>.number>span, - &:nth-child(2)>.number>span, - &:nth-child(3)>.number>span { - background-color: #f25d8e; - } - - a { - color: #99a2aa; - - &:hover { - color: #00a1d6; - } - } - } - - &:empty { - align-items: center; - - &::before { - content: "没有数据(-_-#)"; - color: #99a2aa; - } - } - } - - >.more-link { - flex-shrink: 0; - block-size: 24px; - background-color: #e5e9ef; - border: 1px solid #e0e6ed; - color: #222; - border-radius: 4px; - transition: .2s; - display: flex; - justify-content: center; - align-items: center; - - >svg { - inline-size: 1em; - aspect-ratio: 1; - } - } - } -} \ No newline at end of file diff --git a/src/main/bilibili/home/style/video-info.css b/src/main/bilibili/home/style/video-info.css deleted file mode 100644 index bdc7f07..0000000 --- a/src/main/bilibili/home/style/video-info.css +++ /dev/null @@ -1,107 +0,0 @@ -@scope { - :scope { - inset-area: block-start; - inline-size: 320px; - border: 1px solid #ccd0d7; - border-radius: 4px; - box-shadow: 0 2px 4px rgba(0, 0, 0, .16); - box-sizing: border-box; - overflow: hidden; - background-color: #fff; - padding: 12px; - font-size: 12px; - row-gap: .75em; - margin: 0; - - &:popover-open, - &:hover { - display: flex; - flex-direction: column; - } - } -} - -.v-title { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - - &:empty { - display: none; - } -} - -.v-info { - color: #99a2aa; - display: flex; - column-gap: 10px; - - &:empty { - display: none; - } -} - -.v-preview { - padding-block: 8px; - padding-block-end: 12px; - border-block-end: 1px solid #e5e9ef; - display: flex; - column-gap: 8px; - - >img { - block-size: 63px; - } - - >p { - margin: 0; - display: -webkit-box; - -webkit-box-orient: vertical; - -webkit-line-clamp: 3; - overflow: hidden; - text-overflow: ellipsis; - color: #99a2aa; - line-height: 21px; - } -} - -.v-data { - display: flex; - justify-content: space-between; - - >span { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - color: #99a2aa; - display: flex; - align-items: center; - column-gap: 4px; - - >i { - display: inline-block; - inline-size: 12px; - block-size: 12px; - background-image: url(//static.hdslb.com/images/base/icons.png); - } - - &.play>i { - background-position: -282px -90px; - } - - &.danmu>i { - background-position: -282px -218px; - } - - &.star>i { - background-position: -282px -346px; - } - - &.coin>i { - background-position: -282px -410px; - } - - &.play>i { - background-position: -282px -90px; - } - } -} \ No newline at end of file diff --git a/src/main/bilibili/home/style/video-info.d.css.ts b/src/main/bilibili/home/style/video-info.d.css.ts deleted file mode 100644 index 3e963d8..0000000 --- a/src/main/bilibili/home/style/video-info.d.css.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const css_video_info: string; -export default css_video_info; \ No newline at end of file diff --git a/src/main/bilibili/home/timeline.ts b/src/main/bilibili/home/timeline.ts deleted file mode 100644 index 1a84dd2..0000000 --- a/src/main/bilibili/home/timeline.ts +++ /dev/null @@ -1,107 +0,0 @@ -import { list } from "../../../io/com/bilibili/api/pgc/web/rank/list"; -import { IEpisodes, TYPE, timeline } from "../../../io/com/bilibili/api/pgc/web/timeline"; -import { REGION } from "../../../io/com/bilibili/api/x/web-interface/dynamic/region"; -import svg_sent from "../../../player/assets/svg/sent.svg"; -import { customElement } from "../../../utils/Decorator/customElement"; -import { AV } from "../../../utils/av"; -import { Element } from "../../../utils/element"; -import { https } from "../../../utils/https"; - -/** 顶栏 */ -@customElement('div') -export class Timeline extends HTMLDivElement { - - /** - * 需要监听变动的属性。 - * 与实例方法`attributeChangedCallback`配合使用。 - * 此字符串序列定义了`attributeChangedCallback`回调时的第一个参数的可能值。 - */ - // static observedAttributes = []; - - /** - * 在属性更改、添加、移除或替换时调用。 - * 需要与静态属性`observedAttributes`配合使用。 - * 此回调的第一个参数在`observedAttributes`数组中定义。 - */ - // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} - - /** 初始化标记 */ - // #inited = false; - - /** 每当元素添加到文档中时调用。 */ - // connectedCallback() {} - - /** 每当元素从文档中移除时调用。 */ - // disconnectedCallback() {} - - /** 每当元素被移动到新文档中时调用。 */ - // adoptedCallback() {} - - private $lCon = Element.add('div', { class: 'l-con' }, this, `
    `); - - private $rCon = Element.add('div', { class: 'r-con' }, this, `
    排行
    查看更多${svg_sent}`); - - private $lTab = Element.add('form', { class: 'bili-tab' }, this.$lCon.firstElementChild); - - private $timingBox = Element.add('div', { class: 'timing-box' }, this.$lCon); - - private episodes: IEpisodes[][] = []; - - #tab = 0; - - set $tab(v: number | string) { - this.#tab = +v || 0; - this.flushTabs(); - } - - constructor( - name: string, - href: string, - private type: TYPE, - private rid: REGION, - ) { - super(); - - this.classList.add('bili-timeline'); - (this.$lCon.firstElementChild!.childNodes[1]).href = href; - (this.$lCon.firstElementChild!.childNodes[1]).text = name; - (this.$rCon.childNodes[2]).href = `/ranking/bangumi/${rid}/1/3/`; - (this.$lCon.firstElementChild).insertAdjacentHTML('beforeend', `新番时间表${svg_sent}`); - - const id = crypto.randomUUID(); - this.$lTab.insertAdjacentHTML('afterbegin', ` - - - - - - -`); - - this.$lTab.addEventListener('change', () => { - const form = new FormData(this.$lTab); - this.$tab = +[...form.values()][0]; - }); - - timeline(type).then(d => { - d.forEach(d => { - this.episodes[d.day_of_week] || (this.episodes[d.day_of_week] = []); - this.episodes[d.day_of_week] = this.episodes[d.day_of_week].concat(d.episodes); - this.episodes[0] || (this.episodes[0] = []); - this.episodes[0] = this.episodes[0].concat(d.episodes.filter(d => d.published)); - }); - this.episodes.forEach(d => { - d.sort((a, b) => a.pub_ts > b.pub_ts ? 1 : -1) - }); - this.$tab = 0; - }); - - list(type).then(d => { - (this.$rCon.childNodes[1]).innerHTML = AV.fromStr(https(d.map((d, i) => ``).join(''))) - }) - } - - private flushTabs() { - this.$timingBox.innerHTML = AV.fromStr(https(this.episodes[this.#tab].map(d => ``).join(''))) - } -} \ No newline at end of file diff --git a/src/main/bilibili/home/video-info.ts b/src/main/bilibili/home/video-info.ts deleted file mode 100644 index cbb697c..0000000 --- a/src/main/bilibili/home/video-info.ts +++ /dev/null @@ -1,116 +0,0 @@ -import { pgcAppSeason } from "../../../io/com/bilibili/api/pgc/view/v2/app/season"; -import { cards } from "../../../io/com/bilibili/api/x/article/cards"; -import { customElement } from "../../../utils/Decorator/customElement"; -import { Element } from "../../../utils/element"; -import { Format } from "../../../utils/fomat"; -import { https } from "../../../utils/https"; -import { TOTP } from "../../../utils/TOTP"; -import css_video_info from "./style/video-info.css"; - -@customElement(undefined, `video-info-${TOTP.now()}`) -export class VideoInfo extends HTMLElement { - - /** - * 需要监听变动的属性。 - * 与实例方法`attributeChangedCallback`配合使用。 - * 此字符串序列定义了`attributeChangedCallback`回调时的第一个参数的可能值。 - */ - // static observedAttributes = []; - - /** - * 在属性更改、添加、移除或替换时调用。 - * 需要与静态属性`observedAttributes`配合使用。 - * 此回调的第一个参数在`observedAttributes`数组中定义。 - */ - // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} - - /** 初始化标记 */ - // #inited = false; - - /** 每当元素添加到文档中时调用。 */ - // connectedCallback() { } - - /** 每当元素从文档中移除时调用。 */ - // disconnectedCallback() { } - - /** 每当元素被移动到新文档中时调用。 */ - // adoptedCallback() {} - - #host = this.attachShadow({ mode: 'closed' }); - - private $title = this.#host.appendChild(Element.add('div', { class: 'v-title' })); - - private $info = this.#host.appendChild(Element.add('div', { class: 'v-info' })); - - private $preview = this.#host.appendChild(Element.add('div', { class: 'v-preview' })); - - private $data = this.#host.appendChild(Element.add('div', { class: 'v-data' })); - - constructor() { - super(); - this.$title.insertAdjacentHTML('beforebegin', ``); - this.popover = 'auto'; - - document.addEventListener('pointerover', this.onPointerover); - } - - private onPointerover = ({ target }: PointerEvent) => { - const node = (target)?.closest('[data-v-aid]'); - if (node) { - const { vAid } = node.dataset; - if (vAid) { - const id = crypto.randomUUID(); - node.style.setProperty('anchor-name', `--${id}`); - this.style.setProperty('position-anchor', `--${id}`); - this.onMouseoverAid(vAid); - } - } else { - const node = (target)?.closest('[data-v-ssid]'); - if (node) { - const { vSsid } = node.dataset; - if (vSsid) { - const id = crypto.randomUUID(); - node.style.setProperty('anchor-name', `--${id}`); - this.style.setProperty('position-anchor', `--${id}`); - this.onMouseoverSsid(vSsid); - } - } else { - this.hidePopover(); - } - } - } - - private onMouseoverAid = ( - aid: string | number, - ) => { - cards({ av: aid }).then(d => { - const card = d[`av${aid}`]; - if (card) { - this.$title.innerText = card.title; - this.$info.innerHTML = `${card.owner.name}${Format.eTime(card.pubdate)}`; - this.$preview.innerHTML = https(`

    ${card.desc}

    `); - this.$data.innerHTML = `${Format.carry(card.stat.view)}${Format.carry(card.stat.danmaku)}${Format.carry(card.stat.favorite)}${Format.carry(card.stat.coin)}`; - this.showPopover(); - } - }) - } - - private onMouseoverSsid = ( - ssid: string | number, - ) => { - pgcAppSeason({ season_id: ssid }).then(d => { - if (d) { - this.$title.innerHTML = d.title; - this.$info.innerHTML = `${d.type_desc}`; - this.$preview.innerHTML = https(`

    ${d.evaluate}

    `); - this.$data.innerHTML = `${Format.carry(d.stat.views)}${Format.carry(d.stat.danmakus)}${Format.carry(d.stat.favorites)}${Format.carry(d.stat.coins)}`; - this.showPopover(); - } - }) - } - - showPopover() { - document.body.contains(this) || document.body.appendChild(this); - super.showPopover(); - } -} \ No newline at end of file diff --git a/src/main/bilibili/html/index.ts b/src/main/bilibili/html/index.ts deleted file mode 100644 index ce20ce1..0000000 --- a/src/main/bilibili/html/index.ts +++ /dev/null @@ -1,60 +0,0 @@ -import svg_sent from "../../../player/assets/svg/sent.svg"; -import { customElement } from "../../../utils/Decorator/customElement"; -import { Element } from "../../../utils/element"; -import { Head } from "./head"; - -/** 路由页面 */ -@customElement('html') -export class Html extends HTMLHtmlElement { - - /** - * 需要监听变动的属性。 - * 与实例方法`attributeChangedCallback`配合使用。 - * 此字符串序列定义了`attributeChangedCallback`回调时的第一个参数的可能值。 - */ - // static observedAttributes = []; - - /** - * 在属性更改、添加、移除或替换时调用。 - * 需要与静态属性`observedAttributes`配合使用。 - * 此回调的第一个参数在`observedAttributes`数组中定义。 - */ - // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} - - /** 每当元素添加到文档中时调用。 */ - // connectedCallback(){} - - /** 每当元素从文档中移除时调用。 */ - // disconnectedCallback() {} - - /** 每当元素被移动到新文档中时调用。 */ - // adoptedCallback() {} - - private $head = this.appendChild(new Head()); - - private $body = Element.add('body', undefined, this); - - private $goTop = Element.add('div', { class: 'go-top-m', title: '返回顶部' }, undefined, svg_sent); - - constructor() { - super(); - - this.lang = 'zh-hans'; - - this.$goTop.addEventListener('click', () => { - self.scrollTo({ - top: 0, - behavior: 'smooth', - }); - }); - self.addEventListener('scrollend', () => { - const { scrollTop } = this; - - if (scrollTop > 170) { - this.$body.contains(this.$goTop) || this.$body.appendChild(this.$goTop); - } else { - this.$goTop.remove(); - } - }) - } -} \ No newline at end of file diff --git a/src/main/bilibili/html/style/go-top.css b/src/main/bilibili/html/style/go-top.css deleted file mode 100644 index 91e3d1b..0000000 --- a/src/main/bilibili/html/style/go-top.css +++ /dev/null @@ -1,31 +0,0 @@ -.go-top-m { - position: fixed; - inline-size: 48px; - aspect-ratio: 1; - inset-block-end: 50px; - inset-inline-end: 60px; - background-color: #f6f9fa; - border: 1px solid #e5e9ef; - overflow: hidden; - border-radius: 4px; - cursor: pointer; - display: flex; - justify-content: center; - align-items: center; - - >svg { - inline-size: 1em; - aspect-ratio: 1; - fill: #99a2aa; - rotate: -90deg; - } - - &:hover { - background-color: #00a1d6; - border-color: #00a1d6; - - >svg { - fill: #fff; - } - } -} \ No newline at end of file diff --git a/src/main/bilibili/html/style/index.css b/src/main/bilibili/html/style/index.css deleted file mode 100644 index 7fb68d5..0000000 --- a/src/main/bilibili/html/style/index.css +++ /dev/null @@ -1,43 +0,0 @@ -@import url(./go-top.css); - -article, -aside, -blockquote, -body, -button, -code, -dd, -details, -div, -dl, -dt, -fieldset, -figcaption, -figure, -footer, -form, -h1, -h2, -h3, -h4, -h5, -h6, -header, -hgroup, -hr, -input, -legend, -li, -menu, -nav, -ol, -p, -pre, -section, -td, -textarea, -th, -ul { - margin: 0; - padding: 0; -} \ No newline at end of file diff --git a/src/main/bilibili/index.ts b/src/main/bilibili/index.ts deleted file mode 100644 index 678d470..0000000 --- a/src/main/bilibili/index.ts +++ /dev/null @@ -1,567 +0,0 @@ -import { pgcAppSeason } from "../../io/com/bilibili/api/pgc/view/v2/app/season"; -import { pgcSection } from "../../io/com/bilibili/api/pgc/view/web/season/user/section"; -import { pugvSeason } from "../../io/com/bilibili/api/pugv/view/web/season"; -import { cards } from "../../io/com/bilibili/api/x/article/cards"; -import { video } from "../../player/area/wrap/video"; -import { ev, PLAYER_EVENT } from "../../player/event-target"; -import { PLAYER_MODE, PLAYER_STATE } from "../../player/state"; -import { AV } from "../../utils/av"; -import { ProxyHook } from "../../utils/hook/Proxy"; -import { Comment } from "./comment"; -import { InitComment } from "./comment/initComment"; -import { Desc } from "./desc"; -import { Footer } from "./footer"; -import { Header } from "./header"; -import { BiliHeader } from "./header/BiliHeader"; -import { Home } from "./home"; -import { Html } from "./html"; -import { Info } from "./info"; -import { BilibiliPlayer } from "./player"; -import { NanoInitData } from "./player/nano"; -import { ChannelKind } from "./player/nano/ChannelKind"; -import { EventType } from "./player/nano/EventType"; -import { FetcherKind } from "./player/nano/FetcherKind"; -import { GroupKind } from "./player/nano/GroupKind"; -import { HandoffKind } from "./player/nano/HandoffKind"; -import { InternalKind } from "./player/nano/InternalKind"; -import { ScreenKind } from "./player/nano/ScreenKind"; - -/** Bilibili SPA 路由守护 */ -export class Router { - - aid = 0; - - cid = 0; - - ssid = 0; - - epid = 0; - - #header?: Header; - - /** 顶栏 */ - private get $header() { - return this.#header || (this.#header = new Header()); - } - - #info?: Info; - - /** 视频信息 */ - private get $info() { - return this.#info || (this.#info = new Info()); - } - - #player?: BilibiliPlayer; - - - /** 播放器组件 */ - private get $player() { - return this.#player || (this.#player = new BilibiliPlayer()); - } - - #desc?: Desc; - - /** 视频信息 */ - private get $desc() { - return this.#desc || (this.#desc = new Desc()); - } - - #comment?: Comment; - - /** 评论区 */ - private get $comment() { - return this.#comment || (this.#comment = new Comment(), this.#comment.classList.add('bili-wrapper'), this.#comment); - } - - #footer?: Footer; - - /** 底栏 */ - private get $footer() { - return this.#footer || (this.#footer = new Footer()); - } - - #home?: Home; - - /** 视频信息 */ - private get $home() { - return this.#home || (this.#home = new Home()); - } - - /** nano播放器兼容 */ - #p_router?: ROUTER; - - /** nano播放器兼容 */ - ChannelKind = ChannelKind; - - /** nano播放器兼容 */ - EventType = EventType; - - /** nano播放器兼容 */ - FetcherKind = FetcherKind; - - /** nano播放器兼容 */ - GroupKind = GroupKind; - - /** nano播放器兼容 */ - HandoffKind = HandoffKind; - - /** nano播放器兼容 */ - InternalKind = InternalKind; - - /** nano播放器兼容 */ - ScreenKind = ScreenKind; - - /** nano播放器兼容 */ - metadata = {}; - - /** nano播放器兼容 */ - #kind = GroupKind.Ugc; - - /** nano播放器兼容 */ - danmaku = { - close() { }, - isOpen() { return true } - }; - - /** nano播放器兼容 */ - #mediaElement = document.createElement('div'); - - /** nano播放器兼容 */ - #element = new Proxy(>{}, { - get: (target) => { - return this.#mediaElement; - }, - }); - - constructor() { - switch (location.hostname) { - case 'www.bilibili.com': { - switch (true) { - case /^\/video\/av\d+\/?$/i.test(location.pathname): - case /^\/video\/bv1[a-z0-9]{9}\/?$/i.test(location.pathname): { - document.documentElement.replaceWith(new Html()); - this.navigate(ROUTER.AV, location); - break; - } - case /^\/bangumi\/play\/ss\d+\/?$/i.test(location.pathname): - case /^\/bangumi\/play\/ep\d+\/?$/i.test(location.pathname): { - document.documentElement.replaceWith(new Html()); - this.navigate(ROUTER.BANGUMI, location); - break; - } - case location.pathname === '/': - case location.pathname === '/index.html': { - document.documentElement.replaceWith(new Html()); - this.navigate(ROUTER.HOME, location); - break; - } - case location.pathname === '/watchlater/': - case location.pathname === '/medialist/play/watchlater': - case location.pathname === '/list/watchlater': { - if (location.hash === '#/list') break; - document.documentElement.replaceWith(new Html()); - this.navigate(ROUTER.TOVIEW, location); - break; - } - case /^\/list\/ml\d+\/?$/i.test(location.pathname): { - document.documentElement.replaceWith(new Html()); - this.navigate(ROUTER.MEDIALIST, location); - break; - } - } - break; - } - } - - this.header(); - this.nano(); - } - - async navigate(router: ROUTER, url: URL | Location) { - this.identify(); - url instanceof Location && (url = new URL(url.href)); - switch (router) { - case ROUTER.AV: { - if (this.#p_router !== router) { - document.documentElement.replaceWith(new Html()); - document.body.replaceChildren(this.$header, this.$info, this.$player, this.$desc, this.$comment, this.$footer); - this.#p_router = router; - } - this.$header.navigate(router, url); - this.$info.navigate(router, url); - this.$player.navigate(router, url); - this.$desc.navigate(router, url); - this.$comment.navigate(router, url); - break; - } - case ROUTER.BANGUMI: { - if (this.#p_router !== router) { - document.documentElement.replaceWith(new Html()); - document.body.replaceChildren(this.$header, this.$info, this.$player, this.$desc, this.$comment, this.$footer); - this.#p_router = router; - } - this.$header.navigate(router, url); - this.$info.navigate(router, url); - this.$player.navigate(router, url); - this.$desc.navigate(router, url); - this.$comment.navigate(router, url); - break; - } - case ROUTER.HOME: { - if (this.#p_router !== router) { - document.documentElement.replaceWith(new Html()); - document.body.replaceChildren(this.$header, this.$home, this.$footer); - this.#p_router = router; - } - this.$header.navigate(router, url); - break; - } - case ROUTER.TOVIEW: { - if (this.#p_router !== router) { - document.documentElement.replaceWith(new Html()); - document.body.replaceChildren(this.$header, this.$info, this.$player, this.$desc, this.$comment, this.$footer); - this.#p_router = router; - } - this.$header.navigate(router, url); - this.$info.navigate(router, url); - this.$player.navigate(router, url); - this.$desc.navigate(router, url); - this.$comment.navigate(router, url); - break; - } - case ROUTER.MEDIALIST: { - if (this.#p_router !== router) { - document.documentElement.replaceWith(new Html()); - document.body.replaceChildren(this.$header, this.$info, this.$player, this.$desc, this.$comment, this.$footer); - this.#p_router = router; - } - this.$header.navigate(router, url); - this.$info.navigate(router, url); - this.$player.navigate(router, url); - this.$desc.navigate(router, url); - this.$comment.navigate(router, url); - break; - } - } - } - - /** 拦截新版播放器 */ - private nano() { - ProxyHook.property(self, 'nano', this); - ProxyHook.property(self, 'EmbedPlayer', this.EmbedPlayer); - ProxyHook.property(self, 'initComment', this.initComment); - ProxyHook.property(self, 'bbComment', class { - constructor( - /** 绑定到的节点的选择符 */ - parent: string, - /** 视频aid或话题topic id */ - oid: Record | number, - /** 评论所在页面类型 1:视频,2:话题 */ - pageType: number, - /** 用户信息 */ - userStatus: object, - /** 需要跳转到的评论的id */ - jumpId: number, - ex: unknown, - ) { - const target = document.querySelector(parent); - if (target) { - const $comment = new Comment(); - target.replaceChildren($comment); - if (typeof oid === 'object') { - $comment.init(oid.oid, undefined, undefined, oid.pageType, oid.jumpId); - } else { - $comment.init(oid, undefined, undefined, pageType, jumpId); - } - } - } - }) - } - - /** nano播放器兼容 */ - aspectRatio({ width }: { width: number }) { - return { width, height: width / 16 * 9 + 68 - (self.innerWidth > 1680 ? 56 : 46) }; - } - - /** nano播放器兼容 */ - getStates() { - return { mainScreen: ScreenKind.Wide }; - } - - /** nano播放器兼容 */ - setState() { } - - /** nano播放器兼容 */ - setHandoff() { } - - /** nano播放器兼容 */ - createPlayer(initData: NanoInitData, theme?: Record) { - console.debug('新版播放器试图启动!', initData, theme); - initData.kind && (this.#kind = initData.kind); - initData.aid && (this.aid = +initData.aid); - initData.cid && (this.cid = +initData.cid); - this.aid || (initData.bvid && (this.aid = +AV.fromBV(initData.bvid))); - if (!this.aid) { - const { aid } = this.searchParams(); - aid && (this.aid = aid.startsWith('BV') ? +AV.fromBV(aid) : +aid); // 诡异的写作 aid 读作 bvid ! - } - initData.seasonId && (this.ssid = +initData.seasonId); - initData.episodeId && (this.epid = +initData.episodeId); - initData.element?.replaceChildren(this.$player); - this.$player.classList.add('nano'); // 默认铺满播放器容器 - (PLAYER_STATE.mode & PLAYER_MODE.WIDE) || ev.trigger(PLAYER_EVENT.PLAYER_MODE, PLAYER_STATE.mode ^= PLAYER_MODE.WIDE); // 默认以宽屏模式启动 - - return this; - } - - /** nano播放器兼容 */ - async reload(initData: NanoInitData) { - this.createPlayer(initData); - this.connect(); - } - - /** nano播放器兼容 */ - connect() { - if (this.#kind === GroupKind.Pugv) { - if (this.ssid || this.epid) { - pugvSeason(this.ssid ? { season_id: this.ssid } : { ep_id: this.epid }) - .then(d => { - this.ssid || (this.ssid = d.season_id); - if (!this.epid) { - this.epid = d.episodes[0]?.id; - } - if (this.epid) { - const ep = d.episodes.find(d => d.id === this.epid); - if (ep) { - this.aid = ep.aid; - this.cid = ep.cid; - } - } - if (this.epid && this.cid) { - this.$player.connect(this.aid, this.cid, this.ssid, this.epid, GroupKind.Pugv); - } - }); - } else { - console.error('解析课程出错~'); - } - } else if (this.ssid || this.epid) { - pgcAppSeason(this.ssid ? { season_id: this.ssid } : { ep_id: this.epid }) - .then(async season => { - this.ssid || (this.ssid = season.season_id); - season.modules.forEach(d => { - switch (d.style) { - case "positive": - case "section": { - this.epid || (this.epid = d.data.episodes[0]?.ep_id); - if (this.epid) { - const ep = d.data.episodes.find(d => d.ep_id === this.epid); - if (ep) { - this.aid = ep.aid; - this.cid = ep.cid; - } - } - break; - } - } - }); - if (!this.cid && this.ssid) { - const d = await pgcSection(this.ssid); - const eps = d.main_section.episodes.concat(...d.section.map(d => d.episodes)); - const ep = this.epid ? eps.find(d => d.id === this.epid) : eps[0]; - if (ep) { - this.epid = ep.id; - this.aid = ep.aid; - this.cid = ep.cid; - } - } - if (this.epid && this.cid) { - this.$player.connect(this.aid, this.cid, this.ssid, this.epid); - this.$player.getRelated(); - } - }); - } else if (this.aid) { - cards({ av: this.aid }).then(d => { - const card = d[`av${this.aid}`]; - this.cid || (this.cid = card.cid); - if (card.redirect_url) { - const path = card.redirect_url.split('/'); - switch (true) { - case /^ep\d+$/i.test(path[6]): { - this.epid = +path[6].slice(2); - break; - } - case /^ss\d+$/i.test(path[6]): { - this.ssid = +path[6].slice(2); - break; - } - } - } - if (this.cid) { - this.$player.connect(this.aid, this.cid, this.ssid, this.epid); - this.$player.getRelated(); - } - }) - } else { - console.error('启动播放器出错~'); - } - } - - /** 原生播放器兼容 */ - EmbedPlayer = ( - type: string, - player: any, - playerParamsArg: string, - playerType: string, - upgrade: boolean, - callbackFn: Function, - isIframe: boolean, - ) => { - const bofqi = document.querySelector('#bilibili-player') || document.querySelector('#bofqi'); - if (bofqi) { - bofqi.replaceWith(this.$player); - const params = this.searchParams(playerParamsArg || location.search); - params.aid && (this.aid = +params.aid); - params.cid && (this.cid = +params.cid); - this.aid || (params.bvid && (this.aid = +AV.fromBV(params.bvid))); - this.$player.classList.add('nano'); // 默认铺满播放器容器 - (PLAYER_STATE.mode & PLAYER_MODE.WIDE) || ev.trigger(PLAYER_EVENT.PLAYER_MODE, PLAYER_STATE.mode ^= PLAYER_MODE.WIDE); // 默认以宽屏模式启动 - this.connect(); - } else { - console.error('未找到播放器~'); - } - } - - /** nano播放器兼容 */ - searchParams(url = location.search) { - return Object.fromEntries([...new URLSearchParams(url).entries()]); - } - - /** nano播放器兼容 */ - on(event: EventType, callback: () => void) { - switch (event) { - case EventType.Player_Initialized: - case EventType.Player_Connected: { - Promise.resolve().finally(callback) - break; - } - case EventType.Player_Disconnect: { - ev.bind(PLAYER_EVENT.IDENTIFY, callback); - break; - } - } - } - - /** nano播放器兼容 */ - off(event: EventType, callback: () => void) { - switch (event) { - case EventType.Player_Disconnect: { - ev.unbind(PLAYER_EVENT.IDENTIFY, callback); - break; - } - } - } - - /** nano播放器兼容 */ - fetch(url: string) { - return Promise.reject(url); - } - - /** nano播放器兼容 */ - mediaElement() { - return this.#mediaElement; - } - - /** nano播放器兼容 */ - isMuted() { - return video.muted; - } - - /** nano播放器兼容 */ - getVolume() { - return video.volume; - } - - /** nano播放器兼容 */ - getDuration() { - return video.duration; - } - - /** nano播放器兼容 */ - play() { - return Promise.resolve(); - } - - /** nano播放器兼容 */ - pause() { } - - /** nano播放器兼容 */ - getCurrentTime() { - return video.currentTime; - } - - /** nano播放器兼容 */ - getManifest() { - return Object.create(null); - } - - /** nano播放器兼容 */ - isInitialized() { - return true; - } - - /** nano播放器兼容 */ - getElements() { - return this.#element; - } - - /** nano播放器兼容 */ - isPaused() { - return video.paused; - } - - /** 拦截新版顶栏 */ - private header = () => { - const { $header } = this - ProxyHook.property(self, 'BiliHeader', class { - constructor({ config }: BiliHeader) { - if (config.headerType === 'mini') { - $header.classList.add('mini'); - } - } - init(el: HTMLElement) { - $header.resource_id = 142; - return el.replaceWith($header); - } - }); - } - - /** 拦截新版评论区 */ - initComment = (container: string, { oid, pageType, jumpReplyId }: InitComment) => { - const target = document.querySelector(container); - if (target) { - const $comment = new Comment(); - target.replaceWith($comment); - $comment.init(oid, undefined, undefined, pageType, jumpReplyId); - } - return this; - } - - /** 新版评论区兼容 */ - registerEvent() { } - - identify = () => { - this.aid = 0; - this.cid = 0; - this.ssid = 0; - this.epid = 0; - } -} - -/** 路由列表 */ -export enum ROUTER { - AV, - BANGUMI, - HOME, - TOVIEW, - MEDIALIST, -} \ No newline at end of file diff --git a/src/main/bilibili/info/index.ts b/src/main/bilibili/info/index.ts deleted file mode 100644 index ab1d855..0000000 --- a/src/main/bilibili/info/index.ts +++ /dev/null @@ -1,483 +0,0 @@ -import { ROUTER } from ".."; -import { pgcAppSeason } from "../../../io/com/bilibili/api/pgc/view/v2/app/season"; -import { IEpisode, pgcSection } from "../../../io/com/bilibili/api/pgc/view/web/season/user/section"; -import { status } from "../../../io/com/bilibili/api/pgc/view/web/season/user/status"; -import { followAdd } from "../../../io/com/bilibili/api/pgc/web/follow/add"; -import { followDel } from "../../../io/com/bilibili/api/pgc/web/follow/del"; -import { cards, ICardsOut } from "../../../io/com/bilibili/api/x/article/cards"; -import { folder } from "../../../io/com/bilibili/api/x/v2/fav/folder"; -import { favAdd } from "../../../io/com/bilibili/api/x/v2/fav/video/add"; -import { favDel } from "../../../io/com/bilibili/api/x/v2/fav/video/del"; -import { favoured } from "../../../io/com/bilibili/api/x/v2/fav/video/favoured"; -import { toviewWeb } from "../../../io/com/bilibili/api/x/v2/history/toview/web"; -import { favResourceList } from "../../../io/com/bilibili/api/x/v3/fav/resource/list"; -import { coins } from "../../../io/com/bilibili/api/x/web-interface/archive/coins"; -import { like as hasLike } from "../../../io/com/bilibili/api/x/web-interface/archive/has/like"; -import { like } from "../../../io/com/bilibili/api/x/web-interface/archive/like"; -import { coinAdd } from "../../../io/com/bilibili/api/x/web-interface/coin/add"; -import { coinTodayExp } from "../../../io/com/bilibili/api/x/web-interface/coin/today/exp"; -import { detail } from "../../../io/com/bilibili/api/x/web-interface/view/detail"; -import svg_coin from "../../../player/assets/svg/coin.svg"; -import svg_heart from "../../../player/assets/svg/heart.svg"; -import svg_icon_collection from "../../../player/assets/svg/icon-collection.svg"; -import svg_icon_danmaku from "../../../player/assets/svg/icon-danmaku.svg"; -import svg_icon_paihang from "../../../player/assets/svg/icon-paihang.svg"; -import svg_icon_played from "../../../player/assets/svg/icon-played.svg"; -import svg_like_number from "../../../player/assets/svg/like-number.svg"; -import svg_message from "../../../player/assets/svg/message.svg"; -import { customElement } from "../../../utils/Decorator/customElement"; -import { AV } from "../../../utils/av"; -import { cookie } from "../../../utils/cookie"; -import { Element } from "../../../utils/element"; -import { Format } from "../../../utils/fomat"; -import { https } from "../../../utils/https"; -import SORT from "./sort.json" - -/** 视频信息 */ -@customElement('div') -export class Info extends HTMLDivElement { - - /** - * 需要监听变动的属性。 - * 与实例方法`attributeChangedCallback`配合使用。 - * 此字符串序列定义了`attributeChangedCallback`回调时的第一个参数的可能值。 - */ - // static observedAttributes = []; - - /** - * 在属性更改、添加、移除或替换时调用。 - * 需要与静态属性`observedAttributes`配合使用。 - * 此回调的第一个参数在`observedAttributes`数组中定义。 - */ - // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} - - /** 初始化标记 */ - // #inited = false; - - /** 每当元素添加到文档中时调用。 */ - // connectedCallback() {} - - /** 每当元素从文档中移除时调用。 */ - // disconnectedCallback() {} - - /** 每当元素被移动到新文档中时调用。 */ - // adoptedCallback() {} - - private $wrap = Element.add('div', { class: 'bili-wrapper' }, this); - - private $video = Element.add('div', { class: 'video-info' }, this.$wrap); - - private $title = Element.add('h1', undefined, this.$video); - - private $tm = Element.add('div', { class: 'tm-info' }, this.$video); - - private $number = Element.add('div', { class: 'number' }, this.$video); - - private $up = Element.add('div', { class: 'up-info' }, this.$wrap); - - private $face = Element.add('a', { class: 'face', target: '_blank' }, this.$up); - - private $info = Element.add('div', { class: 'info' }, this.$up); - - private $upUser = Element.add('div', { class: 'user' }, this.$info); - - private $upSign = Element.add('div', { class: 'sign' }, this.$info); - - private $upNumber = Element.add('div', { class: 'number' }, this.$info); - - private $upBtn = Element.add('button', { class: 'btn' }, this.$info, '+ 关注'); - - private $coinId = crypto.randomUUID(); - - private $coinPop = Element.add('div', { class: 'coin-popover', popover: 'auto', id: this.$coinId }, undefined, `
    给UP主投上2枚硬币

    经验值+20(今日0/50)

    `); - - private $favId = crypto.randomUUID(); - - private $favPop = Element.add('div', { class: 'fav-popover', popover: 'auto', id: this.$favId }, undefined, `
    添加到收藏夹
    `); - - private $upStaff = Element.add('div', { class: 'up-staff' }, this.$wrap); - - set like(v: boolean) { - this.$number.querySelector('.like')?.classList.toggle('on', v); - } - - set coin(v: boolean) { - this.$number.querySelector('.coin')?.classList.toggle('on', v); - } - - set fav(v: boolean) { - this.$number.querySelector('.fav')?.classList.toggle('on', v); - } - - set heart(v: boolean) { - this.$number.querySelector('.heart')?.classList.toggle('on', v); - } - - #mc = 2; - - set $mc(v: number) { - this.#mc = v; - this.$coinPop.firstElementChild!.innerHTML = `给UP主投上${v}枚硬币`; - this.$coinPop.lastElementChild!.lastElementChild!.innerHTML = `经验值+${v}0(今日${this.#exp}/50)`; - } - - #exp = 0; - - set $exp(v: number) { - this.#exp = v; - this.$coinPop.lastElementChild!.lastElementChild!.innerHTML = `经验值+${this.#mc}0(今日${v}/50)`; - } - - #aid = 0; - - #cid = 0; - - #ssid = 0; - - #epid = 0; - - #fid_add = new Set(); - - #fid_del = new Set(); - - constructor() { - super(); - this.insertAdjacentHTML('beforeend', ``); - - this.$number.addEventListener('click', e => { - const { target } = e; - const csrf = cookie.get('bili_jct'); - if (csrf) { - if (target instanceof HTMLButtonElement) { - if (target.classList.contains('like')) { - this.#aid && like(csrf, this.#aid, target.classList.contains('on') ? 2 : 1).then(({ code }) => { - code === 0 && target.classList.toggle('on'); - }); - } else if (target.classList.contains('heart')) { - const { ssid } = target.dataset; - if (ssid) { - (target.classList.contains('on') ? followDel(csrf, ssid) : followAdd(csrf, ssid)).then(() => { - target.classList.toggle('on'); - }); - } - } - } - } - }); - this.$coinPop.children[1].addEventListener('change', () => { - const d = new FormData(this.$coinPop.children[1]); - this.$mc = +[...d.values()][0]; - }); - this.$coinPop.addEventListener('toggle', e => { - if (this.$coinPop.matches(":popover-open")) { - const coin = this.$number.querySelector('.coin.on'); - if (coin) { - this.$coinPop.hidePopover(); - } else { - coinTodayExp().then(d => { this.$exp = d }); - } - } - }); - this.$coinPop.lastElementChild!.firstElementChild!.addEventListener('click', () => { - const coin = this.$number.querySelector('.coin'); - const csrf = cookie.get('bili_jct'); - if (csrf && coin && !coin.classList.contains('on')) { - this.#aid && coinAdd(csrf, this.#aid, <1>this.#mc).then(({ code }) => { - code === 0 && coin.classList.toggle('on'); - }).finally(() => { - this.$coinPop.hidePopover(); - }); - } - }); - this.$favPop.addEventListener('toggle', () => { - if (this.$favPop.matches(":popover-open")) { - folder(this.#aid).then(d => { - d.sort((a, b) => b.favoured - a.favoured); - this.#fid_add.clear(); - this.#fid_add.clear(); - const fid_def: number[] = []; - const node = this.$favPop.children[1]; - node.innerHTML = d.map(d => { - d.favoured && fid_def.push(d.fid); - return ``; - }).join(''); - node.replaceChildren(...d.map(d => { - const label = Element.add('label', { 'data-cur': d.cur_count, 'data-max': d.max_count }, undefined, `${d.name}${d.state === 3 ? '[私密]' : ''}`); - label.addEventListener('change', () => { - if ((label.firstElementChild).checked) { - fid_def.includes(d.fid) || this.#fid_add.add(d.fid); - this.#fid_del.delete(d.fid); - } else { - fid_def.includes(d.fid) && this.#fid_del.add(d.fid); - this.#fid_add.delete(d.fid); - } - - (this.$favPop.lastElementChild!.firstElementChild).disabled = !(this.#fid_add.size || this.#fid_del.size) - }); - return label; - })); - }); - } - }); - - this.$favPop.lastElementChild!.firstElementChild!.addEventListener('click', () => { - const csrf = cookie.get('bili_jct'); - if (csrf && this.#aid) { - const arr: Promise[] = [] - this.#fid_add.size && arr.push(favAdd(csrf, this.#aid, ...this.#fid_add)); - this.#fid_del.size && arr.push(favDel(csrf, this.#aid, ...this.#fid_del)); - arr.length && Promise.allSettled(arr).finally(() => { - this.$favPop.hidePopover(); - setTimeout(() => { - favoured(this.#aid).then(({ favoured }) => { this.fav = Boolean(favoured) }); - }, 1e3); - }) - } - }); - } - - async navigate(router: ROUTER, url: URL | Location) { - this.#aid = 0; - this.#cid = 0; - this.#ssid = 0; - this.#epid = 0; - url instanceof Location && (url = new URL(url.href)); - switch (router) { - case ROUTER.AV: { - const path = url.pathname.split('/'); - switch (true) { - case /^av\d+$/i.test(path[2]): { - this.#aid = +path[2].slice(2); - break; - } - case /^bv1[a-z0-9]{9}$/i.test(path[2]): { - this.#aid = +AV.fromBV(path[2]); - break; - } - } - if (this.#aid) { - Promise.allSettled([cards({ av: this.#aid }), detail(this.#aid)]) - .then(([cards, detail]) => { - const d = cards.status === "fulfilled" && cards.value; - const de = detail.status === "fulfilled" && detail.value; - if (d) { - const card = d[`av${this.#aid}`]; - if (de && de.View) { - this.avDetail(de); - } else { - this.avCards(card); - } - } - }); - } else { - console.error('解析av号出错~'); - } - break; - } - case ROUTER.BANGUMI: { - const path = url.pathname.split('/'); - switch (true) { - case /^ss\d+$/i.test(path[3]): { - this.#ssid = +path[3].slice(2); - break; - } - case /^ep\d+$/i.test(path[3]): { - this.#epid = +path[3].slice(2); - break; - } - } - if (this.#ssid || this.#epid) { - pgcAppSeason(this.#ssid ? { season_id: this.#ssid } : { ep_id: this.#epid }) - .then(async season => { - this.#ssid || (this.#ssid = season.season_id); - season.modules.forEach(d => { - switch (d.style) { - case "positive": - case "section": { - this.#epid || (this.#epid = d.data.episodes[0]?.ep_id); - if (this.#epid) { - const ep = d.data.episodes.find(d => d.ep_id === this.#epid); - if (ep) { - this.#cid = ep.cid; - this.bangumi(season, ep); - } - } - break; - } - } - }); - if (!this.#cid && this.#ssid) { - const d = await pgcSection(this.#ssid); - const eps = d.main_section.episodes.concat(...d.section.map(d => d.episodes)); - const ep = this.#epid ? eps.find(d => d.id === this.#epid) : eps[0]; - if (ep) { - this.bangumi(season, ep); - } - } - }); - } else { - console.error('解析Bangumi出错~'); - } - break; - } - case ROUTER.TOVIEW: { - const path = url.hash.split('/'); - switch (true) { - case /^av\d+$/i.test(path[1]): { - this.#aid = +path[1].slice(2); - break; - } - case /^bv1[a-z0-9]{9}$/i.test(path[1]): { - this.#aid = +AV.fromBV(path[1]); - break; - } - } - toviewWeb().then(toview => { - this.#aid || (toview.length && (this.#aid = toview[0].aid)); - if (this.#aid) { - Promise.allSettled([cards({ av: this.#aid }), detail(this.#aid)]) - .then(([cards, detail]) => { - const d = cards.status === "fulfilled" && cards.value; - const de = detail.status === "fulfilled" && detail.value; - if (d) { - const card = d[`av${this.#aid}`]; - if (de && de.View) { - this.avDetail(de); - } else { - this.avCards(card); - } - } - }); - } else { - console.error('解析稍后再看出错~'); - } - }) - break; - } - case ROUTER.MEDIALIST: { - const path = url.pathname.split('/'); - const ml = +path[2].slice(2); - if (ml) { - favResourceList(ml).then(({ medias }) => { - this.#aid = Number(url.searchParams.get('aid')) || medias[0].id; - if (this.#aid) { - Promise.allSettled([cards({ av: this.#aid }), detail(this.#aid)]) - .then(([cards, detail]) => { - const d = cards.status === "fulfilled" && cards.value; - const de = detail.status === "fulfilled" && detail.value; - if (d) { - const card = d[`av${this.#aid}`]; - if (de && de.View) { - this.avDetail(de); - } else { - this.avCards(card); - } - } - }); - } else { - console.error('解析播放列表出错~'); - } - }) - } else { - console.error('解析播放列表出错~'); - } - } - } - } - - private avCards(card: ICardsOut) { - this.identify(); - this.#aid = card.aid; - navigator.mediaSession.metadata = new MediaMetadata({ - title: document.title = this.$title.textContent = this.$title.title = card.title, - artist: card.owner.name, - album: card.tname, - artwork: [ - { - src: https(card.pic, true) - } - ] - }); - const tinfo: any = SORT[card.tid]; - this.$tm.innerHTML = ` - 主页${tinfo ? tinfo[2] ? ` > ${(SORT)[tinfo[2]][0]} > ${tinfo[0]}` : ` > ${tinfo[0]}` : ''} - - -稿件投诉`; - this.$number.innerHTML = `${svg_icon_played}${Format.carry(card.stat.view)} -${svg_icon_danmaku}${Format.carry(card.stat.danmaku)} -${card.stat.his_rank ? `${svg_icon_paihang}最高全站日排行${card.stat.his_rank}名` : ''} - - -`; - this.$face.href = `//space.bilibili.com/${card.owner.mid}`; - this.$face.innerHTML = ``; - this.$upUser.innerHTML = `${card.owner.name} -${svg_message}发消息`; - this.$number.insertAdjacentElement('beforeend', this.$coinPop); - this.$number.insertAdjacentElement('beforeend', this.$favPop); - - this.updateNumber(); - } - - avDetail(data: Awaited>) { - this.avCards(data.View); - this.$upSign.textContent = data.Card.card.sign; - this.$upNumber.innerHTML = `投稿:${Format.carry(data.Card.archive_count)} -粉丝:${Format.carry(data.Card.follower)}`; - data.Card.following && (this.$upBtn.textContent = '已关注'); - data.Card.card.pendant.image && (this.$face.insertAdjacentHTML('beforeend', ``)); - data.Card.card.official_verify.desc && (this.$face.insertAdjacentHTML('beforeend', ``)); - data.Card.card.vip.nickname_color && ((this.$upUser.firstElementChild!).style.color = data.Card.card.vip.nickname_color); - data.View.staff && (this.$upStaff.innerHTML = https(data.View.staff.map(d => `${d.name}`).join(''))); - } - - private async bangumi(data: Awaited>, ep: IEpisode) { - this.identify(); - this.#aid = ep.aid; - navigator.mediaSession.metadata = new MediaMetadata({ - title: document.title = this.$title.textContent = this.$title.title = `${data.title}:${/^\d+$/.test(ep.title) ? `第${ep.title}话` : ep.title} ${ep.long_title}`, - artist: data.actor.info, - album: data.title, - artwork: [ - { - src: https(ep.cover, true) - } - ] - }); - this.$tm.innerHTML = ` - 番剧 - ${data.areas.length ? `${data.areas.map(d => d.name).join(',')}` : ''} - ${data.new_ep.desc} - AV${ep.aid} -`; - this.$number.innerHTML = `${svg_icon_played}${Format.carry(data.stat.views)} -${svg_icon_danmaku}${Format.carry(data.stat.danmakus)} - - -`; - this.$number.insertAdjacentElement('beforeend', this.$coinPop); - - this.updateNumber(data.season_id); - } - - private updateNumber(ssid?: number) { - hasLike(this.#aid).then(d => { this.like = Boolean(d) }); - coins(this.#aid).then(({ multiply }) => { this.coin = Boolean(multiply) }); - if (ssid) { - status(ssid).then(({ follow }) => { this.heart = Boolean(follow) }); - } else { - favoured(this.#aid).then(({ favoured }) => { this.fav = Boolean(favoured) }); - } - } - - private identify = () => { - this.$upStaff.replaceChildren(); - } -} - -//////////////////////////// 全局增强 //////////////////////////// -declare global { - /** 基于哈希消息认证码的一次性口令的密钥 */ - const __BILI_INFO_STYLE__: string; -} \ No newline at end of file diff --git a/src/main/bilibili/info/style/index.css b/src/main/bilibili/info/style/index.css deleted file mode 100644 index 1661a00..0000000 --- a/src/main/bilibili/info/style/index.css +++ /dev/null @@ -1,525 +0,0 @@ -@scope { - :scope { - display: flex; - justify-content: center; - align-items: center; - font-size: 12px; - min-inline-size: 980px; - - >.bili-wrapper { - display: flex; - justify-content: space-between; - inline-size: 980px; - - @media screen and (min-width:1400px) { - - & { - inline-size: 1160px; - } - } - - @media screen and (min-width:2500px) { - - & { - inline-size: 1920px; - } - } - } - - &, - div { - box-sizing: border-box; - } - - a { - outline: none; - text-decoration: none; - } - } -} - -.video-info { - display: flex; - flex-direction: column; - - >h1 { - color: #525659; - font-weight: 400; - font-size: 18px; - margin-block: 8px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } - - >.tm-info { - color: #99a2aa; - margin-block: 8px; - display: flex; - align-items: center; - column-gap: 32px; - - >span { - display: flex; - column-gap: 1em; - } - - - a { - color: #99a2aa; - cursor: pointer; - - &:hover { - color: #00a1d6; - } - } - } - - >.number { - margin-block: 8px; - display: flex; - align-items: center; - flex-wrap: wrap; - column-gap: 30px; - - >span, - >button { - display: flex; - align-items: center; - margin-block-end: 16px; - cursor: pointer; - - svg { - font-size: 18px; - inline-size: 1em; - aspect-ratio: 1; - fill: #b7c0cd; - margin-inline-end: 12px; - transition: .3s; - pointer-events: none; - } - - &.like.on>svg { - fill: #f36392; - } - - &.coin.on>svg { - fill: #00a1d6; - } - - &.fav.on>svg { - fill: #f29144; - } - - &.heart.on>svg { - fill: #f25d8e; - } - - &.like:not(.on):hover>svg { - fill: #f36392; - animation: coin-jump .5s alternate both infinite ease-in-out; - } - - &.coin:not(.on):hover>svg { - fill: #00a1d6; - animation: coin-jump .5s alternate both infinite ease-in-out; - } - - &.fav:not(.on):hover>svg { - fill: #f29144; - animation: coin-jump .5s alternate both infinite ease-in-out; - } - - &.heart:not(.on):hover>svg { - fill: #f25d8e; - animation: coin-jump .5s alternate both infinite ease-in-out; - } - } - - >button { - border: 0; - background-color: inherit; - } - - >:popover-open { - inset-inline-start: 50%; - inset-block-start: 50%; - translate: -50% -50%; - background-color: #fff; - border-radius: 4px; - overflow: hidden; - border: 0; - - &::backdrop { - background-color: rgba(0, 0, 0, .65); - } - - &.coin-popover { - display: flex; - flex-direction: column; - align-items: center; - row-gap: 35px; - - >.coin-title { - font-size: 16px; - margin-block-start: 20px; - color: #222; - - >span { - font-size: 30px; - color: #00a1d6; - } - } - - >.mc { - margin-inline: 35px; - color: #99a2aa; - display: grid; - justify-content: center; - grid-template-columns: repeat(2, 160px); - grid-auto-rows: 230px; - column-gap: 30px; - - >.mc-box { - background-size: contain; - border: 2px dashed #ccd0d6; - border-radius: 5px; - background-position: 50%; - - &.left-con { - background-image: url(//s1.hdslb.com/bfs/static/jinkela/videoplay/asserts/22-gray.png); - } - - &.right-con { - background-image: url(//s1.hdslb.com/bfs/static/jinkela/videoplay/asserts/33-gray.png); - } - - &:hover { - border-color: #02a0d8; - } - - &:has(input:checked) { - border-style: solid; - border-color: #02a0d8; - - &.left-con { - background-image: url(//s1.hdslb.com/bfs/static/jinkela/videoplay/asserts/22.gif); - } - - &.right-con { - background-image: url(//s1.hdslb.com/bfs/static/jinkela/videoplay/asserts/33.gif); - } - } - - >input { - appearance: none; - } - } - } - - >.coin-bottom { - margin-block-end: 25px; - display: flex; - flex-direction: column; - align-items: center; - row-gap: 10px; - - >.bi-btn { - background-color: #00a1d6; - color: #fff; - font-size: 14px; - padding-inline: 18px; - padding-block: 4px; - border-radius: 4px; - transition: all .3s; - user-select: none; - border: 1px solid #00a1d6; - cursor: pointer; - - &:hover { - color: #fff; - background-color: #00b5e5; - border-color: #00b5e5; - } - } - - >.tips { - font-size: 12px; - color: #99a2aa; - } - } - } - - &.fav-popover { - inline-size: 420px; - display: flex; - flex-direction: column; - - >.title { - block-size: 50px; - line-height: 50px; - font-size: 16px; - color: #222; - border-block-end: 1px solid #e5e9ef; - text-align: center; - } - - >.content { - padding-inline: 36px; - block-size: 300px; - overflow-y: auto; - scrollbar-width: thin; - - >label { - block-size: 44px; - display: flex; - align-items: center; - column-gap: 1em; - font-size: 14px; - color: #222; - cursor: pointer; - position: relative; - - >i { - color: #999; - } - - &:hover { - color: #00a1d6; - } - - &::after { - content: attr(data-cur) "/" attr(data-max); - color: #6d757a; - font-size: 12px; - position: absolute; - inset-inline-end: 0; - } - } - } - - >.bottom { - block-size: 76px; - border-block-start: 1px solid #e5e9ef; - display: flex; - justify-content: center; - align-items: center; - - >button { - font-size: 14px; - inline-size: 160px; - block-size: 40px; - background-color: #00a1d6; - color: #fff; - border: none; - border-radius: 4px; - cursor: pointer; - - &:hover { - background-color: #00b5e5; - } - - &:disabled { - background-color: #e5e9ef; - color: #b8c0cc; - pointer-events: none; - } - } - } - } - } - } -} - -.up-info { - inline-size: 315px; - margin-block: 8px; - display: flex; - anchor-name: --up-info; - - >.face { - flex: 0; - inline-size: 68px; - block-size: 68px; - position: relative; - display: flex; - justify-content: center; - align-items: center; - - >.face { - inline-size: inherit; - block-size: inherit; - border-radius: 50%; - } - - >.pendant { - position: absolute; - block-size: 112px; - inline-size: 112px; - } - - >.auth { - position: absolute; - inline-size: 20px; - block-size: 20px; - inset-inline-end: 0; - inset-block-start: 50px; - background-image: url(//s1.hdslb.com/bfs/static/jinkela/videoplay/asserts/user-auth.png); - background-repeat: no-repeat; - z-index: 3; - - &.o-auth { - background-position: -4px -53px; - } - - &.p-auth { - background-position: -38px -53px; - } - } - } - - >.info { - flex: 1; - margin-inline-start: 18px; - display: flex; - flex-direction: column; - - >.user { - display: flex; - justify-content: space-between; - align-items: center; - - >.name { - font-size: 14px; - color: #00a1d6; - word-wrap: break-word; - overflow: hidden; - word-break: break-all; - text-overflow: ellipsis; - white-space: nowrap; - - &.is-vip { - color: #fb7299; - } - } - - >.message { - color: #6d757a; - display: flex; - align-items: center; - - >svg { - margin-inline-end: 3px; - fill: #6d757a; - inline-size: 1em; - aspect-ratio: 1; - } - - - } - } - - >.sign { - min-block-size: 40px; - max-block-size: 48px; - overflow: hidden; - word-break: break-all; - } - - >.number { - color: #99a2aa; - margin-block: 3px; - display: flex; - justify-content: space-between; - align-items: center; - } - - >button { - block-size: 30px; - inline-size: 150px; - font-size: 14px; - border-radius: 4px; - margin-block-start: 10px; - color: #fff; - background-color: #00a1d6; - border: 1px solid #00a1d6; - cursor: pointer; - user-select: none; - transition: all .3s; - - &:hover { - background-color: #00b5e5; - border-color: #00b5e5; - } - } - - &:has(>.user:empty) { - display: none; - } - } - - &:not(:hover)~.up-staff:not(:hover) { - display: none; - opacity: 0; - scale: 0; - } -} - -.up-staff { - position: absolute; - position-anchor: --up-info; - inset-area: inline-end span-block-end; - display: flex; - flex-direction: column; - max-block-size: 300px; - overflow-y: auto; - scrollbar-width: thin; - transform-origin: 0 0; - transition: all 0.7s allow-discrete; - - @starting-style { - opacity: 0; - scale: 0; - } - - >a { - font-size: 14px; - color: #00a1d6; - word-wrap: break-word; - word-break: break-all; - text-overflow: ellipsis; - white-space: nowrap; - display: flex; - align-items: center; - column-gap: 1em; - position: relative; - - >img { - inline-size: 48px; - aspect-ratio: 1; - border-radius: 50%; - } - - &[data-title]::after { - content: attr(data-title); - border-radius: 4px; - color: #fff; - background-color: #fb7299; - border: #fb7299 1px solid; - padding-inline: 2px; - font-weight: normal; - } - } - - &:empty { - display: none; - } -} - -@keyframes coin-jump { - - to { - translate: 0 -100%; - scale: .8 1; - } -} \ No newline at end of file diff --git a/src/main/bilibili/player/danmaku/real-time.ts b/src/main/bilibili/player/danmaku/real-time.ts deleted file mode 100644 index b5718c3..0000000 --- a/src/main/bilibili/player/danmaku/real-time.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { BilibiliPlayer } from ".."; -import { DanmakuEvent } from "../../../../io/protobuf/DanmakuEvent"; -import { IDmSegMobileReply } from "../../../../io/protobuf/DmSegMobileReply"; -import { RoomResp } from "../../../../io/protobuf/RoomResp"; -import { Socket } from "./socket"; -import { EVENTS } from "./state"; - -/** 实时弹幕 */ -export class RealTime { - - private defaultPort = 7826; - - private domain = 'broadcast.chat.bilibili.com'; - - private socketKey!: string; - - private socket?: Socket; - - private realdmPath = 'bilibili.broadcast.message.main.DanmukuEvent'; - - private livePath = 'bilibili.broadcast.message.ogv.CMDBody'; - - constructor(private player: BilibiliPlayer) { } - - async getSevers() { - this.sendBroadcast(); - } - - /** 开启websoket */ - private sendBroadcast() { - const url = `wss://${this.domain}:${this.defaultPort}/sub?platform=web`; - - this.socketKey = `video://${this.player.aid}/${this.player.cid}`; - if (this.player.ssid) { - this.socketKey += `?sid=${this.player.ssid}&epid=${this.player.epid}`; - } - - this.socket = new Socket(url, [this.realdmPath], [this.socketKey]); - - this.socket.bind(EVENTS.B_ROOM, e => { - if (e.body?.value) { - const { id, msg, online } = RoomResp.decode(e.body.value); - if (online) { - this.player.setOnline(online.online); - } - if (msg) { - switch (msg.targetPath) { - case this.realdmPath: { - this.player.addDanmaku((DanmakuEvent.decode(msg.body.value)).elems); - break; - } - } - } - } - }); - } - - - - identify = () => { - this.socket?.dispose(); - } -} \ No newline at end of file diff --git a/src/main/bilibili/player/danmaku/state.ts b/src/main/bilibili/player/danmaku/state.ts deleted file mode 100644 index 0aa3dab..0000000 --- a/src/main/bilibili/player/danmaku/state.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { IBroadcastFrame } from "../../../../io/protobuf/BroadcastFrame"; - -export enum EVENTS { - /** inited */ - B_INITED, - /** socket open */ - B_OPEN, - /** socket close */ - B_CLOSE, - /** socket message */ - B_MSG, - /** room enter */ - B_ROOM, - - /** 鉴权回调(鉴权之后才可以订阅) */ - B_AUTH, - /** 订阅回调 */ - B_SUB, - /** 取消订阅回调 */ - B_UN_SUB, - /** socket HeartBeat */ - B_HEARTBEAT, - /** socket error */ - B_ERROR, -} - -export interface IEvents { - 0: void; - 1: Event; - 2: void; - 3: IBroadcastFrame; - 4: IBroadcastFrame; - - 5: IBroadcastFrame; - 6: IBroadcastFrame; - 7: IBroadcastFrame; - 8: IBroadcastFrame; - 9: Event; -} \ No newline at end of file diff --git a/src/main/bilibili/player/index.ts b/src/main/bilibili/player/index.ts deleted file mode 100644 index 195d1d0..0000000 --- a/src/main/bilibili/player/index.ts +++ /dev/null @@ -1,967 +0,0 @@ -import { ROUTER } from ".."; -import { IDanmaku } from "../../../danmaku"; -import DashPlayer from "../../../dash-player"; -import { pgcPlayurl } from "../../../io/com/bilibili/api/pgc/player/web/playurl"; -import { recommend } from "../../../io/com/bilibili/api/pgc/season/web/related/recommend"; -import { pgcAppSeason } from "../../../io/com/bilibili/api/pgc/view/v2/app/season"; -import { pgcSection } from "../../../io/com/bilibili/api/pgc/view/web/season/user/section"; -import { pugvPlayurl } from "../../../io/com/bilibili/api/pugv/player/web/playurl"; -import { cards } from "../../../io/com/bilibili/api/x/article/cards"; -import { HEARTBEAT_PLAY_TYPE, HEARTBEAT_TYPE, heartbeat } from "../../../io/com/bilibili/api/x/click-interface/web/heartbeat"; -import { total } from "../../../io/com/bilibili/api/x/player/online/total"; -import { pagelist } from "../../../io/com/bilibili/api/x/player/pagelist"; -import { playurl } from "../../../io/com/bilibili/api/x/player/playurl"; -import { IViewPoint, v2 } from "../../../io/com/bilibili/api/x/player/v2"; -import { videoshot } from "../../../io/com/bilibili/api/x/player/videoshot"; -import { segSo } from "../../../io/com/bilibili/api/x/v2/dm/web/seg.so"; -import { view } from "../../../io/com/bilibili/api/x/v2/dm/web/view"; -import { toviewWeb } from "../../../io/com/bilibili/api/x/v2/history/toview/web"; -import { favResourceList } from "../../../io/com/bilibili/api/x/v3/fav/resource/list"; -import { related } from "../../../io/com/bilibili/api/x/web-interface/archive/related"; -import { detail } from "../../../io/com/bilibili/api/x/web-interface/view/detail"; -import { DmSegMobileReply } from "../../../io/protobuf/DmSegMobileReply"; -import { QUALITY_DESCRIBE } from "../../../io/quality"; -import { Player } from "../../../player"; -import { video } from "../../../player/area/wrap/video"; -import { IRecommend } from "../../../player/auxiliary/recommend"; -import { PLAYER_EVENT, ev } from "../../../player/event-target"; -import { options } from "../../../player/option"; -import { POLICY } from "../../../player/policy"; -import { customElement } from "../../../utils/Decorator/customElement"; -import { AV } from "../../../utils/av"; -import { cookie } from "../../../utils/cookie"; -import { https } from "../../../utils/https"; -import { ClosedCaption } from "./closed-caption"; -import { RealTime } from "./danmaku/real-time"; -import { GroupKind } from "./nano/GroupKind"; -import { Part } from "./part"; - -@customElement('figure') -export class BilibiliPlayer extends Player { - - /** - * 需要监听变动的属性。 - * 与实例方法`attributeChangedCallback`配合使用。 - * 此字符串序列定义了`attributeChangedCallback`回调时的第一个参数的可能值。 - */ - // static observedAttributes = []; - - /** - * 在属性更改、添加、移除或替换时调用。 - * 需要与静态属性`observedAttributes`配合使用。 - * 此回调的第一个参数在`observedAttributes`数组中定义。 - */ - // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} - - /** 每当元素添加到文档中时调用。 */ - connectedCallback() { - this.insertAdjacentElement('beforebegin', this.#part); - super.connectedCallback(); - } - - /** 每当元素从文档中移除时调用。 */ - disconnectedCallback() { - this.#part.remove(); - super.disconnectedCallback(); - } - - /** 每当元素被移动到新文档中时调用。 */ - // adoptedCallback() {} - - aid = 0; - - cid = 0; - - ssid = 0; - - epid = 0; - - /** 默认清晰度 */ - qn = 0; - - /** 实际的视频清晰度(实时更新) */ - videoRealQuality = 0; - - /** 正在更新画质,延时:/s */ - updateSourceing = 0; - - /** 允许心跳 */ - heartbeat = true; - - /** 允许seek心跳 */ - heartbeatSeek = false; - - #part = new Part(); - - /** 缩略图信息 */ - pvData?: Awaited>; - - /** 视频看点 */ - viewPoints?: IViewPoint[]; - - playurl?: Awaited> | Awaited> | Awaited>; - - dmView?: Awaited>; - - realDm = new RealTime(this); - - /** 在线数据延时请求句柄 */ - onlineTimer?: number; - - /** - * buffer 策略 - * | 0 | 1 | 2 | 3 | 4 | 5 | - * | :-: | :-: | :-: | :-: | :-: | :-: | - * | 初始 buffer 长度 | 变更 buffer 时间(s) | 中间 buffer 长度 | 变更 buffer 时间(s) | 最终 buffer 长度 | 向前保留长度 | - */ - get dynamicBuffer() { - if (this.videoRealQuality >= 120) { - // 4K 以上用 buffer 长度 - return [20, 5, 25, 20, 30, 10]; - } - return [20, 5, 40, 20, 70, 20]; - } - - #csrf = ''; - - get $csrf() { - return this.#csrf || (this.#csrf = cookie.get('bili_jct')) - } - - constructor() { - super(); - - this.insertAdjacentHTML('beforeend', ``); - - - video.addEventListener('progress', () => { - // buffer policy - // http://info.bilibili.co/pages/viewpage.action?pageId=12877158 - if (this.dashPlayer) { - const { currentTime } = video - const dynamicBufferArray = this.dynamicBuffer; - if (this.dashPlayer.getStableBufferTime() < dynamicBufferArray[4]) { - if (currentTime > dynamicBufferArray[3]) { - this.dashPlayer.setStableBufferTime(dynamicBufferArray[4]); - this.dashPlayer.getCorePlayer().setBufferToKeep(dynamicBufferArray[5]); - } else if (currentTime > dynamicBufferArray[1] && this.dashPlayer.getStableBufferTime() < dynamicBufferArray[2]) { - this.dashPlayer.setStableBufferTime(dynamicBufferArray[2]); - this.dashPlayer.getCorePlayer().setBufferToKeep(dynamicBufferArray[5]); - } - } - } - }); - video.addEventListener('play', () => { - this.heartbeatSeek = true; - this.heartbeat && this.$csrf && heartbeat( - this.$csrf, - this.aid, - this.cid, - video.currentTime, - HEARTBEAT_PLAY_TYPE.CONTINUE, - this.epid ? HEARTBEAT_TYPE.BANGUMI : HEARTBEAT_TYPE.AV - ) - }); - video.addEventListener('pause', () => { - this.heartbeat && this.$csrf && heartbeat( - this.$csrf, - this.aid, - this.cid, - video.currentTime, - HEARTBEAT_PLAY_TYPE.PAUSE, - this.epid ? HEARTBEAT_TYPE.BANGUMI : HEARTBEAT_TYPE.AV - ) - }); - video.addEventListener('seeking', () => { - this.heartbeat && this.heartbeatSeek && this.$csrf && heartbeat( - this.$csrf, - this.aid, - this.cid, - video.currentTime, - HEARTBEAT_PLAY_TYPE.START, - this.epid ? HEARTBEAT_TYPE.BANGUMI : HEARTBEAT_TYPE.AV - ) - }); - video.addEventListener('seeked', () => { - this.heartbeat && this.heartbeatSeek && this.$csrf && heartbeat( - this.$csrf, - this.aid, - this.cid, - video.currentTime, - HEARTBEAT_PLAY_TYPE.PLAYING, - this.epid ? HEARTBEAT_TYPE.BANGUMI : HEARTBEAT_TYPE.AV - ) - }); - video.addEventListener('ended', () => { - this.heartbeat && this.$csrf && heartbeat( - this.$csrf, - this.aid, - this.cid, - -1, - HEARTBEAT_PLAY_TYPE.ENDED, - this.epid ? HEARTBEAT_TYPE.BANGUMI : HEARTBEAT_TYPE.AV - ) - }); - - ev.bind(PLAYER_EVENT.MEDIA_ERROR, () => { - if (!this.updateSourceing) { - const updateSource = () => { - this.updateSource(undefined, () => { - this.updateSourceing = 0; - ev.trigger(PLAYER_EVENT.TOAST, '重新连接至服务器成功'); - }, () => { - this.updateSourceing < 30e3 && (this.updateSourceing += 3e3); - setTimeout(updateSource, this.updateSourceing); - ev.trigger(PLAYER_EVENT.TOAST, '重新连接服务器失败'); - }) - } - updateSource(); - this.updateSourceing += 3e3; - } - }); - ev.bind(PLAYER_EVENT.QUALITY_CHANGE, ({ detail }) => { - cookie.set('CURRENT_QUALITY', this.qn = detail); - if (detail === 0) { - // 自动画质无须切换 - this.setQualityFor(detail); - ev.trigger(PLAYER_EVENT.TOAST, `已经切换至${QUALITY_DESCRIBE[detail]}画质`); - } else { - if (this.playurl?.dash?.video.find(d => d.id === detail)) { - // 无需联网请求的DASH画质 - this.setQualityFor(detail); - ev.trigger(PLAYER_EVENT.TOAST, `已经切换至${QUALITY_DESCRIBE[detail]}画质`); - } else { - ev.trigger(PLAYER_EVENT.TOAST, `正在为您切换到${QUALITY_DESCRIBE[detail]}画质,请稍候...`); - this.updateSource(detail, () => { - ev.trigger(PLAYER_EVENT.TOAST, `已经切换至${QUALITY_DESCRIBE[detail]}画质`); - this.setQualityFor(detail); - }, () => { - ev.trigger(PLAYER_EVENT.TOAST, `切换画质失败,已回滚~`); - }) - } - } - }); - ev.bind(PLAYER_EVENT.QUALITY_CHANGE_RENDERED, ({ detail }) => { - if (detail.mediaType === 'video') { - this.videoRealQuality = detail.newQualityNumber; - } - }); - ev.bind(PLAYER_EVENT.LOCAL_MEDIA_LOAD, () => { - this.heartbeat = false; - }); - ev.bind(PLAYER_EVENT.DANMAKU_ADD, () => { - clearTimeout(this.onlineTimer); - this.onlineTimer = setTimeout(() => { - total(this.aid, this.cid) - .then(({ count }) => { - this.$auxiliary.$info.$number.$count = count; - }); - }, 1e3); - }); - ev.bind(PLAYER_EVENT.OPTINOS_CHANGE, ({ detail }) => { - const { incognito } = detail.player; - this.heartbeat = !incognito; - }); - ev.bind(PLAYER_EVENT.PROGRESS_VIDEOSHOT, ({ detail }) => { - this.progressVideoshot(...detail); - }); - ev.bind(PLAYER_EVENT.CALL_NEXT_PAGE, ({ detail }) => { - navigation?.navigate(detail); - }); - } - - async navigate(router: ROUTER, url: URL | Location) { - this.identify(); - url instanceof Location && (url = new URL(url.href)); - switch (router) { - case ROUTER.AV: { - const path = url.pathname.split('/'); - switch (true) { - case /^av\d+$/i.test(path[2]): { - this.aid = +path[2].slice(2); - break; - } - case /^bv1[a-z0-9]{9}$/i.test(path[2]): { - this.aid = +AV.fromBV(path[2]); - break; - } - } - if (this.aid) { - Promise.allSettled([cards({ av: this.aid }), pagelist(this.aid), detail(this.aid)]) - .then(([cards, pagelist, detail]) => { - const d = cards.status === "fulfilled" && cards.value; - const page = pagelist.status === "fulfilled" && pagelist.value; - const de = detail.status === "fulfilled" && detail.value; - if (d) { - const card = d[`av${this.aid}`]; - card.cid && (this.cid = card.cid); - if (page) { - const p = Number(url.searchParams.get('p')) || 1; - this.cid = page[p - 1].cid; - } - if (card.redirect_url) { - const path = card.redirect_url.split('/'); - switch (true) { - case /^ep\d+$/i.test(path[6]): { - this.epid = +path[6].slice(2); - break; - } - case /^ss\d+$/i.test(path[6]): { - this.ssid = +path[6].slice(2); - break; - } - } - } - if (this.cid) { - this.connect(this.aid, this.cid, this.ssid, this.epid); - (de && de.View.ugc_season) ? this.partUgcSeason(de) : de ? this.getRelated(de.Related.map(d => { - return { - src: d.pic + '@.webp', - title: d.title, - duration: d.duration, - view: d.stat.view, - danmaku: d.stat.danmaku, - author: d.owner.name, - callback() { - navigation?.navigate(`/video/av${d.aid}`); - }, - } - })) : this.getRelated(); - page && this.partAv(page); - } - } - }); - } else { - console.error('解析av号出错~'); - } - break; - } - case ROUTER.BANGUMI: { - const path = url.pathname.split('/'); - switch (true) { - case /^ss\d+$/i.test(path[3]): { - this.ssid = +path[3].slice(2); - break; - } - case /^ep\d+$/i.test(path[3]): { - this.epid = +path[3].slice(2); - break; - } - } - if (this.ssid || this.epid) { - pgcAppSeason(this.ssid ? { season_id: this.ssid } : { ep_id: this.epid }) - .then(async season => { - this.ssid || (this.ssid = season.season_id); - season.modules.forEach(d => { - switch (d.style) { - case "positive": - case "section": { - this.epid || (this.epid = d.data.episodes[0]?.ep_id); - if (this.epid) { - const ep = d.data.episodes.find(d => d.ep_id === this.epid); - if (ep) { - this.aid = ep.aid; - this.cid = ep.cid; - } - } - break; - } - } - }); - if (!this.cid && this.ssid) { - const d = await pgcSection(this.ssid); - const eps = d.main_section.episodes.concat(...d.section.map(d => d.episodes)); - const ep = this.epid ? eps.find(d => d.id === this.epid) : eps[0]; - if (ep) { - this.epid = ep.id; - this.aid = ep.aid; - this.cid = ep.cid; - } - } - if (this.epid && this.cid) { - this.connect(this.aid, this.cid, this.ssid, this.epid); - this.getRelated(); - } - this.partBangumi(season); - }); - } else { - console.error('解析Bangumi出错~'); - } - break; - } - case ROUTER.TOVIEW: { - const path = url.hash.split('/'); - switch (true) { - case /^av\d+$/i.test(path[1]): { - this.aid = +path[1].slice(2); - break; - } - case /^bv1[a-z0-9]{9}$/i.test(path[1]): { - this.aid = +AV.fromBV(path[1]); - break; - } - } - toviewWeb().then(toview => { - this.aid || (toview.length && (this.aid = toview[0].aid)); - if (this.aid) { - Promise.allSettled([cards({ av: this.aid }), pagelist(this.aid)]) - .then(([cards, pagelist]) => { - const d = cards.status === "fulfilled" && cards.value; - const page = pagelist.status === "fulfilled" && pagelist.value; - if (d) { - const card = d[`av${this.aid}`]; - card.cid && (this.cid = card.cid); - if (page && path[2]) { - const p = +path[2].slice(1); - this.cid = page[p - 1].cid; - } - if (card.redirect_url) { - const path = card.redirect_url.split('/'); - switch (true) { - case /^ep\d+$/i.test(path[6]): { - this.epid = +path[6].slice(2); - break; - } - case /^ss\d+$/i.test(path[6]): { - this.ssid = +path[6].slice(2); - break; - } - } - } - if (this.cid) { - this.connect(this.aid, this.cid, this.ssid, this.epid); - page ? this.partToView(toview, page) : this.partToView(toview); - } - } - }); - } else { - console.error('解析稍后再看出错~'); - } - }) - break; - } - case ROUTER.MEDIALIST: { - const path = url.pathname.split('/'); - const ml = +path[2].slice(2); - if (ml) { - favResourceList(ml).then(({ medias, has_more }) => { - this.aid = Number(url.searchParams.get('aid')) || medias[0].id; - if (this.aid) { - Promise.allSettled([cards({ av: this.aid }), pagelist(this.aid)]) - .then(([cards, pagelist]) => { - const d = cards.status === "fulfilled" && cards.value; - const page = pagelist.status === "fulfilled" && pagelist.value; - if (d) { - const card = d[`av${this.aid}`]; - card.cid && (this.cid = card.cid); - if (page) { - const p = Number(url.searchParams.get('p')) || 1; - this.cid = page[p - 1].cid; - } - if (card.redirect_url) { - const path = card.redirect_url.split('/'); - switch (true) { - case /^ep\d+$/i.test(path[6]): { - this.epid = +path[6].slice(2); - break; - } - case /^ss\d+$/i.test(path[6]): { - this.ssid = +path[6].slice(2); - break; - } - } - } - if (this.cid) { - this.connect(this.aid, this.cid, this.ssid, this.epid); - page ? this.partMedialist(medias, page) : this.partMedialist(medias); - } - } - - // 请求更多媒体 - let pn = 2; - const getMore = () => { - favResourceList(ml, pn).then(({ medias, has_more }) => { - pn++; - this.partMedialist(medias); - has_more && getMore(); - }) - } - has_more && getMore(); - }); - } else { - console.error('解析播放列表出错~'); - } - }) - } else { - console.error('解析播放列表出错~'); - } - break; - } - } - } - - connect( - aid: number, - cid: number, - ssid?: number, - epid?: number, - kind?: GroupKind, - ) { - this.identify(); - Reflect.set(self, 'aid', this.aid = aid); - Reflect.set(self, 'cid', this.cid = cid); - ssid && (this.ssid = ssid); - epid && (this.epid = epid); - this.qn = +cookie.get('CURRENT_QUALITY') || 0; - if (kind === GroupKind.Pugv && epid) { - pugvPlayurl(aid, cid, epid, this.qn) - .then(d => { - this.attachMedia(d); - d.is_preview && ev.trigger(PLAYER_EVENT.TOAST, '正在观看预览'); - }) - .finally(() => { - video.addEventListener('canplay', () => { - this.getDanmaku(); - this.getUser(); - this.getVideoShot(); - }, { once: true }); - }); - } else if (epid) { - pgcPlayurl(aid, cid, epid, this.qn).then(d => { - this.attachMedia(d); - d.record_info?.record && this.$area.$wrap.$record.addRecord(d.record_info.record); - d.is_preview && ev.trigger(PLAYER_EVENT.TOAST, '正在观看预览'); - }).finally(() => { - video.addEventListener('canplay', () => { - this.getDanmaku(); - this.getUser(); - this.getVideoShot(); - }, { once: true }); - }); - } else { - playurl(aid, cid, this.qn) - .then(this.attachMedia) - .finally(() => { - video.addEventListener('canplay', () => { - this.getDanmaku(); - this.getUser(); - this.getVideoShot(); - }, { once: true }); - }); - } - } - - private attachMedia = ( - d: Awaited> | Awaited> | Awaited>, - seekTime?: number, - ) => { - if (d) { - this.videoRealQuality = d.quality; - this.playurl = d; - if (d.dash) { - /** Hires或者杜比全景声 */ - const defaultAudioQuality = Number(d.dash.audio?.[0]?.id) || 30280; - const highAudioQuality = d.dash.flac?.audio?.id; - this.dashjs(d.dash, { - defaultAudioQuality: highAudioQuality || defaultAudioQuality, - defaultVideoQuality: this.qn, - enableHEVC: options.player.policy === POLICY.HEVC, - enableAV1: options.player.policy === POLICY.AV1, - isAutoPlay: false, - isDynamic: false, - enableMultiAudioTracks: false, - abrStrategy: DashPlayer.STRING.ABR_BOLA, - stableBufferTime: this.dynamicBuffer[0] || 20, - // DRM fields - // protectionDataSet: d.protection?.protectionData, - // ignoreEmeEncryptedEvent:d.protection?.ignoreEmeEncryptedEvent - }, this.qn, seekTime); - } else { - let seekType = 'range'; - let duration = 0; - const type = d.format.indexOf('mp4') > -1 ? 'mp4' : 'flv' - this.flvjs(d.durl ? type === "mp4" ? { - type, - url: d.durl[0].url, - } : { - segments: d.durl.map(d => { - d.length && (duration = d.length); - /\/ws\.acgvideo\.com\//.test(d.url) && (seekType = 'param'); - return { - duration: d.length, - filesize: d.size, - url: d.url, - backupURL: d.backup_url - } - }), - type, - duration, - } : { - type: 'mp4', - url: '//s1.hdslb.com/bfs/static/player/media/error.mp4', - }, { - enableWorker: false, - stashInitialSize: 1024 * 64, - accurateSeek: true, - seekType: <'range'>seekType, - rangeLoadZeroStart: false, - lazyLoadMaxDuration: 100, - lazyLoadRecoverDuration: 50, - deferLoadAfterSourceOpen: false, - fixAudioTimestampGap: false, - reuseRedirectedURL: true, - }, seekTime); - } - this.$area.$control.$quality.update(d.accept_quality.map((s, i) => { return <[number, string]>[s, d.accept_description[i]] }).reverse()); - this.qn && (this.$area.$control.$quality.$value = d.quality); - } else { - this.flvjs({ - type: 'mp4', - url: '//s1.hdslb.com/bfs/static/player/media/error.mp4', - }); - } - } - - private async getDanmaku() { - this.realDm.getSevers(); - const d = this.dmView = await view(this.cid, this.aid); - if (d.dmSge) { - const { total } = d.dmSge; - for (let i = 1; i <= total; i++) { - segSo(this.cid, this.aid, i) - .then(d => { - this.danmaku.add(d.elems); - }) - .catch(() => { }) - } - } - // 获取弹幕弹幕专包 - d?.specialDms?.forEach(d => { - fetch(https(d)) // 此处不能携带cookie - .then(d => d.arrayBuffer()) - .then(d => DmSegMobileReply.decode(d)) - .then(d => { - this.danmaku.add(d.elems); - }) - .catch(() => { }) - }); - // TODO: 指令弹幕 - } - - addDanmaku(dms: IDanmaku[]) { - this.danmaku.add(dms); - } - - setOnline(num: number) { - this.$auxiliary.$info.$number.$count = num; - } - - /** - * 更新播放源 - * - * @param qn 指定画质,用于画质切换 - * @param success 成功获取对应画质的回调 - * @param fail 获取对应画质失败回调 - */ - private updateSource(qn?: number, success?: Function, fail?: Function) { - const ajax = this.epid ? pgcPlayurl(this.aid, this.cid, this.epid, this.qn) : playurl(this.aid, this.cid, this.qn); - ajax.then(d => { - if (d) { - this.playurl = d; - if (d.dash && this.dashPlayer) { - this.dashPlayer.updateSource(d.dash!); - if (qn === 0) { - this.dashPlayer.setAutoSwitchQualityFor('video', true); - success?.(); - } else if (d.quality === qn) { - this.dashPlayer.setAutoSwitchQualityFor('video', false); - this.dashPlayer.setQualityFor('video', qn); - success?.(); - } else if (qn !== undefined) { - fail?.(); - } else { - success?.(); - } - } else { - this.attachMedia(d, video.currentTime); - if (d.quality === qn) { - success?.(); - } else if (qn !== undefined) { - fail?.(); - } else { - success?.(); - } - } - this.$area.$control.$quality.update(d.accept_quality.map((s, i) => { return <[number, string]>[s, d.accept_description[i]] }).reverse()); - this.qn && (this.$area.$control.$quality.$value = d.quality); - } else { - fail?.(); - } - }).catch(() => { - fail?.(); - }) - } - - /** 选择画质 */ - private setQualityFor(qn = 0) { - if (qn === 0) { - this.dashPlayer?.setAutoSwitchQualityFor('video', true); - } else { - this.dashPlayer?.setAutoSwitchQualityFor('video', false); - this.dashPlayer?.setQualityFor('video', qn); - } - this.$area.$control.$quality.$value = qn; - } - - /** 获取视频缩略图 */ - private async getVideoShot() { - this.pvData = await videoshot(this.cid, this.aid); - } - - /** - * 实时更新缩略图 - * - * @param element 缩略图所在节点 - * @param value 缩略图对应的时间 - */ - private progressVideoshot(element: HTMLElement, value: number) { - if (this.pvData) { - const { - pv_index, - pv_img, - pv_x_len = 10, - pv_y_len = 10, - pv_x_size = 160, - pv_y_size = 90, - } = this.pvData; - if (pv_index && pv_img) { - const p = pv_index.findIndex((d, i) => value >= pv_index[i - 1] && value < d); - if (p >= 0 && pv_img[Math.floor(p / 100)]) { - element.style.inlineSize = pv_x_size + 'px'; - element.style.blockSize = pv_y_size + 'px'; - element.style.transform = `scale(${160 / pv_x_size})`; - element.style.transformOrigin = 'bottom'; - element.style.backgroundImage = `url(${pv_img[Math.floor(p / 100)]})`; - element.style.backgroundPosition = pv_x_size * -(p % 100 % pv_x_len) + "px " + pv_y_size * -Math.floor(p % 100 / pv_y_len) + "px"; - - } - } - if (this.viewPoints) { - const d = this.viewPoints.find(d => d.from <= value && d.to > value); - element.textContent = d ? d.content : ''; - } - } - } - - /** 获取用户数据 */ - private async getUser() { - const user = await v2(this.cid, this.aid, this.ssid); - if (user.subtitle) { - user.subtitle.subtitles.forEach(d => { - fetch(https(d.subtitle_url)) - .then(e => e.json()) - .then(f => { - const cc = new ClosedCaption(); - cc.addCue(...f.body); - const file = new File([cc.toWebVTT().toJSON()], `${d.lan_doc}.vtt`); - this.$area.$control.$closedCaption.load(file); - }) - }); - } - user.view_points && (this.viewPoints = user.view_points); - // 视频看点 - // 高级弹幕、代码弹幕、BAS弹幕发送面板 - // 功能窗口 - // 历史弹幕 - // 指令弹幕 - // TODO:杜比全景声,等待vip和设备支持 - this.$area.$wrap.$panel.userLoad(); - } - - getRelated(items?: IRecommend[]) { - if (items) { - this.$auxiliary.$recommend.add(items); - } else if (this.ssid) { - recommend(this.ssid).then(d => { - this.$auxiliary.$recommend.add(d.map(d => { - return { - src: d.cover + '@.webp', - title: d.title, - view: d.stat.view, - danmaku: d.stat.danmaku, - callback() { - navigation?.navigate(d.url); - }, - } - })); - }) - } else { - related(this.aid).then(d => { - this.$auxiliary.$recommend.add(d.map(d => { - return { - src: d.pic + '@.webp', - title: d.title, - duration: d.duration, - view: d.stat.view, - danmaku: d.stat.danmaku, - author: d.owner.name, - callback() { - navigation?.navigate(`/video/av${d.aid}`); - }, - } - })); - }); - } - } - - /** - * av页分p管理 - * - * @param page 分P数据 - * @param toview 是否稍后再看页面 - */ - private partAv(page: Awaited>) { - this.$area.$control.$next.update(page.map((d, i) => `/video/av${this.aid}?p=${i + 1}`), page.findIndex(d => d.cid === this.cid) || 0); - this.#part.update(page, this.aid, this.cid); - } - - /** - * Bangumi分p管理 - * - * @param page Bangumi数据 - */ - private async partBangumi(page: Awaited>) { - page.modules.forEach(d => { - switch (d.style) { - case "positive": { - this.$area.$control.$next.update(d.data.episodes.map(d => `/bangumi/play/ep${d.ep_id}`), d.data.episodes.findIndex(d => d.cid === this.cid) || 0); - break; - } - } - }); - - if (page.bkg_cover) { - this.style.backgroundImage = `url(${https(page.bkg_cover)}@.webp)`; - this.style.backgroundSize = `cover`; - } - } - - /** - * 合集分P管理 - * - * @param page 合集数据 - */ - private partUgcSeason(page: Awaited>) { - if (page.View.ugc_season) { - this.$auxiliary.$recommend.add(page.View.ugc_season.sections[0].episodes.map(d => { - return { - src: d.arc.pic + '@.webp', - title: d.title, - duration: d.arc.duration, - view: d.arc.stat.view, - danmaku: d.arc.stat.danmaku, - callback() { - navigation?.navigate(`/video/av${d.aid}`); - }, - selected: d.cid === this.cid, - } - })); - this.$auxiliary.$filter.$recommend.textContent = '视频合集'; - } - } - - /** 稍后再看分P管理 */ - private partToView(page: Awaited>, part?: Awaited>) { - const ids: string[] = []; - let ci = 0; - this.$auxiliary.$recommend.add(page.map(d => { - if (d.videos === 1) { - d.aid === this.aid && (ci = ids.length); - ids.push(`/watchlater/#/av${d.aid}`); - } else { - d.pages.forEach(e => { - e.cid === this.cid && (ci = ids.length); - ids.push(`/watchlater/#/av${d.aid}/p${e.page}`); - }); - } - return { - src: d.pic + '@.webp', - title: d.title, - duration: d.duration, - view: d.stat.view, - danmaku: d.stat.danmaku, - callback() { - navigation?.navigate(`/watchlater/#/av${d.aid}`); - }, - selected: d.aid === this.aid, - } - })); - this.$auxiliary.$filter.$recommend.textContent = '稍后再看'; - part && this.#part.updateToview(part, this.aid, this.cid); - this.$area.$control.$next.update(ids, ci); - } - - /** 播放列表分P管理 */ - private partMedialist(page: Awaited>['medias'], part?: Awaited>) { - const ids: string[] = []; - let ci = 0; - this.$auxiliary.$recommend.add(page.map(d => { - d.id === this.aid && (ci = ids.length); - if (d.page === 1) { - const url = new URL(location.href); - url.searchParams.set('aid', d.id); - ids.push(url.href); - } else { - // 暂时没有分p列表 - for (let i = 0; i > d.page; i++) { - const url = new URL(location.href); - url.searchParams.set('aid', d.id); - url.searchParams.set('p', i + 1); - ids.push(url.href); - } - } - return { - src: d.cover + '@.webp', - title: d.title, - duration: d.duration, - view: d.cnt_info.play, - danmaku: d.cnt_info.danmaku, - callback() { - const url = new URL(location.href); - url.searchParams.set('aid', d.id) - navigation?.navigate(url.href); - }, - selected: d.id === this.aid, - } - })); - this.$auxiliary.$filter.$recommend.textContent = '播放列表'; - part && this.#part.updateMedialist(part, this.aid, this.cid); - this.$area.$control.$next.update(ids, ci); - } - - identify = () => { - this.aid = 0; - this.cid = 0; - this.ssid = 0; - this.epid = 0; - this.heartbeat = !options.player.incognito; - this.heartbeatSeek = false; - delete this.playurl; - delete this.pvData; - delete this.dmView; - delete this.viewPoints; - super.identify(); - this.#part.identify(); - this.realDm.identify(); - this.style.backgroundImage = ''; - this.style.backgroundSize = ''; - this.$auxiliary.$filter.$recommend.textContent = '推荐视频'; - this.classList.remove('nano'); - } -} - -//////////////////////////// 全局增强 //////////////////////////// -declare global { - /** 基于哈希消息认证码的一次性口令的密钥 */ - const __BILI_PLAYER_STYLE__: string; -} \ No newline at end of file diff --git a/src/main/bilibili/player/nano/index.ts b/src/main/bilibili/player/nano/index.ts deleted file mode 100644 index efef978..0000000 --- a/src/main/bilibili/player/nano/index.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { GroupKind } from "./GroupKind"; - -export interface NanoInitData { - aid?: number | string; - cid?: number | string; - bvid?: string; - episodeId?: number | string; - seasonId?: number | string; - revision?: number - featureList?: Set, - enableAV1?: boolean; - enableWMP?: boolean; - enableHEVC?: boolean; - hideBoxShadow?: boolean; - t?: number; - kind?: GroupKind; - element?: HTMLDivElement; - auxiliary?: HTMLDivElement; -} \ No newline at end of file diff --git a/src/main/bilibili/player/part/index.ts b/src/main/bilibili/player/part/index.ts deleted file mode 100644 index f355917..0000000 --- a/src/main/bilibili/player/part/index.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { pagelist } from "../../../../io/com/bilibili/api/x/player/pagelist"; -import { customElement } from "../../../../utils/Decorator/customElement"; -import { Element } from "../../../../utils/element"; - -/** 播放器右侧面板 */ -@customElement('div') -export class Part extends HTMLDivElement { - - /** - * 需要监听变动的属性。 - * 与实例方法`attributeChangedCallback`配合使用。 - * 此字符串序列定义了`attributeChangedCallback`回调时的第一个参数的可能值。 - */ - // static observedAttributes = []; - - /** - * 在属性更改、添加、移除或替换时调用。 - * 需要与静态属性`observedAttributes`配合使用。 - * 此回调的第一个参数在`observedAttributes`数组中定义。 - */ - // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} - - /** 初始化标记 */ - // #inited = false; - - /** 每当元素添加到文档中时调用。 */ - // connectedCallback() {} - - /** 每当元素从文档中移除时调用。 */ - // disconnectedCallback() {} - - /** 每当元素被移动到新文档中时调用。 */ - // adoptedCallback() {} - - private $spread = Element.add('button', { class: 'spread' }, undefined, '展开'); - - constructor() { - super(); - this.classList.add('bili-part'); - - this.$spread.addEventListener('click', () => { - this.$spread.textContent = this.classList.toggle('spread') ? '收起' : '展开'; - }); - } - - update( - data: Awaited>, - aid: number, - cid: number, - ) { - if (data.length > 1) { - let p = ''; - data.forEach(d => { - p += `${d.page}、${d.part}`; - }); - this.innerHTML = p; - this.scrollHeight > 25 && this.appendChild(this.$spread); - } - } - - updateToview( - data: Awaited>, - aid: number, - cid: number, - ) { - if (data.length > 1) { - let p = ''; - data.forEach(d => { - p += `${d.page}、${d.part}`; - }); - this.innerHTML = p; - this.scrollHeight > 25 && this.appendChild(this.$spread); - } - } - - updateMedialist( - data: Awaited>, - aid: number, - cid: number, - ) { - if (data.length > 1) { - let p = ''; - const url = new URL(location.href); - data.forEach(d => { - url.searchParams.set('aid', aid); - url.searchParams.set('p', d.page); - p += `${d.page}、${d.part}`; - }); - this.innerHTML = p; - this.scrollHeight > 25 && this.appendChild(this.$spread); - } - } - - identify = () => { - this.replaceChildren(); - } -} \ No newline at end of file diff --git a/src/main/bilibili/player/style/index.css b/src/main/bilibili/player/style/index.css deleted file mode 100644 index 0ffab24..0000000 --- a/src/main/bilibili/player/style/index.css +++ /dev/null @@ -1,32 +0,0 @@ -@import url(./part/index.css); - -@scope { - :scope { - inline-size: 980px; - min-inline-size: 980px; - block-size: calc(980px / 16 * 9 + 68px); - margin-inline: auto; - - @media screen and (min-width:1400px) { - - & { - inline-size: 1160px; - block-size: calc(1160px / 16 * 9 + 68px); - } - } - - @media screen and (min-width:2500px) { - - & { - inline-size: 1920px; - block-size: calc(1920px / 16 * 9 + 68px); - } - } - - &.nano { - inline-size: 100% !important; - block-size: 100% !important; - min-inline-size: initial; - } - } -} \ No newline at end of file diff --git a/src/main/bilibili/player/style/part/index.css b/src/main/bilibili/player/style/part/index.css deleted file mode 100644 index d293aec..0000000 --- a/src/main/bilibili/player/style/part/index.css +++ /dev/null @@ -1,102 +0,0 @@ -.bili-part { - position: relative; - font-size: 12px; - display: flex; - flex-wrap: wrap; - align-items: center; - row-gap: 12px; - column-gap: 20px; - margin-inline: auto; - margin-block: 1em; - inline-size: 980px; - min-inline-size: 980px; - overflow: hidden; - - @media screen and (min-width:1400px) { - - & { - inline-size: 1160px; - } - } - - @media screen and (min-width:2500px) { - - & { - inline-size: 1920px; - } - } - - >a { - flex-shrink: 0; - inline-size: 117px; - block-size: 17px; - color: #222; - border-radius: 4px; - background-color: #fff; - border: 1px solid #ccd0d7; - padding-block: 3px; - padding-inline: 7px; - outline: none; - text-decoration: none; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - transition: .15s; - cursor: pointer; - content-visibility: auto; - contain-intrinsic-inline-size: 133px; - contain-intrinsic-block-size: 25px; - - &.on, - &:hover { - color: #fff; - background-color: #00a1d6; - border-color: #00a1d6; - } - - &.on { - pointer-events: none; - } - } - - &:empty { - display: none; - } - - &:not(.spread):has(>.spread) { - flex-wrap: nowrap; - } - - &.spread { - flex-wrap: wrap; - - >.spread { - position: inherit; - } - } - - >.spread { - position: absolute; - inset-inline-end: 0; - inline-size: 117px; - block-size: 17px; - box-sizing: content-box; - color: #222; - border-radius: 4px; - background-color: #fff; - border: 1px solid #ccd0d7; - padding-block: 3px; - padding-inline: 7px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - transition: .15s; - cursor: pointer; - - &:hover { - color: #fff; - background-color: #00a1d6; - border-color: #00a1d6; - } - } -} \ No newline at end of file diff --git a/src/main/bilibili/space/index.ts b/src/main/bilibili/space/index.ts deleted file mode 100644 index 83575ed..0000000 --- a/src/main/bilibili/space/index.ts +++ /dev/null @@ -1,40 +0,0 @@ -// B站用户空间专属脚本 - -import { FetchHook } from "../../../utils/hook/fetch"; -import json from "./mid.json"; - -enum SPACE { - '哔哩哔哩番剧出差' = 11783021, - 'b站_戲劇咖' = 1988098633, - 'b站_綜藝咖' = 2042149112, -} - -switch (true) { - case location.pathname.includes('11783021'): { - json.data.mid = 11783021; - json.data.name = SPACE[11783021]; - json.data.official.desc = SPACE[11783021] + ' 官方帐号'; - break; - } - case location.pathname.includes('1988098633'): { - json.data.mid = 1988098633; - json.data.name = SPACE[1988098633]; - json.data.official.desc = SPACE[1988098633] + ' 官方帐号'; - break; - } - case location.pathname.includes('2042149112'): { - json.data.mid = 2042149112; - json.data.name = SPACE[2042149112]; - json.data.official.desc = SPACE[2042149112] + ' 官方帐号'; - break; - } -} - - - -new FetchHook('acc/info?').response(async res => { - const text = await res.text(); - if (text.includes('-404')) { - return JSON.stringify(json); - } -}); \ No newline at end of file diff --git a/src/main/bilibili/space/mid.json b/src/main/bilibili/space/mid.json deleted file mode 100644 index c936dc0..0000000 --- a/src/main/bilibili/space/mid.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "code": 0, - "data": { - "birthday": "1980-01-01", - "coins": 0, - "face": "http://i2.hdslb.com/bfs/face/9f10323503739e676857f06f5e4f5eb323e9f3f2.jpg", - "fans_badge": false, - "is_followed": true, - "jointime": 1436351229, - "level": 6, - "mid": 11783021, - "moral": 0, - "name": "哔哩哔哩番剧出差", - "official": { - "type": 1, - "desc": "哔哩哔哩番剧出差 官方账号" - }, - "pendant": { - "pid": 0, - "name": "", - "image": "", - "expire": 0 - }, - "rank": "10000", - "sex": "保密", - "sign": "", - "silence": 0, - "sys_notice": {}, - "theme": {}, - "user_honour_info": { - "colour": null, - "mid": 0, - "tags": null - }, - "vip": { - "avatar_subscript": 1, - "avatar_subscript_url": "http://i0.hdslb.com/bfs/vip/icon_Certification_big_member_22_3x.png", - "due_date": 1655740800000, - "label": { - "bg_color": "#FB7299", - "bg_style": 1, - "border_color": "", - "label_theme": "annual_vip", - "path": "", - "text": "年度大会员", - "text_color": "#FFFFFF" - }, - "nickname_color": "#FB7299", - "role": 3, - "status": 1, - "theme_type": 0, - "type": 2, - "vip_pay_type": 1 - } - }, - "message": "0", - "ttl": 1 -} \ No newline at end of file diff --git a/src/main/comment/avatar.ts b/src/main/comment/avatar.ts new file mode 100644 index 0000000..2b5c489 --- /dev/null +++ b/src/main/comment/avatar.ts @@ -0,0 +1,7 @@ +/** 头像图标 //i0.hdslb.com/bfs/seed/jinkela/short/user-avatar/${Avatar}.svg */ +export enum Avatar { + personal, + business, + 'big-vip', + 'small-vip', +} \ No newline at end of file diff --git a/src/main/bilibili/comment/icon-level.ts b/src/main/comment/icon-level.ts similarity index 100% rename from src/main/bilibili/comment/icon-level.ts rename to src/main/comment/icon-level.ts diff --git a/src/main/comment/index.css b/src/main/comment/index.css new file mode 100644 index 0000000..7fb82a4 --- /dev/null +++ b/src/main/comment/index.css @@ -0,0 +1,241 @@ +@import url(./send.css); +@import url(./reply.css); +@import url(./sub-reply.css); + +@scope { + :scope { + color-scheme: light dark; + + --222: #222; + --e5e9ef: #e5e9ef; + --00a1d6: #00a1d6; + --f4f5f7: #f4f5f7; + --555: #555; + --fff: #fff; + --99a2aa: #99a2aa; + --6a738540: #6a738540; + --00b5e5: #00b5e5; + --6d757a: #6d757a; + --6a73854d: #6a73854d; + --757575: #757575; + --ddd: #ddd; + --f4f4f4: #f4f4f4; + --fb7299: #fb7299; + --ff81aa: #ff81aa; + --b8c0cc: #b8c0cc; + --0000005c: #0000005c; + --736ce721: #736ce721; + --00a1d621: #00a1d621; + --00d4ff21: #00d4ff21; + --18191cd9: #18191cd9; + + inline-size: 100%; + font-size: 12px; + display: flex; + flex-direction: column; + row-gap: 20px; + } + + a { + text-decoration: none; + } +} + +.head { + font-size: 18px; + color: var(--222); + display: flex; + column-gap: 10px; + + &::before { + content: attr(data-total); + } +} + +.body { + display: flex; + flex-direction: column; + + >.header { + margin-block-end: 24px; + border-block-end: 1px solid var(--e5e9ef); + display: flex; + justify-content: space-between; + + >form { + display: flex; + column-gap: 16px; + + >label { + block-size: 20px; + font-size: 14px; + border-block-end: 1px solid transparent; + padding-block: 8px; + font-weight: bold; + color: var(--222); + cursor: pointer; + text-align: center; + align-content: center; + position: relative; + + &:hover { + color: var(--00a1d6); + } + + &:has(input:checked) { + border-block-end-color: var(--00a1d6); + color: var(--00a1d6); + pointer-events: none; + + &::after { + content: ""; + position: absolute; + inset-block-end: 0; + inset-inline-start: calc(50% - 3px); + border-block-end: 3px solid var(--00a1d6); + border-block-start: 3px solid transparent; + border-inline: 3px solid transparent; + } + } + + >input { + appearance: none; + display: none; + } + } + } + + >.interaction { + display: flex; + align-items: center; + + &::before { + content: "共" attr(data-num) "页"; + margin-inline-end: 10px; + } + + >a { + margin-inline: 4px; + cursor: pointer; + + &:hover { + color: var(--00a1d6); + } + + &.on, + &.more { + pointer-events: none; + } + + &.on { + color: var(--00a1d6); + font-weight: bold; + } + } + } + } + + >.list { + padding-block-start: 20px; + display: flex; + flex-direction: column; + } + + >.paging { + inline-size: 100%; + position: relative; + + >a { + font-size: 14px; + block-size: 36px; + border-radius: 4px; + min-inline-size: 15px; + margin-inline: 2px; + padding-inline: 10px; + background-color: var(--fff); + border: solid 1px var(--ddd); + cursor: pointer; + transition: 0.2s all; + display: inline-flex; + align-items: center; + justify-content: center; + + &.prev, + &.next { + padding-inline: 15px; + } + + &.on, + &.more { + pointer-events: none; + } + + &.more { + border: 0; + } + + &:hover, + &.on { + background-color: var(--00a1d6); + color: var(--fff); + border: 1px solid var(--00a1d6); + } + } + + >.page-jump { + color: var(--99a2aa); + position: absolute; + inset-inline-end: 0; + + >input { + inline-size: 24px; + block-size: 24px; + margin-inline: 5px; + padding-inline: 10px; + border-radius: 4px; + font-size: 12px; + border: 1px solid var(--ddd); + text-align: center; + outline: none; + box-shadow: none; + } + } + + &:empty { + display: none; + } + } +} + +.image-popover { + inline-size: 100vi; + block-size: 100vb; + border: 0; + background: var(--18191cd9); + transition: all .5s allow-discrete; + + @starting-style { + scale: 0; + } + + &:not(:popover-open):not(dialog[open]) { + scale: 0; + } + + >.image-con { + inline-size: 100%; + block-size: 100%; + padding-inline: 100px; + display: flex; + justify-content: center; + align-items: center; + overflow-y: auto; + scrollbar-width: none; + cursor: zoom-out; + + >img { + max-inline-size: 100%; + cursor: default; + } + } +} \ No newline at end of file diff --git a/src/main/comment/index.d.css.ts b/src/main/comment/index.d.css.ts new file mode 100644 index 0000000..eb9bc4f --- /dev/null +++ b/src/main/comment/index.d.css.ts @@ -0,0 +1,2 @@ +declare const stylesheet: CSSStyleSheet; +export default stylesheet; \ No newline at end of file diff --git a/src/main/comment/index.ts b/src/main/comment/index.ts new file mode 100644 index 0000000..c1d1693 --- /dev/null +++ b/src/main/comment/index.ts @@ -0,0 +1,467 @@ +import { pgcAppSeason } from "../../io/com/bilibili/api/pgc/view/v2/app/season"; +import { IEpisode, pgcSection } from "../../io/com/bilibili/api/pgc/view/web/season/user/section"; +import { toviewWeb } from "../../io/com/bilibili/api/x/v2/history/toview/web"; +import { reply } from "../../io/com/bilibili/api/x/v2/reply"; +import { action } from "../../io/com/bilibili/api/x/v2/reply/action"; +import { replyAdd } from "../../io/com/bilibili/api/x/v2/reply/add"; +import { replyMain } from "../../io/com/bilibili/api/x/v2/reply/main"; +import { nav } from "../../io/com/bilibili/api/x/web-interface/nav"; +import { toastr } from "../../toastr"; +import { AV } from "../../utils/av"; +import { cookie } from "../../utils/cookie"; +import { customElement } from "../../utils/Decorator/customElement"; +import { Element } from "../../utils/element"; +import { MAIN_EVENT, mainEv } from "../event"; +import { ROUTER } from "../router"; +import stylesheet from "./index.css" with {type: 'css'}; +import { Reply } from "./reply"; +import { CommentSend } from "./send"; +import { SubReply } from "./sub-reply"; + +/** 评论区 */ +@customElement(undefined, `comment-${Date.now()}`) +export class Comment extends HTMLElement { + + /** + * 需要监听变动的属性。 + * 与实例方法`attributeChangedCallback`配合使用。 + * 此字符串序列定义了`attributeChangedCallback`回调时的第一个参数的可能值。 + */ + // static observedAttributes = []; + + /** + * 在属性更改、添加、移除或替换时调用。 + * 需要与静态属性`observedAttributes`配合使用。 + * 此回调的第一个参数在`observedAttributes`数组中定义。 + */ + // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} + + /** 每当元素添加到文档中时调用。 */ + // connectedCallback() {} + + /** 每当元素从文档中移除时调用。 */ + // disconnectedCallback() {} + + /** 每当元素被移动到新文档中时调用。 */ + // adoptedCallback() {} + + #host = this.attachShadow({ mode: 'closed' }); + + #head = Element.add('div', { class: 'head', appendTo: this.#host, innerText: `评论`, data: { total: '-' } }); + + #body = Element.add('div', { class: 'body', appendTo: this.#host }); + + #header = Element.add('div', { class: 'header', appendTo: this.#body }); + + #id = crypto.randomUUID(); + + #order = Element.add('form', { appendTo: this.#header }); + + #interaction = Element.add('div', { class: 'interaction', data: { num: '-' }, attribute: { title: 'B站已不提供页码数,估算数据仅供参考!' }, appendTo: this.#header }); + + #send = this.#body.appendChild(new CommentSend(this)); + + #list = Element.add('div', { class: 'list', appendTo: this.#body }); + + #paging = Element.add('div', { class: 'paging', appendTo: this.#body }); + + #imagePopover = Element.add('div', { class: 'image-popover', appendTo: this.#host }); + + #imageCon = Element.add('div', { class: 'image-con', appendTo: this.#imagePopover }); + + #oid: number | string = 0; + + set $oid(v) { + if (v !== this.#oid) { + this.#identify(); + this.#oid = v; + this.#load(); + } + } + + get $oid() { + return this.#oid; + } + + #pn = 1; + + set $pn(v) { + if (v !== this.#pn) { + this.#pn = v; + this.#load(); + } + } + + get $pn() { + return this.#pn; + } + + #sort: 0 | 2 = 2; + + set $sort(v) { + if (v !== this.#sort) { + this.#pn = 1; + this.#sort = v; + this.#load(); + } + } + + get $sort() { + return this.#sort; + } + + #type = 1; + + set $type(v) { + if (v !== this.#type) { + this.#type = v; + this.#load(); + } + } + + get $type() { + return this.#type; + } + + #uid = 0; + + #hash = ''; + + // 回复评论相关 + $root = ''; + + $parent = ''; + + $uname = ''; + + $replyCurrent?: Reply; + + constructor( + oid?: number | string, + pn?: number, + sort?: 0 | 2, + type?: number, + seek_rpid?: number, + ) { + super(); + + this.#host.adoptedStyleSheets = [stylesheet]; + this.#imagePopover.popover = 'auto'; + + this.#order.addEventListener('change', () => { + const d = new FormData(this.#order); + this.$sort = <0>+[...d.values()][0]; + }); + this.#interaction.addEventListener('click', this.$onPageClick); + this.#paging.addEventListener('click', this.$onPageClick); + this.#send.addEventListener('submit', () => { + const d = new FormData(this.#send.$submit); + const message = [...d.values()][0]; + const csrf = cookie.get('bili_jct'); + if (csrf && message) { + replyAdd({ csrf, oid: this.#oid, message: (this.$uname && (this.$root !== this.$parent) ? `回复 @${this.$uname} :` : '') + message, type: this.#type, root: this.$root, parent: this.$parent }) + .then(({ code, message, data }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, data } }); + this.$replyCurrent ? this.$replyCurrent.$replyBox.insertAdjacentElement('afterbegin', new SubReply(this, data.reply, 0)) : this.#list.insertAdjacentElement('afterbegin', new Reply(this, data.reply, 0)); + this.#send.$empty(); + this.matches(":popover-open") && this.#send.hidePopover(); + }) + .catch(e => { + toastr.error('弹幕发送错误', e); + console.error(e); + }); + } + }); + this.#list.addEventListener('click', ({ target, screenX, screenY }) => { + if (target instanceof HTMLImageElement && target.classList.contains('image')) { + // 评论图片放大 + this.#imageCon.innerHTML = ``; + this.#imagePopover.style.transformOrigin = `${screenX}px ${screenY}px`; + this.#imagePopover.showPopover(); + } else if (target instanceof HTMLSpanElement && target.classList.contains('reply')) { + const id = crypto.randomUUID(); + target.style.setProperty('anchor-name', `--${id}`); + this.#send.style.setProperty('position-anchor', `--${id}`); + this.#send.popover = 'auto'; + this.#send.showPopover(); + } else if (target instanceof HTMLSpanElement && target.classList.contains('like-num')) { + const csrf = cookie.get('bili_jct'); + const { rpid } = target.dataset; + const liked = target.classList.contains('liked') ? 0 : 1; + if (csrf && rpid) { + action(csrf, this.#oid, rpid, liked, this.#type) + .then(({ code, message }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message } }); + (target.lastChild).data = target.classList.toggle('liked') ? +(target.lastChild).data + 1 : +(target.lastChild).data - 1; + + }) + .catch(e => { + toastr.error(`${liked ? '点赞' : '取消'}失败`, e); + console.error(e) + }) + } + } + }); + this.#imageCon.addEventListener('click', e => { + const { target } = e; + if (target === this.#imageCon) { + this.#imagePopover.hidePopover(); + } + }); + + mainEv.bind(MAIN_EVENT.NAVIGATE, ({ detail }) => { this.$navigate(...detail) }); + + nav() + .then(({ code, message, data }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, data } }); + data.isLogin && (this.#uid = data.mid); + }) + .catch(e => { + console.error(e); + }); + + // 初始化 + this.#identify(); + oid && (this.#oid = oid); + pn && (this.#pn = pn); + sort === undefined || (this.#sort = sort); + seek_rpid && (this.#hash = seek_rpid); + type && (this.#type = type); + this.#load(); + } + + /** 页面路由 */ + private async $navigate(router: ROUTER, url = new URL(location.href)) { + url.hash.replace(/reply(\d+)/, (d, h) => { + this.#hash = h; + return d; + }); + switch (router) { + case ROUTER.AV: { + const path = url.pathname.split('/'); + let aid = 0; + switch (true) { + case /^av\d+$/i.test(path[2]): { + aid = +path[2].slice(2); + break; + } + case /^bv1[a-z0-9]{9}$/i.test(path[2]): { + aid = +AV.fromBV(path[2]); + break; + } + } + aid && (this.$oid = aid); + break; + } + case ROUTER.BANGUMI: { + const path = url.pathname.split('/'); + let ssid = 0, epid = 0; + switch (true) { + case /^ss\d+$/i.test(path[3]): { + ssid = +path[3].slice(2); + break; + } + case /^ep\d+$/i.test(path[3]): { + epid = +path[3].slice(2); + break; + } + } + if (ssid || epid) { + pgcAppSeason(ssid ? { season_id: ssid } : { ep_id: epid }) + .then(({ code, message, data }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, data } }); + epid || (data.user_status.progress?.last_ep_id && (epid = data.user_status.progress?.last_ep_id)); + data.modules.forEach(d => { + switch (d.style) { + case "positive": + case "section": { + epid || (epid = d.data.episodes[0]?.ep_id); + if (epid) { + const ep = d.data.episodes.find(d => d.ep_id === epid); + ep?.aid && (this.$oid = ep.aid); + } + break; + } + } + }); + if (!this.$oid && ssid) { + pgcSection(ssid) + .then(({ code, message, result }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, result } }); + const eps = ([]).concat(...(result.main_section?.episodes || []), ...result.section.map(d => d.episodes)); + const ep = epid ? eps.find(d => d.id === epid) : eps[0]; + ep?.aid && (this.$oid = ep.aid); + }) + .catch(e => { + console.error(e); + }); + } + }) + .catch(e => { + console.error(e); + }); + } + break; + } + case ROUTER.TOVIEW: { + const path = url.hash.split('/'); + let aid = 0; + switch (true) { + case /^av\d+$/i.test(path[1]): { + aid = +path[1].slice(2); + break; + } + case /^bv1[a-z0-9]{9}$/i.test(path[1]): { + aid = +AV.fromBV(path[1]); + break; + } + } + if (aid) { + this.$oid = aid; + } else { + toviewWeb() + .then(({ code, message, data }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, data } }); + this.$oid = data.list[0].aid; + }) + .catch(e => { + console.error(e); + }); + } + break; + } + } + } + + /** 加载弹幕 */ + #load() { + if (this.#hash && this.#oid) { + const hash = this.#hash; + this.#hash = ''; + replyMain(this.#oid, undefined, undefined, hash) + .then(({ code, message, data }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, data } }); + if (data.replies) { + this.#list.replaceChildren(); + data.top_replies?.forEach(d => { + this.#list.appendChild(new Reply(this, d, data.upper.mid)); + }); + data.replies.forEach(d => { + this.#list.appendChild(new Reply(this, d, data.upper.mid)); + }); + this.#head.dataset.total = data.cursor.all_count; + const d = this.#host.querySelector(`[data-rpid="${hash}"]`); + if (d) { + d.scrollIntoView({ behavior: 'smooth', block: 'center' }); + d.style.backgroundImage = 'linear-gradient(45deg, var(--736ce721) 0%, var(--00a1d621) 67%, var(--00d4ff21) 100%)'; + d.style.borderRadius = '1em'; + } + } + }) + .catch(e => { + toastr.error('获取评论出错', e); + console.error(e); + }); + } else { + this.#oid && reply(this.#oid, this.#pn, this.#sort, this.#type) + .then(({ code, message, data }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, data } }); + this.#list.replaceChildren(); + data.top && this.#list.appendChild(new Reply(this, data.top, data.upper.mid)); + data.replies.forEach(d => { + this.#list.appendChild(new Reply(this, d, data.upper.mid)); + }); + this.#head.dataset.total = data.page.count; + this.$interaction(Math.floor(data.page.count / data.page.size)); + }) + .catch(e => { + toastr.error('获取评论出错', e); + console.error(e); + }); + } + } + + private $interaction(pages: number) { + this.#interaction.dataset.num = Math.floor(pages); + if (pages > 1) { + let html = ''; + if (this.#pn > 1) { + html += '1'; + } + + if (this.#pn - 3 > 1) { + html += `...`; + } + if (this.#pn - 2 > 1) { + html += `${this.#pn - 2}`; + } + if (this.#pn - 1 > 1) { + html += `${this.#pn - 1}`; + } + + html += `${this.#pn}`; + + if (this.#pn + 1 > 1 && this.#pn + 1 <= pages) { + html += `${this.#pn + 1}`; + } + if (this.#pn + 2 > 1 && this.#pn + 2 <= pages) { + html += `${this.#pn + 2}`; + } + if (this.#pn + 3 > 1 && this.#pn + 3 <= pages) { + if (this.#pn + 3 < pages) { + html += `...`; + } + + html += `${pages}`; + } + + if (this.#pn < pages) { + html += ''; + } + + this.#interaction.innerHTML = html; + + this.#paging.innerHTML = html + `共${pages}页,跳至`; + this.#paging.querySelector('input')?.addEventListener('change', e => { + const input = e.target; + const i = Number(input.value); + input.value = ''; + i > 1 && (this.$pn = i, this.#order.scrollIntoView({ block: 'center' })); + }); + } else { + this.#interaction.replaceChildren(); + this.#paging.replaceChildren(); + } + } + + private $onPageClick = (e: MouseEvent) => { + const a = e.target; + if (a instanceof HTMLAnchorElement) { + const i = Number(a.dataset.value); + switch (i) { + case -1: { + this.#pn > 1 && this.$pn--; + break; + } + case 0: { + this.$pn++; + break; + } + default: { + i > 0 && (this.$pn = i); + break; + } + } + this.#order.scrollIntoView({ block: 'center' }); + } + } + + #identify = () => { + this.#list.replaceChildren(); + this.#oid = 0; + this.#pn = 1; + this.#sort = 2; + this.#type = 1; + this.#order.innerHTML = ``; + } +} \ No newline at end of file diff --git a/src/main/comment/initComment.ts b/src/main/comment/initComment.ts new file mode 100644 index 0000000..f7154b5 --- /dev/null +++ b/src/main/comment/initComment.ts @@ -0,0 +1,104 @@ +import { Comment } from "."; +import { ProxyHook } from "../../utils/hook/Proxy"; + +export class InitComment { + + constructor() { + ProxyHook.property(self, 'initComment', this.initComment); + ProxyHook.property(self, 'bbComment', class { + constructor( + /** 绑定到的节点的选择符 */ + parent: string, + /** 视频aid或话题topic id */ + oid: Record | number, + /** 评论所在页面类型 1:视频,2:话题 */ + pageType: number, + /** 用户信息 */ + userStatus: object, + /** 需要跳转到的评论的id */ + jumpId: number, + ex: unknown, + ) { + const target = document.querySelector(parent); + if (target) { + if (typeof oid === 'object') { + target.replaceChildren(new Comment(oid.oid, undefined, undefined, oid.pageType, oid.jumpId)); + } else { + target.replaceChildren(new Comment(oid, undefined, undefined, pageType, jumpId)); + } + } + } + }); + ProxyHook.property(self, 'BiliComments', class extends EventTarget { + + $parent?: HTMLElement; + + constructor(private arg: IBiliComments) { + super(); + } + + mount(parent: HTMLElement) { + this.$parent = parent; + const [type, oid] = this.arg.params.split(","); + parent.replaceChildren(new Comment(oid, undefined, undefined, +type, this.arg.seekId)); + setTimeout(() => { + this.dispatchEvent(new Event('inited')); + this.dispatchEvent(new Event('expand')); + // this.dispatchEvent(new Event('seek')); + }); + return this; + } + + dispatchAction({ type, args, callback }: IDispatchAction) { + switch (type) { + case 'reload': { + const [type, oid] = args[0].params.split(","); + this.$parent?.replaceChildren(new Comment(oid, undefined, undefined, +type, this.arg.seekId)); + callback?.(); + break; + } + } + } + }) + } + + /** 拦截新版评论区 */ + initComment = (container: string, { oid, pageType, jumpReplyId }: IInitComment) => { + const target = document.querySelector(container); + if (target) { + target.replaceWith(new Comment(oid, undefined, undefined, pageType, jumpReplyId)); + } + return this; + } + + /** 新版评论区兼容 */ + registerEvent() { } + + /** 新版评论区兼容 */ + on() { } + + /** 新版评论区兼容 */ + Off() { } +} + +interface IInitComment { + allowForwardToDynamic?: boolean; + commentType?: string; + jumpReplyId?: number; + oid: string | number; + pageType: number; + scene?: string; + showMaxReplyCount?: number; + theme?: string; +} + +interface IBiliComments { + params: string; + seekId?: number; +} + +interface IDispatchAction { + type: 'reload'; + callback?: () => void; + args: { params: string }[]; +} \ No newline at end of file diff --git a/src/main/bilibili/comment/style/comment.css b/src/main/comment/reply.css similarity index 51% rename from src/main/bilibili/comment/style/comment.css rename to src/main/comment/reply.css index 9494d9e..31d1de5 100644 --- a/src/main/bilibili/comment/style/comment.css +++ b/src/main/comment/reply.css @@ -1,91 +1,84 @@ -@import url(./sub-reply.css); +@font-face { + font-family: 'fanscard'; + src: url('//s1.hdslb.com/bfs/static/jinkela/mall-h5/asserts/fansCard.ttf'); +} -.comment-wrap { +.reply-item { display: flex; + column-gap: .5em; - .user-face { + >:first-child { flex-shrink: 0; - margin-block-start: 24px; - margin-inline: 8px; - position: relative; - &:is(a) { + >.avatar { display: flex; justify-content: center; align-items: center; - } + position: relative; - >.face { - inline-size: 48px; - aspect-ratio: 1; - border-radius: 50%; - } + --size: 48px; - >.pendant { - position: absolute; - block-size: 72px; - inline-size: 72px; - } + >.avatar-face { + block-size: var(--size); + inline-size: var(--size); + margin: calc(var(--size) / 3); + border-radius: 50%; + } - >.auth { - position: absolute; - inline-size: 20px; - block-size: 20px; - inset-inline-end: 0; - inset-block-end: 0; - background-image: url(//s1.hdslb.com/bfs/static/jinkela/videoplay/asserts/user-auth.png); - background-repeat: no-repeat; - z-index: 3; - - &.o-auth { - background-position: -4px -53px; + >.avatar-pendant { + position: absolute; + block-size: calc(var(--size) / 2* 3); + inline-size: calc(var(--size) / 2* 3); } - &.p-auth { - background-position: -38px -53px; + >.avatar-icon { + position: absolute; + block-size: calc(var(--size) * 3 / 8); + inline-size: calc(var(--size) * 3 / 8); + inset-inline-end: calc(var(--size) / 3); + inset-block-end: calc(var(--size) / 3); } } - } - .con { - flex-grow: 1; + >.reply-con { + flex: 1; + min-inline-size: 0; padding-block-start: 22px; padding-block-end: 14px; - border-block-start: 1px solid #e5e9ef; + border-block-start: 1px solid var(--e5e9ef); + box-sizing: border-box; display: flex; flex-direction: column; - overflow: hidden; + row-gap: .5em; - .user { - min-block-size: 40px; + >.user { font-weight: bold; display: flex; align-items: center; - position: relative; column-gap: 8px; + position: relative; .name { - color: #6d757a; + color: var(--6d757a); text-wrap: nowrap; } >img { - font-size: 12px; block-size: 1em; } - .is-up { + >.is-up { font-size: 9px; border-radius: 1px; - background-color: #fb7299; - color: #fff; - border-color: #fb7299; + background-color: var(--fb7299); + color: var(--fff); + border-color: var(--fb7299); padding-inline: 2px; font-weight: normal; } - .fans { + >.fans { font-weight: normal; font-size: 10px; display: inline-flex; @@ -106,14 +99,12 @@ } } - .nameplate>img { + >.nameplate { font-size: 24px; - block-size: 1em; - vertical-align: text-bottom; } - .sailing { - display: inline-flex; + >.sailing { + display: flex; align-items: center; position: absolute; inset-inline-end: 0; @@ -126,111 +117,128 @@ position: absolute; inset-inline-end: 0; font-family: fanscard; - scale: .7; - transform-origin: left center; } } } - .text { + >.text { padding-block: 2px; font-size: 14px; - overflow: hidden; + text-shadow: none; + overflow: clip; word-wrap: break-word; word-break: break-word; white-space: pre-wrap; - .stick { + >.eif { + block-size: 2em; + } + + >.small { + block-size: 1em; + } + + >.image { + display: block; + max-inline-size: 100%; + margin-block-start: 8px; + border-radius: 4px; + cursor: zoom-in; + } + + >.stick { display: inline-block; text-align: center; font-size: 12px; - color: #ff81aa; - font-weight: normal; - border: 1px solid #ff81aa; + color: var(--ff81aa); + border: 1px solid var(--ff81aa); border-radius: 3px; margin-inline-end: 5px; } >a { - color: #00a1d6; - } - - >img { - max-inline-size: 100%; - - &.emote { - inline-size: 20px; - aspect-ratio: 1; - vertical-align: text-bottom; - } - - &.topopover { - display: block; - cursor: zoom-in; - } + color: var(--00a1d6); } } - .info { - color: #99a2aa; - margin-block-start: 6px; - font-size: 12px; + >.info { display: flex; align-items: center; + color: var(--99a2aa); column-gap: 20px; - >span { display: flex; align-items: center; - - >svg { - inline-size: 12px; - aspect-ratio: 1; - fill: #99a2aa; - margin-inline-end: 5px; + column-gap: .5em; + + >i { + block-size: 14px; + inline-size: 14px; + display: inline-block; + background-image: url(//s1.hdslb.com/bfs/seed/jinkela/commentpc/static/img/icons-comment.2f36fc5.png); + background-repeat: no-repeat; } - &.reply { - padding: 5px; - border-radius: 4px; + &.like-num { cursor: pointer; - &:hover { - color: #00a1d6; - background: #e5e9ef; + >i { + pointer-events: none; + background-position: -153px -25px; + } + + &[data-num]::after { + content: attr(data-num); + } + + &:hover>i, + &.liked>i { + background-position: -218px -25px; } } - &.like-num { + &.hate-num { cursor: pointer; - >svg { + >i { pointer-events: none; + background-position: -153px -153px; } - &:hover>svg { - fill: #00a1d6; + &[data-num]::after { + content: attr(data-num); } - &.liked>svg { - fill: #00a1d6; + &:hover>i, + &.hated>i { + background-position: -217px -153px; } } - &:empty { - display: none; + &.reply { + padding: 5px; + border-radius: 4px; + cursor: pointer; + + &:hover { + color: var(--00a1d6); + background: var(--e5e9ef); + } } } } - .reply-box { - .view-more { - color: #6d757a; + >.reply-box { + box-sizing: border-box; + padding-inline: 24px; + + >.view-more { + color: var(--6d757a); pointer-events: none; >a { - color: #00a1d6; + color: var(--00a1d6); cursor: pointer; padding-block: 2px; padding-inline: 3px; @@ -238,13 +246,13 @@ pointer-events: initial; &:hover { - background-color: #e5e9ef; - color: #00a1d6; + background-color: var(--e5e9ef); + color: var(--00a1d6); } } } - .interaction { + >.interaction { display: flex; align-items: center; @@ -258,7 +266,7 @@ cursor: pointer; &:hover { - color: #00a1d6; + color: var(--00a1d6); } &.on, @@ -267,37 +275,11 @@ } &.on { - color: #00a1d6; + color: var(--00a1d6); font-weight: bold; } } } - - &[popover] { - inset-inline-start: 50%; - inset-block-start: 50%; - translate: -50% -50%; - border: 0; - box-shadow: 0 0 20px 3px #0000005c; - border-radius: 10px; - padding-inline: 18px; - min-block-size: 200px; - max-block-size: 70vh; - overflow-y: auto; - scrollbar-width: thin; - - .dialog { - display: none; - } - - &::after { - content: attr(data-loading); - color: #99a2aa; - display: block; - text-align: center; - margin-block-end: 30px; - } - } } } } \ No newline at end of file diff --git a/src/main/comment/reply.ts b/src/main/comment/reply.ts new file mode 100644 index 0000000..c2af5ef --- /dev/null +++ b/src/main/comment/reply.ts @@ -0,0 +1,232 @@ +import { Comment } from "."; +import { IReplies } from "../../io/com/bilibili/api/x/v2/reply"; +import { replyReply } from "../../io/com/bilibili/api/x/v2/reply/reply"; +import { toastr } from "../../toastr"; +import { customElement } from "../../utils/Decorator/customElement"; +import { Element } from "../../utils/element"; +import { Format } from "../../utils/fomat"; +import { https } from "../../utils/https"; +import { Avatar } from "./avatar"; +import { LEVEL } from "./icon-level"; +import { SubReply } from "./sub-reply"; + +/** 评论 */ +@customElement('div') +export class Reply extends HTMLDivElement { + + /** + * 需要监听变动的属性。 + * 与实例方法`attributeChangedCallback`配合使用。 + * 此字符串序列定义了`attributeChangedCallback`回调时的第一个参数的可能值。 + */ + // static observedAttributes = []; + + /** + * 在属性更改、添加、移除或替换时调用。 + * 需要与静态属性`observedAttributes`配合使用。 + * 此回调的第一个参数在`observedAttributes`数组中定义。 + */ + // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} + + /** 每当元素添加到文档中时调用。 */ + // connectedCallback() {} + + /** 每当元素从文档中移除时调用。 */ + // disconnectedCallback() {} + + /** 每当元素被移动到新文档中时调用。 */ + // adoptedCallback() {} + + #left = Element.add('div', { appendTo: this }); + + #right = Element.add('div', { appendTo: this, class: 'reply-con' }); + + #avatar = Element.add('a', { appendTo: this.#left, class: 'avatar', attribute: { target: '_blank' } }); + + #user = Element.add('div', { appendTo: this.#right, class: 'user' }); + + #text = Element.add('div', { appendTo: this.#right, class: 'text' }); + + #info = Element.add('div', { appendTo: this.#right, class: 'info' }); + + $replyBox = Element.add('div', { appendTo: this.#right, class: 'reply-box' }); + + constructor( + private cnt: Comment, + private reply: IReplies, + private upid: number, + ) { + + super(); + + this.classList.add('reply-item'); + this.dataset.rpid = reply.rpid_str; + + this.emote(); + this.jump_url(); + this.pictures(); + + this.#avatar.innerHTML = https(`${reply.member.pendant.image ? `` : ''}${reply.member.official_verify.type >= 0 || reply.member.vip.vipStatus ? `` : ''}`); + this.#avatar.href = `//space.bilibili.com/${reply.member.mid}`; + this.#user.innerHTML = `${reply.member.uname} + +${upid === reply.mid ? 'UP' : ''} +${reply.member.fans_detail ? `${reply.member.fans_detail.medal_name}${reply.member.fans_detail.level}` : ''} +${reply.member.nameplate?.image ? `` : ''} +${reply.member.user_sailing?.cardbg ? `
    ${reply.member.user_sailing.cardbg.fan.num_prefix}
    ${reply.member.user_sailing.cardbg.fan.num_desc}
    ` : ''}`; + this.#text.innerHTML = `${reply.reply_control.is_up_top ? '置顶' : ''}${reply.content.message}`; + this.#info.innerHTML = `${reply.floor ? `#${reply.floor}` : ''} +${Format.eTime(reply.ctime)} +${reply.reply_control.location ? `${reply.reply_control.location}` : ''} + + +${reply.up_action?.like ? 'UP主觉得很赞' : ''} +${reply.up_action?.reply ? 'UP主有回复' : ''} +${reply.label?.content ? `${reply.label.content}` : ''} +回复`; + reply.replies?.forEach(d => { + this.$replyBox.appendChild(new SubReply(cnt, d, upid)); + }); + if (reply.replies.length < reply.rcount) { + Element.add('div', { class: 'view-more', appendTo: this.$replyBox, innerHTML: `${reply.reply_control.sub_reply_entry_text},点击查看` }) + .addEventListener('click', () => { + this.replyReply(reply.rpid_str, reply.oid_str); + }, { once: true }); + } + + this.addEventListener('click', ({ target }) => { + if (target instanceof HTMLSpanElement && target.classList.contains('reply')) { + cnt.$replyCurrent = this; + if (!this.$replyBox.contains(target)) { + cnt.$root = +reply.root_str ? reply.root_str : reply.rpid_str; + cnt.$parent = reply.rpid_str; + cnt.$uname = reply.member.uname; + } + } + }); + } + + /** 处理表情 */ + private emote() { + if (this.reply.content.emote) { + Object.entries(this.reply.content.emote).forEach(d => { + this.reply.content.message = this.reply.content.message.replaceAll(d[0], `${d[1].text}`) + }); + } + } + + /** 处理超链接 */ + private jump_url() { + this.at_name_to_mid_str(); + this.reply.content.message = Format.superLink(this.reply.content.message); + } + + /** 处理@ */ + private at_name_to_mid_str() { + if (this.reply.content.at_name_to_mid_str) { + Object.entries(this.reply.content.at_name_to_mid_str).forEach(d => { + this.reply.content.message = this.reply.content.message.replaceAll(`@${d[0]}`, `@${d[0]}`) + }) + } + } + + /** 处理图片 */ + private pictures() { + this.reply.content.pictures?.forEach(d => { + this.reply.content.message += ``; + }); + this.reply.content.rich_text?.note?.images?.forEach(d => { + this.reply.content.message += ``; + }); + } + + private replyReply( + root: number | string, + oid: number | string, + pn = 1, + type = this.cnt.$type, + ) { + replyReply(root, oid, pn, type) + .then(({ code, message, data }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, data } }); + if (data.replies?.length) { + this.$replyBox.replaceChildren(...data.replies.map(t => new SubReply(this.cnt, t, this.upid))); + this.$interaction(Math.floor(data.page.count / data.page.size), data.page.num, root, oid); + } else { + toastr.warn('未获取到更多评论数据~'); + } + }) + .catch(e => { + toastr.error('获取楼中楼评论失败', e); + console.error(e); + }) + } + + private $interaction( + pages: number, + pn: number, + root: number | string, + oid: number | string, + ) { + if (pages > 1) { + const $interaction = Element.add('div', { class: 'interaction', appendTo: this.$replyBox }); + $interaction.dataset.num = Math.floor(pages); + let html = ''; + if (pn > 1) { + html += '1'; + } + + if (pn - 3 > 1) { + html += `...`; + } + if (pn - 2 > 1) { + html += `${pn - 2}`; + } + if (pn - 1 > 1) { + html += `${pn - 1}`; + } + + html += `${pn}`; + + if (pn + 1 > 1 && pn + 1 <= pages) { + html += `${pn + 1}`; + } + if (pn + 2 > 1 && pn + 2 <= pages) { + html += `${pn + 2}`; + } + if (pn + 3 > 1 && pn + 3 <= pages) { + if (pn + 3 < pages) { + html += `...`; + } + + html += `${pages}`; + } + + if (pn < pages) { + html += ''; + } + + $interaction.innerHTML = html; + $interaction.addEventListener('click', e => { + const a = e.target; + if (a instanceof HTMLAnchorElement) { + const i = Number(a.dataset.value); + switch (i) { + case -1: { + pn > 1 && this.replyReply(root, oid, pn - 1); + break; + } + case 0: { + this.replyReply(root, oid, pn + 1); + break; + } + default: { + i > 0 && this.replyReply(root, oid, i); + break; + } + } + } + }); + } + } +} \ No newline at end of file diff --git a/src/main/comment/send.css b/src/main/comment/send.css new file mode 100644 index 0000000..3a9f700 --- /dev/null +++ b/src/main/comment/send.css @@ -0,0 +1,244 @@ +.comment-send { + margin-block: 10px; + padding-block-start: 15px; + display: flex; + column-gap: .5em; + + .avatar { + display: flex; + justify-content: center; + align-items: center; + position: relative; + + --size: 48px; + + >.avatar-face { + block-size: var(--size); + inline-size: var(--size); + margin: calc(var(--size) / 3); + border-radius: 50%; + } + + >.avatar-pendant { + position: absolute; + block-size: calc(var(--size) / 2* 3); + inline-size: calc(var(--size) / 2* 3); + } + + >.avatar-icon { + position: absolute; + block-size: calc(var(--size) * 3 / 8); + inline-size: calc(var(--size) * 3 / 8); + inset-inline-end: calc(var(--size) / 3); + inset-block-end: calc(var(--size) / 3); + } + } + + >.send-right { + display: flex; + flex-wrap: wrap; + row-gap: .5em; + position: relative; + + >form { + inline-size: 100%; + display: flex; + column-gap: 1em; + + >textarea { + block-size: 65px; + border: 1px solid var(--e5e9ef); + border-radius: 4px; + outline: none; + padding-block: 5px; + padding-inline: 10px; + resize: none; + box-sizing: border-box; + font-size: 12px; + background-color: var(--f4f5f7); + color: var(--555); + overflow: auto; + scrollbar-width: thin; + font-family: Helvetica Neue, Helvetica, Arial, Microsoft Yahei, Hiragino Sans GB, Heiti SC, WenQuanYi Micro Hei, sans-serif; + + &:focus { + background-color: var(--fff); + border-color: var(--00a1d6); + } + + &:disabled { + background-color: var(--e5e9ef); + cursor: not-allowed; + } + } + + >button { + user-select: none; + outline: none; + display: flex; + align-items: center; + justify-content: center; + padding-block: 0; + padding-inline: 15px; + inline-size: 70px; + box-sizing: border-box; + border: 1px solid var(--00a1d6); + border-radius: 4px; + font-size: 14px; + color: var(--fff); + cursor: pointer; + background-color: var(--00a1d6); + transition: 0.1s; + + &:hover { + background-color: var(--00b5e5); + border-color: var(--00b5e5) + } + + &:disabled { + background-color: var(--e5e9ef); + border-color: var(--e5e9ef); + color: var(--b8c0cc); + cursor: not-allowed; + } + } + + &:hover>textarea:not(:disabled) { + background-color: var(--fff); + border-color: var(--00a1d6); + } + + + } + + >.emoji { + display: flex; + color: var(--99a2aa); + column-gap: 5px; + inline-size: 62px; + box-sizing: border-box; + padding-block: 3px; + padding-inline: 0; + border: 1px solid var(--e5e9ef); + border-radius: 4px; + justify-content: center; + cursor: pointer; + box-shadow: 0px 1px 10px 0 var(--6a738540); + background-color: transparent; + anchor-name: --emoji; + + >i { + background: url(//s1.hdslb.com/bfs/seed/jinkela/commentpc/static/img/icons-comment.2f36fc5.png) no-repeat -408px -24px; + inline-size: 16px; + block-size: 16px; + } + + &:hover { + color: var(--6d757a); + } + } + + >.emoji-wrap { + position-anchor: --emoji; + position-area: block-end span-inline-end; + inline-size: 363px; + padding: 0; + margin-block-start: .5em; + margin-block-end: 0; + margin-inline: 0; + border: 1px solid var(--e5e9ef); + box-shadow: 0 11px 12px 0 var(--6a73854d); + border-radius: 8px; + color: var(--222); + background: var(--fff); + font-family: "Microsoft YaHei", Arial, Helvetica, sans-serif; + --columns: 10; + + >.emoji-title { + margin-block-start: 13px; + margin-inline: 15px; + color: var(--757575); + } + + >.emoji-box { + margin-block-start: 6px; + margin-inline: 11px; + max-block-size: 196px; + overflow-y: auto; + scrollbar-width: thin; + overflow-x: clip; + box-sizing: border-box; + display: grid; + grid-template-columns: repeat(var(--columns), 1fr); + gap: 2px; + + >label { + border-radius: 4px; + cursor: pointer; + + >input { + appearance: none; + display: none; + } + + >img { + inline-size: 100%; + } + + &:hover { + background-color: var(--ddd); + } + } + } + + >.emoji-tab { + background-color: var(--f4f4f4); + border-end-start-radius: 4px; + border-end-end-radius: 4px; + display: flex; + overflow-x: auto; + scrollbar-width: thin; + + >label { + padding-block: 7px; + padding-inline: 18px; + block-size: 36px; + box-sizing: border-box; + cursor: pointer; + + >input { + appearance: none; + display: none; + } + + >img { + block-size: 100%; + } + + &:has(input:checked) { + background-color: var(--fff); + pointer-events: none; + } + } + } + + &:has(.emoji-box:empty) { + text-align: center; + + &::before { + content: "加载中(-_-#)"; + } + } + } + } + + &[popover] { + position-area: block-end; + margin: 0; + border: 0; + box-shadow: 0 0 20px 3px var(--0000005c); + border-radius: 10px; + padding-inline: 18px; + padding-block-end: 40px; + } +} \ No newline at end of file diff --git a/src/main/comment/send.ts b/src/main/comment/send.ts new file mode 100644 index 0000000..89598cf --- /dev/null +++ b/src/main/comment/send.ts @@ -0,0 +1,158 @@ +import { Comment } from "."; +import { emoteWeb } from "../../io/com/bilibili/api/x/emote/user/panel/web"; +import { nav } from "../../io/com/bilibili/api/x/web-interface/nav"; +import { toastr } from "../../toastr"; +import { customElement } from "../../utils/Decorator/customElement"; +import { Element } from "../../utils/element"; +import { https } from "../../utils/https"; +import { Avatar } from "./avatar"; + +/** 评论区 */ +@customElement('div') +export class CommentSend extends HTMLDivElement { + + /** + * 需要监听变动的属性。 + * 与实例方法`attributeChangedCallback`配合使用。 + * 此字符串序列定义了`attributeChangedCallback`回调时的第一个参数的可能值。 + */ + // static observedAttributes = []; + + /** + * 在属性更改、添加、移除或替换时调用。 + * 需要与静态属性`observedAttributes`配合使用。 + * 此回调的第一个参数在`observedAttributes`数组中定义。 + */ + // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} + + /** 每当元素添加到文档中时调用。 */ + // connectedCallback() {} + + /** 每当元素从文档中移除时调用。 */ + // disconnectedCallback() {} + + /** 每当元素被移动到新文档中时调用。 */ + // adoptedCallback() {} + + #left = Element.add('div', { appendTo: this }); + + #avatar = Element.add('div', { appendTo: this.#left, class: 'avatar' }); + + #face = Element.add('img', { appendTo: this.#avatar, class: 'avatar-face', attribute: { loading: 'lazy', src: '//static.hdslb.com/images/akari.jpg' } }); + + #right = Element.add('div', { appendTo: this, class: 'send-right' }); + + $submit = Element.add('form', { appendTo: this.#right }); + + #textArea = Element.add('textarea', { appendTo: this.$submit, attribute: { name: 'msg', cols: '80', rows: '5', placeholder: '发一条友善的评论' } }); + + #button = Element.add('button', { appendTo: this.$submit, innerText: '发表评论' }); + + #emoji = Element.add('button', { appendTo: this.#right, class: 'emoji', innerHTML: `表情` }); + + #emojiWrap = Element.add('div', { appendTo: this.#right, class: 'emoji-wrap' }); + + #emojiTitle = Element.add('div', { appendTo: this.#emojiWrap, class: 'emoji-title' }); + + #emojiBox = Element.add('form', { appendTo: this.#emojiWrap, class: 'emoji-box' }); + + #emojiTab = Element.add('form', { appendTo: this.#emojiWrap, class: 'emoji-tab' }); + + #id = crypto.randomUUID(); + + #emote?: Awaited>['data'] + + constructor( + cnt: Comment, + ) { + super(); + + this.classList.add('comment-send'); + this.#textArea.required = true; + this.#textArea.disabled = true; + this.#button.disabled = true; + this.#emojiWrap.popover = 'auto'; + this.#emoji.popoverTargetElement = this.#emojiWrap; + + this.$submit.addEventListener('submit', e => { + e.preventDefault(); + }); + + this.#emoji.addEventListener('click', () => { + this.#textArea.focus(); + }); + + this.#emojiTab.addEventListener('change', () => { + this.#textArea.focus(); + const d = new FormData(this.#emojiTab); + const i = +[...d.values()][0]; + if (i >= 0 && this.#emote) { + const emote = this.#emote.packages[i]; + emote.type === 1 ? emote.meta.size === 1 ? this.#emojiWrap.style.removeProperty('--columns') : this.#emojiWrap.style.setProperty('--columns', '5') : this.#emojiWrap.style.setProperty('--columns', '4'); + this.#emojiTitle.textContent = emote.text; + const id = crypto.randomUUID(); + this.#emojiBox.innerHTML = https(emote.emote.map(d => ``).join('')); + } + }); + this.#emojiBox.addEventListener('change', () => { + this.#textArea.focus(); + const d = new FormData(this.#emojiBox); + this.#textArea.setRangeText([...d.values()][0], this.#textArea.selectionStart, this.#textArea.selectionEnd, 'end'); + }); + this.#emojiWrap.addEventListener('toggle', () => { + emoteWeb() + .then(({ code, message, data }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, data } }); + this.#emote = data; + this.#emojiTab.innerHTML = https(data.packages.map((d, i) => ``).join('')); + (this.#emojiTab.firstElementChild)?.click(); + }) + .catch(e => { + toastr.error('获取表情失败~', e); + console.error(e); + }); + }, { once: true }); + this.addEventListener('toggle', () => { + if (!this.matches(":popover-open")) { + this.popover = null; + this.style.removeProperty('position-anchor'); + this.#textArea.placeholder = `发一条友善的评论`; + cnt.$root = cnt.$parent = cnt.$uname = ''; + delete cnt.$replyCurrent; + } else { + this.#textArea.placeholder = `回复 @${cnt.$uname} :`; + } + }); + + nav() + .then(({ code, message, data }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, data } }); + + if (data.isLogin) { + this.#textArea.disabled = false; + this.#button.disabled = false; + this.#face.src = data.face; + data.pendant.image && this.$setPendant(data.pendant.image); + (data.officialVerify.type > 0 || data.vip.status) && this.$setIcon(Avatar[data.officialVerify.type > 0 ? data.officialVerify.type : 2]); + } + }) + .catch(e => { + console.error(e); + }); + } + + /** 头像框 */ + $setPendant(src: string) { + this.#avatar.insertAdjacentHTML('beforeend', ``) + } + + /** 头像图标 */ + $setIcon(type: string) { + this.#avatar.insertAdjacentHTML('beforeend', ``); + } + + /** 清空输入框 */ + $empty() { + this.#textArea.value = ''; + } +} \ No newline at end of file diff --git a/src/main/comment/sub-reply.css b/src/main/comment/sub-reply.css new file mode 100644 index 0000000..060ddab --- /dev/null +++ b/src/main/comment/sub-reply.css @@ -0,0 +1,213 @@ +.sub-reply { + padding-block: 10px; + display: flex; + column-gap: .5em; + + >:first-child { + flex-shrink: 0; + + >.avatar-sub { + display: flex; + justify-content: center; + align-items: center; + position: relative; + + --size: 24px; + + >.avatar-face { + block-size: var(--size); + inline-size: var(--size); + margin: calc(var(--size) / 3); + border-radius: 50%; + } + + >.avatar-pendant { + position: absolute; + block-size: calc(var(--size) / 2* 3); + inline-size: calc(var(--size) / 2* 3); + } + + >.avatar-icon { + position: absolute; + block-size: calc(var(--size) * 3 / 8); + inline-size: calc(var(--size) * 3 / 8); + inset-inline-end: calc(var(--size) / 3); + inset-block-end: calc(var(--size) / 3); + } + } + } + + >.sub-con { + flex: 1; + min-inline-size: 0; + display: flex; + flex-direction: column; + row-gap: .5em; + + >.text { + >* { + vertical-align: middle; + } + + >:not(:last-child) { + margin-inline-end: 8px; + } + + .name { + color: var(--6d757a); + text-wrap: nowrap; + } + + >img { + block-size: 1em; + } + + >.is-up { + font-size: 9px; + border-radius: 1px; + background-color: var(--fb7299); + color: var(--fff); + border-color: var(--fb7299); + padding-inline: 2px; + font-weight: normal; + } + + >.fans { + font-weight: normal; + font-size: 10px; + display: inline-flex; + align-items: center; + + .fans-name { + padding-inline: 2px; + border-width: 0.5px; + border-start-start-radius: 1px; + border-end-start-radius: 1px; + } + + .fans-level { + border-width: 0.5px; + border-style: solid; + border-end-end-radius: 1px; + border-start-end-radius: 1px; + } + } + + >.nameplate { + font-size: 24px; + } + + >.content { + white-space: pre-wrap; + font-size: 14px; + + >img { + block-size: 1em; + } + + >.eif { + block-size: 2em; + } + + >a { + color: var(--00a1d6); + } + } + } + + >.info { + display: flex; + align-items: center; + color: var(--99a2aa); + column-gap: 20px; + + >span { + display: flex; + align-items: center; + column-gap: .5em; + + >i { + block-size: 14px; + inline-size: 14px; + display: inline-block; + background-image: url(//s1.hdslb.com/bfs/seed/jinkela/commentpc/static/img/icons-comment.2f36fc5.png); + background-repeat: no-repeat; + } + + &.like-num { + cursor: pointer; + + >i { + pointer-events: none; + background-position: -153px -25px; + } + + &[data-num]::after { + content: attr(data-num); + } + + &:hover>i, + &.liked>i { + background-position: -218px -25px; + } + } + + &.hate-num { + cursor: pointer; + + >i { + pointer-events: none; + background-position: -153px -153px; + } + + &[data-num]::after { + content: attr(data-num); + } + + &:hover>i, + &.hated>i { + background-position: -217px -153px; + } + } + + &.reply, + &.dialog { + padding: 5px; + border-radius: 4px; + cursor: pointer; + + &:hover { + color: var(--00a1d6); + background: var(--e5e9ef); + } + } + } + } + } +} + +.dialog-box[popover] { + position-area: block-end; + max-inline-size: 25vi; + max-block-size: 25vh; + margin: 0; + border: 0; + box-shadow: 0 0 20px 3px var(--0000005c); + border-radius: 10px; + padding-inline: 18px; + overflow-y: auto; + scrollbar-width: thin; + + .dialog, + .reply { + display: none !important; + } + + &::after { + content: attr(data-loading); + color: var(--99a2aa); + display: block; + text-align: center; + margin-block-end: 30px; + } +} \ No newline at end of file diff --git a/src/main/comment/sub-reply.ts b/src/main/comment/sub-reply.ts new file mode 100644 index 0000000..f17a2e9 --- /dev/null +++ b/src/main/comment/sub-reply.ts @@ -0,0 +1,185 @@ +import { Comment } from "."; +import { IReplies } from "../../io/com/bilibili/api/x/v2/reply"; +import { cursor } from "../../io/com/bilibili/api/x/v2/reply/dialog/cursor"; +import { toastr } from "../../toastr"; +import { customElement } from "../../utils/Decorator/customElement"; +import { Element } from "../../utils/element"; +import { Format } from "../../utils/fomat"; +import { https } from "../../utils/https"; +import { Avatar } from "./avatar"; +import { LEVEL } from "./icon-level"; + +/** 评论楼中楼 */ +@customElement('div') +export class SubReply extends HTMLDivElement { + + /** + * 需要监听变动的属性。 + * 与实例方法`attributeChangedCallback`配合使用。 + * 此字符串序列定义了`attributeChangedCallback`回调时的第一个参数的可能值。 + */ + // static observedAttributes = []; + + /** + * 在属性更改、添加、移除或替换时调用。 + * 需要与静态属性`observedAttributes`配合使用。 + * 此回调的第一个参数在`observedAttributes`数组中定义。 + */ + // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} + + /** 每当元素添加到文档中时调用。 */ + // connectedCallback() {} + + /** 每当元素从文档中移除时调用。 */ + // disconnectedCallback() {} + + /** 每当元素被移动到新文档中时调用。 */ + // adoptedCallback() {} + + #left = Element.add('div', { appendTo: this }); + + #right = Element.add('div', { appendTo: this, class: 'sub-con' }); + + #avatar = Element.add('a', { appendTo: this.#left, class: 'avatar-sub', attribute: { target: '_blank' } }); + + #text = Element.add('div', { appendTo: this.#right, class: 'text' }); + + #info = Element.add('div', { appendTo: this.#right, class: 'info' }); + + constructor( + private cnt: Comment, + private reply: IReplies, + private upid: number, + ) { + + super(); + + this.classList.add('sub-reply'); + this.dataset.rpid = reply.rpid_str; + + this.emote(); + this.jump_url(); + + this.#avatar.innerHTML = https(`${reply.member.pendant.image ? `` : ''}${reply.member.official_verify.type >= 0 || reply.member.vip.vipStatus ? `` : ''}`); + this.#text.innerHTML = `${reply.member.uname} + +${upid === reply.mid ? 'UP' : ''} +${reply.member.fans_detail ? `${reply.member.fans_detail.medal_name}${reply.member.fans_detail.level}` : ''} +${reply.member.nameplate?.image ? `` : ''} +${reply.content.message}`; + this.#info.innerHTML = `${reply.floor ? `#${reply.floor}` : ''} +${Format.eTime(reply.ctime)} +${reply.reply_control.location ? `${reply.reply_control.location}` : ''} + + +${reply.up_action?.like ? 'UP主觉得很赞' : ''} +${reply.up_action?.reply ? 'UP主有回复' : ''} +${reply.label?.content ? `${reply.label.content}` : ''} +回复 +${reply.dialog_str && reply.dialog_str !== reply.rpid_str ? `查看对话` : ''}`; + + this.addEventListener('click', ({ target }) => { + if (target instanceof HTMLSpanElement && target.classList.contains('reply')) { + cnt.$root = +reply.root_str ? reply.root_str : reply.rpid_str; + cnt.$parent = reply.rpid_str; + cnt.$uname = reply.member.uname; + } + }); + this.addEventListener('click', e => { + const { target } = e; + if (target instanceof HTMLSpanElement) { + if (target.classList.contains('dialog')) { + // 处理查看对话 + const { oid, dialog, root, rpid } = target.dataset; + oid && dialog && root && cursor(oid, root, dialog) + .then(({ code, message, data }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, data } }); + if (data.replies.length) { + const popover = Element.add('div', { class: "dialog-box", data: { loading: '加载更多……' } }); + const id = crypto.randomUUID(); + target.style.setProperty('anchor-name', `--${id}`); + popover.style.setProperty('position-anchor', `--${id}`); + popover.popover = 'auto'; + data.replies.forEach(d => { + const sr = new SubReply(this.cnt, d, this.upid); + popover.appendChild(sr); + if (d.rpid_str === rpid) { + sr.style.backgroundImage = 'linear-gradient(45deg, var(--736ce721) 0%, var(--00a1d621) 67%, var(--00d4ff21) 100%)'; + sr.style.borderRadius = '1em'; + } + }); + target.insertAdjacentElement('afterend', popover); + popover.addEventListener('toggle', () => { + popover.matches(":popover-open") || popover.remove(); + }); + popover.showPopover(); + if (popover.scrollHeight - popover.scrollTop < popover.clientHeight * 1.2) { + popover.dataset.loading = ''; + } else { + let { next } = data.cursor; const { max_floor } = data.dialog; + const scrollend = () => { + if (popover.scrollHeight - popover.scrollTop < popover.clientHeight * 1.2) { + if (next < max_floor) { + if (data.replies.length) { + cursor(oid, root, dialog, next).then(({ code, message, data }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, data } }); + data.replies.forEach(d => { + const sr = new SubReply(this.cnt, d, this.upid); + popover.appendChild(sr); + if (d.rpid_str === rpid) { + sr.style.backgroundImage = 'linear-gradient(45deg, var(--736ce721) 0%, var(--00a1d621) 67%, var(--00d4ff21) 100%)'; + sr.style.borderRadius = '1em'; + } + }); + next = data.cursor.next; + }).catch(e => { + toastr.error('获取更多对话失败', e); + console.error(e); + }); + } else { + popover.dataset.loading = '一滴也没有啦~'; + popover.removeEventListener('scrollend', scrollend); + } + } else { + popover.dataset.loading = '一滴也没有啦~'; + popover.removeEventListener('scrollend', scrollend); + } + } + } + popover.addEventListener('scrollend', scrollend); + } + } + }).catch(e => { + toastr.error('获取对话失败', e); + console.error(e); + }); + e.stopPropagation(); + } + } + }); + } + + /** 处理表情 */ + private emote() { + if (this.reply.content.emote) { + Object.entries(this.reply.content.emote).forEach(d => { + this.reply.content.message = this.reply.content.message.replaceAll(d[0], `${d[1].text}`) + }); + } + } + + /** 处理超链接 */ + private jump_url() { + this.at_name_to_mid_str(); + this.reply.content.message = Format.superLink(this.reply.content.message); + } + + /** 处理@ */ + private at_name_to_mid_str() { + if (this.reply.content.at_name_to_mid_str) { + Object.entries(this.reply.content.at_name_to_mid_str).forEach(d => { + this.reply.content.message = this.reply.content.message.replaceAll(`@${d[0]}`, `@${d[0]}`) + }) + } + } +} \ No newline at end of file diff --git a/src/main/desc/index.css b/src/main/desc/index.css new file mode 100644 index 0000000..f899bbf --- /dev/null +++ b/src/main/desc/index.css @@ -0,0 +1,137 @@ +@scope { + :scope { + color-scheme: light dark; + + --e5e9ef: #e5e9ef; + --6d757a: #6d757a; + --00a1d6: #00a1d6; + --222: #222; + } + + a { + text-decoration: none; + outline: none; + } +} + +.container { + inline-size: 980px; + margin-inline: auto; + box-sizing: border-box; + font-size: 12px; + display: flex; + + @media screen and (min-width:1400px) { + + & { + inline-size: 1160px; + } + } + + @media screen and (min-width:2500px) { + + & { + inline-size: 1920px; + } + } + + >.left { + flex: 1; + min-inline-size: 0; + padding-block-start: 30px; + display: flex; + flex-direction: column; + row-gap: 30px; + + >.inside-wrp { + font-size: 14px; + border: 1px solid var(--e5e9ef); + border-radius: 4px; + display: flex; + + >:first-child { + flex: 1; + min-inline-size: 0; + padding-inline-start: 20px; + font-weight: bold; + align-content: center; + } + + >:last-child { + flex-shrink: 0; + block-size: 100px; + inline-size: 320px; + background-repeat: no-repeat; + background-size: cover; + } + } + + >.s_tag { + border-block-end: 1px solid var(--e5e9ef); + padding-block-end: 30px; + display: flex; + flex-wrap: wrap; + column-gap: 10px; + row-gap: 8px; + + >a { + padding-inline: 10px; + border: 1px solid var(--e5e9ef); + border-radius: 20px; + color: var(--6d757a); + line-height: 22px; + transition: all .3s; + + &:hover { + border-color: var(--00a1d6); + color: var(--00a1d6); + } + } + } + + >.v-desc { + border-block-end: 1px solid var(--e5e9ef); + padding-block-end: 30px; + display: flex; + flex-direction: column; + row-gap: 20px; + + >:first-child { + color: var(--222); + display: flex; + align-items: center; + column-gap: 3px; + + &::before { + content: ""; + display: inline-block; + inline-size: 14px; + block-size: 14px; + background-image: url(//static.hdslb.com/images/base/icons.png); + background-position: -793px -537px; + } + + &::after { + content: "未经作者授权 禁止转载"; + } + } + + >:last-child { + line-height: 20px; + color: var(--6d757a); + overflow: clip; + white-space: pre-line; + transition: all .3s; + + >a { + color: var(--00a1d6); + } + } + } + } + + >.right { + flex-shrink: 0; + inline-size: 300px; + } +} \ No newline at end of file diff --git a/src/main/desc/index.d.css.ts b/src/main/desc/index.d.css.ts new file mode 100644 index 0000000..eb9bc4f --- /dev/null +++ b/src/main/desc/index.d.css.ts @@ -0,0 +1,2 @@ +declare const stylesheet: CSSStyleSheet; +export default stylesheet; \ No newline at end of file diff --git a/src/main/desc/index.ts b/src/main/desc/index.ts new file mode 100644 index 0000000..6538ec0 --- /dev/null +++ b/src/main/desc/index.ts @@ -0,0 +1,134 @@ +import { activityInfo } from "../../io/com/bilibili/api/x/activity/subject/info"; +import { toviewWeb } from "../../io/com/bilibili/api/x/v2/history/toview/web"; +import { detail } from "../../io/com/bilibili/api/x/web-interface/view/detail"; +import { toastr } from "../../toastr"; +import { AV } from "../../utils/av"; +import { customElement } from "../../utils/Decorator/customElement"; +import { Element } from "../../utils/element"; +import { Format } from "../../utils/fomat"; +import { mainEv, MAIN_EVENT } from "../event"; +import { ROUTER } from "../router"; +import stylesheet from "./index.css" with {type: 'css'}; + +/** 信息区 */ +@customElement(undefined, `desc-${Date.now()}`) +export class Desc extends HTMLElement { + + /** + * 需要监听变动的属性。 + * 与实例方法`attributeChangedCallback`配合使用。 + * 此字符串序列定义了`attributeChangedCallback`回调时的第一个参数的可能值。 + */ + // static observedAttributes = []; + + /** + * 在属性更改、添加、移除或替换时调用。 + * 需要与静态属性`observedAttributes`配合使用。 + * 此回调的第一个参数在`observedAttributes`数组中定义。 + */ + // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} + + /** 每当元素添加到文档中时调用。 */ + // connectedCallback() {} + + /** 每当元素从文档中移除时调用。 */ + // disconnectedCallback() {} + + /** 每当元素被移动到新文档中时调用。 */ + // adoptedCallback() {} + + #host = this.attachShadow({ mode: 'closed' }); + + #container = Element.add('div', { class: 'container', appendTo: this.#host }); + + #left = Element.add('div', { class: 'left', appendTo: this.#container }); + + #right = Element.add('div', { class: 'right', appendTo: this.#container }); + + constructor() { + super(); + + this.#host.adoptedStyleSheets = [stylesheet]; + + mainEv.bind(MAIN_EVENT.NAVIGATE, ({ detail }) => { this.$navigate(...detail) }); + } + + /** 页面路由 */ + private async $navigate(router: ROUTER, url = new URL(location.href)) { + switch (router) { + case ROUTER.AV: { + const path = url.pathname.split('/'); + let aid = 0; + switch (true) { + case /^av\d+$/i.test(path[2]): { + aid = +path[2].slice(2); + break; + } + case /^bv1[a-z0-9]{9}$/i.test(path[2]): { + aid = +AV.fromBV(path[2]); + break; + } + } + if (aid) { + detail(aid) + .then(({ code, message, data }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, data } }); + // 视频信息 + const { View, Tags } = data; + View.mission_id && activityInfo(View.mission_id, aid) + .then(({ code, message, data }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, data } }); + this.#left.insertAdjacentHTML('afterbegin', `
    +
    ${data.dic}
    + +
    `) + }) + .catch(e => { + toastr.error('获取活动信息出错', e); + console.error(e); + }); + this.#left.innerHTML = `
    ${Tags.map(d => `${d.tag_name}`).join('')}
    +
    +
    +
    ${Format.superLink(View.desc)}
    +
    `; + }) + .catch(e => { + toastr.error('获取视频信息失败', e); + console.error(e); + }); + } + break; + } + case ROUTER.TOVIEW: { + const path = url.hash.split('/'); + let aid = 0; + switch (true) { + case /^av\d+$/i.test(path[1]): { + aid = +path[1].slice(2); + break; + } + case /^bv1[a-z0-9]{9}$/i.test(path[1]): { + aid = +AV.fromBV(path[1]); + break; + } + } + if (aid) { + this.$navigate(ROUTER.AV, new URL(`https://www.bilibili.com/video/av${aid}`)); + } else { + toviewWeb() + .then(({ code, message, data }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, data } }); + aid || (aid = data.list[0].aid); + this.$navigate(ROUTER.AV, new URL(`https://www.bilibili.com/video/av${aid}`)); + }) + .catch(e => { + toastr.error('请求稍后再看信息错误~', e); + console.error(e); + }); + } + break; + } + } + } +} \ No newline at end of file diff --git a/src/main/event.ts b/src/main/event.ts new file mode 100644 index 0000000..2c7b0d7 --- /dev/null +++ b/src/main/event.ts @@ -0,0 +1,101 @@ +import { pgcPlayurl } from "../io/com/bilibili/api/pgc/player/web/playurl"; +import { pugvPlayurl } from "../io/com/bilibili/api/pugv/player/web/playurl"; +import { playurl } from "../io/com/bilibili/api/x/player/playurl"; +import { IMainOptions } from "./option"; +import { ROUTER } from "./router"; + +class Event extends EventTarget { + + /** + * 事件监听(只监听一次) + * + * @param type 事件类型 + * @param listener 事件回调 + */ + one(type: K, listener: (evt: CustomEvent) => void) { + this.addEventListener('bofqi' + type, listener, { once: true }); + } + + /** + * 事件监听 + * + * @param type 事件类型 + * @param listener 事件回调 + */ + bind = (type: K, listener: (evt: CustomEvent) => void) => { + this.addEventListener('bofqi' + type, listener); + } + + /** + * 取消事件监听 + * + * @param type 事件类型 + * @param listener 事件回调 + */ + unbind = (type: K, listener: (evt: CustomEvent) => void) => { + this.removeEventListener('bofqi' + type, listener); + } + + /** + * 分发事件 + * + * @param type 事件类型 + * @param detail 分发给回调的数据 + */ + trigger = (type: K, detail: IPlayerEvent[K]) => { + // Promise.resolve().then(() => { + // 原生`dispatchEvent`方法只能同步发送消息,使用`Promise`转为异步以合乎正常事件处理流程 + this.dispatchEvent(new CustomEvent('bofqi' + type, { detail })); + // }); + } +} + +/** 播放器事件组件 */ +export const mainEv = new Event(); + +/** 播放器事件 */ +export enum MAIN_EVENT { + /** 设置变动 */ + OPTINOS_CHANGE, + + /** 更新playurl数据 */ + PLAYURL, + + /** 初始化 */ + IDENTIFY, + + /** 连接视频 */ + CONNECT, + + /** 新导航 */ + NAVIGATE, + + /** 刷新视频互动数据 */ + RELATION_FLASH, + + /** 关注 */ + GUAN_ZHU, + + /** 请求弹出投币窗口 */ + REQUSET_COIN, + + /** 请求弹出收藏窗口 */ + REQUEST_FAV, + + /** 追番 */ + ZHUI_FAN, +} + +/** 播放器事件基类 */ +export interface IPlayerEvent { + 0: IMainOptions; + 1: Awaited>['result'] | Awaited>['data'] | Awaited>['data']; + 2: void; + 3: void; + 4: [ROUTER, URL]; + 5: void; + 6: boolean; + 7: void; + 8: void; + 9: boolean; +} \ No newline at end of file diff --git a/src/main/gotop/index.css b/src/main/gotop/index.css new file mode 100644 index 0000000..c94d0ab --- /dev/null +++ b/src/main/gotop/index.css @@ -0,0 +1,42 @@ +@scope { + :scope { + color-scheme: light dark; + + --f6f9fa: #f6f9fa; + --e5e9ef: #e5e9ef; + --00a1d6: #00a1d6; + + position: fixed; + inset-inline-end: 10%; + inset-block-end: 10%; + animation: gotop 3s linear; + animation-timeline: scroll(); + } +} + +.gotop { + inline-size: 46px; + block-size: 48px; + background-image: url(//static.hdslb.com/images/base/icons.png); + background-color: var(--f6f9fa); + background-position: -648px -72px; + background-repeat: no-repeat; + border: 1px solid var(--e5e9ef); + cursor: pointer; + opacity: calc(max(var(--scroll-positon) - 17, 0) / max(var(--scroll-positon) - 17, 0)); + + &:hover { + background-color: var(--00a1d6); + background-position: -714px -72px; + border-color: var(--00a1d6); + } +} + + +/* shadowroot stylesheet 里似乎无法注册自定义属性,将在 js 里注册 */ +@keyframes gotop { + + to { + --scroll-positon: 100; + } +} \ No newline at end of file diff --git a/src/main/gotop/index.d.css.ts b/src/main/gotop/index.d.css.ts new file mode 100644 index 0000000..eb9bc4f --- /dev/null +++ b/src/main/gotop/index.d.css.ts @@ -0,0 +1,2 @@ +declare const stylesheet: CSSStyleSheet; +export default stylesheet; \ No newline at end of file diff --git a/src/main/gotop/index.ts b/src/main/gotop/index.ts new file mode 100644 index 0000000..6a4519a --- /dev/null +++ b/src/main/gotop/index.ts @@ -0,0 +1,52 @@ +import { customElement } from "../../utils/Decorator/customElement"; +import stylesheet from "./index.css" with {type: 'css'}; + +/** 评论区 */ +@customElement(undefined, `go-top-${Date.now()}`) +export class Gotop extends HTMLElement { + + /** + * 需要监听变动的属性。 + * 与实例方法`attributeChangedCallback`配合使用。 + * 此字符串序列定义了`attributeChangedCallback`回调时的第一个参数的可能值。 + */ + // static observedAttributes = []; + + /** + * 在属性更改、添加、移除或替换时调用。 + * 需要与静态属性`observedAttributes`配合使用。 + * 此回调的第一个参数在`observedAttributes`数组中定义。 + */ + // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} + + /** 每当元素添加到文档中时调用。 */ + // connectedCallback() {} + + /** 每当元素从文档中移除时调用。 */ + // disconnectedCallback() {} + + /** 每当元素被移动到新文档中时调用。 */ + // adoptedCallback() {} + + #host = this.attachShadow({ mode: 'closed' }); + + constructor() { + super(); + + this.#host.adoptedStyleSheets = [stylesheet]; + this.#host.innerHTML = `
    `; + CSS.registerProperty({ + name: "--scroll-positon", + syntax: "", + inherits: true, + initialValue: "0", + }); + + this.addEventListener('click', () => { + self.scrollTo({ + top: 0, + behavior: 'smooth', + }); + }); + } +} \ No newline at end of file diff --git a/src/main/html/av/index.ts b/src/main/html/av/index.ts new file mode 100644 index 0000000..962ec05 --- /dev/null +++ b/src/main/html/av/index.ts @@ -0,0 +1,41 @@ +import { Html } from ".."; +import { AV } from "../../../utils/av"; +import { Desc } from "../../desc"; +import { MAIN_EVENT, mainEv } from "../../event"; +import { Gotop } from "../../gotop"; +import { Info } from "../../info"; +import { ROUTER } from "../../router"; +import { Bofqi } from "../bofqi"; +import { CommentBox } from "../comment"; +import { Footer } from "../footer"; +import { Header } from "../header"; + +/** AV页组件 */ +export class Av { + + #header = new Header(); + + #info = new Info(); + + #bofqi = new Bofqi(); + + #desc = new Desc(); + + #comment = new CommentBox(); + + #footer = new Footer(); + + constructor() { + document.documentElement.replaceWith(new Html()); + + document.body.append(this.#header, this.#info, this.#bofqi, this.#desc, this.#comment, this.#footer, new Gotop()); + + mainEv.trigger(MAIN_EVENT.NAVIGATE, [ROUTER.AV, new URL(location.href)]); + + const url = new URL(location.href); + if (/bv/i.test(url.pathname)) { + url.pathname = AV.fromStr(url.pathname); + history.replaceState(undefined, '', url); + } + } +} \ No newline at end of file diff --git a/src/main/html/bangumi/detail/index.css b/src/main/html/bangumi/detail/index.css new file mode 100644 index 0000000..3953f5a --- /dev/null +++ b/src/main/html/bangumi/detail/index.css @@ -0,0 +1,547 @@ +@scope { + :scope { + color-scheme: light dark; + + --e5e9ef: #e5e9ef; + --222: #222; + --f36392: #f36392; + --fff: #fff; + --6d757a: #6d757a; + --00a1d6: #00a1d6; + --99a2aa: #99a2aa; + --999: #999; + --ffa058: #ffa058; + --61666d: #61666d; + --f5b23d: #f5b23d; + --ffdc7c80: #ffdc7c80; + --ffc154: #ffc154; + --ccd0d7: #ccd0d7; + + font-size: 12px; + } + + a { + text-decoration: none; + outline: none; + } +} + +.container { + inline-size: 980px; + margin-inline: auto; + box-sizing: border-box; + font-size: 12px; + display: flex; + flex-direction: column; + + @media screen and (min-width:1400px) { + + & { + inline-size: 1160px; + } + } + + @media screen and (min-width:2500px) { + + & { + inline-size: 1920px; + } + } + + >.info { + padding-block-start: 24px; + padding-block-end: 20px; + border-block-end: 1px solid var(--e5e9ef); + display: flex; + column-gap: 24px; + + >.left { + flex-shrink: 0; + + img { + inline-size: 120px; + } + } + + >.right { + flex: 1; + min-inline-size: 0; + display: flex; + flex-direction: column; + + >.title { + display: flex; + justify-content: space-between; + + >.name { + color: var(--222); + font-weight: 400; + font-size: 18px; + line-height: 18px; + display: flex; + column-gap: .5em; + + >.score { + font-size: 13px; + color: var(--ffa058); + + &::before { + content: attr(data-score); + font-size: 20px; + } + } + + >.count { + font-size: 12px; + color: var(--61666d); + } + } + + >.func { + display: flex; + column-gap: 20px; + + >.follow { + inline-size: 80px; + block-size: 28px; + padding: 0; + background-color: var(--f36392); + border: 1px solid var(--f36392); + border-radius: 4px; + color: var(--fff); + font-size: 14px; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + column-gap: 7px; + + &::before { + display: inline-block; + inline-size: 14px; + block-size: 12px; + background-repeat: no-repeat; + background-image: url(//s1.hdslb.com/bfs/static/bangumi/play/asserts/icons.png); + background-position: -665px -1882px; + } + + &.d { + color: var(--6d757a); + border-color: var(--e5e9ef); + background-color: transparent; + } + } + + >.app { + inline-size: 86px; + block-size: 28px; + font-size: 14px; + padding: 0; + color: var(--6d757a); + border: 1px solid var(--e5e9ef); + border-radius: 4px; + background-color: transparent; + cursor: pointer; + + &:hover { + color: var(--00a1d6); + border-color: var(--00a1d6); + } + } + + >.icon { + inline-size: 30px; + block-size: 30px; + background-repeat: no-repeat; + background-image: url(//s1.hdslb.com/bfs/static/bangumi/play/asserts/icons.png); + cursor: pointer; + padding: 0; + border: 0; + background-color: transparent; + + &.weibo { + background-position: -1168px -913px; + + &:hover { + background-position: -1105px -913px; + } + } + + &.zone { + background-position: -1168px -720px; + + &:hover { + background-position: -1105px -720px; + } + } + + &.qq { + background-position: -977px -913px; + + &:hover { + background-position: -977px -849px; + } + } + + &.baidu { + background-position: -1168px -785px; + + &:hover { + background-position: -1105px -785px; + } + } + } + } + } + + >.top-block { + margin-block-start: 16px; + border-block-end: 1px solid var(--e5e9ef); + display: flex; + justify-content: space-between; + + >.ss-list-wrapper { + display: flex; + column-gap: 30px; + + >label { + display: inline-block; + block-size: 26px; + border-block-end: 1px solid transparent; + font-size: 12px; + cursor: pointer; + position: relative; + transition: .3s; + + >input { + appearance: none; + display: none; + } + + &:hover { + color: var(--00a1d6); + } + + &:has(input:checked) { + border-block-end-color: var(--00a1d6); + color: var(--00a1d6); + pointer-events: none; + + &::after { + content: ""; + position: absolute; + inset-block-end: 0; + inset-inline-start: calc(50% - 3px); + border-block-end: 3px solid var(--00a1d6); + border-block-start: 3px solid transparent; + border-inline: 3px solid transparent; + } + } + } + } + + >.mode-select { + display: flex; + column-gap: 20px; + + >label { + inline-size: 16px; + block-size: 16px; + background-repeat: no-repeat; + background-image: url(//s1.hdslb.com/bfs/static/bangumi/play/asserts/icons.png); + cursor: pointer; + + >input { + appearance: none; + display: none; + } + + &.detail-mode { + background-position: -279px -1815px; + + &:has(input:checked) { + background-position: -343px -1815px; + } + } + + &.simple-mode { + background-position: -281px -1880px; + + &:has(input:checked) { + background-position: -345px -1880px; + } + } + } + } + } + + >.episode-list { + display: flex; + column-gap: 10px; + margin-block-start: 14px; + padding-block: 6px; + overflow-x: auto; + scrollbar-width: thin; + + >label { + flex-shrink: 0; + inline-size: 100px; + block-size: 48px; + cursor: pointer; + background-color: var(--fff); + border: 1px solid var(--e5e9ef); + border-radius: 4px; + padding: 8px; + position: relative; + + >input { + appearance: none; + display: none; + } + + >.ep-index { + line-height: 19px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + + &.d { + &::before { + content: "第"; + } + + &::after { + content: "话"; + } + } + } + + >.ep-title { + line-height: 16px; + max-block-size: 32px; + color: var(--99a2aa); + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + white-space: normal; + word-break: break-word; + word-wrap: break-word; + overflow: hidden; + text-overflow: ellipsis; + } + + >.mark { + position: absolute; + inset-block-start: -7px; + inset-inline-end: -7px; + padding-inline: 4px; + line-height: 18px; + border-radius: 9px; + background-color: var(--999); + color: var(--fff); + } + + &:has(input:checked) { + border-color: var(--00a1d6); + background-color: var(--00a1d6); + pointer-events: none; + + >.ep-index, + >.ep-title { + color: var(--fff); + } + } + + &:hover { + border-color: var(--00a1d6); + + >.ep-index, + >.ep-title { + color: var(--00a1d6); + } + } + } + + &.simple { + flex-wrap: wrap; + column-gap: 8px; + row-gap: 9px; + + >label { + inline-size: 66px; + block-size: 28px; + padding: 0; + + >.ep-index { + font-size: 14px; + color: var(--6d757a); + line-height: 28px; + text-align: center; + + &::before, + &::after { + display: none; + } + } + + >.ep-title { + display: none; + } + } + } + } + } + } + + >.sponsor { + padding-block: 20px; + border-block-end: 1px solid var(--e5e9ef); + display: flex; + + >.left { + flex: 1; + min-inline-size: 0; + display: flex; + flex-direction: column; + row-gap: 20px; + + >.header { + display: flex; + justify-content: space-between; + color: var(--222); + + >.title { + font-size: 18px; + line-height: 24px; + font-weight: bold; + } + + >.view { + line-height: 22px; + padding-inline: 15px; + border: 1px solid var(--ccd0d7); + border-radius: 4px; + cursor: pointer; + + &:hover { + color: var(--00a1d6); + border-color: var(--00a1d6); + } + } + } + + >.body { + display: flex; + flex-wrap: wrap; + column-gap: 30px; + overflow-x: auto; + scrollbar-width: thin; + + >div { + display: flex; + + >:first-child>.avatar { + display: flex; + justify-content: center; + align-items: center; + position: relative; + --size: 50px; + + >img { + block-size: var(--size); + inline-size: var(--size); + margin-inline-end: calc(var(--size) / 3); + border-radius: 50%; + } + } + + >:last-child { + display: flex; + flex-direction: column; + row-gap: 10px; + + >.info { + font-size: 14px; + line-height: 16px; + color: var(--222); + font-weight: bold; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + } + + >.msg { + line-height: 20px; + padding-block: 1px; + padding-inline: 5px; + border: 1px solid var(--e5e9ef); + border-start-end-radius: 4px; + border-end-end-radius: 4px; + color: var(--99a2aa); + word-break: break-all; + inline-size: 145px; + position: relative; + + >i { + border-color: var(--e5e9ef) var(--e5e9ef) transparent transparent; + border-style: solid; + border-width: 5px; + position: absolute; + inset-inline-start: -10px; + inset-block-start: -2px; + + &::before { + content: ""; + border-color: var(--fff) var(--fff) transparent transparent; + } + } + } + } + } + } + } + + >.right { + flex-shrink: 0; + display: flex; + flex-direction: column; + row-gap: 5px; + + >img { + inline-size: 260px; + } + + >.count { + text-align: center; + font-size: 12px; + margin-block-end: 15px; + color: var(--6d757a); + + >span { + color: var(--222); + font-weight: 700; + } + } + + >button { + inline-size: 192px; + line-height: 47px; + margin-inline: auto; + border: 0; + padding: 0; + font-size: 18px; + font-weight: 700; + color: var(--fff); + background-color: var(--f5b23d); + border-radius: 4px; + box-shadow: 0 2px 4px var(--ffdc7c80); + cursor: pointer; + transition: background-color .3s ease; + + &:hover { + background-color: var(--ffc154); + } + } + } + } +} \ No newline at end of file diff --git a/src/main/html/bangumi/detail/index.d.css.ts b/src/main/html/bangumi/detail/index.d.css.ts new file mode 100644 index 0000000..eb9bc4f --- /dev/null +++ b/src/main/html/bangumi/detail/index.d.css.ts @@ -0,0 +1,2 @@ +declare const stylesheet: CSSStyleSheet; +export default stylesheet; \ No newline at end of file diff --git a/src/main/html/bangumi/detail/index.ts b/src/main/html/bangumi/detail/index.ts new file mode 100644 index 0000000..e9deeb6 --- /dev/null +++ b/src/main/html/bangumi/detail/index.ts @@ -0,0 +1,257 @@ +import { IModulePositiveEpisode, pgcAppSeason } from "../../../../io/com/bilibili/api/pgc/view/v2/app/season"; +import { IEpisode, pgcSection } from "../../../../io/com/bilibili/api/pgc/view/web/season/user/section"; +import { followAdd } from "../../../../io/com/bilibili/api/pgc/web/follow/add"; +import { followDel } from "../../../../io/com/bilibili/api/pgc/web/follow/del"; +import { toastr } from "../../../../toastr"; +import { cookie } from "../../../../utils/cookie"; +import { customElement } from "../../../../utils/Decorator/customElement"; +import { Element } from "../../../../utils/element"; +import { Format } from "../../../../utils/fomat"; +import { https } from "../../../../utils/https"; +import { mainEv, MAIN_EVENT } from "../../../event"; +import { ROUTER } from "../../../router"; +import stylesheet from "./index.css" with {type: 'css'}; + +/** 详情区 */ +@customElement(undefined, `desctail-${Date.now()}`) +export class Detail extends HTMLElement { + + /** + * 需要监听变动的属性。 + * 与实例方法`attributeChangedCallback`配合使用。 + * 此字符串序列定义了`attributeChangedCallback`回调时的第一个参数的可能值。 + */ + // static observedAttributes = []; + + /** + * 在属性更改、添加、移除或替换时调用。 + * 需要与静态属性`observedAttributes`配合使用。 + * 此回调的第一个参数在`observedAttributes`数组中定义。 + */ + // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} + + /** 每当元素添加到文档中时调用。 */ + // connectedCallback() {} + + /** 每当元素从文档中移除时调用。 */ + // disconnectedCallback() {} + + /** 每当元素被移动到新文档中时调用。 */ + // adoptedCallback() {} + + #host = this.attachShadow({ mode: 'closed' }); + + #container = Element.add('div', { class: 'container', appendTo: this.#host }); + + #info = Element.add('div', { class: 'info', appendTo: this.#container }); + + #left = Element.add('div', { class: 'left', appendTo: this.#info }); + + #right = Element.add('div', { class: 'right', appendTo: this.#info }); + + #title = Element.add('div', { class: 'title', appendTo: this.#right }); + + #titleFunc = Element.add('div', { + class: 'func', appendTo: this.#title, innerHTML: ` + + + + +` } + ); + + #topBlock = Element.add('div', { class: 'top-block', appendTo: this.#right }); + + #ssList = Element.add('form', { class: 'ss-list-wrapper', appendTo: this.#topBlock }); + + #modSelect = Element.add('form', { class: 'mode-select', appendTo: this.#topBlock, innerHTML: `` }); + + #episodeList = Element.add('form', { class: 'episode-list', appendTo: this.#right }); + + #sponsor = Element.add('div', { class: 'sponsor' }); + + #sponsorLeft = Element.add('div', { + class: 'left', appendTo: this.#sponsor, innerHTML: `
    +
    承包榜
    +
    查看承包榜
    +
    ` }); + + #sponsorRight = Element.add('div', { class: 'right', appendTo: this.#sponsor, innerHTML: '' }); + + #sponsorList = Element.add('div', { class: 'body', appendTo: this.#sponsorLeft }); + + #follow = this.#titleFunc.firstElementChild! + + #ssid = 0; + + #epid = 0; + + constructor() { + super(); + + this.#host.adoptedStyleSheets = [stylesheet]; + + this.#ssList.addEventListener('change', () => { + const d = new FormData(this.#ssList); + const i = Number(d.get('ssid')); + if (i) { + pgcSection(i) + .then(({ code, message, result }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, result } }); + this.#episodeList.replaceChildren(); + const eps = ([]).concat(...(result.main_section?.episodes || []), ...result.section.map(d => d.episodes)); + eps.forEach(d => Element.add('label', { appendTo: this.#episodeList, innerHTML: `
    ${d.title}
    ${d.long_title}
    ${d.badge_info.text ? `
    ${d.badge_info.text}
    ` : ''}` })); + }) + .catch(e => { + toastr.error('获取分集列表出错', e); + console.error(e) + }) + } + }); + this.#modSelect.addEventListener('change', () => { + const d = new FormData(this.#modSelect); + const i = +[...d.values()][0]; + this.#episodeList.classList.toggle('simple', Boolean(i)); + }); + this.#episodeList.addEventListener('change', () => { + const d = new FormData(this.#episodeList); + const i = Number(d.get('epid')); + if (i) { + const url = new URL(`https://www.bilibili.com/bangumi/play/ep${i}`); + mainEv.trigger(MAIN_EVENT.NAVIGATE, [ROUTER.BANGUMI, url]); + history.replaceState(undefined, '', url); + } + }); + this.#follow.addEventListener('click', () => { + const csrf = cookie.get('bili_jct'); + const i = this.#follow.classList.contains('d'); + if (csrf && this.#ssid) { + (i ? followDel(csrf, this.#ssid) : followAdd(csrf, this.#ssid)) + .then(({ code, message, result }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message } }); + toastr.success(result.toast); + mainEv.trigger(MAIN_EVENT.ZHUI_FAN, Boolean(result.status)); + }) + .catch(e => { + toastr.error(`${i ? '取消' : '添加'}追番出错`, e); + console.error(e); + }); + } + }); + this.#container.addEventListener('click', ({ target }) => { + if (target instanceof HTMLElement) { + if (target.closest('.chengbao')) { + toastr.warn('【承包】功能属于支付类风险操作!', '请移步到番剧详情页等原生页面进行,已保护您的财产安全~').$delay = 10; + } + } + }); + + mainEv.bind(MAIN_EVENT.NAVIGATE, ({ detail }) => { this.$navigate(...detail) }); + mainEv.bind(MAIN_EVENT.ZHUI_FAN, ({ detail }) => { + this.#follow.innerText = this.#follow.classList.toggle('d', detail) ? '已追番' : '追番'; + }); + } + + /** 页面路由 */ + private async $navigate(router: ROUTER, url = new URL(location.href)) { + this.#identify(); + switch (router) { + case ROUTER.BANGUMI: { + const path = url.pathname.split('/'); + switch (true) { + case /^ss\d+$/i.test(path[3]): { + this.#ssid = +path[3].slice(2); + break; + } + case /^ep\d+$/i.test(path[3]): { + this.#epid = +path[3].slice(2); + break; + } + } + if (this.#ssid || this.#epid) { + pgcAppSeason(this.#ssid ? { season_id: this.#ssid } : { ep_id: this.#epid }) + .then(async ({ code, message, data }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, data } }); + this.#ssid || (this.#ssid = data.season_id); + this.#epid || (data.user_status.progress?.last_ep_id && (this.#epid = data.user_status.progress?.last_ep_id)); + let ep: IEpisode | IModulePositiveEpisode | undefined; + data.modules.forEach(d => { + switch (d.style) { + case "positive": + case "section": { + if (!ep) { + this.#epid || (this.#epid = d.data.episodes[0]?.ep_id); + if (this.#epid) { + ep = d.data.episodes.find(d => d.ep_id === this.#epid); + } + } + break; + } + } + }); + if (!ep) { + if (this.#ssid) { + const { code, message, result } = await pgcSection(this.#ssid); + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, result } }); + const eps = ([]).concat(...(result.main_section?.episodes || []), ...result.section.map(d => d.episodes)); + ep = this.#epid ? eps.find(d => d.id === this.#epid) : eps[0]; + eps.forEach(d => Element.add('label', { appendTo: this.#episodeList, innerHTML: `
    ${d.title}
    ${d.long_title}
    ${d.badge_info.text ? `
    ${d.badge_info.text}
    ` : ''}` })); + } else { + throw new ReferenceError('番剧信息里未包含有效分集列表?', { cause: { code, message, data } }); + } + } + if (!ep) throw new ReferenceError('番剧信息里未包含有效分集列表?', { cause: { code, message, data } }); + this.#left.innerHTML = https(``); + this.#title.insertAdjacentHTML('afterbegin', `${data.title}${data.rating ? `
    ${Format.carry(data.rating.count)}人评分
    ` : ''}
    `); + for (const module of data.modules) { + switch (module.style) { + case "positive": { + module.data.episodes.forEach(d => Element.add('label', { appendTo: this.#episodeList, innerHTML: `
    ${d.title}
    ${d.long_title}
    ${d.badge_info.text ? `
    ${d.badge_info.text}
    ` : ''}` })); + break; + } + case "season": { + module.data.seasons.forEach(d => Element.add('label', { appendTo: this.#ssList, innerHTML: `${d.season_title}` })); + break; + } + case "section": { + module.data.episodes.forEach(d => Element.add('label', { appendTo: this.#episodeList, innerHTML: `
    ${d.title}
    ${d.long_title}
    ${d.badge_info.text ? `
    ${d.badge_info.text}
    ` : ''}` })); + break; + } + case "pugv": { + break; + } + } + } + if (data.sponsor) { + this.#container.appendChild(this.#sponsor); + this.#sponsorRight.innerHTML = `
    已有${Format.carry(data.sponsor.total)}人承包
    `; + data.sponsor.list.forEach(d => { + Element.add('div', { + appendTo: this.#sponsorList, innerHTML: `
    +
    + ${d.uname} +
    ${d.msg || 'TA并沒有留言'}
    +
    ` }); + }); + } + this.#follow.innerText = this.#follow.classList.toggle('d', Boolean(data.user_status.follow)) ? '已追番' : '追番'; + }) + .catch(e => { + console.error(e); + }); + } + break; + } + } + } + + #identify = () => { + this.#ssid = this.#epid = 0; + this.#left.replaceChildren(); + this.#title.replaceChildren(this.#titleFunc); + this.#ssList.replaceChildren(); + this.#episodeList.replaceChildren(); + this.#sponsor.remove(); + this.#sponsorList.replaceChildren(); + } +} \ No newline at end of file diff --git a/src/main/html/bangumi/index.ts b/src/main/html/bangumi/index.ts new file mode 100644 index 0000000..926512d --- /dev/null +++ b/src/main/html/bangumi/index.ts @@ -0,0 +1,34 @@ +import { Html } from ".."; +import { mainEv, MAIN_EVENT } from "../../event"; +import { Gotop } from "../../gotop"; +import { Info } from "../../info"; +import { ROUTER } from "../../router"; +import { Bofqi } from "../bofqi"; +import { CommentBox } from "../comment"; +import { Footer } from "../footer"; +import { Header } from "../header"; +import { Detail } from "./detail"; + +/** Bangumi页组件 */ +export class Bangumi { + + #header = new Header(); + + #info = new Info(); + + #bofqi = new Bofqi(); + + #detail = new Detail(); + + #comment = new CommentBox(); + + #footer = new Footer(); + + constructor() { + document.documentElement.replaceWith(new Html()); + + document.body.append(this.#header, this.#info, this.#bofqi, this.#detail, this.#comment, this.#footer, new Gotop()); + + mainEv.trigger(MAIN_EVENT.NAVIGATE, [ROUTER.BANGUMI, new URL(location.href)]); + } +} \ No newline at end of file diff --git a/src/main/html/bofqi/index.css b/src/main/html/bofqi/index.css new file mode 100644 index 0000000..82c3dc6 --- /dev/null +++ b/src/main/html/bofqi/index.css @@ -0,0 +1,54 @@ +@import url(./part/index.css); +@import url(./toolbar/index.css); + +@scope { + :scope { + --e5e9ef: #e5e9ef; + --f6f9fa: #f6f9fa; + --fff: #fff; + --444: #444; + --6d757a: #6d757a; + --00000029: #00000029; + --333: #333; + --ccd0d7: #ccd0d7; + --00a1d6: #00a1d6; + --ccc: #ccc; + --000: #000; + + display: block; + border-block: 1px solid var(--e5e9ef); + background-color: var(--f6f9fa); + padding-block: 22px; + background-size: cover; + } + + a { + text-decoration: none; + outline: none; + } +} + +.player-box { + --inline-size: 980px; + + inline-size: var(--inline-size); + margin-inline: auto; + + @media screen and (min-width:1400px) { + + & { + --inline-size: 1160px; + } + } + + @media screen and (min-width:2500px) { + + & { + --inline-size: 1920px; + } + } + + >.bilibili-player { + block-size: calc(var(--inline-size) / 16 * 9 + 68px); + } +} \ No newline at end of file diff --git a/src/main/html/bofqi/index.d.css.ts b/src/main/html/bofqi/index.d.css.ts new file mode 100644 index 0000000..eb9bc4f --- /dev/null +++ b/src/main/html/bofqi/index.d.css.ts @@ -0,0 +1,2 @@ +declare const stylesheet: CSSStyleSheet; +export default stylesheet; \ No newline at end of file diff --git a/src/main/html/bofqi/index.ts b/src/main/html/bofqi/index.ts new file mode 100644 index 0000000..f04eb95 --- /dev/null +++ b/src/main/html/bofqi/index.ts @@ -0,0 +1,28 @@ +import { customElement } from "../../../utils/Decorator/customElement"; +import { Element } from "../../../utils/element"; +import { BilibiliPlayer } from "../../player"; +import stylesheet from "./index.css" with {type: 'css'}; +import { Part } from "./part"; +import { Toolbar } from "./toolbar"; + +/** 播放器模块 */ +@customElement(undefined, `player-box-${Date.now()}`) +export class Bofqi extends HTMLElement { + + #host = this.attachShadow({ mode: 'closed' }); + + #player = new BilibiliPlayer(); + + #part = new Part(); + + #toolbar = new Toolbar(); + + #playerBox = Element.add('div', { class: 'player-box', appendTo: this.#host, children: [this.#part, this.#player, this.#toolbar] }); + + constructor() { + super(); + + this.#host.adoptedStyleSheets = [stylesheet]; + this.#player.classList.add('bilibili-player'); + } +} \ No newline at end of file diff --git a/src/main/html/bofqi/part/index.css b/src/main/html/bofqi/part/index.css new file mode 100644 index 0000000..d53ef5e --- /dev/null +++ b/src/main/html/bofqi/part/index.css @@ -0,0 +1,79 @@ +.multi-page { + position: relative; + font-size: 12px; + + &:has(>.list:empty) { + display: none; + } + + >.list { + margin-inline-end: 55px; + margin-block-end: 12px; + anchor-name: --part; + display: flex; + flex-wrap: wrap; + column-gap: 20px; + row-gap: 12px; + transition: .3s; + + >div { + min-inline-size: 117px; + block-size: 17px; + margin: 0; + padding-block: 3px; + padding-inline: 7px; + border-radius: 4px; + color: var(--222); + border: 1px solid var(--ccd0d7); + cursor: pointer; + align-content: center; + text-overflow: ellipsis; + white-space: nowrap; + transition: .3s; + + + &:hover, + &.active { + color: var(--fff); + background-color: var(--00a1d6); + border-color: var(--00a1d6); + } + + &.active { + pointer-events: none; + } + } + + &:not(.span) { + max-block-size: 25px; + overflow: clip; + } + } + + >.more { + position: absolute; + position-anchor: --part; + position-area: inline-end; + inline-size: 44px; + block-size: 17px; + padding-block: 3px; + padding-inline: 7px; + border: 1px solid var(--ccd0d7); + border-radius: 4px; + color: var(--222); + cursor: pointer; + text-align: center; + align-content: center; + transition: .3s; + + &:hover { + color: var(--fff); + background-color: var(--00a1d6); + border-color: var(--00a1d6); + } + + &:not(.active) { + display: none; + } + } +} \ No newline at end of file diff --git a/src/main/html/bofqi/part/index.ts b/src/main/html/bofqi/part/index.ts new file mode 100644 index 0000000..719e64d --- /dev/null +++ b/src/main/html/bofqi/part/index.ts @@ -0,0 +1,138 @@ +import { pagelist } from "../../../../io/com/bilibili/api/x/player/pagelist"; +import { toviewWeb } from "../../../../io/com/bilibili/api/x/v2/history/toview/web"; +import { AV } from "../../../../utils/av"; +import { customElement } from "../../../../utils/Decorator/customElement"; +import { Element } from "../../../../utils/element"; +import { mainEv, MAIN_EVENT } from "../../../event"; +import { ROUTER } from "../../../router"; + +/** 播放器主区域 */ +@customElement('div') +export class Part extends HTMLDivElement { + + /** + * 需要监听变动的属性。 + * 与实例方法`attributeChangedCallback`配合使用。 + * 此字符串序列定义了`attributeChangedCallback`回调时的第一个参数的可能值。 + */ + // static observedAttributes = []; + + /** + * 在属性更改、添加、移除或替换时调用。 + * 需要与静态属性`observedAttributes`配合使用。 + * 此回调的第一个参数在`observedAttributes`数组中定义。 + */ + // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} + + /** 每当元素添加到文档中时调用。 */ + // connectedCallback() { } + + /** 每当元素从文档中移除时调用。 */ + // disconnectedCallback() {} + + /** 每当元素被移动到新文档中时调用。 */ + // adoptedCallback() {} + + #list = Element.add('div', { class: 'list', appendTo: this }); + + #more = Element.add('div', { class: 'more', appendTo: this, innerText: '展开' }); + + constructor() { + super(); + + this.classList.add('multi-page'); + + mainEv.bind(MAIN_EVENT.NAVIGATE, ({ detail }) => { this.$navigate(...detail) }); + + this.#list.addEventListener('click', ({ target }) => { + if (target instanceof HTMLDivElement) { + const { url, router } = target.dataset; + if (url && router) { + mainEv.trigger(MAIN_EVENT.NAVIGATE, [+router, new URL(url, location.origin)]); + history.replaceState(undefined, '', url); + } + } + }); + this.#more.addEventListener('click', () => { + this.#more.textContent = this.#list.classList.toggle('span') ? '收起' : '展开'; + }) + } + + /** 页面路由 */ + private async $navigate(router: ROUTER, url = new URL(location.href)) { + this.identify(); + switch (router) { + case ROUTER.AV: { + const path = url.pathname.split('/'); + let aid = 0; + switch (true) { + case /^av\d+$/i.test(path[2]): { + aid = +path[2].slice(2); + break; + } + case /^bv1[a-z0-9]{9}$/i.test(path[2]): { + aid = +AV.fromBV(path[2]); + break; + } + } + if (aid) { + pagelist(aid) + .then(({ code, message, data }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, data } }); + const p = Number(new URLSearchParams(url.search).get('p')) || 1; + if (data.length > 1) { + data.forEach((d, i) => { + const a = Element.add('div', { attribute: { title: d.part }, innerText: `${d.page}、${d.part}`, appendTo: this.#list, data: { url: `/video/av${aid}/?p=${d.page}`, router: router } }); + i === p - 1 && a.classList.add('active'); + }); + this.#more.classList.toggle('active', this.#list.scrollHeight > 25); + } + }) + .catch(e => { + console.error('获取分P数据失败~', e) + }); + } + break; + } + case ROUTER.TOVIEW: { + const path = url.hash.split('/'); + let aid = 0; + switch (true) { + case /^av\d+$/i.test(path[1]): { + aid = +path[1].slice(2); + break; + } + case /^bv1[a-z0-9]{9}$/i.test(path[1]): { + aid = +AV.fromBV(path[1]); + break; + } + } + const p = +path[2]?.slice(1) || 1; + toviewWeb() + .then(({ code, message, data }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, data } }); + aid || (aid = data.list[0].aid); + const { pages } = data.list.find(d => d.aid === aid) || data.list[0]; + if (pages.length > 1) { + pages.forEach((d, i) => { + const a = Element.add('div', { attribute: { title: d.part }, innerText: `${d.page}、${d.part}`, appendTo: this.#list, data: { url: `https://www.bilibili.com/watchlater/#/av${aid}/p${d.page}`, router: router } }); + i === p - 1 && a.classList.add('active'); + }); + this.#more.classList.toggle('active', this.#list.scrollHeight > 25); + } + }) + .catch(e => { + console.error('获取分P数据失败~', e) + }); + break; + } + } + } + + private identify() { + this.#list.replaceChildren(); + this.#more.classList.remove('active'); + this.#list.classList.remove('span'); + this.#more.textContent = '展开'; + } +} \ No newline at end of file diff --git a/src/main/html/bofqi/toolbar/index.css b/src/main/html/bofqi/toolbar/index.css new file mode 100644 index 0000000..70ccf13 --- /dev/null +++ b/src/main/html/bofqi/toolbar/index.css @@ -0,0 +1,353 @@ +.toolbar { + block-size: 80px; + margin-block-start: 20px; + background-color: var(--fff); + color: var(--444); + border: 1px solid var(--e5e9ef); + border-radius: 4px; + font-size: 12px; + display: flex; + align-items: center; + column-gap: 15px; + + &.hide { + display: none; + } + + >.share-box { + display: flex; + position: relative; + + >.s-text { + display: grid; + grid-template-columns: repeat(2, 1fr); + grid-template-areas: + "t i" + "n i"; + align-items: center; + padding: 20px; + justify-items: center; + anchor-name: --share-box; + cursor: pointer; + + >.t { + grid-area: t; + font-size: 18px; + } + + >.num { + grid-area: n; + color: var(--6d757a); + font-size: 12px; + } + + >.icon { + grid-area: i; + inline-size: 10px; + block-size: 10px; + background-image: url(//static.hdslb.com/images/base/icons.png); + background-position: -933px -927px; + background-repeat: no-repeat; + } + } + + >.share-btn { + display: flex; + align-items: center; + column-gap: 7px; + + >i { + background-image: url(//static.hdslb.com/images/base/icons.png); + background-repeat: no-repeat; + inline-size: 40px; + block-size: 40px; + cursor: pointer; + + &.dynmic { + background-position: -1357px -972px; + + &:hover { + background-position-x: -1421px; + } + } + + &.weibo { + background-position: -1357px -586px; + } + + &.zone { + background-position: -1357px -726px; + + } + + &.qq { + background-position: -1357px -796px; + } + + &.baidu { + background-position: -1357px -656px; + + } + + &:hover { + background-position-x: -1427px; + } + } + } + + >.share-popup { + position: absolute; + position-anchor: --share-box; + position-area: block-end span-inline-end; + border: 0 solid var(--e5e9ef); + border-end-start-radius: 4px; + border-end-end-radius: 4px; + box-shadow: 0 3px 4px var(--00000029); + background-color: var(--fff); + z-index: 1; + display: flex; + transform-origin: center 0; + transition: all .5s allow-discrete; + + @starting-style { + scale: 1 0; + } + + >.share-address { + padding: 20px; + display: flex; + flex-direction: column; + row-gap: 20px; + + >.t { + font-size: 18px; + color: var(--333); + margin-block-start: 20px; + } + + >.item { + display: flex; + + >.name { + display: inline-block; + inline-size: 62px; + font-size: 13px; + } + + >input { + border: 1px solid var(--ccd0d7); + inline-size: 200px; + margin-inline-start: 10px; + border-radius: 0; + padding-inline: 4px; + text-decoration: none; + outline: none; + } + + >button { + inline-size: 40px; + padding: 0; + border: 0; + background-color: var(--00a1d6); + color: var(--fff); + font-size: 12px; + cursor: pointer; + } + } + } + + >.or-code { + inline-size: 195px; + margin: 26px; + padding-block: 10px; + padding-inline: 20px; + border-inline-start: 1px solid var(--e5e9ef); + display: flex; + flex-direction: column; + align-items: center; + row-gap: 18px; + + >.t { + font-size: 16px; + } + + >.or-code-pic { + inline-size: 100px; + block-size: 100px; + } + } + } + + &:not(:hover)>.share-popup { + display: none; + scale: 1 0; + } + } + + + >.tb-line { + block-size: 20px; + border-inline-end: 1px solid var(--ccc); + } + + >.fav-box { + display: flex; + align-items: center; + cursor: pointer; + + &::before { + content: ""; + display: inline-block; + inline-size: 80px; + block-size: 80px; + background: url(//s1.hdslb.com/bfs/static/jinkela/videoplay/asserts/anim-fav.png); + } + + + &.d::before { + background-position: -1120px 0; + } + + &:not(.d):hover::before { + animation: fav 1400ms steps(14) infinite; + } + + >div { + display: flex; + flex-direction: column; + align-items: center; + + >:first-child { + color: var(--000); + font-size: 16px; + } + + >:last-child { + color: var(--6d757a); + font-size: 12px; + } + } + } + + >.box { + display: flex; + align-items: center; + cursor: pointer; + + &::before { + content: ""; + display: inline-block; + inline-size: 80px; + block-size: 80px; + } + + >div { + display: flex; + flex-direction: column; + align-items: center; + + >:first-child { + color: var(--000); + font-size: 16px; + } + + >:last-child { + color: var(--6d757a); + font-size: 12px; + } + } + + &.d>div>:first-child::before { + content: "已"; + } + + &.fav { + &::before { + background-image: url(//s1.hdslb.com/bfs/static/jinkela/videoplay/asserts/anim-fav.png); + } + + + &.d::before { + background-position-x: -1120px; + } + + &:not(.d):hover::before { + animation: fav 1400ms steps(14) infinite; + } + } + + &.coin { + + &::before { + background-position-y: -11px; + background-image: url(//s1.hdslb.com/bfs/static/jinkela/videoplay/asserts/anim-coin.png); + } + + + &.d::before { + background-position-x: -641px; + } + + &:not(.d):hover::before { + animation: coin 800ms steps(8) infinite; + } + } + + &.wait { + + &::before { + background-image: url(//s1.hdslb.com/bfs/static/jinkela/videoplay/asserts/anim-wait.png); + } + + + &.d::before { + background-position-x: -960px; + } + + &:not(.d):hover::before { + animation: wait 800ms steps(8) infinite; + } + } + + &.app { + + &::before { + transition: background-position-x 720ms steps(9); + background-image: url(//s1.hdslb.com/bfs/static/jinkela/videoplay/asserts/anim-app.png); + } + + &:hover::before { + background-position-x: -720px; + animation: app 560ms steps(7) 720ms infinite; + } + } + } +} + +@keyframes fav { + to { + background-position-x: -1120px; + } +} + +@keyframes coin { + to { + background-position-x: -640px; + } +} + +@keyframes wait { + to { + background-position-x: -640px; + } +} + +@keyframes app { + from { + background-position-x: -720px; + } + + to { + background-position-x: -1280px; + } +} \ No newline at end of file diff --git a/src/main/html/bofqi/toolbar/index.ts b/src/main/html/bofqi/toolbar/index.ts new file mode 100644 index 0000000..feb3468 --- /dev/null +++ b/src/main/html/bofqi/toolbar/index.ts @@ -0,0 +1,241 @@ +import { toviewAdd } from "../../../../io/com/bilibili/api/x/v2/history/toview/add"; +import { toviewDel } from "../../../../io/com/bilibili/api/x/v2/history/toview/del"; +import { toviewWeb } from "../../../../io/com/bilibili/api/x/v2/history/toview/web"; +import { relation } from "../../../../io/com/bilibili/api/x/web-interface/archive/relation"; +import { nav } from "../../../../io/com/bilibili/api/x/web-interface/nav"; +import { detail } from "../../../../io/com/bilibili/api/x/web-interface/view/detail"; +import { toastr } from "../../../../toastr"; +import { AV } from "../../../../utils/av"; +import { cookie } from "../../../../utils/cookie"; +import { customElement } from "../../../../utils/Decorator/customElement"; +import { Element } from "../../../../utils/element"; +import { Format } from "../../../../utils/fomat"; +import { mainEv, MAIN_EVENT } from "../../../event"; +import { ROUTER } from "../../../router"; + +/** 播放器主区域 */ +@customElement('div') +export class Toolbar extends HTMLDivElement { + + /** + * 需要监听变动的属性。 + * 与实例方法`attributeChangedCallback`配合使用。 + * 此字符串序列定义了`attributeChangedCallback`回调时的第一个参数的可能值。 + */ + // static observedAttributes = []; + + /** + * 在属性更改、添加、移除或替换时调用。 + * 需要与静态属性`observedAttributes`配合使用。 + * 此回调的第一个参数在`observedAttributes`数组中定义。 + */ + // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} + + /** 每当元素添加到文档中时调用。 */ + // connectedCallback() { } + + /** 每当元素从文档中移除时调用。 */ + // disconnectedCallback() {} + + /** 每当元素被移动到新文档中时调用。 */ + // adoptedCallback() {} + + #shre = Element.add('div', { appendTo: this, class: "share-box" }); + + #shareText = Element.add('div', { + appendTo: this.#shre, class: "s-text", innerHTML: `分享` + }); + + #shareBtn = Element.add('div', { + appendTo: this.#shre, class: "share-btn", innerHTML: ` + + + +` + }); + + #sharePopup = Element.add('div', { + appendTo: this.#shre, class: "share-popup", innerHTML: ` +
    + 微信扫一扫分享 +
    +
    ` }); + + #favBox = Element.add('div', { appendTo: this, class: ['fav', 'box'] }); + + #coinBox = Element.add('div', { appendTo: this, class: ['coin', 'box'] }); + + #waitBox = Element.add('div', { appendTo: this, class: ['wait', 'box'], innerHTML: '
    稍后再看马克一下~
    ' }); + + #appBox = Element.add('a', { appendTo: this, class: ['app', 'box'], attribute: { href: '//app.bilibili.com', target: "_blank" }, innerHTML: '
    用手机看转移阵地~
    ' }); + + #aid = 0; + + get $aid() { + return this.#aid + } + + set $aid(v) { + this.#aid = v; + v && nav() + .then(({ code, message, data }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, data } }); + if (data.isLogin) { + relation(v) + .then(({ code, message, data }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, data } }); + const { favorite, coin } = data; + this.#favBox.classList.toggle('d', Boolean(favorite)); + this.#coinBox.classList.toggle('d', Boolean(coin)); + }) + .catch(e => { + toastr.error('获取互动状态出错', e); + console.error(e); + }); + } + }) + .catch(console.error); + } + + constructor() { + super(); + + this.classList.add('toolbar'); + this.#shre.insertAdjacentHTML('afterend', '
    '); + + mainEv.bind(MAIN_EVENT.NAVIGATE, ({ detail }) => { this.$navigate(...detail) }); + mainEv.bind(MAIN_EVENT.RELATION_FLASH, () => { + this.$aid = this.#aid; + }); + + this.#shre.addEventListener('click', ({ target }) => { + if (target instanceof HTMLButtonElement) { + switch (target.id) { + case 'link-btn-0': { + navigator.clipboard.writeText(this.#sharePopup.querySelector('#link0')!.value) + .then(() => { + toastr.success('已成功复制到剪切板') + }) + .catch(e => { + toastr.error('复制失败', e); + console.error(e); + }) + break; + } + case 'link-btn-2': { + navigator.clipboard.writeText(this.#sharePopup.querySelector('#link2')!.value) + .then(() => { + toastr.success('已成功复制到剪切板') + }) + .catch(e => { + toastr.error('复制失败', e); + console.error(e); + }) + break; + } + } + } + }); + this.#favBox.addEventListener('click', () => { + mainEv.trigger(MAIN_EVENT.REQUEST_FAV, void 0); + }); + this.#coinBox.addEventListener('click', () => { + this.#coinBox.classList.contains('d') || mainEv.trigger(MAIN_EVENT.REQUSET_COIN, void 0); + }); + this.#waitBox.addEventListener('click', () => { + const csrf = cookie.get('bili_jct'); + if (csrf && this.#aid) { + const i = this.#waitBox.classList.contains('d'); + (i ? toviewDel(csrf, this.#aid) : toviewAdd(csrf, this.#aid)) + .then(({ code, message }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message } }); + toastr.success(`已${i ? '移除' : '添加'}稍后再看`, `aid:${this.#aid}`); + this.#waitBox.classList.toggle('d'); + }) + .catch(e => { + toastr.error(`${i ? '移除' : '添加'}稍后再看失败`, e); + console.error(e); + }); + } + }); + } + + /** 页面路由 */ + private async $navigate(router: ROUTER, url = new URL(location.href)) { + switch (router) { + case ROUTER.AV: { + this.classList.remove('hide'); + const path = url.pathname.split('/'); + let aid = 0, cid = 0; + switch (true) { + case /^av\d+$/i.test(path[2]): { + aid = +path[2].slice(2); + break; + } + case /^bv1[a-z0-9]{9}$/i.test(path[2]): { + aid = +AV.fromBV(path[2]); + break; + } + } + const p = Number(new URLSearchParams(url.search).get('p')) || 1; + if (aid) { + detail(aid) + .then(({ code, message, data }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, data } }); + // 视频信息 + const { View } = data; + cid = View.pages[p - 1].cid; + this.#shareText.querySelector('.num')!.textContent = Format.carry(View.stat.share); + this.#sharePopup.querySelector('#link0')!.value = `https://www.bilibili.com/video/av${aid}/`; + this.#sharePopup.querySelector('#link2')!.value = ``; + this.#favBox.innerHTML = `
    收藏${Format.carry(View.stat.favorite)}
    `; + this.#coinBox.innerHTML = `
    投币${Format.carry(View.stat.coin)}
    `; + this.$aid = aid; + }) + .catch(e => { + toastr.error('获取视频信息失败', e); + console.error(e); + }); + } + break; + } + case ROUTER.TOVIEW: { + const path = url.hash.split('/'); + let aid = 0; + switch (true) { + case /^av\d+$/i.test(path[1]): { + aid = +path[1].slice(2); + break; + } + case /^bv1[a-z0-9]{9}$/i.test(path[1]): { + aid = +AV.fromBV(path[1]); + break; + } + } + if (aid) { + this.$navigate(ROUTER.AV, new URL(`https://www.bilibili.com/video/av${aid}`)); + } else { + toviewWeb() + .then(({ code, message, data }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, data } }); + aid || (aid = data.list[0].aid); + this.$navigate(ROUTER.AV, new URL(`https://www.bilibili.com/video/av${aid}`)); + }) + .catch(e => { + toastr.error('请求稍后再看信息错误~', e); + console.error(e); + }); + } + break; + } + default: { + this.classList.add('hide'); + break + } + } + } +} \ No newline at end of file diff --git a/src/main/html/comment/index.css b/src/main/html/comment/index.css new file mode 100644 index 0000000..fcbd7da --- /dev/null +++ b/src/main/html/comment/index.css @@ -0,0 +1,27 @@ +@scope { + :scope { + color-scheme: light dark; + } +} + +.comment-box { + inline-size: 980px; + padding-block: 30px; + padding-inline-end: 300px; + margin-inline: auto; + box-sizing: border-box; + + @media screen and (min-width:1400px) { + + & { + inline-size: 1160px; + } + } + + @media screen and (min-width:2500px) { + + & { + inline-size: 1920px; + } + } +} \ No newline at end of file diff --git a/src/main/html/comment/index.d.css.ts b/src/main/html/comment/index.d.css.ts new file mode 100644 index 0000000..eb9bc4f --- /dev/null +++ b/src/main/html/comment/index.d.css.ts @@ -0,0 +1,2 @@ +declare const stylesheet: CSSStyleSheet; +export default stylesheet; \ No newline at end of file diff --git a/src/player/area/wrap/state.ts b/src/main/html/comment/index.ts similarity index 66% rename from src/player/area/wrap/state.ts rename to src/main/html/comment/index.ts index 491a664..dd1d825 100644 --- a/src/player/area/wrap/state.ts +++ b/src/main/html/comment/index.ts @@ -1,10 +1,11 @@ import { customElement } from "../../../utils/Decorator/customElement"; import { Element } from "../../../utils/element"; -import svg_play_state from "../../assets/svg/play-state.svg"; +import { Comment } from "../../comment"; +import stylesheet from "./index.css" with {type: 'css'}; -/** 播放状态 */ -@customElement('div') -export class State extends HTMLDivElement { +/** 评论区 */ +@customElement(undefined, `comment-box-${Date.now()}`) +export class CommentBox extends HTMLElement { /** * 需要监听变动的属性。 @@ -20,11 +21,8 @@ export class State extends HTMLDivElement { */ // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} - /** 初始化标记 */ - // #inited = false; - /** 每当元素添加到文档中时调用。 */ - // connectedCallback() { } + // connectedCallback() {} /** 每当元素从文档中移除时调用。 */ // disconnectedCallback() {} @@ -32,13 +30,15 @@ export class State extends HTMLDivElement { /** 每当元素被移动到新文档中时调用。 */ // adoptedCallback() {} + #host = this.attachShadow({ mode: 'closed' }); + + #comment = new Comment(); + + #box = Element.add('div', { class: 'comment-box', appendTo: this.#host, children: this.#comment }); + constructor() { super(); - this.classList.add('bofqi-state'); + this.#host.adoptedStyleSheets = [stylesheet]; } - - private $play = Element.add('div', { class: 'bofqi-state-play' }, this, svg_play_state); - - private $buff = Element.add('img', { class: 'bofqi-state-buff', src: '//static.hdslb.com/images/loading.gif' }, this, svg_play_state); } \ No newline at end of file diff --git a/src/main/html/footer/index.css b/src/main/html/footer/index.css new file mode 100644 index 0000000..9a5e61b --- /dev/null +++ b/src/main/html/footer/index.css @@ -0,0 +1,216 @@ +@scope { + :scope { + color-scheme: light dark; + + --99a2aa: #99a2aa; + --f6f9fa: #f6f9fa; + --e5e9ef: #e5e9ef; + --00a1d6: #00a1d6; + --fff: #fff; + --eee: #eee; + + padding-block-start: 20px; + color: var(--99a2aa); + text-align: center; + font-size: 12px; + + a { + text-decoration: none; + } + } +} + +.footer-wrp { + background-color: var(--f6f9fa); + padding-block-start: 40px; + padding-block-end: 60px; + + >.footer-cnt { + inline-size: 980px; + margin-inline: auto; + + @media screen and (min-width:1400px) { + + & { + inline-size: 1160px; + } + } + + @media screen and (min-width:2500px) { + + & { + inline-size: 1920px; + } + } + + .boston-postcards { + margin-block-end: 30px; + display: flex; + justify-content: space-between; + + >div { + text-align: left; + inline-size: 300px; + block-size: 112px; + padding-inline-end: 25px; + padding-inline-start: 24px; + border-left: solid 1px var(--e5e9ef); + font-size: 14px; + display: flex; + flex-wrap: wrap; + row-gap: 16px; + + @media screen and (min-width: 1400px) { + & { + padding-inline-end: 40px; + padding-inline-start: 39px; + inline-size: 360px; + } + } + + @media screen and (min-width:2500px) { + + & { + padding-inline-end: 55px; + padding-inline-start: 54px; + inline-size: 420px; + } + } + + &:first-child { + border-inline-start-width: 0; + padding-inline-start: 0; + } + + &:last-child { + inline-size: 280px; + padding-inline-end: 0; + column-gap: 15px; + } + + >.tips { + inline-size: 100%; + margin-block-end: 6px; + } + + >.cards { + inline-size: 100px; + + @media screen and (min-width: 1400px) { + & { + inline-size: 120px; + } + } + + @media screen and (min-width:2500px) { + + & { + inline-size: 140px; + } + } + + >a { + color: var(--222); + + &:hover { + color: var(--00a1d6); + } + } + } + + >a { + position: relative; + display: flex; + flex-direction: column; + align-items: center; + + >div { + block-size: 60px; + aspect-ratio: 1; + background: url(//static.hdslb.com/images/base/icons.png) no-repeat; + + &.app { + background-position: -1024px -194px; + } + + &.weibo { + background-position: -1024px -322px; + } + + &.weixin { + background-position: -1024px -66px; + } + + } + + >em { + font-style: normal; + color: var(--222); + } + + >img { + position: absolute; + background: var(--fff); + border: 1px solid var(--eee); + padding: 10px; + inset-block-end: calc(100% + 1em); + } + + &:not(:hover)>img { + display: none; + } + } + } + } + + .partner { + display: flex; + text-align: start; + + .pic-box { + inline-size: 100px; + display: flex; + flex-direction: column; + + .jvs-cert { + aspect-ratio: 66 / 25; + background-repeat: no-repeat; + background-size: contain; + animation-name: jvs-cert; + animation-play-state: running; + animation-duration: 6s; + animation-delay: 3s; + animation-iteration-count: infinite; + animation-direction: alternate-reverse; + } + } + + .content-box { + line-height: 2; + margin-inline-start: 15px; + + p { + margin-block-end: 5px; + } + } + + a { + color: inherit; + } + + p { + margin: 0; + } + } + } +} + +@keyframes jvs-cert { + from { + background-image: url(//backup.hdslb.com/bfs/mainfront/websafe.png); + } + + to { + background-image: url(//backup.hdslb.com/bfs/mainfront/confirm.png); + } +} \ No newline at end of file diff --git a/src/main/html/footer/index.d.css.ts b/src/main/html/footer/index.d.css.ts new file mode 100644 index 0000000..eb9bc4f --- /dev/null +++ b/src/main/html/footer/index.d.css.ts @@ -0,0 +1,2 @@ +declare const stylesheet: CSSStyleSheet; +export default stylesheet; \ No newline at end of file diff --git a/src/main/html/footer/index.ts b/src/main/html/footer/index.ts new file mode 100644 index 0000000..f3b32d2 --- /dev/null +++ b/src/main/html/footer/index.ts @@ -0,0 +1,38 @@ +import { customElement } from "../../../utils/Decorator/customElement"; +import { Element } from "../../../utils/element"; +import stylesheet from "./index.css" with {type: 'css'}; + +/** 底栏模块 */ +@customElement(undefined, `footer-${Date.now()}`) +export class Footer extends HTMLElement { + + #host = this.attachShadow({ mode: 'closed' }); + + #wrp = Element.add('div', { class: 'footer-wrp', appendTo: this.#host }); + + #cnt = Element.add('div', { class: 'footer-cnt', appendTo: this.#wrp }); + + #post = Element.add('div', { + class: 'boston-postcards', appendTo: this.#cnt, innerHTML: ` + +` }); + + #partner = Element.add('div', { + class: 'partner', appendTo: this.#cnt, innerHTML: `
    + + +
    +
    +
    +

    广播电视节目制作经营许可证:(沪)字第1248号 | 网络文化经营许可证:沪网文[2016]2296-134号 | 信息网络传播视听节目许可证:0910417 | 互联网ICP备案:沪ICP备13002172号-3 沪ICP证:沪B2-20100043 | 违法不良信息举报邮箱:help@bilibili.com | 违法不良信息举报电话:4000233233转3 | 营业执照

    +

    上海互联网举报中心 |12345政务服务便民热线 | 沪公网安备 31011002002436号 |儿童色情信息举报专区 |扫黄打非举报

    +

    网上有害信息举报专区: 中国互联网违法和不良信息举报中心

    +

    亲爱的市民朋友,上海警方反诈劝阻电话“962110”系专门针对避免您财产被骗受损而设,请您一旦收到来电,立即接听。

    +

    公司名称:上海宽娱数码科技有限公司 | 公司地址:上海市杨浦区政立路485号 | 电话:021-25099888

    +
    ` }) + constructor() { + super(); + + this.#host.adoptedStyleSheets = [stylesheet]; + } +} \ No newline at end of file diff --git a/src/main/bilibili/html/head.ts b/src/main/html/head/index.ts similarity index 85% rename from src/main/bilibili/html/head.ts rename to src/main/html/head/index.ts index f96f704..19ff7c1 100644 --- a/src/main/bilibili/html/head.ts +++ b/src/main/html/head/index.ts @@ -33,14 +33,6 @@ export class Head extends HTMLHeadElement { this.innerHTML = ` 哔哩哔哩 (゜-゜)つロ 干杯~-bilibili - -` +`; } -} - - -//////////////////////////// 全局增强 //////////////////////////// -declare global { - /** 基于哈希消息认证码的一次性口令的密钥 */ - const __HTML_STYLE__: string; } \ No newline at end of file diff --git a/src/main/html/header/BiliHeader.ts b/src/main/html/header/BiliHeader.ts new file mode 100644 index 0000000..fa15e10 --- /dev/null +++ b/src/main/html/header/BiliHeader.ts @@ -0,0 +1,37 @@ +import { Header } from "."; +import { Element } from "../../../utils/element"; +import { ProxyHook } from "../../../utils/hook/Proxy"; + +export class BiliHeader { + + constructor() { + + ProxyHook.property(self, 'BiliHeader', class { + $header = new Header(); + constructor({ config }: IBiliHeader) { + if (config.headerType === 'mini') { + this.$header.classList.add('mini'); + } else { + this.$header.$resource_id = 142; + } + } + init(el: HTMLElement) { + // 兼容处理:部分页面使用顶栏脚本提供的UserStatus对象判定登录状态 + Element.add('script', { appendTo: document.head, attribute: { src: '//s1.hdslb.com/bfs/seed/jinkela/header/header.js' } }); + return el.replaceWith(this.$header); + } + }); + } +} + +interface IBiliHeader { + components: unknown; + config: { + disableChannelEntry?: boolean; + disableSticky?: boolean; + forceVersion?: number; + headerType: 'mini' | 'medium'; + theme?: 'light'; + tokenSupport?: boolean; + } +} \ No newline at end of file diff --git a/src/main/html/header/avatar.css b/src/main/html/header/avatar.css new file mode 100644 index 0000000..1cdaf61 --- /dev/null +++ b/src/main/html/header/avatar.css @@ -0,0 +1,254 @@ +.avatar { + display: flex; + justify-content: center; + align-items: center; + position: relative; + transition: .3s; + anchor-name: --avatar; + + --size: 32px; + + >.avatar-face { + block-size: var(--size); + inline-size: var(--size); + border-radius: 50%; + } + + >.avatar-pendant { + position: absolute; + block-size: calc(var(--size) / 2* 3); + inline-size: calc(var(--size) / 2* 3); + } + + &:not(:hover) { + >.avatar-pendant { + display: none; + } + + ~.avatar-wrap:not(:hover) { + display: none; + scale: 1 0; + } + } + + &:hover, + &:has(~.avatar-wrap:hover) { + --size: 64px; + translate: 0 50%; + z-index: 1; + } + + &:not(.d)~.avatar-wrap { + display: none; + } +} + +.avatar-wrap { + position: absolute; + position-anchor: --avatar; + position-area: block-end; + inline-size: 280px; + padding-block-start: 40px; + background-color: var(--fff); + box-shadow: 0 2px 4px var(--00000029); + display: flex; + flex-direction: column; + row-gap: 15px; + transform-origin: center 0; + transition: all .3s allow-discrete; + + @starting-style { + scale: 1 0; + } + + >.header-uname { + text-align: center; + font-weight: bold; + } + + >.btns-profile { + display: flex; + justify-content: space-around; + + >a { + color: var(--222); + display: flex; + + &::before { + content: ""; + display: inline-block; + inline-size: 18px; + block-size: 18px; + background-image: url(//static.hdslb.com/images/base/icons.png); + } + } + + >.coin::before { + background-position: -343px -471px; + } + + >.b-coin::before { + background-position: -407px -471px; + } + + >.bindphone { + &::before { + background-position: -279px -534px; + } + + &.verified::before { + background-position: -343px -534px; + } + } + + >.bindmail { + &::before { + background-position: -279px -599px; + } + + &.verified::before { + background-position: -343px -599px; + } + } + } + + >.grade { + margin-block: 15px; + display: flex; + justify-content: center; + align-items: center; + column-gap: 1em; + position: relative; + + >progress { + appearance: none; + inline-size: 170px; + block-size: 8px; + display: flex; + align-items: center; + + &::-webkit-progress-bar { + background-color: var(--eee); + } + + &::-webkit-progress-value { + background-color: var(--f3cb85); + } + + &::before { + content: ""; + display: inline-block; + inline-size: 18px; + block-size: 18px; + border-radius: 9px; + background-color: var(--f3cb85); + background-image: url(//s1.hdslb.com/bfs/static/jinkela/home/asserts/grade_icon.png); + background-repeat: no-repeat; + } + + &.lv0::before { + background-position: -153px -8px; + background-color: var(--ccc); + } + + &.lv1::before { + background-position: -153px -44px + } + + &.lv2::before { + background-position: -153px -80px + } + + &.lv3::before { + background-position: -153px -116px + } + + &.lv4::before { + background-position: -153px -152px + } + + &.lv5::before { + background-position: -153px -188px + } + + &.lv6::before { + background-position: -153px -224px + } + } + + >label { + position: absolute; + inset-block-end: -20px; + inset-inline-end: 1em; + } + } + + >.member-menu { + border-block-start: 1px solid #e5e9ef; + padding-block: 10px; + display: flex; + flex-wrap: wrap; + justify-content: center; + row-gap: 10px; + column-gap: 20px; + + >a { + inline-size: 100px; + color: var(--222); + white-space: nowrap; + display: flex; + column-gap: 10px; + + &::before { + content: ""; + inline-size: 16px; + block-size: 16px; + background-image: url(//static.hdslb.com/images/base/icons.png); + } + + &.account::before { + background-position: -472px -407px; + } + + &.member::before { + background-position: -536px -1046px; + } + + &.wallet::before { + background-position: -472px -472px; + } + + &.live::before { + background-position: -473px -855px; + } + + &.bml::before { + background-position: -663px -280px; + } + + &.cheese::before { + background-position: -1368px -216px; + } + } + } + + >.member-bottom { + block-size: 30px; + padding-inline: 20px; + background-color: var(--f4f5f7); + display: flex; + justify-content: flex-end; + + >button { + padding: 0; + border: 0; + color: var(--222); + background-color: transparent; + cursor: pointer; + + &:hover { + color: var(--00a1d6); + } + } + } +} \ No newline at end of file diff --git a/src/main/html/header/avatar.ts b/src/main/html/header/avatar.ts new file mode 100644 index 0000000..9308db7 --- /dev/null +++ b/src/main/html/header/avatar.ts @@ -0,0 +1,108 @@ +import { nav } from "../../../io/com/bilibili/api/x/web-interface/nav"; +import { exit } from "../../../io/com/bilibili/passport/login/exit/v2"; +import { toastr } from "../../../toastr"; +import { cookie } from "../../../utils/cookie"; +import { customElement } from "../../../utils/Decorator/customElement"; +import { Element } from "../../../utils/element"; + +/** 头像 */ +@customElement('a') +export class Avatar extends HTMLAnchorElement { + + /** + * 需要监听变动的属性。 + * 与实例方法`attributeChangedCallback`配合使用。 + * 此字符串序列定义了`attributeChangedCallback`回调时的第一个参数的可能值。 + */ + // static observedAttributes = []; + + /** + * 在属性更改、添加、移除或替换时调用。 + * 需要与静态属性`observedAttributes`配合使用。 + * 此回调的第一个参数在`observedAttributes`数组中定义。 + */ + // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} + + /** 每当元素添加到文档中时调用。 */ + connectedCallback() { + this.insertAdjacentElement('afterend', this.#wrap); + } + + /** 每当元素从文档中移除时调用。 */ + disconnectedCallback() { + this.#wrap.remove(); + } + + /** 每当元素被移动到新文档中时调用。 */ + // adoptedCallback() {} + + #wrap = Element.add('div', { class: 'avatar-wrap' }); + + constructor() { + super(); + + this.classList.add('avatar'); + this.innerHTML = ``; + this.href = '//space.bilibili.com'; + this.target = '_black'; + + this.#wrap.addEventListener('click', ({ target }) => { + if (target instanceof HTMLButtonElement && target.classList.contains('logout')) { + const toast = toastr.warn('即将退出登录!'); + toast.$delay = 0; + const bili_jct = cookie.get('bili_jct'); + if (bili_jct) { + exit(bili_jct) + .then(({ code, message, data }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, data } }); + toast.$type = 'success'; + toast.appendText('退出登录成功~', '3 秒后即将刷新页面...'); + setTimeout(() => { + location.replace(data.redirectUrl || location.href); + }, 3e3); + }) + .catch(e => { + toast.$type = "error"; + toast.appendText('退出登录失败', e); + console.error(e); + }) + .finally(() => { + toast.$delay = 4; + }) + } + } + }); + + nav() + .then(({ code, message, data }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, data } }); + if (data.isLogin) { + this.classList.add('d'); + this.innerHTML = `${data.pendant.image ? `` : ''}`; + this.#wrap.innerHTML = `
    ${data.uname}
    + +
    + 等级 + + +
    + +
    `; + } + }).catch(e => { + console.error(e); + }); + } +} \ No newline at end of file diff --git a/src/main/bilibili/assets/channel-list.json b/src/main/html/header/channel-list.json similarity index 100% rename from src/main/bilibili/assets/channel-list.json rename to src/main/html/header/channel-list.json diff --git a/src/main/html/header/dynamic.css b/src/main/html/header/dynamic.css new file mode 100644 index 0000000..a280b38 --- /dev/null +++ b/src/main/html/header/dynamic.css @@ -0,0 +1,200 @@ +.dynamic { + anchor-name: --dynamic; + position: relative; + + &[data-num]::after { + content: attr(data-num); + position: absolute; + color: var(--fff); + background-color: var(--f25d8e); + inset-block-start: 0; + inset-inline-end: 0; + line-height: 1; + border-radius: 10px; + padding-block: .1em; + padding-inline: .5em; + } + + &:not(:hover)~.dynamic-wrap:not(:hover) { + display: none; + scale: 1 0; + } + +} + +.dynamic-wrap { + position: absolute; + position-anchor: --dynamic; + position-area: block-end; + inline-size: 360px; + background: var(--fff); + box-shadow: 0 2px 4px var(--00000029); + border-end-start-radius: 4px; + border-end-end-radius: 4px; + display: flex; + flex-direction: column; + transform-origin: center 0; + transition: all .3s allow-discrete; + + @starting-style { + scale: 1 0; + } + + &:not(.d) { + display: none; + } + + >.dyn_menu { + padding-block-start: 16px; + padding-block-end: 10px; + display: flex; + justify-content: center; + column-gap: 1.5em; + + >label { + cursor: pointer; + line-height: 30px; + border-block-end: 1px solid transparent; + position: relative; + + >input { + appearance: none; + display: none; + } + + &:hover { + color: var(--00a1d6); + } + + &:has(input:checked) { + border-block-end-color: var(--00a1d6); + color: var(--00a1d6); + pointer-events: none; + + &::after { + content: ""; + position: absolute; + inset-block-end: 0; + inset-inline-start: calc(50% - 3px); + border-block-end: 3px solid var(--00a1d6); + border-block-start: 3px solid transparent; + border-inline: 3px solid transparent; + } + } + } + } + + >.dyn_list { + block-size: 300px; + overflow-y: auto; + scrollbar-width: thin; + display: flex; + flex-direction: column; + row-gap: 16px; + + + >div { + padding-inline: 1em; + display: flex; + align-items: center; + column-gap: 1em; + + >:first-child { + flex-shrink: 0; + inline-size: 48px; + block-size: 30px; + border-radius: 4px; + background-size: cover; + display: flex; + justify-content: center; + align-items: center; + + &:hover>i[data-aid] { + display: inline-block; + inline-size: 22px; + block-size: 22px; + background-image: url(//static.hdslb.com/images/base/icons.png); + background-position: -1366px -880px; + + &.d { + background-position: -1436px -880px; + } + } + + &.live { + border-radius: 50%; + block-size: 48px; + } + } + + >:last-child { + flex: 1; + min-inline-size: 0; + line-height: 20px; + display: flex; + flex-direction: column; + + >.title { + color: var(--99a2aa); + display: flex; + column-gap: 1.5em; + overflow: clip; + white-space: nowrap; + text-overflow: ellipsis; + + >a { + color: var(--222); + + &:hover { + color: var(--00a1d6); + } + } + + >.live { + color: var(--e5739e); + } + } + + >:last-child { + color: var(--00a1d6); + overflow: clip; + white-space: nowrap; + text-overflow: ellipsis; + + &:hover { + text-decoration: underline; + } + } + } + } + + &:empty::before { + content: "暂时没有新动态了哦!"; + color: var(--99a2aa); + text-align: center; + } + + &:not(:empty):after { + content: "(´・ω・`) 点击下方按钮查看全部"; + color: var(--99a2aa); + text-align: center; + } + } + + >.wnd_bottom { + line-height: 25px; + margin-block: 10px; + margin-inline: 12px; + border-radius: 4px; + padding: 0; + background-color: var(--e5e9ef); + border: 1px solid var(--e0e6ed); + color: var(--666); + text-align: center; + cursor: pointer; + + &:hover { + background-color: var(--ccd0d7); + } + } +} \ No newline at end of file diff --git a/src/main/html/header/dynamic.ts b/src/main/html/header/dynamic.ts new file mode 100644 index 0000000..c032c91 --- /dev/null +++ b/src/main/html/header/dynamic.ts @@ -0,0 +1,209 @@ +import { toviewAdd } from "../../../io/com/bilibili/api/x/v2/history/toview/add"; +import { toviewDel } from "../../../io/com/bilibili/api/x/v2/history/toview/del"; +import { nav } from "../../../io/com/bilibili/api/x/web-interface/nav"; +import { feed_list } from "../../../io/com/bilibili/live/api/relation/v1/feed/feed_list"; +import { dynamic_new, IDynamicArticle, IDynamicNewCard } from "../../../io/com/bilibili/vc/api/dynamic_svr/v1/dynamic_svr/dynamic_new"; +import { dynamic_num } from "../../../io/com/bilibili/vc/api/dynamic_svr/v1/dynamic_svr/dynamic_num"; +import { toastr } from "../../../toastr"; +import { cookie } from "../../../utils/cookie"; +import { customElement } from "../../../utils/Decorator/customElement"; +import { Element } from "../../../utils/element"; +import { https } from "../../../utils/https"; + +/** 动态 */ +@customElement('div') +export class Dynamic extends HTMLDivElement { + + /** + * 需要监听变动的属性。 + * 与实例方法`attributeChangedCallback`配合使用。 + * 此字符串序列定义了`attributeChangedCallback`回调时的第一个参数的可能值。 + */ + // static observedAttributes = []; + + /** + * 在属性更改、添加、移除或替换时调用。 + * 需要与静态属性`observedAttributes`配合使用。 + * 此回调的第一个参数在`observedAttributes`数组中定义。 + */ + // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} + + /** 每当元素添加到文档中时调用。 */ + // connectedCallback() { } + + /** 每当元素从文档中移除时调用。 */ + // disconnectedCallback() { } + + /** 每当元素被移动到新文档中时调用。 */ + // adoptedCallback() {} + + #menu = Element.add('form', { + class: 'dyn_menu', appendTo: this, innerHTML: ` + + +`}); + + #list = Element.add('div', { class: 'dyn_list', appendTo: this }); + + #wnd = Element.add('a', { class: 'wnd_bottom', appendTo: this, innerText: '查看全部', attribute: { href: '//t.bilibili.com', target: 'blank' } }); + + #mid = 0; + + constructor() { + super(); + + this.classList.add('dynamic-wrap'); + + this.addEventListener('click', e => { + const { target } = e; + if (target instanceof HTMLElement && target.classList.contains('wl')) { + e.preventDefault(); + const d = target.classList.toggle('d'); + target.title = d ? '移除' : '稍后再看'; + const { aid } = target.dataset; + const csrf = cookie.get('bili_jct'); + if (aid && csrf) { + if (d) { + toviewAdd(csrf, aid) + .then(({ code, message }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message } }); + toastr.success(`已添加稍后再看:av${aid}`); + }) + .catch(e => { + target.classList.remove('d'); + target.title = '稍后再看'; + toastr.error('添加稍后再看出错', e) + }) + } else { + toviewDel(csrf, aid) + .then(({ code, message }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message } }); + toastr.success(`已移除稍后再看:av${aid}`); + }) + .catch(e => { + target.classList.add('d'); + target.title = '移除'; + toastr.error('移除稍后再看出错', e); + }) + } + } else { + target.classList.remove('d'); + target.title = '稍后再看'; + } + } + }); + this.#menu.addEventListener('change', () => { + const d = new FormData(this.#menu); + const i = +[...d.values()][0]; + switch (i) { + case 0: { + this.dynamic_video(); + break; + } + case 1: { + this.dynamic_live(); + break; + } + case 2: { + this.dynamic_article(); + break; + } + default: { + this.#list.replaceChildren(); + } + } + }); + + nav() + .then(({ code, message, data }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, data } }); + if (data.isLogin) { + this.classList.add('d'); + this.#mid = data.mid; + // 请求新动态信息 + const id = localStorage.getItem(`bp_t_offset_${data.mid}`); + dynamic_num(data.mid, id || '0', 8, 64, 512).then(d => { + d && ((this.parentElement!.querySelector('.dynamic')).dataset.num = d > 99 ? '99+' : d); + }); + + (this.parentElement!.querySelector('.dynamic')).addEventListener('pointerenter', () => { + (this.parentElement!.querySelector('.dynamic'))?.removeAttribute('data-num'); + this.dynamic_video(); + }, { once: true }); + } + }).catch(e => { + console.error(e); + }); + } + + private dynamic_video() { + this.#mid && dynamic_new(this.#mid || 0, 8, 512) + .then(({ code, message, data }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, data } }); + localStorage.setItem(`bp_t_offset_${this.#mid}`, data.max_dynamic_id); + this.#list.innerHTML = https(data.cards.map(d => { + const card: IDynamicNewCard = JSON.parse(d.card); + return `
    + +
    +
    + ${card.owner.name} + ${d.display.usr_action_txt || '投稿了'} +
    + ${card.title} +
    +
    ` + }).join('')); + }) + .catch(e => { + toastr.error('获取新视频动态出错', e); + console.error(e); + }); + } + + private dynamic_article() { + this.#mid && dynamic_new(this.#mid || 0, 64) + .then(({ code, message, data }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, data } }); + localStorage.setItem(`bp_t_offset_${this.#mid}`, data.max_dynamic_id); + this.#list.innerHTML = https(data.cards.map(d => { + const card: IDynamicArticle = JSON.parse(d.card); + return `
    + +
    +
    + ${card.author.name} + ${d.display.usr_action_txt || '投稿了'} +
    + ${card.title} +
    +
    ` + }).join('')); + }) + .catch(e => { + toastr.error('获取新专栏动态出错', e); + console.error(e); + }); + } + + private dynamic_live() { + this.#mid && feed_list() + .then(({ code, message, data }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, data } }); + this.#list.innerHTML = https(data.list.map(d => `
    + +
    +
    + ${d.uname} + 正在直播 +
    + ${d.title} +
    +
    `).join('')) + }) + .catch(e => { + toastr.error('获取新直播动态失败', e); + console.error(e); + }); + } +} \ No newline at end of file diff --git a/src/main/html/header/favorite.css b/src/main/html/header/favorite.css new file mode 100644 index 0000000..6ee352c --- /dev/null +++ b/src/main/html/header/favorite.css @@ -0,0 +1,83 @@ +.favorite { + anchor-name: --favorite; + + &:not(:hover)~.favorite-wrap:not(:hover) { + display: none; + scale: 1 0; + } +} + +.favorite-wrap { + position: absolute; + position-anchor: --favorite; + position-area: block-end; + inline-size: 320px; + background: var(--fff); + box-shadow: 0 2px 4px var(--00000029); + border-end-start-radius: 4px; + border-end-end-radius: 4px; + display: flex; + flex-direction: column; + transform-origin: center 0; + transition: all .3s allow-discrete; + + @starting-style { + scale: 1 0; + } + + &:not(.d) { + display: none; + } + + >.list { + padding-block-start: 10px; + display: flex; + flex-direction: column; + + >a { + padding-inline: 1em; + line-height: 28px; + color: var(--222); + overflow: clip; + white-space: nowrap; + text-overflow: ellipsis; + display: list-item; + list-style-position: inside; + transition: .2s; + + &:hover { + background-color: var(--e5e9ef); + color: var(--00a1d6); + } + } + } + + >.grp { + line-height: 22px; + margin-block-start: 4px; + margin-block-end: 12px; + margin-inline: 12px; + border-radius: 4px; + border: 1px solid var(--e0e6ed); + background-color: var(--e5e9ef); + color: var(--222); + transition: color .2s; + display: flex; + justify-content: center; + align-items: center; + column-gap: .5em; + + &::after { + content: ""; + display: inline-block; + inline-size: 6px; + block-size: 12px; + background-image: url(//static.hdslb.com/images/base/icons.png); + background-position: -478px -217px; + } + + &:hover { + background-color: var(--ccd0d7); + } + } +} \ No newline at end of file diff --git a/src/main/html/header/favorite.ts b/src/main/html/header/favorite.ts new file mode 100644 index 0000000..ff0fd2c --- /dev/null +++ b/src/main/html/header/favorite.ts @@ -0,0 +1,66 @@ +import { medialistRecent } from "../../../io/com/bilibili/api/medialist/gateway/coll/resource/recent"; +import { nav } from "../../../io/com/bilibili/api/x/web-interface/nav"; +import { toastr } from "../../../toastr"; +import { customElement } from "../../../utils/Decorator/customElement"; +import { Element } from "../../../utils/element"; + +/** 收藏 */ +@customElement('div') +export class Favorite extends HTMLDivElement { + + /** + * 需要监听变动的属性。 + * 与实例方法`attributeChangedCallback`配合使用。 + * 此字符串序列定义了`attributeChangedCallback`回调时的第一个参数的可能值。 + */ + // static observedAttributes = []; + + /** + * 在属性更改、添加、移除或替换时调用。 + * 需要与静态属性`observedAttributes`配合使用。 + * 此回调的第一个参数在`observedAttributes`数组中定义。 + */ + // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} + + /** 每当元素添加到文档中时调用。 */ + // connectedCallback() { } + + /** 每当元素从文档中移除时调用。 */ + // disconnectedCallback() { } + + /** 每当元素被移动到新文档中时调用。 */ + // adoptedCallback() {} + + #list = Element.add('div', { appendTo: this, class: 'list' }); + + #grp = Element.add('a', { appendTo: this, class: 'grp', innerText: '查看更多', attribute: { target: '_blank' } }); + + constructor() { + super(); + + this.classList.add('favorite-wrap'); + + nav() + .then(({ code, message, data }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, data } }); + if (data.isLogin) { + this.classList.add('d'); + const p = this.parentElement!.querySelector('.favorite')!; + p.href = this.#grp.href = `//space.bilibili.com/${data.mid}/favlist`; + p.addEventListener('pointerenter', () => { + data.mid && medialistRecent() + .then(({ code, message, data }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, data } }); + this.#list.innerHTML = data.map(d => `${d.title}`).join(''); + }) + .catch(e => { + toastr.error('获取收藏数据出错', e); + console.error(e) + }); + }, { once: true }); + } + }).catch(e => { + console.error(e); + }); + } +} \ No newline at end of file diff --git a/src/main/html/header/history.css b/src/main/html/header/history.css new file mode 100644 index 0000000..020f28b --- /dev/null +++ b/src/main/html/header/history.css @@ -0,0 +1,143 @@ +.history { + anchor-name: --history; + + &:not(:hover)~.history-wrap:not(:hover) { + display: none; + scale: 1 0; + } +} + +.history-wrap { + position: absolute; + position-anchor: --history; + position-area: block-end; + inline-size: 400px; + background: var(--fff); + box-shadow: 0 2px 4px var(--00000029); + border-end-start-radius: 4px; + border-end-end-radius: 4px; + display: flex; + flex-direction: column; + transform-origin: center 0; + transition: all .3s allow-discrete; + + @starting-style { + scale: 1 0; + } + + &:not(.d) { + display: none; + } + + >.list { + padding-block-start: 10px; + display: flex; + flex-direction: column; + + >.timeline { + block-size: 0; + margin-block: 10px; + border-block-start: 1px solid var(--e5e9ef); + color: var(--99a2aa); + display: flex; + align-items: center; + + >span { + padding-inline: 10px; + background-color: #fff; + } + } + + >a { + padding-inline: 1em; + line-height: 28px; + color: var(--222); + display: flex; + transition: .2s; + + >:first-child { + flex-shrink: 0; + inline-size: 240px; + overflow: clip; + white-space: nowrap; + text-overflow: ellipsis; + display: list-item; + list-style-position: inside; + transition: .2s; + } + + >:last-child { + flex: 1; + min-inline-size: 0; + color: var(--99a2aa); + display: flex; + justify-content: flex-end; + align-items: center; + column-gap: 1em; + + &::after { + content: ""; + display: inline-block; + inline-size: 20px; + block-size: 20px; + background-image: url(//static.hdslb.com/images/base/icons.png); + background-position: -1430px -407px; + } + + &.pc::after { + background-position: -1367px -406px; + } + + &.phone::after { + background-position: -1367px -466px; + } + + &.pad::after { + background-position: -1367px -526px; + } + + &.tv::after { + background-position: -1430px -472px; + } + + } + + &:hover { + background-color: var(--e5e9ef); + + >:first-child { + color: var(--00a1d6); + } + } + } + } + + >.grp { + line-height: 22px; + margin-block-start: 4px; + margin-block-end: 12px; + margin-inline: 12px; + border-radius: 4px; + border: 1px solid var(--e0e6ed); + background-color: var(--e5e9ef); + color: var(--222); + transition: color .2s; + display: flex; + justify-content: center; + align-items: center; + column-gap: .5em; + + &::after { + content: ""; + display: inline-block; + inline-size: 6px; + block-size: 12px; + background: url(//static.hdslb.com/images/base/icons.png); + background-position: -478px -217px; + } + + &:hover { + background-color: var(--ccd0d7); + } + } +} \ No newline at end of file diff --git a/src/main/html/header/history.ts b/src/main/html/header/history.ts new file mode 100644 index 0000000..500dc39 --- /dev/null +++ b/src/main/html/header/history.ts @@ -0,0 +1,82 @@ +import { history } from "../../../io/com/bilibili/api/x/v2/history"; +import { nav } from "../../../io/com/bilibili/api/x/web-interface/nav"; +import { toastr } from "../../../toastr"; +import { customElement } from "../../../utils/Decorator/customElement"; +import { Element } from "../../../utils/element"; +import { https } from "../../../utils/https"; + +/** 历史记录 */ +@customElement('div') +export class History extends HTMLDivElement { + + /** + * 需要监听变动的属性。 + * 与实例方法`attributeChangedCallback`配合使用。 + * 此字符串序列定义了`attributeChangedCallback`回调时的第一个参数的可能值。 + */ + // static observedAttributes = []; + + /** + * 在属性更改、添加、移除或替换时调用。 + * 需要与静态属性`observedAttributes`配合使用。 + * 此回调的第一个参数在`observedAttributes`数组中定义。 + */ + // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} + + /** 每当元素添加到文档中时调用。 */ + // connectedCallback() { } + + /** 每当元素从文档中移除时调用。 */ + // disconnectedCallback() { } + + /** 每当元素被移动到新文档中时调用。 */ + // adoptedCallback() {} + + #list = Element.add('div', { appendTo: this, class: 'list' }); + + #grp = Element.add('a', { appendTo: this, class: 'grp', innerText: '查看更多', attribute: { target: '_blank', href: '//www.bilibili.com/account/history' } }); + + constructor() { + super(); + + this.classList.add('history-wrap'); + + nav() + .then(({ code, message, data }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, data } }); + if (data.isLogin) { + this.classList.add('d'); + (this.parentElement!.querySelector('.history')).addEventListener('pointerenter', () => { + if (data.mid) { + const t = new Date(); + const now = new Date(t.getFullYear(), t.getMonth(), t.getDate()).getTime(); + const pasts: string[] = []; + history() + .then(({ code, message, data }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, data } }); + this.#list.innerHTML = https(data.map(d => { + const past = this.past(now - d.view_at * 1e3); + const label = pasts.includes(past); + label || pasts.push(past); + const url = new URL(d.redirect_url || d.redirect_link); + d.redirect_url || (d.page && d.page.page > 1 && url.searchParams.set('p', d.page.page)); + d.progress > 0 && url.searchParams.set('t', d.progress); + return `${label ? '' : `
    ${past}
    `}
    ${d.title}
    ${d.page && d.page.page > 1 ? `第${d.page.page}P|` : ''}${d.progress ? d.progress > 0 ? Math.floor(d.progress / d.duration * 100) : '100' : '1'}%
    `; + }).join('')); + }) + .catch(e => { + toastr.error('获取历史记录出错', e); + console.error(e); + }) + } + }, { once: true }); + } + }).catch(e => { + console.error(e); + }); + } + + private past(tsp: number) { + return tsp <= 0 ? "今日" : tsp > 0 && tsp <= 864e5 ? "昨日" : tsp > 864e5 && tsp <= 6048e5 ? "近1周" : tsp > 6048e5 && tsp <= 2592e6 ? "1周前" : tsp > 2592e6 && tsp <= 7776e6 ? "1个月前" : "last"; + } +} \ No newline at end of file diff --git a/src/main/html/header/index.css b/src/main/html/header/index.css new file mode 100644 index 0000000..78e87f5 --- /dev/null +++ b/src/main/html/header/index.css @@ -0,0 +1,320 @@ +@import url(./avatar.css); +@import url(./message.css); +@import url(./dynamic.css); +@import url(./toview.css); +@import url(./favorite.css); +@import url(./history.css); +@import url(./ulink.css); +@import url(./search.css); + +@scope { + :scope { + color-scheme: light dark; + + --eee: #eee; + --222: #222; + --fff6: #fff6; + --0000001a: #0000001a; + --ffffff4d: #ffffff4d; + --000: #000; + --00a1d6: #00a1d6; + --23ade5: #23ade5; + --fff: #fff; + --f45a8d: #f45a8d; + --fb7299: #fb7299; + --ffafc9: #ffafc9; + --00000029: #00000029; + --e5e9ef: #e5e9ef; + --f3cb85: #f3cb85; + --ccc: #ccc; + --f4f5f7: #f4f5f7; + --f25d8e: #f25d8e; + --e0e6ed: #e0e6ed; + --666: #666; + --ccd0d7: #ccd0d7; + --99a2aa: #99a2aa; + --0000001f: #0000001f; + --ffffffe0: #ffffffe0; + --e5739e: #e5739e; + + display: flex; + flex-direction: column; + font-size: 12px; + min-inline-size: 980px; + box-sizing: border-box; + + &.mini { + .head-banner { + block-size: 42px; + background-color: transparent; + background-image: unset !important; + + >div { + display: none; + } + } + + .nav-menu { + z-index: 99; + } + + .primary-menu { + display: none; + } + + .search { + display: none; + } + } + } + + a { + text-decoration: none; + } +} + +.head-banner { + background-size: cover; + background-position: center; + background-repeat: no-repeat; + background-color: var(--eee); + aspect-ratio: 32 / 3; + anchor-name: --head-banner; + container: banner / size; + position: relative; + overflow: clip; + + >.layer { + position: absolute; + inset: 0; + inline-size: 100%; + block-size: 100%; + display: flex; + align-items: center; + justify-content: center; + + >img, + >video { + inline-size: 100%; + block-size: 100%; + } + } +} + +.nav-menu { + position: absolute; + inset-block-start: anchor(--head-banner start); + inset-inline-start: anchor(--head-banner start); + inset-inline-end: anchor(--head-banner end); + block-size: 42px; + color: var(--222); + background-color: var(--fff6); + box-shadow: 0 1px 2px var(--0000001a); + display: flex; + z-index: 1; + + >.nav-wrap { + inline-size: 980px; + margin-inline: auto; + display: flex; + justify-content: space-between; + + @media screen and (min-width:1400px) { + + & { + inline-size: 1160px; + } + } + + @media screen and (min-width:2500px) { + + & { + inline-size: 1920px; + } + } + + >.nav-organ { + display: flex; + + >a { + block-size: 42px; + padding-inline: 7px; + color: var(--222); + white-space: nowrap; + text-align: center; + align-content: center; + + + &:hover { + background-color: var(--ffffff4d); + } + + &.home, + &.mobile { + display: flex; + align-items: center; + column-gap: .5em; + + >svg { + block-size: 20px; + fill: var(--00a1d6); + } + } + + &.mobile>svg { + block-size: 16px; + fill: var(--23ade5); + } + + &.u-link { + inline-size: 68px; + border-end-start-radius: 6px; + border-end-end-radius: 6px; + background-color: var(--f45a8d); + font-size: 14px; + color: var(--fff); + + &:hover { + background-color: var(--fb7299); + } + } + } + } + } +} + +.primary-menu { + inline-size: 980px; + block-size: 50px; + margin-block-end: 4px; + margin-inline: auto; + border-block-end: 1px solid var(--fff); + padding-block-start: 8px; + display: flex; + position: relative; + + @media screen and (min-width:1400px) { + + & { + inline-size: 1160px; + } + } + + @media screen and (min-width:2500px) { + + & { + inline-size: 1920px; + } + } + + >.menu { + flex: 1; + min-inline-size: 0; + display: flex; + justify-content: space-between; + align-items: flex-end; + + >a { + inline-size: 48px; + color: var(--222); + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + position: relative; + + &::before { + content: attr(data-online); + padding-block: 1px; + padding-inline: 3px; + border-radius: 3px; + font-size: 9px; + color: var(--fff); + background-color: var(--ffafc9); + } + + &.position::before { + content: " "; + block-size: 18px; + aspect-ratio: 1; + background: url(//static.hdslb.com/images/base/icons.png) no-repeat; + background-position: var(--background-position); + } + + .sub-item { + position: absolute; + inset-block-start: 100%; + inset-inline-start: 0; + color: var(--222); + background-color: var(--fff); + box-shadow: 0 2px 4px var(--00000029); + border-end-start-radius: 4px; + border-end-end-radius: 4px; + display: flex; + flex-direction: column; + z-index: 1; + transform-origin: center 0; + transition: all .3s allow-discrete; + + @starting-style { + opacity: 0; + scale: 1 0; + } + + >a { + min-inline-size: 120px; + block-size: 30px; + padding-block: 5px; + box-sizing: border-box; + display: flex; + align-items: center; + color: var(--222); + + &::before { + content: ">"; + color: var(--00a1d6); + padding-inline-start: 10px; + padding-inline-end: 5px; + transition: all .2s; + } + + &::after { + content: "<"; + color: var(--00a1d6); + padding-inline-start: 15px; + opacity: 0; + transition: all .2s; + } + + &:hover { + background-color: var(--e5e9ef); + + &::before { + padding-inline-start: 15px; + } + + &::after { + padding-inline-start: 5px; + opacity: 1; + } + } + } + } + + &:not(:hover) .sub-item { + display: none; + opacity: 0; + scale: 1 0; + } + } + } + + >.menu-gif { + flex-shrink: 0; + inline-size: 69px; + margin-block-start: 3px; + padding-block-start: 4px; + display: flex; + justify-content: center; + } +} \ No newline at end of file diff --git a/src/main/html/header/index.d.css.ts b/src/main/html/header/index.d.css.ts new file mode 100644 index 0000000..eb9bc4f --- /dev/null +++ b/src/main/html/header/index.d.css.ts @@ -0,0 +1,2 @@ +declare const stylesheet: CSSStyleSheet; +export default stylesheet; \ No newline at end of file diff --git a/src/main/html/header/index.ts b/src/main/html/header/index.ts new file mode 100644 index 0000000..f5801aa --- /dev/null +++ b/src/main/html/header/index.ts @@ -0,0 +1,178 @@ +import svg_bilibili_tv from "../../../assets/svg/bilibili-tv.svg"; +import svg_Navbar_mobile from "../../../assets/svg/Navbar_mobile.svg"; +import { header, ISplitLayer } from "../../../io/com/bilibili/api/x/web-show/page/header"; +import { customElement } from "../../../utils/Decorator/customElement"; +import { Element } from "../../../utils/element"; +import { https } from "../../../utils/https"; +import { MAIN_EVENT, mainEv } from "../../event"; +import { ROUTER } from "../../router"; +import channelList from "./channel-list.json"; +import navGif from "./nav-gift.json" +import stylesheet from "./index.css" with {type: 'css'}; +import { Format } from "../../../utils/fomat"; +import { online } from "../../../io/com/bilibili/api/x/web-interface/online"; +import { Avatar } from "./avatar"; +import { Message } from "./message"; +import { Dynamic } from "./dynamic"; +import { Toview } from "./toview"; +import { Favorite } from "./favorite"; +import { History } from "./history"; +import { Ulink } from "./ulink"; +import { Search } from "./search"; + +/** 顶栏模块 */ +@customElement(undefined, `header-${Date.now()}`) +export class Header extends HTMLElement { + + #host = this.attachShadow({ mode: 'closed' }); + + #banner = Element.add('div', { class: 'head-banner', appendTo: this.#host }); + + #nav = Element.add('div', { class: 'nav-menu', appendTo: this.#host }); + + #navWarp = Element.add('div', { class: 'nav-wrap', appendTo: this.#nav }); + + #navLeft = Element.add('div', { + class: 'nav-organ', appendTo: this.#navWarp, innerHTML: `${svg_bilibili_tv}主站 +游戏中心 +直播 +会员购 +漫画 +赛事 +${svg_Navbar_mobile}下载APP` + }); + + #navRight = Element.add('div', { + class: 'nav-organ', appendTo: this.#navWarp, innerHTML: `大会员 +消息 +动态 +稍后再看 +收藏 +历史 +创作中心 +投稿` + }); + + #avatar = this.#navRight.insertAdjacentElement('afterbegin', new Avatar()); + + #message = this.#navRight.appendChild(new Message()); + + #dynamic = this.#navRight.appendChild(new Dynamic()); + + #toview = this.#navRight.appendChild(new Toview()); + + #favorite = this.#navRight.appendChild(new Favorite()); + + #history = this.#navRight.appendChild(new History()); + + #ulink = this.#navRight.appendChild(new Ulink()); + + + #primary = Element.add('div', { class: 'primary-menu', appendTo: this.#host }); + + #menu = Element.add('div', { class: 'menu', appendTo: this.#primary }); + + #gif = Element.add('a', { class: 'menu-gif', appendTo: this.#primary }); + + #search = this.#primary.appendChild(new Search()); + + constructor() { + super(); + + this.#host.adoptedStyleSheets = [stylesheet]; + + mainEv.bind(MAIN_EVENT.NAVIGATE, ({ detail }) => { this.$navigate(...detail) }); + + // 导航分区 + channelList.forEach(({ url, name, tid, position, sub }) => { + const a = Element.add('a', { appendTo: this.#menu, attribute: { href: url }, innerText: name, data: { online: '...' } }); + if (position) { + a.classList.add('position'); + a.style.setProperty('--background-position', `${position[0]}px ${position[1]}px`); + } + tid && (a.dataset.tid = tid); + sub && a.insertAdjacentHTML('beforeend', `
    ${sub.map(d => `${d.name}`).join('')}
    `); + }) + // 随机推荐动图 + const gif = Format.subArray(navGif); + this.#gif.target = "_blank"; + this.#gif.href = gif.links[0]; + this.#gif.innerHTML = ``; + this.#gif.title = gif.title; + + // 获取各分区在线人数 + online() + .then(({ code, message, data }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, data } }); + const online = data.region_count; + Array.from(this.#menu.childNodes).forEach(d => { + const tid = (d).dataset.tid; + if (tid && online[tid]) { + (d).dataset.online = online[tid] > 999 ? '999+' : online[tid]; + } + }) + }).catch(e => { + console.error('获取在线人数失败', e) + }); + } + + #resource_id = 0; + + set $resource_id(v: number) { + if (this.#resource_id !== v) { + this.#resource_id = v; + header(v) + .then(({ code, message, data }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, data } }); + this.#banner.style.backgroundImage = `url(${https(data.pic)}@.webp)`; + // 使用动画资源 + const animatedBannerConfig: ISplitLayer = JSON.parse(data.split_layer); + for (const { resources, scale, rotate, translate, blur, opacity } of animatedBannerConfig.layers) { + const div = Element.add('div', { class: 'layer', appendTo: this.#banner }); + for (const { id, src } of resources) { + const $scale = (scale?.initial || 0) + (scale?.offset || 0); + const $rotate = (rotate?.initial || 0) + (rotate?.offset || 0); + const $translate = [((translate?.initial?.[0] || 0) + (translate?.offset?.[0] || 0)) * ($scale || 1), (translate?.initial?.[1] || 0) + (translate?.offset?.[1] || 0) * ($scale || 1)]; + const $blur = blur?.wrap === 'alternate' ? Math.abs((blur.initial || 0) + (blur.offset || 0)) : Math.max(0, (blur?.initial || 0) + (blur?.offset || 0)); + const x = (opacity?.initial === undefined ? 1 : opacity.initial) + (opacity?.offset || 0); + let y = Math.abs(x % 1); + if (Math.abs(x % 2) >= 1) { + y = 1 - y; + } + if (/\.(webm|mp4)$/.test(src)) { + const video = Element.add('video', { attribute: { src, id: id }, appendTo: div },); + video.muted = true; + video.autoplay = true; + video.loop = true; + video.playsInline = true; + + $rotate && (video.style.rotate = $rotate + 'deg'); + ($translate[0] || $translate[1]) && (video.style.translate = `calc(100cqb / 155 * (${$translate[0]})) calc(100cqb / 155 * (${$translate[1]}))`); + ($blur < 1e-4) || (video.style.filter = `blur(${$blur}px)`); + video.style.opacity = opacity?.wrap === 'alternate' ? y : Math.max(0, Math.min(1, x)); + } else { + const img = Element.add('img', { attribute: { src, id: id }, appendTo: div },); + $rotate && (img.style.rotate = $rotate + 'deg'); + // ($translate[0] || $translate[1]) && (img.style.translate = `calc(100cqb / 155 * (${$translate[0]})) calc(100cqb / 155 * (${$translate[1]}))`); + ($blur < 1e-4) || (img.style.filter = `blur(${$blur}px)`); + img.style.opacity = opacity?.wrap === 'alternate' ? y : Math.max(0, Math.min(1, x)); + } + } + } + }) + .catch(e => { + console.error('获取顶栏 Banner 失败~', e); + }) + } + } + + /** 页面路由 */ + private async $navigate(router: ROUTER, url = new URL(location.href)) { + switch (router) { + default: { + this.$resource_id = 142; + break; + } + } + } +} \ No newline at end of file diff --git a/src/main/html/header/message.css b/src/main/html/header/message.css new file mode 100644 index 0000000..d1ebd9d --- /dev/null +++ b/src/main/html/header/message.css @@ -0,0 +1,70 @@ +.message { + anchor-name: --message; + position: relative; + + &[data-num]::after { + content: attr(data-num); + position: absolute; + color: var(--fff); + background-color: var(--f25d8e); + inset-block-start: 0; + inset-inline-end: 0; + line-height: 1; + border-radius: 10px; + padding-block: .1em; + padding-inline: .5em; + } + + &:not(:hover)~.message-wrap:not(:hover) { + display: none; + scale: 1 0; + } + +} + +.message-wrap { + position: absolute; + position-anchor: --message; + position-area: block-end; + inline-size: 110px; + background: var(--fff); + box-shadow: 0 2px 4px var(--00000029); + border-end-start-radius: 4px; + border-end-end-radius: 4px; + display: flex; + flex-direction: column; + transform-origin: center 0; + transition: all .3s allow-discrete; + + @starting-style { + scale: 1 0; + } + + &:not(.d) { + display: none; + } + + >a { + line-height: 42px; + color: inherit; + text-align: center; + position: relative; + + &[data-num]::after { + content: attr(data-num); + position: absolute; + color: var(--fff); + background-color: var(--f25d8e); + line-height: 1; + inset-inline-end: 0; + border-radius: 10px; + padding-block: .1em; + padding-inline: .5em; + } + + &:hover { + color: var(--00a1d6); + background-color: var(--e5e9ef); + } + } +} \ No newline at end of file diff --git a/src/main/html/header/message.ts b/src/main/html/header/message.ts new file mode 100644 index 0000000..a518667 --- /dev/null +++ b/src/main/html/header/message.ts @@ -0,0 +1,70 @@ +import { unread } from "../../../io/com/bilibili/api/x/msgfeed/unread"; +import { nav } from "../../../io/com/bilibili/api/x/web-interface/nav"; +import { customElement } from "../../../utils/Decorator/customElement"; + +/** 消息 */ +@customElement('div') +export class Message extends HTMLDivElement { + + /** + * 需要监听变动的属性。 + * 与实例方法`attributeChangedCallback`配合使用。 + * 此字符串序列定义了`attributeChangedCallback`回调时的第一个参数的可能值。 + */ + // static observedAttributes = []; + + /** + * 在属性更改、添加、移除或替换时调用。 + * 需要与静态属性`observedAttributes`配合使用。 + * 此回调的第一个参数在`observedAttributes`数组中定义。 + */ + // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} + + /** 每当元素添加到文档中时调用。 */ + // connectedCallback() { } + + /** 每当元素从文档中移除时调用。 */ + // disconnectedCallback() { } + + /** 每当元素被移动到新文档中时调用。 */ + // adoptedCallback() {} + + constructor() { + super(); + + this.classList.add('message-wrap'); + this.innerHTML = `回复我的 +@我的 +收到的赞 +系统通知 +我的消息`; + + this.addEventListener('pointerover', ({ target }) => { + (this.parentElement!.querySelector('.message'))?.removeAttribute('data-num'); + if (target instanceof HTMLAnchorElement) { + target.removeAttribute('data-num'); + } + }); + + nav() + .then(({ code, message, data }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, data } }); + if (data.isLogin) { + this.classList.add('d'); + // 请求新消息信息 + unread().then(({ code, message, data }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, data } }); + const { reply, at, like, sys_msg } = data; + const all = reply + at + like + sys_msg; + reply && ((this.children[0]).dataset.num = reply > 99 ? '99+' : reply); + at && ((this.children[1]).dataset.num = at > 99 ? '99+' : at); + like && ((this.children[2]).dataset.num = like > 99 ? '99+' : like); + sys_msg && ((this.children[3]).dataset.num = sys_msg > 99 ? '99+' : sys_msg); + all && ((this.parentElement!.querySelector('.message')).dataset.num = all > 99 ? '99+' : all); + }); + } + }).catch(e => { + console.error(e); + }); + } +} \ No newline at end of file diff --git a/src/main/bilibili/assets/nav-gift.json b/src/main/html/header/nav-gift.json similarity index 100% rename from src/main/bilibili/assets/nav-gift.json rename to src/main/html/header/nav-gift.json diff --git a/src/main/html/header/search.css b/src/main/html/header/search.css new file mode 100644 index 0000000..5212dda --- /dev/null +++ b/src/main/html/header/search.css @@ -0,0 +1,149 @@ +.search { + position: absolute; + inset-block-end: 70px; + inset-inline-end: 0; + border-radius: 6px; + background-color: var(--0000001f); + display: flex; + column-gap: 4px; + + >a { + padding-inline: 3px; + border-radius: 4px; + color: var(--f25d8e); + background-color: var(--ffffffe0); + transition: background-color .2s; + display: flex; + align-items: center; + + &::before { + content: ""; + display: inline-block; + inline-size: 26px; + block-size: 26px; + background: url(//static.hdslb.com/images/base/icons.png) -659px -657px no-repeat; + } + + &:hover { + background-color: var(--fff); + } + } + + >form { + display: flex; + background-color: var(--ffffffe0); + border-radius: 4px; + transition: background-color .2s; + anchor-name: --keyword; + + &:hover { + background-color: var(--fff); + } + + >input { + inline-size: 200px; + line-height: 32px; + outline: 0; + border: 0; + padding-inline: 12px; + color: var(--222); + box-shadow: none; + background-color: transparent; + } + + >button { + inline-size: 48px; + margin: 0; + padding: 0; + border: 0; + background: url(//static.hdslb.com/images/base/icons.png) -653px -720px; + cursor: pointer; + + &:hover { + background-position: -718px -720px; + } + } + + &:not(:has(:focus))~.suggest:not(:hover) { + display: none; + scale: 1 0; + } + } + + >.suggest { + position: absolute; + position-anchor: --keyword; + position-area: block-end; + inline-size: anchor-size(inline); + margin-block-start: 2px; + border: 1px solid var(--e5e9ef); + border-radius: 4px; + box-shadow: 0 2px 4px var(--00000029); + background-color: var(--fff); + display: flex; + flex-direction: column; + + transform-origin: center 0; + transition: all .3s allow-discrete; + + @starting-style { + scale: 1 0; + } + + >.line { + border-block-start: 1px solid var(--e5e9ef); + block-size: 0; + margin-inline: 10px; + margin-block: 10px; + display: flex; + align-items: center; + justify-content: center; + + >span { + padding-inline: 10px; + color: var(--99a2aa); + background-color: var(--fff); + } + } + + >.item { + padding-block: 0; + padding-inline: 10px; + display: flex; + align-items: center; + cursor: pointer; + + >:first-child { + flex: 1; + min-inline-size: 0; + block-size: 28px; + color: var(--222); + align-content: center; + overflow: clip; + white-space: nowrap; + text-overflow: ellipsis; + pointer-events: none; + } + + >.cancel { + flex-shrink: 0; + inline-size: 38px; + block-size: 28px; + background: url(//static.hdslb.com/images/base/icons.png) -461px -530px no-repeat; + } + + &:hover { + background-color: var(--e5e9ef); + + >:last-child { + background-position: -525px -530px; + } + } + } + + &:empty { + display: none; + scale: 1 0; + } + } +} \ No newline at end of file diff --git a/src/main/html/header/search.ts b/src/main/html/header/search.ts new file mode 100644 index 0000000..76f98b2 --- /dev/null +++ b/src/main/html/header/search.ts @@ -0,0 +1,146 @@ +import { searchDefault } from "../../../io/com/bilibili/api/x/web-interface/search/default"; +import { searchSuggest } from "../../../io/com/bilibili/search/s/main/suggest"; +import { cookie } from "../../../utils/cookie"; +import { customElement } from "../../../utils/Decorator/customElement"; +import { Element } from "../../../utils/element"; + +/** 搜索 */ +@customElement('div') +export class Search extends HTMLDivElement { + + /** + * 需要监听变动的属性。 + * 与实例方法`attributeChangedCallback`配合使用。 + * 此字符串序列定义了`attributeChangedCallback`回调时的第一个参数的可能值。 + */ + // static observedAttributes = []; + + /** + * 在属性更改、添加、移除或替换时调用。 + * 需要与静态属性`observedAttributes`配合使用。 + * 此回调的第一个参数在`observedAttributes`数组中定义。 + */ + // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} + + /** 每当元素添加到文档中时调用。 */ + // connectedCallback() { } + + /** 每当元素从文档中移除时调用。 */ + // disconnectedCallback() { } + + /** 每当元素被移动到新文档中时调用。 */ + // adoptedCallback() {} + + #rank = Element.add('a', { appendTo: this, attribute: { href: '//www.bilibili.com/ranking', target: '_blank' }, innerText: '排行榜' }); + + #form = Element.add('form', { appendTo: this }); + + #input = Element.add('input', { appendTo: this.#form, attribute: { type: 'text', autocomplete: 'off', name: 'keyword' } }); + + #button = Element.add('button', { appendTo: this.#form }); + + #suggest = Element.add('div', { appendTo: this, class: 'suggest' }); + + #composition = true; + + #url = ''; + + #abortSignal?: AbortController; + + constructor() { + super(); + + this.classList.add('search'); + this.#form.action = '//search.bilibili.com/all'; + this.#form.method = 'get'; + this.#form.target = '_blank'; + + this.#form.addEventListener('compositionstart', () => { + this.#composition = false; + }); + this.#form.addEventListener('compositionend', () => { + this.#composition = true; + this.onInput(); + }); + this.#form.addEventListener('input', this.onInput); + this.#form.addEventListener('submit', e => { + if (!this.#input.value) { + e.preventDefault(); + self.open(this.#url || '//search.bilibili.com'); + } else { + try { + const data = JSON.parse(localStorage.getItem('search_history')!); + const newDate = data.filter(d => d.value !== this.#input.value); + newDate.push({ isHistory: 1, value: this.#input.value, timestamp: Date.now() }); + localStorage.setItem('search_history', JSON.stringify(newDate)); + } catch { + localStorage.setItem('search_history', JSON.stringify([{ isHistory: 1, value: this.#input.value, timestamp: Date.now() }])); + } + } + }); + this.#suggest.addEventListener('click', ({ target }) => { + if (target instanceof HTMLDivElement && target.classList.contains('item')) { + const { value } = target.dataset; + if (value) { + this.#input.value = value; + this.#button.click(); + } + } else if (target instanceof HTMLElement && target.classList.contains('cancel')) { + target.parentElement?.remove(); + try { + const { ts } = target.dataset; + if (ts) { + const data = JSON.parse(localStorage.getItem('search_history')!); + localStorage.setItem('search_history', JSON.stringify(data.filter(d => d.timestamp !== +ts))); + } + } catch { + localStorage.removeItem('search_history'); + } + } + }); + + searchDefault() + .then(({ code, message, data }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, data } }); + this.#input.placeholder = data.show_name || data.name; + this.#url = data.url; + }) + .catch(console.error); + + this.initHistory(); + + } + + private onInput = () => { + if (this.#composition) { + if (this.#input.value) { + this.#abortSignal?.abort(); + this.#abortSignal = new AbortController(); + searchSuggest(this.#input.value, cookie.get('DedeUserID') || '0', this.#abortSignal.signal) + .then(({ code, message, result }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, result } }); + this.#suggest.innerHTML = result.tag.map(d => `
    ${d.term}
    `).join(''); + }) + .catch(console.error) + } else { + this.initHistory(); + } + } + } + + /** 获取本地历史记录 */ + private initHistory() { + try { + const data = JSON.parse(localStorage.getItem('search_history')!); + this.#suggest.innerHTML = '
    历史搜索
    ' + data.map(d => `
    ${d.value}
    `).join(''); + } catch { + localStorage.removeItem('search_history'); + } + } +} + +interface ISearchHistory { + isHistory: number; + timestamp: number; + value: string; +} \ No newline at end of file diff --git a/src/main/html/header/toview.css b/src/main/html/header/toview.css new file mode 100644 index 0000000..e1f5c18 --- /dev/null +++ b/src/main/html/header/toview.css @@ -0,0 +1,89 @@ +.toview { + anchor-name: --toview; + + &:not(:hover)~.toview-wrap:not(:hover) { + display: none; + scale: 1 0; + } +} + +.toview-wrap { + position: absolute; + position-anchor: --toview; + position-area: block-end; + inline-size: 320px; + background: var(--fff); + box-shadow: 0 2px 4px var(--00000029); + border-end-start-radius: 4px; + border-end-end-radius: 4px; + display: flex; + flex-direction: column; + transform-origin: center 0; + transition: all .3s allow-discrete; + + @starting-style { + scale: 1 0; + } + + &:not(.d) { + display: none; + } + + >.list { + padding-block-start: 10px; + display: flex; + flex-direction: column; + + >a { + padding-inline: 1em; + line-height: 28px; + color: var(--222); + overflow: clip; + white-space: nowrap; + text-overflow: ellipsis; + display: list-item; + list-style-position: inside; + transition: .2s; + + &:hover { + background-color: var(--e5e9ef); + color: var(--00a1d6); + } + } + } + + >.grp { + display: flex; + padding-block-start: 4px; + padding-block-end: 12px; + padding-inline: 12px; + column-gap: 12px; + + >a { + inline-size: 140px; + line-height: 22px; + border-radius: 4px; + border: 1px solid var(--e0e6ed); + background-color: var(--e5e9ef); + color: var(--222); + transition: color .2s; + display: flex; + justify-content: center; + align-items: center; + column-gap: .5em; + + &::after { + content: ""; + display: inline-block; + inline-size: 6px; + block-size: 12px; + background-image: url(//static.hdslb.com/images/base/icons.png); + background-position: -478px -217px; + } + + &:hover { + background-color: var(--ccd0d7); + } + } + } +} \ No newline at end of file diff --git a/src/main/html/header/toview.ts b/src/main/html/header/toview.ts new file mode 100644 index 0000000..e5add2c --- /dev/null +++ b/src/main/html/header/toview.ts @@ -0,0 +1,65 @@ +import { toviewWeb } from "../../../io/com/bilibili/api/x/v2/history/toview/web"; +import { nav } from "../../../io/com/bilibili/api/x/web-interface/nav"; +import { toastr } from "../../../toastr"; +import { customElement } from "../../../utils/Decorator/customElement"; +import { Element } from "../../../utils/element"; +import { https } from "../../../utils/https"; + +/** 稍后再看 */ +@customElement('div') +export class Toview extends HTMLDivElement { + + /** + * 需要监听变动的属性。 + * 与实例方法`attributeChangedCallback`配合使用。 + * 此字符串序列定义了`attributeChangedCallback`回调时的第一个参数的可能值。 + */ + // static observedAttributes = []; + + /** + * 在属性更改、添加、移除或替换时调用。 + * 需要与静态属性`observedAttributes`配合使用。 + * 此回调的第一个参数在`observedAttributes`数组中定义。 + */ + // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} + + /** 每当元素添加到文档中时调用。 */ + // connectedCallback() { } + + /** 每当元素从文档中移除时调用。 */ + // disconnectedCallback() { } + + /** 每当元素被移动到新文档中时调用。 */ + // adoptedCallback() {} + + #list = Element.add('div', { appendTo: this, class: 'list' }); + + #grp = Element.add('div', { appendTo: this, class: 'grp', innerHTML: '播放全部查看全部' }); + + constructor() { + super(); + + this.classList.add('toview-wrap'); + + nav() + .then(({ code, message, data }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, data } }); + if (data.isLogin) { + this.classList.add('d'); + (this.parentElement!.querySelector('.toview')).addEventListener('pointerenter', () => { + data.mid && toviewWeb(6) + .then(({ code, message, data }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, data } }); + this.#list.innerHTML = https(data.list.map(d => `${d.title}`).join('')); + }) + .catch(e => { + toastr.error('获取稍后再看数据出错', e); + console.error(e) + }) + }, { once: true }); + } + }).catch(e => { + console.error(e); + }); + } +} \ No newline at end of file diff --git a/src/main/html/header/ulink.css b/src/main/html/header/ulink.css new file mode 100644 index 0000000..6a93cb2 --- /dev/null +++ b/src/main/html/header/ulink.css @@ -0,0 +1,69 @@ +.u-link { + anchor-name: --u-link; + + &:not(:hover)~.u-link-wrap:not(:hover) { + display: none; + scale: 1 0; + } +} + +.u-link-wrap { + position: absolute; + position-anchor: --u-link; + position-area: block-end span-inline-start; + background: var(--fff); + box-shadow: 0 2px 4px var(--00000029); + border-end-start-radius: 4px; + border-end-end-radius: 4px; + display: flex; + transform-origin: center 0; + transition: all .3s allow-discrete; + + @starting-style { + scale: 1 0; + } + + >a { + inline-size: 68px; + block-size: 64px; + color: var(--f25d8e); + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + transition: .2s; + + &::before { + content: ""; + display: inline-block; + inline-size: 20px; + block-size: 20px; + background-image: url(//static.hdslb.com/images/base/icons.png); + transition: .2s; + } + + &.i-art::before { + background-position: -534px -919px; + } + + &.i-ap::before { + background-position: -534px -983px; + } + + &.i-vp::before { + background-position: -471px -919px; + } + + &.i-vm::before { + background-position: -471px -982px; + } + + &:hover { + background-color: var(--e5e9ef); + + &::before { + translate: 0 -2px; + } + } + } +} \ No newline at end of file diff --git a/src/main/html/header/ulink.ts b/src/main/html/header/ulink.ts new file mode 100644 index 0000000..16020ac --- /dev/null +++ b/src/main/html/header/ulink.ts @@ -0,0 +1,39 @@ +import { customElement } from "../../../utils/Decorator/customElement"; + +/** 投稿 */ +@customElement('div') +export class Ulink extends HTMLDivElement { + + /** + * 需要监听变动的属性。 + * 与实例方法`attributeChangedCallback`配合使用。 + * 此字符串序列定义了`attributeChangedCallback`回调时的第一个参数的可能值。 + */ + // static observedAttributes = []; + + /** + * 在属性更改、添加、移除或替换时调用。 + * 需要与静态属性`observedAttributes`配合使用。 + * 此回调的第一个参数在`observedAttributes`数组中定义。 + */ + // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} + + /** 每当元素添加到文档中时调用。 */ + // connectedCallback() { } + + /** 每当元素从文档中移除时调用。 */ + // disconnectedCallback() { } + + /** 每当元素被移动到新文档中时调用。 */ + // adoptedCallback() {} + + constructor() { + super(); + + this.classList.add('u-link-wrap'); + this.innerHTML = `专栏投稿 +音频投稿 +视频投稿 +投稿管理`; + } +} \ No newline at end of file diff --git a/src/main/html/home/chief.css b/src/main/html/home/chief.css new file mode 100644 index 0000000..91bc3ad --- /dev/null +++ b/src/main/html/home/chief.css @@ -0,0 +1,210 @@ +.chief { + display: grid; + grid-template-columns: repeat(5, 1fr); + grid-template-rows: repeat(2, 1fr); + grid-template-areas: + "ca ca rc rc rc" + "ca ca rc rc rc"; + grid-auto-rows: 0; + gap: 20px; + overflow-y: clip; + position: relative; + + @media screen and (min-width:1400px) { + + & { + grid-template-columns: repeat(6, 1fr); + grid-template-areas: + "ca ca rc rc rc rc" + "ca ca rc rc rc rc"; + } + } + + .carousel { + grid-area: ca; + position: relative; + + >.box { + aspect-ratio: 16 / 9; + position: relative; + container: carousel / inline-size; + overflow: clip; + anchor-name: --carousel; + + >a { + position: absolute; + inline-size: 100%; + block-size: 100%; + background-size: cover; + background-repeat: no-repeat; + border-radius: 4px; + transition: all 0.7s allow-discrete; + + &:not(.show) { + /* 隐藏样式:显示区域左侧外部 */ + translate: -100%; + display: none; + } + + @starting-style { + /* 初始样式:显示区域右侧外部 */ + translate: 100cqi; + } + + &::after { + content: attr(title); + position: absolute; + display: block; + inset-block-end: 0; + inset-inline: 0; + line-height: 35px; + padding-inline: 10px; + font-size: 14px; + color: var(--fff); + background-image: linear-gradient(transparent, var(--00000080)); + overflow: clip; + white-space: nowrap; + text-overflow: ellipsis; + } + } + } + + >form { + position: absolute; + position-anchor: --carousel; + position-area: block-end center; + translate: 0 calc(-100% - 6px); + inline-size: anchor-size(inline); + padding-inline-end: 20px; + display: flex; + justify-content: flex-end; + column-gap: 8px; + pointer-events: none; + + >input { + appearance: none; + display: inline-block; + inline-size: 18px; + block-size: 18px; + margin: 0; + padding: 0; + border-radius: 50%; + background-image: url(//static.hdslb.com/images/base/icons.png); + background-position: -855px -790px; + cursor: pointer; + pointer-events: auto; + + &:checked { + background-position: -855px -727px; + pointer-events: none; + } + } + } + } + + .recommend { + grid-area: rc; + display: contents; + + >a { + aspect-ratio: 16 / 9; + background-size: cover; + background-repeat: no-repeat; + border-radius: 4px; + overflow: clip; + position: relative; + + >.mark { + position: absolute; + inset-block-end: 0; + inset-inline: 0; + line-height: 20px; + padding-block: 10px; + padding-inline: 5px; + background-image: linear-gradient(transparent, var(--0000001a) 20%, var(--00000033) 35%, var(--00000099) 65%, var(--000000e6)); + color: var(--99a2aa); + display: flex; + flex-direction: column; + + >p { + margin: 0; + padding: 0; + flex-shrink: 0; + } + + >.title { + flex: 1; + min-block-size: 0; + color: var(--fff); + overflow: hidden; + word-break: break-all; + word-wrap: break-word; + } + } + + >i { + display: inline-block; + position: absolute; + inline-size: 22px; + block-size: 22px; + inset-block-end: .5em; + inset-inline-end: .5em; + background-image: url(//static.hdslb.com/images/base/icons.png); + background-position: -1366px -880px; + + &.d { + background-position: -1436px -880px; + } + } + + &:not(:hover) { + + >.mark>:not(:first-child), + >i { + display: none; + } + } + + &:hover { + + >.mark { + inset-block-start: 0; + background: var(--000000b3); + } + } + } + } + + .rec { + position: absolute; + inset-inline-start: 100%; + inset-block-start: 0; + margin-inline-start: 1em; + padding: 10px; + cursor: pointer; + border: 1px solid var(--ccd0d7); + border-radius: 4px; + font-size: 14px; + display: flex; + flex-direction: column; + row-gap: 5px; + + &::before { + content: ""; + display: inline-block; + inline-size: 12px; + block-size: 13px; + background-image: url(//static.hdslb.com/images/base/icons.png); + background-position: -475px -89px; + transition: all .5s; + } + + &:hover { + background-color: var(--ccd0d7); + + &::before { + rotate: 1turn; + } + } + } +} \ No newline at end of file diff --git a/src/main/html/home/chief.ts b/src/main/html/home/chief.ts new file mode 100644 index 0000000..3607ebb --- /dev/null +++ b/src/main/html/home/chief.ts @@ -0,0 +1,123 @@ +import { rcmd } from "../../../io/com/bilibili/api/x/web-interface/index/top/rcmd"; +import { locs } from "../../../io/com/bilibili/api/x/web-show/res/locs"; +import { toastr } from "../../../toastr"; +import { AV } from "../../../utils/av"; +import { customElement } from "../../../utils/Decorator/customElement"; +import { Element } from "../../../utils/element"; +import { Format } from "../../../utils/fomat"; +import { https } from "../../../utils/https"; + +/** 主推荐位 */ +@customElement('div') +export class Chief extends HTMLDivElement { + + /** + * 需要监听变动的属性。 + * 与实例方法`attributeChangedCallback`配合使用。 + * 此字符串序列定义了`attributeChangedCallback`回调时的第一个参数的可能值。 + */ + // static observedAttributes = []; + + /** + * 在属性更改、添加、移除或替换时调用。 + * 需要与静态属性`observedAttributes`配合使用。 + * 此回调的第一个参数在`observedAttributes`数组中定义。 + */ + // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} + + /** 每当元素添加到文档中时调用。 */ + connectedCallback() { + this.carouselPointerLeave(); + } + + /** 每当元素从文档中移除时调用。 */ + disconnectedCallback() { + this.carouselPointerEnter(); + } + + /** 每当元素被移动到新文档中时调用。 */ + // adoptedCallback() {} + + #carousel = Element.add('div', { appendTo: this, class: 'carousel' }); + + #recommend = Element.add('div', { appendTo: this, class: 'recommend' }); + + #rec = Element.add('div', { appendTo: this, class: 'rec', innerText: '刷新' }); + + #box = Element.add('div', { appendTo: this.#carousel, class: 'box' }); + + #trig = Element.add('form', { appendTo: this.#carousel }); + + #rollTimer?: ReturnType; + + constructor() { + super(); + + this.classList.add('chief'); + + this.#carousel.addEventListener('pointerenter', this.carouselPointerEnter); + this.#carousel.addEventListener('pointerleave', this.carouselPointerLeave); + this.#trig.addEventListener('change', () => { + const d = new FormData(this.#trig); + const i = [...d.values()][0]; + const next = this.#box.querySelector(`[data-i="${i}"]`); + if (next instanceof HTMLElement) { + this.#box.querySelector(".show")?.classList.remove('show'); + next.classList.add('show'); + } + }); + this.#rec.addEventListener('click', this.rcmd); + + locs(4694) + .then(({ code, message, data }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, data } }); + // 主推荐滚动图 + const chief = data[4694]; + let form = '' + this.#box.innerHTML = https(chief.filter(d => !d.ad_cb && !d.null_frame).map((d, i) => { + form += ``; + return `` + }).join('')); + this.#trig.innerHTML = form; + }) + .catch(e => { + toastr.error('获取轮播推荐失败', e); + console.error(e); + }); + this.rcmd(); + } + + private rcmd = () => { + rcmd() + .then(({ code, message, data }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, data } }); + this.#recommend.innerHTML = https(AV.fromStr(data.item.map(d => ` +

    ${d.title}

    up主:${d.owner.name}

    播放:${Format.carry(d.stat.view)}

    + +
    `).join(''))) + }) + .catch(e => { + toastr.error('获取推荐数据失败', e); + console.error(e); + }) + } + + private carouselPointerLeave = () => { + this.#rollTimer = setInterval(() => { + const now = this.#box.querySelector('.show'); + if (now) { + const next = now.nextSibling || this.#box.firstElementChild; + if (next instanceof HTMLElement && next !== now) { + next.classList.toggle('show'); + now.classList.toggle('show'); + const trig = this.#trig.querySelector(`[value="${next.dataset.i}"]`); + trig && ((trig).checked = true); + } + } + }, 5e3); + } + + private carouselPointerEnter = () => { + clearInterval(this.#rollTimer); + } +} \ No newline at end of file diff --git a/src/main/html/home/index.css b/src/main/html/home/index.css new file mode 100644 index 0000000..a7b66d7 --- /dev/null +++ b/src/main/html/home/index.css @@ -0,0 +1,243 @@ +@import url(./chief.css); +@import url(./module.css); +@import url(./zone.css); +@import url(./popularize.css); +@import url(./live.css); +@import url(./rank.css); +@import url(./timeline.css); +@import url(./region.css); + +@scope { + :scope { + color-scheme: light dark; + + --99a2aa: #99a2aa; + --0000001a: #0000001a; + --00000033: #00000033; + --00000099: #00000099; + --000000e6: #000000e6; + --fff: #fff; + --000000b3: #000000b3; + --00000080: #00000080; + --ccd0d7: #ccd0d7; + --6d757a: #6d757a; + --e5e9ef: #e5e9ef; + --00a1d6: #00a1d6; + --fff: #fff; + --222: #222; + --555: #555; + --000: #000; + --9ba3ab: #9ba3ab; + --b8c0cc: #b8c0cc; + --f25d8e: #f25d8e; + --ff8eb3: #ff8eb3; + --00000029: #00000029; + --f6f9fa: #f6f9fa; + + font-size: 12px; + } + + a { + text-decoration: none; + outline: none; + } +} + +.container { + --inline-size: 980px; + + inline-size: var(--inline-size); + margin-inline: auto; + box-sizing: border-box; + font-size: 12px; + display: flex; + flex-direction: column; + row-gap: 30px; + + @media screen and (min-width:1400px) { + + & { + --inline-size: 1160px; + } + } + + @media screen and (min-width:2500px) { + + & { + --inline-size: 1920px; + } + } + + >.video-info { + position: absolute; + position-anchor: --rank; + position-area: block-start; + inline-size: 320px; + border: 1px solid var(--ccd0d7); + border-radius: 4px; + box-shadow: 0 2px 4px var(--00000029); + background-color: var(--fff); + padding: 12px; + display: flex; + flex-direction: column; + row-gap: 10px; + + >.v-title { + line-height: 20px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + >.v-info { + color: var(--99a2aa); + display: flex; + column-gap: 10px; + + >span { + white-space: nowrap; + overflow: clip; + text-overflow: ellipsis; + + &.line { + display: inline-block; + border-inline-end: 1px solid var(--99a2aa); + } + } + } + + >.v-preview { + border-block: 1px solid var(--e5e9ef); + padding-block: 10px; + display: flex; + column-gap: 8px; + + >:first-child { + block-size: 60px; + inline-size: 96px; + border-radius: 4px; + background-size: cover; + } + + >:last-child { + contain: strict; + line-height: 21px; + color: var(--99a2aa); + overflow: clip; + word-wrap: break-word; + word-break: break-all; + } + } + + >.v-season { + border-block-end: 1px solid var(--e5e9ef); + padding-block: 10px; + display: flex; + column-gap: 12px; + + >:first-child { + flex-shrink: 0; + block-size: 128px; + inline-size: 96px; + border-radius: 4px; + background-size: cover; + } + + >:last-child { + flex: 1; + contain: strict; + display: flex; + flex-direction: column; + row-gap: 6px; + + >span { + font-size: 12px; + line-height: 12px; + color: var(--99a2aa); + + &:first-child { + line-height: 34px; + color: var(--222); + font-size: 14px; + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + } + } + } + } + + >.v-data { + display: grid; + grid-template-columns: repeat(4, 1fr); + + >span { + color: var(--99a2aa); + display: flex; + align-items: center; + column-gap: 4px; + + &::before { + content: ""; + display: inline-block; + inline-size: 12px; + block-size: 12px; + background-image: url(//static.hdslb.com/images/base/icons.png); + } + + &.play::before { + background-position: -282px -90px; + } + + &.danmu::before { + background-position: -282px -218px; + } + + &.star::before { + background-position: -282px -346px; + } + + &.coin::before { + background-position: -282px -410px; + } + } + } + } + + >form { + position: fixed; + inset-block-start: 50%; + inset-inline-start: calc(100vi - (100vi - var(--inline-size)) / 2 + 1em); + translate: 0 -50%; + background-color: var(--f6f9fa); + border: 1px solid var(--e5e9ef); + border-start-start-radius: 4px; + border-start-end-radius: 4px; + display: flex; + flex-direction: column; + + >label { + min-inline-size: 48px; + line-height: 24px; + text-align: center; + transition: background-color .3s, color .3s; + cursor: pointer; + user-select: none; + + &:hover, + &:has(input:checked) { + background-color: var(--00a1d6); + color: var(--fff); + } + + &:has(input:checked) { + pointer-events: none; + } + + >input { + appearance: none; + display: none; + } + } + } +} \ No newline at end of file diff --git a/src/main/html/home/index.d.css.ts b/src/main/html/home/index.d.css.ts new file mode 100644 index 0000000..eb9bc4f --- /dev/null +++ b/src/main/html/home/index.d.css.ts @@ -0,0 +1,2 @@ +declare const stylesheet: CSSStyleSheet; +export default stylesheet; \ No newline at end of file diff --git a/src/main/html/home/index.ts b/src/main/html/home/index.ts new file mode 100644 index 0000000..5c3f311 --- /dev/null +++ b/src/main/html/home/index.ts @@ -0,0 +1,240 @@ +import { Html } from ".."; +import { toviewAdd } from "../../../io/com/bilibili/api/x/v2/history/toview/add"; +import { toviewDel } from "../../../io/com/bilibili/api/x/v2/history/toview/del"; +import { toastr } from "../../../toastr"; +import { cookie } from "../../../utils/cookie"; +import { customElement } from "../../../utils/Decorator/customElement"; +import { Element } from "../../../utils/element"; +import { mainEv, MAIN_EVENT } from "../../event"; +import { Gotop } from "../../gotop"; +import { ROUTER } from "../../router"; +import { Footer } from "../footer"; +import { Header } from "../header"; +import { Chief } from "./chief"; +import { Rank } from "./rank"; +import stylesheet from "./index.css" with {type: 'css'}; +import { Live } from "./live"; +import { Popularize } from "./popularize"; +import { REGION } from "../../../io/com/bilibili/api/x/web-interface/dynamic/region"; +import { Timeline } from "./timeline"; +import { TYPE } from "../../../io/com/bilibili/api/pgc/web/timeline"; +import { Region } from "./region"; +import { Format } from "../../../utils/fomat"; +import { rankRegion } from "../../../io/com/bilibili/api/x/web-interface/ranking/region"; +import { list } from "../../../io/com/bilibili/api/pgc/web/rank/list"; + +/** 主页组件 */ +@customElement(undefined, `home-${Date.now()}`) +export class Home extends HTMLElement { + + static avs: Awaited>['data'] = []; + + static sss: Awaited>['result']['list'] = []; + + #header = new Header(); + + #footer = new Footer(); + + #host = this.attachShadow({ mode: 'closed' }); + + #container = Element.add('div', { appendTo: this.#host, class: 'container' }); + + #chief = this.#container.appendChild(new Chief()); + + #popularize = this.#container.appendChild(new Popularize()); + + #live = this.#container.appendChild(new Live()); + + #douga = this.#container.appendChild(new Rank(undefined, undefined, undefined, '-141px -908px')); + + #timeline_bangumi = this.#container.appendChild(new Timeline(TYPE.BANGUMI, '/anime', '番剧', '/anime/timeline', '-141px -140px')); + + #bangumi = this.#container.appendChild(new Rank(REGION.BANGUMI, '/anime', '番剧动态')); + + #timeline_guochuang = this.#container.appendChild(new Timeline(TYPE.GUOCHUANG, '/guochuang', '国创', 'guochuang/timeline', '-140px -1611px')); + + #guochuang = this.#container.appendChild(new Rank(REGION.GUOCHUANG, '/guochuang', '国产原创相关')); + + #music = this.#container.appendChild(new Rank(REGION.MUSCI, '/v/music', '音乐', '-140px -266px')); + + #dance = this.#container.appendChild(new Rank(REGION.DANCE, '/v/dance', '舞蹈', '-141px -461px')); + + #game = this.#container.appendChild(new Rank(REGION.GAME, '/v/game', '游戏', '-141px -203px')); + + #knowledge = this.#container.appendChild(new Rank(REGION.KNOWLEDGE, '/v/knowledge', '知识', '-141px -525px')); + + #tech = this.#container.appendChild(new Rank(REGION.TECHNOLOGY, '/v/tech/', '科技', '-140px -1741px')); + + #life = this.#container.appendChild(new Rank(REGION.LIFE, '/v/life', '生活', '-137px -970px')); + + #kichiku = this.#container.appendChild(new Rank(REGION.KICHIKU, '/v/kichiku', '鬼畜', '-141px -332px')); + + #fashion = this.#container.appendChild(new Rank(REGION.FASHION, '/v/fashion', '时尚', '-141px -718px')); + + #ent = this.#container.appendChild(new Rank(REGION.ENT, '/v/ent', '娱乐', '-141px -1032px')); + + #timeline_movie = this.#container.appendChild(new Region(REGION.MOVIE, TYPE.MOVIE, '/movie', '电影', '-141px -396px')); + + #timeline_tv = this.#container.appendChild(new Region(REGION.TV, TYPE.TV, '/tv', '电视剧', '-141px -845px')); + + #cinephile = this.#container.appendChild(new Rank(REGION.CINEPHILE, '/v/cinephile', '影视', '-140px -1356px')); + + #timeline_jl = this.#container.appendChild(new Region(REGION.DOCUMENTARY, TYPE.DOCUMENTARY, '/documentary', '纪录片', '-140px -1292px')); + + #videoInfo = Element.add('div', { class: 'video-info' }); + + #nav = Element.add('form', { + appendTo: this.#container, innerHTML: ` + + + + + + + + + + + + + + + +`} + ); + + constructor() { + super(); + + document.documentElement.replaceWith(new Html()); + + this.#host.adoptedStyleSheets = [stylesheet]; + + this.#live.classList.add('t0'); + this.#douga.classList.add('t1'); + this.#timeline_bangumi.classList.add('t2'); + this.#timeline_guochuang.classList.add('t3'); + this.#music.classList.add('t4'); + this.#dance.classList.add('t5'); + this.#game.classList.add('t6'); + this.#knowledge.classList.add('t7'); + this.#tech.classList.add('t8'); + this.#life.classList.add('t9'); + this.#kichiku.classList.add('t10'); + this.#fashion.classList.add('t11'); + this.#ent.classList.add('t12'); + this.#timeline_movie.classList.add('t13'); + this.#timeline_tv.classList.add('t14'); + this.#cinephile.classList.add('t15'); + this.#timeline_jl.classList.add('t16'); + + document.body.append(this.#header, this, this.#footer, new Gotop()); + + mainEv.trigger(MAIN_EVENT.NAVIGATE, [ROUTER.HOME, new URL(location.href)]); + + this.#container.addEventListener('click', e => { + const { target } = e; + if (target instanceof HTMLElement && target.classList.contains('wl')) { + e.preventDefault(); + const d = target.classList.toggle('d'); + target.title = d ? '移除' : '稍后再看'; + const { aid } = target.dataset; + const csrf = cookie.get('bili_jct'); + if (aid && csrf) { + if (d) { + toviewAdd(csrf, aid) + .then(({ code, message }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message } }); + toastr.success(`已添加稍后再看:av${aid}`); + }) + .catch(e => { + target.classList.remove('d'); + target.title = '稍后再看'; + toastr.error('添加稍后再看出错', e) + }) + } else { + toviewDel(csrf, aid) + .then(({ code, message }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message } }); + toastr.success(`已移除稍后再看:av${aid}`); + }) + .catch(e => { + target.classList.add('d'); + target.title = '移除'; + toastr.error('移除稍后再看出错', e); + }) + } + } else { + target.classList.remove('d'); + target.title = '稍后再看'; + } + } + }); + + this.#container.addEventListener('pointerover', ({ target }) => { + if (target instanceof HTMLElement) { + const p = target.closest('.vt'); + if (p) { + const { aid, ssid } = p.dataset; + if (ssid) { + const d = Home.sss[ssid]; + this.#videoInfo.innerHTML = `
    +
    +
    + ${d.title} + ${d.new_ep.index_show} + ${d.rating ? `${d.rating}` : ''} +
    +
    +
    + ${Format.carry(d.stat.view)} + ${Format.carry(d.stat.danmaku)} + ${Format.carry(d.stat.follow)} +
    `; + const id = crypto.randomUUID(); + p.style.setProperty('anchor-name', `--${id}`); + this.#videoInfo.style.setProperty('position-anchor', `--${id}`); + this.#container.appendChild(this.#videoInfo); + p.addEventListener('pointerleave', () => { + this.#videoInfo.remove(); + }, { once: true }); + + } else if (aid) { + const d = Home.avs[aid]; + this.#videoInfo.innerHTML = `
    ${d.title}
    +
    + ${d.author} + + ${d.create} +
    +
    +
    +
    ${d.description}
    +
    +
    + ${Format.carry(d.play)} + ${Format.carry(d.review)} + ${Format.carry(d.favorites)} + ${Format.carry(d.coins)} +
    `; + const id = crypto.randomUUID(); + p.style.setProperty('anchor-name', `--${id}`); + this.#videoInfo.style.setProperty('position-anchor', `--${id}`); + this.#container.appendChild(this.#videoInfo); + p.addEventListener('pointerleave', () => { + this.#videoInfo.remove(); + }, { once: true }); + } + } + } + }); + + this.#nav.addEventListener('change', () => { + const d = new FormData(this.#nav); + const i = Number(d.get('tab')); + if (!Number.isNaN(i)) { + this.#container.querySelector(`.t${i}`)?.scrollIntoView({ behavior: 'smooth', block: 'center' }); + } + }); + } +} \ No newline at end of file diff --git a/src/main/html/home/live.css b/src/main/html/home/live.css new file mode 100644 index 0000000..dc53706 --- /dev/null +++ b/src/main/html/home/live.css @@ -0,0 +1,267 @@ +.live { + + .headline { + &::before { + background-position: -141px -652px; + } + + >.online { + color: var(--99a2aa); + + >span { + color: var(--00a1d6); + } + } + + >.fire { + margin-inline-start: 30px; + color: var(--6d757a); + display: flex; + column-gap: .5em; + + &::before { + content: ""; + display: inline-block; + inline-size: 14px; + block-size: 14px; + background-image: url(//static.hdslb.com/images/base/icons.png); + background-position: -665px -1113px; + } + } + } + + .storey-box { + >a { + .mask { + background-size: cover; + border-radius: 4px; + background-color: var(--000); + } + + >.snum { + position: absolute; + position-anchor: --img; + position-area: block-end; + translate: 0px calc(-100% - 2px); + inline-size: 100%; + block-size: 34px; + padding-inline: 6px; + box-sizing: border-box; + color: var(--fff); + pointer-events: none; + display: flex; + justify-content: space-between; + align-items: flex-end; + background: linear-gradient(transparent, transparent, var(--00000080)); + + >.online { + display: flex; + align-items: center; + column-gap: 5px; + + &::before { + content: ""; + display: inline-block; + inline-size: 15px; + block-size: 15px; + background-image: url(//static.hdslb.com/images/base/icons.png); + background-position: -279px -280px; + } + } + } + + >.num { + position: absolute; + inset-block-end: 0; + inline-size: 100%; + line-height: 20px; + color: var(--9ba3ab); + background-color: var(--fff); + transition: all .3s; + } + + &:hover { + >.snum { + display: none; + } + + >.num { + inset-block-end: -20px; + } + } + } + } + + >.right { + row-gap: 20px; + + >form { + flex-shrink: 0; + display: flex; + column-gap: 12px; + + >label { + line-height: 20px; + padding-block-start: 1px; + padding-block-end: 2px; + border-block-end: 1px solid transparent; + cursor: pointer; + transition: .2s; + position: relative; + + &:hover { + color: var(--00a1d6); + } + + &:has(input:checked) { + border-block-end-color: var(--00a1d6); + color: var(--00a1d6); + pointer-events: none; + + &::after { + content: ""; + position: absolute; + inset-block-end: 0; + inset-inline-start: calc(50% - 3px); + border-block-end: 3px solid var(--00a1d6); + border-block-start: 3px solid transparent; + border-inline: 3px solid transparent; + } + } + + >input { + appearance: none; + display: none; + } + } + } + + >.rank { + flex: 1; + min-block-size: 0; + display: flex; + flex-direction: column; + row-gap: 14px; + overflow-y: auto; + scrollbar-width: thin; + counter-reset: rank; + transition: all .3s allow-discrete; + + @starting-style { + translate: 100% 0; + } + + >div { + display: flex; + align-items: flex-start; + + &::before { + flex-shrink: 0; + counter-increment: rank; + content: counter(rank); + display: inline-block; + min-inline-size: 12px; + line-height: 18px; + margin-inline-end: 6px; + padding-inline: 3px; + border-radius: 4px; + background-color: var(--b8c0cc); + color: var(--fff); + font-weight: bolder; + text-align: center; + } + + &:nth-child(-n+3)::before { + background-color: var(--f25d8e); + } + + >.preview { + flex-shrink: 0; + inline-size: 40px; + block-size: 40px; + margin-inline-end: 12px; + border-radius: 50%; + background-size: cover; + background-repeat: no-repeat; + } + + >.r-i { + flex: 1; + min-inline-size: 0; + color: var(--99a2aa); + display: flex; + flex-direction: column; + row-gap: 4px; + + >:first-child { + line-height: 16px; + display: flex; + justify-content: space-between; + + >.u-name { + min-inline-size: 0; + color: var(--222); + overflow: clip; + white-space: nowrap; + text-overflow: ellipsis; + } + + >.u-online { + min-inline-size: 0; + white-space: nowrap; + display: flex; + align-items: center; + column-gap: 3px; + + &::before { + content: ""; + display: inline-block; + inline-size: 14px; + block-size: 10px; + background-image: url(//static.hdslb.com/images/base/icons.png); + background-position: -345px -219px; + } + } + } + + >:last-child { + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + } + + &:hover .u-name { + color: var(--00a1d6); + } + } + } + + &:empty { + flex-direction: row; + justify-content: center; + align-items: flex-start; + column-gap: 5px; + + &::before { + content: ""; + display: inline-block; + inline-size: 20px; + block-size: 20px; + background-image: url(//s1.hdslb.com/bfs/static/jinkela/home/asserts/empty-icon.png); + background-repeat: no-repeat; + background-position: center -598px; + } + + &::after { + content: '没有数据(-_-#)'; + color: var(--99a2aa); + } + } + + &.hide { + display: none; + translate: -100% 0; + } + } + } +} \ No newline at end of file diff --git a/src/main/html/home/live.ts b/src/main/html/home/live.ts new file mode 100644 index 0000000..f37262b --- /dev/null +++ b/src/main/html/home/live.ts @@ -0,0 +1,116 @@ +import { getList, IRecommendRoomList } from "../../../io/com/bilibili/live/api/xlive/web-interface/v1/webMain/getList"; +import { getMoreRecList } from "../../../io/com/bilibili/live/api/xlive/web-interface/v1/webMain/getMoreRecList"; +import { toastr } from "../../../toastr"; +import { customElement } from "../../../utils/Decorator/customElement"; +import { Element } from "../../../utils/element"; +import { Format } from "../../../utils/fomat"; +import { https } from "../../../utils/https"; + +/** 正在直播 */ +@customElement('div') +export class Live extends HTMLDivElement { + + /** + * 需要监听变动的属性。 + * 与实例方法`attributeChangedCallback`配合使用。 + * 此字符串序列定义了`attributeChangedCallback`回调时的第一个参数的可能值。 + */ + // static observedAttributes = []; + + /** + * 在属性更改、添加、移除或替换时调用。 + * 需要与静态属性`observedAttributes`配合使用。 + * 此回调的第一个参数在`observedAttributes`数组中定义。 + */ + // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} + + /** 每当元素添加到文档中时调用。 */ + // connectedCallback() {} + + /** 每当元素从文档中移除时调用。 */ + // disconnectedCallback() {} + + #left = Element.add('div', { appendTo: this, class: ['left', 'zone'] }); + + #right = Element.add('div', { appendTo: this, class: 'right' }); + + #headline = Element.add('div', { appendTo: this.#left, class: 'headline', innerHTML: '正在直播更多' }); + + #box = Element.add('div', { appendTo: this.#left, class: 'storey-box' }); + + #tab = Element.add('form', { appendTo: this.#right, innerHTML: '' }); + + #rank = Element.add('div', { appendTo: this.#right, class: 'rank' }); + + constructor() { + super(); + + this.classList.add('module', 'live'); + + this.addEventListener('click', ({ target }) => { + if (target instanceof HTMLDivElement && target.classList.contains('push')) { + getMoreRecList() + .then(({ code, message, data }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, data } }); + const { recommend_room_list } = data; + this.recommend_room_list(recommend_room_list); + }) + .catch(e => { + toastr.error('刷新直播推荐失败', e); + console.error(e); + }) + .finally(() => { + target.innerHTML = '刷新'; + }); + } + }); + this.#tab.addEventListener('change', () => { + const d = new FormData(this.#tab); + const i = Number(d.get('tab')); + switch (i) { + case 0: { + this.#rank.classList.remove('hide'); + break; + } + default: { + this.#rank.classList.add('hide'); + break; + } + } + }); + + getList() + .then(({ code, message, data }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, data } }); + const { recommend_room_list, online_total, ranking_list, dynamic } = data; + this.#headline.insertAdjacentHTML('beforeend', `
    当前共有${online_total}个在线直播
    233秒居然能做这些!
    ${dynamic}条新动态
    `); + this.recommend_room_list(recommend_room_list); + if (ranking_list) { + this.#rank.innerHTML = https(ranking_list.map(d => ``).join('')); + } + }) + .catch(e => { + toastr.error('获取直播推荐失败', e); + console.error(e); + }); + } + + private recommend_room_list(d: IRecommendRoomList[]) { + this.#box.innerHTML = https(d.map(d => ` + ${d.title} +
    +
    ${d.uname}${Format.carry(d.online)}
    +

    ${d.title}

    +
    ${d.area_v2_parent_name} · ${d.area_v2_name}
    +
    `).join('')); + } +} \ No newline at end of file diff --git a/src/main/html/home/module.css b/src/main/html/home/module.css new file mode 100644 index 0000000..af879d2 --- /dev/null +++ b/src/main/html/home/module.css @@ -0,0 +1,18 @@ +.module { + display: flex; + column-gap: 20px; + + >.left { + flex: 1; + min-inline-size: 0; + } + + >.right { + flex-shrink: 0; + inline-size: 260px; + display: flex; + flex-direction: column; + contain: strict; + overflow-y: clip; + } +} \ No newline at end of file diff --git a/src/main/html/home/popularize.css b/src/main/html/home/popularize.css new file mode 100644 index 0000000..fb3aed0 --- /dev/null +++ b/src/main/html/home/popularize.css @@ -0,0 +1,25 @@ +.popularize { + + .headline::before { + background-position: -141px -75px; + } + + .storey-box { + grid-template-rows: 1fr; + } + + >.right>.online { + line-height: 34px; + border-radius: 4px; + color: var(--6d757a); + background-color: var(--e5e9ef); + white-space: nowrap; + text-align: center; + align-content: center; + transition: .2s; + + &:hover { + color: var(--00a1d6); + } + } +} \ No newline at end of file diff --git a/src/main/html/home/popularize.ts b/src/main/html/home/popularize.ts new file mode 100644 index 0000000..15c0c6f --- /dev/null +++ b/src/main/html/home/popularize.ts @@ -0,0 +1,59 @@ +import { locs } from "../../../io/com/bilibili/api/x/web-show/res/locs"; +import { toastr } from "../../../toastr"; +import { customElement } from "../../../utils/Decorator/customElement"; +import { Element } from "../../../utils/element"; +import { Format } from "../../../utils/fomat"; +import { https } from "../../../utils/https"; + +/** 推广 */ +@customElement('div') +export class Popularize extends HTMLDivElement { + + /** + * 需要监听变动的属性。 + * 与实例方法`attributeChangedCallback`配合使用。 + * 此字符串序列定义了`attributeChangedCallback`回调时的第一个参数的可能值。 + */ + // static observedAttributes = []; + + /** + * 在属性更改、添加、移除或替换时调用。 + * 需要与静态属性`observedAttributes`配合使用。 + * 此回调的第一个参数在`observedAttributes`数组中定义。 + */ + // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} + + /** 每当元素添加到文档中时调用。 */ + // connectedCallback() {} + + /** 每当元素从文档中移除时调用。 */ + // disconnectedCallback() {} + + #left = Element.add('div', { appendTo: this, class: ['left', 'zone'], innerHTML: '
    推广
    ' }); + + #right = Element.add('div', { appendTo: this, class: 'right', innerHTML: '在线列表' }); + + #box = Element.add('div', { appendTo: this.#left, class: 'storey-box' }); + + constructor() { + super(); + + this.classList.add('module', 'popularize'); + + locs(34) + .then(({ code, message, data }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, data } }); + const popularize = data[34]; + this.#box.innerHTML = https(popularize.filter(d => !d.ad_cb && !d.null_frame).map(d => ` + ${d.name} + ${d.archive ? `
    ${Format.fmSeconds(d.archive.duration)}` : ''} +

    ${d.name}

    + ${d.archive ? `` : ''} +
    `).join('')); + }) + .catch(e => { + toastr.error('获取推广位失败', e); + console.error(e); + }); + } +} \ No newline at end of file diff --git a/src/main/html/home/rank.css b/src/main/html/home/rank.css new file mode 100644 index 0000000..1e9b66c --- /dev/null +++ b/src/main/html/home/rank.css @@ -0,0 +1,248 @@ +.m-rank { + .headline { + --background-position: initial; + + &::before { + background-position: var(--background-position); + } + + >form { + display: flex; + column-gap: 12px; + + >label { + line-height: 20px; + padding-block-start: 1px; + padding-block-end: 2px; + border-block-end: 1px solid transparent; + cursor: pointer; + transition: .2s; + position: relative; + + &:hover { + color: var(--00a1d6); + } + + &:has(input:checked) { + border-block-end-color: var(--00a1d6); + color: var(--00a1d6); + pointer-events: none; + + &::after { + content: ""; + position: absolute; + inset-block-end: 0; + inset-inline-start: calc(50% - 3px); + border-block-end: 3px solid var(--00a1d6); + border-block-start: 3px solid transparent; + border-inline: 3px solid transparent; + } + } + + >input { + appearance: none; + display: none; + } + } + } + } + + .storey-box { + >a { + >.num { + position: absolute; + inset-block-end: 0; + inline-size: 100%; + line-height: 20px; + color: var(--9ba3ab); + background-color: var(--fff); + transition: all .3s; + display: flex; + + >span { + flex: 1; + display: flex; + align-items: center; + column-gap: 5px; + + &::before { + content: ""; + display: inline-block; + inline-size: 12px; + block-size: 12px; + background-image: url(//static.hdslb.com/images/base/icons.png); + } + + &.play::before { + background-position: -282px -90px; + } + + &.danmu::before { + background-position: -282px -218px; + } + } + } + + &:hover>.num { + inset-block-end: -20px; + } + } + } + + >.right { + row-gap: 20px; + + >header { + flex-shrink: 0; + display: flex; + justify-content: space-between; + + >h3 { + margin: 0; + font-size: 18px; + font-weight: 400; + } + + >form { + display: flex; + column-gap: 12px; + cursor: not-allowed; + + >label { + line-height: 20px; + padding-block-start: 1px; + padding-block-end: 2px; + border-block-end: 1px solid transparent; + cursor: pointer; + transition: .2s; + position: relative; + pointer-events: none; + + &:hover { + color: var(--00a1d6); + } + + &:has(input:checked) { + border-block-end-color: var(--00a1d6); + color: var(--00a1d6); + pointer-events: none; + + &::after { + content: ""; + position: absolute; + inset-block-end: 0; + inset-inline-start: calc(50% - 3px); + border-block-end: 3px solid var(--00a1d6); + border-block-start: 3px solid transparent; + border-inline: 3px solid transparent; + } + } + + >input { + appearance: none; + display: none; + } + } + } + + >select { + border: 1px solid var(--ccd0d7); + border-radius: 4px; + cursor: pointer; + outline: 0; + pointer-events: none; + } + } + + >.rank { + flex: 1; + min-block-size: 0; + display: flex; + flex-direction: column; + row-gap: 15px; + overflow-y: auto; + scrollbar-width: thin; + counter-reset: rank; + + >a { + display: flex; + align-items: flex-start; + column-gap: 5px; + + &::before { + flex-shrink: 0; + counter-increment: rank; + content: counter(rank); + display: inline-block; + min-inline-size: 12px; + line-height: 18px; + padding-inline: 3px; + border-radius: 4px; + background-color: var(--b8c0cc); + color: var(--fff); + font-weight: bolder; + text-align: center; + } + + &:nth-child(-n+3)::before { + background-color: var(--f25d8e); + } + + >.preview { + flex-shrink: 0; + inline-size: 80px; + block-size: 50px; + background-size: cover; + background-repeat: no-repeat; + } + + &:not(:first-child)>.preview { + display: none; + } + + >.r-i { + flex: 1; + min-inline-size: 0; + line-height: 18px; + color: var(--222); + overflow: clip; + white-space: nowrap; + text-overflow: ellipsis; + } + + &:first-child>.r-i { + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 3; + white-space: initial; + } + + &:hover>.r-i { + color: var(--00a1d6); + } + } + + &:empty { + flex-direction: row; + justify-content: center; + align-items: flex-start; + column-gap: 5px; + + &::before { + content: ""; + display: inline-block; + inline-size: 20px; + block-size: 20px; + background-image: url(//s1.hdslb.com/bfs/static/jinkela/home/asserts/empty-icon.png); + background-repeat: no-repeat; + background-position: center -598px; + } + + &::after { + content: '没有数据(-_-#)'; + color: var(--99a2aa); + } + } + } + } +} \ No newline at end of file diff --git a/src/main/html/home/rank.ts b/src/main/html/home/rank.ts new file mode 100644 index 0000000..a49c629 --- /dev/null +++ b/src/main/html/home/rank.ts @@ -0,0 +1,112 @@ +import { Home } from "."; +import { dynamicRegion, REGION } from "../../../io/com/bilibili/api/x/web-interface/dynamic/region"; +import { newlist } from "../../../io/com/bilibili/api/x/web-interface/newlist"; +import { rankRegion } from "../../../io/com/bilibili/api/x/web-interface/ranking/region"; +import { customElement } from "../../../utils/Decorator/customElement"; +import { Element } from "../../../utils/element"; +import { Format } from "../../../utils/fomat"; +import { https } from "../../../utils/https"; + +/** 排行型分区 */ +@customElement('div') +export class Rank extends HTMLDivElement { + + /** + * 需要监听变动的属性。 + * 与实例方法`attributeChangedCallback`配合使用。 + * 此字符串序列定义了`attributeChangedCallback`回调时的第一个参数的可能值。 + */ + // static observedAttributes = []; + + /** + * 在属性更改、添加、移除或替换时调用。 + * 需要与静态属性`observedAttributes`配合使用。 + * 此回调的第一个参数在`observedAttributes`数组中定义。 + */ + // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} + + /** 每当元素添加到文档中时调用。 */ + // connectedCallback() {} + + /** 每当元素从文档中移除时调用。 */ + // disconnectedCallback() {} + + #left = Element.add('div', { appendTo: this, class: ['left', 'zone'] }); + + #right = Element.add('div', { appendTo: this, class: 'right' }); + + #headline = Element.add('div', { appendTo: this.#left, class: 'headline' }); + + #tab = Element.add('form', { appendTo: this.#headline, innerHTML: '' }); + + #box = Element.add('div', { appendTo: this.#left, class: 'storey-box', innerHTML: '正在加载

    正在加载...

    ' }); + + #push = Element.add('div', { appendTo: this.#headline, class: 'push', innerText: '刷新' }); + + #header = Element.add('header', { appendTo: this.#right, innerHTML: `

    排行

    ` }); + + #rank = Element.add('div', { appendTo: this.#right, class: 'rank' }); + + #i = 0; + + constructor( + private $rid = REGION.DOUGA, + href = '/v/douga', + name = '动画', + backgroundPosition?: string, + ) { + super(); + + this.classList.add('module', 'm-rank'); + this.#headline.insertAdjacentHTML('afterbegin', `${name}更多`); + backgroundPosition && this.#headline.style.setProperty('--background-position', backgroundPosition); + this.#tab.addEventListener('change', () => { + const d = new FormData(this.#tab); + this.#i = Number(d.get('tab')); + this.region(); + }); + this.#push.addEventListener('click', this.region); + + new IntersectionObserver((entries, observer) => { + for (const entry of entries) { + if (entry.isIntersecting) { + observer.disconnect(); + this.region(); + rankRegion(this.$rid) + .then(({ code, message, data }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, data } }); + this.#rank.innerHTML = https(data.map(d => { + Home.avs[d.aid] = d; + return ` +
    +
    ${d.title}
    +
    `}).join('')); + }) + .catch(e => { + console.error('获取视频排行出错', e); + console.error(e); + }); + break; + } + } + }).observe(this); + } + + private region = () => { + (this.#i ? newlist(this.$rid) : dynamicRegion(this.$rid)) + .then(({ code, message, data }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, data } }); + this.#box.innerHTML = https(data.archives.map(d => ` + ${d.title} +
    ${Format.fmSeconds(d.duration)} +

    ${d.title}

    + +
    ${Format.carry(d.stat.view)}${Format.carry(d.stat.danmaku)}
    +
    `).join('')); + }) + .catch(e => { + console.error('获取视频动态出错', e); + console.error(e); + }); + } +} \ No newline at end of file diff --git a/src/main/html/home/region.css b/src/main/html/home/region.css new file mode 100644 index 0000000..4635b2a --- /dev/null +++ b/src/main/html/home/region.css @@ -0,0 +1,192 @@ +.m-region { + .headline { + --background-position: initial; + + &::before { + background-position: var(--background-position); + } + + >form { + display: flex; + column-gap: 12px; + + >label { + line-height: 20px; + padding-block-start: 1px; + padding-block-end: 2px; + border-block-end: 1px solid transparent; + cursor: pointer; + transition: .2s; + position: relative; + + &:hover { + color: var(--00a1d6); + } + + &:has(input:checked) { + border-block-end-color: var(--00a1d6); + color: var(--00a1d6); + pointer-events: none; + + &::after { + content: ""; + position: absolute; + inset-block-end: 0; + inset-inline-start: calc(50% - 3px); + border-block-end: 3px solid var(--00a1d6); + border-block-start: 3px solid transparent; + border-inline: 3px solid transparent; + } + } + + >input { + appearance: none; + display: none; + } + } + } + } + + .storey-box { + >a { + >.num { + position: absolute; + inset-block-end: 0; + inline-size: 100%; + line-height: 20px; + color: var(--9ba3ab); + background-color: var(--fff); + transition: all .3s; + display: flex; + + >span { + flex: 1; + display: flex; + align-items: center; + column-gap: 5px; + + &::before { + content: ""; + display: inline-block; + inline-size: 12px; + block-size: 12px; + background-image: url(//static.hdslb.com/images/base/icons.png); + } + + &.play::before { + background-position: -282px -90px; + } + + &.danmu::before { + background-position: -282px -218px; + } + } + } + + &:hover>.num { + inset-block-end: -20px; + } + } + } + + >.right { + row-gap: 20px; + + >header { + flex-shrink: 0; + display: flex; + justify-content: space-between; + + >h3 { + margin: 0; + font-size: 18px; + font-weight: 400; + } + + >select { + border: 1px solid var(--ccd0d7); + border-radius: 4px; + cursor: pointer; + outline: 0; + pointer-events: none; + } + } + + >.rank { + flex: 1; + min-block-size: 0; + display: flex; + flex-direction: column; + row-gap: 15px; + overflow-y: auto; + scrollbar-width: thin; + counter-reset: rank; + + >a { + display: flex; + align-items: flex-start; + column-gap: 10px; + + &::before { + flex-shrink: 0; + counter-increment: rank; + content: counter(rank); + display: inline-block; + min-inline-size: 12px; + line-height: 18px; + padding-inline: 3px; + border-radius: 4px; + background-color: var(--b8c0cc); + color: var(--fff); + font-weight: bolder; + text-align: center; + } + + >:first-child { + min-inline-size: 0; + color: var(--222); + overflow: clip; + white-space: nowrap; + text-overflow: ellipsis; + + &:hover { + color: var(--00a1d6); + } + } + + >:last-child { + color: var(--99a2aa); + overflow: clip; + white-space: nowrap; + text-overflow: ellipsis; + } + + &:nth-child(-n+3)::before { + background-color: var(--f25d8e); + } + } + + &:empty { + flex-direction: row; + justify-content: center; + align-items: flex-start; + column-gap: 5px; + + &::before { + content: ""; + display: inline-block; + inline-size: 20px; + block-size: 20px; + background-image: url(//s1.hdslb.com/bfs/static/jinkela/home/asserts/empty-icon.png); + background-repeat: no-repeat; + background-position: center -598px; + } + + &::after { + content: '没有数据(-_-#)'; + color: var(--99a2aa); + } + } + } + } +} \ No newline at end of file diff --git a/src/main/html/home/region.ts b/src/main/html/home/region.ts new file mode 100644 index 0000000..9fd099f --- /dev/null +++ b/src/main/html/home/region.ts @@ -0,0 +1,115 @@ +import { Home } from "."; +import { list } from "../../../io/com/bilibili/api/pgc/web/rank/list"; +import { TYPE } from "../../../io/com/bilibili/api/pgc/web/timeline"; +import { dynamicRegion, REGION } from "../../../io/com/bilibili/api/x/web-interface/dynamic/region"; +import { newlist } from "../../../io/com/bilibili/api/x/web-interface/newlist"; +import { toastr } from "../../../toastr"; +import { customElement } from "../../../utils/Decorator/customElement"; +import { Element } from "../../../utils/element"; +import { Format } from "../../../utils/fomat"; +import { https } from "../../../utils/https"; + +/** 影视型分区 */ +@customElement('div') +export class Region extends HTMLDivElement { + + /** + * 需要监听变动的属性。 + * 与实例方法`attributeChangedCallback`配合使用。 + * 此字符串序列定义了`attributeChangedCallback`回调时的第一个参数的可能值。 + */ + // static observedAttributes = []; + + /** + * 在属性更改、添加、移除或替换时调用。 + * 需要与静态属性`observedAttributes`配合使用。 + * 此回调的第一个参数在`observedAttributes`数组中定义。 + */ + // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} + + /** 每当元素添加到文档中时调用。 */ + // connectedCallback() {} + + /** 每当元素从文档中移除时调用。 */ + // disconnectedCallback() {} + + #left = Element.add('div', { appendTo: this, class: ['left', 'zone'] }); + + #right = Element.add('div', { appendTo: this, class: 'right' }); + + #headline = Element.add('div', { appendTo: this.#left, class: 'headline' }); + + #tab = Element.add('form', { appendTo: this.#headline, innerHTML: '' }); + + #box = Element.add('div', { appendTo: this.#left, class: 'storey-box', innerHTML: '正在加载

    正在加载...

    ' }); + + #push = Element.add('div', { appendTo: this.#headline, class: 'push', innerText: '刷新' }); + + #header = Element.add('header', { appendTo: this.#right, innerHTML: `

    排行

    ` }); + + #rank = Element.add('div', { appendTo: this.#right, class: 'rank' }); + + #i = 0; + + constructor( + private $rid = REGION.DOUGA, + private type: TYPE, + href = '/v/douga', + name = '动画', + backgroundPosition?: string, + ) { + super(); + + this.classList.add('module', 'm-region'); + this.#headline.insertAdjacentHTML('afterbegin', `${name}更多`); + backgroundPosition && this.#headline.style.setProperty('--background-position', backgroundPosition); + this.#tab.addEventListener('change', () => { + const d = new FormData(this.#tab); + this.#i = Number(d.get('tab')); + this.region(); + }); + this.#push.addEventListener('click', this.region); + + new IntersectionObserver((entries, observer) => { + for (const entry of entries) { + if (entry.isIntersecting) { + observer.disconnect(); + this.region(); + list(type) + .then(({ code, message, result }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, result } }); + this.#rank.innerHTML = https(result.list.map(d => { + Home.sss[d.season_id] = d; + return ` + ${d.title} + ${d.new_ep.index_show} +`}).join('')); + }) + .catch(e => { + toastr.error(`获取${name}排行出错`, e); + console.error(e); + }); + break; + } + } + }).observe(this); + } + + private region = () => { + (this.#i ? newlist(this.$rid) : dynamicRegion(this.$rid)) + .then(({ code, message, data }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, data } }); + this.#box.innerHTML = https(data.archives.map(d => ` + ${d.title} +
    ${Format.fmSeconds(d.duration)} +

    ${d.title}

    + +
    ${Format.carry(d.stat.view)}${Format.carry(d.stat.danmaku)}
    +
    `).join('')); + }) + .catch(e => { + console.error('获取视频动态出错', e); + console.error(e); + }); + } +} \ No newline at end of file diff --git a/src/main/html/home/timeline.css b/src/main/html/home/timeline.css new file mode 100644 index 0000000..d4ee8b5 --- /dev/null +++ b/src/main/html/home/timeline.css @@ -0,0 +1,273 @@ +.m-timeline { + + >.left { + >.headline { + --background-position: initial; + + &::before { + background-position: var(--background-position); + } + + >form { + font-size: 18px; + display: flex; + column-gap: 8px; + + >label { + inline-size: 70px; + line-height: 20px; + padding-block-start: 2px; + padding-block-end: 7px; + border-block-end: 1px solid transparent; + text-align: center; + cursor: pointer; + transition: .2s; + position: relative; + + @media screen and (max-width: 1400px) { + inline-size: 48px; + } + + &:hover { + color: var(--00a1d6); + } + + &:has(input:checked) { + border-block-end-color: var(--00a1d6); + color: var(--00a1d6); + pointer-events: none; + + &::after { + content: ""; + position: absolute; + inset-block-end: 0; + inset-inline-start: calc(50% - 3px); + border-block-end: 3px solid var(--00a1d6); + border-block-start: 3px solid transparent; + border-inline: 3px solid transparent; + } + } + + >input { + appearance: none; + display: none; + } + } + } + + >.c-clink { + inline-size: 104px; + line-height: 34px; + color: var(--f25d8e); + border-color: var(--f25d8e); + font-size: 14px; + column-gap: 5px; + + &:hover { + color: var(--fff); + background-color: var(--f25d8e); + + &::after { + background-position: -541px -217px; + } + } + + &::after { + background-position: -478px -280px; + } + + } + } + + >.timeline-box { + block-size: 478px; + display: grid; + grid-auto-rows: min-content; + grid-template-columns: repeat(3, 1fr); + column-gap: 34px; + row-gap: 36px; + overflow-y: auto; + scrollbar-width: thin; + + @media screen and (min-width:1400px) { + + & { + grid-template-columns: repeat(4, 1fr); + block-size: 456px; + } + } + + @media screen and (min-width:2500px) { + + & { + block-size: 777px; + } + } + + >div { + display: flex; + column-gap: 12px; + + >.pic { + flex: 3; + aspect-ratio: 1; + border-radius: 4px; + background-size: cover; + } + + >.r-text { + flex: 4; + display: flex; + flex-direction: column; + justify-content: space-between; + align-items: flex-start; + contain: strict; + + >:first-child { + line-height: 18px; + color: var(--222); + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 3; + overflow: clip; + text-overflow: ellipsis; + word-break: break-all; + word-wrap: break-word; + transition: color .2s; + + &:hover { + color: var(--00a1d6); + } + } + + >:last-child { + min-inline-size: 28px; + line-height: 18px; + padding-inline: 4px; + border-radius: 9px; + color: var(--fff); + background-color: var(--b8c0cc); + white-space: nowrap; + text-align: center; + text-overflow: ellipsis; + overflow: hidden; + + &.published { + background-color: var(--ff8eb3); + } + } + } + } + + &:empty { + display: flex; + justify-content: center; + + &::before { + content: url(//s1.hdslb.com/bfs/static/jinkela/home/asserts/bgm-nodata.png); + } + } + } + } + + >.right { + row-gap: 20px; + + >header { + flex-shrink: 0; + display: flex; + justify-content: space-between; + + >h3 { + margin: 0; + font-size: 18px; + font-weight: 400; + } + + >select { + border: 1px solid var(--ccd0d7); + border-radius: 4px; + cursor: pointer; + outline: 0; + pointer-events: none; + } + } + + >.rank { + flex: 1; + min-block-size: 0; + display: flex; + flex-direction: column; + row-gap: 15px; + overflow-y: auto; + scrollbar-width: thin; + counter-reset: rank; + + >a { + display: flex; + align-items: flex-start; + column-gap: 10px; + + &::before { + flex-shrink: 0; + counter-increment: rank; + content: counter(rank); + display: inline-block; + min-inline-size: 12px; + line-height: 18px; + padding-inline: 3px; + border-radius: 4px; + background-color: var(--b8c0cc); + color: var(--fff); + font-weight: bolder; + text-align: center; + } + + >:first-child { + min-inline-size: 0; + color: var(--222); + overflow: clip; + white-space: nowrap; + text-overflow: ellipsis; + + &:hover { + color: var(--00a1d6); + } + } + + >:last-child { + color: var(--99a2aa); + overflow: clip; + white-space: nowrap; + text-overflow: ellipsis; + } + + &:nth-child(-n+3)::before { + background-color: var(--f25d8e); + } + } + + &:empty { + flex-direction: row; + justify-content: center; + align-items: flex-start; + column-gap: 5px; + + &::before { + content: ""; + display: inline-block; + inline-size: 20px; + block-size: 20px; + background-image: url(//s1.hdslb.com/bfs/static/jinkela/home/asserts/empty-icon.png); + background-repeat: no-repeat; + background-position: center -598px; + } + + &::after { + content: '没有数据(-_-#)'; + color: var(--99a2aa); + } + } + } + } +} \ No newline at end of file diff --git a/src/main/html/home/timeline.ts b/src/main/html/home/timeline.ts new file mode 100644 index 0000000..d179ffc --- /dev/null +++ b/src/main/html/home/timeline.ts @@ -0,0 +1,117 @@ +import { Home } from "."; +import { list } from "../../../io/com/bilibili/api/pgc/web/rank/list"; +import { IEpisodes, timeline, TYPE } from "../../../io/com/bilibili/api/pgc/web/timeline"; +import { toastr } from "../../../toastr"; +import { customElement } from "../../../utils/Decorator/customElement"; +import { Element } from "../../../utils/element"; +import { Format } from "../../../utils/fomat"; +import { https } from "../../../utils/https"; + +/** 时间轴型分区 */ +@customElement('div') +export class Timeline extends HTMLDivElement { + + /** + * 需要监听变动的属性。 + * 与实例方法`attributeChangedCallback`配合使用。 + * 此字符串序列定义了`attributeChangedCallback`回调时的第一个参数的可能值。 + */ + // static observedAttributes = []; + + /** + * 在属性更改、添加、移除或替换时调用。 + * 需要与静态属性`observedAttributes`配合使用。 + * 此回调的第一个参数在`observedAttributes`数组中定义。 + */ + // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} + + /** 每当元素添加到文档中时调用。 */ + // connectedCallback() {} + + /** 每当元素从文档中移除时调用。 */ + // disconnectedCallback() {} + + #left = Element.add('div', { appendTo: this, class: ['left', 'zone'] }); + + #right = Element.add('div', { appendTo: this, class: 'right' }); + + #headline = Element.add('div', { appendTo: this.#left, class: 'headline' }); + + #form = Element.add('form', { appendTo: this.#headline, innerHTML: '' }); + + #box = Element.add('div', { appendTo: this.#left, class: 'timeline-box', innerHTML: '
    ' }); + + #header = Element.add('header', { appendTo: this.#right, innerHTML: `

    排行

    ` }); + + #rank = Element.add('div', { appendTo: this.#right, class: 'rank' }); + + #episodes: IEpisodes[][] = []; + + #tab = 0; + + set $tab(v: number | string) { + this.#tab = +v || 0; + this.flushTabs(); + } + + constructor( + private type: TYPE, + href: string, + name: string, + tlink: string, + backgroundPosition?: string, + ) { + super(); + + this.classList.add('module', 'm-timeline'); + this.#headline.insertAdjacentHTML('afterbegin', `${name}新番时间表`); + backgroundPosition && this.#headline.style.setProperty('--background-position', backgroundPosition); + this.#form.addEventListener('change', () => { + const form = new FormData(this.#form); + this.$tab = +[...form.values()][0]; + }); + + timeline(type) + .then(({ code, message, result }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, result } }); + result.forEach(d => { + this.#episodes[d.day_of_week] || (this.#episodes[d.day_of_week] = []); + this.#episodes[d.day_of_week] = this.#episodes[d.day_of_week].concat(d.episodes); + this.#episodes[0] || (this.#episodes[0] = []); + this.#episodes[0] = this.#episodes[0].concat(d.episodes.filter(d => d.published)); + }); + this.#episodes.forEach(d => { + d.sort((a, b) => a.pub_ts > b.pub_ts ? 1 : -1) + }); + this.$tab = 0; + }) + .catch(e => { + toastr.error(`获取${name}时间轴出错`, e); + console.error(e); + }); + list(type) + .then(({ code, message, result }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, result } }); + this.#rank.innerHTML = https(result.list.map(d => { + Home.sss[d.season_id] = d; + return ` + ${d.title} + ${d.new_ep.index_show} +`}).join('')); + }) + .catch(e => { + toastr.error(`获取${name}排行出错`, e); + console.error(e); + }); + } + + private flushTabs() { + this.#box.innerHTML = https(this.#episodes[this.#tab].map(d => ``).join('')); + } +} \ No newline at end of file diff --git a/src/main/html/home/zone.css b/src/main/html/home/zone.css new file mode 100644 index 0000000..0c91cdc --- /dev/null +++ b/src/main/html/home/zone.css @@ -0,0 +1,197 @@ +.zone { + display: flex; + flex-direction: column; + row-gap: 15px; + + >.headline { + display: flex; + column-gap: 10px; + align-items: center; + position: relative; + + &::before { + content: ""; + display: inline-block; + inline-size: 40px; + block-size: 40px; + background-image: url(//static.hdslb.com/images/base/icons.png); + } + + >.name { + font-size: 24px; + + &:is(a) { + color: var(--222); + transition: color .2s; + + &:hover { + color: var(--00a1d6); + } + } + } + + >.more { + position: absolute; + inset-inline-end: 0; + inline-size: 52px; + line-height: 22px; + border: 1px solid var(--ccd0d7); + border-radius: 4px; + color: var(--555); + display: flex; + justify-content: center; + align-items: center; + column-gap: 2px; + transition: all .2s; + + &::after { + content: ""; + display: inline-block; + inline-size: 6px; + block-size: 12px; + background-image: url(//static.hdslb.com/images/base/icons.png); + background-position: -478px -217px; + } + + &:hover { + background-color: var(--ccd0d7); + column-gap: 5px; + } + } + + >.push { + position: absolute; + inset-inline-end: 62px; + line-height: 22px; + border: 1px solid var(--ccd0d7); + border-radius: 4px; + padding-inline: 10px; + display: flex; + justify-content: center; + align-items: center; + transition: all .2s; + cursor: pointer; + + >b { + pointer-events: none; + } + + &::before { + content: ""; + display: inline-block; + inline-size: 12px; + block-size: 12px; + margin-inline-end: 5px; + background-image: url(//static.hdslb.com/images/base/icons.png); + background-position: -475px -89px; + transition: all .5s; + } + + &:hover { + background-color: var(--ccd0d7); + + &::before { + rotate: 1turn; + } + } + } + } + + >.storey-box { + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 20px; + grid-template-rows: repeat(2, 1fr); + grid-auto-rows: 0; + overflow-y: clip; + + @media screen and (min-width:1400px) { + + & { + grid-template-columns: repeat(5, 1fr); + } + } + + >a { + position: relative; + display: flex; + flex-direction: column; + row-gap: 8px; + overflow-y: clip; + + >img { + inline-size: 100%; + aspect-ratio: 16 / 9; + border-radius: 4px; + anchor-name: --img; + } + + >.mask { + position: absolute; + position-anchor: --img; + position-area: center; + inline-size: anchor-size(inline); + block-size: anchor-size(block); + background-color: var(--00000033); + pointer-events: none; + } + + >.dur { + color: var(--fff); + position: absolute; + position-anchor: --img; + position-area: block-end; + translate: 0 calc(-100% - 2px); + inline-size: 100%; + padding-inline: 6px; + box-sizing: border-box; + pointer-events: none; + } + + >p { + margin: 0; + border: 0; + block-size: 40px; + line-height: 20px; + color: var(--222); + word-wrap: break-word; + word-break: break-all; + overflow: clip; + transition: all .2s linear; + } + + >.wl { + position: absolute; + position-anchor: --img; + position-area: block-end; + translate: 0 calc(-100% - 2px); + inline-size: 22px; + block-size: 22px; + inset-block-end: .5em; + inset-inline-end: .5em; + margin-inline-start: calc(100% - 32px); + background-image: url(//static.hdslb.com/images/base/icons.png); + background-position: -1366px -880px; + + &.d { + background-position: -1436px -880px; + } + } + + &:not(:hover) { + + >.mask, + >.dur, + >.wl { + display: none; + } + + + } + + &:hover>p { + color: var(--00a1d6); + } + } + } +} \ No newline at end of file diff --git a/src/main/html/index.css b/src/main/html/index.css new file mode 100644 index 0000000..a1afd90 --- /dev/null +++ b/src/main/html/index.css @@ -0,0 +1,13 @@ +:root { + color-scheme: light dark; + + --fff: #fff; + --00a1d6: #00a1d6; + --222: #222; + --ccd0d7: #ccd0d7; +} + +body { + margin: 0; + padding: 0; +} \ No newline at end of file diff --git a/src/main/html/index.d.css.ts b/src/main/html/index.d.css.ts new file mode 100644 index 0000000..eb9bc4f --- /dev/null +++ b/src/main/html/index.d.css.ts @@ -0,0 +1,2 @@ +declare const stylesheet: CSSStyleSheet; +export default stylesheet; \ No newline at end of file diff --git a/src/player/auxiliary/info/setting/index.ts b/src/main/html/index.ts similarity index 57% rename from src/player/auxiliary/info/setting/index.ts rename to src/main/html/index.ts index ed60de0..52ea393 100644 --- a/src/player/auxiliary/info/setting/index.ts +++ b/src/main/html/index.ts @@ -1,9 +1,11 @@ -import { customElement } from "../../../../utils/Decorator/customElement"; -import svg_setting from "../../../assets/svg/setting.svg"; +import { customElement } from "../../utils/Decorator/customElement"; +import { Element } from "../../utils/element"; +import { Head } from "./head"; +import stylesheet from "./index.css" with {type: 'css'}; -/** 设置按钮 */ -@customElement('button') -export class Setting extends HTMLButtonElement { +/** 路由页面 */ +@customElement('html') +export class Html extends HTMLHtmlElement { /** * 需要监听变动的属性。 @@ -19,11 +21,10 @@ export class Setting extends HTMLButtonElement { */ // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} - /** 初始化标记 */ - // #inited = false; - /** 每当元素添加到文档中时调用。 */ - // connectedCallback() {} + connectedCallback() { + document.adoptedStyleSheets = [stylesheet]; + } /** 每当元素从文档中移除时调用。 */ // disconnectedCallback() {} @@ -31,10 +32,15 @@ export class Setting extends HTMLButtonElement { /** 每当元素被移动到新文档中时调用。 */ // adoptedCallback() {} + private $head = this.appendChild(new Head()); + + private $body = Element.add('body', { appendTo: this }); + + // private $goTop = Element.add('div', { class: 'go-top-m', title: '返回顶部' }, undefined, svg_sent); + constructor() { super(); - this.classList.add('bofqi-auxiliary-info-setting'); - this.insertAdjacentHTML('afterbegin', svg_setting); + this.lang = 'zh-hans'; } } \ No newline at end of file diff --git a/src/main/bilibili/live/index.ts b/src/main/html/live/index.ts similarity index 57% rename from src/main/bilibili/live/index.ts rename to src/main/html/live/index.ts index 56bad18..caae1a2 100644 --- a/src/main/bilibili/live/index.ts +++ b/src/main/html/live/index.ts @@ -1,28 +1,30 @@ +import { toastr } from "../../../toastr"; + // 阻止直播间挂机检测! -const SetInterval = self.setInterval; -const SetTimeout = self.setTimeout; +const SetInterval: (handler: TimerHandler, timeout?: number) => number = setInterval; +const SetTimeout: (handler: TimerHandler, timeout?: number) => number = setTimeout; let sleep = false; -self.setInterval = (...args) => { +self.setInterval = ((...args: [any, any]) => { // 定时器经过二次包装,toString方法不便来源,延时5分钟一律过滤 // if (args[0].toString().includes('triggerSleepCallback')) { if (args[1] === 300000) { if (!sleep) { sleep = true; - console.warn('成功阻止直播间挂机检测!'); + toastr.success('成功阻止直播间挂机检测!'); } return Number.MIN_VALUE; } return SetInterval.call(self, ...args); -} -self.setTimeout = (...args) => { +}) +self.setTimeout = ((...args: [any, any]) => { // if (args[0].toString().includes('triggerSleepCallback')) { if (args[1] === 300000) { if (!sleep) { sleep = true; - console.warn('成功阻止直播间挂机检测!'); + toastr.success('成功阻止直播间挂机检测!'); } return Number.MIN_VALUE; } return SetTimeout.call(self, ...args); -} \ No newline at end of file +}) \ No newline at end of file diff --git a/src/main/html/watchlater/index.ts b/src/main/html/watchlater/index.ts new file mode 100644 index 0000000..0ca06bd --- /dev/null +++ b/src/main/html/watchlater/index.ts @@ -0,0 +1,34 @@ +import { Html } from ".."; +import { Desc } from "../../desc"; +import { MAIN_EVENT, mainEv } from "../../event"; +import { Gotop } from "../../gotop"; +import { Info } from "../../info"; +import { ROUTER } from "../../router"; +import { Bofqi } from "../bofqi"; +import { CommentBox } from "../comment"; +import { Footer } from "../footer"; +import { Header } from "../header"; + +/** 稍后再看页组件 */ +export class Toview { + + #header = new Header(); + + #info = new Info(); + + #bofqi = new Bofqi(); + + #desc = new Desc(); + + #comment = new CommentBox(); + + #footer = new Footer(); + + constructor() { + document.documentElement.replaceWith(new Html()); + + document.body.append(this.#header, this.#info, this.#bofqi, this.#desc, this.#comment, this.#footer, new Gotop()); + + mainEv.trigger(MAIN_EVENT.NAVIGATE, [ROUTER.TOVIEW, new URL(location.href)]); + } +} \ No newline at end of file diff --git a/src/main/index.ts b/src/main/index.ts index d91f42f..e7a448f 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -1,91 +1,54 @@ -// 用户脚本 +import { InitComment } from "./comment/initComment"; +import { Av } from "./html/av"; +import { Bangumi } from "./html/bangumi"; +import { BiliHeader } from "./html/header/BiliHeader"; +import { Home } from "./html/home"; +import { Toview } from "./html/watchlater"; +import { Nano } from "./player/nano"; +import "./WebRTC"; -import { Router, ROUTER } from "./bilibili"; -import "../utils/support"; - -const router = new Router(); - -navigation?.addEventListener('navigate', e => { - if (e.canIntercept && e.navigationType !== 'reload') { - - const url = new URL(e.destination.url, location.origin); - - if (url.pathname !== location.pathname || url.search !== location.search) { - - switch (url.hostname) { - case 'www.bilibili.com': { - switch (true) { - case /^\/video\/av\d+\/?$/i.test(url.pathname): - case /^\/video\/bv1[a-z0-9]{9}\/?$/i.test(url.pathname): { - // av页面 - e.intercept({ - handler() { - return router.navigate(ROUTER.AV, url); - } - }) - break; - } - case /^\/bangumi\/play\/ss\d+\/?$/i.test(url.pathname): - case /^\/bangumi\/play\/ep\d+\/?$/i.test(url.pathname): { - // Bangumi页面 - e.intercept({ - handler() { - return router.navigate(ROUTER.BANGUMI, url); - } - }) - break; - } - case url.pathname === '/': - case url.pathname === '/index.html': { - // Bangumi页面 - e.intercept({ - handler() { - return router.navigate(ROUTER.HOME, url); - } - }) - break; - } - case url.pathname === '/404': { - // 禁止404重定向 - e.intercept({ - handler() { - return Promise.resolve(); - } - }); - break; - } - case url.pathname === '/watchlater/': - case url.pathname === '/medialist/play/watchlater': - case url.pathname === '/list/watchlater': { - // 稍后再看页面 - if (url.hash === '#/list') break; - e.intercept({ - handler() { - return router.navigate(ROUTER.TOVIEW, url); - } - }) - break; - } - case /^\/list\/ml\d+\/?$/i.test(url.pathname): { - // 播放列表页面 - e.intercept({ - handler() { - return router.navigate(ROUTER.MEDIALIST, url); - } - }) - break; - } - } - break; - } +switch (location.hostname) { + case 'www.bilibili.com': { + switch (true) { + case /^\/video\/av\d+\/?$/i.test(location.pathname): + case /^\/video\/bv1[a-z0-9]{9}\/?$/i.test(location.pathname): { + new Av(); + break; + } + case /^\/bangumi\/play\/ss\d+\/?$/i.test(location.pathname): + case /^\/bangumi\/play\/ep\d+\/?$/i.test(location.pathname): { + new Bangumi(); + break; + } + case location.pathname === '/': + case location.pathname === '/index.html': { + new Home(); + break; + } + case location.pathname === '/watchlater/': + case location.pathname === '/medialist/play/watchlater': + case location.pathname === '/list/watchlater': { + if (location.hash === '#/list') break; + new Toview(); + break; + } + default: { + new BiliHeader(); + new Nano(); + new InitComment(); + break; } - } else if (url.hash !== location.hash && url.pathname === '/watchlater/') { - // 稍后再看页面 - e.intercept({ - handler() { - return router.navigate(ROUTER.TOVIEW, url); - } - }) } + break; + } + case 'live.bilibili.com': { + import('./html/live/index'); + break; + } + default: { + new BiliHeader(); + new Nano(); + new InitComment(); + break; } -}); \ No newline at end of file +} \ No newline at end of file diff --git a/src/main/info/collection.css b/src/main/info/collection.css new file mode 100644 index 0000000..06bcc94 --- /dev/null +++ b/src/main/info/collection.css @@ -0,0 +1,109 @@ +.collection { + padding: 0; + border: 0; + border-radius: 4px; + background-color: var(--fff); + font-size: 12px; + + &[popover]:popover-open:not(dialog) { + display: flex; + flex-direction: column; + } + + &::backdrop { + background-color: var(--000000a6); + } + + >header { + line-height: 50px; + padding-inline: 20px; + font-size: 16px; + color: var(--222); + border-block-end: 1px solid var(--e5e9ef); + position: relative; + display: flex; + justify-content: center; + align-items: center; + + >i { + display: inline-block; + position: absolute; + inset-block-start: 16px; + inset-inline-end: 16px; + inline-size: 20px; + block-size: 20px; + background-image: url(//static.hdslb.com/images/base/icons.png); + background-position: -470px -534px; + cursor: pointer; + + &:hover { + background-position: -534px -534px; + } + } + } + + >form { + block-size: 300px; + inline-size: 420px; + padding-inline: 36px; + padding-block: 24px; + box-sizing: border-box; + overflow-y: auto; + scrollbar-width: thin; + display: flex; + flex-direction: column; + row-gap: 24px; + + >label { + font-size: 14px; + color: var(--222); + cursor: pointer; + display: flex; + align-items: center; + position: relative; + + >input { + inline-size: 20px; + block-size: 20px; + margin-inline: 0; + margin-block-start: 0; + margin-inline-end: 18px; + cursor: pointer; + } + + &::after { + content: attr(data-value); + position: absolute; + inset-inline-end: 0; + color: var(--6d757a); + font-size: 12px; + } + + &:hover { + color: var(--00a1d6); + } + } + } + + >button { + inline-size: 160px; + block-size: 40px; + margin-inline: auto; + margin-block: 18px; + border: 0; + border-radius: 4px; + color: var(--fff); + background-color: var(--00a1d6); + cursor: pointer; + + &:hover { + background-color: var(--00b5e5); + } + + &:disabled { + cursor: not-allowed; + background-color: var(--e5e9ef); + color: var(--b8c0cc); + } + } +} \ No newline at end of file diff --git a/src/main/info/collection.ts b/src/main/info/collection.ts new file mode 100644 index 0000000..9ceecbe --- /dev/null +++ b/src/main/info/collection.ts @@ -0,0 +1,133 @@ +import { folder } from "../../io/com/bilibili/api/x/v2/fav/folder"; +import { favAdd } from "../../io/com/bilibili/api/x/v2/fav/video/add"; +import { favDel } from "../../io/com/bilibili/api/x/v2/fav/video/del"; +import { relation } from "../../io/com/bilibili/api/x/web-interface/archive/relation"; +import { toastr } from "../../toastr"; +import { cookie } from "../../utils/cookie"; +import { customElement } from "../../utils/Decorator/customElement"; +import { Element } from "../../utils/element"; +import { MAIN_EVENT, mainEv } from "../event"; + +/** 收藏弹窗 */ +@customElement('div') +export class Collection extends HTMLDivElement { + + /** + * 需要监听变动的属性。 + * 与实例方法`attributeChangedCallback`配合使用。 + * 此字符串序列定义了`attributeChangedCallback`回调时的第一个参数的可能值。 + */ + // static observedAttributes = []; + + /** + * 在属性更改、添加、移除或替换时调用。 + * 需要与静态属性`observedAttributes`配合使用。 + * 此回调的第一个参数在`observedAttributes`数组中定义。 + */ + // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} + + /** 每当元素添加到文档中时调用。 */ + // connectedCallback() {} + + /** 每当元素从文档中移除时调用。 */ + // disconnectedCallback() {} + + /** 每当元素被移动到新文档中时调用。 */ + // adoptedCallback() {} + + #uuid = crypto.randomUUID(); + + #header = Element.add('header', { appendTo: this, innerText: '添加到收藏夹' }); + + #i = Element.add('i', { appendTo: this.#header }); + + #form = Element.add('form', { appendTo: this }); + + #button = Element.add('button', { appendTo: this, innerText: '确定' }); + + #favoured_0 = new Set(); + + #favoured_add: number[] = []; + + #favoured_del: number[] = []; + + constructor() { + super(); + + this.classList.add('collection'); + this.popover = 'manual'; + + const id = crypto.randomUUID(); + this.#form.id = id; + this.#button.setAttribute('form', id); + + this.addEventListener('toggle', () => { + if (this.matches(':popover-open')) { + const { aid } = this.dataset; + aid && folder(aid) + .then(({ code, message, data }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, data } }); + this.#favoured_0.clear(); + this.#form.innerHTML = data.map(d => { + d.favoured && this.#favoured_0.add(d.fid); + return `` + }).join('') + }) + .catch(e => { + toastr.error('获取收藏列表失败', e); + console.error(e); + }) + } + this.#button.disabled = true; + }); + this.#i.addEventListener('click', () => { + this.hidePopover(); + }); + this.#form.addEventListener('change', () => { + const d = new FormData(this.#form); + const i = d.getAll('fav').map(d => +d); + this.#favoured_add = []; + this.#favoured_del = []; + i.forEach(d => { + this.#favoured_0.has(d) || this.#favoured_add.push(d); + }); + this.#favoured_0.forEach(d => { + i.includes(d) || this.#favoured_del.push(d); + }); + + this.#button.disabled = !(this.#favoured_add.length || this.#favoured_del.length); + }); + this.#form.addEventListener('submit', e => { + e.preventDefault(); + const csrf = cookie.get('bili_jct'); + const { aid } = this.dataset; + if (csrf && aid) { + const p: Promise[] = []; + this.#favoured_add.length && p.push(favAdd(csrf, aid, ...this.#favoured_add) + .then(({ code, message }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message } }); + toastr.success('添加收藏成功'); + }) + .catch(e => { + toastr.error('添加收藏失败', e); + console.error(e); + })); + this.#favoured_del.length && p.push(favDel(csrf, aid, ...this.#favoured_del) + .then(({ code, message }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message } }); + toastr.success('取消收藏成功'); + }) + .catch(e => { + toastr.error('取消收藏失败', e); + console.error(e); + })); + Promise.all(p) + .finally(() => { + relation.flesh(); + mainEv.trigger(MAIN_EVENT.RELATION_FLASH, void 0); + }); + } + this.hidePopover(); + }); + } +} \ No newline at end of file diff --git a/src/main/info/index.css b/src/main/info/index.css new file mode 100644 index 0000000..60e126e --- /dev/null +++ b/src/main/info/index.css @@ -0,0 +1,404 @@ +@import url(./operated.css); +@import url(./collection.css); + +@scope { + :scope { + color-scheme: light dark; + + --99a2aa: #99a2aa; + --00a1d6: #00a1d6; + --eee: #eee; + --ff8dae: #ff8dae; + --6d757a: #6d757a; + --fff: #fff; + --00b5e5: #00b5e5; + --f25d8e: #f25d8e; + --fb7299: #fb7299; + --222: #222; + --ccd0d6: #ccd0d6; + --02a0d8: #02a0d8; + --000000a6: #000000a6; + --e5e9ef: #e5e9ef; + --b8c0cc: #b8c0cc; + } + + a { + text-decoration: none; + outline: none; + transition: color .2s; + } +} + +.container { + inline-size: 980px; + margin-inline: auto; + box-sizing: border-box; + font-size: 12px; + display: flex; + + @media screen and (min-width:1400px) { + + & { + inline-size: 1160px; + } + } + + @media screen and (min-width:2500px) { + + & { + inline-size: 1920px; + } + } + + >.video { + flex: 1; + min-inline-size: 0; + padding-block-start: 5px; + padding-block-end: 8px; + + >h1 { + margin: 0; + padding-block: 8px; + font-size: 18px; + font-weight: 400; + overflow: clip; + text-overflow: ellipsis; + white-space: nowrap; + } + + >.tm-info { + color: var(--99a2aa); + margin-block-start: 6px; + display: flex; + column-gap: 32px; + + a { + color: var(--99a2aa); + + &:hover { + color: var(--00a1d6); + } + } + } + + >.number { + display: flex; + column-gap: 30px; + + >span { + display: flex; + align-items: center; + justify-content: center; + column-gap: 3px; + } + + >.v::before { + content: ""; + display: inline-block; + inline-size: 28px; + block-size: 28px; + background: url(//static.hdslb.com/images/base/icons.png); + } + + >.play:before { + background-position: -659px -210px; + } + + >.dm:before { + background-position: -659px -275px; + } + + >.rank:before { + background-position: -660px -1682px; + } + + >.line { + border-inline-start: 1px solid var(--eee); + } + + >.u { + cursor: pointer; + + &::before { + content: ""; + display: inline-block; + inline-size: 45px; + block-size: 60px; + } + } + + >.coin { + &::before { + background-image: url(//s1.hdslb.com/bfs/static/jinkela/videoplay/asserts/anim-coin-small.png); + } + + &.d::before { + background-position: -2340px -60px; + } + + &:not(.d):hover::before { + animation: coin 1140ms steps(19) infinite; + } + } + + >.fav { + &::before { + background-image: url(//s1.hdslb.com/bfs/static/jinkela/videoplay/asserts/anim-collect.png); + } + + &.d::before { + background-position: -1740px -60px; + } + + &:not(.d):hover::before { + animation: fav 1260ms steps(21) infinite; + } + } + + >.order { + &::before { + background-image: url(//s1.hdslb.com/bfs/static/bangumi/play/asserts/anim-order.png); + } + + &.d::before { + background-position: -1860px -60px; + } + + &:not(.d):hover::before { + animation: order 1440ms steps(24) infinite; + } + } + } + } + + >.up { + flex-shrink: 0; + inline-size: 340px; + padding-block: 15px; + display: flex; + + >:first-child { + flex-shrink: 0; + + >.avatar { + display: flex; + justify-content: center; + align-items: center; + position: relative; + + --size: 68px; + + >.avatar-face { + block-size: var(--size); + inline-size: var(--size); + margin: calc(var(--size) / 3); + border-radius: 50%; + } + + >.avatar-pendant { + position: absolute; + block-size: calc(var(--size) / 2* 3); + inline-size: calc(var(--size) / 2* 3); + } + + >.avatar-icon { + position: absolute; + block-size: calc(var(--size) * 3 / 8); + inline-size: calc(var(--size) * 3 / 8); + inset-inline-end: calc(var(--size) / 3); + inset-block-end: calc(var(--size) / 3); + } + } + } + + >.info { + flex: 1; + min-inline-size: 0; + display: flex; + flex-direction: column; + row-gap: 10px; + anchor-name: --up-info; + + >.user { + display: flex; + justify-content: space-between; + + >.name { + max-inline-size: 150px; + font-size: 14px; + color: var(--00a1d6); + word-wrap: break-word; + overflow: hidden; + word-break: break-all; + text-overflow: ellipsis; + } + + >.message { + color: var(--6d757a); + display: flex; + justify-content: center; + align-items: center; + + &::before { + content: ""; + padding-inline: 15px; + block-size: 100%; + background-image: url(//static.hdslb.com/images/base/icons.png); + background-repeat: no-repeat; + background-position: -272px -2007px; + } + + &:hover { + color: var(--00a1d6); + } + } + } + + >.sign { + line-height: 20px; + overflow: clip; + word-break: break-all; + } + + >.number { + color: var(--99a2aa); + display: flex; + justify-content: space-between; + } + + >.followe { + display: flex; + column-gap: 12px; + + >.b-gz { + padding-block: 4px; + padding-inline: 53px; + border-radius: 4px; + border: 1px solid var(--00a1d6); + font-size: 14px; + color: var(--fff); + background-color: var(--00a1d6); + transition: all .3s; + cursor: pointer; + + &:not(.d):hover { + background-color: var(--00b5e5); + border-color: var(--00b5e5); + } + + &.d { + background-color: var(--fff); + color: var(--99a2aa); + border: 1px solid var(--e5e9ef); + } + } + + >.b-cd { + padding: 4px; + border-radius: 4px; + border: 1px solid var(--f25d8e); + font-size: 14px; + inline-size: 52px; + background-color: var(--fff); + color: var(--f25d8e); + transition: all .3s; + cursor: pointer; + + &:hover { + background-color: var(--f25d8e); + color: var(--fff); + } + } + } + } + + >.staff { + position: absolute; + position-anchor: --up-info; + position-area: inline-end span-block-end; + background-color: var(--fff); + display: flex; + flex-direction: column; + transform-origin: center 0; + transition: all .5s allow-discrete; + + @starting-style { + scale: 1 0; + } + + >.avatar { + display: flex; + justify-content: center; + align-items: center; + position: relative; + + --size: 68px; + + >.avatar-face { + block-size: var(--size); + inline-size: var(--size); + margin: calc(var(--size) / 3); + border-radius: 50%; + } + + >.avatar-pendant { + position: absolute; + block-size: calc(var(--size) / 2* 3); + inline-size: calc(var(--size) / 2* 3); + } + + >.avatar-icon { + position: absolute; + block-size: calc(var(--size) * 3 / 8); + inline-size: calc(var(--size) * 3 / 8); + inset-inline-end: calc(var(--size) / 3); + inset-block-end: calc(var(--size) / 3); + } + + >.name { + position: absolute; + inset-block-end: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + max-inline-size: 100%; + color: var(--00a1d6); + } + + >.title { + position: absolute; + background-color: var(--fb7299); + color: var(--fff); + inset-inline-start: 0; + inset-block-start: 0; + padding-inline: 3px; + padding-block: 2px; + border-radius: 4px; + } + } + } + + &:not(:hover)>.staff { + display: none; + scale: 1 0; + } + } +} + +@keyframes coin { + to { + background-position-x: -1140px; + } +} + +@keyframes fav { + to { + background-position-x: -1260px; + } +} + +@keyframes order { + to { + background-position-x: -1440px; + } +} \ No newline at end of file diff --git a/src/main/info/index.d.css.ts b/src/main/info/index.d.css.ts new file mode 100644 index 0000000..eb9bc4f --- /dev/null +++ b/src/main/info/index.d.css.ts @@ -0,0 +1,2 @@ +declare const stylesheet: CSSStyleSheet; +export default stylesheet; \ No newline at end of file diff --git a/src/main/info/index.ts b/src/main/info/index.ts new file mode 100644 index 0000000..2ed6435 --- /dev/null +++ b/src/main/info/index.ts @@ -0,0 +1,329 @@ +import { AV } from "../../utils/av"; +import { customElement } from "../../utils/Decorator/customElement"; +import { Element } from "../../utils/element"; +import { MAIN_EVENT, mainEv } from "../event"; +import { ROUTER } from "../router"; +import SORT from "./sort.json" +import stylesheet from "./index.css" with {type: 'css'}; +import { Format } from "../../utils/fomat"; +import { detail } from "../../io/com/bilibili/api/x/web-interface/view/detail"; +import { toastr } from "../../toastr"; +import { https } from "../../utils/https"; +import { Avatar } from "../comment/avatar"; +import { IModulePositiveEpisode, pgcAppSeason } from "../../io/com/bilibili/api/pgc/view/v2/app/season"; +import { IEpisode, pgcSection } from "../../io/com/bilibili/api/pgc/view/web/season/user/section"; +import { toviewWeb } from "../../io/com/bilibili/api/x/v2/history/toview/web"; +import { relation } from "../../io/com/bilibili/api/x/web-interface/archive/relation"; +import { nav } from "../../io/com/bilibili/api/x/web-interface/nav"; +import { Operated } from "./operated"; +import { Collection } from "./collection"; +import { relationModify } from "../../io/com/bilibili/api/x/relation/modify"; +import { cookie } from "../../utils/cookie"; +import { followDel } from "../../io/com/bilibili/api/pgc/web/follow/del"; +import { followAdd } from "../../io/com/bilibili/api/pgc/web/follow/add"; + +/** 评论区 */ +@customElement(undefined, `info-${Date.now()}`) +export class Info extends HTMLElement { + + /** + * 需要监听变动的属性。 + * 与实例方法`attributeChangedCallback`配合使用。 + * 此字符串序列定义了`attributeChangedCallback`回调时的第一个参数的可能值。 + */ + // static observedAttributes = []; + + /** + * 在属性更改、添加、移除或替换时调用。 + * 需要与静态属性`observedAttributes`配合使用。 + * 此回调的第一个参数在`observedAttributes`数组中定义。 + */ + // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} + + /** 每当元素添加到文档中时调用。 */ + // connectedCallback() {} + + /** 每当元素从文档中移除时调用。 */ + // disconnectedCallback() {} + + /** 每当元素被移动到新文档中时调用。 */ + // adoptedCallback() {} + + #host = this.attachShadow({ mode: 'closed' }); + + #container = Element.add('div', { class: 'container', appendTo: this.#host }); + + #video = Element.add('div', { class: 'video', appendTo: this.#container }); + + #up = Element.add('div', { class: 'up', appendTo: this.#container }); + + #title = Element.add('h1', { appendTo: this.#video }); + + #tm = Element.add('div', { appendTo: this.#video, class: 'tm-info' }); + + #number = Element.add('div', { appendTo: this.#video, class: 'number' }); + + #coin = this.#host.appendChild(new Operated()); + + #collection = this.#host.appendChild(new Collection()); + + #aid = 0; + + get $aid() { + return this.#aid + } + + set $aid(v) { + this.#aid = v; + v && nav() + .then(({ code, message, data }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, data } }); + if (data.isLogin) { + relation(v) + .then(({ code, message, data }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, data } }); + const { favorite, coin } = data; + this.#number.querySelector('.fav')?.classList.toggle('d', Boolean(favorite)); + this.#number.querySelector('.coin')?.classList.toggle('d', Boolean(coin)); + }) + .catch(console.error); + } + }) + .catch(console.error); + } + + constructor() { + super(); + + this.#host.adoptedStyleSheets = [stylesheet]; + + mainEv.bind(MAIN_EVENT.NAVIGATE, ({ detail }) => { this.$navigate(...detail) }); + mainEv.bind(MAIN_EVENT.RELATION_FLASH, () => { + this.$aid = this.#aid; + }); + mainEv.bind(MAIN_EVENT.GUAN_ZHU, ({ detail }) => { + const d = this.#host.querySelector('.b-gz'); + if (d) { + d.classList.toggle('d', detail); + d.innerText = detail ? '已关注' : '+ 关注'; + } + }); + mainEv.bind(MAIN_EVENT.ZHUI_FAN, ({ detail }) => { + const d = this.#host.querySelector('.order'); + if (d) { + d.classList.toggle('d', detail); + } + }); + mainEv.bind(MAIN_EVENT.REQUSET_COIN, () => { + this.#coin.showPopover(); + }); + mainEv.bind(MAIN_EVENT.REQUEST_FAV, () => { + this.#collection.showPopover(); + }); + + this.#host.addEventListener('click', ({ target }) => { + if (target instanceof HTMLElement) { + const coin = target.closest('.coin'); + if (coin && !coin.classList.contains('d')) { + mainEv.trigger(MAIN_EVENT.REQUSET_COIN, void 0); + } else if (target.closest('.fav')) { + mainEv.trigger(MAIN_EVENT.REQUEST_FAV, void 0); + } else if (target.closest('.b-gz')) { + const gz = target.closest('.b-gz')!; + const csrf = cookie.get('bili_jct'); + const i = gz.classList.contains('d') ? 2 : 1; + const { mid } = gz.dataset; + if (csrf && mid) { + relationModify(csrf, mid, i) + .then(({ code, message }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message } }); + toastr.success(`已${i === 2 ? '取消' : '关注'}关注`, `mid:${mid}`); + mainEv.trigger(MAIN_EVENT.GUAN_ZHU, i === 1); + }) + .catch(e => { + toastr.error(`${i === 2 ? '取消' : '关注'}关注出错`, e); + console.error(e); + }) + } + } else if (target.closest('.b-cd')) { + toastr.warn('【充电】功能属于支付类风险操作!', '请移步到UP主页等原生页面进行,已保护您的财产安全~').$delay = 10; + } else if (target.closest('.order')) { + const gz = target.closest('.order')!; + const csrf = cookie.get('bili_jct'); + const { ssid } = gz.dataset; + const i = gz.classList.contains('d'); + if (csrf && ssid) { + (i ? followDel(csrf, ssid) : followAdd(csrf, ssid)) + .then(({ code, message, result }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message } }); + toastr.success(result.toast); + mainEv.trigger(MAIN_EVENT.ZHUI_FAN, Boolean(result.status)); + }) + .catch(e => { + toastr.error(`${i ? '取消' : '添加'}追番出错`, e); + console.error(e); + }); + } + } + } + }); + } + + /** 页面路由 */ + private async $navigate(router: ROUTER, url = new URL(location.href)) { + switch (router) { + case ROUTER.AV: { + const path = url.pathname.split('/'); + let aid = 0; + switch (true) { + case /^av\d+$/i.test(path[2]): { + aid = +path[2].slice(2); + break; + } + case /^bv1[a-z0-9]{9}$/i.test(path[2]): { + aid = +AV.fromBV(path[2]); + break; + } + } + if (aid) { + detail(aid) + .then(({ code, message, data }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, data } }); + // 视频信息 + const { View, Card } = data; + document.title = this.#title.innerText = this.#title.title = View.title; + const tinfo: any = SORT[View.tid]; + this.#tm.innerHTML = `主页${tinfo ? tinfo[2] ? ` > ${(SORT)[tinfo[2]][0]} > ${tinfo[0]}` : ` > ${tinfo[0]}` : ''} + +稿件投诉`; + this.#number.innerHTML = `${Format.carry(View.stat.view)} +${Format.carry(View.stat.danmaku)} +${View.stat.his_rank ? `最高全站日排行${View.stat.his_rank}名` : ''} + +硬币 ${Format.carry(View.stat.coin)} +收藏 ${Format.carry(View.stat.favorite)}`; + // UP信息 + this.#up.innerHTML = https(` +
    + +
    ${Card.card.sign}
    +
    + 投稿:${Format.carry(Card.archive_count)} + 粉丝:${Format.carry(Card.follower)} +
    +
    + + +
    +
    `); + View.staff && this.#up.insertAdjacentHTML('beforeend', ``); + this.#coin.dataset.aid = this.#collection.dataset.aid = this.$aid = aid; + }) + .catch(e => { + console.error(e); + }); + } + break; + } + case ROUTER.BANGUMI: { + const path = url.pathname.split('/'); + let ssid = 0, epid = 0; + switch (true) { + case /^ss\d+$/i.test(path[3]): { + ssid = +path[3].slice(2); + break; + } + case /^ep\d+$/i.test(path[3]): { + epid = +path[3].slice(2); + break; + } + } + if (ssid || epid) { + pgcAppSeason(ssid ? { season_id: ssid } : { ep_id: epid }) + .then(async ({ code, message, data }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, data } }); + ssid || (ssid = data.season_id); + epid || (data.user_status.progress?.last_ep_id && (epid = data.user_status.progress?.last_ep_id)); + let ep: IEpisode | IModulePositiveEpisode | undefined; + data.modules.forEach(d => { + switch (d.style) { + case "positive": + case "section": { + if (!ep) { + epid || (epid = d.data.episodes[0]?.ep_id); + if (epid) { + ep = d.data.episodes.find(d => d.ep_id === epid); + } + } + break; + } + } + }); + if (!ep) { + if (ssid) { + const { code, message, result } = await pgcSection(ssid); + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, result } }); + const eps = ([]).concat(...(result.main_section?.episodes || []), ...result.section.map(d => d.episodes)); + ep = epid ? eps.find(d => d.id === epid) : eps[0]; + } else { + throw new ReferenceError('番剧信息里未包含有效分集列表?', { cause: { code, message, data } }); + } + } + if (!ep) throw new ReferenceError('番剧信息里未包含有效分集列表?', { cause: { code, message, data } }); + document.title = this.#title.innerText = this.#title.title = `${data.title}:${isNaN(+ep.title) ? ep.title : `第${ep.title}话`} ${ep.long_title}`; + this.#tm.innerHTML = `${data.type_name} | ${data.areas.map(d => d.name).join(',')} +${data.new_ep.desc} +av${ep.aid}`; + this.#number.innerHTML = `${Format.carry(data.stat.views)} +${Format.carry(data.stat.danmakus)} + +硬币 ${Format.carry(data.stat.coins)} +追番 ${Format.carry(data.stat.favorites)}`; + this.#coin.dataset.aid = this.#collection.dataset.aid = this.$aid = ep.aid; + }) + .catch(e => { + console.error(e); + }); + } + break; + } + case ROUTER.TOVIEW: { + const path = url.hash.split('/'); + let aid = 0; + switch (true) { + case /^av\d+$/i.test(path[1]): { + aid = +path[1].slice(2); + break; + } + case /^bv1[a-z0-9]{9}$/i.test(path[1]): { + aid = +AV.fromBV(path[1]); + break; + } + } + if (aid) { + this.$navigate(ROUTER.AV, new URL(`https://www.bilibili.com/video/av${aid}`)); + } else { + toviewWeb() + .then(({ code, message, data }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, data } }); + aid || (aid = data.list[0].aid); + this.$navigate(ROUTER.AV, new URL(`https://www.bilibili.com/video/av${aid}`)); + }) + .catch(console.error); + } + break; + } + } + } +} \ No newline at end of file diff --git a/src/main/info/operated.css b/src/main/info/operated.css new file mode 100644 index 0000000..b44d279 --- /dev/null +++ b/src/main/info/operated.css @@ -0,0 +1,145 @@ +.operated { + padding: 0; + border: 0; + background-color: var(--fff); + border-radius: 4px; + font-size: 12px; + + &[popover]:popover-open:not(dialog) { + display: flex; + flex-direction: column; + row-gap: 20px; + } + + &::backdrop { + background-color: var(--000000a6); + } + + >i { + display: inline-block; + position: absolute; + inset-block-start: 16px; + inset-inline-end: 16px; + inline-size: 20px; + block-size: 20px; + background-image: url(//static.hdslb.com/images/base/icons.png); + background-position: -470px -534px; + cursor: pointer; + + &:hover { + background-position: -534px -534px; + } + } + + >header { + margin-block-start: 20px; + font-size: 30px; + color: var(--00a1d6); + text-align: center; + + &::before, + &::after { + content: "给UP主投上"; + font-size: 16px; + color: var(--222); + } + + &::after { + content: "枚硬币"; + } + } + + >form { + display: grid; + grid-template-columns: repeat(2, 1fr); + padding-inline: 35px; + column-gap: 30px; + + >label { + inline-size: 160px; + block-size: 230px; + border: 2px dashed var(--ccd0d6); + border-radius: 5px; + background-repeat: no-repeat; + background-position: center; + background-size: contain; + cursor: pointer; + + &::before { + content: attr(value) "硬币"; + color: var(--99a2aa); + font-size: 14px; + line-height: 40px; + padding-inline-start: 15px; + } + + &:first-child { + background-image: url(//s1.hdslb.com/bfs/static/jinkela/videoplay/asserts/22-gray.png); + } + + &:last-child { + background-image: url(//s1.hdslb.com/bfs/static/jinkela/videoplay/asserts/33-gray.png); + } + + &:has(input:checked) { + border-style: solid; + border-color: var(--02a0d8); + + &::before { + color: var(--00a1d6); + } + + &:first-child { + background-image: url(//s1.hdslb.com/bfs/static/jinkela/videoplay/asserts/22.gif); + } + + &:last-child { + background-image: url(//s1.hdslb.com/bfs/static/jinkela/videoplay/asserts/33.gif); + } + } + + >input { + appearance: none; + display: none; + } + } + } + + >.buttom { + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; + row-gap: 10px; + + >button { + padding-block: 4px; + padding-inline: 18px; + border-radius: 4px; + font-size: 14px; + background-color: var(--00a1d6); + border: 1px solid var(--00a1d6); + color: var(--fff); + cursor: pointer; + + &:hover { + color: var(--fff); + background: var(--00b5e5); + border-color: var(--00b5e5); + } + } + + >.tips { + margin-block-end: 25px; + color: var(--99a2aa); + + &::before { + content: "经验值+" attr(data-value) "0"; + } + + &::after { + content: "(今日" attr(data-exp) "/50)"; + } + } + } +} \ No newline at end of file diff --git a/src/main/info/operated.ts b/src/main/info/operated.ts new file mode 100644 index 0000000..6fdbca7 --- /dev/null +++ b/src/main/info/operated.ts @@ -0,0 +1,94 @@ +import { coinAdd } from "../../io/com/bilibili/api/x/web-interface/coin/add"; +import { coinTodayExp } from "../../io/com/bilibili/api/x/web-interface/coin/today/exp"; +import { toastr } from "../../toastr"; +import { cookie } from "../../utils/cookie"; +import { customElement } from "../../utils/Decorator/customElement"; +import { Element } from "../../utils/element"; +import { MAIN_EVENT, mainEv } from "../event"; + +/** 投币弹窗 */ +@customElement('div') +export class Operated extends HTMLDivElement { + + /** + * 需要监听变动的属性。 + * 与实例方法`attributeChangedCallback`配合使用。 + * 此字符串序列定义了`attributeChangedCallback`回调时的第一个参数的可能值。 + */ + // static observedAttributes = []; + + /** + * 在属性更改、添加、移除或替换时调用。 + * 需要与静态属性`observedAttributes`配合使用。 + * 此回调的第一个参数在`observedAttributes`数组中定义。 + */ + // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} + + /** 每当元素添加到文档中时调用。 */ + // connectedCallback() {} + + /** 每当元素从文档中移除时调用。 */ + // disconnectedCallback() {} + + /** 每当元素被移动到新文档中时调用。 */ + // adoptedCallback() {} + + #i = Element.add('i', { appendTo: this, class: 'i' }); + + #header = Element.add('header', { appendTo: this, innerText: '2' }); + + #mc = Element.add('form', { appendTo: this, innerHTML: `` }); + + #buttom = Element.add('div', { appendTo: this, class: 'buttom' }); + + #button = Element.add('button', { appendTo: this.#buttom, innerText: '确定' }); + + #tips = Element.add('div', { appendTo: this.#buttom, class: 'tips', data: { value: '2', exp: '0' } }); + + constructor() { + super(); + + this.classList.add('operated'); + this.popover = 'manual'; + + this.addEventListener('toggle', () => { + coinTodayExp() + .then(({ code, message, data }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, data } }); + this.#tips.dataset.exp = data; + }) + .catch(e => { + toastr.error('获取今日经验值出错', e); + console.error(e); + }); + }, { once: true }); + this.#i.addEventListener('click', () => { + this.hidePopover(); + }); + this.#mc.addEventListener('change', () => { + const d = new FormData(this.#mc); + const i = Number(d.get('coin')); + if (!Number.isNaN(i)) { + this.#header.innerText = this.#tips.dataset.value = i; + } + }); + this.#button.addEventListener('click', () => { + const csrf = cookie.get('bili_jct'); + const { aid } = this.dataset; + const d = new FormData(this.#mc); + const i = <1>Number(d.get('coin')); + if (csrf && aid && !Number.isNaN(i)) { + coinAdd(csrf, aid, i) + .then(({ code, message }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message } }); + this.hidePopover(); + mainEv.trigger(MAIN_EVENT.RELATION_FLASH, void 0); + }) + .catch(e => { + toastr.error('投币出错', e); + console.error(e); + }) + } + }); + } +} \ No newline at end of file diff --git a/src/main/bilibili/info/sort.json b/src/main/info/sort.json similarity index 100% rename from src/main/bilibili/info/sort.json rename to src/main/info/sort.json diff --git a/src/main/option.ts b/src/main/option.ts new file mode 100644 index 0000000..4f9b83e --- /dev/null +++ b/src/main/option.ts @@ -0,0 +1,42 @@ +import { ProxyHook } from "../utils/hook/Proxy"; +import { MAIN_EVENT, mainEv } from "./event"; +import { POLICY } from "./policy"; + +/** 默认设置 */ +export const MAIN_OPTIONS: IMainOptions = { + policy: POLICY.AVC, + incognito: false, +} + +try { + // 初始化本地设置 + const local = localStorage.getItem('BILI_2019_OPTIONS'); + if (local) { + const obj = JSON.parse(local); + for (const key of Object.keys(MAIN_OPTIONS)) { + Object.hasOwn(obj, key) && Reflect.set(MAIN_OPTIONS, key, Reflect.get(obj, key)); + } + } +} catch { } + +let timer: ReturnType; +/** + * 播放器设置 + * 本对象经过多层代理,如果要短时间内多次访问,为提高性能。 + * 请缓存到局部变量或属性中,并通过监听`PlayerEvent.options_change`事件更新缓存 + */ +export const mainOptions = ProxyHook.onChange(MAIN_OPTIONS, () => { + clearTimeout(timer); + timer = setTimeout(() => { + localStorage.setItem('BILI_2019_OPTIONS', JSON.stringify(MAIN_OPTIONS)); + mainEv.trigger(MAIN_EVENT.OPTINOS_CHANGE, MAIN_OPTIONS); + }); +}); + +/** 设置项 */ +export interface IMainOptions { + /** 播放策略 */ + policy: POLICY, + /** 无痕模式 */ + incognito: boolean, +}; \ No newline at end of file diff --git a/src/main/bilibili/player/danmaku/socket.ts b/src/main/player/danmaku/index.ts similarity index 56% rename from src/main/bilibili/player/danmaku/socket.ts rename to src/main/player/danmaku/index.ts index ade83c7..8c4e01b 100644 --- a/src/main/bilibili/player/danmaku/socket.ts +++ b/src/main/player/danmaku/index.ts @@ -1,20 +1,21 @@ -import { AnyType, IAnyType } from "../../../../io/protobuf/AnyType"; -import { BroadcastFrame, PATH, IBroadcastFrame } from "../../../../io/protobuf/BroadcastFrame"; -import { MessageAckReq } from "../../../../io/protobuf/MessageAckReq"; -import { RoomJoinEvent } from "../../../../io/protobuf/RoomJoinEvent"; -import { RoomReq } from "../../../../io/protobuf/RoomReq"; -import { TargetPath } from "../../../../io/protobuf/TargetPath"; -import { EVENTS, IEvents } from "./state"; +import { BilibiliPlayer } from ".."; +import { AnyType, IAnyType } from "../../../io/protobuf/AnyType"; +import { BroadcastFrame, IBroadcastFrame, PATH } from "../../../io/protobuf/BroadcastFrame"; +import { DanmakuEvent } from "../../../io/protobuf/DanmakuEvent"; +import { IDmSegMobileReply } from "../../../io/protobuf/DmSegMobileReply"; +import { MessageAckReq } from "../../../io/protobuf/MessageAckReq"; +import { RoomJoinEvent } from "../../../io/protobuf/RoomJoinEvent"; +import { RoomReq } from "../../../io/protobuf/RoomReq"; +import { RoomResp } from "../../../io/protobuf/RoomResp"; +import { TargetPath } from "../../../io/protobuf/TargetPath"; +import { ev, PLAYER_EVENT } from "../../../player/event"; + +export class Danmaku { + + #ws?: WebSocket; -/** webSoket */ -export class Socket { - - private mutex = Math.random().toString(36).substring(2); - - static weakmap = new WeakMap(); - - /** WebSocket实例 */ - private ws?: WebSocket; + /* 每次发送消息,唯一标识 */ + private seq = 1; /** 重连次数 每次重连,时间间隔递增 有最大值 */ private retryCount = 0; @@ -32,147 +33,117 @@ export class Socket { private heartTime = 20000; /** 重试定时器, 链接成功,清除定时器 */ - private retryTimer?: number; + private retryTimer?: ReturnType; /** 心跳定时器 */ - private beatTimer?: number; - - /* 每次发送消息,唯一标识 */ - private seq = 1; + private beatTimer?: ReturnType; /** 心跳三次没收到回复,重连 */ private beatCount = 0; + /** 是否已鉴权 */ + private authed = false; + + private realdmPath = 'bilibili.broadcast.message.main.DanmukuEvent'; + /** webSocket 状态 */ private get readyState() { - return this.ws?.readyState; + return this.#ws?.readyState; } - /** 是否已鉴权 */ - private authed = false; - constructor( - private url: string, - private subscribe: string[] = [], + private player: BilibiliPlayer, private room: string[] = [], - private protocols = 'proto', ) { this.init(); + + ev.one(PLAYER_EVENT.DANMAKU_IDENTIFY, this.dispose); + ev.one(PLAYER_EVENT.LOAD_VIDEO_FILE, this.dispose); } - /** 初始化或重连websocket */ private init() { try { - this.ws = new WebSocket(this.url, this.protocols); - this.ws.binaryType = 'arraybuffer'; - this.ws.addEventListener('open', this.onopen); - this.ws.addEventListener('message', this.onmessage); - this.ws.addEventListener('close', this.onclose); - this.ws.addEventListener('error', this.onerror); - this.trigger(EVENTS.B_INITED, void 0); + this.#ws = new WebSocket(`wss://broadcast.chat.bilibili.com:7826/sub?platform=web`, 'proto'); + this.#ws.binaryType = 'arraybuffer'; + this.#ws.addEventListener('open', this.onopen); + this.#ws.addEventListener('message', this.onmessage); + this.#ws.addEventListener('close', this.onclose); + this.#ws.addEventListener('error', this.onerror); } catch (e) { - this.trigger(EVENTS.B_ERROR, e); + // TODO: 链接失败 } } - /** - * 事件监听(只监听一次) - * - * @param type 事件类型 - * @param listener 事件回调 - */ - one(type: K, listener: (evt: IEvents[K]) => void) { - this.ws?.addEventListener(this.mutex + type, (({ detail }: CustomEvent) => listener(detail)), { once: true }); - } - - /** - * 事件监听 - * - * @param type 事件类型 - * @param listener 事件回调 - */ - bind = (type: K, listener: (evt: IEvents[K]) => void) => { - function nListener({ detail }: CustomEvent) { - listener(detail); - } - Socket.weakmap.set(listener, nListener); - this.ws?.addEventListener(this.mutex + type, nListener); - } - - /** - * 取消事件监听 - * - * @param type 事件类型 - * @param listener 事件回调 - */ - unbind = (type: K, listener: (evt: IEvents[K]) => void) => { - const nListener = Socket.weakmap.get(listener); - if (nListener) { - this.ws?.removeEventListener(this.mutex + type, nListener); - Socket.weakmap.delete(listener); - } - } - - /** - * 分发事件 - * - * @param type 事件类型 - * @param detail 分发给回调的数据 - */ - trigger = (type: K, detail: IEvents[K]) => { - this.ws?.dispatchEvent(new CustomEvent(this.mutex + type, { detail })) - } - private onopen = (e: Event) => { this.retryCount = 0; - this.trigger(EVENTS.B_OPEN, e); - this.auth(); + this.send({ + options: { + sequence: ++this.seq, + }, + targetPath: PATH.AUTH, + body: this.encodeAny(PATH.AUTHREQ, AnyType.encode({})) + }) } private onmessage = (e: MessageEvent) => { - const msg = BroadcastFrame.decode(e.data); + const message = BroadcastFrame.decode(e.data); this.beatCount = 0; this.retryTimer && clearTimeout(this.retryTimer); this.heartBeat(); - if (msg) { - if (msg.options.isAck) { + if (message) { + if (message.options.isAck) { this.send({ options: { sequence: ++this.seq, }, targetPath: PATH.MSG_ACK, body: this.encodeAny(PATH.MSG_ACK_REQ, MessageAckReq.encode({ - ackId: msg.options.messageId, - ackOrigin: msg.options.ackOrigin, - targetPath: msg.targetPath, + ackId: message.options.messageId, + ackOrigin: message.options.ackOrigin, + targetPath: message.targetPath, }), ), }); } - if (msg.targetPath) { - switch (msg.targetPath) { + if (message.targetPath) { + switch (message.targetPath) { case PATH.AUTH: { - this.onAuthed(msg); + this.onAuthed(message); break; } case PATH.HEARTBEAT: { - this.trigger(EVENTS.B_HEARTBEAT, msg); + // console.log(PATH.HEARTBEAT, message); break; } case PATH.SUBSCRIBE: { - this.trigger(EVENTS.B_SUB, msg); + // console.log(PATH.SUBSCRIBE, message); break; } case PATH.UNSUBSCRIBE: { - this.trigger(EVENTS.B_UN_SUB, msg); + // console.log(PATH.UNSUBSCRIBE, message); break; } case PATH.ENTER: { - this.trigger(EVENTS.B_ROOM, msg); + if (message.body?.value) { + const { id, msg, online } = RoomResp.decode(message.body.value); + if (online) { + console.log('在线人数', online); // 失效了? + } + if (msg) { + switch (msg.targetPath) { + case this.realdmPath: { + const d = (DanmakuEvent.decode(msg.body.value)); + // console.log('实时弹幕', d); + this.player.addDanmaku(d.elems); + break; + } + } + } + } break; } default: { - this.trigger(EVENTS.B_MSG, msg); + console.log(message); break; } } @@ -180,73 +151,10 @@ export class Socket { } } - private onclose = (e: CloseEvent) => { - this.trigger(EVENTS.B_CLOSE, void 0); - } + private onclose = (e: CloseEvent) => { } private onerror = (e: Event) => { - this.trigger(EVENTS.B_ERROR, e); - } - - /** 重连 */ - private retry() { - this.dispose(); - this.init(); - const time = Math.min(this.retryTime + this.retryCount * this.retryStep, this.maxTime); - this.retryCount++; - this.retryTimer = setTimeout(() => { - this.retry(); - }, time); - } - - /** 销毁 */ - dispose() { - this.retryTimer && clearTimeout(this.retryTimer); - this.beatTimer && clearTimeout(this.beatTimer); - this.close(); - } - - /** 关闭webSocket */ - private close() { - this.ws?.close(); - } - - private send(data: IBroadcastFrame, type = BroadcastFrame) { - const info = type.encode(data); - if (info) { - switch (this.readyState) { - case 0: - console.warn('is CONNECTING'); - break; - case 1: - this.ws?.send(info); - break; - default: - this.retry(); - break; - } - } else { - console.warn('格式不对'); - } - } - - /** 鉴权 */ - private auth() { - this.send({ - options: { - sequence: ++this.seq, - }, - targetPath: PATH.AUTH, - body: this.encodeAny(PATH.AUTHREQ, AnyType.encode({})) - }) - } - - /** any 类型文件 */ - private encodeAny(path: PATH, arrayBuffer: ArrayBuffer) { - return AnyType.create({ - type_url: `type.googleapis.com${path}`, - value: arrayBuffer - }) + console.error(e); } /** 心跳 */ @@ -272,7 +180,6 @@ export class Socket { /** 鉴权后执行订阅 以及进入房间 */ private onAuthed(msg: IBroadcastFrame) { - this.trigger(EVENTS.B_AUTH, msg); this.authed = true; // 订阅 @@ -282,7 +189,7 @@ export class Socket { this.enRoom(); } - /** 订阅,如果是初始话传入的subscribe,则不进行重复判断 */ + /** 订阅,如果是初始化传入的subscribe,则不进行重复判断 */ private enSubscribe(subscribe = true) { this.send({ options: { @@ -290,7 +197,7 @@ export class Socket { }, targetPath: subscribe ? PATH.SUBSCRIBE : PATH.UNSUBSCRIBE, body: this.encodeAny(PATH.TARGETPATH, TargetPath.encode({ - targetPaths: this.subscribe + targetPaths: [this.realdmPath] })) }) } @@ -310,4 +217,54 @@ export class Socket { }) } } + + private send(data: IBroadcastFrame, type = BroadcastFrame) { + const info = type.encode(data); + if (info) { + switch (this.readyState) { + case 0: + console.warn('is CONNECTING'); + break; + case 1: + this.#ws?.send(info); + break; + default: + this.retry(); + break; + } + } else { + console.warn('格式不对'); + } + } + + /** any 类型文件 */ + private encodeAny(path: PATH, arrayBuffer: ArrayBuffer) { + return AnyType.create({ + type_url: `type.googleapis.com${path}`, + value: arrayBuffer + }) + } + + /** 重连 */ + private retry() { + this.dispose(); + this.init(); + const time = Math.min(this.retryTime + this.retryCount * this.retryStep, this.maxTime); + this.retryCount++; + this.retryTimer = setTimeout(() => { + this.retry(); + }, time); + } + + /** 销毁 */ + private dispose = () => { + this.retryTimer && clearTimeout(this.retryTimer); + this.beatTimer && clearTimeout(this.beatTimer); + this.close(); + } + + /** 关闭webSocket */ + private close() { + this.#ws?.close(); + } } \ No newline at end of file diff --git a/src/main/player/heartbeat/index.ts b/src/main/player/heartbeat/index.ts new file mode 100644 index 0000000..853a257 --- /dev/null +++ b/src/main/player/heartbeat/index.ts @@ -0,0 +1,113 @@ +import { BilibiliPlayer } from ".."; +import { heartbeat, HEARTBEAT_PLAY_TYPE, HEARTBEAT_TYPE } from "../../../io/com/bilibili/api/x/click-interface/web/heartbeat"; +import { ev, PLAYER_EVENT } from "../../../player/event"; +import { Checkbox } from "../../../player/widget/checkbox"; +import { cookie } from "../../../utils/cookie"; +import { Element } from "../../../utils/element"; +import { MAIN_EVENT, mainEv } from "../../event"; +import { mainOptions } from "../../option"; +import { POLICY } from "../../policy"; + +export class HeartBeat { + + /** 允许心跳 */ + private heartbeat = true; + + /** 允许seek心跳 */ + private heartbeatSeek = false; + + private $csrf = cookie.get('bili_jct'); + + constructor( + private player: BilibiliPlayer, + ) { + player.$video.addEventListener('play', this.onPlay); + player.$video.addEventListener('pause', this.onPause); + player.$video.addEventListener('seeking', this.onSeeking); + player.$video.addEventListener('seeked', this.onSeeked); + player.$video.addEventListener('ended', this.onEnded); + + ev.bind(PLAYER_EVENT.LOAD_VIDEO_FILE, () => { + this.heartbeat = false; + }); + + const $policyContent = Element.add('div', { class: 'bofqi-setting-content', appendTo: this.player.$auxiliary.$info.$setting.$list, data: { label: '播放策略' } }); + const $incognitoContent = Element.add('div', { class: 'bofqi-setting-content', appendTo: this.player.$auxiliary.$info.$setting.$list }); + /** 播放策略 */ + const $policy = Element.add('select', { class: 'bpui-select', appendTo: $policyContent }); + /** 无痕模式 */ + const $incognito = $incognitoContent.appendChild(new Checkbox()); + $policy.innerHTML = ``; + $incognito.$text = '无痕模式'; + + mainEv.bind(MAIN_EVENT.OPTINOS_CHANGE, ({ detail }) => { + const { incognito, policy } = detail; + + $incognito.$value = incognito; + this.heartbeat = !incognito; + $policy.value = policy; + }); + $policy.addEventListener('change', () => { + mainOptions.policy = +$policy.value; + }); + $incognito.addEventListener('change', () => { + mainOptions.incognito = $incognito.$value; + }); + } + + private onPlay = () => { + this.heartbeatSeek = true; + this.player.cid && this.heartbeat && this.$csrf && heartbeat( + this.$csrf, + this.player.aid, + this.player.cid, + this.player.$video.currentTime, + HEARTBEAT_PLAY_TYPE.CONTINUE, + this.player.epid ? HEARTBEAT_TYPE.BANGUMI : HEARTBEAT_TYPE.AV + ); + } + + private onPause = () => { + this.player.cid && this.heartbeat && this.$csrf && heartbeat( + this.$csrf, + this.player.aid, + this.player.cid, + this.player.$video.currentTime, + HEARTBEAT_PLAY_TYPE.PAUSE, + this.player.epid ? HEARTBEAT_TYPE.BANGUMI : HEARTBEAT_TYPE.AV + ); + } + + private onSeeking = () => { + this.player.cid && this.heartbeat && this.heartbeatSeek && this.$csrf && heartbeat( + this.$csrf, + this.player.aid, + this.player.cid, + this.player.$video.currentTime, + HEARTBEAT_PLAY_TYPE.START, + this.player.epid ? HEARTBEAT_TYPE.BANGUMI : HEARTBEAT_TYPE.AV + ); + } + + private onSeeked = () => { + this.player.cid && this.heartbeat && this.heartbeatSeek && this.$csrf && heartbeat( + this.$csrf, + this.player.aid, + this.player.cid, + this.player.$video.currentTime, + HEARTBEAT_PLAY_TYPE.PLAYING, + this.player.epid ? HEARTBEAT_TYPE.BANGUMI : HEARTBEAT_TYPE.AV + ); + } + + private onEnded = () => { + this.player.cid && this.heartbeat && this.$csrf && heartbeat( + this.$csrf, + this.player.aid, + this.player.cid, + -1, + HEARTBEAT_PLAY_TYPE.ENDED, + this.player.epid ? HEARTBEAT_TYPE.BANGUMI : HEARTBEAT_TYPE.AV + ); + } +} \ No newline at end of file diff --git a/src/main/player/index.ts b/src/main/player/index.ts new file mode 100644 index 0000000..298ba81 --- /dev/null +++ b/src/main/player/index.ts @@ -0,0 +1,478 @@ +import { IDanmaku } from "../../danmaku"; +import DashPlayer from "../../dash-player"; +import { pgcPlayurl } from "../../io/com/bilibili/api/pgc/player/web/playurl"; +import { pgcAppSeason } from "../../io/com/bilibili/api/pgc/view/v2/app/season"; +import { IEpisode, pgcSection } from "../../io/com/bilibili/api/pgc/view/web/season/user/section"; +import { pugvPlayurl } from "../../io/com/bilibili/api/pugv/player/web/playurl"; +import { cards } from "../../io/com/bilibili/api/x/article/cards"; +import { total } from "../../io/com/bilibili/api/x/player/online/total"; +import { pagelist } from "../../io/com/bilibili/api/x/player/pagelist"; +import { playurl } from "../../io/com/bilibili/api/x/player/playurl"; +import { dmPost } from "../../io/com/bilibili/api/x/v2/dm/post"; +import { segSo } from "../../io/com/bilibili/api/x/v2/dm/web/seg.so"; +import { view } from "../../io/com/bilibili/api/x/v2/dm/web/view"; +import { toviewWeb } from "../../io/com/bilibili/api/x/v2/history/toview/web"; +import { DmSegMobileReply } from "../../io/protobuf/DmSegMobileReply"; +import { Player } from "../../player"; +import { DashAgent } from "../../player/dash"; +import { ev, PLAYER_EVENT } from "../../player/event"; +import { FlvAgent } from "../../player/flv"; +import { toastr } from "../../toastr"; +import { AV } from "../../utils/av"; +import { cookie } from "../../utils/cookie"; +import { customElement } from "../../utils/Decorator/customElement"; +import { https } from "../../utils/https"; +import { MAIN_EVENT, mainEv } from "../event"; +import { mainOptions } from "../option"; +import { POLICY } from "../policy"; +import { ROUTER } from "../router"; +import { Danmaku } from "./danmaku"; +import { HeartBeat } from "./heartbeat"; +import { GroupKind } from "./nano/GroupKind"; +import { Progress } from "./progress"; +import { Quality } from "./quality"; +import { Recommend } from "./recommend"; +import { V2 } from "./v2"; + +/** 播放器IO核心 */ +@customElement(undefined, `bilibili-player-${Date.now()}`) +export class BilibiliPlayer extends Player { + + aid = 0; + + cid = 0; + + ssid = 0; + + epid = 0; + + kind = GroupKind.Ugc; + + // 视频推荐组件 + #recommend = new Recommend(this); + + constructor() { + super(); + + // 心跳组件 + new HeartBeat(this); + // 画质组件 + new Quality(this); + // 缩略图组件 + new Progress(this); + // v2 接口 + new V2(this); + mainEv.trigger(MAIN_EVENT.OPTINOS_CHANGE, mainOptions); + ev.bind(PLAYER_EVENT.LOAD_VIDEO_FILE, () => { + this.aid = this.cid = this.ssid = this.epid = 0; + this.kind = GroupKind.Ugc; + }); + ev.bind(PLAYER_EVENT.DANMAKU_INPUT, ({ detail }) => { + const csrf = cookie.get('bili_jct'); + if (csrf) { + dmPost({ + csrf, + aid: this.aid, + oid: this.cid, + ...detail, + }) + .then(({ code, message, data }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, data, ...detail } }); + }) + .catch(e => { + toastr.error('发送弹幕失败', e); + console.error(e); + }) + } else { + toastr.warn('请先登录!') + } + }); + mainEv.bind(MAIN_EVENT.NAVIGATE, ({ detail }) => { this.$navigate(...detail) }); + } + + /** 页面路由 */ + private async $navigate(router: ROUTER, url = new URL(location.href)) { + this.identify(); + switch (router) { + case ROUTER.AV: { + const path = url.pathname.split('/'); + switch (true) { + case /^av\d+$/i.test(path[2]): { + this.aid = +path[2].slice(2); + break; + } + case /^bv1[a-z0-9]{9}$/i.test(path[2]): { + this.aid = +AV.fromBV(path[2]); + break; + } + } + if (this.aid) { + Promise.allSettled([cards({ av: this.aid }), pagelist(this.aid)]) + .then(([cards, pagelist]) => { + const card = cards.status === "fulfilled" && cards.value; + const page = pagelist.status === "fulfilled" && pagelist.value; + if (page) { + const p = Number(new URLSearchParams(url.search).get('p')) || 1; + this.cid = page.data[p - 1].cid; + } + if (card) { + const d = card.data[`av${this.aid}`]; + this.cid || (this.cid = d.cid); + if (d.redirect_url) { + const path = d.redirect_url.split('/'); + /^ep\d+$/i.test(path[5]) && (this.epid = +path[5].slice(2)); + } + navigator.mediaSession.metadata = new MediaMetadata({ + album: d.title, + artist: d.owner.name, + artwork: [{ + src: d.pic + }], + title: d.title, + }); + } + if (!this.cid) throw new ReferenceError(`cid 无效`, { cause: [cards, pagelist] }); + this.$connect(); + this.#recommend.av(); + }) + .catch(e => { + toastr.error('请求 aid 数据错误~', `aid: ${this.aid}`, e); + console.error(e); + }); + } else { + toastr.error('识别 aid 信息错误~', `aid: ${this.aid}`); + } + break; + } + case ROUTER.BANGUMI: { + const path = url.pathname.split('/'); + switch (true) { + case /^ss\d+$/i.test(path[3]): { + this.ssid = +path[3].slice(2); + break; + } + case /^ep\d+$/i.test(path[3]): { + this.epid = +path[3].slice(2); + break; + } + } + if (this.ssid || this.epid) { + pgcAppSeason(this.ssid ? { season_id: this.ssid } : { ep_id: this.epid }) + .then(({ code, message, data }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, data } }); + this.ssid || (this.ssid = data.season_id); + this.epid || (data.user_status.progress?.last_ep_id && (this.epid = data.user_status.progress?.last_ep_id)); + data.modules.forEach(d => { + switch (d.style) { + case "positive": + case "section": { + this.epid || (this.epid = d.data.episodes[0]?.ep_id); + if (this.epid) { + const ep = d.data.episodes.find(d => d.ep_id === this.epid); + if (ep) { + this.aid = ep.aid; + this.cid = ep.cid; + } + } + break; + } + } + }); + if (this.cid && this.epid) { + this.$connect(); + this.#recommend.bangumi(); + } else if (this.ssid) { + pgcSection(this.ssid) + .then(({ code, message, result }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, result } }); + const eps = ([]).concat(...(result.main_section?.episodes || []), ...result.section.map(d => d.episodes)); + const ep = this.epid ? eps.find(d => d.id === this.epid) : eps[0]; + if (ep) { + this.epid = ep.id; + this.aid = ep.aid; + this.cid = ep.cid; + this.$connect(); + this.#recommend.bangumi(); + } + }) + .catch(e => { + toastr.error('获取分集列表失败~', e); + console.error(e); + }); + } else { + throw new ReferenceError(`cid 无效`, { cause: data }); + } + }) + .catch(e => { + toastr.error('请求 Bangumi 信息错误~', `ssid: ${this.ssid}`, `epid: ${this.epid}`, e); + console.error(e); + }); + } else { + toastr.error('识别 Bangumi 信息错误~', `ssid: ${this.ssid}`, `epid: ${this.epid}`); + } + break; + } + case ROUTER.TOVIEW: { + const path = url.hash.split('/'); + switch (true) { + case /^av\d+$/i.test(path[1]): { + this.aid = +path[1].slice(2); + break; + } + case /^bv1[a-z0-9]{9}$/i.test(path[1]): { + this.aid = +AV.fromBV(path[1]); + break; + } + } + const p = +path[2]?.slice(1) || 1; + toviewWeb() + .then(({ code, message, data }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, data } }); + this.aid || (this.aid = data.list[0].aid); + const { cid, redirect_url, pages } = data.list.find(d => d.aid === this.aid) || data.list[0]; + this.cid = pages[p - 1].cid || cid; + if (redirect_url) { + const path = redirect_url.split('/'); + /^ep\d+$/i.test(path[5]) && (this.epid = +path[5].slice(2)); + } + if (!cid) throw new ReferenceError(`cid 无效`, { cause: data }); + this.$connect(); + this.#recommend.toview(data.list); + }) + .catch(e => { + toastr.error('请求稍后再看信息错误~', e); + console.error(e); + }); + break; + } + } + } + + /** 请求媒体信息 */ + async $connect( + aid?: number, + cid?: number, + epid?: number, + kind?: GroupKind, + ) { + aid && (this.aid = aid); + cid && (this.cid = cid); + epid && (this.epid = epid); + kind && (this.kind = kind); + // 请求 playurl + const qn = +cookie.get('CURRENT_QUALITY') || 0; + if (this.kind === GroupKind.Pugv && this.epid) { + pugvPlayurl(this.aid, this.cid, this.epid, qn) + .then(({ code, message, data }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, data } }); + data.is_preview && toastr.warn('正在观看预览片段~', '可能需要开通大会员或者付费才能观看全片'); + this.#attachMedia(data); + }) + .catch(e => { + toastr.error('请求 playurl 数据错误~', e); + console.error(e); + new FlvAgent(this.$video, { type: 'mp4', url: '//s1.hdslb.com/bfs/static/player/media/error.mp4' }); + }) + } else if (this.epid) { + pgcPlayurl(this.aid, this.cid, this.epid, qn) + .then(({ code, message, result }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, result } }); + result.is_preview && toastr.warn('正在观看预览片段~', '可能需要开通大会员或者付费才能观看全片'); + this.#attachMedia(result); + }) + .catch(e => { + toastr.error('请求 playurl 数据错误~', e); + console.error(e); + new FlvAgent(this.$video, { type: 'mp4', url: '//s1.hdslb.com/bfs/static/player/media/error.mp4' }); + }); + } else { + playurl(this.aid, this.cid, qn) + .then(({ code, message, data }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, data } }); + this.#attachMedia(data); + }) + .catch(e => { + toastr.error('请求 playurl 数据错误~', e); + console.error(e); + new FlvAgent(this.$video, { type: 'mp4', url: '//s1.hdslb.com/bfs/static/player/media/error.mp4' }); + }); + } + // 请求弹幕 + const d = await view(this.cid, this.aid); + if (d.dmSge) { + const { total } = d.dmSge; + for (let i = 1; i <= total; i++) { + segSo(this.cid, this.aid, i) + .then(d => { + this.addDanmaku(d.elems); + }) + .catch(() => { }) + } + } + // 获取弹幕弹幕专包 + d?.specialDms?.forEach(d => { + fetch(https(d)) // 此处不能携带cookie + .then(d => d.arrayBuffer()) + .then(d => DmSegMobileReply.decode(d)) + .then(d => { + this.addDanmaku(d.elems); + }) + .catch(() => { }) + }); + // 实时弹幕 + new Danmaku(this, [`video://${this.aid}/${this.cid}${this.ssid ? `?sid=${this.ssid}&epid=${this.epid}` : ''}`]); + // 观看人数 + total(this.aid, this.cid) + .then(({ code, message, data }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, data } }); + ev.trigger(PLAYER_EVENT.ONLINE_NUMBER, data); + }) + .catch(e => { + console.error('更新在线人数失败', e); + }); + mainEv.trigger(MAIN_EVENT.CONNECT, void 0); + // TODO: 指令弹幕 + } + + /** 启动视频播放 */ + #attachMedia = ( + result: Awaited>['result'] | Awaited>['data'] | Awaited>['data'], + seekTime?: number, + ) => { + const qn = +cookie.get('CURRENT_QUALITY') || 0; + if (result.dash) { + /** Hires或者杜比全景声 */ + const defaultAudioQuality = Number(result.dash.audio?.[0]?.id) || 30280; + const highAudioQuality = result.dash.flac?.audio?.id; + new DashAgent(this.$video, result.dash, { + defaultAudioQuality: highAudioQuality || defaultAudioQuality, + defaultVideoQuality: result.dash.video.find(d => d.id === qn) ? qn : result.dash.video[0].id, // DASH 播放器碧油鸡,可获得的qn才能使播放策略正常生效 + enableHEVC: mainOptions.policy === POLICY.HEVC, + enableAV1: mainOptions.policy === POLICY.AV1, + isAutoPlay: false, + isDynamic: false, + enableMultiAudioTracks: false, + abrStrategy: DashPlayer.STRING.ABR_BOLA, + stableBufferTime: 20, + // DRM fields + // protectionDataSet: d.protection?.protectionData, + // ignoreEmeEncryptedEvent:d.protection?.ignoreEmeEncryptedEvent + }, qn, seekTime); + this.$area.$control.$quality.update(result.accept_quality.map((s, i) => { return <[number, string]>[s, result.accept_description[i]] }).reverse()); + qn && (this.$area.$control.$quality.$value = result.quality); + } else if (result.durl) { + let seekType = 'range'; + let duration = 0; + const type = result.format.indexOf('mp4') > -1 ? 'mp4' : 'flv'; + new FlvAgent(this.$video, type === "mp4" ? { + type, + url: result.durl[0].url, + } : { + segments: result.durl.map(d => { + d.length && (duration = d.length); + /\/ws\.acgvideo\.com\//.test(d.url) && (seekType = 'param'); + return { + duration: d.length, + filesize: d.size, + url: d.url, + backupURL: d.backup_url + } + }), + type, + duration, + }, { + enableWorker: false, + stashInitialSize: 1024 * 64, + accurateSeek: true, + seekType: <'range'>seekType, + rangeLoadZeroStart: false, + lazyLoadMaxDuration: 100, + lazyLoadRecoverDuration: 50, + deferLoadAfterSourceOpen: false, + fixAudioTimestampGap: false, + reuseRedirectedURL: true, + }, seekTime); + this.$area.$control.$quality.update(result.accept_quality.map((s, i) => { return <[number, string]>[s, result.accept_description[i]] }).reverse()); + qn && (this.$area.$control.$quality.$value = result.quality); + } else { + throw new ReferenceError('未获取到任何播放数据', { cause: result }); + } + mainEv.trigger(MAIN_EVENT.PLAYURL, result); + } + + /** 添加弹幕 */ + addDanmaku(dms: IDanmaku[]) { + this.$danmaku.$add(dms); + } + + /** + * 更新播放源 + * + * @param qn 指定画质,用于画质切换 + * @param success 成功获取对应画质的回调 + * @param fail 获取对应画质失败回调 + */ + updateSource(qn?: number, success?: Function, fail?: Function) { + if (this.kind === GroupKind.Pugv && this.epid) { + pugvPlayurl(this.aid, this.cid, this.epid, qn) + .then(({ code, message, data }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, result: data } }); + this.#attachMedia(data, this.$video.currentTime); + if (data.quality === qn) { + success?.(); + } else if (qn !== undefined) { + fail?.(); + } else { + success?.(); + } + }) + .catch(e => { + toastr.error('请求更新画质失败~', e) + fail?.(); + }); + } else if (this.epid) { + pgcPlayurl(this.aid, this.cid, this.epid, qn) + .then(({ code, message, result }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, result } }); + this.#attachMedia(result, this.$video.currentTime); + if (result.quality === qn) { + success?.(); + } else if (qn !== undefined) { + fail?.(); + } else { + success?.(); + } + }) + .catch(e => { + toastr.error('请求更新画质失败~', e) + fail?.(); + }); + } else { + playurl(this.aid, this.cid, qn) + .then(({ code, message, data }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, result: data } }); + this.#attachMedia(data, this.$video.currentTime); + if (data.quality === qn) { + success?.(); + } else if (qn !== undefined) { + fail?.(); + } else { + success?.(); + } + }) + .catch(e => { + toastr.error('请求更新画质失败~', e) + fail?.(); + }); + } + } + + /** 重置媒体信息 */ + identify() { + this.aid = this.cid = this.ssid = this.epid = 0; + this.kind = GroupKind.Ugc; + super.identify(); + mainEv.trigger(MAIN_EVENT.IDENTIFY, void 0); + ev.trigger(PLAYER_EVENT.CALL_NEXT_REGISTER, void 0); + } +} \ No newline at end of file diff --git a/src/main/bilibili/player/nano/ChannelKind.ts b/src/main/player/nano/ChannelKind.ts similarity index 100% rename from src/main/bilibili/player/nano/ChannelKind.ts rename to src/main/player/nano/ChannelKind.ts diff --git a/src/main/bilibili/player/nano/EventType.ts b/src/main/player/nano/EventType.ts similarity index 100% rename from src/main/bilibili/player/nano/EventType.ts rename to src/main/player/nano/EventType.ts diff --git a/src/main/bilibili/player/nano/FetcherKind.ts b/src/main/player/nano/FetcherKind.ts similarity index 100% rename from src/main/bilibili/player/nano/FetcherKind.ts rename to src/main/player/nano/FetcherKind.ts diff --git a/src/main/bilibili/player/nano/GroupKind.ts b/src/main/player/nano/GroupKind.ts similarity index 100% rename from src/main/bilibili/player/nano/GroupKind.ts rename to src/main/player/nano/GroupKind.ts diff --git a/src/main/bilibili/player/nano/HandoffKind.ts b/src/main/player/nano/HandoffKind.ts similarity index 100% rename from src/main/bilibili/player/nano/HandoffKind.ts rename to src/main/player/nano/HandoffKind.ts diff --git a/src/main/bilibili/player/nano/InternalKind.ts b/src/main/player/nano/InternalKind.ts similarity index 100% rename from src/main/bilibili/player/nano/InternalKind.ts rename to src/main/player/nano/InternalKind.ts diff --git a/src/main/bilibili/player/nano/ScreenKind.ts b/src/main/player/nano/ScreenKind.ts similarity index 100% rename from src/main/bilibili/player/nano/ScreenKind.ts rename to src/main/player/nano/ScreenKind.ts diff --git a/src/main/player/nano/index.ts b/src/main/player/nano/index.ts new file mode 100644 index 0000000..003db22 --- /dev/null +++ b/src/main/player/nano/index.ts @@ -0,0 +1,364 @@ +import { BilibiliPlayer } from ".."; +import { pgcAppSeason } from "../../../io/com/bilibili/api/pgc/view/v2/app/season"; +import { IEpisode, pgcSection } from "../../../io/com/bilibili/api/pgc/view/web/season/user/section"; +import { pugvSeason } from "../../../io/com/bilibili/api/pugv/view/web/season"; +import { cards } from "../../../io/com/bilibili/api/x/article/cards"; +import { pagelist } from "../../../io/com/bilibili/api/x/player/pagelist"; +import { toastr } from "../../../toastr"; +import { AV } from "../../../utils/av"; +import { ProxyHook } from "../../../utils/hook/Proxy"; +import { ChannelKind } from "./ChannelKind"; +import { EventType } from "./EventType"; +import { FetcherKind } from "./FetcherKind"; +import { GroupKind } from "./GroupKind"; +import { HandoffKind } from "./HandoffKind"; +import { InternalKind } from "./InternalKind"; +import { ScreenKind } from "./ScreenKind"; + +/** nano 播放器兼容 */ +export class Nano { + + #player?: BilibiliPlayer; + + #aid = 0; + + #cid = 0; + + #ssid = 0; + + #epid = 0; + + /** nano播放器兼容 */ + ChannelKind = ChannelKind; + + /** nano播放器兼容 */ + EventType = EventType; + + /** nano播放器兼容 */ + FetcherKind = FetcherKind; + + /** nano播放器兼容 */ + GroupKind = GroupKind; + + /** nano播放器兼容 */ + HandoffKind = HandoffKind; + + /** nano播放器兼容 */ + InternalKind = InternalKind; + + /** nano播放器兼容 */ + ScreenKind = ScreenKind; + + /** nano播放器兼容 */ + metadata = {}; + + /** nano播放器兼容 */ + #kind = GroupKind.Ugc; + + /** nano播放器兼容 */ + #danmaku = { + close() { }, + isOpen() { return true } + }; + + /** nano播放器兼容 */ + #mediaElement = document.createElement('div'); + + /** nano播放器兼容 */ + #element = new Proxy(>{}, { + get: (target) => { + return this.#mediaElement; + }, + }); + + constructor() { + + ProxyHook.property(self, 'nano', this); + ProxyHook.property(self, 'EmbedPlayer', this.#EmbedPlayer); + } + + #identify() { + this.#player?.identify(); + this.#aid = this.#cid = this.#ssid = this.#epid = 0; + this.#kind = GroupKind.Ugc; + } + + /** 2.0 启用入口 */ + #EmbedPlayer = ( + type: string, + player: any, + playerParamsArg: string, + playerType: string, + upgrade: boolean, + callbackFn: Function, + isIframe: boolean, + ) => { + this.#identify(); + const bofqi = document.querySelector('#bilibili-player') || document.querySelector('#bofqi'); + if (bofqi) { + bofqi.replaceWith(this.#player || (this.#player = new BilibiliPlayer())); + const params = this.searchParams(playerParamsArg || location.search); + params.aid && (this.#aid = +params.aid); + params.cid && (this.#cid = +params.cid); + this.#aid || (params.bvid && (this.#aid = +AV.fromBV(params.bvid))); + this.#player?.classList.add('screen-wide'); // 默认以宽屏模式启动 + this.connect(); + } else { + console.error('未找到播放器~'); + } + } + + /** 3.0 启动入口 */ + createPlayer(initData: NanoInitData, theme?: Record) { + this.#identify(); + initData.kind && (this.#kind = initData.kind); + initData.aid && (this.#aid = +initData.aid); + initData.cid && (this.#cid = +initData.cid); + this.#aid || (initData.bvid && (this.#aid = +AV.fromBV(initData.bvid))); + if (!this.#aid) { + const { aid } = this.searchParams(); + aid && (this.#aid = aid.startsWith('BV') ? +AV.fromBV(aid) : +aid); // 诡异的写作 aid 读作 bvid ! + } + initData.seasonId && (this.#ssid = +initData.seasonId); + initData.episodeId && (this.#epid = +initData.episodeId); + initData.element?.replaceChildren(this.#player || (this.#player = new BilibiliPlayer())); + this.#player?.classList.add('screen-wide'); // 默认以宽屏模式启动 + + return this; + } + + connect() { + if (this.#kind === GroupKind.Pugv) { + if (this.#ssid || this.#epid) { + pugvSeason(this.#ssid ? { season_id: this.#ssid } : { ep_id: this.#epid }) + .then(({ code, message, data }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, data } }); + this.#epid || (this.#epid = data.episodes[0]?.id); + const ep = data.episodes.find(d => d.id === this.#epid); + if (ep) { + this.#aid = ep.aid; + this.#cid = ep.cid; + } + if (this.#epid && this.#cid) { + this.#player?.$connect(this.#aid, this.#cid, this.#epid, GroupKind.Pugv); + } else { + throw new ReferenceError(`cid 无效`, { cause: data }); + } + }) + .catch(e => { + toastr.error('请求课程数据错误~', `ssid: ${this.#ssid}`, `epid: ${this.#epid}`, e); + console.error(e); + }) + } else { + toastr.error('播放器不支持的课程类型~').$delay = 0; + } + } else if (this.#ssid || this.#epid) { + pgcAppSeason(this.#ssid ? { season_id: this.#ssid } : { ep_id: this.#epid }) + .then(({ code, message, data }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, data } }); + this.#ssid || (this.#ssid = data.season_id); + this.#epid || (data.user_status.progress?.last_ep_id && (this.#epid = data.user_status.progress?.last_ep_id)); + data.modules.forEach(d => { + switch (d.style) { + case "positive": + case "section": { + this.#epid || (this.#epid = d.data.episodes[0]?.ep_id); + if (this.#epid) { + const ep = d.data.episodes.find(d => d.ep_id === this.#epid); + if (ep) { + this.#aid = ep.aid; + this.#cid = ep.cid; + } + } + break; + } + } + }); + if (this.#cid && this.#epid) { + this.#player?.$connect(this.#aid, this.#cid, this.#epid); + } else if (this.#ssid) { + toastr.warn('番剧信息里未包含有效分集列表?', '尝试单独获~'); + pgcSection(this.#ssid) + .then(({ code, message, result }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, result } }); + const eps = ([]).concat(...(result.main_section?.episodes || []), ...result.section.map(d => d.episodes)); + const ep = this.#epid ? eps.find(d => d.id === this.#epid) : eps[0]; + if (ep) { + this.#epid = ep.id; + this.#aid = ep.aid; + this.#cid = ep.cid; + this.#player?.$connect(this.#aid, this.#cid, this.#epid); + } + }) + .catch(e => { + toastr.error('获取分集列表失败~', e); + console.error(e); + }); + } else { + throw new ReferenceError(`cid 无效`, { cause: data }); + } + }) + .catch(e => { + toastr.error('请求 Bangumi 信息错误~', `ssid: ${this.#ssid}`, `epid: ${this.#epid}`, e); + console.error(e); + }); + } else if (this.#aid) { + Promise.allSettled([cards({ av: this.#aid }), pagelist(this.#aid)]) + .then(([cards, pagelist]) => { + const card = cards.status === "fulfilled" && cards.value; + const page = pagelist.status === "fulfilled" && pagelist.value; + if (page) { + const p = Number(new URLSearchParams(location.search).get('p')) || 1; + this.#cid = page.data[p - 1].cid; + } + if (card) { + const d = card.data[`av${this.#aid}`]; + this.#cid || (this.#cid = d.cid); + if (d.redirect_url) { + const path = d.redirect_url.split('/'); + /^ep\d+$/i.test(path[5]) && (this.#epid = +path[5].slice(2)); + } + navigator.mediaSession.metadata = new MediaMetadata({ + album: d.title, + artist: d.owner.name, + artwork: [{ + src: d.pic + }], + title: d.title, + }); + } + if (!this.#cid) throw new ReferenceError(`cid 无效`, { cause: [cards, pagelist] }); + this.#player?.$connect(this.#aid, this.#cid, this.#epid); + }) + .catch(e => { + toastr.error('请求 aid 数据错误~', `aid: ${this.#aid}`, e); + console.error(e); + }); + } else { + toastr.error('播放器不支持的视频类型~').$delay = 0; + } + } + + /** nano播放器兼容 */ + async reload(initData: NanoInitData) { + this.#identify(); + this.createPlayer(initData); + this.connect(); + } + + /** 提取启动参数 */ + searchParams(url = location.search) { + return Object.fromEntries([...new URLSearchParams(url).entries()]); + } + + /** nano播放器兼容 */ + aspectRatio({ width }: { width: number }) { + return { width, height: width / 16 * 9 + 68 - (self.innerWidth > 1680 ? 56 : 46) }; + } + + /** nano播放器兼容 */ + getStates() { + return { mainScreen: ScreenKind.Wide }; + } + + /** nano播放器兼容 */ + setState() { } + + /** nano播放器兼容 */ + setHandoff() { } + + /** nano播放器兼容 */ + on(event: EventType, callback: () => void) { + switch (event) { + case EventType.Player_Initialized: + case EventType.Player_Connected: { + Promise.resolve().finally(callback) + break; + } + } + } + + /** nano播放器兼容 */ + off(event: EventType, callback: () => void) { } + + /** nano播放器兼容 */ + fetch(url: string) { + return Promise.reject(url); + } + + /** nano播放器兼容 */ + mediaElement() { + return this.#mediaElement; + } + + /** nano播放器兼容 */ + isMuted() { + return this.#player?.$video.muted; + } + + /** nano播放器兼容 */ + getVolume() { + return this.#player?.$video.volume; + } + + /** nano播放器兼容 */ + getDuration() { + return this.#player?.$video.duration; + } + + /** nano播放器兼容 */ + play() { + return Promise.resolve(); + } + + /** nano播放器兼容 */ + pause() { } + + /** nano播放器兼容 */ + getCurrentTime() { + return this.#player?.$video.currentTime; + } + + /** nano播放器兼容 */ + getManifest() { + return Object.create(null); + } + + /** nano播放器兼容 */ + isInitialized() { + return true; + } + + /** nano播放器兼容 */ + getElements() { + return this.#element; + } + + /** nano播放器兼容 */ + isPaused() { + return this.#player?.$video.paused; + } + + /** nano播放器兼容 */ + getLightOff() { + return false; + } +} + +interface NanoInitData { + aid?: number | string; + cid?: number | string; + bvid?: string; + episodeId?: number | string; + seasonId?: number | string; + revision?: number + featureList?: Set, + enableAV1?: boolean; + enableWMP?: boolean; + enableHEVC?: boolean; + hideBoxShadow?: boolean; + t?: number; + kind?: GroupKind; + element?: HTMLDivElement; + auxiliary?: HTMLDivElement; +} \ No newline at end of file diff --git a/src/main/player/progress/index.ts b/src/main/player/progress/index.ts new file mode 100644 index 0000000..5aea7e7 --- /dev/null +++ b/src/main/player/progress/index.ts @@ -0,0 +1,65 @@ +import { BilibiliPlayer } from ".."; +import { videoshot } from "../../../io/com/bilibili/api/x/player/videoshot"; +import { ev, PLAYER_EVENT } from "../../../player/event"; +import { MAIN_EVENT, mainEv } from "../../event"; + +export class Progress { + + #img = document.createElement('div'); + + /** 缩略图信息 */ + pvData?: Awaited>; + + constructor(private player: BilibiliPlayer) { + + player.$area.$control.$progress.$hover.appendChild(this.#img); + + ev.bind(PLAYER_EVENT.LOAD_VIDEO_FILE, this.#identify); + ev.bind(PLAYER_EVENT.PROGRESS_HOVER, this.#hover); + mainEv.bind(MAIN_EVENT.IDENTIFY, this.#identify); + mainEv.bind(MAIN_EVENT.CONNECT, this.#init); + } + + #init = () => { + videoshot(this.player.cid, this.player.aid) + .then(d => { + this.pvData = d; + }) + .catch(e => { + console.error('获取缩略图出错', e); + }); + } + + #hover = (evt: CustomEvent) => { + if (this.pvData) { + const { + pv_index, + pv_img, + pv_x_len = 10, + pv_y_len = 10, + pv_x_size = 160, + pv_y_size = 90, + } = this.pvData; + const { detail: value } = evt; + if (pv_index && pv_img) { + const p = pv_index.findIndex((d, i) => value >= pv_index[i - 1] && value < d); + if (p >= 0 && pv_img[Math.floor(p / 100)]) { + this.#img.style.flexShrink = '0'; + this.#img.style.inlineSize = pv_x_size + 'px'; + this.#img.style.blockSize = pv_y_size + 'px'; + this.#img.style.scale = `${160 / pv_x_size}`; + this.#img.style.transformOrigin = 'bottom'; + this.#img.style.backgroundImage = `url(${pv_img[Math.floor(p / 100)]})`; + this.#img.style.backgroundPosition = pv_x_size * -(p % 100 % pv_x_len) + "px " + pv_y_size * -Math.floor(p % 100 / pv_y_len) + "px"; + + } + } + } + } + + #identify = () => { + delete this.pvData; + this.#img.removeAttribute('style'); + } +} + diff --git a/src/main/player/quality/index.ts b/src/main/player/quality/index.ts new file mode 100644 index 0000000..6d7eaee --- /dev/null +++ b/src/main/player/quality/index.ts @@ -0,0 +1,43 @@ +import { BilibiliPlayer } from ".."; +import { pgcPlayurl } from "../../../io/com/bilibili/api/pgc/player/web/playurl"; +import { pugvPlayurl } from "../../../io/com/bilibili/api/pugv/player/web/playurl"; +import { playurl } from "../../../io/com/bilibili/api/x/player/playurl"; +import { QUALITY_DESCRIBE } from "../../../io/quality"; +import { ev, PLAYER_EVENT } from "../../../player/event"; +import { toastr } from "../../../toastr"; +import { cookie } from "../../../utils/cookie"; +import { MAIN_EVENT, mainEv } from "../../event"; + +export class Quality { + + playurl?: Awaited>['result'] | Awaited>['data'] | Awaited>['data'] + + constructor(private player: BilibiliPlayer) { + + ev.bind(PLAYER_EVENT.QUALITY_CHANGE, ({ detail }) => { + cookie.set('CURRENT_QUALITY', detail); + if (detail === 0) { + // 自动画质无须切换 + ev.trigger(PLAYER_EVENT.QUALITY_SET_FOR, detail); + toastr.success(`已经切换至${QUALITY_DESCRIBE[detail]}画质`); + } else { + if (this.playurl?.dash?.video.find(d => d.id === detail)) { + // 无需联网请求的DASH画质 + ev.trigger(PLAYER_EVENT.QUALITY_SET_FOR, detail); + toastr.success(`已经切换至${QUALITY_DESCRIBE[detail]}画质`); + } else { + toastr.info(`正在为您切换到${QUALITY_DESCRIBE[detail]}画质,请稍候...`); + this.player.updateSource(detail, () => { + toastr.success(`已经切换至${QUALITY_DESCRIBE[detail]}画质`); + ev.trigger(PLAYER_EVENT.QUALITY_SET_FOR, detail); + }, () => { + toastr.error(`切换画质失败,已回滚~`); + }) + } + } + }); + mainEv.bind(MAIN_EVENT.PLAYURL, ({ detail }) => { + this.playurl = detail; + }); + } +} \ No newline at end of file diff --git a/src/main/player/recommend/index.ts b/src/main/player/recommend/index.ts new file mode 100644 index 0000000..a76315a --- /dev/null +++ b/src/main/player/recommend/index.ts @@ -0,0 +1,161 @@ +import { BilibiliPlayer } from ".."; +import { recommend } from "../../../io/com/bilibili/api/pgc/season/web/related/recommend"; +import { pgcSection } from "../../../io/com/bilibili/api/pgc/view/web/season/user/section"; +import { toviewWeb } from "../../../io/com/bilibili/api/x/v2/history/toview/web"; +import { related } from "../../../io/com/bilibili/api/x/web-interface/archive/related"; +import { detail } from "../../../io/com/bilibili/api/x/web-interface/view/detail"; +import { ev, PLAYER_EVENT } from "../../../player/event"; +import { MAIN_EVENT, mainEv } from "../../event"; +import { ROUTER } from "../../router"; + +export class Recommend { + + constructor(private player: BilibiliPlayer) { } + + async av() { + detail(this.player.aid) + .then(({ code, message, data }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, data } }); + if (data.View.ugc_season) { + // 视频合集 + if (data.View.ugc_season) { + this.player.$auxiliary.$recommend.add(data.View.ugc_season.sections[0].episodes.map(d => { + return { + src: d.arc.pic + '@.webp', + title: d.title, + duration: d.arc.duration, + view: d.arc.stat.view, + danmaku: d.arc.stat.danmaku, + callback() { + const url = new URL(`https://www.bilibili.com/video/av${d.aid}/`); + mainEv.trigger(MAIN_EVENT.NAVIGATE, [ROUTER.AV, url]); + history.replaceState(undefined, '', url); + }, + selected: d.cid === this.player.cid, + } + })); + this.player.$auxiliary.$filter.$recommend.textContent = '视频合集'; + } + } else { + related(this.player.aid).then(({ code, message, data }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, data } }); + this.player.$auxiliary.$recommend.add(data.map(d => { + return { + src: d.pic + '@.webp', + title: d.title, + duration: d.duration, + view: d.stat.view, + danmaku: d.stat.danmaku, + author: d.owner.name, + callback() { + const url = new URL(`https://www.bilibili.com/video/av${d.aid}/`); + mainEv.trigger(MAIN_EVENT.NAVIGATE, [ROUTER.AV, url]); + history.replaceState(undefined, '', url); + }, + } + })); + this.player.$auxiliary.$filter.$recommend.textContent = '推荐视频'; + }) + } + if (data.View.pages.length > 1) { + let ni = data.View.pages.findIndex(d => d.cid === this.player.cid); + if (ni >= 0 && ni + 1 < data.View.pages.length) { + const ep = data.View.pages[ni + 1]; + ep && ev.trigger(PLAYER_EVENT.CALL_NEXT_REGISTER, () => { + const url = new URL(`https://www.bilibili.com/video/av${this.player.aid}/?p=${ep.page}`); + mainEv.trigger(MAIN_EVENT.NAVIGATE, [ROUTER.AV, url]); + history.replaceState(undefined, '', url); + }); + } + } + }) + .catch(e => { + console.error('获取推荐数据失败', e); + }); + } + + async bangumi() { + recommend(this.player.ssid) + .then(({ code, message, data }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, data } }); + this.player.$auxiliary.$recommend.add(data.season.map(d => { + return { + src: d.cover + '@.webp', + title: d.title, + view: d.stat.view, + danmaku: d.stat.danmaku, + callback() { + const url = new URL(d.url); + mainEv.trigger(MAIN_EVENT.NAVIGATE, [ROUTER.BANGUMI, url]); + history.replaceState(undefined, '', url); + }, + } + })); + }) + .catch(e => { + console.error('获取推荐数据失败', e); + }); + pgcSection(this.player.ssid) + .then(({ code, message, result }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, result } }); + if (result.main_section) { + const ni = result.main_section.episodes.findIndex(d => d.id === this.player.epid); + if (ni >= 0 && ni + 1 < result.main_section.episodes.length) { + const ep = result.main_section.episodes[ni + 1]; + ep && ev.trigger(PLAYER_EVENT.CALL_NEXT_REGISTER, () => { + const url = new URL(`https://www.bilibili.com/bangumi/play/ep${ep.id}`); + mainEv.trigger(MAIN_EVENT.NAVIGATE, [ROUTER.BANGUMI, url]); + history.replaceState(undefined, '', url); + }); + } + } + }) + .catch(e => { + console.error('获取分P数据失败', e); + }) + } + + async toview(list: Awaited>['data']['list']) { + let ni = -1; + this.player.$auxiliary.$recommend.add(list.map((d, i) => { + d.aid === this.player.aid && (ni = i); + return { + src: d.pic + '@.webp', + title: d.title, + duration: d.duration, + view: d.stat.view, + danmaku: d.stat.danmaku, + callback() { + const url = new URL(`https://www.bilibili.com/watchlater/#/av${d.aid}`); + mainEv.trigger(MAIN_EVENT.NAVIGATE, [ROUTER.TOVIEW, url]); + history.replaceState(undefined, '', url); + }, + selected: d.aid === this.player.aid, + } + })); + this.player.$auxiliary.$filter.$recommend.textContent = '稍后再看'; + if (ni >= 0) { + const ep = list[ni]; + if (ep.pages.length > 1) { + const ni = ep.pages.findIndex(d => d.cid === this.player.cid); + if (ni >= 0 && ni + 1 < ep.pages.length) { + const part = ep.pages[ni + 1]; + part && ev.trigger(PLAYER_EVENT.CALL_NEXT_REGISTER, () => { + const url = new URL(`https://www.bilibili.com/watchlater/#/av${ep.aid}/p${part.page}`); + mainEv.trigger(MAIN_EVENT.NAVIGATE, [ROUTER.TOVIEW, url]); + history.replaceState(undefined, '', url); + }); + return + } + } + if (ni + 1 < list.length) { + const ep = list[ni + 1]; + ep && ev.trigger(PLAYER_EVENT.CALL_NEXT_REGISTER, () => { + const url = new URL(`https://www.bilibili.com/watchlater/#/av${ep.aid}/`); + mainEv.trigger(MAIN_EVENT.NAVIGATE, [ROUTER.TOVIEW, url]); + history.replaceState(undefined, '', url); + }); + } + } + } +} \ No newline at end of file diff --git a/src/main/bilibili/player/closed-caption/index.ts b/src/main/player/v2/ClosedCaption.ts similarity index 100% rename from src/main/bilibili/player/closed-caption/index.ts rename to src/main/player/v2/ClosedCaption.ts diff --git a/src/main/bilibili/player/closed-caption/WebVTT.ts b/src/main/player/v2/WebVTT.ts similarity index 99% rename from src/main/bilibili/player/closed-caption/WebVTT.ts rename to src/main/player/v2/WebVTT.ts index 841f73e..0738c30 100644 --- a/src/main/bilibili/player/closed-caption/WebVTT.ts +++ b/src/main/player/v2/WebVTT.ts @@ -1,4 +1,4 @@ -import { Format } from "../../../../utils/fomat"; +import { Format } from "../../../utils/fomat"; /** WebVTT */ export class WebVTT { diff --git a/src/main/player/v2/index.ts b/src/main/player/v2/index.ts new file mode 100644 index 0000000..d74c658 --- /dev/null +++ b/src/main/player/v2/index.ts @@ -0,0 +1,41 @@ +import { BilibiliPlayer } from ".."; +import { v2 } from "../../../io/com/bilibili/api/x/player/v2"; +import { https } from "../../../utils/https"; +import { MAIN_EVENT, mainEv } from "../../event"; +import { ClosedCaption } from "./ClosedCaption"; + +export class V2 { + + constructor(private player: BilibiliPlayer) { + + mainEv.bind(MAIN_EVENT.CONNECT, this.connect) + } + + private connect = () => { + v2(this.player.cid, this.player.aid, this.player.ssid) + .then(({ code, message, data }) => { + if (code !== 0) throw new ReferenceError(message, { cause: { code, message, data } }); + this.subtitle(data.subtitle) + }) + .catch(e => { + console.error('获取播放器接口数据错误', e) + }) + } + + /** 添加字幕 */ + private subtitle(list: Awaited>['data']['subtitle']) { + list?.subtitles.forEach(d => { + fetch(https(d.subtitle_url)) + .then(e => e.json()) + .then(f => { + const cc = new ClosedCaption(); + cc.addCue(...f.body); + const file = new File([cc.toWebVTT().toJSON()], d.lan_doc); + this.player.$area.$control.$subtitle.load(file); + }) + .catch(e => { + console.error('获取 CC 字幕出错', e); + }); + }) + } +} \ No newline at end of file diff --git a/src/player/policy.ts b/src/main/policy.ts similarity index 100% rename from src/player/policy.ts rename to src/main/policy.ts diff --git a/src/main/router.ts b/src/main/router.ts new file mode 100644 index 0000000..1126b39 --- /dev/null +++ b/src/main/router.ts @@ -0,0 +1,7 @@ +/** 路由列表 */ +export enum ROUTER { + AV, + BANGUMI, + HOME, + TOVIEW, +} \ No newline at end of file diff --git a/src/main/tsconfig.json b/src/main/tsconfig.json deleted file mode 100644 index 68697f3..0000000 --- a/src/main/tsconfig.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "extends": "../tsconfig-base.json", - "include": [ - "**/*", - "../utils", - "../player", - "../io", - ], - "references": [ - { - "path": "../utils" - }, - { - "path": "../player" - }, - { - "path": "../io" - } - ], - "compilerOptions": { - "types": [ - "navigation-api-types" - ] - } -} \ No newline at end of file diff --git a/dist/manifest.json b/src/manifest.json similarity index 50% rename from dist/manifest.json rename to src/manifest.json index f1a607d..eb24364 100644 --- a/dist/manifest.json +++ b/src/manifest.json @@ -1,14 +1,11 @@ { + "$schema": "https://json.schemastore.org/chrome-manifest", "action": { "default_icon": { "128": "assets/icon.png" } }, "author": "Akari", - "background": { - "service_worker": "sw/index.js", - "type": "module" - }, "content_scripts": [ { "all_frames": true, @@ -20,39 +17,6 @@ ], "run_at": "document_start", "world": "MAIN" - }, - { - "all_frames": true, - "js": [ - "/main/WebRTC.js" - ], - "matches": [ - "" - ], - "run_at": "document_start", - "world": "MAIN" - }, - { - "all_frames": true, - "js": [ - "/main/bilibili/space/index.js" - ], - "matches": [ - "*://space.bilibili.com/*" - ], - "run_at": "document_start", - "world": "MAIN" - }, - { - "all_frames": true, - "js": [ - "/main/bilibili/live/index.js" - ], - "matches": [ - "*://live.bilibili.com/*" - ], - "run_at": "document_start", - "world": "MAIN" } ], "declarative_net_request": { @@ -73,12 +37,26 @@ "128": "assets/icon.png" }, "manifest_version": 3, + "minimum_chrome_version": "125", "name": "__MSG_name__", + "options_page": "options/index.html", "permissions": [ - "storage", - "cookies", "declarativeNetRequest", - "declarativeNetRequestWithHostAccess" + "declarativeNetRequestWithHostAccess", + "sidePanel" ], - "version": "1.0.3.0" + "side_panel": { + "default_path": "sidebar/index.html" + }, + "version": "1.0.4.0", + "web_accessible_resources": [ + { + "resources": [ + "*" + ], + "matches": [ + "*://*.bilibili.com/*" + ] + } + ] } \ No newline at end of file diff --git a/src/options/index.css b/src/options/index.css new file mode 100644 index 0000000..110c8c0 --- /dev/null +++ b/src/options/index.css @@ -0,0 +1,37 @@ +:root { + color-scheme: light dark; +} + +body { + margin: 0; + + /* 测试用 */ + block-size: 100vb; + text-align: center; + align-content: center; +} + +.player-box { + --inline-size: 980px; + + inline-size: var(--inline-size); + margin-inline: auto; + + @media screen and (min-width:1400px) { + + & { + --inline-size: 1160px; + } + } + + @media screen and (min-width:2500px) { + + & { + --inline-size: 1920px; + } + } + + >.bilibili-player { + block-size: calc(var(--inline-size) / 16 * 9 + 68px); + } +} \ No newline at end of file diff --git a/src/options/index.d.css.ts b/src/options/index.d.css.ts new file mode 100644 index 0000000..eb9bc4f --- /dev/null +++ b/src/options/index.d.css.ts @@ -0,0 +1,2 @@ +declare const stylesheet: CSSStyleSheet; +export default stylesheet; \ No newline at end of file diff --git a/src/options/index.html b/src/options/index.html new file mode 100644 index 0000000..d62e25c --- /dev/null +++ b/src/options/index.html @@ -0,0 +1,15 @@ + + + + + + 哔哩哔哩 (゜-゜)つロ 干杯~-bilibili + + + + +
    + + + + \ No newline at end of file diff --git a/src/options/index.ts b/src/options/index.ts new file mode 100644 index 0000000..3969fe1 --- /dev/null +++ b/src/options/index.ts @@ -0,0 +1,20 @@ +import { Player } from "../player"; +import { toastr } from "../toastr"; +import stylesheet from "./index.css" with {type: 'css'}; + +document.adoptedStyleSheets = [stylesheet]; + +const player = new Player(); +player.classList.add('bilibili-player'); + +document.body.querySelector('.player-box')?.appendChild(player); + +const toast = toastr.info( + '欢迎使用 Bilibili-2019 核心播放器~', + '您可以点击播放器右上角三点【加载文件】来作为离线播放器使用', + '格式支持:', + '视频:.mp4', + '弹幕:.xml .json', + '字幕:.vtt', +); +toast.$delay = 60; \ No newline at end of file diff --git a/src/player/README.md b/src/player/README.md deleted file mode 100644 index e3f2607..0000000 --- a/src/player/README.md +++ /dev/null @@ -1 +0,0 @@ -播放器组件 \ No newline at end of file diff --git a/src/player/area/control/danmaku.ts b/src/player/area/control/danmaku.ts index 73f3e3d..10ba638 100644 --- a/src/player/area/control/danmaku.ts +++ b/src/player/area/control/danmaku.ts @@ -1,20 +1,20 @@ +import { Player } from "../.."; +import svg_24danmuforbid from "../../../assets/svg/24danmuforbid.svg"; +import svg_24danmuoff from "../../../assets/svg/24danmuoff.svg"; +import svg_24danmuon from "../../../assets/svg/24danmuon.svg"; +import svg_48danmubottom from "../../../assets/svg/48danmubottom.svg"; +import svg_48danmunormal from "../../../assets/svg/48danmunormal.svg"; +import svg_48danmutop from "../../../assets/svg/48danmutop.svg"; import { DANMAKU } from "../../../danmaku/block"; import { customElement } from "../../../utils/Decorator/customElement"; import { Element } from "../../../utils/element"; -import svg_danmaku_forbid from "../../assets/svg/danmaku-forbid.svg"; -import svg_danmaku_mode_1 from "../../assets/svg/danmaku-mode-1.svg"; -import svg_danmaku_mode_4 from "../../assets/svg/danmaku-mode-4.svg"; -import svg_danmaku_mode_5 from "../../assets/svg/danmaku-mode-5.svg"; -import svg_danmaku_off from "../../assets/svg/danmaku-off.svg"; -import svg_danmaku from "../../assets/svg/danmaku.svg"; -import { PLAYER_EVENT, ev } from "../../event-target"; +import { ev, PLAYER_EVENT } from "../../event"; import { options } from "../../option"; -import { Checkbox } from "../../widget/checkbox"; import { Slider } from "../../widget/slider"; -/** 播放器弹幕控制 */ -@customElement('button') -export class Danmaku extends HTMLButtonElement { +/** 弹幕控制 */ +@customElement('label') +export class Danmaku extends HTMLLabelElement { /** * 需要监听变动的属性。 @@ -30,88 +30,110 @@ export class Danmaku extends HTMLButtonElement { */ // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} - /** 初始化标记 */ - // #inited = false; - /** 每当元素添加到文档中时调用。 */ - // connectedCallback() {} + connectedCallback() { + this.insertAdjacentElement('afterend', this.#wrap); + self.addEventListener('keydown', this.#onKeyDown); + } /** 每当元素从文档中移除时调用。 */ - // disconnectedCallback() {} + disconnectedCallback() { + this.#wrap.remove(); + self.removeEventListener('keydown', this.#onKeyDown); + } /** 每当元素被移动到新文档中时调用。 */ // adoptedCallback() {} - /** 浮动面板 */ - private $wrap = Element.add('div', { class: 'bofqi-control-danmaku-wrap' }, this); + #player: Player; + + #wrap = Element.add('div', { class: 'bofqi-danmaku-wrap' }); /** 不透明度 */ - private $opacity = this.$wrap.appendChild(new Slider()); + #opacity = new Slider(); /** 防挡字幕 */ - private $preventShade = this.$wrap.appendChild(new Checkbox()); + #preventShade = Element.add('input', { attribute: { type: 'checkbox' } }); /** 顶端弹幕 */ - private $top = Element.add('div', { class: 'bofqi-control-danmaku-block' }, this.$wrap, svg_danmaku_mode_5 + svg_danmaku_forbid); + #top = Element.add('div', { class: 'bofqi-danmaku-block', innerHTML: svg_48danmutop + svg_24danmuforbid, data: { label: '顶端弹幕' } }); /** 底端弹幕 */ - private $bottom = Element.add('div', { class: 'bofqi-control-danmaku-block' }, this.$wrap, svg_danmaku_mode_4 + svg_danmaku_forbid); + #bottom = Element.add('div', { class: 'bofqi-danmaku-block', innerHTML: svg_48danmubottom + svg_24danmuforbid, data: { label: '底端弹幕' } }); /** 滚动弹幕 */ - private $scroll = Element.add('div', { class: 'bofqi-control-danmaku-block' }, this.$wrap, svg_danmaku_mode_1 + svg_danmaku_forbid); + #scroll = Element.add('div', { class: 'bofqi-danmaku-block', innerHTML: svg_48danmunormal + svg_24danmuforbid, data: { label: '滚动弹幕' } }); - constructor() { + constructor(player: Player) { super(); - this.classList.add('bofqi-control-button', 'bofqi-control-danmaku'); - this.insertAdjacentHTML('afterbegin', svg_danmaku + svg_danmaku_off); - this.$opacity.insertAdjacentHTML('beforebegin', '不透明度'); - this.$preventShade.insertAdjacentHTML('beforebegin', '
    '); - this.$top.insertAdjacentHTML('beforebegin', '
    '); + this.#player = player; + this.classList.add('bofqi-area-control-btn', 'bofqi-area-danmaku'); + this.innerHTML = svg_24danmuon + svg_24danmuoff; - this.$opacity.classList.add('bofqi-control-danmaku-opacity'); - this.$opacity.min = '1'; - this.$opacity.$hint = true; - this.$opacity.formatHint(v => { + this.#opacity.$hint = true; + this.#opacity.formatHint(v => { return Math.floor(+v) + '%'; }); - this.$preventShade.$prev = true; - this.$preventShade.$text = '防挡字幕'; - this.$top.dataset.label = '顶端弹幕'; - this.$bottom.dataset.label = '底端弹幕'; - this.$scroll.dataset.label = '滚动弹幕'; - ev.bind(PLAYER_EVENT.OPTINOS_CHANGE, ({ detail }) => { - const { visible, opacity, preventShade, block } = detail.danmaku; + this.#wrap.append( + Element.add('div', { class: 'bofqi-danmaku-wrap-item', data: { label: '不透明度' }, children: this.#opacity }), + Element.add('div', { class: 'bofqi-danmaku-wrap-item', data: { label: '防档字幕' }, children: this.#preventShade }), + Element.add('div', { class: ['bofqi-danmaku-wrap-item', 'space-between'], children: [this.#top, this.#bottom, this.#scroll] }), + ); - this.classList.toggle('off', !visible); - this.$opacity.$value = Math.max(1, Math.min(100, opacity * 100)); - this.$preventShade.$value = preventShade; - this.$top.classList.toggle('block', Boolean(block & DANMAKU.TOP)); - this.$bottom.classList.toggle('block', Boolean(block & DANMAKU.BOTTOM)); - this.$scroll.classList.toggle('block', Boolean(block & DANMAKU.SCROLL)); - }); - this.$wrap.addEventListener('click', e => { - e.stopPropagation(); - }); - this.addEventListener('click', () => { - options.danmaku.visible = !options.danmaku.visible; - }); - this.$opacity.addEventListener('change', () => { - options.danmaku.opacity = Math.max(0.01, Math.min(1, +this.$opacity.$value / 100)); + this.addEventListener('click', this.#toggle); + this.#opacity.addEventListener('change', () => { + options.danmaku.opacity = Math.max(0.01, Math.min(1, +this.#opacity.$value / 100)); }); - this.$preventShade.addEventListener('change', () => { - options.danmaku.preventShade = this.$preventShade.$value; + this.#preventShade.addEventListener('change', () => { + options.danmaku.preventShade = this.#preventShade.checked; }); - this.$top.addEventListener('click', () => { + this.#top.addEventListener('click', () => { options.danmaku.block ^= DANMAKU.TOP; }); - this.$bottom.addEventListener('click', () => { + this.#bottom.addEventListener('click', () => { options.danmaku.block ^= DANMAKU.BOTTOM; }); - this.$scroll.addEventListener('click', () => { + this.#scroll.addEventListener('click', () => { options.danmaku.block ^= DANMAKU.SCROLL; }); + + ev.bind(PLAYER_EVENT.OPTINOS_CHANGE, ({ detail }) => { + const { visible, opacity, preventShade, block } = detail.danmaku; + + this.classList.toggle('off', !visible); + this.#opacity.$value = Math.max(1, Math.min(100, opacity * 100)); + this.#preventShade.checked = preventShade; + this.#top.classList.toggle('block', Boolean(block & DANMAKU.TOP)); + this.#bottom.classList.toggle('block', Boolean(block & DANMAKU.BOTTOM)); + this.#scroll.classList.toggle('block', Boolean(block & DANMAKU.SCROLL)); + }); + } + + /** 键盘事件回调 */ + #onKeyDown = ({ key, shiftKey, ctrlKey, altKey, metaKey }: KeyboardEvent) => { + try { + const { activeElement } = document; + if (activeElement === document.body || activeElement === this.#player || this.#player.contains(activeElement) && !(activeElement?.hasAttribute('contenteditable') || activeElement instanceof HTMLInputElement || activeElement instanceof HTMLTextAreaElement)) { + switch (key) { + // 不能区分小键盘,但能识别 Shift 后的值 + case 'D': case 'd': { + // 弹幕开关 + shiftKey || ctrlKey || altKey || metaKey || this.#toggle(); + break; + } + } + // switch (code) { + // // 能区分小键盘,但不识别 Shift 后的值 + // } + } + } catch { } + } + + /** 开关切换 */ + #toggle = () => { + options.danmaku.visible = !options.danmaku.visible; } } \ No newline at end of file diff --git a/src/player/area/control/fullscreen-web.ts b/src/player/area/control/fullscreen-web.ts deleted file mode 100644 index d2b9c57..0000000 --- a/src/player/area/control/fullscreen-web.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { customElement } from "../../../utils/Decorator/customElement"; -import svg_screen_full_web_exit from "../../assets/svg/screen-full-web-exit.svg"; -import svg_screen_full_web from "../../assets/svg/screen-full-web.svg"; -import { PLAYER_EVENT, ev } from "../../event-target"; -import { PLAYER_MODE, PLAYER_STATE } from "../../state"; - -/** 播放器网页全屏控制 */ -@customElement('button') -export class FullscreenWeb extends HTMLButtonElement { - - /** - * 需要监听变动的属性。 - * 与实例方法`attributeChangedCallback`配合使用。 - * 此字符串序列定义了`attributeChangedCallback`回调时的第一个参数的可能值。 - */ - // static observedAttributes = []; - - /** - * 在属性更改、添加、移除或替换时调用。 - * 需要与静态属性`observedAttributes`配合使用。 - * 此回调的第一个参数在`observedAttributes`数组中定义。 - */ - // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} - - /** 初始化标记 */ - // #inited = false; - - /** 每当元素添加到文档中时调用。 */ - // connectedCallback() {} - - /** 每当元素从文档中移除时调用。 */ - // disconnectedCallback() {} - - /** 每当元素被移动到新文档中时调用。 */ - // adoptedCallback() {} - - constructor() { - super(); - - this.classList.add('bofqi-control-button', 'bofqi-control-fullscreen-web'); - - this.insertAdjacentHTML('afterbegin', svg_screen_full_web); - - this.addEventListener('click', () => { - ev.trigger(PLAYER_EVENT.PLAYER_MODE, PLAYER_STATE.mode ^= PLAYER_MODE.WEB); - }); - - ev.bind(PLAYER_EVENT.PLAYER_MODE, ({ detail }) => { - this.innerHTML = detail & PLAYER_MODE.WEB ? svg_screen_full_web_exit : svg_screen_full_web; - }); - } -} \ No newline at end of file diff --git a/src/player/area/control/fullscreen.ts b/src/player/area/control/fullscreen.ts deleted file mode 100644 index 0724f70..0000000 --- a/src/player/area/control/fullscreen.ts +++ /dev/null @@ -1,119 +0,0 @@ -import { customElement } from "../../../utils/Decorator/customElement"; -import svg_screen_full from "../../assets/svg/screen-full.svg"; -import { PLAYER_EVENT, ev } from "../../event-target"; -import { PLAYER_MODE, PLAYER_STATE } from "../../state"; - -/** 播放器全屏控制 */ -@customElement('button') -export class Fullscreen extends HTMLButtonElement { - - /** - * 需要监听变动的属性。 - * 与实例方法`attributeChangedCallback`配合使用。 - * 此字符串序列定义了`attributeChangedCallback`回调时的第一个参数的可能值。 - */ - // static observedAttributes = []; - - /** - * 在属性更改、添加、移除或替换时调用。 - * 需要与静态属性`observedAttributes`配合使用。 - * 此回调的第一个参数在`observedAttributes`数组中定义。 - */ - // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} - - /** 初始化标记 */ - // #inited = false; - - /** 每当元素添加到文档中时调用。 */ - // connectedCallback() {} - - /** 每当元素从文档中移除时调用。 */ - // disconnectedCallback() {} - - /** 每当元素被移动到新文档中时调用。 */ - // adoptedCallback() {} - - /** 全屏时鼠标显隐控制句柄 */ - protected cursorTimer?: number; - /** 全屏时控制栏显隐控制句柄 */ - protected opacityTimer?: number; - /** 是否显示鼠标 */ - protected isCursor = true; - /** 是否显示控制栏 */ - protected isOpacity = true; - - constructor() { - super(); - - this.classList.add('bofqi-control-button', 'bofqi-control-fullscreen'); - - this.insertAdjacentHTML('afterbegin', svg_screen_full); - - this.addEventListener('click', () => { - ev.trigger(PLAYER_EVENT.PLAYER_MODE, PLAYER_STATE.mode ^= PLAYER_MODE.FULL); - }); - - - ev.bind(PLAYER_EVENT.FULLSCREEN_CHANGE, ({ detail }) => { - if (detail) { - self.addEventListener('pointermove', this.pointerMove); - } else { - clearTimeout(this.cursorTimer); - clearTimeout(this.opacityTimer); - self.removeEventListener('pointermove', this.pointerMove); - this.cursor(); - this.opacity(); - } - }); - } - - /** 全屏移动鼠标响应 */ - protected pointerMove = (ev: MouseEvent) => { - clearTimeout(this.cursorTimer); - clearTimeout(this.opacityTimer); - if (ev.clientY) { - if (ev.clientY < this.parentElement!.parentElement!.clientHeight / 3 * 2) { - // 仅显示鼠标 - this.cursor(); - this.noCursor(2000); - this.noOpacity(2000); - } else { - // 显示鼠标和控制栏 - this.cursor(); - this.opacity(); - this.noCursor(2000); - this.noOpacity(2000); - } - } - } - - /** 显示鼠标 */ - protected cursor() { - this.isCursor || this.parentElement!.parentElement!.classList.remove('no-cursor'); - this.isCursor = true; - } - - /** 隐藏鼠标 */ - protected noCursor(time = 5000) { - this.cursorTimer = setTimeout(() => { - this.parentElement!.parentElement!.classList.add('no-cursor'); - this.isCursor = false; - }, time); - } - - /** 显示控制栏 */ - protected opacity() { - if (!this.isOpacity) { - this.parentElement!.parentElement!.classList.remove('hide'); - this.isOpacity = true; - } - } - - /** 隐藏控制栏 */ - protected noOpacity(time = 5000) { - this.opacityTimer = setTimeout(() => { - this.parentElement!.parentElement!.classList.add('hide'); - this.isOpacity = false; - }, time); - } -} \ No newline at end of file diff --git a/src/player/area/control/index.css b/src/player/area/control/index.css new file mode 100644 index 0000000..8453e00 --- /dev/null +++ b/src/player/area/control/index.css @@ -0,0 +1,555 @@ +.bofqi-area-control { + block-size: 30px; + padding-inline: .75em; + flex-shrink: 0; + border-block: 1px solid var(--e2e2e2); + display: flex; + column-gap: .75em; + z-index: 1; + + @container bofqi style(--fullscreen: 1) { + & { + border-block: 0; + background-color: var(--fff); + border-block-start: 1px solid var(--e2e2e2); + pointer-events: auto; + } + } + + >.bofqi-area-control-btn { + flex-shrink: 0; + transition: .3s; + cursor: pointer; + text-align: center; + align-content: center; + + >svg { + block-size: 1.5em; + aspect-ratio: 1; + fill: var(--99a2aa); + transition: .3s; + } + + &:hover { + background-color: var(--f4f5f7); + color: var(--6d757a); + + >svg { + fill: var(--6d757a); + } + } + + &.disabled { + pointer-events: none; + + >svg { + fill: var(--ccd0d7); + } + } + + &.hidden { + display: none; + } + } + + >.bofqi-area-toggle { + + &.paused>svg:last-child { + display: none; + } + + &:not(.paused)>svg:first-child { + display: none; + } + } + + >.bofqi-control-time { + flex-shrink: 0; + display: flex; + justify-content: center; + block-size: inherit; + align-items: center; + + .bofqi-control-time-seek { + display: none; + inline-size: 60px; + margin-inline: 10px; + padding-inline: 5px; + block-size: 20px; + font-size: 12px; + color: var(--99a2aa); + text-align: center; + border: 1px solid var(--e2e2e2); + + &:focus-visible { + outline: none; + } + } + + &.seeking { + .bofqi-control-time-seek { + display: flex; + } + } + + .bofqi-control-time-wrap { + margin-inline: 10px; + block-size: inherit; + display: flex; + align-items: center; + justify-content: center; + white-space: nowrap; + color: var(--99a2aa); + + .bofqi-control-divider { + padding-inline: 2px; + } + } + + &.seeking { + .bofqi-control-time-wrap { + display: none; + } + } + } + + >.bofqi-area-volume { + anchor-name: --bofqi-volume; + + &:not(.large, .muted)>svg { + + &:nth-child(2), + &:last-child { + display: none; + } + } + + &.large:not(.muted)>svg { + + &:first-child, + &:last-child { + display: none; + } + } + + &.muted>svg { + + &:first-child, + &:nth-child(2) { + display: none; + } + } + + &:not(:hover)~.bofqi-volume-wrap:not(:hover) { + display: none; + opacity: 0; + scale: 1 0; + } + } + + >.bofqi-volume-wrap { + position: absolute; + position-anchor: --bofqi-volume; + position-area: block-start; + inline-size: 30px; + block-size: 100px; + background-color: var(--fff); + border: 1px solid var(--e2e2e2); + border-start-start-radius: 4px; + border-start-end-radius: 4px; + display: flex; + flex-direction: column; + align-items: center; + overflow: clip; + transform-origin: center 100%; + transition: all .3s allow-discrete; + + @starting-style { + opacity: 0; + scale: 1 0; + } + + &::before { + content: attr(data-volume); + padding-block: .5em; + } + + >.bofqi-volume-slider { + rotate: -90deg; + inline-size: 70px; + } + } + + >.bofqi-control-quality { + anchor-name: --bofqi-quality; + min-inline-size: 50px; + + &::before { + content: attr(data-label); + font-size: 12px; + color: var(--99a2aa); + } + + &.disabled { + display: none; + } + + &:not(:hover)~.bofqi-quality-wrap:not(:hover) { + display: none; + opacity: 0; + scale: 1 0; + } + } + + >.bofqi-quality-wrap { + list-style: none; + margin: 0; + padding: 0; + position: absolute; + position-anchor: --bofqi-quality; + position-area: block-start; + color: var(--6d757a); + background-color: var(--fff); + border: 1px solid var(--e2e2e2); + border-start-start-radius: 4px; + border-start-end-radius: 4px; + display: flex; + align-items: center; + flex-direction: column-reverse; + overflow: clip; + transform-origin: center 100%; + transition: all .3s allow-discrete; + + >li { + display: flex; + align-items: center; + justify-content: center; + color: var(--222); + block-size: 32px; + padding-inline: 20px; + transition: background-color .3s; + cursor: pointer; + + &:hover { + background-color: var(--e5e9ef); + } + + &.selected { + color: var(--00a1d6); + cursor: default; + pointer-events: none; + } + } + } + + >.bofqi-area-danmaku { + anchor-name: --bofqi-danmaku; + + &.off>svg:first-child { + display: none; + } + + &:not(.off)>svg:last-child { + display: none; + } + + &:not(:hover)~.bofqi-danmaku-wrap:not(:hover) { + display: none; + opacity: 0; + scale: 1 0; + } + } + + >.bofqi-danmaku-wrap { + position: absolute; + position-anchor: --bofqi-danmaku; + position-area: block-start; + position-try: block-start span-inline-start; + padding-inline: 1.5em; + padding-block-start: 1.5em; + padding-block-end: 2em; + color: var(--6d757a); + background-color: var(--fff); + border: 1px solid var(--e2e2e2); + border-start-start-radius: 4px; + border-start-end-radius: 4px; + display: flex; + flex-direction: column; + row-gap: 1em; + overflow: clip; + transform-origin: center 100%; + transition: all .3s allow-discrete; + + @starting-style { + opacity: 0; + scale: 1 0; + } + + >.bofqi-danmaku-wrap-item { + display: flex; + column-gap: 1em; + + &.space-between { + justify-content: space-between; + } + + &[data-label]::before { + content: attr(data-label); + white-space: nowrap; + } + + .bofqi-danmaku-block { + display: flex; + flex-direction: column; + align-items: center; + transition: color .3s; + position: relative; + cursor: pointer; + + &::after { + content: attr(data-label); + display: block; + } + + >svg { + block-size: 1em; + aspect-ratio: 1; + + &:not(:last-child) { + font-size: 48px; + fill: #99a2aa; + transition: fill .3s; + anchor-name: --danmaku-block; + } + + &:last-child { + position: absolute; + inset-inline-end: anchor(--danmaku-block end); + inset-block-end: anchor(--danmaku-block end); + font-size: 24px; + background-color: #fff; + border: 3px solid #fff; + fill: #99a2aa; + border-radius: 50%; + box-sizing: border-box; + opacity: 0; + transition: all .3s; + } + } + + &:hover { + + >svg { + &:not(:last-child) { + fill: #6d757a; + } + + &:last-child { + opacity: 1; + fill: #6d757a; + } + } + } + + &.block { + color: #99a2aa; + + >svg { + + &:last-child { + opacity: 1; + fill: #f25d8e; + } + } + } + } + } + } + + .bofqi-area-subtitle { + font-size: 20px; + anchor-name: --bofqi-subtitle; + + &.disabled { + display: none; + } + + &.on>svg:first-child { + display: none; + } + + &:not(.on)>svg:last-child { + display: none; + } + + &:not(:hover)~.bofqi-subtitle-wrap:not(:hover) { + display: none; + opacity: 0; + scale: 1 0; + } + } + + >.bofqi-subtitle-wrap { + position: absolute; + position-anchor: --bofqi-subtitle; + position-area: block-start span-inline-start; + inline-size: 280px; + padding-inline: 1em; + padding-block-start: 1em; + padding-block-end: 1.5em; + color: var(--6d757a); + background-color: var(--fff); + border: 1px solid var(--e2e2e2); + border-start-start-radius: 4px; + border-start-end-radius: 4px; + display: flex; + flex-wrap: wrap; + justify-content: space-between; + align-items: center; + row-gap: .5em; + overflow: clip; + transform-origin: center 100%; + transition: all .3s allow-discrete; + + @starting-style { + opacity: 0; + scale: 1 0; + } + + .bofqi-subtitle-title { + inline-size: 100%; + } + + .bofqi-subtitle-select { + max-inline-size: 75%; + } + + .bpui-slider { + flex-grow: 1; + block-size: 1.5em; + } + + .closed-caption-color { + flex-grow: 1; + block-size: 1.5em; + border: 1px solid var(--e2e2e2); + border-radius: 4px; + background-color: var(--caption-color); + } + + .bpui-select { + flex-grow: 1; + color: var(--6d757a); + block-size: 1.5em; + } + + hr { + inline-size: 100%; + block-size: 0; + border: 0; + margin: 0; + } + } + + >.bofqi-area-repeat { + &.on>svg:first-child { + display: none; + } + + &:not(.on)>svg:last-child { + display: none; + } + } + + >.bofqi-area-screen-wide { + @container bofqi style(--screen-wide: 0) { + &>svg:last-child { + display: none; + } + } + + @container bofqi style(--screen-wide: 1) { + &>svg:first-child { + display: none; + } + } + } + + >.bofqi-area-screen-full { + anchor-name: --screen-full; + + @media (display-mode: picture-in-picture) { + & { + display: none; + } + } + + @container bofqi style(--fullscreen: 0) { + &>svg:last-child { + display: none; + } + } + + @container bofqi style(--fullscreen: 1) { + &>svg:first-child { + display: none; + } + } + } + + >.bofqi-area-screen-web { + position: absolute; + position-anchor: --screen-full; + position-area: block-start; + background-color: var(--fff); + overflow: clip; + transform-origin: center 100%; + transition: all .3s allow-discrete; + anchor-name: --screen-web; + + @starting-style { + scale: 1 0; + } + + @container bofqi style(--screen-web: 0) { + &>svg:last-child { + display: none; + } + } + + @container bofqi style(--screen-web: 1) { + &>svg:first-child { + display: none; + } + } + } + + >.bofqi-area-screen-pip { + position: absolute; + position-anchor: --screen-web; + position-area: block-start; + border-start-start-radius: 4px; + border-start-end-radius: 4px; + background-color: var(--fff); + overflow: clip; + transform-origin: center 100%; + transition: all .3s allow-discrete; + + @starting-style { + scale: 1 0; + } + + &>svg:last-child { + display: none; + } + } + + &:not(:has(.bofqi-area-screen-full:hover), :has(.bofqi-area-screen-web:hover), :has(.bofqi-area-screen-pip:hover)) { + + >.bofqi-area-screen-web, + >.bofqi-area-screen-pip { + scale: 1 0; + display: none; + } + } +} \ No newline at end of file diff --git a/src/player/area/control/index.ts b/src/player/area/control/index.ts index 19a93bc..aa022af 100644 --- a/src/player/area/control/index.ts +++ b/src/player/area/control/index.ts @@ -1,17 +1,18 @@ +import { Player } from "../.."; import { customElement } from "../../../utils/Decorator/customElement"; -import { ClosedCaption } from "./closed-caption"; import { Danmaku } from "./danmaku"; -import { Fullscreen } from "./fullscreen"; -import { FullscreenWeb } from "./fullscreen-web"; import { Next } from "./next"; -import { Pip } from "./pip"; import { Progress } from "./progress"; import { Quality } from "./quality"; import { Repeat } from "./repeat"; +import { ScreenFull } from "./screen-full"; +import { ScreenPip } from "./screen-pip"; +import { ScreenWeb } from "./screen-web"; +import { ScreenWide } from "./screen-wide"; +import { Subtitle } from "./subtitle"; import { Time } from "./time"; import { Toggle } from "./toggle"; import { Volume } from "./volume"; -import { Wide } from "./wide"; /** 播放器控制区域 */ @customElement('div') @@ -31,9 +32,6 @@ export class Control extends HTMLDivElement { */ // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} - /** 初始化标记 */ - // #inited = false; - /** 每当元素添加到文档中时调用。 */ // connectedCallback() { } @@ -43,48 +41,51 @@ export class Control extends HTMLDivElement { /** 每当元素被移动到新文档中时调用。 */ // adoptedCallback() {} - /** 播放器播放暂停控制 */ - $play = this.appendChild(new Toggle()); + #player: Player; + + #toggle: Toggle; - /** 播放器下一视频控制 */ - $next = this.appendChild(new Next()); + #next: Next; - /** 进度条 */ - $progress = this.appendChild(new Progress()); + $progress: Progress; - /** 播放器时间轴显示 */ - $time = this.appendChild(new Time()); + #time: Time; - /** 播放器音量控制 */ - $volume = this.appendChild(new Volume()); + #volume: Volume; - /** 播放器画质控制 */ - $quality = this.appendChild(new Quality()); + $quality: Quality; - /** 播放器弹幕控制 */ - $danmaku = this.appendChild(new Danmaku()); + #danmaku: Danmaku; - /** 播放器字幕控制 */ - $closedCaption = this.appendChild(new ClosedCaption()); + $subtitle: Subtitle; - /** 播放器洗脑循环控制 */ - $repeat = this.appendChild(new Repeat()); + #repeat: Repeat; - /** 播放器宽屏控制 */ - $wide = this.appendChild(new Wide()); + #screenWide: ScreenWide; - /** 播放器全屏控制 */ - $fullscreen = this.appendChild(new Fullscreen()); + #screenFull: ScreenFull; - /** 播放器网页全屏控制 */ - $fullscreenWeb = this.appendChild(new FullscreenWeb()); + #screenWeb: ScreenWeb; - /** 播放器画中画控制 */ - $pip = this.appendChild(new Pip()); + #screenPip: ScreenPip; - constructor() { + constructor(player: Player) { super(); + this.#player = player; this.classList.add('bofqi-area-control'); + this.#toggle = this.appendChild(new Toggle(player)); + this.#next = this.appendChild(new Next(player)); + this.$progress = this.appendChild(new Progress(player)); + this.#time = this.appendChild(new Time(player)); + this.#volume = this.appendChild(new Volume(player)); + this.$quality = this.appendChild(new Quality(player)); + this.#danmaku = this.appendChild(new Danmaku(player)); + this.$subtitle = this.appendChild(new Subtitle(player)); + this.#repeat = this.appendChild(new Repeat(player)); + this.#screenWide = this.appendChild(new ScreenWide(player)); + this.#screenFull = this.appendChild(new ScreenFull(player)); + this.#screenWeb = this.appendChild(new ScreenWeb(player)); + this.#screenPip = this.appendChild(new ScreenPip(player)); } } \ No newline at end of file diff --git a/src/player/area/control/next.ts b/src/player/area/control/next.ts index 7f20d6b..5946693 100644 --- a/src/player/area/control/next.ts +++ b/src/player/area/control/next.ts @@ -1,10 +1,11 @@ +import { Player } from "../.."; +import svg_xiayiji from "../../../assets/svg/xiayiji.svg"; import { customElement } from "../../../utils/Decorator/customElement"; -import svg_next from "../../assets/svg/next.svg"; -import { PLAYER_EVENT, ev } from "../../event-target"; +import { ev, PLAYER_EVENT } from "../../event"; -/** 播放器下一视频控制 */ -@customElement('button') -export class Next extends HTMLButtonElement { +/** 下一个 */ +@customElement('label') +export class Next extends HTMLLabelElement { /** * 需要监听变动的属性。 @@ -20,11 +21,8 @@ export class Next extends HTMLButtonElement { */ // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} - /** 初始化标记 */ - // #inited = false; - /** 每当元素添加到文档中时调用。 */ - // connectedCallback() {} + // connectedCallback() { } /** 每当元素从文档中移除时调用。 */ // disconnectedCallback() {} @@ -32,74 +30,34 @@ export class Next extends HTMLButtonElement { /** 每当元素被移动到新文档中时调用。 */ // adoptedCallback() {} - private ids: any[] = []; + #player: Player; - private currentIndex = 0; + private $callNext?: () => void; - constructor() { + constructor(player: Player) { super(); - this.classList.add('bofqi-control-button', 'bofqi-control-pause'); - this.insertAdjacentHTML('beforeend', svg_next); - - this.hide(); - - ev.bind(PLAYER_EVENT.IDENTIFY, this.identify); - - this.addEventListener('click', this.nextTrack); - - navigator.mediaSession.setActionHandler('nexttrack', this.nextTrack); - navigator.mediaSession.setActionHandler('previoustrack', this.previousTrack); - } - - /** - * 更新切P数据 - * - * @param ids 分P列表数据,当切P时间发生时对应数据将发送给您 - * @param currentId 当前P在`ids`中的索引 - */ - update(ids: any[], currentIndex: number) { - this.ids = ids; - this.currentIndex = currentIndex; - (ids.length > 1 && currentIndex + 1 < ids.length) ? this.show() : this.hide(); - } - - private show = () => { - this.classList.toggle('hidden', false); - } - - private hide = () => { - this.classList.toggle('hidden', true); - } - - private toggle = () => { - this.classList.toggle('hidden'); - } - - private previousTrack = () => { - const i = --this.currentIndex; - if (this.ids[i]) { - ev.trigger(PLAYER_EVENT.CALL_NEXT_PAGE, this.ids[i]); - } else { - ++this.currentIndex; - } - } - - private nextTrack = () => { - const i = ++this.currentIndex; - if (this.ids[i]) { - ev.trigger(PLAYER_EVENT.CALL_NEXT_PAGE, this.ids[i]); - if (i + 1 >= this.ids.length) { - this.hide() + this.#player = player; + this.classList.add('bofqi-area-control-btn', 'hidden'); + this.innerHTML = svg_xiayiji; + + ev.bind(PLAYER_EVENT.CALL_NEXT_REGISTER, ({ detail }) => { + if (detail) { + this.$callNext = detail; + this.classList.remove('hidden'); + navigator.mediaSession.setActionHandler('nexttrack', this.#callNext); + } else { + delete this.$callNext; + this.classList.add('hidden'); + navigator.mediaSession.setActionHandler('nexttrack', null); } - } else { - --this.currentIndex; - } + }) + this.addEventListener('click', this.#callNext); } - private identify = () => { - this.ids.length = 0; - this.currentIndex = 0; - this.hide(); + #callNext = () => { + this.$callNext?.(); + delete this.$callNext; + this.classList.add('hidden'); } } \ No newline at end of file diff --git a/src/player/area/control/progress.ts b/src/player/area/control/progress.ts deleted file mode 100644 index 087e622..0000000 --- a/src/player/area/control/progress.ts +++ /dev/null @@ -1,129 +0,0 @@ -import { customElement } from "../../../utils/Decorator/customElement"; -import { Element } from "../../../utils/element"; -import { Format } from "../../../utils/fomat"; -import { PLAYER_EVENT, ev } from "../../event-target"; -import { Slider } from "../../widget/slider"; -import { video } from "../wrap/video"; - -/** 进度条 */ -@customElement('input') -export class Progress extends Slider { - - /** - * 需要监听变动的属性。 - * 与实例方法`attributeChangedCallback`配合使用。 - * 此字符串序列定义了`attributeChangedCallback`回调时的第一个参数的可能值。 - */ - // static observedAttributes = []; - - /** - * 在属性更改、添加、移除或替换时调用。 - * 需要与静态属性`observedAttributes`配合使用。 - * 此回调的第一个参数在`observedAttributes`数组中定义。 - */ - // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} - - /** 初始化标记 */ - // #inited = false; - - /** 每当元素添加到文档中时调用。 */ - connectedCallback() { - this.insertAdjacentElement('afterend', this.$detail); - } - - /** 每当元素从文档中移除时调用。 */ - // disconnectedCallback() {} - - /** 每当元素被移动到新文档中时调用。 */ - // adoptedCallback() {} - - #currentTime = 0; - - constructor() { - super(); - - this.classList.add('bofqi-control-progress'); - this.disabled = true; - - video.addEventListener('durationchange', () => { - this.max = Math.floor(video.duration); - this.disabled = video.duration > 0 ? false : true; - }); - video.addEventListener('timeupdate', () => { - if (this.#currentTime !== Math.ceil(video.currentTime)) { - // 按秒更新即可,无需频繁修改 - this.$value = video.currentTime; - } - this.#currentTime = Math.ceil(video.currentTime); - }); - video.addEventListener('progress', () => { - this.buffer(video); - }); - - ev.bind(PLAYER_EVENT.IDENTIFY, this.identity); - - this.addEventListener('click', (e: MouseEvent) => { - const detail = this.getPersent(e); - const { duration } = video; - const time = Format.fmRange(detail * duration, 0, duration); - video.seek(time); - }); - this.addEventListener('pointerenter', this.mouseEnter); - this.addEventListener('pointerleave', this.mouseLeave); - } - - /** 浮动提示容器 */ - protected $detail = Element.add('div', { - class: 'bofqi-control-progress-detail' - }, undefined, `
    `); - - /** 时间 */ - protected $time = Element.add('div', { class: 'bofqi-control-progress-detail-time' }, this.$detail, undefined, true); - - /** 预览图 */ - protected $img = Element.add('div', { class: 'bofqi-control-progress-detail-img' }, this.$detail, undefined, true); - - /** 更新缓冲条 */ - protected buffer(video: HTMLVideoElement) { - const { buffered, duration } = video; - if (duration) { - const arr: [number, number][] = []; - for (let i = 0, len = buffered.length; i < len; i++) { - arr.push([buffered.start(i) / duration, buffered.end(i) / duration]); - } - this.$buffer = arr; - } - } - - private mouseEnter = (e: MouseEvent) => { - self.addEventListener('pointermove', this.mouseHoverLinster); - } - - private mouseLeave = (e: MouseEvent) => { - self.removeEventListener('pointermove', this.mouseHoverLinster); - } - - /** 根据鼠标事件获取百分比 */ - private getPersent(e: MouseEvent) { - const { offsetX } = e; - const $all = this.clientWidth; - return Format.fmRange(offsetX, 0, $all) / $all - } - - private mouseHoverLinster = (e: MouseEvent) => { - const { offsetX } = e; - const detail = this.getPersent(e); - const { duration } = video; - this.$detail.style.setProperty('--inline-start', `${offsetX}px`); - const time = Format.fmRange(detail * duration, 0, duration); - this.$time.textContent = Format.fmSeconds(time); - ev.trigger(PLAYER_EVENT.PROGRESS_VIDEOSHOT, [this.$img, time]); - } - - protected identity = () => { - this.$value = 0; - this.$buffer = []; - this.$img.removeAttribute('style'); - this.$img.replaceChildren(); - } -} \ No newline at end of file diff --git a/src/player/area/control/progress/index.css b/src/player/area/control/progress/index.css new file mode 100644 index 0000000..9099a00 --- /dev/null +++ b/src/player/area/control/progress/index.css @@ -0,0 +1,74 @@ +@scope { + :scope { + &:hover>.bofqi-area-progress-detail { + display: flex; + flex-direction: column-reverse; + align-items: center; + cursor: pointer; + } + + &.disabled { + pointer-events: none; + } + } + + progress.buffer { + --linear-gradient: linear-gradient(to right, transparent 0% 100%); + + &::-webkit-progress-bar { + background-image: var(--linear-gradient); + } + } + + .bofqi-area-progress-detail { + --inline-start: 0px; + position: absolute; + pointer-events: none; + display: none; + inset-inline-start: var(--inline-start); + inset-block-end: 50%; + inline-size: 160px; + block-size: 90px; + translate: -50% 0; + + >.progress-detail-sign { + block-size: 16px; + inset-block-end: -8px; + position: relative; + display: flex; + flex-direction: column; + align-items: center; + + &:before, + &::after { + content: ""; + position: absolute; + inline-size: 0; + block-size: 0; + border-block-color: var(--00a1d6); + border-inline: 4px transparent; + border-style: solid; + } + + &::before { + inset-block-start: 0; + border-block-start-width: 4px; + border-block-end-width: 0; + } + + &::after { + inset-block-end: 0; + border-block-start: 0px; + border-block-end-width: 4px; + } + } + + >time { + background-color: var(--e5e9ef); + color: var(--6b6b6b); + padding-block: 3px; + padding-inline: 5px; + border-radius: 4px; + } + } +} \ No newline at end of file diff --git a/src/player/area/control/progress/index.d.css.ts b/src/player/area/control/progress/index.d.css.ts new file mode 100644 index 0000000..eb9bc4f --- /dev/null +++ b/src/player/area/control/progress/index.d.css.ts @@ -0,0 +1,2 @@ +declare const stylesheet: CSSStyleSheet; +export default stylesheet; \ No newline at end of file diff --git a/src/player/area/control/progress/index.ts b/src/player/area/control/progress/index.ts new file mode 100644 index 0000000..50ea172 --- /dev/null +++ b/src/player/area/control/progress/index.ts @@ -0,0 +1,178 @@ +import { Player } from "../../.."; +import { CSSStyleSheet2HTMLStyleElement } from "../../../../utils/CSSStyleSheet2HTMLStyleElement"; +import { customElement } from "../../../../utils/Decorator/customElement"; +import { Format } from "../../../../utils/fomat"; +import { ev, PLAYER_EVENT } from "../../../event"; +import { Slider } from "../../../widget/slider"; +import stylesheet from "./index.css" with {type: 'css'}; + +/** 播放器进度条 */ +@customElement('form') +export class Progress extends Slider { + + /** + * 需要监听变动的属性。 + * 与实例方法`attributeChangedCallback`配合使用。 + * 此字符串序列定义了`attributeChangedCallback`回调时的第一个参数的可能值。 + */ + // static observedAttributes = []; + + /** + * 在属性更改、添加、移除或替换时调用。 + * 需要与静态属性`observedAttributes`配合使用。 + * 此回调的第一个参数在`observedAttributes`数组中定义。 + */ + // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} + + /** 每当元素添加到文档中时调用。 */ + connectedCallback() { + self.addEventListener('keydown', this.#onKeyDown); + } + + /** 每当元素从文档中移除时调用。 */ + disconnectedCallback() { + self.removeEventListener('keydown', this.#onKeyDown); + } + + /** 每当元素被移动到新文档中时调用。 */ + // adoptedCallback() {} + + #player: Player; + + /** 缓冲 */ + #buffer = document.createElement('progress'); + + /** 时间戳 */ + $hover = this.appendChild(document.createElement('div')); + + #time = document.createElement('time'); + + #preTime = 0; + + constructor(player: Player) { + super(); + + // this.#host.adoptedStyleSheets = [stylesheet]; // 文档画中画模式会导致构造的样式表丢失,暂时取道 style 元素代替 + this.appendChild(CSSStyleSheet2HTMLStyleElement(stylesheet)); + this.#player = player; + this.classList.add('bofqi-area-progress'); + this.$bar.insertAdjacentElement('afterend', this.#buffer); + + this.$defaultValue = 0; + this.$step = 'any'; + this.#buffer.classList.add('buffer'); + this.$hover.classList.add('bofqi-area-progress-detail'); + this.$hover.innerHTML = `
    `; + this.$hover.appendChild(this.#time); + + player.$video.addEventListener('durationchange', () => { + this.$max = player.$video.duration; + }); + player.$video.addEventListener('timeupdate', () => { + const time = Math.floor(player.$video.currentTime); + if (this.#preTime !== time) { + // 本事件一秒可能触发几十次,精确到整数以避免频繁操作带来性能问题 + this.$value = player.$video.currentTime; + this.#preTime = time; + } + }); + player.$video.addEventListener('canplay', () => { + this.classList.remove('disabled'); + }); + player.$video.addEventListener('progress', () => { + const { buffered, duration } = player.$video; + if (duration) { + const arr: [number, number][] = []; + for (let i = 0, len = buffered.length; i < len; i++) { + arr.push([buffered.start(i) / duration, buffered.end(i) / duration]); + } + arr.sort((a, b) => a[0] - b[0] > 0 ? 1 : -1); + const linear: [string, string, string][] = []; + /** 当前最大值,用于还原背景色 */ + let max = 0; + for (const buffer of arr) { + linear.push( + ['transparent', Format.toFixed(max * 100) + '%', Format.toFixed(buffer[0] * 100) + '%'], + ['var(--8adced)', Format.toFixed(buffer[0] * 100) + '%', Format.toFixed(buffer[1] * 100) + '%'] + ); + max = buffer[1]; + } + linear.push(['transparent', Format.toFixed(max * 100) + '%', '100%']); + this.#buffer.style.setProperty('--linear-gradient', `linear-gradient(to right,${linear.map(d => d.join(' ')).join(',')})`); + } + }); + // this.addEventListener('change', () => { + // const detail = this.$value; + // const { duration } = player.$video; + // const time = Format.fmRange((detail || 0), 0, duration); + // player.$video.$seek(time); + // }); + this.addEventListener('click', e => { + const detail = this.#getPersent(e); + const { duration } = this.#player.$video; + const time = Format.fmRange(detail * duration, 0, duration); + player.$video.$seek(time); + }); + this.addEventListener('pointerenter', this.#mouseEnter); + this.addEventListener('pointerleave', this.#mouseLeave); + ev.bind(PLAYER_EVENT.VIDEO_DESTORY, this.#indetify); + + this.#indetify(); + } + + /** 键盘事件回调 */ + #onKeyDown = ({ key, shiftKey, ctrlKey, altKey, metaKey }: KeyboardEvent) => { + try { + const { activeElement } = document; + if (activeElement === document.body || activeElement === this.#player || this.#player.contains(activeElement) && !(activeElement?.hasAttribute('contenteditable') || activeElement instanceof HTMLInputElement || activeElement instanceof HTMLTextAreaElement)) { + switch (key) { + // 不能区分小键盘,但能识别 Shift 后的值 + case 'ArrowRight': { + // 快进 5 秒 + shiftKey || ctrlKey || altKey || metaKey || (this.#player.$video.currentTime += 5); + break; + } + case 'ArrowLeft': { + // 快退 5 秒 + shiftKey || ctrlKey || altKey || metaKey || (this.#player.$video.currentTime -= 5); + break; + } + } + // switch (code) { + // // 能区分小键盘,但不识别 Shift 后的值 + // } + } + } catch { } + } + + #mouseEnter = () => { + this.addEventListener('pointermove', this.#mouseHoverLinster); + } + + #mouseLeave = () => { + this.removeEventListener('pointermove', this.#mouseHoverLinster); + } + + /** 根据鼠标事件获取百分比 */ + #getPersent(e: MouseEvent) { + const { offsetX } = e; + const $all = this.clientWidth; + return Format.fmRange(offsetX, 0, $all) / $all + } + + #mouseHoverLinster = (e: MouseEvent) => { + const { offsetX } = e; + const detail = this.#getPersent(e); + const { duration } = this.#player.$video; + this.$hover.style.setProperty('--inline-start', `${offsetX}px`); + const time = Format.fmRange(detail * duration, 0, duration); + this.#time.textContent = this.#time.dateTime = Format.fmSeconds(time); + ev.trigger(PLAYER_EVENT.PROGRESS_HOVER, time); + } + + #indetify = () => { + this.#preTime = -1; + this.classList.add('disabled'); + this.$value = 0; + } +} \ No newline at end of file diff --git a/src/player/area/control/quality.ts b/src/player/area/control/quality.ts index 4999097..7b83808 100644 --- a/src/player/area/control/quality.ts +++ b/src/player/area/control/quality.ts @@ -1,10 +1,11 @@ +import { Player } from "../.."; import { customElement } from "../../../utils/Decorator/customElement"; import { Element } from "../../../utils/element"; -import { PLAYER_EVENT, ev } from "../../event-target"; +import { ev, PLAYER_EVENT } from "../../event"; -/** 播放器画质控制 */ -@customElement('button') -export class Quality extends HTMLButtonElement { +/** 洗脑循环 */ +@customElement('label') +export class Quality extends HTMLLabelElement { /** * 需要监听变动的属性。 @@ -20,21 +21,24 @@ export class Quality extends HTMLButtonElement { */ // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} - /** 初始化标记 */ - // #inited = false; - /** 每当元素添加到文档中时调用。 */ - // connectedCallback() { } + connectedCallback() { + this.insertAdjacentElement('afterend', this.#wrap); + } /** 每当元素从文档中移除时调用。 */ - // disconnectedCallback() {} + disconnectedCallback() { + this.#wrap.remove(); + } /** 每当元素被移动到新文档中时调用。 */ // adoptedCallback() {} - private $wrap = Element.add('ul', { class: 'bofqi-quality-wrap' }, this); + #player: Player; - private $auto = Element.add('li', { class: 'selected', 'data-value': '0' }, this.$wrap, '自动'); + #wrap = Element.add('ul', { class: 'bofqi-quality-wrap' }); + + #auto = Element.add('li', { class: 'selected', data: { value: '0' }, appendTo: this.#wrap, innerText: '自动' }); #value = 0; @@ -43,24 +47,25 @@ export class Quality extends HTMLButtonElement { } set $value(v) { - this.$wrap.querySelector('.selected')?.classList.remove('selected'); - const li = this.$wrap.querySelector(`[data-value="${v}"]`); + this.#wrap.querySelector('.selected')?.classList.remove('selected'); + const li = this.#wrap.querySelector(`[data-value="${v}"]`); if (li) { li.classList.add('selected'); this.dataset.label = li.textContent!.split(' ').at(-1); } } - constructor() { + constructor(player: Player) { super(); - this.classList.add('bofqi-control-button', 'bofqi-control-quality'); + this.#player = player; + this.classList.add('bofqi-area-control-btn', 'bofqi-control-quality'); this.dataset.label = '自动'; - ev.bind(PLAYER_EVENT.IDENTIFY, this.identify); - ev.bind(PLAYER_EVENT.LOCAL_MEDIA_LOAD, this.identify); - this.$wrap.addEventListener('click', e => { + ev.bind(PLAYER_EVENT.VIDEO_DESTORY, this.#identify); + ev.bind(PLAYER_EVENT.LOAD_VIDEO_FILE, this.#identify); + this.#wrap.addEventListener('click', e => { const li = e.target; if (li instanceof HTMLLIElement) { const qn = Number(li.dataset.value) || 0; @@ -76,19 +81,22 @@ export class Quality extends HTMLButtonElement { } } }); + ev.bind(PLAYER_EVENT.QUALITY_SET_FOR, ({ detail }) => { + this.$value = detail; + }); } update(qns: [number, string][]) { - this.$wrap.replaceChildren(this.$auto); + this.#wrap.replaceChildren(this.#auto); qns.forEach(d => { - Element.add('li', { 'data-value': d[0] }, this.$wrap, d[1]); + Element.add('li', { data: { value: d[0] }, appendTo: this.#wrap, innerText: d[1] }); }); - this.classList.toggle('hidden', !Boolean(qns.length)); + this.classList.toggle('disabled', !Boolean(qns.length)); } - identify = () => { + #identify = () => { this.dataset.label = '自动'; - this.$wrap.replaceChildren(this.$auto); - this.classList.add('hidden'); + this.#wrap.replaceChildren(this.#auto); + this.classList.add('disabled'); } } \ No newline at end of file diff --git a/src/player/area/control/repeat.ts b/src/player/area/control/repeat.ts index 13b9070..9455006 100644 --- a/src/player/area/control/repeat.ts +++ b/src/player/area/control/repeat.ts @@ -1,11 +1,11 @@ +import { Player } from "../.."; +import svg_24repeatoff from "../../../assets/svg/24repeatoff.svg"; +import svg_24repeaton from "../../../assets/svg/24repeaton.svg"; import { customElement } from "../../../utils/Decorator/customElement"; -import svg_repeat_on from "../../assets/svg/repeat-on.svg"; -import svg_repeat from "../../assets/svg/repeat.svg"; -import { video } from "../wrap/video"; -/** 播放器洗脑循环控制 */ -@customElement('button') -export class Repeat extends HTMLButtonElement { +/** 洗脑循环 */ +@customElement('label') +export class Repeat extends HTMLLabelElement { /** * 需要监听变动的属性。 @@ -21,11 +21,8 @@ export class Repeat extends HTMLButtonElement { */ // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} - /** 初始化标记 */ - // #inited = false; - /** 每当元素添加到文档中时调用。 */ - // connectedCallback() {} + // connectedCallback() { } /** 每当元素从文档中移除时调用。 */ // disconnectedCallback() {} @@ -33,26 +30,31 @@ export class Repeat extends HTMLButtonElement { /** 每当元素被移动到新文档中时调用。 */ // adoptedCallback() {} - constructor() { - super(); + #player: Player; - this.classList.add('bofqi-control-button'); - this.insertAdjacentHTML('beforeend', video.loop ? svg_repeat_on : svg_repeat); + constructor(player: Player) { + super(); - this.disabled = true; + this.#player = player; + this.classList.add('bofqi-area-control-btn', 'bofqi-area-repeat'); + this.innerHTML = svg_24repeatoff + svg_24repeaton; - video.addEventListener('loadstart', () => { - this.disabled = true; - }); - video.addEventListener('loadeddata', () => { - this.disabled = false; + player.$video.addEventListener('loadstart', this.#indetify); + player.$video.addEventListener('loadeddata', () => { + this.classList.remove('disabled'); }); - video.addEventListener('loopchange', () => { - this.innerHTML = video.loop ? svg_repeat_on : svg_repeat; + player.$video.addEventListener('loopchange', () => { + this.classList.toggle('on'); }); this.addEventListener('click', () => { - video.loop = !video.loop; + player.$video.loop = !player.$video.loop; }); + + this.#indetify(); + } + + #indetify = () => { + this.classList.add('disabled'); } } \ No newline at end of file diff --git a/src/player/area/control/screen-full.ts b/src/player/area/control/screen-full.ts new file mode 100644 index 0000000..afaf344 --- /dev/null +++ b/src/player/area/control/screen-full.ts @@ -0,0 +1,73 @@ +import { Player } from "../.."; +import svg_24exitfullscreen from "../../../assets/svg/24exitfullscreen.svg"; +import svg_24fullscreen from "../../../assets/svg/24fullscreen.svg"; +import { customElement } from "../../../utils/Decorator/customElement"; + +/** 全屏 */ +@customElement('label') +export class ScreenFull extends HTMLLabelElement { + + /** + * 需要监听变动的属性。 + * 与实例方法`attributeChangedCallback`配合使用。 + * 此字符串序列定义了`attributeChangedCallback`回调时的第一个参数的可能值。 + */ + // static observedAttributes = []; + + /** + * 在属性更改、添加、移除或替换时调用。 + * 需要与静态属性`observedAttributes`配合使用。 + * 此回调的第一个参数在`observedAttributes`数组中定义。 + */ + // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} + + /** 每当元素添加到文档中时调用。 */ + connectedCallback() { + self.addEventListener('keydown', this.#onKeyDown); + } + + /** 每当元素从文档中移除时调用。 */ + disconnectedCallback() { + self.removeEventListener('keydown', this.#onKeyDown); + } + + /** 每当元素被移动到新文档中时调用。 */ + // adoptedCallback() {} + + #player: Player; + + constructor(player: Player) { + super(); + + this.#player = player; + this.classList.add('bofqi-area-control-btn', 'bofqi-area-screen-full'); + this.innerHTML = svg_24fullscreen + svg_24exitfullscreen; + + this.addEventListener('click', this.#toggle); + } + + /** 键盘事件回调 */ + #onKeyDown = ({ key, shiftKey, ctrlKey, altKey, metaKey }: KeyboardEvent) => { + try { + const { activeElement } = document; + if (activeElement === document.body || activeElement === this.#player || this.#player.contains(activeElement) && !(activeElement?.hasAttribute('contenteditable') || activeElement instanceof HTMLInputElement || activeElement instanceof HTMLTextAreaElement)) { + switch (key) { + // 不能区分小键盘,但能识别 Shift 后的值 + case 'F': case 'f': { + // 全屏 + shiftKey || ctrlKey || altKey || metaKey || this.#toggle(); + break; + } + } + // switch (code) { + // // 能区分小键盘,但不识别 Shift 后的值 + // } + } + } catch { } + } + + /** 全屏切换 */ + #toggle = () => { + this.#player.$isFullScreen ? document.exitFullscreen() : this.#player.requestFullscreen(); + } +} \ No newline at end of file diff --git a/src/player/area/control/screen-pip.ts b/src/player/area/control/screen-pip.ts new file mode 100644 index 0000000..9403654 --- /dev/null +++ b/src/player/area/control/screen-pip.ts @@ -0,0 +1,65 @@ +import { Player } from "../.."; +import svg_24exitpip from "../../../assets/svg/24exitpip.svg"; +import svg_24pip from "../../../assets/svg/24pip.svg"; +import { toastr } from "../../../toastr"; +import { customElement } from "../../../utils/Decorator/customElement"; + +/** 画中画模式 */ +@customElement('label') +export class ScreenPip extends HTMLLabelElement { + + /** + * 需要监听变动的属性。 + * 与实例方法`attributeChangedCallback`配合使用。 + * 此字符串序列定义了`attributeChangedCallback`回调时的第一个参数的可能值。 + */ + // static observedAttributes = []; + + /** + * 在属性更改、添加、移除或替换时调用。 + * 需要与静态属性`observedAttributes`配合使用。 + * 此回调的第一个参数在`observedAttributes`数组中定义。 + */ + // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} + + /** 每当元素添加到文档中时调用。 */ + // connectedCallback() { } + + /** 每当元素从文档中移除时调用。 */ + // disconnectedCallback() {} + + /** 每当元素被移动到新文档中时调用。 */ + // adoptedCallback() {} + + #player: Player; + + constructor(player: Player) { + super(); + + this.#player = player; + this.classList.add('bofqi-area-control-btn', 'bofqi-area-screen-pip'); + this.innerHTML = svg_24pip + svg_24exitpip; + + this.addEventListener('click', () => { + if (documentPictureInPicture.window) { } else { + const parrent = this.#player.parentElement!; + const prev = this.#player.nextElementSibling; + documentPictureInPicture.requestWindow({ + width: this.#player.clientWidth, + height: this.#player.clientHeight + }).then(d => { + d.window.document.documentElement.style.colorScheme = 'light dark'; // 适配颜色模式 + d.window.document.body.style.margin = '0'; // 取消外边距 + d.window.document.body.append(this.#player); + d.addEventListener('pagehide', () => { + parrent.insertBefore(this.#player, prev); + documentPictureInPicture.window?.close(); + }, { once: true }); + }).catch(e => { + toastr.error(e); + console.log(e); + }); + } + }); + } +} \ No newline at end of file diff --git a/src/player/area/wrap/record.ts b/src/player/area/control/screen-web.ts similarity index 54% rename from src/player/area/wrap/record.ts rename to src/player/area/control/screen-web.ts index ecd8d3b..8eb8e2c 100644 --- a/src/player/area/wrap/record.ts +++ b/src/player/area/control/screen-web.ts @@ -1,11 +1,11 @@ +import { Player } from "../.."; +import svg_24exitwebfull from "../../../assets/svg/24exitwebfull.svg"; +import svg_24webfull from "../../../assets/svg/24webfull.svg"; import { customElement } from "../../../utils/Decorator/customElement"; -import { Element } from "../../../utils/element"; -import { PLAYER_EVENT, ev } from "../../event-target"; -import { video } from "./video"; -/** 播放登记信息 */ -@customElement('div') -export class Record extends HTMLDivElement { +/** 网页全屏模式 */ +@customElement('label') +export class ScreenWeb extends HTMLLabelElement { /** * 需要监听变动的属性。 @@ -21,9 +21,6 @@ export class Record extends HTMLDivElement { */ // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} - /** 初始化标记 */ - // #inited = false; - /** 每当元素添加到文档中时调用。 */ // connectedCallback() { } @@ -33,25 +30,17 @@ export class Record extends HTMLDivElement { /** 每当元素被移动到新文档中时调用。 */ // adoptedCallback() {} - /** 登记信息内容 */ - protected $item = Element.add('div', { class: 'bofqi-record-item' }, this); + #player: Player; - constructor() { + constructor(player: Player) { super(); - this.classList.add('bofqi-record'); - } + this.#player = player; + this.classList.add('bofqi-area-control-btn', 'bofqi-area-screen-web'); + this.innerHTML = svg_24webfull + svg_24exitwebfull; - /** 添加登记信息 */ - addRecord(str: string) { - this.$item.textContent = str; - video.addEventListener('play', () => { - setTimeout(this.identify, 3e3); + this.addEventListener('click', () => { + player.classList.toggle('screen-web'); }); - ev.bind(PLAYER_EVENT.IDENTIFY, this.identify); - } - - protected identify = () => { - this.$item.replaceChildren(); } } \ No newline at end of file diff --git a/src/player/area/control/pip.ts b/src/player/area/control/screen-wide.ts similarity index 63% rename from src/player/area/control/pip.ts rename to src/player/area/control/screen-wide.ts index 4caf71c..91a6cfb 100644 --- a/src/player/area/control/pip.ts +++ b/src/player/area/control/screen-wide.ts @@ -1,10 +1,11 @@ +import { Player } from "../.."; +import svg_24wideoff from "../../../assets/svg/24wideoff.svg"; +import svg_24wideon from "../../../assets/svg/24wideon.svg"; import { customElement } from "../../../utils/Decorator/customElement"; -import svg_screen_pip from "../../assets/svg/screen-pip.svg"; -import { PLAYER_EVENT, ev } from "../../event-target"; -/** 播放器画中画控制 */ -@customElement('button') -export class Pip extends HTMLButtonElement { +/** 宽屏模式 */ +@customElement('label') +export class ScreenWide extends HTMLLabelElement { /** * 需要监听变动的属性。 @@ -20,11 +21,8 @@ export class Pip extends HTMLButtonElement { */ // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} - /** 初始化标记 */ - // #inited = false; - /** 每当元素添加到文档中时调用。 */ - // connectedCallback() {} + // connectedCallback() { } /** 每当元素从文档中移除时调用。 */ // disconnectedCallback() {} @@ -32,15 +30,17 @@ export class Pip extends HTMLButtonElement { /** 每当元素被移动到新文档中时调用。 */ // adoptedCallback() {} - constructor() { - super(); + #player: Player; - this.classList.add('bofqi-control-button', 'bofqi-control-fullscreen-pip'); + constructor(player: Player) { + super(); - this.insertAdjacentHTML('afterbegin', svg_screen_pip); + this.#player = player; + this.classList.add('bofqi-area-control-btn', 'bofqi-area-screen-wide'); + this.innerHTML = svg_24wideoff + svg_24wideon; this.addEventListener('click', () => { - ev.trigger(PLAYER_EVENT.PICTURE_IN_PICTURE, void 0); + player.classList.toggle('screen-wide'); }); } } \ No newline at end of file diff --git a/src/player/area/control/closed-caption.ts b/src/player/area/control/subtitle.ts similarity index 59% rename from src/player/area/control/closed-caption.ts rename to src/player/area/control/subtitle.ts index a5d1742..656b9ed 100644 --- a/src/player/area/control/closed-caption.ts +++ b/src/player/area/control/subtitle.ts @@ -1,17 +1,18 @@ +import { Player } from "../.."; +import svg_24subtitle from "../../../assets/svg/24subtitle.svg"; +import svg_24subtitleon from "../../../assets/svg/24subtitleon.svg"; +import { toastr } from "../../../toastr"; import { customElement } from "../../../utils/Decorator/customElement"; import { Element } from "../../../utils/element"; -import svg_closed_caption_on from "../../assets/svg/closed-caption-on.svg"; -import svg_closed_caption from "../../assets/svg/closed-caption.svg"; -import { PLAYER_EVENT, ev } from "../../event-target"; +import { ev, PLAYER_EVENT } from "../../event"; import { options } from "../../option"; import { Checkbox } from "../../widget/checkbox"; import { Color } from "../../widget/color"; import { Slider } from "../../widget/slider"; -import { track } from "../wrap/video/track"; -/** 播放器字幕控制 */ -@customElement('button') -export class ClosedCaption extends HTMLButtonElement { +/** 字幕控制 */ +@customElement('label') +export class Subtitle extends HTMLLabelElement { /** * 需要监听变动的属性。 @@ -27,64 +28,69 @@ export class ClosedCaption extends HTMLButtonElement { */ // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} - /** 初始化标记 */ - // #inited = false; - /** 每当元素添加到文档中时调用。 */ - // connectedCallback() {} + connectedCallback() { + this.insertAdjacentElement('afterend', this.#wrap); + } /** 每当元素从文档中移除时调用。 */ - // disconnectedCallback() {} + disconnectedCallback() { + this.#wrap.remove(); + } /** 每当元素被移动到新文档中时调用。 */ // adoptedCallback() {} + #player: Player; + /** 浮动面板 */ - private $wrap = Element.add('div', { class: 'bofqi-control-closed-caption-wrap' }, this); + #wrap = Element.add('div', { class: 'bofqi-subtitle-wrap' }); /** 字幕选择 */ - private $select = Element.add('select', { class: 'bofqi-control-closed-caption-select bpui-select' }, this.$wrap); + private $select = Element.add('select', { class: ['bofqi-subtitle-select', 'bpui-select'], appendTo: this.#wrap }); /** 下载按钮 */ - private $download = Element.add('button', { class: 'bpui-button' }, this.$wrap, '下载'); + private $download = Element.add('button', { class: 'bpui-button', appendTo: this.#wrap, innerText: '下载' }); /** 字体大小 */ - private $fontSize = this.$wrap.appendChild(new Slider()); + private $fontSize = this.#wrap.appendChild(new Slider()); /** 等比缩放 */ - private $scale = this.$wrap.appendChild(new Checkbox()); + private $scale = this.#wrap.appendChild(new Checkbox()); - private $colorButton = Element.add('button', { class: 'closed-caption-color' }, this.$wrap); + private $colorButton = Element.add('button', { class: 'closed-caption-color', appendTo: this.#wrap }); /** 字幕颜色 */ private $color = new Color(this.$colorButton, '--closed-caption-color'); /** 字幕描边 */ - private $shadow = Element.add('select', { class: 'bpui-select' }, this.$wrap); + private $shadow = Element.add('select', { class: 'bpui-select', appendTo: this.#wrap }); /** 字体 */ - private $font = Element.add('select', { class: 'bpui-select' }, this.$wrap); + private $font = Element.add('select', { class: 'bpui-select', appendTo: this.#wrap }); /** 背景不透明度 */ - private $opacity = this.$wrap.appendChild(new Slider()); + private $opacity = this.#wrap.appendChild(new Slider()); /** 已有字幕文件 */ private $files: Record = {}; - constructor() { + constructor(player: Player) { super(); - this.classList.add('bofqi-control-button', 'bofqi-control-closed-caption'); - this.insertAdjacentHTML('afterbegin', svg_closed_caption + svg_closed_caption_on); - this.$select.insertAdjacentHTML('beforebegin', `
    字幕
    `); - this.$fontSize.insertAdjacentHTML('beforebegin', `
    字体大小
    `); - this.$opacity.insertAdjacentHTML('beforebegin', `
    背景不透明度
    `); - this.$colorButton.insertAdjacentHTML('beforebegin', `字幕颜色`); + this.#player = player; + this.classList.add('bofqi-area-control-btn', 'bofqi-area-subtitle'); + this.innerHTML = svg_24subtitle + svg_24subtitleon; + + this.$select.insertAdjacentHTML('beforebegin', `
    字幕
    `); + this.$fontSize.insertAdjacentHTML('beforebegin', `
    字体大小
    `); + this.$opacity.insertAdjacentHTML('beforebegin', `
    背景不透明度
    `); + this.$colorButton.insertAdjacentHTML('beforebegin', `
    字幕颜色`); this.$shadow.insertAdjacentHTML('beforebegin', `
    字幕描边`); this.$font.insertAdjacentHTML('beforebegin', `
    字幕字体`); - this.disabled = true; - this.$fontSize.max = '4'; + this.classList.add('disabled'); + this.$fontSize.$max = 4; this.$fontSize.$hint = true; this.$scale.$text = '等比缩放'; this.$shadow.dataset.label = '字幕描边'; @@ -99,14 +105,14 @@ export class ClosedCaption extends HTMLButtonElement { `; this.$opacity.$hint = true; - this.$opacity.step = '5'; + this.$opacity.$step = 5; - ev.bind(PLAYER_EVENT.IDENTIFY, this.identify); + ev.bind(PLAYER_EVENT.VIDEO_DESTORY, this.identify); ev.one(PLAYER_EVENT.OPTINOS_CHANGE, ({ detail }) => { const { fontFamily, fontFamilyCustom } = detail.subtile; if (fontFamilyCustom) { const [fullName, family] = fontFamilyCustom; - Element.add('option', { style: `font-family: ${family}`, value: family }, this.$font, fullName); + Element.add('option', { style: { fontFamily: family }, attribute: { value: family }, appendTo: this.$font, innerText: fullName }); this.$font.value = family; } this.$font.value = fontFamily || '默认'; @@ -115,66 +121,67 @@ export class ClosedCaption extends HTMLButtonElement { const { fontSize, scale, color, shadow, opacity, fontFamily } = detail.subtile; switch (fontSize) { case 0.6: { - this.$fontSize.$value = '0'; - this.$fontSize.dataset.value = '极小'; + this.$fontSize.$value = 0; + this.$fontSize.$hintValue = '极小'; break } case 0.8: { - this.$fontSize.$value = '1'; - this.$fontSize.dataset.value = '0.5'; + this.$fontSize.$value = 1; + this.$fontSize.$hintValue = '0.5'; break } case 1.3: { - this.$fontSize.$value = '3'; - this.$fontSize.dataset.value = '1.5'; + this.$fontSize.$value = 3; + this.$fontSize.$hintValue = '1.5'; break } case 1.6: { - this.$fontSize.$value = '4'; - this.$fontSize.dataset.value = '极大'; + this.$fontSize.$value = 4; + this.$fontSize.$hintValue = '极大'; break } default: { - this.$fontSize.$value = '2'; - this.$fontSize.dataset.value = '适中'; + this.$fontSize.$value = 2; + this.$fontSize.$hintValue = '适中'; break } } this.$scale.$value = scale; - this.style.setProperty('--caption-color', this.$color.$value = color || '#ffffff'); + this.#wrap.style.setProperty('--caption-color', this.$color.$value = color || '#ffffff'); this.$shadow.value = shadow || '无描边'; this.$opacity.$value = opacity ?? '100'; this.$font.value = fontFamily || '默认'; }); + ev.bind(PLAYER_EVENT.VTT_FILE_LOAD, ({ detail }) => { this.load(detail) }); this.addEventListener('click', () => { - if (!track.$hasTrack && this.$files[this.$select.value]) { + if (!player.$video.$track.$hasTrack && this.$files[this.$select.value]) { const file = this.$files[this.$select.value]; - track.attach( + player.$video.$track.attach( file.src, file.srclang, file.label ); - track.show(); + player.$video.$track.show(); } else { - track.toggle(); + player.$video.$track.toggle(); } - this.classList.toggle('on', track.track.mode === 'showing'); + this.classList.toggle('on', player.$video.$track.track.mode === 'showing'); }); - this.$wrap.addEventListener('click', e => { + this.#wrap.addEventListener('click', e => { e.stopPropagation(); }); this.$select.addEventListener('change', () => { if (this.$files[this.$select.value]) { const file = this.$files[this.$select.value]; - track.attach( + player.$video.$track.attach( file.src, file.srclang, file.label ); - track.show(); + player.$video.$track.show(); } - this.classList.toggle('on', track.track.mode === 'showing'); + this.classList.toggle('on', player.$video.$track.track.mode === 'showing'); }); this.$download.addEventListener('click', () => { const file = this.$files[this.$select.value]; @@ -189,27 +196,31 @@ export class ClosedCaption extends HTMLButtonElement { } } ], - }).then(d => d.createWritable()) + }) + .then(d => d.createWritable()) .then(d => { fetch(file.src).then(d => d.blob()).then(e => d.write(e)).finally(() => { d.close() }) + }).catch(e => { + toastr.error(e); + console.error(e); }); } }); this.$fontSize.addEventListener('change', () => { - switch (this.$fontSize.value) { - case '0': { + switch (this.$fontSize.$value) { + case 0: { options.subtile.fontSize = 0.6; break; } - case '1': { + case 1: { options.subtile.fontSize = 0.8; break; } - case '3': { + case 3: { options.subtile.fontSize = 1.3; break; } - case '4': { + case 4: { options.subtile.fontSize = 1.6; break; } @@ -235,32 +246,37 @@ export class ClosedCaption extends HTMLButtonElement { // 更改字体 if (this.$font.value === '...') { this.$font.value = options.subtile.fontFamily; - queryLocalFonts().then(d => { - const popover = document.createElement('div'); - popover.popover = 'auto'; - popover.classList.add('popover-local-font'); - this.append(popover); - d.forEach(d => { - if (/[\p{L}&&\p{Script=Han}]/v.test(d.fullName)) { - const button = document.createElement('button'); - button.textContent = d.fullName; - button.style.fontFamily = `"${d.family}"`; - button.addEventListener('click', e => { - e.stopPropagation(); - options.subtile.fontFamilyCustom = [d.fullName, d.family]; - options.subtile.fontFamily = d.family; - Element.add('option', { style: `font-family: ${d.family}`, value: d.family }, this.$font, d.fullName); - this.$font.value = d.family; - popover.hidePopover(); - }) - popover.appendChild(button); - } - }); - popover.addEventListener('toggle', () => { - popover.matches(":popover-open") || popover.remove(); + queryLocalFonts() + .then(d => { + const popover = document.createElement('div'); + popover.popover = 'auto'; + popover.classList.add('popover-local-font'); + this.append(popover); + d.forEach(({ family, fullName }) => { + if (/[\p{L}&&\p{Script=Han}]/v.test(fullName)) { + const button = document.createElement('button'); + button.textContent = fullName; + button.style.fontFamily = family; + button.addEventListener('click', e => { + e.stopPropagation(); + options.subtile.fontFamilyCustom = [fullName, family]; + options.subtile.fontFamily = family; + Element.add('option', { style: { fontFamily: family }, attribute: { value: family }, appendTo: this.$font, innerText: fullName }); + this.$font.value = family; + popover.hidePopover(); + }) + popover.appendChild(button); + } + }); + popover.addEventListener('toggle', () => { + popover.matches(":popover-open") || popover.remove(); + }); + popover.showPopover(); + }) + .catch(e => { + toastr.error(e); + console.log(e); }); - popover.showPopover(); - }); } else { options.subtile.fontFamily = this.$font.value; } @@ -283,8 +299,8 @@ export class ClosedCaption extends HTMLButtonElement { src: URL.createObjectURL(file), label: file.name.replace(/\.vtt$/g, '') } - Element.add('option', { value: file.name }, this.$select, file.name); - this.disabled = false; + Element.add('option', { attribute: { value: file.name }, appendTo: this.$select, innerText: file.name }); + this.classList.remove('disabled'); } } @@ -296,9 +312,9 @@ export class ClosedCaption extends HTMLButtonElement { } }) this.$files = {}; - track.identify(); + this.#player.$video.$track.identify(); this.classList.remove('on'); - this.disabled = true; + this.classList.add('disabled'); } } diff --git a/src/player/area/control/time.ts b/src/player/area/control/time.ts index 1740284..5cd8a38 100644 --- a/src/player/area/control/time.ts +++ b/src/player/area/control/time.ts @@ -1,8 +1,7 @@ +import { Player } from "../.."; import { customElement } from "../../../utils/Decorator/customElement"; -import { Element } from "../../../utils/element"; import { Format } from "../../../utils/fomat"; -import { PLAYER_EVENT, ev } from "../../event-target"; -import { video } from "../wrap/video"; +import { ev, PLAYER_EVENT } from "../../event"; /** 播放器时间轴显示 */ @customElement('output') @@ -34,98 +33,111 @@ export class Time extends HTMLOutputElement { /** 每当元素被移动到新文档中时调用。 */ // adoptedCallback() {} + #player: Player; + /** 跳转输入 */ - protected $seek = Element.add('input', { class: 'bofqi-control-time-seek', value: "00:00" }, this); + #seek = this.appendChild(document.createElement('input')); /** 时间戳容器 */ - protected $wrap = Element.add('div', { class: 'bofqi-control-time-wrap' }, this); + #wrap = this.appendChild(document.createElement('div')); /** 当前时间 */ - protected $current = Element.add('span', { class: 'bofqi-control-time-now' }, this.$wrap, '00:00'); + #current = this.#wrap.appendChild(document.createElement('time')); /** 分隔符 */ - protected $divider = Element.add('span', { class: 'bofqi-control-divider' }, this.$wrap, '/'); + #divider = this.#wrap.appendChild(document.createElement('span')); /** 合计时间 */ - protected $total = Element.add('span', { class: 'bofqi-control-time-total' }, this.$wrap, '00:00'); + #total = this.#wrap.appendChild(document.createElement('time')); - #current = 0; + #currentTime = 0; /** 当前时间 */ - set current(value: number) { - this.$current.textContent = Format.fmSeconds(this.#current = value); + set $current(value: number) { + this.#current.dateTime = this.#current.textContent = Format.fmSeconds(this.#currentTime = value); } - #total = 0; + #totalTime = 0; /** 合计时间 */ - set total(value: number) { - this.$total.textContent = Format.fmSeconds(this.#total = value); + set $total(value: number) { + this.#total.dateTime = this.#total.textContent = Format.fmSeconds(this.#totalTime = value); } - constructor() { + constructor(player: Player) { super(); + this.#player = player; this.classList.add('bofqi-control-time'); + this.#seek.classList.add('bofqi-control-time-seek'); + this.#seek.value = '00:00'; + this.#wrap.classList.add('bofqi-control-time-wrap'); + this.#current.classList.add('bofqi-control-time-now'); + this.#divider.classList.add('bofqi-control-divider'); + this.#divider.innerText = '/'; + this.#total.classList.add('bofqi-control-time-total'); + - video.addEventListener('timeupdate', () => { + player.$video.addEventListener('timeupdate', () => { // 当前时间更新 - this.current = video.currentTime; + this.$current = player.$video.currentTime; }) - video.addEventListener('durationchange', () => { + player.$video.addEventListener('durationchange', () => { // 合计时间更新 - this.total = video.duration; + this.$total = player.$video.duration; }); - ev.bind(PLAYER_EVENT.IDENTIFY, this.identity); - this.$wrap.addEventListener('click', e => { + ev.bind(PLAYER_EVENT.VIDEO_DESTORY, this.#identity); + this.#wrap.addEventListener('click', e => { // 输入跳帧 e.stopPropagation(); - if (this.#total) { + if (this.#totalTime) { this.classList.add('seeking'); - this.$seek.focus(); - this.$seek.value = Format.fmSeconds(this.#current); - this.$seek.addEventListener('focusout', this.focusout, { once: true }); - this.$seek.addEventListener('keydown', this.keydown); + this.#seek.focus(); + this.#seek.value = Format.fmSeconds(this.#currentTime); + this.#seek.addEventListener('focusout', this.#onFocusOut, { once: true }); + this.#seek.addEventListener('keydown', this.#onKeyDown); } }); + + this.#identity(); } - protected focusout = () => { - this.$seek.removeEventListener('keydown', this.keydown); + #onFocusOut = () => { + this.#seek.removeEventListener('keydown', this.#onKeyDown); this.classList.remove('seeking'); - const newTime = Format.fmSecondsReverse(this.$seek.value); - if (this.#current !== newTime) { + const newTime = Format.fmSecondsReverse(this.#seek.value); + if (this.#currentTime !== newTime) { // 跳帧播放 - video.seek(newTime); + this.#player.$video.$seek(newTime); } } - protected keydown = (e: KeyboardEvent) => { + #onKeyDown = (e: KeyboardEvent) => { e.stopPropagation(); switch (e.key) { case 'Enter': { - this.$seek.removeEventListener('focusout', this.focusout); - this.$seek.removeEventListener('keydown', this.keydown); + this.#seek.removeEventListener('focusout', this.#onFocusOut); + this.#seek.removeEventListener('keydown', this.#onKeyDown); this.classList.remove('seeking'); - const newTime = Format.fmSecondsReverse(this.$seek.value); - if (this.#current !== newTime) { + const newTime = Format.fmSecondsReverse(this.#seek.value); + if (this.#currentTime !== newTime) { // 跳帧播放 - video.seek(newTime); + this.#player.$video.$seek(newTime); } - this.$seek.removeEventListener('keydown', this.keydown); + this.#seek.removeEventListener('keydown', this.#onKeyDown); break; } case 'Escape': { - this.$seek.removeEventListener('focusout', this.focusout); - this.$seek.removeEventListener('keydown', this.keydown); + this.#seek.removeEventListener('focusout', this.#onFocusOut); + this.#seek.removeEventListener('keydown', this.#onKeyDown); this.classList.remove('seeking'); - this.$seek.removeEventListener('keydown', this.keydown); + this.#seek.removeEventListener('keydown', this.#onKeyDown); break; } } } - protected identity = () => { - this.#current = this.#total = 0; + #identity = () => { + this.$current = this.$total = 0; } } \ No newline at end of file diff --git a/src/player/area/control/toggle.ts b/src/player/area/control/toggle.ts index e1e6f7f..bc12cbf 100644 --- a/src/player/area/control/toggle.ts +++ b/src/player/area/control/toggle.ts @@ -1,11 +1,12 @@ +import { Player } from "../.."; +import svg_24pause from "../../../assets/svg/24pause.svg"; +import svg_24play from "../../../assets/svg/24play.svg"; import { customElement } from "../../../utils/Decorator/customElement"; -import svg_pause from "../../assets/svg/pause.svg"; -import svg_play from "../../assets/svg/play.svg"; -import { video } from "../wrap/video"; +import { ev, PLAYER_EVENT } from "../../event"; -/** 播放器播放暂停控制 */ -@customElement('button') -export class Toggle extends HTMLButtonElement { +/** 播放/暂停 */ +@customElement('label') +export class Toggle extends HTMLLabelElement { /** * 需要监听变动的属性。 @@ -21,9 +22,6 @@ export class Toggle extends HTMLButtonElement { */ // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} - /** 初始化标记 */ - // #inited = false; - /** 每当元素添加到文档中时调用。 */ // connectedCallback() { } @@ -33,32 +31,33 @@ export class Toggle extends HTMLButtonElement { /** 每当元素被移动到新文档中时调用。 */ // adoptedCallback() {} - constructor() { + #player: Player; + + constructor(player: Player) { super(); - this.classList.add('bofqi-control-button'); - this.insertAdjacentHTML('beforeend', svg_play); + this.#player = player; + this.classList.add('bofqi-area-control-btn', 'bofqi-area-toggle', 'paused'); + this.innerHTML = svg_24play + svg_24pause; - this.disabled = true; - this.addEventListener('click', () => { - video.paused ? video.play() : video.pause(); - }); - video.addEventListener('click', () => { - video.paused ? video.play() : video.pause(); - }); - video.addEventListener('pause', () => { - this.innerHTML = svg_play; - }); - video.addEventListener('play', () => { - this.innerHTML = svg_pause; - }); - video.addEventListener('loadstart', () => { - this.disabled = true; - this.innerHTML = svg_play; - }); - video.addEventListener('loadeddata', () => { - this.disabled = false; + this.addEventListener('click', player.$video.$toggle); + player.$video.addEventListener('click', player.$video.$toggle); + player.$video.addEventListener('pause', this.#toggle); + player.$video.addEventListener('play', this.#toggle); + player.$video.addEventListener('canplay', () => { + this.classList.remove('disabled'); }); + ev.bind(PLAYER_EVENT.VIDEO_DESTORY, this.#indetify); + + this.#indetify(); + } + + #toggle = () => { + this.classList.toggle('paused', Boolean(this.#player.$video?.paused)); + } + + #indetify = () => { + this.classList.add('disabled'); } } \ No newline at end of file diff --git a/src/player/area/control/volume.ts b/src/player/area/control/volume.ts index 22b9177..43e90d5 100644 --- a/src/player/area/control/volume.ts +++ b/src/player/area/control/volume.ts @@ -1,14 +1,14 @@ +import { Player } from "../.."; +import svg_24soundlarge from "../../../assets/svg/24soundlarge.svg"; +import svg_24soundoff from "../../../assets/svg/24soundoff.svg"; +import svg_24soundsmall from "../../../assets/svg/24soundsmall.svg"; import { customElement } from "../../../utils/Decorator/customElement"; -import { Element } from "../../../utils/element"; -import svg_volume_large from "../../assets/svg/volume-large.svg"; -import svg_volume_muted from "../../assets/svg/volume-muted.svg"; -import svg_volume from "../../assets/svg/volume.svg"; +import { ev, PLAYER_EVENT } from "../../event"; import { Slider } from "../../widget/slider"; -import { video } from "../wrap/video"; -/** 播放器音量控制 */ -@customElement('button') -export class Volume extends HTMLButtonElement { +/** 音量控制 */ +@customElement('label') +export class Volume extends HTMLLabelElement { /** * 需要监听变动的属性。 @@ -24,75 +24,100 @@ export class Volume extends HTMLButtonElement { */ // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} - /** 初始化标记 */ - // #inited = false; - /** 每当元素添加到文档中时调用。 */ - // connectedCallback() { } + connectedCallback() { + this.insertAdjacentElement('afterend', this.#wrap); + self.addEventListener('keydown', this.#onKeyDown); + } /** 每当元素从文档中移除时调用。 */ - // disconnectedCallback() {} + disconnectedCallback() { + this.#wrap.remove(); + self.removeEventListener('keydown', this.#onKeyDown); + } /** 每当元素被移动到新文档中时调用。 */ // adoptedCallback() {} - /** 浮动面板 */ - $wrap = Element.add('div', { class: 'bofqi-control-volume-wrap' }, this); + #player: Player; - /** 音量条 */ - $slider = this.$wrap.appendChild(new Slider()); - - constructor() { - super(); + #wrap = document.createElement('div'); - this.classList.add('bofqi-control-button', 'bofqi-control-volume'); - this.insertAdjacentHTML('afterbegin', svg_volume + svg_volume_large + svg_volume_muted); + #slider = this.#wrap.appendChild(new Slider()); - this.$slider.defaultValue = '0'; - this.disabled = true; + constructor(player: Player) { + super(); - video.addEventListener('loadstart', () => { - this.disabled = true; - this.classList.toggle('muted', video.muted); - this.classList.toggle('large', video.volume >= 0.5); - this.$slider.$value = Math.floor(video.volume * 100); - this.$wrap.dataset.value = Math.floor(video.volume * 100); - }); - video.addEventListener('loadeddata', () => { - this.disabled = false; - }); - video.addEventListener('volumechange', () => { - this.classList.toggle('muted', video.muted); - this.classList.toggle('large', video.volume >= 0.5); - this.$slider.$value = Math.floor(video.volume * 100); - this.$wrap.dataset.value = Math.floor(video.volume * 100); - }); + this.#player = player; + this.classList.add('bofqi-area-control-btn', 'bofqi-area-volume'); + this.innerHTML = svg_24soundsmall + svg_24soundlarge + svg_24soundoff; + this.#wrap.classList.add('bofqi-volume-wrap'); + this.#slider.classList.add('bofqi-volume-slider'); - this.addEventListener('click', () => { - video.muted = !video.muted; + player.$video.addEventListener('volumechange', this.#volumeChange); + player.$video.addEventListener('canplay', () => { + this.classList.remove('disabled'); + this.#volumeChange(); }); - this.$wrap.addEventListener('click', e => { - e.stopPropagation(); - }) - this.$slider.addEventListener('change', () => { - video.volume = +this.$slider.$value / 100; + ev.bind(PLAYER_EVENT.VIDEO_DESTORY, this.#indetify); + this.addEventListener('click', this.#toggle); + this.#slider.addEventListener('change', () => { + player.$video.volume = +this.#slider.$value / 100; }); - this.addEventListener('wheel', e => { + this.#wrap.addEventListener('wheel', e => { // 响应滚轮 e.stopPropagation(); - e.preventDefault(); - this.wheel(e); - }); + this.#wheel(e); + }, { passive: true }); + + this.#indetify(); + } + + /** 键盘事件回调 */ + #onKeyDown = ({ key, shiftKey, ctrlKey, altKey, metaKey }: KeyboardEvent) => { + try { + const { activeElement } = document; + if (activeElement === document.body || activeElement === this.#player || this.#player.contains(activeElement) && !(activeElement?.hasAttribute('contenteditable') || activeElement instanceof HTMLInputElement || activeElement instanceof HTMLTextAreaElement)) { + switch (key) { + // 不能区分小键盘,但能识别 Shift 后的值 + case 'M': case 'm': { + // 音量开关 + shiftKey || ctrlKey || altKey || metaKey || this.#toggle(); + break; + } + } + // switch (code) { + // // 能区分小键盘,但不识别 Shift 后的值 + // } + } + } catch { } + } + + /** 开关切换 */ + #toggle = () => { + this.#player.$video.muted = !this.#player.$video.muted; + } + + /** 视频音量变动回调 */ + #volumeChange = () => { + const { muted, volume } = this.#player.$video; + this.classList.toggle('muted', muted); + this.classList.toggle('large', volume >= 0.5); + this.#wrap.dataset.volume = this.#slider.$value = Math.floor(volume * 100); } /** 滚轮响应 */ - protected wheel = (e: WheelEvent) => { + #wheel = (e: WheelEvent) => { const { deltaY } = e; // 超出范围会报错,直接忽略 if (deltaY < 0) { - video.volume = Math.min(1, video.volume + 0.1); + this.#player.$video.volume = Math.min(1, this.#player.$video.volume + 0.1); } else { - video.volume = Math.max(0, video.volume - 0.1); + this.#player.$video.volume = Math.max(0, this.#player.$video.volume - 0.1); } } + + #indetify = () => { + this.classList.add('disabled'); + } } \ No newline at end of file diff --git a/src/player/area/control/wide.ts b/src/player/area/control/wide.ts deleted file mode 100644 index 56aaf32..0000000 --- a/src/player/area/control/wide.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { customElement } from "../../../utils/Decorator/customElement"; -import svg_screen_wide_on from "../../assets/svg/screen-wide-on.svg"; -import svg_screen_wide from "../../assets/svg/screen-wide.svg"; -import { PLAYER_EVENT, ev } from "../../event-target"; -import { PLAYER_MODE, PLAYER_STATE } from "../../state"; - -/** 播放器宽屏控制 */ -@customElement('button') -export class Wide extends HTMLButtonElement { - - /** - * 需要监听变动的属性。 - * 与实例方法`attributeChangedCallback`配合使用。 - * 此字符串序列定义了`attributeChangedCallback`回调时的第一个参数的可能值。 - */ - // static observedAttributes = []; - - /** - * 在属性更改、添加、移除或替换时调用。 - * 需要与静态属性`observedAttributes`配合使用。 - * 此回调的第一个参数在`observedAttributes`数组中定义。 - */ - // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} - - /** 初始化标记 */ - // #inited = false; - - /** 每当元素添加到文档中时调用。 */ - // connectedCallback() { } - - /** 每当元素从文档中移除时调用。 */ - // disconnectedCallback() {} - - /** 每当元素被移动到新文档中时调用。 */ - // adoptedCallback() {} - - constructor() { - super(); - - this.classList.add('bofqi-control-button'); - - this.insertAdjacentHTML('beforeend', svg_screen_wide); - - this.addEventListener('click', () => { - ev.trigger(PLAYER_EVENT.PLAYER_MODE, PLAYER_STATE.mode ^= PLAYER_MODE.WIDE); - }); - - ev.bind(PLAYER_EVENT.PLAYER_MODE, ({ detail }) => { - this.innerHTML = detail & PLAYER_MODE.WIDE ? svg_screen_wide_on : svg_screen_wide; - }); - } -} \ No newline at end of file diff --git a/src/player/area/index.css b/src/player/area/index.css new file mode 100644 index 0000000..a720d31 --- /dev/null +++ b/src/player/area/index.css @@ -0,0 +1,40 @@ +@import url(./message/index.css); +@import url(./wrap/index.css); +@import url(./control/index.css); +@import url(./sendbar/index.css); + +.bofqi-area { + flex-grow: 1; + display: flex; + flex-direction: column; + position: relative; + + >.bofqi-area-contents { + display: contents; + + @container bofqi style(--fullscreen: 1) { + & { + position: absolute; + inset-inline: 0; + inset-block-end: 0; + display: flex; + flex-direction: column-reverse; + pointer-events: none; + opacity: 0; + transition: all .3s; + + &:has(:hover) { + opacity: 1; + } + } + } + + @container bofqi (inline-size < 780px) { + + & { + block-size: 0; + display: none; + } + } + } +} \ No newline at end of file diff --git a/src/player/area/index.ts b/src/player/area/index.ts index bf70c0d..6dcc0d1 100644 --- a/src/player/area/index.ts +++ b/src/player/area/index.ts @@ -1,10 +1,11 @@ +import { Player } from ".."; import { customElement } from "../../utils/Decorator/customElement"; import { Control } from "./control"; import { Message } from "./message"; import { Sendbar } from "./sendbar"; import { Wrap } from "./wrap"; -/** 播放器区域 */ +/** 播放器主区域 */ @customElement('div') export class Area extends HTMLDivElement { @@ -22,11 +23,8 @@ export class Area extends HTMLDivElement { */ // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} - /** 初始化标记 */ - // #inited = false; - /** 每当元素添加到文档中时调用。 */ - // connectedCallback() {} + // connectedCallback() { } /** 每当元素从文档中移除时调用。 */ // disconnectedCallback() {} @@ -34,21 +32,26 @@ export class Area extends HTMLDivElement { /** 每当元素被移动到新文档中时调用。 */ // adoptedCallback() {} - /** 播放器通知区域 */ - $message = this.appendChild(new Message()); + #player: Player; + + #message: Message; - /** 播放器容器区域 */ - $wrap = this.appendChild(new Wrap()); + #wrap: Wrap; - /** 播放器控制区域 */ - $control = this.appendChild(new Control()); + $control: Control; - /** 播放器发送区域 */ - $sendbar = this.appendChild(new Sendbar()); + #sendbar: Sendbar; - constructor() { + constructor(player: Player) { super(); + this.#player = player; this.classList.add('bofqi-area'); + this.#message = this.appendChild(new Message(this.#player)); + this.#wrap = this.appendChild(new Wrap(this.#player)); + const buttom = this.appendChild(document.createElement('div')); + buttom.classList.add('bofqi-area-contents'); + this.$control = buttom.appendChild(new Control(this.#player)); + this.#sendbar = buttom.appendChild(new Sendbar(this.#player)); } } \ No newline at end of file diff --git a/src/player/area/message/index.css b/src/player/area/message/index.css new file mode 100644 index 0000000..3243595 --- /dev/null +++ b/src/player/area/message/index.css @@ -0,0 +1,72 @@ +.bofqi-area-message { + + block-size: 48px; + flex-shrink: 0; + background-color: var(--353535); + display: flex; + padding: 6px; + gap: 12px; + overflow: clip; + transition: all .3s allow-discrete; + + @starting-style { + block-size: 0; + } + + @container bofqi style(--screen-wide: 1) { + & { + block-size: 0; + display: none; + } + } + + @container bofqi style(--fullscreen: 1) { + & { + block-size: 0; + display: none; + } + } + + @container bofqi (inline-size < 780px) { + + & { + block-size: 0; + display: none; + } + } + + >.bofqi-message-prev, + >.bofqi-message-next { + flex-shrink: 0; + border-radius: 3px; + padding-inline: 2px; + background: var(--262626); + text-align: center; + align-content: center; + cursor: pointer; + + >svg { + inline-size: 1em; + aspect-ratio: 1; + fill: var(--fff); + transition: fill .3s; + } + + &:hover>svg { + fill: var(--00a1d6) + } + } + + >.bofqi-message-prev>svg { + rotate: 180deg; + } + + >.bofqi-message-panel { + flex-grow: 1; + background: #262626; + font-size: 13px; + color: #fff; + text-align: center; + align-content: center; + } +} \ No newline at end of file diff --git a/src/player/area/message/index.ts b/src/player/area/message/index.ts index 9190214..72ed3dc 100644 --- a/src/player/area/message/index.ts +++ b/src/player/area/message/index.ts @@ -1,8 +1,8 @@ +import { Player } from "../.."; +import svg_12sent from "../../../assets/svg/12sent.svg"; import { customElement } from "../../../utils/Decorator/customElement"; -import { Element } from "../../../utils/element"; -import svg_sent from "../../assets/svg/sent.svg"; -/** 播放器通知区域 */ +/** 播放器消息区域 */ @customElement('div') export class Message extends HTMLDivElement { @@ -20,11 +20,8 @@ export class Message extends HTMLDivElement { */ // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} - /** 初始化标记 */ - // #inited = false; - /** 每当元素添加到文档中时调用。 */ - // connectedCallback() {} + // connectedCallback() { } /** 每当元素从文档中移除时调用。 */ // disconnectedCallback() {} @@ -32,15 +29,23 @@ export class Message extends HTMLDivElement { /** 每当元素被移动到新文档中时调用。 */ // adoptedCallback() {} - private $prev = Element.add('button', { class: 'bofqi-message-prev' }, this, svg_sent); + #player: Player; + + #prev = this.appendChild(document.createElement('div')); - private $panel = Element.add('div', { class: 'bofqi-message-panel' }, this); + #panel = this.appendChild(document.createElement('div')); - private $next = Element.add('button', { class: 'bofqi-message-next' }, this, svg_sent); + #next = this.appendChild(document.createElement('div')); - constructor() { + constructor(player: Player) { super(); + this.#player = player; this.classList.add('bofqi-area-message'); + this.#prev.classList.add('bofqi-message-prev'); + this.#prev.innerHTML = svg_12sent; + this.#panel.classList.add('bofqi-message-panel'); + this.#next.classList.add('bofqi-message-next'); + this.#next.innerHTML = svg_12sent; } } \ No newline at end of file diff --git a/src/player/area/sendbar/choose.ts b/src/player/area/sendbar/choose.ts deleted file mode 100644 index 174eb7a..0000000 --- a/src/player/area/sendbar/choose.ts +++ /dev/null @@ -1,168 +0,0 @@ -import { customElement } from "../../../utils/Decorator/customElement"; -import { Element } from "../../../utils/element"; -import svg_danmaku_current from "../../assets/svg/danmaku-current.svg"; -import svg_danmaku_mode_1 from "../../assets/svg/danmaku-mode-1.svg"; -import svg_danmaku_mode_4 from "../../assets/svg/danmaku-mode-4.svg"; -import svg_danmaku_mode_5 from "../../assets/svg/danmaku-mode-5.svg"; -import svg_danmaku_mode_6 from "../../assets/svg/danmaku-mode-6.svg"; -import svg_danmaku_setting from "../../assets/svg/danmaku-setting.svg"; - -/** 弹幕配置 */ -@customElement('button') -export class DanmakuChoose extends HTMLButtonElement { - - /** - * 需要监听变动的属性。 - * 与实例方法`attributeChangedCallback`配合使用。 - * 此字符串序列定义了`attributeChangedCallback`回调时的第一个参数的可能值。 - */ - // static observedAttributes = []; - - /** - * 在属性更改、添加、移除或替换时调用。 - * 需要与静态属性`observedAttributes`配合使用。 - * 此回调的第一个参数在`observedAttributes`数组中定义。 - */ - // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} - - /** 初始化标记 */ - // #inited = false; - - /** 每当元素添加到文档中时调用。 */ - // connectedCallback() {} - - /** 每当元素从文档中移除时调用。 */ - // disconnectedCallback() {} - - /** 每当元素被移动到新文档中时调用。 */ - // adoptedCallback() {} - - private $wrap = Element.add('div', { class: 'bofqi-sendbar-choose-wrap', popover: 'auto' }, this); - - /** 类型 */ - private $type = Element.add('div', { class: 'bofqi-sendbar-choose-row' }, this.$wrap, '
    类型
    '); - - private $typeSelection = Element.add('div', { class: 'bofqi-sendbar-choose-selection' }, this.$type); - - /** 普通弹幕 */ - private $typeNormal = Element.add('div', { class: 'active', 'data-pool': '0' }, this.$typeSelection, '普通弹幕'); - - /** 字幕弹幕 */ - private $typeCaption = Element.add('div', { 'data-pool': '1' }, this.$typeSelection, '字幕弹幕'); - - /** 特殊弹幕 */ - private $typeSpecial = Element.add('div', { 'data-pool': '2' }, this.$typeSelection, '特殊弹幕'); - - /** 字号 */ - private $dmSize = Element.add('div', { class: 'bofqi-sendbar-choose-row' }, this.$wrap, '
    字号
    '); - - private $sizeSelection = Element.add('div', { class: 'bofqi-sendbar-choose-selection' }, this.$dmSize); - - /** 极小 */ - private $sizeSmallMost = Element.add('div', { 'data-size': '12' }, this.$sizeSelection, '极小'); - - /** 超小 */ - private $sizeSmallMore = Element.add('div', { 'data-size': '16' }, this.$sizeSelection, '超小'); - - /** 小 */ - private $sizeSmall = Element.add('div', { 'data-size': '18' }, this.$sizeSelection, '小'); - - /** 中 */ - private $sizeNormal = Element.add('div', { class: 'active', 'data-size': '25' }, this.$sizeSelection, '中'); - - /** 大 */ - private $sizeBig = Element.add('div', { 'data-size': '36' }, this.$sizeSelection, '大'); - - /** 超大 */ - private $sizeBigMore = Element.add('div', { 'data-size': '45' }, this.$sizeSelection, '超大'); - - /** 极大 */ - private $sizeBigMost = Element.add('div', { 'data-size': '64' }, this.$sizeSelection, '极大'); - - /** 模式 */ - private $dmMode = Element.add('div', { class: 'bofqi-sendbar-choose-row' }, this.$wrap, '
    模式
    '); - - private $modeSelection = Element.add('div', { class: 'bofqi-sendbar-choose-selection' }, this.$dmMode); - - /** 滚动弹幕 */ - private $scroll = Element.add('div', { class: 'bofqi-sendbar-choose-mode active', 'data-mode': '1' }, this.$modeSelection, svg_danmaku_mode_1 + svg_danmaku_current); - - /** 顶部弹幕 */ - private $top = Element.add('div', { class: 'bofqi-sendbar-choose-mode', 'data-mode': '5' }, this.$modeSelection, svg_danmaku_mode_5 + svg_danmaku_current); - - /** 底部弹幕 */ - private $bottom = Element.add('div', { class: 'bofqi-sendbar-choose-mode', 'data-mode': '4' }, this.$modeSelection, svg_danmaku_mode_4 + svg_danmaku_current); - - /** 逆向弹幕 */ - private $reverse = Element.add('div', { class: 'bofqi-sendbar-choose-mode', 'data-mode': '6' }, this.$modeSelection, svg_danmaku_mode_6 + svg_danmaku_current); - - /** - * 弹幕池 - * | 0 | 1 | 2 | - * | :-: | :-: | :-: | - * | 普通弹幕 | 字幕弹幕 | 特殊弹幕 | - */ - $pool: 0 | 1 | 2 = 0; - - /** 字体大小 */ - $size = 25; - - /** - * 弹幕模式 - * | 1 | 4 | 5 | 6 | 7 | 8 | 9 | - * | :-: | :-: | :-: | :-: | :-: | :-: | :-: | - * | 普通 | 底部 | 顶部 | 逆向 | 高级 | 代码 | BAS | - */ - $mode: 1 | 4 | 5 | 6 | 7 | 8 | 9 = 1; - - constructor() { - super(); - - this.classList.add('bofqi-sendbar-choose'); - this.$wrap.insertAdjacentHTML('beforebegin', svg_danmaku_setting); - this.popoverTargetElement = this.$wrap; - - this.$typeSpecial.classList.add('disabled'); // 特殊弹幕被禁用 - this.$scroll.dataset.label = '滚动弹幕'; - this.$top.dataset.label = '顶部弹幕'; - this.$bottom.dataset.label = '底部弹幕'; - this.$reverse.dataset.label = '逆向弹幕'; - - this.$wrap.addEventListener('click', e => { - e.preventDefault(); - e.stopPropagation(); - - switch (e.target) { - case this.$typeNormal: - case this.$typeCaption: - case this.$typeSpecial: - case this.$sizeSmallMost: - case this.$sizeSmallMore: - case this.$sizeSmall: - case this.$sizeNormal: - case this.$sizeBig: - case this.$sizeBigMore: - case this.$sizeBigMost: - case this.$scroll: - case this.$top: - case this.$bottom: - case this.$reverse: { - if (!(e.target).classList.contains('disabled')) { - (e.target).parentElement!.querySelector('.active')?.classList.remove('active'); - (e.target).classList.add('active'); - if ((e.target).dataset.pool) { - this.$pool = <1>+(e.target).dataset.pool!; - } - if ((e.target).dataset.size) { - this.$size = +(e.target).dataset.size!; - } - if ((e.target).dataset.mode) { - this.$mode = <1>+(e.target).dataset.mode!; - } - } - break; - } - } - }); - } -} \ No newline at end of file diff --git a/src/player/area/sendbar/choose/index.css b/src/player/area/sendbar/choose/index.css new file mode 100644 index 0000000..c2b439a --- /dev/null +++ b/src/player/area/sendbar/choose/index.css @@ -0,0 +1,148 @@ +.bofqi-sendbar-choose { + flex-shrink: 0; + padding: 0; + font-size: 14px; + cursor: pointer; + text-align: center; + align-content: center; + transition: 0.3s; + anchor-name: --sendbar-choose; + + >svg { + block-size: 1em; + aspect-ratio: 1; + fill: var(--99a2aa); + transition: 0.3s; + } + + &:hover { + background-color: var(--f4f5f7); + color: var(--6d757a); + + >svg { + fill: var(--6d757a); + } + } +} + +.sendbar-choose-wrap { + position-anchor: --sendbar-choose; + position-area: block-start span-inline-end; + position-visibility: anchors-visible; + padding-inline: 10px; + padding-block: 20px; + border: 1px solid var(--e2e2e2); + border-radius: 4px; + background-color: var(--fff); + transition: all .3s ease-in-out; + border-start-start-radius: 4px; + border-start-end-radius: 4px; + flex-direction: column; + row-gap: 1em; + transform-origin: 0 100%; + transition: all .3s allow-discrete; + + @starting-style { + opacity: 0; + scale: 0 1; + } + + &[popover]:popover-open:not(dialog) { + display: flex; + } + + &[popover]:not(:popover-open):not(dialog[open]) { + opacity: 0; + scale: 0 1; + } + + >.choose-wrap-form { + display: flex; + align-items: center; + column-gap: 1em; + + &::before { + content: attr(data-label); + inline-size: 3em; + } + + >label { + border: 1px solid transparent; + padding: 4px 5px; + border-radius: 4px; + transition: all .3s ease-in-out; + cursor: pointer; + display: flex; + flex-direction: column; + align-items: center; + transition: color .3s; + position: relative; + + >input { + display: none; + appearance: none; + } + + >svg { + block-size: 1em; + aspect-ratio: 1; + + &:not(:first-child) { + font-size: 48px; + fill: var(--99a2aa); + transition: fill .3s; + anchor-name: --danmaku-select; + } + + &:last-child { + position: absolute; + inset-inline-end: anchor(--danmaku-select end); + inset-block-end: anchor(--danmaku-select end); + font-size: 24px; + background-color: var(--fff); + border: 3px solid var(--fff); + fill: var(--99a2aa); + border-radius: 50%; + box-sizing: border-box; + opacity: 0; + transition: all .3s; + } + } + + &[data-label]:after { + pointer-events: none; + content: attr(data-label); + display: block; + } + + &:not(:has(input:disabled)):hover { + color: #00a1d6; + + >svg { + fill: var(--6d757a); + + &:last-child { + opacity: 1; + } + } + } + + &:has(input:checked) { + color: var(--00a1d6); + border-color: var(--00a1d6); + pointer-events: none; + cursor: default; + + >svg:last-child { + opacity: 1; + fill: var(--00a1d6); + } + } + + &:has(input:disabled) { + color: var(--99a2aa); + cursor: not-allowed; + } + } + } +} \ No newline at end of file diff --git a/src/player/area/sendbar/choose/index.ts b/src/player/area/sendbar/choose/index.ts new file mode 100644 index 0000000..6ca7c17 --- /dev/null +++ b/src/player/area/sendbar/choose/index.ts @@ -0,0 +1,113 @@ +import { Player } from "../../.."; +import svg_24danmucurrent from "../../../../assets/svg/24danmucurrent.svg"; +import svg_24danmusetting from "../../../../assets/svg/24danmusetting.svg"; +import svg_48danmuback from "../../../../assets/svg/48danmuback.svg"; +import svg_48danmubottom from "../../../../assets/svg/48danmubottom.svg"; +import svg_48danmunormal from "../../../../assets/svg/48danmunormal.svg"; +import svg_48danmutop from "../../../../assets/svg/48danmutop.svg"; +import { customElement } from "../../../../utils/Decorator/customElement"; +import { Element } from "../../../../utils/element"; + +/** 弹幕类型 */ +@customElement('button') +export class DanmakuChoose extends HTMLButtonElement { + + /** + * 需要监听变动的属性。 + * 与实例方法`attributeChangedCallback`配合使用。 + * 此字符串序列定义了`attributeChangedCallback`回调时的第一个参数的可能值。 + */ + // static observedAttributes = []; + + /** + * 在属性更改、添加、移除或替换时调用。 + * 需要与静态属性`observedAttributes`配合使用。 + * 此回调的第一个参数在`observedAttributes`数组中定义。 + */ + // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} + + /** 每当元素添加到文档中时调用。 */ + connectedCallback() { + this.insertAdjacentElement('afterend', this.#wrap); + } + + /** 每当元素从文档中移除时调用。 */ + disconnectedCallback() { + this.#wrap.remove(); + } + + /** 每当元素被移动到新文档中时调用。 */ + // adoptedCallback() {} + + #player: Player; + + #wrap = Element.add('div', { class: 'sendbar-choose-wrap' }); + + #id = crypto.randomUUID(); + + #pool = Element.add('form', { class: 'choose-wrap-form', appendTo: this.#wrap, data: { label: '类型' }, innerHTML: `` }); + + #size = Element.add('form', { + class: 'choose-wrap-form', appendTo: this.#wrap, data: { label: '字号' }, innerHTML: ` + + + + + +` + }); + + #mode = Element.add('form', { + class: 'choose-wrap-form', appendTo: this.#wrap, data: { label: '类型' }, innerHTML: ` + + +` + }); + + /** + * 弹幕池 + * | 0 | 1 | 2 | + * | :-: | :-: | :-: | + * | 普通弹幕 | 字幕弹幕 | 特殊弹幕 | + */ + $pool: 0 | 1 | 2 = 0; + + /** 字体大小 */ + $size = 25; + + /** + * 弹幕模式 + * | 1 | 4 | 5 | 6 | 7 | 8 | 9 | + * | :-: | :-: | :-: | :-: | :-: | :-: | :-: | + * | 普通 | 底部 | 顶部 | 逆向 | 高级 | 代码 | BAS | + */ + $mode: 1 | 4 | 5 | 6 | 7 | 8 | 9 = 1; + + constructor(player: Player) { + super(); + + this.#player = player; + this.classList.add('bofqi-sendbar-choose'); + this.innerHTML = svg_24danmusetting; + this.#wrap.popover = 'auto'; + this.popoverTargetElement = this.#wrap; + + this.#pool.addEventListener('change', () => { + const d = new FormData(this.#pool); + const i = +[...d.values()][0]; + this.$pool = <0>i; + }); + + this.#size.addEventListener('change', () => { + const d = new FormData(this.#size); + const i = +[...d.values()][0]; + this.$size = i; + }); + + this.#mode.addEventListener('change', () => { + const d = new FormData(this.#mode); + const i = +[...d.values()][0]; + this.$mode = <1>i; + }); + } +} \ No newline at end of file diff --git a/src/player/area/sendbar/color/index.css b/src/player/area/sendbar/color/index.css new file mode 100644 index 0000000..7900d27 --- /dev/null +++ b/src/player/area/sendbar/color/index.css @@ -0,0 +1,41 @@ +.bofqi-sendbar-color { + flex-shrink: 0; + padding: 0; + font-size: 14px; + cursor: pointer; + text-align: center; + align-content: center; + transition: 0.3s; + background-color: var(--color-select); + anchor-name: --bpui-color; + + >svg { + block-size: 1em; + aspect-ratio: 1; + fill: var(--99a2aa); + transition: 0.3s; + } + + &:hover { + color: var(--6d757a); + + >svg { + fill: var(--6d757a); + } + } + + &~.bpui-color { + transform-origin: 0 100%; + transition: all .3s allow-discrete; + + @starting-style { + opacity: 0; + scale: 0 1; + } + + &[popover]:not(:popover-open):not(dialog[open]) { + opacity: 0; + scale: 0 1; + } + } +} \ No newline at end of file diff --git a/src/player/area/sendbar/color.ts b/src/player/area/sendbar/color/index.ts similarity index 67% rename from src/player/area/sendbar/color.ts rename to src/player/area/sendbar/color/index.ts index 72e2fbc..01a8423 100644 --- a/src/player/area/sendbar/color.ts +++ b/src/player/area/sendbar/color/index.ts @@ -1,6 +1,7 @@ -import { customElement } from "../../../utils/Decorator/customElement"; -import svg_color from "../../assets/svg/color.svg"; -import { Color } from "../../widget/color"; +import { Player } from "../../.."; +import svg_24color from "../../../../assets/svg/24color.svg"; +import { customElement } from "../../../../utils/Decorator/customElement"; +import { Color } from "../../../widget/color"; /** 弹幕颜色 */ @customElement('button') @@ -20,19 +21,22 @@ export class DanmakuColor extends HTMLButtonElement { */ // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} - /** 初始化标记 */ - // #inited = false; - /** 每当元素添加到文档中时调用。 */ - // connectedCallback() {} + connectedCallback() { + this.insertAdjacentElement('afterend', this.#wrap); + } /** 每当元素从文档中移除时调用。 */ - // disconnectedCallback() {} + disconnectedCallback() { + this.#wrap.remove(); + } /** 每当元素被移动到新文档中时调用。 */ // adoptedCallback() {} - private $wrap = new Color(this); + #player: Player; + + #wrap = new Color(this); #value = '#ffffff' @@ -49,13 +53,15 @@ export class DanmakuColor extends HTMLButtonElement { this.style.setProperty('--color-select', v); } - constructor() { + constructor(player: Player) { super(); + this.#player = player; this.classList.add('bofqi-sendbar-color'); - this.$wrap.insertAdjacentHTML('beforebegin', svg_color); - this.$wrap.addEventListener('change', () => { - this.$value = this.$wrap.$value; + this.innerHTML = svg_24color; + + this.#wrap.addEventListener('change', () => { + this.$value = this.#wrap.$value; }); } } \ No newline at end of file diff --git a/src/player/area/sendbar/index.css b/src/player/area/sendbar/index.css new file mode 100644 index 0000000..1b7eb37 --- /dev/null +++ b/src/player/area/sendbar/index.css @@ -0,0 +1,22 @@ +@import url(./choose/index.css); +@import url(./color/index.css); +@import url(./input/input.css); + +.bofqi-area-sendbar { + block-size: 38px; + flex-shrink: 0; + display: flex; + align-items: center; + padding-inline-start: 1em; + column-gap: 2em; + position: relative; + + @container bofqi style(--fullscreen: 1) { + & { + margin-inline: 230px; + background-color: var(--fff); + border: 1px solid var(--e2e2e2); + pointer-events: auto; + } + } +} \ No newline at end of file diff --git a/src/player/area/sendbar/index.ts b/src/player/area/sendbar/index.ts index b4876d3..96bc01b 100644 --- a/src/player/area/sendbar/index.ts +++ b/src/player/area/sendbar/index.ts @@ -1,9 +1,11 @@ +import { Player } from "../.."; import { customElement } from "../../../utils/Decorator/customElement"; +import { ev, PLAYER_EVENT } from "../../event"; import { DanmakuChoose } from "./choose"; import { DanmakuColor } from "./color"; -import { DanmakuInput } from "./input"; +import { DanmakuInput } from "./input/input"; -/** 播放器发送区域 */ +/** 播放器弹幕发送区域 */ @customElement('div') export class Sendbar extends HTMLDivElement { @@ -21,11 +23,8 @@ export class Sendbar extends HTMLDivElement { */ // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} - /** 初始化标记 */ - // #inited = false; - /** 每当元素添加到文档中时调用。 */ - // connectedCallback() {} + // connectedCallback() { } /** 每当元素从文档中移除时调用。 */ // disconnectedCallback() {} @@ -33,19 +32,76 @@ export class Sendbar extends HTMLDivElement { /** 每当元素被移动到新文档中时调用。 */ // adoptedCallback() {} - /** 弹幕配置 */ - $choose = this.appendChild(new DanmakuChoose()); + #player: Player; + + #choose: DanmakuChoose; - /** 弹幕颜色 */ - $color = this.appendChild(new DanmakuColor()); + #color: DanmakuColor; - /** 弹幕发送 */ - $input = this.appendChild(new DanmakuInput()); + #input: DanmakuInput; - /** 弹幕配置 */ - constructor() { + constructor(player: Player) { super(); + this.#player = player; this.classList.add('bofqi-area-sendbar'); + this.#choose = this.appendChild(new DanmakuChoose(player)); + this.#color = this.appendChild(new DanmakuColor(player)); + this.#input = this.appendChild(new DanmakuInput(player)); + + this.#input.addEventListener('submit', e => { + const d = new FormData(this.#input); + const i = d.get('danmaku'); + i && ev.trigger(PLAYER_EVENT.DANMAKU_INPUT, { + msg: i, + progress: Math.ceil(player.$video.currentTime * 1e3), + color: this.#color.$value, + fontsize: this.#choose.$size, + pool: this.#choose.$pool, + mode: this.#choose.$mode, + }); + e.preventDefault(); + this.#input.$input.value = ''; + }); } +} + +export interface IDanmakuInput { + /** 弹幕内容(长度小于 100 字符) */ + msg: string; + /** 弹幕出现在视频内的时间:/毫秒 */ + progress: number; + /** 弹幕颜色,十进制 RGB888 值 */ + color?: number; + /** 弹幕字号 */ + fontsize?: number; + /** + * 弹幕池 + * | 0 | 1 | 2 | + * | :-: | :-: | :-: | + * | 普通弹幕 | 字幕弹幕 | 特殊弹幕 | + */ + pool?: 0 | 1 | 2; + /** + * 弹幕模式 + * | 1 | 4 | 5 | 6 | 7 | 8 | 9 | + * | :-: | :-: | :-: | :-: | :-: | :-: | :-: | + * | 普通 | 底部 | 顶部 | 逆向 | 高级 | 代码 | BAS | + */ + mode?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9; + /** + * | 1 | 2 | + * | :-: | :-: | + * | 视频 | 漫画 | + */ + type?: 1 | 2; + /** 专属渐变彩色 */ + colorful?: 60001; + /** + * 是否带 UP 身份标识 + * | 0 | 4 | + * | :-: | :-: | + * | 普通 | 带有标识 | + */ + checkbox_type?: 0 | 4; } \ No newline at end of file diff --git a/src/player/area/sendbar/input.ts b/src/player/area/sendbar/input.ts deleted file mode 100644 index 1c6e2ab..0000000 --- a/src/player/area/sendbar/input.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { customElement } from "../../../utils/Decorator/customElement"; -import { Element } from "../../../utils/element"; - -/** 弹幕发送 */ -@customElement('div') -export class DanmakuInput extends HTMLDivElement { - - /** - * 需要监听变动的属性。 - * 与实例方法`attributeChangedCallback`配合使用。 - * 此字符串序列定义了`attributeChangedCallback`回调时的第一个参数的可能值。 - */ - // static observedAttributes = []; - - /** - * 在属性更改、添加、移除或替换时调用。 - * 需要与静态属性`observedAttributes`配合使用。 - * 此回调的第一个参数在`observedAttributes`数组中定义。 - */ - // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} - - /** 初始化标记 */ - // #inited = false; - - /** 每当元素添加到文档中时调用。 */ - // connectedCallback() {} - - /** 每当元素从文档中移除时调用。 */ - // disconnectedCallback() {} - - /** 每当元素被移动到新文档中时调用。 */ - // adoptedCallback() {} - - /** 输入框 */ - private $input = Element.add('input', { class: 'sendbar-input', placeholder: '您可以在这里输入弹幕吐槽哦~' }, this); - - /** 提示词 */ - private $hint = Element.add('div', { class: 'sendbar-hint' }, this, '弹幕礼仪 >'); - - /** 发送 */ - private $send = Element.add('button', { class: 'sendbar-send bpui-button' }, this, '发送 >'); - - /** 弹幕内容 */ - $value = ''; - - constructor() { - super(); - - this.classList.add('bofqi-area-sendbar-input'); - - this.$input.required = true; - this.$input.maxLength = 100; - - this.$send.addEventListener('click', this.send); - this.$input.addEventListener('keypress', e => { - if (!e.altKey && !e.ctrlKey && !e.metaKey && !e.shiftKey && e.key === 'Enter') { - this.$send.click(); - } - }); - } - - /** 发送弹幕 */ - private send = () => { - if (this.$input.reportValidity()) { - this.$value = ''; - this.dispatchEvent(new Event('send')); - this.$input.value = ''; - } - } -} \ No newline at end of file diff --git a/src/player/style/area/sendbar/input.css b/src/player/area/sendbar/input/input.css similarity index 50% rename from src/player/style/area/sendbar/input.css rename to src/player/area/sendbar/input/input.css index 3239e98..20ebbcd 100644 --- a/src/player/style/area/sendbar/input.css +++ b/src/player/area/sendbar/input/input.css @@ -1,39 +1,40 @@ -.bofqi-area-sendbar-input { +.bofqi-sendbar-input { flex-grow: 1; block-size: 100%; - background-image: linear-gradient(#f5f5f5, #fff); + background-image: linear-gradient(var(--f5f5f5), var(--fff)); display: flex; - align-items: center; >input { flex-grow: 1; border: 0; - padding-inline: 10px; - color: #6b6b6b; + outline: none; + color: var(--6b6b6b); } - .sendbar-hint { + >.sendbar-hint { flex-shrink: 0; - color: #99a2aa; + color: var(--99a2aa); cursor: pointer; + align-content: center; &:hover { - color: #00a1d6; + color: var(--00a1d6); } } - .sendbar-send { + >button { flex-shrink: 0; block-size: 28px; + margin-block: auto; margin-inline-start: 9px; margin-inline-end: 5px; padding-inline-start: 14px; padding-inline-end: 10px; &:hover { - color: #fff; - background-color: #00b5e5; - border-color: #00b5e5; + color: var(--fff); + background-color: var(--00b5e5); + border-color: var(--00b5e5); } } } \ No newline at end of file diff --git a/src/player/area/sendbar/input/input.ts b/src/player/area/sendbar/input/input.ts new file mode 100644 index 0000000..a76061a --- /dev/null +++ b/src/player/area/sendbar/input/input.ts @@ -0,0 +1,51 @@ +import { Player } from "../../.."; +import { customElement } from "../../../../utils/Decorator/customElement"; +import { Element } from "../../../../utils/element"; + +/** 弹幕输入 */ +@customElement('form') +export class DanmakuInput extends HTMLFormElement { + + /** + * 需要监听变动的属性。 + * 与实例方法`attributeChangedCallback`配合使用。 + * 此字符串序列定义了`attributeChangedCallback`回调时的第一个参数的可能值。 + */ + // static observedAttributes = []; + + /** + * 在属性更改、添加、移除或替换时调用。 + * 需要与静态属性`observedAttributes`配合使用。 + * 此回调的第一个参数在`observedAttributes`数组中定义。 + */ + // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} + + /** 每当元素添加到文档中时调用。 */ + connectedCallback() { } + + /** 每当元素从文档中移除时调用。 */ + disconnectedCallback() { } + + /** 每当元素被移动到新文档中时调用。 */ + // adoptedCallback() {} + + #player: Player; + + $input = Element.add('input', { appendTo: this }); + + #hint = Element.add('div', { class: 'sendbar-hint', appendTo: this, innerText: '弹幕礼仪 >' }); + + #submit = Element.add('button', { class: 'bpui-button', appendTo: this, innerText: '发送 >' }); + + constructor(player: Player) { + super(); + + this.#player = player; + this.classList.add('bofqi-sendbar-input'); + this.autocomplete = 'off'; + this.$input.placeholder = '您可以在这里输入弹幕吐槽哦~'; + this.$input.name = 'danmaku'; + this.$input.required = true; + this.$input.maxLength = 100; + } +} \ No newline at end of file diff --git a/src/player/area/wrap/index.css b/src/player/area/wrap/index.css new file mode 100644 index 0000000..8cb96b8 --- /dev/null +++ b/src/player/area/wrap/index.css @@ -0,0 +1,22 @@ +@import url(./video/index.css); +@import url(./state/index.css); +@import url(./panel/index.css); + +.bofqi-area-wrap { + flex: 1; + min-inline-size: 0; + background-color: var(--000); + position: relative; + contain: strict; + + @container bofqi style(--fullscreen: 1) { + & { + cursor: none; + } + } + + >video { + inline-size: 100%; + block-size: 100%; + } +} \ No newline at end of file diff --git a/src/player/area/wrap/index.ts b/src/player/area/wrap/index.ts index e916871..45f3c0a 100644 --- a/src/player/area/wrap/index.ts +++ b/src/player/area/wrap/index.ts @@ -1,10 +1,9 @@ +import { Player } from "../.."; import { customElement } from "../../../utils/Decorator/customElement"; import { Panel } from "./panel"; -import { Record } from "./record"; import { State } from "./state"; -import { Toast } from "./toast"; -/** 播放器容器区域 */ +/** 播放器视频区域 */ @customElement('div') export class Wrap extends HTMLDivElement { @@ -22,11 +21,8 @@ export class Wrap extends HTMLDivElement { */ // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} - /** 初始化标记 */ - // #inited = false; - /** 每当元素添加到文档中时调用。 */ - // connectedCallback() {} + // connectedCallback() { } /** 每当元素从文档中移除时调用。 */ // disconnectedCallback() {} @@ -34,21 +30,20 @@ export class Wrap extends HTMLDivElement { /** 每当元素被移动到新文档中时调用。 */ // adoptedCallback() {} - /** 播放登记信息 */ - $record = this.appendChild(new Record()); - - /** 播放状态 */ - $state = this.appendChild(new State()); + #player: Player; - /** 播放幕布 */ - $panel = this.appendChild(new Panel()); + #state: State; - /** 浮窗通知 */ - $toast = this.appendChild(new Toast()); + #panel: Panel; - constructor() { + constructor(player: Player) { super(); + this.#player = player; this.classList.add('bofqi-area-wrap'); + this.#state = new State(player); + this.#panel = new Panel(player); + + this.append(player.$video, player.$danmaku, this.#state, this.#panel); } } \ No newline at end of file diff --git a/src/player/area/wrap/panel.ts b/src/player/area/wrap/panel.ts deleted file mode 100644 index e29055c..0000000 --- a/src/player/area/wrap/panel.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { customElement } from "../../../utils/Decorator/customElement"; -import { Element } from "../../../utils/element"; -import { PLAYER_EVENT, ev } from "../../event-target"; -import { video } from "./video"; - -/** 播放幕布 */ -@customElement('div') -export class Panel extends HTMLDivElement { - - /** - * 需要监听变动的属性。 - * 与实例方法`attributeChangedCallback`配合使用。 - * 此字符串序列定义了`attributeChangedCallback`回调时的第一个参数的可能值。 - */ - // static observedAttributes = []; - - /** - * 在属性更改、添加、移除或替换时调用。 - * 需要与静态属性`observedAttributes`配合使用。 - * 此回调的第一个参数在`observedAttributes`数组中定义。 - */ - // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} - - /** 初始化标记 */ - // #inited = false; - - /** 每当元素添加到文档中时调用。 */ - // connectedCallback() { } - - /** 每当元素从文档中移除时调用。 */ - // disconnectedCallback() {} - - /** 每当元素被移动到新文档中时调用。 */ - // adoptedCallback() {} - - private $image = Element.add('div', { class: 'bofqi-panel-image' }, this); - - private $text = Element.add('div', { class: 'bofqi-panel-text' }, this); - - /** 播放器初始化 */ - private $player = Element.add('div', { class: 'bofqi-panel-row' }, this.$text, '播放器初始化...'); - - /** 加载视频地址 */ - private $url = Element.add('div', { class: 'bofqi-panel-row' }, this.$text, '加载视频地址...'); - - /** 加载视频内容 */ - private $media = Element.add('div', { class: 'bofqi-panel-row' }, this.$text, '加载视频内容...'); - - /** 加载用户配置 */ - private $user = Element.add('div', { class: 'bofqi-panel-row' }, this.$text, '加载用户配置...'); - - constructor() { - super(); - - this.classList.add('bofqi-panel', 'active'); - - ev.one(PLAYER_EVENT.INITED, () => { - this.$player.dataset.stage = '完成'; - }); - ev.bind(PLAYER_EVENT.IDENTIFY, this.identify); - - video.addEventListener('loadstart', () => { - this.$url.dataset.stage = '完成'; - }); - video.addEventListener('loadedmetadata', () => { - this.$media.dataset.stage = '完成'; - this.classList.remove('active'); - }); - video.addEventListener('emptied', this.identify); - } - - /** 加载用户配置 */ - userLoad() { - this.$user.dataset.stage = '完成'; - } - - private identify = () => { - this.classList.add('active'); - this.$url.removeAttribute('data-stage'); - this.$media.removeAttribute('data-stage'); - this.$user.removeAttribute('data-stage'); - } -} \ No newline at end of file diff --git a/src/player/area/wrap/panel/index.css b/src/player/area/wrap/panel/index.css new file mode 100644 index 0000000..4cbf36f --- /dev/null +++ b/src/player/area/wrap/panel/index.css @@ -0,0 +1,22 @@ +.bofqi-panel { + position: absolute; + inline-size: 100%; + block-size: 100%; + display: flex; + align-items: center; + justify-content: center; + background-color: var(--fff); + cursor: pointer; + z-index: 1; + + .bofqi-panel-image { + inline-size: 90px; + block-size: 90px; + background-image: url(../../../../assets/panel.gif); + background-repeat: no-repeat; + } + + &:not(.active) { + display: none; + } +} \ No newline at end of file diff --git a/src/player/auxiliary/info/more/index.ts b/src/player/area/wrap/panel/index.ts similarity index 59% rename from src/player/auxiliary/info/more/index.ts rename to src/player/area/wrap/panel/index.ts index d4d9975..3af0b85 100644 --- a/src/player/auxiliary/info/more/index.ts +++ b/src/player/area/wrap/panel/index.ts @@ -1,9 +1,10 @@ +import { Player } from "../../.."; import { customElement } from "../../../../utils/Decorator/customElement"; -import svg_setting_more from "../../../assets/svg/setting-more.svg"; +import { Element } from "../../../../utils/element"; -/** 更多按钮 */ -@customElement('button') -export class More extends HTMLButtonElement { +/** 播放幕布 */ +@customElement('div') +export class Panel extends HTMLDivElement { /** * 需要监听变动的属性。 @@ -23,7 +24,7 @@ export class More extends HTMLButtonElement { // #inited = false; /** 每当元素添加到文档中时调用。 */ - // connectedCallback() {} + // connectedCallback() { } /** 每当元素从文档中移除时调用。 */ // disconnectedCallback() {} @@ -31,10 +32,20 @@ export class More extends HTMLButtonElement { /** 每当元素被移动到新文档中时调用。 */ // adoptedCallback() {} - constructor() { + private $image = Element.add('div', { class: 'bofqi-panel-image', appendTo: this }); + + #player: Player; + + constructor(player: Player) { super(); - this.classList.add('bofqi-auxiliary-info-more'); - this.insertAdjacentHTML('afterbegin', svg_setting_more); + this.#player = player; + this.classList.add('bofqi-panel', 'active'); + player.$video.addEventListener('loadedmetadata', () => { + this.classList.remove('active'); + }); + player.$video.addEventListener('emptied', () => { + this.classList.add('active'); + }); } } \ No newline at end of file diff --git a/src/player/style/area/wrap/state.css b/src/player/area/wrap/state/index.css similarity index 55% rename from src/player/style/area/wrap/state.css rename to src/player/area/wrap/state/index.css index 3accaa6..45ef231 100644 --- a/src/player/style/area/wrap/state.css +++ b/src/player/area/wrap/state/index.css @@ -5,10 +5,8 @@ align-items: center; justify-content: center; pointer-events: none; - z-index: 1; .bofqi-state-play { - display: none; position: absolute; inline-size: 100px; block-size: 100px; @@ -20,36 +18,29 @@ inline-size: 100%; block-size: 100%; } + + &:not(.on) { + display: none; + } + + @container bofqi style(--fullscreen: 1) { + & { + inset-block-end: 42px; + } + } } .bofqi-state-buff { - display: none; inline-size: 48px; block-size: 48px; - padding: 6px; box-sizing: border-box; border-radius: 4px; - background: rgba(0, 0, 0, .8); - } -} - -@container bofqi style(--mode-fullscreen:1) { + background-image: url(../../../../assets/loading.gif); + background-repeat: no-repeat; + background-position: center; - .bofqi-state .bofqi-state-play { - inset-block-end: 42px; - } -} - -@container bofqi style(--state: 0) { - - .bofqi-state .bofqi-state-play { - display: block; - } -} - -@container bofqi style(--state: 2) { - - .bofqi-state .bofqi-state-buff { - display: block; + &:not(.on) { + display: none; + } } } \ No newline at end of file diff --git a/src/player/area/wrap/state/index.ts b/src/player/area/wrap/state/index.ts new file mode 100644 index 0000000..a268199 --- /dev/null +++ b/src/player/area/wrap/state/index.ts @@ -0,0 +1,81 @@ +import { Player } from "../../.."; +import svg_play_state from "../../../../assets/svg/play-state.svg"; +import { customElement } from "../../../../utils/Decorator/customElement"; +import { Element } from "../../../../utils/element"; + +/** 播放状态 */ +@customElement('div') +export class State extends HTMLDivElement { + + /** + * 需要监听变动的属性。 + * 与实例方法`attributeChangedCallback`配合使用。 + * 此字符串序列定义了`attributeChangedCallback`回调时的第一个参数的可能值。 + */ + // static observedAttributes = []; + + /** + * 在属性更改、添加、移除或替换时调用。 + * 需要与静态属性`observedAttributes`配合使用。 + * 此回调的第一个参数在`observedAttributes`数组中定义。 + */ + // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} + + /** 初始化标记 */ + // #inited = false; + + /** 每当元素添加到文档中时调用。 */ + // connectedCallback() { } + + /** 每当元素从文档中移除时调用。 */ + // disconnectedCallback() {} + + /** 每当元素被移动到新文档中时调用。 */ + // adoptedCallback() {} + + #player: Player; + + #preTime = 0; + + constructor(player: Player) { + super(); + + this.#player = player; + this.classList.add('bofqi-state'); + + player.$video.addEventListener('play', () => { + this.$play.classList.toggle('on', player.$video.paused); + this.$buff.classList.toggle('on', false); + }); + player.$video.addEventListener('playing', () => { + this.$play.classList.toggle('on', player.$video.paused); + this.$buff.classList.toggle('on', false); + }); + player.$video.addEventListener('pause', () => { + this.$play.classList.toggle('on', player.$video.paused); + this.$buff.classList.toggle('on', false); + }); + player.$video.addEventListener('ended', () => { + this.$play.classList.toggle('on', player.$video.paused); + this.$buff.classList.toggle('on', false); + }); + player.$video.addEventListener('emptied', () => { + this.$play.classList.toggle('on', player.$video.paused); + this.$buff.classList.toggle('on', false); + }); + player.$video.addEventListener('waiting', () => { + this.$buff.classList.toggle('on', true); + }); + player.$video.addEventListener('timeupdate', () => { + const time = Math.floor(player.$video.currentTime); + if (this.#preTime !== time) { + this.$buff.classList.toggle('on', false); + this.#preTime = time; + } + }); + } + + private $play = Element.add('div', { class: 'bofqi-state-play', appendTo: this, innerHTML: svg_play_state }); + + private $buff = Element.add('div', { class: 'bofqi-state-buff', appendTo: this }); +} \ No newline at end of file diff --git a/src/player/area/wrap/toast.ts b/src/player/area/wrap/toast.ts deleted file mode 100644 index fbe2e81..0000000 --- a/src/player/area/wrap/toast.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { customElement } from "../../../utils/Decorator/customElement"; -import { Element } from "../../../utils/element"; -import { PLAYER_EVENT, ev } from "../../event-target"; - -/** 浮窗通知 */ -@customElement('div') -export class Toast extends HTMLDivElement { - - /** - * 需要监听变动的属性。 - * 与实例方法`attributeChangedCallback`配合使用。 - * 此字符串序列定义了`attributeChangedCallback`回调时的第一个参数的可能值。 - */ - // static observedAttributes = []; - - /** - * 在属性更改、添加、移除或替换时调用。 - * 需要与静态属性`observedAttributes`配合使用。 - * 此回调的第一个参数在`observedAttributes`数组中定义。 - */ - // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} - - /** 初始化标记 */ - // #inited = false; - - /** 每当元素添加到文档中时调用。 */ - // connectedCallback() { } - - /** 每当元素从文档中移除时调用。 */ - // disconnectedCallback() {} - - /** 每当元素被移动到新文档中时调用。 */ - // adoptedCallback() {} - - constructor() { - super(); - - this.classList.add('bofqi-toast'); - - ev.bind(PLAYER_EVENT.TOAST, ({ detail }) => { - this.addItem(detail); - }); - ev.bind(PLAYER_EVENT.IDENTIFY, this.ientify); - } - - /** - * 添加通知 - * - * @param text 通知内容 - * @param timeout 持续时间,默认5,单位秒 - */ - addItem(text: string, timeout = 5) { - const div = Element.add('div', { class: 'bofqi-toast-item' }, this, text); - setTimeout(() => { - div.remove(); - }, timeout * 1e3); - } - - ientify = () => { - this.replaceChildren(); - } -} \ No newline at end of file diff --git a/src/player/style/area/wrap/video/context.css b/src/player/area/wrap/video/context.css similarity index 83% rename from src/player/style/area/wrap/video/context.css rename to src/player/area/wrap/video/context.css index 1bd8d1d..7cdbcde 100644 --- a/src/player/style/area/wrap/video/context.css +++ b/src/player/area/wrap/video/context.css @@ -12,7 +12,7 @@ transition: all .3s ease-in-out; font-size: 12px; white-space: nowrap; - color: #eee; + color: var(--eee); cursor: default; display: flex; justify-content: space-between; @@ -32,7 +32,7 @@ margin-inline-end: 10px; &.hint::after { - inset-area: block-satrt; + position-area: block-satrt; } } } @@ -54,12 +54,12 @@ padding: 4px; &.active { - color: #00a1d6; + color: var(--00a1d6); pointer-events: none; } &:hover { - background-color: hsla(0, 0%, 100%, .2); + background-color: var(--ffffff33); } } } @@ -81,7 +81,7 @@ } &.active { - color: #00a1d6; + color: var(--00a1d6); } } @@ -101,24 +101,16 @@ } &.active { - color: #00a1d6; + color: var(--00a1d6); } } &:not(:first-child) { - border-block-start: 1px solid hsla(0, 0%, 100%, .12); + border-block-start: 1px solid var(--ffffff1f); } &:hover { - background-color: hsla(0, 0%, 100%, .12); + background-color: var(--ffffff1f); } } -} - -body.deglim { - background-color: black; - - &>* { - background-color: black; - } } \ No newline at end of file diff --git a/src/player/style/area/wrap/video/index.css b/src/player/area/wrap/video/index.css similarity index 100% rename from src/player/style/area/wrap/video/index.css rename to src/player/area/wrap/video/index.css diff --git a/src/player/area/wrap/video/index.ts b/src/player/area/wrap/video/index.ts index ba1d14d..e52eb66 100644 --- a/src/player/area/wrap/video/index.ts +++ b/src/player/area/wrap/video/index.ts @@ -1,12 +1,15 @@ +import { Player } from "../../.."; import { customElement } from "../../../../utils/Decorator/customElement"; import { Element } from "../../../../utils/element"; import { Context } from "../../../widget/context"; import { Slider } from "../../../widget/slider"; import { Statistic } from "./statistic"; +import { Track } from "./track"; -/** 播放器节点 */ + +/** 视频元素 */ @customElement('video') -class Video extends HTMLVideoElement { +export class Video extends HTMLVideoElement { /** * 需要监听变动的属性。 @@ -40,9 +43,6 @@ class Video extends HTMLVideoElement { } } - /** 初始化标记 */ - // #inited = false; - /** 每当元素添加到文档中时调用。 */ // connectedCallback() {} @@ -52,52 +52,69 @@ class Video extends HTMLVideoElement { /** 每当元素被移动到新文档中时调用。 */ // adoptedCallback() {} + #player: Player; + /** 右键菜单 */ private $context = new Context(this); /** 播放速度 */ - private $playbackRate = Element.add('li', { class: 'context-playback-rate' }, this.$context); + private $playbackRate = Element.add('li', { class: 'context-playback-rate', appendTo: this.$context }); /** 播放速率 */ private $inputRate = this.$playbackRate.appendChild(new Slider()); /** 画面比例 */ - private $ratio = Element.add('li', { class: 'context-playback-ratio' }, this.$context); + private $ratio = Element.add('li', { class: 'context-playback-ratio', appendTo: this.$context }); - private $ratioToggle = Element.add('div', undefined, this.$ratio); + private $ratioToggle = Element.add('div', { appendTo: this.$ratio }); /** 画面比例:默认 */ - private $ratio1 = Element.add('button', { class: 'bpui-button' }, this.$ratioToggle); + private $ratio1 = Element.add('button', { class: 'bpui-button', appendTo: this.$ratioToggle }); /** 画面比例:4:3 */ - private $ratio43 = Element.add('button', { class: 'bpui-button' }, this.$ratioToggle); + private $ratio43 = Element.add('button', { class: 'bpui-button', appendTo: this.$ratioToggle }); /** 画面比例:16:9 */ - private $ratio169 = Element.add('button', { class: 'bpui-button' }, this.$ratioToggle); + private $ratio169 = Element.add('button', { class: 'bpui-button', appendTo: this.$ratioToggle }); /** 关灯 */ - private $deglim = Element.add('li', { class: 'context-playback-deglim' }, this.$context); + private $deglim = Element.add('li', { class: 'context-playback-deglim', appendTo: this.$context }); /** 镜像 */ - private $mirror = Element.add('li', { class: 'context-playback-mirror' }, this.$context); + private $mirror = Element.add('li', { class: 'context-playback-mirror', appendTo: this.$context }); /** 更新历史 8c739d8a */ - private $history = Element.add('li', { class: 'context-playback-history' }, this.$context); + private $history = Element.add('li', { class: 'context-playback-history', appendTo: this.$context }); /** 视频统计信息 */ - private $statistic = Element.add('li', { class: 'context-playback-statistic' }, this.$context); + private $statistic = Element.add('li', { class: 'context-playback-statistic', appendTo: this.$context }); /** 视频统计信息面板 */ private $statistics = new Statistic(); - constructor() { + /** 字幕轨道 */ + $track = new Track(this); + + /** 播放/暂停 */ + $toggle = () => { + this.paused ? this.play() : this.pause(); + } + + /** 跳帧播放:/秒 */ + $seek = (v: number) => { + this.currentTime = v; + this.play(); + } + + constructor(player: Player) { super(); + this.#player = player; + this.$context.classList.add('bofqi-video-context-menu', 'black'); - this.$inputRate.min = '0.25'; - this.$inputRate.step = '0.25'; - this.$inputRate.max = '5'; - this.$inputRate.defaultValue = '1'; + this.$inputRate.$step = '0.25'; + this.$inputRate.$max = 5; + this.$inputRate.$defaultValue = 1; this.$inputRate.$hint = true; this.$inputRate.classList.add('label'); this.$ratio1.textContent = '默认'; @@ -128,54 +145,36 @@ class Video extends HTMLVideoElement { this.$ratio169.classList.remove('active'); }); this.$ratio43.addEventListener('click', () => { - this.style.setProperty('--aspect-ratio', '4 / 3'); + // TODO: 画面分辨率切换 + // this.style.setProperty('--aspect-ratio', '4 / 3'); this.$ratio1.classList.remove('active'); this.$ratio43.classList.add('active'); this.$ratio169.classList.remove('active'); }); this.$ratio169.addEventListener('click', () => { - this.style.setProperty('--aspect-ratio', '16 / 9'); + // this.style.setProperty('--aspect-ratio', '16 / 9'); this.$ratio1.classList.remove('active'); this.$ratio43.classList.remove('active'); this.$ratio169.classList.add('active'); }); this.$deglim.addEventListener('click', () => { - this.$deglim.dataset.value = document.body.classList.toggle('deglim') ? '开灯' : '关灯'; + // this.$deglim.dataset.value = document.body.classList.toggle('deglim') ? '开灯' : '关灯'; + // TODO: 关灯 }); this.$mirror.addEventListener('click', () => { this.$mirror.classList.toggle('active', this.classList.toggle('mirror')); }); - this.$history.addEventListener('click', () => { - self.open('/blackboard/webplayer_history.html#html5'); - }); + // this.$history.addEventListener('click', () => { + // self.open('https://www.bilibili.com/blackboard/webplayer_history.html#html5'); + // }); this.$statistic.addEventListener('click', () => { this.parentElement?.contains(this.$statistics) || this.parentElement?.appendChild(this.$statistics) this.$statistic.classList.toggle('active', this.$statistics.toggle()); }); this.$context.addEventListener('open', () => { - this.$deglim.dataset.value = document.body.classList.contains('deglim') ? '开灯' : '关灯'; + // this.$deglim.dataset.value = document.body.classList.contains('deglim') ? '开灯' : '关灯'; this.$mirror.classList.toggle('active', this.classList.contains('mirror')); this.$statistic.classList.toggle('active', this.$statistics.classList.contains('active')); }); } - - /** - * 跳帧播放 - * - * @param cur 目标时间:/s - */ - seek(t: number) { - this.currentTime = t; - } - - toggle() { - this.paused ? this.play() : this.pause(); - } -} - -/** - * 视频节点 - * 作为播放器的核心组件,不是以属性而是以模块的形式提供, - * 以便能够从任何地方访问。 - */ -export const video = new Video(); \ No newline at end of file +} \ No newline at end of file diff --git a/src/player/style/area/wrap/video/statistic.css b/src/player/area/wrap/video/statistic.css similarity index 93% rename from src/player/style/area/wrap/video/statistic.css rename to src/player/area/wrap/video/statistic.css index 76b984a..e36fbf7 100644 --- a/src/player/style/area/wrap/video/statistic.css +++ b/src/player/area/wrap/video/statistic.css @@ -1,8 +1,8 @@ .bofqi-statistic { display: none; - background: rgba(28, 28, 28, .8); + background: var(--1c1c1ccc); border-radius: 4px; - color: #fff; + color: var(--fff); font-family: Noto Sans CJK SC DemiLight, Roboto, Segoe UI, Tahoma, Arial, Helvetica, sans-serif; position: absolute; inset-inline-start: 10px; @@ -19,7 +19,7 @@ .bofqi-statistic-close::after { content: '[x]'; - color: #ddd; + color: var(--ddd); font-size: 13px; position: absolute; inset-inline-end: 10px; diff --git a/src/player/area/wrap/video/statistic.ts b/src/player/area/wrap/video/statistic.ts index 83432d4..064c97e 100644 --- a/src/player/area/wrap/video/statistic.ts +++ b/src/player/area/wrap/video/statistic.ts @@ -1,9 +1,9 @@ -import { IVideoInfoEvent } from "../../../../dash-player"; -import flvjs from "../../../../flv.js"; +import FlvJs from "flv.js"; import { customElement } from "../../../../utils/Decorator/customElement"; import { Element } from "../../../../utils/element"; import { Format } from "../../../../utils/fomat"; -import { PLAYER_EVENT, ev } from "../../../event-target"; +import { ev, PLAYER_EVENT } from "../../../event"; +import { IVideoInfoEvent } from "../../../../dash-player"; /** 播放器节点区域 */ @customElement('ul') @@ -36,7 +36,7 @@ export class Statistic extends HTMLUListElement { // adoptedCallback() {} /** 关闭按钮 */ - private $close = Element.add('i', { class: 'bofqi-statistic-close' }, this); + private $close = Element.add('i', { class: 'bofqi-statistic-close', appendTo: this }); /** 是否可见 */ private isIntersecting = false; @@ -107,7 +107,7 @@ export class Statistic extends HTMLUListElement { ev.bind(PLAYER_EVENT.VIDEO_INFO_DASH, this.dashjs); ev.bind(PLAYER_EVENT.VIDEO_INFO_FLV, this.flvjs); ev.bind(PLAYER_EVENT.VIDEO_INFO_NATIVE, this.native); - ev.bind(PLAYER_EVENT.IDENTIFY, this.identify); + ev.bind(PLAYER_EVENT.VIDEO_DESTORY, this.identify); } @@ -137,7 +137,7 @@ export class Statistic extends HTMLUListElement { } /** 刷新FLV数据 */ - private flvjs = (ev: CustomEvent<[flvjs.FlvPlayerMediaInfo, flvjs.FlvPlayerStatisticsInfo]>) => { + private flvjs = (ev: CustomEvent<[FlvJs.FlvPlayerMediaInfo, FlvJs.FlvPlayerStatisticsInfo]>) => { const [mediaInfo, statisticsInfo] = ev.detail; this.$mimeType.update(mediaInfo.mimeType); this.$playerType.update(statisticsInfo.playerType); @@ -156,7 +156,7 @@ export class Statistic extends HTMLUListElement { } /** 刷新NATIVE数据 */ - private native = (ev: CustomEvent<[flvjs.NativePlayerMediaInfo, flvjs.NativePlayerStatisticsInfo]>) => { + private native = (ev: CustomEvent<[FlvJs.NativePlayerMediaInfo, FlvJs.NativePlayerStatisticsInfo]>) => { const [mediaInfo, statisticsInfo] = ev.detail; this.$mimeType.update(mediaInfo.mimeType); this.$playerType.update(statisticsInfo.playerType); diff --git a/src/player/style/area/wrap/video/track.css b/src/player/area/wrap/video/track.css similarity index 86% rename from src/player/style/area/wrap/video/track.css rename to src/player/area/wrap/video/track.css index a060e88..9870563 100644 --- a/src/player/style/area/wrap/video/track.css +++ b/src/player/area/wrap/video/track.css @@ -1,7 +1,7 @@ .bofqi-area-wrap>video { - --cue-color: #fff; + --cue-color: var(--fff); --cue-shadow: none; - --cue-background: rgba(0, 0, 0, 0.8); + --cue-background: var(--000000cc); --cue-white-space: normal; --cue-line-height: normal; --cue-font-family: inherit; @@ -23,7 +23,7 @@ } } -@scope (:fullscreen) { +@container bofqi style(--fullscreen: 1) { .bofqi-area-wrap>video::-webkit-media-text-track-container { translate: 0 -70px; } diff --git a/src/player/area/wrap/video/track.ts b/src/player/area/wrap/video/track.ts index f2279c7..d35b4f7 100644 --- a/src/player/area/wrap/video/track.ts +++ b/src/player/area/wrap/video/track.ts @@ -1,11 +1,11 @@ -import { video } from "."; +import { Video } from "."; import { customElement } from "../../../../utils/Decorator/customElement"; -import { PLAYER_EVENT, ev } from "../../../event-target"; +import { ev, PLAYER_EVENT } from "../../../event"; import { options } from "../../../option"; /** 播放器字幕节点 */ @customElement('track') -class Track extends HTMLTrackElement { +export class Track extends HTMLTrackElement { /** * 需要监听变动的属性。 @@ -27,7 +27,7 @@ class Track extends HTMLTrackElement { this.track.mode = 'disabled'; this.remove(); } else { - video.contains(this) || video.appendChild(this); + this.#video.contains(this) || this.#video.appendChild(this); this.track.mode = 'showing'; } break; @@ -50,15 +50,18 @@ class Track extends HTMLTrackElement { /** 每当元素被移动到新文档中时调用。 */ // adoptedCallback() {} + #video: Video + /** 是否初始化 */ $hasTrack = false; /** 等比缩放 */ private $scale = false; - constructor() { + constructor(video: Video) { super(); + this.#video = video; new ResizeObserver((e) => { for (const entry of e) { for (const size of entry.borderBoxSize) { @@ -75,37 +78,37 @@ class Track extends HTMLTrackElement { private $styleFlush(ops = options) { const { fontSize, scale, color, shadow, opacity, fontFamily } = ops.subtile; - video.style.setProperty('--cue-font-size', `${28 * fontSize}px`); - video.style.setProperty('--cue-scale', ((this.$scale = scale) ? (video.getBoundingClientRect().width || 1) / 1139 : 1)); - video.style.setProperty('--cue-color', color); + this.#video.style.setProperty('--cue-font-size', `${28 * fontSize}px`); + this.#video.style.setProperty('--cue-scale', ((this.$scale = scale) ? (this.#video.getBoundingClientRect().width || 1) / 1139 : 1)); + this.#video.style.setProperty('--cue-color', color); switch (shadow) { case '重墨': { - video.style.setProperty('--cue-shadow', '#000 1px 0px 1px, #000 0px 1px 1px, #000 0px -1px 1px, #000 -1px 0px 1px'); + this.#video.style.setProperty('--cue-shadow', '#000 1px 0px 1px, #000 0px 1px 1px, #000 0px -1px 1px, #000 -1px 0px 1px'); break; } case '描边': { - video.style.setProperty('--cue-shadow', '#000 0px 0px 1px, #000 0px 0px 1px ,#000 0px 0px 1px'); + this.#video.style.setProperty('--cue-shadow', '#000 0px 0px 1px, #000 0px 0px 1px ,#000 0px 0px 1px'); break; } case '45°投影': { - video.style.setProperty('--cue-shadow', '#000 1px 1px 2px, #000 0px 0px 1px'); + this.#video.style.setProperty('--cue-shadow', '#000 1px 1px 2px, #000 0px 0px 1px'); break; } default: { - video.style.removeProperty('--cue-shadow'); + this.#video.style.removeProperty('--cue-shadow'); break; } } - video.style.setProperty('--cue-background', `rgba(0, 0, 0, ${opacity / 100})`); + this.#video.style.setProperty('--cue-background', `rgba(0, 0, 0, ${opacity / 100})`); switch (fontFamily) { case '': case '默认': case 'inherit': { - video.style.removeProperty('--cue-font-family'); + this.#video.style.removeProperty('--cue-font-family'); break; } default: { - video.style.setProperty('--cue-font-family', fontFamily); + this.#video.style.setProperty('--cue-font-family', fontFamily); break; } } @@ -148,6 +151,3 @@ class Track extends HTMLTrackElement { this.$hasTrack = false; } } - -/** 播放器字幕节点 */ -export const track = new Track(); \ No newline at end of file diff --git a/src/player/assets/image/checked.png b/src/player/assets/image/checked.png deleted file mode 100644 index 0a61cc2..0000000 Binary files a/src/player/assets/image/checked.png and /dev/null differ diff --git a/src/player/assets/image/live-eye.png b/src/player/assets/image/live-eye.png deleted file mode 100644 index 18fd224..0000000 Binary files a/src/player/assets/image/live-eye.png and /dev/null differ diff --git a/src/player/assets/image/online.png b/src/player/assets/image/online.png deleted file mode 100644 index 4bfb645..0000000 Binary files a/src/player/assets/image/online.png and /dev/null differ diff --git a/src/player/assets/image/watchlater.png b/src/player/assets/image/watchlater.png deleted file mode 100644 index b694e6b..0000000 Binary files a/src/player/assets/image/watchlater.png and /dev/null differ diff --git a/src/player/assets/svg/account.d.svg.ts b/src/player/assets/svg/account.d.svg.ts deleted file mode 100644 index f6af5ab..0000000 --- a/src/player/assets/svg/account.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_account: string; -export default svg_account; \ No newline at end of file diff --git a/src/player/assets/svg/account.svg b/src/player/assets/svg/account.svg deleted file mode 100644 index 51c177b..0000000 --- a/src/player/assets/svg/account.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/player/assets/svg/activity.d.svg.ts b/src/player/assets/svg/activity.d.svg.ts deleted file mode 100644 index 04e2513..0000000 --- a/src/player/assets/svg/activity.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_activity: string; -export default svg_activity; \ No newline at end of file diff --git a/src/player/assets/svg/back.d.svg.ts b/src/player/assets/svg/back.d.svg.ts deleted file mode 100644 index 2041045..0000000 --- a/src/player/assets/svg/back.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_back: string; -export default svg_back; \ No newline at end of file diff --git a/src/player/assets/svg/bilibili.d.svg.ts b/src/player/assets/svg/bilibili.d.svg.ts deleted file mode 100644 index 1ce2cc3..0000000 --- a/src/player/assets/svg/bilibili.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_bilibili: string; -export default svg_bilibili; \ No newline at end of file diff --git a/src/player/assets/svg/blingbling-left.d.svg.ts b/src/player/assets/svg/blingbling-left.d.svg.ts deleted file mode 100644 index a1ae025..0000000 --- a/src/player/assets/svg/blingbling-left.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_blingbling_left: string; -export default svg_blingbling_left; \ No newline at end of file diff --git a/src/player/assets/svg/blingbling-right.d.svg.ts b/src/player/assets/svg/blingbling-right.d.svg.ts deleted file mode 100644 index 31e02f7..0000000 --- a/src/player/assets/svg/blingbling-right.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_blingbling_right: string; -export default svg_blingbling_right; \ No newline at end of file diff --git a/src/player/assets/svg/block-setting.d.svg.ts b/src/player/assets/svg/block-setting.d.svg.ts deleted file mode 100644 index 2941cca..0000000 --- a/src/player/assets/svg/block-setting.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_block_setting: string; -export default svg_block_setting; \ No newline at end of file diff --git a/src/player/assets/svg/bml.d.svg.ts b/src/player/assets/svg/bml.d.svg.ts deleted file mode 100644 index b484cd8..0000000 --- a/src/player/assets/svg/bml.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_bml: string; -export default svg_bml; \ No newline at end of file diff --git a/src/player/assets/svg/bml.svg b/src/player/assets/svg/bml.svg deleted file mode 100644 index c9c8582..0000000 --- a/src/player/assets/svg/bml.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/player/assets/svg/broadcast.d.svg.ts b/src/player/assets/svg/broadcast.d.svg.ts deleted file mode 100644 index 1710e99..0000000 --- a/src/player/assets/svg/broadcast.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_broadcast: string; -export default svg_broadcast; \ No newline at end of file diff --git a/src/player/assets/svg/cancel.d.svg.ts b/src/player/assets/svg/cancel.d.svg.ts deleted file mode 100644 index 07fa768..0000000 --- a/src/player/assets/svg/cancel.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_cancel: string; -export default svg_cancel; \ No newline at end of file diff --git a/src/player/assets/svg/chat-on.d.svg.ts b/src/player/assets/svg/chat-on.d.svg.ts deleted file mode 100644 index 5913aeb..0000000 --- a/src/player/assets/svg/chat-on.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_chat_on: string; -export default svg_chat_on; \ No newline at end of file diff --git a/src/player/assets/svg/checkbox-selected.d.svg.ts b/src/player/assets/svg/checkbox-selected.d.svg.ts deleted file mode 100644 index 46b84b3..0000000 --- a/src/player/assets/svg/checkbox-selected.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_checkbox_selected: string; -export default svg_checkbox_selected; \ No newline at end of file diff --git a/src/player/assets/svg/checkbox.d.svg.ts b/src/player/assets/svg/checkbox.d.svg.ts deleted file mode 100644 index a87e9a3..0000000 --- a/src/player/assets/svg/checkbox.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_checkbox: string; -export default svg_checkbox; \ No newline at end of file diff --git a/src/player/assets/svg/cheese.d.svg.ts b/src/player/assets/svg/cheese.d.svg.ts deleted file mode 100644 index 112bb70..0000000 --- a/src/player/assets/svg/cheese.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_cheese: string; -export default svg_cheese; \ No newline at end of file diff --git a/src/player/assets/svg/cheese.svg b/src/player/assets/svg/cheese.svg deleted file mode 100644 index ae56d88..0000000 --- a/src/player/assets/svg/cheese.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/player/assets/svg/circle.d.svg.ts b/src/player/assets/svg/circle.d.svg.ts deleted file mode 100644 index 3f549e6..0000000 --- a/src/player/assets/svg/circle.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_circle: string; -export default svg_circle; \ No newline at end of file diff --git a/src/player/assets/svg/closed-caption-on.d.svg.ts b/src/player/assets/svg/closed-caption-on.d.svg.ts deleted file mode 100644 index 54ddf19..0000000 --- a/src/player/assets/svg/closed-caption-on.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_closed_caption_on: string; -export default svg_closed_caption_on; \ No newline at end of file diff --git a/src/player/assets/svg/closed-caption.d.svg.ts b/src/player/assets/svg/closed-caption.d.svg.ts deleted file mode 100644 index ad9cb82..0000000 --- a/src/player/assets/svg/closed-caption.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_closed_caption: string; -export default svg_closed_caption; \ No newline at end of file diff --git a/src/player/assets/svg/coin.d.svg.ts b/src/player/assets/svg/coin.d.svg.ts deleted file mode 100644 index d4d69f3..0000000 --- a/src/player/assets/svg/coin.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_coin: string; -export default svg_coin; \ No newline at end of file diff --git a/src/player/assets/svg/color.d.svg.ts b/src/player/assets/svg/color.d.svg.ts deleted file mode 100644 index 6350ee0..0000000 --- a/src/player/assets/svg/color.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_color: string; -export default svg_color; \ No newline at end of file diff --git a/src/player/assets/svg/danmaku-color.d.svg.ts b/src/player/assets/svg/danmaku-color.d.svg.ts deleted file mode 100644 index e446e6c..0000000 --- a/src/player/assets/svg/danmaku-color.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_danmaku_color: string; -export default svg_danmaku_color; \ No newline at end of file diff --git a/src/player/assets/svg/danmaku-current.d.svg.ts b/src/player/assets/svg/danmaku-current.d.svg.ts deleted file mode 100644 index fa21cb2..0000000 --- a/src/player/assets/svg/danmaku-current.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_danmaku_current: string; -export default svg_danmaku_current; \ No newline at end of file diff --git a/src/player/assets/svg/danmaku-forbid.d.svg.ts b/src/player/assets/svg/danmaku-forbid.d.svg.ts deleted file mode 100644 index dbceab5..0000000 --- a/src/player/assets/svg/danmaku-forbid.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_danmaku_forbid: string; -export default svg_danmaku_forbid; \ No newline at end of file diff --git a/src/player/assets/svg/danmaku-history.d.svg.ts b/src/player/assets/svg/danmaku-history.d.svg.ts deleted file mode 100644 index 3af8bcc..0000000 --- a/src/player/assets/svg/danmaku-history.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_danmaku_history: string; -export default svg_danmaku_history; \ No newline at end of file diff --git a/src/player/assets/svg/danmaku-list.d.svg.ts b/src/player/assets/svg/danmaku-list.d.svg.ts deleted file mode 100644 index 3f4089a..0000000 --- a/src/player/assets/svg/danmaku-list.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_danmaku_list: string; -export default svg_danmaku_list; \ No newline at end of file diff --git a/src/player/assets/svg/danmaku-mode-1.d.svg.ts b/src/player/assets/svg/danmaku-mode-1.d.svg.ts deleted file mode 100644 index 597dfac..0000000 --- a/src/player/assets/svg/danmaku-mode-1.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_danmaku_mode_1: string; -export default svg_danmaku_mode_1; \ No newline at end of file diff --git a/src/player/assets/svg/danmaku-mode-4.d.svg.ts b/src/player/assets/svg/danmaku-mode-4.d.svg.ts deleted file mode 100644 index f26b9d6..0000000 --- a/src/player/assets/svg/danmaku-mode-4.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_danmaku_mode_4: string; -export default svg_danmaku_mode_4; \ No newline at end of file diff --git a/src/player/assets/svg/danmaku-mode-5.d.svg.ts b/src/player/assets/svg/danmaku-mode-5.d.svg.ts deleted file mode 100644 index 58385c0..0000000 --- a/src/player/assets/svg/danmaku-mode-5.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_danmaku_mode_5: string; -export default svg_danmaku_mode_5; \ No newline at end of file diff --git a/src/player/assets/svg/danmaku-mode-6.d.svg.ts b/src/player/assets/svg/danmaku-mode-6.d.svg.ts deleted file mode 100644 index 592dd06..0000000 --- a/src/player/assets/svg/danmaku-mode-6.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_danmaku_mode_6: string; -export default svg_danmaku_mode_6; \ No newline at end of file diff --git a/src/player/assets/svg/danmaku-mode-7.d.svg.ts b/src/player/assets/svg/danmaku-mode-7.d.svg.ts deleted file mode 100644 index 9fe5e44..0000000 --- a/src/player/assets/svg/danmaku-mode-7.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_danmaku_mode_7: string; -export default svg_danmaku_mode_7; \ No newline at end of file diff --git a/src/player/assets/svg/danmaku-mode-8.d.svg.ts b/src/player/assets/svg/danmaku-mode-8.d.svg.ts deleted file mode 100644 index 9e497fc..0000000 --- a/src/player/assets/svg/danmaku-mode-8.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_danmaku_mode_8: string; -export default svg_danmaku_mode_8; \ No newline at end of file diff --git a/src/player/assets/svg/danmaku-normal.d.svg.ts b/src/player/assets/svg/danmaku-normal.d.svg.ts deleted file mode 100644 index 8a5b81b..0000000 --- a/src/player/assets/svg/danmaku-normal.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_danmaku_normal: string; -export default svg_danmaku_normal; \ No newline at end of file diff --git a/src/player/assets/svg/danmaku-off.d.svg.ts b/src/player/assets/svg/danmaku-off.d.svg.ts deleted file mode 100644 index 36ede45..0000000 --- a/src/player/assets/svg/danmaku-off.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_danmaku_off: string; -export default svg_danmaku_off; \ No newline at end of file diff --git a/src/player/assets/svg/danmaku-setting.d.svg.ts b/src/player/assets/svg/danmaku-setting.d.svg.ts deleted file mode 100644 index 78fa69e..0000000 --- a/src/player/assets/svg/danmaku-setting.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_danmaku_setting: string; -export default svg_danmaku_setting; \ No newline at end of file diff --git a/src/player/assets/svg/danmaku-special.d.svg.ts b/src/player/assets/svg/danmaku-special.d.svg.ts deleted file mode 100644 index 478faa7..0000000 --- a/src/player/assets/svg/danmaku-special.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_danmaku_special: string; -export default svg_danmaku_special; \ No newline at end of file diff --git a/src/player/assets/svg/danmaku-text.d.svg.ts b/src/player/assets/svg/danmaku-text.d.svg.ts deleted file mode 100644 index e62356c..0000000 --- a/src/player/assets/svg/danmaku-text.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_danmaku_text: string; -export default svg_danmaku_text; \ No newline at end of file diff --git a/src/player/assets/svg/danmaku-unregister.d.svg.ts b/src/player/assets/svg/danmaku-unregister.d.svg.ts deleted file mode 100644 index 8e58580..0000000 --- a/src/player/assets/svg/danmaku-unregister.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_danmaku_unregister: string; -export default svg_danmaku_unregister; \ No newline at end of file diff --git a/src/player/assets/svg/danmaku.d.svg.ts b/src/player/assets/svg/danmaku.d.svg.ts deleted file mode 100644 index 6a1f4c6..0000000 --- a/src/player/assets/svg/danmaku.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_danmaku: string; -export default svg_danmaku; \ No newline at end of file diff --git a/src/player/assets/svg/delete.d.svg.ts b/src/player/assets/svg/delete.d.svg.ts deleted file mode 100644 index 5a1a7ad..0000000 --- a/src/player/assets/svg/delete.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_delete: string; -export default svg_delete; \ No newline at end of file diff --git a/src/player/assets/svg/donghua.d.svg.ts b/src/player/assets/svg/donghua.d.svg.ts deleted file mode 100644 index 6459889..0000000 --- a/src/player/assets/svg/donghua.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_donghua: string; -export default svg_donghua; \ No newline at end of file diff --git a/src/player/assets/svg/down.d.svg.ts b/src/player/assets/svg/down.d.svg.ts deleted file mode 100644 index 1c93da0..0000000 --- a/src/player/assets/svg/down.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_down: string; -export default svg_down; \ No newline at end of file diff --git a/src/player/assets/svg/emoji.d.svg.ts b/src/player/assets/svg/emoji.d.svg.ts deleted file mode 100644 index c92efe4..0000000 --- a/src/player/assets/svg/emoji.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_emoji: string; -export default svg_emoji; \ No newline at end of file diff --git a/src/player/assets/svg/emoji.svg b/src/player/assets/svg/emoji.svg deleted file mode 100644 index 1c224f4..0000000 --- a/src/player/assets/svg/emoji.svg +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/src/player/assets/svg/heart.d.svg.ts b/src/player/assets/svg/heart.d.svg.ts deleted file mode 100644 index 322e490..0000000 --- a/src/player/assets/svg/heart.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_heart: string; -export default svg_heart; \ No newline at end of file diff --git a/src/player/assets/svg/icon-backstage.d.svg.ts b/src/player/assets/svg/icon-backstage.d.svg.ts deleted file mode 100644 index 46e3326..0000000 --- a/src/player/assets/svg/icon-backstage.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_icon_backstage: string; -export default svg_icon_backstage; \ No newline at end of file diff --git a/src/player/assets/svg/icon-collection-full.d.svg.ts b/src/player/assets/svg/icon-collection-full.d.svg.ts deleted file mode 100644 index a4f4313..0000000 --- a/src/player/assets/svg/icon-collection-full.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_icon_collection_full: string; -export default svg_icon_collection_full; \ No newline at end of file diff --git a/src/player/assets/svg/icon-collection.d.svg.ts b/src/player/assets/svg/icon-collection.d.svg.ts deleted file mode 100644 index ba08ce2..0000000 --- a/src/player/assets/svg/icon-collection.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_icon_collection: string; -export default svg_icon_collection; \ No newline at end of file diff --git a/src/player/assets/svg/icon-comment.d.svg.ts b/src/player/assets/svg/icon-comment.d.svg.ts deleted file mode 100644 index 11a5eea..0000000 --- a/src/player/assets/svg/icon-comment.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_icon_comment: string; -export default svg_icon_comment; \ No newline at end of file diff --git a/src/player/assets/svg/icon-danmaku.d.svg.ts b/src/player/assets/svg/icon-danmaku.d.svg.ts deleted file mode 100644 index de0339c..0000000 --- a/src/player/assets/svg/icon-danmaku.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_icon_danmaku: string; -export default svg_icon_danmaku; \ No newline at end of file diff --git a/src/player/assets/svg/icon-delete.d.svg.ts b/src/player/assets/svg/icon-delete.d.svg.ts deleted file mode 100644 index 0c3732f..0000000 --- a/src/player/assets/svg/icon-delete.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_icon_delete: string; -export default svg_icon_delete; \ No newline at end of file diff --git a/src/player/assets/svg/icon-download.d.svg.ts b/src/player/assets/svg/icon-download.d.svg.ts deleted file mode 100644 index 0f9d46c..0000000 --- a/src/player/assets/svg/icon-download.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_icon_download: string; -export default svg_icon_download; \ No newline at end of file diff --git a/src/player/assets/svg/icon-download.svg b/src/player/assets/svg/icon-download.svg deleted file mode 100644 index 932c84c..0000000 --- a/src/player/assets/svg/icon-download.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/player/assets/svg/icon-follow.d.svg.ts b/src/player/assets/svg/icon-follow.d.svg.ts deleted file mode 100644 index 14fe9f9..0000000 --- a/src/player/assets/svg/icon-follow.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_icon_follow: string; -export default svg_icon_follow; \ No newline at end of file diff --git a/src/player/assets/svg/icon-ignore.d.svg.ts b/src/player/assets/svg/icon-ignore.d.svg.ts deleted file mode 100644 index 93251df..0000000 --- a/src/player/assets/svg/icon-ignore.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_icon_ignore: string; -export default svg_icon_ignore; \ No newline at end of file diff --git a/src/player/assets/svg/icon-move.d.svg.ts b/src/player/assets/svg/icon-move.d.svg.ts deleted file mode 100644 index 43453c6..0000000 --- a/src/player/assets/svg/icon-move.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_icon_move: string; -export default svg_icon_move; \ No newline at end of file diff --git a/src/player/assets/svg/icon-paihang.d.svg.ts b/src/player/assets/svg/icon-paihang.d.svg.ts deleted file mode 100644 index 43dbbe0..0000000 --- a/src/player/assets/svg/icon-paihang.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_icon_paihang: string; -export default svg_icon_paihang; \ No newline at end of file diff --git a/src/player/assets/svg/icon-paihang.svg b/src/player/assets/svg/icon-paihang.svg deleted file mode 100644 index eaa6942..0000000 --- a/src/player/assets/svg/icon-paihang.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/player/assets/svg/icon-played.d.svg.ts b/src/player/assets/svg/icon-played.d.svg.ts deleted file mode 100644 index 481d812..0000000 --- a/src/player/assets/svg/icon-played.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_icon_played: string; -export default svg_icon_played; \ No newline at end of file diff --git a/src/player/assets/svg/icon-protect.d.svg.ts b/src/player/assets/svg/icon-protect.d.svg.ts deleted file mode 100644 index 8c1a2bf..0000000 --- a/src/player/assets/svg/icon-protect.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_icon_protect: string; -export default svg_icon_protect; \ No newline at end of file diff --git a/src/player/assets/svg/icon-wechat.d.svg.ts b/src/player/assets/svg/icon-wechat.d.svg.ts deleted file mode 100644 index ca10575..0000000 --- a/src/player/assets/svg/icon-wechat.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_icon_wechat: string; -export default svg_icon_wechat; \ No newline at end of file diff --git a/src/player/assets/svg/icon-wechat.svg b/src/player/assets/svg/icon-wechat.svg deleted file mode 100644 index 5df49ab..0000000 --- a/src/player/assets/svg/icon-wechat.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/player/assets/svg/icon-weibo.d.svg.ts b/src/player/assets/svg/icon-weibo.d.svg.ts deleted file mode 100644 index c0739a3..0000000 --- a/src/player/assets/svg/icon-weibo.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_icon_weibo: string; -export default svg_icon_weibo; \ No newline at end of file diff --git a/src/player/assets/svg/icon-weibo.svg b/src/player/assets/svg/icon-weibo.svg deleted file mode 100644 index 1aed429..0000000 --- a/src/player/assets/svg/icon-weibo.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/player/assets/svg/left.d.svg.ts b/src/player/assets/svg/left.d.svg.ts deleted file mode 100644 index 3354e29..0000000 --- a/src/player/assets/svg/left.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_left: string; -export default svg_left; \ No newline at end of file diff --git a/src/player/assets/svg/like-number.d.svg.ts b/src/player/assets/svg/like-number.d.svg.ts deleted file mode 100644 index 46181aa..0000000 --- a/src/player/assets/svg/like-number.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_like_number: string; -export default svg_like_number; \ No newline at end of file diff --git a/src/player/assets/svg/like-number.svg b/src/player/assets/svg/like-number.svg deleted file mode 100644 index 714f78b..0000000 --- a/src/player/assets/svg/like-number.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/player/assets/svg/live-center.d.svg.ts b/src/player/assets/svg/live-center.d.svg.ts deleted file mode 100644 index 2b89ea8..0000000 --- a/src/player/assets/svg/live-center.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_live_center: string; -export default svg_live_center; \ No newline at end of file diff --git a/src/player/assets/svg/live-center.svg b/src/player/assets/svg/live-center.svg deleted file mode 100644 index df7656f..0000000 --- a/src/player/assets/svg/live-center.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/player/assets/svg/love-solid.d.svg.ts b/src/player/assets/svg/love-solid.d.svg.ts deleted file mode 100644 index 8ff0878..0000000 --- a/src/player/assets/svg/love-solid.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_love_solid: string; -export default svg_love_solid; \ No newline at end of file diff --git a/src/player/assets/svg/member.d.svg.ts b/src/player/assets/svg/member.d.svg.ts deleted file mode 100644 index 06647cc..0000000 --- a/src/player/assets/svg/member.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_member: string; -export default svg_member; \ No newline at end of file diff --git a/src/player/assets/svg/member.svg b/src/player/assets/svg/member.svg deleted file mode 100644 index 3c4b35c..0000000 --- a/src/player/assets/svg/member.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/player/assets/svg/men.d.svg.ts b/src/player/assets/svg/men.d.svg.ts deleted file mode 100644 index c85bd29..0000000 --- a/src/player/assets/svg/men.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_men: string; -export default svg_men; \ No newline at end of file diff --git a/src/player/assets/svg/men.svg b/src/player/assets/svg/men.svg deleted file mode 100644 index 5f0c84c..0000000 --- a/src/player/assets/svg/men.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/player/assets/svg/message.d.svg.ts b/src/player/assets/svg/message.d.svg.ts deleted file mode 100644 index 3bbce1c..0000000 --- a/src/player/assets/svg/message.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_message: string; -export default svg_message; \ No newline at end of file diff --git a/src/player/assets/svg/message.svg b/src/player/assets/svg/message.svg deleted file mode 100644 index 4dbe368..0000000 --- a/src/player/assets/svg/message.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/player/assets/svg/mobile.d.svg.ts b/src/player/assets/svg/mobile.d.svg.ts deleted file mode 100644 index f342e3f..0000000 --- a/src/player/assets/svg/mobile.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_mobile: string; -export default svg_mobile; \ No newline at end of file diff --git a/src/player/assets/svg/more.d.svg.ts b/src/player/assets/svg/more.d.svg.ts deleted file mode 100644 index 8c3f5ea..0000000 --- a/src/player/assets/svg/more.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_more: string; -export default svg_more; \ No newline at end of file diff --git a/src/player/assets/svg/nailhead-left.d.svg.ts b/src/player/assets/svg/nailhead-left.d.svg.ts deleted file mode 100644 index 0e204cd..0000000 --- a/src/player/assets/svg/nailhead-left.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_nailhead_left: string; -export default svg_nailhead_left; \ No newline at end of file diff --git a/src/player/assets/svg/nailhead-right.d.svg.ts b/src/player/assets/svg/nailhead-right.d.svg.ts deleted file mode 100644 index 64fb416..0000000 --- a/src/player/assets/svg/nailhead-right.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_nailhead_right: string; -export default svg_nailhead_right; \ No newline at end of file diff --git a/src/player/assets/svg/next.d.svg.ts b/src/player/assets/svg/next.d.svg.ts deleted file mode 100644 index 26b91a6..0000000 --- a/src/player/assets/svg/next.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_next: string; -export default svg_next; \ No newline at end of file diff --git a/src/player/assets/svg/pause.d.svg.ts b/src/player/assets/svg/pause.d.svg.ts deleted file mode 100644 index 5cf5d06..0000000 --- a/src/player/assets/svg/pause.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_pause: string; -export default svg_pause; \ No newline at end of file diff --git a/src/player/assets/svg/play.d.svg.ts b/src/player/assets/svg/play.d.svg.ts deleted file mode 100644 index 013a384..0000000 --- a/src/player/assets/svg/play.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_play: string; -export default svg_play; \ No newline at end of file diff --git a/src/player/assets/svg/pull-up.d.svg.ts b/src/player/assets/svg/pull-up.d.svg.ts deleted file mode 100644 index 6243b3e..0000000 --- a/src/player/assets/svg/pull-up.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_pull_up: string; -export default svg_pull_up; \ No newline at end of file diff --git a/src/player/assets/svg/qq.d.svg.ts b/src/player/assets/svg/qq.d.svg.ts deleted file mode 100644 index 0d945f2..0000000 --- a/src/player/assets/svg/qq.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_qq: string; -export default svg_qq; \ No newline at end of file diff --git a/src/player/assets/svg/qqzone.d.svg.ts b/src/player/assets/svg/qqzone.d.svg.ts deleted file mode 100644 index e861adb..0000000 --- a/src/player/assets/svg/qqzone.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_qqzone: string; -export default svg_qqzone; \ No newline at end of file diff --git a/src/player/assets/svg/question.d.svg.ts b/src/player/assets/svg/question.d.svg.ts deleted file mode 100644 index c4a1d0d..0000000 --- a/src/player/assets/svg/question.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_question: string; -export default svg_question; \ No newline at end of file diff --git a/src/player/assets/svg/radio.d.svg.ts b/src/player/assets/svg/radio.d.svg.ts deleted file mode 100644 index 6356b48..0000000 --- a/src/player/assets/svg/radio.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_radio: string; -export default svg_radio; \ No newline at end of file diff --git a/src/player/assets/svg/random.d.svg.ts b/src/player/assets/svg/random.d.svg.ts deleted file mode 100644 index 5126806..0000000 --- a/src/player/assets/svg/random.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_random: string; -export default svg_random; \ No newline at end of file diff --git a/src/player/assets/svg/reflesh.d.svg.ts b/src/player/assets/svg/reflesh.d.svg.ts deleted file mode 100644 index 5e30b85..0000000 --- a/src/player/assets/svg/reflesh.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_reflesh: string; -export default svg_reflesh; \ No newline at end of file diff --git a/src/player/assets/svg/remove.d.svg.ts b/src/player/assets/svg/remove.d.svg.ts deleted file mode 100644 index 601e0c8..0000000 --- a/src/player/assets/svg/remove.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_remove: string; -export default svg_remove; \ No newline at end of file diff --git a/src/player/assets/svg/repeat.d.svg.ts b/src/player/assets/svg/repeat.d.svg.ts deleted file mode 100644 index a9c3e4f..0000000 --- a/src/player/assets/svg/repeat.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_repeat: string; -export default svg_repeat; \ No newline at end of file diff --git a/src/player/assets/svg/replay.d.svg.ts b/src/player/assets/svg/replay.d.svg.ts deleted file mode 100644 index b447357..0000000 --- a/src/player/assets/svg/replay.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_replay: string; -export default svg_replay; \ No newline at end of file diff --git a/src/player/assets/svg/reverse.d.svg.ts b/src/player/assets/svg/reverse.d.svg.ts deleted file mode 100644 index 788deca..0000000 --- a/src/player/assets/svg/reverse.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_reverse: string; -export default svg_reverse; \ No newline at end of file diff --git a/src/player/assets/svg/right.d.svg.ts b/src/player/assets/svg/right.d.svg.ts deleted file mode 100644 index 6f3a27c..0000000 --- a/src/player/assets/svg/right.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_right: string; -export default svg_right; \ No newline at end of file diff --git a/src/player/assets/svg/screen-full-web-exit.d.svg.ts b/src/player/assets/svg/screen-full-web-exit.d.svg.ts deleted file mode 100644 index 5d5f761..0000000 --- a/src/player/assets/svg/screen-full-web-exit.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_screen_full_web_exit: string; -export default svg_screen_full_web_exit; \ No newline at end of file diff --git a/src/player/assets/svg/screen-full-web.d.svg.ts b/src/player/assets/svg/screen-full-web.d.svg.ts deleted file mode 100644 index 8e63228..0000000 --- a/src/player/assets/svg/screen-full-web.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_screen_full_web: string; -export default svg_screen_full_web; \ No newline at end of file diff --git a/src/player/assets/svg/screen-full.d.svg.ts b/src/player/assets/svg/screen-full.d.svg.ts deleted file mode 100644 index 5fbd486..0000000 --- a/src/player/assets/svg/screen-full.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_screen_full: string; -export default svg_screen_full; \ No newline at end of file diff --git a/src/player/assets/svg/screen-pip.d.svg.ts b/src/player/assets/svg/screen-pip.d.svg.ts deleted file mode 100644 index 86d2c76..0000000 --- a/src/player/assets/svg/screen-pip.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_screen_pip: string; -export default svg_screen_pip; \ No newline at end of file diff --git a/src/player/assets/svg/screen-wide-on.d.svg.ts b/src/player/assets/svg/screen-wide-on.d.svg.ts deleted file mode 100644 index db35224..0000000 --- a/src/player/assets/svg/screen-wide-on.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_screen_wide_on: string; -export default svg_screen_wide_on; \ No newline at end of file diff --git a/src/player/assets/svg/screen-wide.d.svg.ts b/src/player/assets/svg/screen-wide.d.svg.ts deleted file mode 100644 index b01681d..0000000 --- a/src/player/assets/svg/screen-wide.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_screen_wide: string; -export default svg_screen_wide; \ No newline at end of file diff --git a/src/player/assets/svg/search.d.svg.ts b/src/player/assets/svg/search.d.svg.ts deleted file mode 100644 index a3563fb..0000000 --- a/src/player/assets/svg/search.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_search: string; -export default svg_search; \ No newline at end of file diff --git a/src/player/assets/svg/select.d.svg.ts b/src/player/assets/svg/select.d.svg.ts deleted file mode 100644 index 4e412b5..0000000 --- a/src/player/assets/svg/select.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_select: string; -export default svg_select; \ No newline at end of file diff --git a/src/player/assets/svg/sent.d.svg.ts b/src/player/assets/svg/sent.d.svg.ts deleted file mode 100644 index 5e4715c..0000000 --- a/src/player/assets/svg/sent.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_sent: string; -export default svg_sent; \ No newline at end of file diff --git a/src/player/assets/svg/sequence-block.d.svg.ts b/src/player/assets/svg/sequence-block.d.svg.ts deleted file mode 100644 index a679110..0000000 --- a/src/player/assets/svg/sequence-block.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_sequence_block: string; -export default svg_sequence_block; \ No newline at end of file diff --git a/src/player/assets/svg/sequence-inline.d.svg.ts b/src/player/assets/svg/sequence-inline.d.svg.ts deleted file mode 100644 index 7b303c9..0000000 --- a/src/player/assets/svg/sequence-inline.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_sequence_inline: string; -export default svg_sequence_inline; \ No newline at end of file diff --git a/src/player/assets/svg/setting-more.d.svg.ts b/src/player/assets/svg/setting-more.d.svg.ts deleted file mode 100644 index d1405c9..0000000 --- a/src/player/assets/svg/setting-more.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_setting_more: string; -export default svg_setting_more; \ No newline at end of file diff --git a/src/player/assets/svg/setting.d.svg.ts b/src/player/assets/svg/setting.d.svg.ts deleted file mode 100644 index 0de162e..0000000 --- a/src/player/assets/svg/setting.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_setting: string; -export default svg_setting; \ No newline at end of file diff --git a/src/player/assets/svg/share.d.svg.ts b/src/player/assets/svg/share.d.svg.ts deleted file mode 100644 index 9b1da10..0000000 --- a/src/player/assets/svg/share.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_share: string; -export default svg_share; \ No newline at end of file diff --git a/src/player/assets/svg/suggest.d.svg.ts b/src/player/assets/svg/suggest.d.svg.ts deleted file mode 100644 index 27a080d..0000000 --- a/src/player/assets/svg/suggest.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_suggest: string; -export default svg_suggest; \ No newline at end of file diff --git a/src/player/assets/svg/tieba.d.svg.ts b/src/player/assets/svg/tieba.d.svg.ts deleted file mode 100644 index 2ed4189..0000000 --- a/src/player/assets/svg/tieba.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_tieba: string; -export default svg_tieba; \ No newline at end of file diff --git a/src/player/assets/svg/title-left.d.svg.ts b/src/player/assets/svg/title-left.d.svg.ts deleted file mode 100644 index 6eb48d0..0000000 --- a/src/player/assets/svg/title-left.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_title_left: string; -export default svg_title_left; \ No newline at end of file diff --git a/src/player/assets/svg/title-right.d.svg.ts b/src/player/assets/svg/title-right.d.svg.ts deleted file mode 100644 index d3e0240..0000000 --- a/src/player/assets/svg/title-right.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_title_right: string; -export default svg_title_right; \ No newline at end of file diff --git a/src/player/assets/svg/triangle-end-end.d.svg.ts b/src/player/assets/svg/triangle-end-end.d.svg.ts deleted file mode 100644 index bd292a6..0000000 --- a/src/player/assets/svg/triangle-end-end.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_triangle_end_end: string; -export default svg_triangle_end_end; \ No newline at end of file diff --git a/src/player/assets/svg/triangle-end-start.d.svg.ts b/src/player/assets/svg/triangle-end-start.d.svg.ts deleted file mode 100644 index 916b9dc..0000000 --- a/src/player/assets/svg/triangle-end-start.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_triangle_end_start: string; -export default svg_triangle_end_start; \ No newline at end of file diff --git a/src/player/assets/svg/triangle-start-end.d.svg.ts b/src/player/assets/svg/triangle-start-end.d.svg.ts deleted file mode 100644 index 1e97442..0000000 --- a/src/player/assets/svg/triangle-start-end.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_triangle_start_end: string; -export default svg_triangle_start_end; \ No newline at end of file diff --git a/src/player/assets/svg/triangle-start-start.d.svg.ts b/src/player/assets/svg/triangle-start-start.d.svg.ts deleted file mode 100644 index bf6763e..0000000 --- a/src/player/assets/svg/triangle-start-start.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_triangle_start_start: string; -export default svg_triangle_start_start; \ No newline at end of file diff --git a/src/player/assets/svg/tv-head.d.svg.ts b/src/player/assets/svg/tv-head.d.svg.ts deleted file mode 100644 index 74fd324..0000000 --- a/src/player/assets/svg/tv-head.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_tv_head: string; -export default svg_tv_head; \ No newline at end of file diff --git a/src/player/assets/svg/tv.d.svg.ts b/src/player/assets/svg/tv.d.svg.ts deleted file mode 100644 index cf65cee..0000000 --- a/src/player/assets/svg/tv.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_tv: string; -export default svg_tv; \ No newline at end of file diff --git a/src/player/assets/svg/up.d.svg.ts b/src/player/assets/svg/up.d.svg.ts deleted file mode 100644 index 57c26af..0000000 --- a/src/player/assets/svg/up.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_up: string; -export default svg_up; \ No newline at end of file diff --git a/src/player/assets/svg/upload.d.svg.ts b/src/player/assets/svg/upload.d.svg.ts deleted file mode 100644 index 8d37628..0000000 --- a/src/player/assets/svg/upload.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_upload: string; -export default svg_upload; \ No newline at end of file diff --git a/src/player/assets/svg/upper.d.svg.ts b/src/player/assets/svg/upper.d.svg.ts deleted file mode 100644 index 2182a9b..0000000 --- a/src/player/assets/svg/upper.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_upper: string; -export default svg_upper; \ No newline at end of file diff --git a/src/player/assets/svg/volume-large.d.svg.ts b/src/player/assets/svg/volume-large.d.svg.ts deleted file mode 100644 index cc846ed..0000000 --- a/src/player/assets/svg/volume-large.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_volume_large: string; -export default svg_volume_large; \ No newline at end of file diff --git a/src/player/assets/svg/volume-muted.d.svg.ts b/src/player/assets/svg/volume-muted.d.svg.ts deleted file mode 100644 index 7356cbc..0000000 --- a/src/player/assets/svg/volume-muted.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_volume_muted: string; -export default svg_volume_muted; \ No newline at end of file diff --git a/src/player/assets/svg/volume.d.svg.ts b/src/player/assets/svg/volume.d.svg.ts deleted file mode 100644 index e9e6649..0000000 --- a/src/player/assets/svg/volume.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_volume: string; -export default svg_volume; \ No newline at end of file diff --git a/src/player/assets/svg/wallet.d.svg.ts b/src/player/assets/svg/wallet.d.svg.ts deleted file mode 100644 index 3f53701..0000000 --- a/src/player/assets/svg/wallet.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_wallet: string; -export default svg_wallet; \ No newline at end of file diff --git a/src/player/assets/svg/wallet.svg b/src/player/assets/svg/wallet.svg deleted file mode 100644 index d542a9a..0000000 --- a/src/player/assets/svg/wallet.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/player/assets/svg/weibo.d.svg.ts b/src/player/assets/svg/weibo.d.svg.ts deleted file mode 100644 index 7dd3e3c..0000000 --- a/src/player/assets/svg/weibo.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_weibo: string; -export default svg_weibo; \ No newline at end of file diff --git a/src/player/assets/svg/women.d.svg.ts b/src/player/assets/svg/women.d.svg.ts deleted file mode 100644 index dce6db4..0000000 --- a/src/player/assets/svg/women.d.svg.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const svg_women: string; -export default svg_women; \ No newline at end of file diff --git a/src/player/assets/svg/women.svg b/src/player/assets/svg/women.svg deleted file mode 100644 index 47ebd98..0000000 --- a/src/player/assets/svg/women.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/player/style/auxiliary/block/index.css b/src/player/auxiliary/block/index.css similarity index 83% rename from src/player/style/auxiliary/block/index.css rename to src/player/auxiliary/block/index.css index 6c2fee9..e06bc4d 100644 --- a/src/player/style/auxiliary/block/index.css +++ b/src/player/auxiliary/block/index.css @@ -17,7 +17,7 @@ >header { inline-size: 100%; font-weight: 700; - color: #222; + color: var(--222); padding-block-start: 16px; padding-block-end: 10px; } @@ -39,7 +39,7 @@ >svg { &:not(:last-child) { font-size: 48px; - fill: #99a2aa; + fill: var(--99a2aa); transition: fill .3s; anchor-name: --danmaku-block; } @@ -49,9 +49,9 @@ inset-inline-end: anchor(--danmaku-block end); inset-block-end: anchor(--danmaku-block end); font-size: 24px; - background-color: #fff; - border: 3px solid #fff; - fill: #99a2aa; + background-color: var(--fff); + border: 3px solid var(--fff); + fill: var(--99a2aa); border-radius: 50%; box-sizing: border-box; opacity: 0; @@ -63,18 +63,18 @@ >svg { &:not(:last-child) { - fill: #6d757a; + fill: var(--6d757a); } &:last-child { opacity: 1; - fill: #6d757a; + fill: var(--6d757a); } } } &.block { - color: #99a2aa; + color: var(--99a2aa); >svg { @@ -91,4 +91,9 @@ block-size: 30px; } } + + svg { + block-size: 1em; + aspect-ratio: 1; + } } \ No newline at end of file diff --git a/src/player/auxiliary/block/index.ts b/src/player/auxiliary/block/index.ts index 164dadb..b98865e 100644 --- a/src/player/auxiliary/block/index.ts +++ b/src/player/auxiliary/block/index.ts @@ -1,17 +1,18 @@ +import { Player } from "../.."; +import svg_24danmuforbid from "../../../assets/svg/24danmuforbid.svg"; +import svg_48danmubottom from "../../../assets/svg/48danmubottom.svg"; +import svg_48danmucode from "../../../assets/svg/48danmucode.svg"; +import svg_48danmucolor from "../../../assets/svg/48danmucolor.svg"; +import svg_48danmunorm from "../../../assets/svg/48danmunorm.svg"; +import svg_48danmunormal from "../../../assets/svg/48danmunormal.svg"; +import svg_48danmuspe from "../../../assets/svg/48danmuspe.svg"; +import svg_48danmutext from "../../../assets/svg/48danmutext.svg"; +import svg_48danmutop from "../../../assets/svg/48danmutop.svg"; +import svg_48danmuunreg from "../../../assets/svg/48danmuunreg.svg"; import { DANMAKU } from "../../../danmaku/block"; import { customElement } from "../../../utils/Decorator/customElement"; import { Element } from "../../../utils/element"; -import svg_danmaku_color from "../../assets/svg/danmaku-color.svg"; -import svg_danmaku_forbid from "../../assets/svg/danmaku-forbid.svg"; -import svg_danmaku_mode_1 from "../../assets/svg/danmaku-mode-1.svg"; -import svg_danmaku_mode_4 from "../../assets/svg/danmaku-mode-4.svg"; -import svg_danmaku_mode_5 from "../../assets/svg/danmaku-mode-5.svg"; -import svg_danmaku_mode_7 from "../../assets/svg/danmaku-mode-7.svg"; -import svg_danmaku_mode_8 from "../../assets/svg/danmaku-mode-8.svg"; -import svg_danmaku_normal from "../../assets/svg/danmaku-normal.svg"; -import svg_danmaku_text from "../../assets/svg/danmaku-text.svg"; -import svg_danmaku_unregister from "../../assets/svg/danmaku-unregister.svg"; -import { PLAYER_EVENT, ev } from "../../event-target"; +import { ev, PLAYER_EVENT } from "../../event"; import { options } from "../../option"; import { Slider } from "../../widget/slider"; @@ -33,11 +34,8 @@ export class Block extends HTMLDivElement { */ // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} - /** 初始化标记 */ - // #inited = false; - /** 每当元素添加到文档中时调用。 */ - // connectedCallback() {} + // connectedCallback() { } /** 每当元素从文档中移除时调用。 */ // disconnectedCallback() {} @@ -46,59 +44,50 @@ export class Block extends HTMLDivElement { // adoptedCallback() {} /** 按类型屏蔽 */ - private $type = Element.add('div', { class: 'bofqi-auxiliary-block-filter' }, this, `
    按类型屏蔽
    `); + #type = Element.add('div', { class: 'bofqi-auxiliary-block-filter', appendTo: this, innerHTML: '
    按类型屏蔽
    ' }); /** 滚动弹幕 */ - private $scroll = Element.add('div', { class: 'bofqi-auxiliary-block-filter-type' }, this.$type, svg_danmaku_mode_1 + svg_danmaku_forbid); + #scroll = Element.add('div', { class: 'bofqi-auxiliary-block-filter-type', appendTo: this.#type, innerHTML: svg_48danmunormal + svg_24danmuforbid, data: { label: '滚动弹幕' } }); /** 顶端弹幕 */ - private $top = Element.add('div', { class: 'bofqi-auxiliary-block-filter-type' }, this.$type, svg_danmaku_mode_5 + svg_danmaku_forbid); + #top = Element.add('div', { class: 'bofqi-auxiliary-block-filter-type', appendTo: this.#type, innerHTML: svg_48danmutop + svg_24danmuforbid, data: { label: '顶端弹幕' } }); /** 底端弹幕 */ - private $bottom = Element.add('div', { class: 'bofqi-auxiliary-block-filter-type' }, this.$type, svg_danmaku_mode_4 + svg_danmaku_forbid); + #bottom = Element.add('div', { class: 'bofqi-auxiliary-block-filter-type', appendTo: this.#type, innerHTML: svg_48danmubottom + svg_24danmuforbid, data: { label: '底端弹幕' } }); /** 彩色弹幕 */ - private $color = Element.add('div', { class: 'bofqi-auxiliary-block-filter-type' }, this.$type, svg_danmaku_color + svg_danmaku_forbid); + #color = Element.add('div', { class: 'bofqi-auxiliary-block-filter-type', appendTo: this.#type, innerHTML: svg_48danmucolor + svg_24danmuforbid, data: { label: '彩色弹幕' } }); /** 高级弹幕 */ - private $mode7 = Element.add('div', { class: 'bofqi-auxiliary-block-filter-type' }, this.$type, svg_danmaku_normal + svg_danmaku_forbid); + #mode7 = Element.add('div', { class: 'bofqi-auxiliary-block-filter-type', appendTo: this.#type, innerHTML: svg_48danmunorm + svg_24danmuforbid, data: { label: '高级弹幕' } }); /** 代码弹幕 */ - private $mode8 = Element.add('div', { class: 'bofqi-auxiliary-block-filter-type' }, this.$type, svg_danmaku_mode_8 + svg_danmaku_forbid); + #mode8 = Element.add('div', { class: 'bofqi-auxiliary-block-filter-type', appendTo: this.#type, innerHTML: svg_48danmucode + svg_24danmuforbid, data: { label: '代码弹幕' } }); /** BAS弹幕 */ - private $mode9 = Element.add('div', { class: 'bofqi-auxiliary-block-filter-type' }, this.$type, svg_danmaku_mode_7 + svg_danmaku_forbid); + #mode9 = Element.add('div', { class: 'bofqi-auxiliary-block-filter-type', appendTo: this.#type, innerHTML: svg_48danmuspe + svg_24danmuforbid, data: { label: 'BAS弹幕' } }); /** 高赞弹幕 */ - private $like = Element.add('div', { class: 'bofqi-auxiliary-block-filter-type' }, this.$type, svg_danmaku_unregister + svg_danmaku_forbid); + #like = Element.add('div', { class: 'bofqi-auxiliary-block-filter-type', appendTo: this.#type, innerHTML: svg_48danmuunreg + svg_24danmuforbid, data: { label: '高赞弹幕' } }); /** VIP弹幕 */ - private $vip = Element.add('div', { class: 'bofqi-auxiliary-block-filter-type' }, this.$type, svg_danmaku_text + svg_danmaku_forbid); + #vip = Element.add('div', { class: 'bofqi-auxiliary-block-filter-type', appendTo: this.#type, innerHTML: svg_48danmutext + svg_24danmuforbid, data: { label: 'VIP弹幕' } }); /** 等级屏蔽 */ - private $level = Element.add('div', { class: 'bofqi-auxiliary-block-filter' }, this, `
    等级屏蔽
    `); + #level = Element.add('div', { class: 'bofqi-auxiliary-block-filter', appendTo: this, innerHTML: '
    等级屏蔽
    ' }); /** 等级滑块 */ - private $weight = this.$level.appendChild(new Slider()); + #weight = this.#level.appendChild(new Slider()); constructor() { super(); this.classList.add('bofqi-auxiliary-block'); - this.$scroll.dataset.label = '滚动弹幕'; - this.$top.dataset.label = '顶端弹幕'; - this.$bottom.dataset.label = '底端弹幕'; - this.$color.dataset.label = '彩色弹幕'; - this.$mode7.dataset.label = '高级弹幕'; - this.$mode8.dataset.label = '代码弹幕'; - this.$mode9.dataset.label = 'BAS弹幕'; - this.$like.dataset.label = '高赞弹幕'; - this.$vip.dataset.label = 'VIP弹幕'; - this.$weight.classList.add('bofqi-auxiliary-block-filter-weight'); - this.$weight.max = '11'; - this.$weight.$hint = true; - this.$weight.formatHint(v => { + this.#weight.classList.add('bofqi-auxiliary-block-filter-weight'); + this.#weight.max = 11; + this.#weight.$hint = true; + this.#weight.formatHint(v => { switch (v) { case 0: { return '关闭' @@ -115,47 +104,47 @@ export class Block extends HTMLDivElement { ev.bind(PLAYER_EVENT.OPTINOS_CHANGE, ({ detail }) => { const { block, weight } = detail.danmaku; - this.$scroll.classList.toggle('block', Boolean(block & DANMAKU.SCROLL)); - this.$top.classList.toggle('block', Boolean(block & DANMAKU.TOP)); - this.$bottom.classList.toggle('block', Boolean(block & DANMAKU.BOTTOM)); - this.$color.classList.toggle('block', Boolean(block & DANMAKU.COLOR)); - this.$mode7.classList.toggle('block', Boolean(block & DANMAKU.ADVANCE)); - this.$mode8.classList.toggle('block', Boolean(block & DANMAKU.SCRIPT)); - this.$mode9.classList.toggle('block', Boolean(block & DANMAKU.BAS)); - this.$like.classList.toggle('block', Boolean(block & DANMAKU.LIKE)); - this.$vip.classList.toggle('block', Boolean(block & DANMAKU.VIP)); - this.$weight.$value = weight; + this.#scroll.classList.toggle('block', Boolean(block & DANMAKU.SCROLL)); + this.#top.classList.toggle('block', Boolean(block & DANMAKU.TOP)); + this.#bottom.classList.toggle('block', Boolean(block & DANMAKU.BOTTOM)); + this.#color.classList.toggle('block', Boolean(block & DANMAKU.COLOR)); + this.#mode7.classList.toggle('block', Boolean(block & DANMAKU.ADVANCE)); + this.#mode8.classList.toggle('block', Boolean(block & DANMAKU.SCRIPT)); + this.#mode9.classList.toggle('block', Boolean(block & DANMAKU.BAS)); + this.#like.classList.toggle('block', Boolean(block & DANMAKU.LIKE)); + this.#vip.classList.toggle('block', Boolean(block & DANMAKU.VIP)); + this.#weight.$value = weight; }); - this.$scroll.addEventListener('click', () => { + this.#scroll.addEventListener('click', () => { options.danmaku.block ^= DANMAKU.SCROLL; }); - this.$top.addEventListener('click', () => { + this.#top.addEventListener('click', () => { options.danmaku.block ^= DANMAKU.TOP; }); - this.$bottom.addEventListener('click', () => { + this.#bottom.addEventListener('click', () => { options.danmaku.block ^= DANMAKU.BOTTOM; }); - this.$color.addEventListener('click', () => { + this.#color.addEventListener('click', () => { options.danmaku.block ^= DANMAKU.COLOR; }); - this.$mode7.addEventListener('click', () => { + this.#mode7.addEventListener('click', () => { options.danmaku.block ^= DANMAKU.ADVANCE; }); - this.$mode8.addEventListener('click', () => { + this.#mode8.addEventListener('click', () => { options.danmaku.block ^= DANMAKU.SCRIPT; }); - this.$mode9.addEventListener('click', () => { + this.#mode9.addEventListener('click', () => { options.danmaku.block ^= DANMAKU.BAS; }); - this.$like.addEventListener('click', () => { + this.#like.addEventListener('click', () => { options.danmaku.block ^= DANMAKU.LIKE; }); - this.$vip.addEventListener('click', () => { + this.#vip.addEventListener('click', () => { options.danmaku.block ^= DANMAKU.VIP; }); - this.$weight.addEventListener('change', () => { - options.danmaku.weight = +this.$weight.$value || 0; + this.#weight.addEventListener('change', () => { + options.danmaku.weight = +this.#weight.$value || 0; }); } } \ No newline at end of file diff --git a/src/player/style/auxiliary/danmaku/context.css b/src/player/auxiliary/danmaku/context.css similarity index 81% rename from src/player/style/auxiliary/danmaku/context.css rename to src/player/auxiliary/danmaku/context.css index 6982d72..7353e34 100644 --- a/src/player/style/auxiliary/danmaku/context.css +++ b/src/player/auxiliary/danmaku/context.css @@ -17,7 +17,7 @@ &.context-danmaku-silent { display: none; - border-block-start: 1px solid #e2e2e2; + border-block-start: 1px solid var(--e2e2e2); &.active { display: flex; @@ -25,10 +25,10 @@ } &.context-danmaku-border-top { - border-block-start: 1px solid #e2e2e2; + border-block-start: 1px solid var(--e2e2e2); } &:hover { - background: #ddd; + background: var(--ddd); } } \ No newline at end of file diff --git a/src/player/style/auxiliary/danmaku/danmaku.css b/src/player/auxiliary/danmaku/danmaku.css similarity index 68% rename from src/player/style/auxiliary/danmaku/danmaku.css rename to src/player/auxiliary/danmaku/danmaku.css index 6db635b..c67fcf5 100644 --- a/src/player/style/auxiliary/danmaku/danmaku.css +++ b/src/player/auxiliary/danmaku/danmaku.css @@ -46,7 +46,7 @@ inset-inline-end: 0; block-size: 100%; inline-size: 88px; - background-color: var(--background-color); + background-color: var(--fff); text-align: end; padding-inline-end: 26px; } @@ -63,19 +63,19 @@ &:is(.protect, .live, .like):hover { .attr { display: block; - background: conic-gradient(#ffa726 120deg, #47be8d 120deg 240deg, #f2509e 240deg); + background: conic-gradient(var(--ffa726) 120deg, var(--47be8d) 120deg 240deg, var(--f2509e) 240deg); } &:not(.live) { .attr { - background: conic-gradient(#ffa726 180deg, #47be8d 180deg); + background: conic-gradient(var(--ffa726) 180deg, var(--47be8d) 180deg); } &:not(.protect) { .attr { - background: #ffa726; + background: var(--ffa726); } } @@ -83,13 +83,13 @@ &:not(.protect) { .attr { - background: conic-gradient(#ffa726 180deg, #f2509e 180deg); + background: conic-gradient(var(--ffa726) 180deg, var(--f2509e) 180deg); } &:not(.like) { .attr { - background: #f2509e; + background: var(--f2509e); } } @@ -97,13 +97,13 @@ &:not(.like) { .attr { - background: conic-gradient(#47be8d 180deg, #f2509e 180deg); + background: conic-gradient(var(--47be8d) 180deg, var(--f2509e) 180deg); } &:not(.live) { .attr { - background: #47be8d; + background: var(--47be8d); } } @@ -111,22 +111,22 @@ } &.mode7 { - background-color: #effef5; - color: #47be8d; + background-color: var(--effef5); + color: var(--47be8d); } &.mode8 { - background-color: #fffde1; - color: #ffa726; + background-color: var(--fffde1); + color: var(--ffa726); } &.mode9 { - background-color: #ffefd8; - color: #ff792b; + background-color: var(--ffefd8); + color: var(--ff792b); } &:hover { - color: #00a1d6; + color: var(--00a1d6); } } @@ -134,10 +134,10 @@ .mode1, .mode6 { - color: #ccd0d7; + color: var(--ccd0d7); &:hover { - color: #ccd0d7; + color: var(--ccd0d7); } .block { @@ -148,10 +148,10 @@ .block-4 { .mode4 { - color: #ccd0d7; + color: var(--ccd0d7); &:hover { - color: #ccd0d7; + color: var(--ccd0d7); } .block { @@ -162,10 +162,10 @@ .block-5 { .mode5 { - color: #ccd0d7; + color: var(--ccd0d7); &:hover { - color: #ccd0d7; + color: var(--ccd0d7); } .block { @@ -176,10 +176,10 @@ .block-7 { .mode7 { - color: #ccd0d7; + color: var(--ccd0d7); &:hover { - color: #ccd0d7; + color: var(--ccd0d7); } .block { @@ -190,10 +190,10 @@ .block-8 { .mode8 { - color: #ccd0d7; + color: var(--ccd0d7); &:hover { - color: #ccd0d7; + color: var(--ccd0d7); } .block { @@ -204,10 +204,10 @@ .block-9 { .mode9 { - color: #ccd0d7; + color: var(--ccd0d7); &:hover { - color: #ccd0d7; + color: var(--ccd0d7); } .block { diff --git a/src/player/auxiliary/danmaku/danmaku.ts b/src/player/auxiliary/danmaku/danmaku.ts index 99363f5..d8f48d4 100644 --- a/src/player/auxiliary/danmaku/danmaku.ts +++ b/src/player/auxiliary/danmaku/danmaku.ts @@ -1,10 +1,10 @@ +import { Player } from "../.."; import { IDanmaku } from "../../../danmaku"; import { ATTR } from "../../../danmaku/attr"; import { customElement } from "../../../utils/Decorator/customElement"; import { Element } from "../../../utils/element"; import { Format } from "../../../utils/fomat"; -import { video } from "../../area/wrap/video"; -import { PLAYER_EVENT, ev } from "../../event-target"; +import { ev, PLAYER_EVENT } from "../../event"; /** 弹幕项 */ @customElement('div') @@ -37,25 +37,24 @@ export class DanmakuElem extends HTMLDivElement { // adoptedCallback() {} /** 时间 */ - private $progress = Element.add('div', { class: 'progress' }, this); + private $progress = Element.add('div', { class: 'progress', appendTo: this }); /** 弹幕内容 */ - private $content = Element.add('div', { class: 'content' }, this); + private $content = Element.add('div', { class: 'content', appendTo: this }); /** 发送时间 */ - private $sent = Element.add('div', { class: 'sent' }, this); + private $sent = Element.add('div', { class: 'sent', appendTo: this }); /** 屏蔽显示 */ - private $block = Element.add('div', { class: 'block' }, this, '已屏蔽'); + private $block = Element.add('div', { class: 'block', appendTo: this, innerText: '已屏蔽' }); /** 弹幕属性 */ - private $attr = Element.add('div', { class: 'attr' }, this); + private $attr = Element.add('div', { class: 'attr', appendTo: this }); - constructor($dm: IDanmaku) { + constructor($dm: IDanmaku, player: Player) { super(); this.classList.add('danmaku-elem', `mode${$dm.mode}`); - $dm.midHash && (this.dataset.mid = $dm.midHash); $dm.color && this.classList.add('color'); this.$progress.textContent = Format.fmSeconds($dm.progress / 1000); this.$content.appendChild(document.createElement('p')).textContent = $dm.content || ''; @@ -79,7 +78,7 @@ export class DanmakuElem extends HTMLDivElement { : this.getDate($dm.ctime * 1000); this.addEventListener('dblclick', () => { - video.seek($dm.progress / 1000) + player.$video.$seek($dm.progress / 1000) }); this.addEventListener('contextmenu', () => { ev.trigger(PLAYER_EVENT.DANMAKU_CONTEXT, $dm); diff --git a/src/player/style/auxiliary/danmaku/index.css b/src/player/auxiliary/danmaku/index.css similarity index 100% rename from src/player/style/auxiliary/danmaku/index.css rename to src/player/auxiliary/danmaku/index.css diff --git a/src/player/auxiliary/danmaku/index.ts b/src/player/auxiliary/danmaku/index.ts index 13c9785..9c4ee8e 100644 --- a/src/player/auxiliary/danmaku/index.ts +++ b/src/player/auxiliary/danmaku/index.ts @@ -1,8 +1,11 @@ +import { Player } from "../.."; import { IDanmaku } from "../../../danmaku"; import { DANMAKU } from "../../../danmaku/block"; +import { toastr } from "../../../toastr"; import { customElement } from "../../../utils/Decorator/customElement"; import { Element } from "../../../utils/element"; -import { PLAYER_EVENT, ev } from "../../event-target"; +import { Format } from "../../../utils/fomat"; +import { ev, PLAYER_EVENT } from "../../event"; import { Context } from "../../widget/context"; import { DanmakuElem } from "./danmaku"; @@ -36,56 +39,59 @@ export class Danmaku extends HTMLDivElement { /** 每当元素被移动到新文档中时调用。 */ // adoptedCallback() {} + #player: Player; + /** 被选中弹幕 */ - private $dmSelected: IDanmaku[] = []; + #dmSelected: IDanmaku[] = []; - private $dms: IDanmaku[] = []; + #dms: IDanmaku[] = []; /** 列表抬头 */ - private $header = Element.add('div', { class: 'bofqi-auxiliary-danmaku-header' }, this); + #header = Element.add('div', { class: 'bofqi-auxiliary-danmaku-header', appendTo: this }); /** 列表抬头·时间 */ - private $progress = Element.add('div', { class: 'bofqi-auxiliary-danmaku-progress' }, this.$header, '时间'); + #progress = Element.add('div', { class: 'bofqi-auxiliary-danmaku-progress', appendTo: this.#header, innerText: '时间' }); /** 列表抬头·时间 */ - private $content = Element.add('div', { class: 'bofqi-auxiliary-danmaku-content' }, this.$header, '弹幕内容'); + #content = Element.add('div', { class: 'bofqi-auxiliary-danmaku-content', appendTo: this.#header, innerText: '弹幕内容' }); /** 列表抬头·时间 */ - private $sent = Element.add('div', { class: 'bofqi-auxiliary-danmaku-sent' }, this.$header, '发送时间'); + #sent = Element.add('div', { class: 'bofqi-auxiliary-danmaku-sent', appendTo: this.#header, innerText: '发送时间' }); /** 列表 */ - private $list = Element.add('div', { class: 'bofqi-auxiliary-danmaku-list' }, this); + #list = Element.add('div', { class: 'bofqi-auxiliary-danmaku-list', appendTo: this }); /** 右键菜单 */ - private $context = new Context(this.$list); + #context = new Context(this.#list); /** 右键菜单·屏蔽发送者 */ - private $block = Element.add('li', { class: 'context-danmaku-block' }, this.$context, '屏蔽发送者'); + #block = Element.add('li', { class: 'context-danmaku-block', appendTo: this.#context, innerText: '屏蔽发送者' }); /** 右键菜单·复制选中弹幕 */ - private $copy = Element.add('li', { class: 'context-danmaku-copy' }, this.$context, '复制选中弹幕'); + #copy = Element.add('li', { class: 'context-danmaku-copy', appendTo: this.#context, innerText: '复制选中弹幕' }); /** 右键菜单·举报弹幕 */ - private $report = Element.add('li', { class: 'context-danmaku-report' }, this.$context, '举报弹幕'); + #report = Element.add('li', { class: 'context-danmaku-report', appendTo: this.#context, innerText: '举报弹幕' }); /** 右键菜单·查看该发送者的所有弹幕 */ - private $filter = Element.add('li', { class: 'context-danmaku-filter' }, this.$context, '查看该发送者的所有弹幕'); + #filter = Element.add('li', { class: 'context-danmaku-filter', appendTo: this.#context, innerText: '查看该发送者的所有弹幕' }); /** 右键菜单·UP主视频中禁言此用户 */ - private $silent = Element.add('li', { class: 'context-danmaku-silent' }, this.$context, 'UP主视频中禁言此用户'); + #silent = Element.add('li', { class: 'context-danmaku-silent', appendTo: this.#context, innerText: 'UP主视频中禁言此用户' }); /** 右键菜单·导出弹幕 */ - private $export = Element.add('li', { class: 'context-danmaku-border-top' }, this.$context, '导出弹幕'); + #export = Element.add('li', { class: 'context-danmaku-border-top', appendTo: this.#context, innerText: '导出弹幕' }); - private $startIndex = 0; + #startIndex = 0; - private $seeLength = 0; + #seeLength = 0; - constructor() { + constructor(player: Player) { super(); + this.#player = player; this.classList.add('bofqi-auxiliary-danmaku'); - this.$context.classList.add('bofqi-video-context-menu'); + this.#context.classList.add('bofqi-video-context-menu'); ev.bind(PLAYER_EVENT.OPTINOS_CHANGE, ({ detail }) => { const { block } = detail.danmaku; @@ -98,20 +104,22 @@ export class Danmaku extends HTMLDivElement { this.classList.toggle('block-9', Boolean(block & DANMAKU.BAS)); }); ev.bind(PLAYER_EVENT.DANMAKU_ADD, ({ detail }) => { - this.$dms = this.$dms.concat(detail); + this.#dms = this.#dms.concat(detail); + this.#list.hasChildNodes() || this.render(); }); ev.bind(PLAYER_EVENT.DANMAKU_CONTEXT, ({ detail }) => { - this.$dmSelected = [detail]; + this.#dmSelected = [detail]; }); - ev.bind(PLAYER_EVENT.IDENTIFY, this.identify); ev.bind(PLAYER_EVENT.DANMAKU_IDENTIFY, this.identify); - this.$copy.addEventListener('click', () => { - if (this.$dmSelected.length) { - navigator.clipboard.writeText(this.$dmSelected.map(d => d.content).join('\n')) + this.#copy.addEventListener('click', () => { + if (this.#dmSelected.length) { + navigator.clipboard.writeText(this.#dmSelected.map(d => d.content).join('\n')) } }); - this.$export.addEventListener('click', () => { + this.#export.addEventListener('click', () => { + const toast = toastr.warn('请选择保存位置~'); + toast.$delay = 0; showSaveFilePicker({ suggestedName: `${crypto.randomUUID()}.dm.json.gz`, types: [ @@ -122,53 +130,68 @@ export class Danmaku extends HTMLDivElement { } } ], - }).then(async d => { - const blob = new Blob([JSON.stringify(this.$dms, undefined, '\t')]); - const stream = blob.stream().pipeThrough(new CompressionStream('gzip')); - return <[FileSystemWritableFileStream, Blob]>[ - await d.createWritable(), - await new Response(stream).blob() - ]; - }).then(([handle, file]) => { - handle.write(file).finally(() => { handle.close() }); - }); + }) + .then(async d => { + toast.appendText(`> 正在打包弹幕 -> ${d.name}`); + const blob = new Blob([JSON.stringify(this.#dms, undefined, '\t')]); + const stream = blob.stream().pipeThrough(new CompressionStream('gzip')); + return <[FileSystemWritableFileStream, Blob]>[ + await d.createWritable(), + await new Response(stream).blob() + ]; + }) + .then(([handle, file]) => { + toast.appendText('> 保存中...请勿关闭标签页~', `> 大小:${Format.fileSize(file.size)}`); + handle.write(file).finally(() => { + handle.close(); + toast.appendText('已保存到本地磁盘~ <') + toast.$type = 'success'; + toast.$delay = 4; + }); + }) + .catch(e => { + toast.appendText(e); + toast.$type = 'error'; + toast.$delay = 4; + console.log(e); + }); }); - new ResizeObserver(this.observeResizeCallback).observe(this.$list); - new IntersectionObserver(this.observeIntersectionCallback).observe(this.$list); - this.$list.addEventListener('scroll', () => { - const { scrollTop } = this.$list; + new ResizeObserver(this.observeResizeCallback).observe(this.#list); + new IntersectionObserver(this.observeIntersectionCallback).observe(this.#list); + this.#list.addEventListener('scroll', () => { + const { scrollTop } = this.#list; const startindex = Math.floor(scrollTop / 24); - const { length } = this.$dms; - if (this.$startIndex < startindex) { + const { length } = this.#dms; + if (this.#startIndex < startindex) { // 向下滚动 - for (; (this.$startIndex < startindex && (this.$startIndex + this.$seeLength) < length); this.$startIndex++) { - this.$list.appendChild(new DanmakuElem(this.$dms[this.$startIndex + this.$seeLength])); - this.$list.firstElementChild?.remove(); + for (; (this.#startIndex < startindex && (this.#startIndex + this.#seeLength) < length); this.#startIndex++) { + this.#list.appendChild(new DanmakuElem(this.#dms[this.#startIndex + this.#seeLength], player)); + this.#list.firstElementChild?.remove(); } } else { // 向上滚动 - for (; (this.$startIndex > startindex && (this.$startIndex - 1) >= 0); this.$startIndex--) { - this.$list.prepend(new DanmakuElem(this.$dms[this.$startIndex - 1])); - this.$list.lastElementChild?.remove(); + for (; (this.#startIndex > startindex && (this.#startIndex - 1) >= 0); this.#startIndex--) { + this.#list.prepend(new DanmakuElem(this.#dms[this.#startIndex - 1], player)); + this.#list.lastElementChild?.remove(); } } this.marginFix(); }); } - private observeIntersectionCallback = (entries: IntersectionObserverEntry[]) => { + observeIntersectionCallback = (entries: IntersectionObserverEntry[]) => { let isIntersecting = false; for (const entry of entries) { isIntersecting = entry.isIntersecting; } if (!isIntersecting) { - this.$startIndex = 0; + this.#startIndex = 0; } } - private observeResizeCallback = (entries: ResizeObserverEntry[]) => { + observeResizeCallback = (entries: ResizeObserverEntry[]) => { let blockSize = 0; for (const entry of entries) { for (const borderBoxSize of entry.borderBoxSize) { @@ -181,27 +204,29 @@ export class Danmaku extends HTMLDivElement { } } - private render(blockSize: number) { - this.$list.replaceChildren(); - const { length } = this.$dms; + render(blockSize?: number) { + blockSize || (blockSize = this.#list.clientHeight); + this.#list.replaceChildren(); + const { length } = this.#dms; const max = Math.ceil(blockSize / 24) + 5; - for (let i = 0; (i < max && (this.$startIndex + i) < length); i++) { - this.$list.appendChild(new DanmakuElem(this.$dms[this.$startIndex + i])); - this.$seeLength = i + 1; + for (let i = 0; (i < max && (this.#startIndex + i) < length); i++) { + this.#list.appendChild(new DanmakuElem(this.#dms[this.#startIndex + i], this.#player)); + this.#seeLength = i + 1; } this.marginFix(); } - private marginFix() { - const { length } = this.$dms; - this.$list.style.setProperty('--margin-block-start', `${this.$startIndex * 24}px`); - this.$list.style.setProperty('--margin-block-end', `${(length - this.$seeLength - this.$startIndex) * 24}px`); + marginFix() { + const { length } = this.#dms; + this.#list.style.setProperty('--margin-block-start', `${this.#startIndex * 24}px`); + this.#list.style.setProperty('--margin-block-end', `${(length - this.#seeLength - this.#startIndex) * 24}px`); } - private identify = () => { - this.$dmSelected.length = 0; - this.$dms.length = 0; - this.$startIndex = 0; + identify = () => { + this.#list.replaceChildren(); + this.#dmSelected.length = 0; + this.#dms.length = 0; + this.#startIndex = 0; } } \ No newline at end of file diff --git a/src/player/style/auxiliary/filter.css b/src/player/auxiliary/filter/index.css similarity index 72% rename from src/player/style/auxiliary/filter.css rename to src/player/auxiliary/filter/index.css index 4d2d92c..f6a392d 100644 --- a/src/player/style/auxiliary/filter.css +++ b/src/player/auxiliary/filter/index.css @@ -3,7 +3,7 @@ display: flex; justify-content: space-around; align-items: center; - border-block: 1px solid #e2e2e2; + border-block: 1px solid var(--e2e2e2); block-size: 32px; .bofqi-auxiliary-filter-btn { @@ -12,19 +12,19 @@ block-size: 100%; display: flex; align-items: center; - color: #222; + color: var(--222); transition: all .3s; border: 2px solid transparent; &:hover { - color: #00a1d6; + color: var(--00a1d6); } &.active { pointer-events: none; cursor: default; - color: #00a1d6; - border-block-end-color: #00a1d6; + color: var(--00a1d6); + border-block-end-color: var(--00a1d6); } } } \ No newline at end of file diff --git a/src/player/auxiliary/filter.ts b/src/player/auxiliary/filter/index.ts similarity index 67% rename from src/player/auxiliary/filter.ts rename to src/player/auxiliary/filter/index.ts index 3c50bf6..6f8c5dc 100644 --- a/src/player/auxiliary/filter.ts +++ b/src/player/auxiliary/filter/index.ts @@ -1,6 +1,7 @@ -import { customElement } from "../../utils/Decorator/customElement"; -import { Element } from "../../utils/element"; -import { PLAYER_EVENT, ev } from "../event-target"; +import { Player } from "../.."; +import { customElement } from "../../../utils/Decorator/customElement"; +import { Element } from "../../../utils/element"; +import { ev, PLAYER_EVENT } from "../../event"; /** 播放列表控制栏 */ @customElement('div') @@ -32,38 +33,41 @@ export class Filter extends HTMLDivElement { /** 每当元素被移动到新文档中时调用。 */ // adoptedCallback() {} + #player: Player; + /** 推荐视频 */ - $recommend = Element.add('button', { class: 'bofqi-auxiliary-filter-btn' }, this, '推荐视频'); + $recommend = Element.add('button', { class: 'bofqi-auxiliary-filter-btn', appendTo: this, innerText: '推荐视频' }); /** 弹幕列表 */ - private $danmaku = Element.add('button', { class: 'bofqi-auxiliary-filter-btn' }, this, '弹幕列表'); + #danmaku = Element.add('button', { class: 'bofqi-auxiliary-filter-btn', appendTo: this, innerText: '弹幕列表' }); /** 屏蔽设定 */ - private $block = Element.add('button', { class: 'bofqi-auxiliary-filter-btn' }, this, '屏蔽设定'); + #block = Element.add('button', { class: 'bofqi-auxiliary-filter-btn', appendTo: this, innerText: '屏蔽设定' }); - constructor() { + constructor(player: Player) { super(); + this.#player = player; this.classList.add('bofqi-auxiliary-filter'); ev.bind(PLAYER_EVENT.AUXILIARY_FILTER, ({ detail }) => { switch (detail) { case 1: { this.$recommend.classList.remove('active'); - this.$danmaku.classList.add('active'); - this.$block.classList.remove('active'); + this.#danmaku.classList.add('active'); + this.#block.classList.remove('active'); break; } case 2: { this.$recommend.classList.remove('active'); - this.$danmaku.classList.remove('active'); - this.$block.classList.add('active'); + this.#danmaku.classList.remove('active'); + this.#block.classList.add('active'); break; } default: { this.$recommend.classList.add('active'); - this.$danmaku.classList.remove('active'); - this.$block.classList.remove('active'); + this.#danmaku.classList.remove('active'); + this.#block.classList.remove('active'); break } } @@ -72,10 +76,10 @@ export class Filter extends HTMLDivElement { this.$recommend.addEventListener('click', () => { ev.trigger(PLAYER_EVENT.AUXILIARY_FILTER, 0); }); - this.$danmaku.addEventListener('click', () => { + this.#danmaku.addEventListener('click', () => { ev.trigger(PLAYER_EVENT.AUXILIARY_FILTER, 1); }); - this.$block.addEventListener('click', () => { + this.#block.addEventListener('click', () => { ev.trigger(PLAYER_EVENT.AUXILIARY_FILTER, 2); }); @@ -83,10 +87,5 @@ export class Filter extends HTMLDivElement { ev.one(PLAYER_EVENT.INITED, () => { ev.trigger(PLAYER_EVENT.AUXILIARY_FILTER, 0); }); - ev.bind(PLAYER_EVENT.IDENTIFY, this.identify); - } - - private identify = () => { - this.$recommend.textContent = '推荐视频'; } } \ No newline at end of file diff --git a/src/player/auxiliary/index.css b/src/player/auxiliary/index.css new file mode 100644 index 0000000..0028b3a --- /dev/null +++ b/src/player/auxiliary/index.css @@ -0,0 +1,43 @@ +@import url(./info/index.css); +@import url(./filter/index.css); +@import url(./recommend/index.css); +@import url(./danmaku/index.css); +@import url(./block/index.css); + +.bofqi-auxiliary { + flex-shrink: 0; + inline-size: 300px; + border-inline-start: 1px solid var(--e2e2e2); + overflow: clip; + display: flex; + flex-direction: column; + color: var(--99a2aa); + transition: all .3s allow-discrete; + anchor-name: --bofqi-auxiliary; + + @starting-style { + inline-size: 0; + } + + @container bofqi style(--screen-wide: 1) { + & { + inline-size: 0; + display: none; + } + } + + @container bofqi style(--fullscreen: 1) { + & { + inline-size: 0; + display: none; + } + } + + @container bofqi (inline-size < 780px) { + + & { + inline-size: 0; + display: none; + } + } +} \ No newline at end of file diff --git a/src/player/auxiliary/index.ts b/src/player/auxiliary/index.ts index e84c8d6..bc9e47c 100644 --- a/src/player/auxiliary/index.ts +++ b/src/player/auxiliary/index.ts @@ -1,12 +1,13 @@ +import { Player } from ".."; import { customElement } from "../../utils/Decorator/customElement"; -import { PLAYER_EVENT, ev } from "../event-target"; +import { ev, PLAYER_EVENT } from "../event"; import { Block } from "./block"; import { Danmaku } from "./danmaku"; import { Filter } from "./filter"; import { Info } from "./info"; import { Recommend } from "./recommend"; -/** 播放器右侧面板 */ +/** 播放器辅助区域 */ @customElement('div') export class Auxiliary extends HTMLDivElement { @@ -24,9 +25,6 @@ export class Auxiliary extends HTMLDivElement { */ // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} - /** 初始化标记 */ - // #inited = false; - /** 每当元素添加到文档中时调用。 */ // connectedCallback() { } @@ -36,43 +34,45 @@ export class Auxiliary extends HTMLDivElement { /** 每当元素被移动到新文档中时调用。 */ // adoptedCallback() {} - /** 播放信息栏 */ - $info = this.appendChild(new Info()); + #player: Player; - /** 播放列表控制栏 */ - $filter = this.appendChild(new Filter()); + $info: Info; - /** 当前播放列表 */ - $currentList?: HTMLDivElement; + $filter: Filter; - /** 推荐列表 */ $recommend = new Recommend(); - /** 弹幕列表 */ - $danmaku = new Danmaku(); + #danmaku: Danmaku; - /** 屏蔽列表 */ - $block = new Block(); + #block = new Block(); - constructor() { + /** 当前播放列表 */ + #currentList?: HTMLDivElement; + + constructor(player: Player) { super(); + this.#player = player; this.classList.add('bofqi-auxiliary'); + this.$info = this.appendChild(new Info(player)); + this.$filter = this.appendChild(new Filter(player)); + this.#danmaku = new Danmaku(player); + ev.bind(PLAYER_EVENT.AUXILIARY_FILTER, ({ detail }) => { switch (detail) { case 1: { - this.$currentList ? this.$currentList.replaceWith(this.$danmaku) : this.appendChild(this.$danmaku); - this.$currentList = this.$danmaku; + this.#currentList ? this.#currentList.replaceWith(this.#danmaku) : this.appendChild(this.#danmaku); + this.#currentList = this.#danmaku; break; } case 2: { - this.$currentList ? this.$currentList.replaceWith(this.$block) : this.appendChild(this.$block); - this.$currentList = this.$block; + this.#currentList ? this.#currentList.replaceWith(this.#block) : this.appendChild(this.#block); + this.#currentList = this.#block; break; } default: { - this.$currentList ? this.$currentList.replaceWith(this.$recommend) : this.appendChild(this.$recommend); - this.$currentList = this.$recommend; + this.#currentList ? this.#currentList.replaceWith(this.$recommend) : this.appendChild(this.$recommend); + this.#currentList = this.$recommend; break; } } diff --git a/src/player/auxiliary/info/index.css b/src/player/auxiliary/info/index.css new file mode 100644 index 0000000..d326553 --- /dev/null +++ b/src/player/auxiliary/info/index.css @@ -0,0 +1,230 @@ +.bofqi-info { + flex-shrink: 0; + block-size: 60px; + padding-inline: 20px; + display: flex; + align-items: center; + anchor-name: --bofqi-info; + + .bofqi-info-number { + align-content: center; + + >span { + &::before { + content: attr(data-num); + } + + &:first-child::before { + font-size: 18px; + font-weight: 700; + color: var(--222); + } + } + } + + >.bofqi-info-more { + position: absolute; + font-size: 24px; + inline-size: 30px; + aspect-ratio: 1; + inset-inline-end: calc(anchor(--bofqi-info end) + 6px); + anchor-name: --bofqi-more; + transition: all .3s; + + >svg { + block-size: 1em; + aspect-ratio: 1; + fill: var(--99a2aa); + transition: all .3s; + } + + &:hover { + background-color: var(--f4f5f7); + color: var(--6d757a); + + >svg { + fill: var(--6d757a); + } + } + } + + >.bofqi-more-wrap { + position-anchor: --bofqi-more; + position-area: block-end span-inline-start; + margin-block-start: 5px; + padding-block: 10px; + border: 1px solid var(--99a2aa); + border-radius: 4px; + box-shadow: 0 2px 1px var(--e2e2e2); + + >.bofqi-wrap-triangle { + position: absolute; + inset-block-start: -4px; + inset-inline-end: 10px; + inline-size: 6px; + block-size: 6px; + border: 1px solid var(--99a2aa); + rotate: 45deg; + border-block-end-color: var(--fafafa); + border-inline-end-color: var(--fafafa); + } + + >button { + block-size: 30px; + padding-block: 0; + padding-inline-end: 14px; + padding-inline-start: 22px; + cursor: pointer; + text-align: start; + color: var(--222); + transition: background-color .3s; + display: flex; + align-items: center; + justify-content: flex-start; + + &:disabled { + cursor: not-allowed; + color: var(--ccd0d7); + } + + &:hover { + background-color: var(--e5e9ef); + } + } + + &[popover]:popover-open:not(dialog) { + display: flex; + flex-direction: column; + align-items: stretch; + overflow: initial; + } + } + + >.bofqi-info-setting { + position: absolute; + font-size: 24px; + inline-size: 30px; + aspect-ratio: 1; + position-anchor: --bofqi-more; + position-area: inline-start; + transition: all .3s; + + >svg { + block-size: 1em; + aspect-ratio: 1; + fill: var(--99a2aa); + transition: all .3s; + } + + &:hover { + background-color: var(--f4f5f7); + color: var(--6d757a); + + >svg { + fill: var(--6d757a); + } + } + } + + >.bofqi-setting-wrap { + position-anchor: --bofqi-auxiliary; + position-area: center center; + inline-size: anchor-size(--bofqi-auxiliary inline); + block-size: anchor-size(--bofqi-auxiliary block); + scrollbar-width: thin; + + &[popover]:popover-open:not(dialog) { + display: flex; + flex-direction: column; + align-items: stretch; + overflow-x: hidden; + overflow-y: auto; + } + + >header { + flex-shrink: 0; + block-size: 60px; + color: var(--222); + font-size: 14px; + border-block-end: 1px solid var(--e2e2e2); + display: flex; + align-items: center; + padding-inline-start: 22px; + } + + >.bofqi-setting { + flex-grow: 1; + display: flex; + flex-direction: column; + align-items: stretch; + padding-inline-start: 22px; + padding-inline-end: 15px; + + >hr { + inline-size: 100%; + border-color: var(--e2e2e2); + } + + .bofqi-setting-title { + color: var(--222); + block-size: 50px; + font-weight: 700; + display: flex; + align-items: center; + } + + .bofqi-setting-content { + display: flex; + align-items: center; + block-size: 26px; + + &:not(.compact, :last-child) { + margin-block-end: 14px; + } + + > :last-child { + flex-grow: 1; + } + + &::before { + content: attr(data-label); + flex-shrink: 0; + inline-size: 72px; + color: var(--99a2aa); + margin-inline-end: 9px; + } + + >.bofqi-setting-font-border { + display: flex; + align-items: center; + + >label { + display: flex; + align-items: center; + block-size: 24px; + margin-inline: 4px; + padding-inline: 6px; + border-radius: 4px; + color: var(--222); + border: 1px solid transparent; + transition: all .3s; + cursor: pointer; + + &:has(input:checked) { + color: var(--00a1d6); + border-color: var(--00a1d6); + } + + >input { + appearance: none; + } + } + } + + >.bpui-select { + block-size: 100%; + } + } + } + } +} \ No newline at end of file diff --git a/src/player/auxiliary/info/index.ts b/src/player/auxiliary/info/index.ts index 8ea57ad..f030958 100644 --- a/src/player/auxiliary/info/index.ts +++ b/src/player/auxiliary/info/index.ts @@ -1,11 +1,10 @@ +import { Player } from "../.."; import { customElement } from "../../../utils/Decorator/customElement"; import { More } from "./more"; -import { MoreWrap } from "./more/wrap"; import { Number } from "./number"; import { Setting } from "./setting"; -import { SettingWrap } from "./setting/wrap"; -/** 播放信息栏 */ +/** 播放器信息区域 */ @customElement('div') export class Info extends HTMLDivElement { @@ -23,11 +22,8 @@ export class Info extends HTMLDivElement { */ // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} - /** 初始化标记 */ - // #inited = false; - /** 每当元素添加到文档中时调用。 */ - // connectedCallback() {} + // connectedCallback() { } /** 每当元素从文档中移除时调用。 */ // disconnectedCallback() {} @@ -35,26 +31,21 @@ export class Info extends HTMLDivElement { /** 每当元素被移动到新文档中时调用。 */ // adoptedCallback() {} - /** 弹幕数及播放人数信息 */ - $number = this.appendChild(new Number); - - /** 更多按钮 */ - $more = this.appendChild(new More()); + #player: Player; - /** 更多设置面板 */ - $moreWrap = this.appendChild(new MoreWrap()); + #number: Number; - /** 设置按钮 */ - $setting = this.appendChild(new Setting()); + #more: More; - /** 设置面版 */ - $settingWrap = this.appendChild(new SettingWrap()); + $setting: Setting; - constructor() { + constructor(player: Player) { super(); - this.classList.add('bofqi-auxiliary-info'); - this.$more.popoverTargetElement = this.$moreWrap; - this.$setting.popoverTargetElement = this.$settingWrap; + this.#player = player; + this.classList.add('bofqi-info'); + this.#number = this.appendChild(new Number(player)); + this.#more = this.appendChild(new More(player)); + this.$setting = this.appendChild(new Setting(player)); } } \ No newline at end of file diff --git a/src/player/auxiliary/info/more.ts b/src/player/auxiliary/info/more.ts new file mode 100644 index 0000000..6495082 --- /dev/null +++ b/src/player/auxiliary/info/more.ts @@ -0,0 +1,125 @@ +import { Player } from "../.."; +import svg_24more from "../../../assets/svg/24more.svg"; +import { toastr } from "../../../toastr"; +import { customElement } from "../../../utils/Decorator/customElement"; +import { Element } from "../../../utils/element"; + +/** 播放器更多设置 */ +@customElement('button') +export class More extends HTMLButtonElement { + + /** + * 需要监听变动的属性。 + * 与实例方法`attributeChangedCallback`配合使用。 + * 此字符串序列定义了`attributeChangedCallback`回调时的第一个参数的可能值。 + */ + // static observedAttributes = []; + + /** + * 在属性更改、添加、移除或替换时调用。 + * 需要与静态属性`observedAttributes`配合使用。 + * 此回调的第一个参数在`observedAttributes`数组中定义。 + */ + // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} + + /** 每当元素添加到文档中时调用。 */ + connectedCallback() { + this.insertAdjacentElement('afterend', this.#wrap); + } + + /** 每当元素从文档中移除时调用。 */ + disconnectedCallback() { + this.#wrap.remove(); + } + + /** 每当元素被移动到新文档中时调用。 */ + // adoptedCallback() {} + + #player: Player; + + #wrap = Element.add('div', { class: 'bofqi-more-wrap', innerHTML: '
    ' }); + + constructor(player: Player) { + super(); + + this.#player = player; + this.classList.add('bofqi-info-more'); + this.innerHTML = svg_24more; + + this.#wrap.popover = 'auto'; + this.popoverTargetElement = this.#wrap; + + this.#wrap.addEventListener('click', () => this.#wrap.hidePopover()); + + this.#add( + { + text: '加载文件', callback: () => { + toastr.info('请选择要打开的视频、弹幕或字幕文件'); + showOpenFilePicker({ + multiple: true, + types: [ + { + description: '所有媒体', + accept: { + 'application/bofqi': ['.mp4', '.flv', '.json', '.xml', '.vtt', '.gz'], + } + }, + { + description: "MP4", + accept: { + 'video/mp4': ['.mp4', '.flv'], + } + }, + { + description: "弹幕", + accept: { + 'application/danmaku+json': ['.json'], + 'application/danmaku+xml': ['.xml'], + 'application/danmkau+gzip': ['.gz'], + } + }, + { + description: "字幕", + accept: { + 'text/vtt': ['.vtt'], + 'application/vtt+gzip': ['.gz'], + } + } + ], + }) + .then(d => Promise.all(d.map(d => d.getFile()))) + .then(player.fileHandle) + .catch(e => { + toastr.error(e); + console.error(e); + }); + } + }, + { text: '高级弹幕' }, + { text: 'HTML5播放器', disable: true }, + { text: 'Flash播放器', disable: true } + ); + } + + /** 添加选项 */ + #add(...items: Item[]) { + const f = document.createDocumentFragment(); + items.forEach(({ text, disable, callback }) => { + const button = document.createElement('button'); + button.textContent = text; + disable && (button.disabled = true); + callback && button.addEventListener('click', callback); + f.appendChild(button); + }); + this.#wrap.appendChild(f); + } +} + +interface Item { + /** 按钮文字 */ + text: string; + /** 是否禁用 */ + disable?: boolean; + /** 点击回调 */ + callback?: () => void; +} \ No newline at end of file diff --git a/src/player/auxiliary/info/more/wrap.ts b/src/player/auxiliary/info/more/wrap.ts deleted file mode 100644 index e91f355..0000000 --- a/src/player/auxiliary/info/more/wrap.ts +++ /dev/null @@ -1,98 +0,0 @@ -import { customElement } from "../../../../utils/Decorator/customElement"; -import { Element } from "../../../../utils/element"; -import { ev, PLAYER_EVENT } from "../../../event-target"; - -/** 更多设置面板 */ -@customElement('div') -export class MoreWrap extends HTMLDivElement { - - /** - * 需要监听变动的属性。 - * 与实例方法`attributeChangedCallback`配合使用。 - * 此字符串序列定义了`attributeChangedCallback`回调时的第一个参数的可能值。 - */ - // static observedAttributes = []; - - /** - * 在属性更改、添加、移除或替换时调用。 - * 需要与静态属性`observedAttributes`配合使用。 - * 此回调的第一个参数在`observedAttributes`数组中定义。 - */ - // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} - - /** 初始化标记 */ - // #inited = false; - - /** 每当元素添加到文档中时调用。 */ - // connectedCallback() {} - - /** 每当元素从文档中移除时调用。 */ - // disconnectedCallback() {} - - /** 每当元素被移动到新文档中时调用。 */ - // adoptedCallback() {} - - /** 位置指示器 */ - $triangle = Element.add('div', { class: 'bofqi-auxiliary-setting-more-triangle' }, this); - - /** 加载文件 */ - $loadFiles = Element.add('button', { class: 'bofqi-auxiliary-setting-more-list' }, this, '加载文件'); - - /** 高级弹幕 */ - $danmaku7 = Element.add('button', { class: 'bofqi-auxiliary-setting-more-list' }, this, '高级弹幕'); - - /** HTML5播放器 */ - $html5 = Element.add('button', { class: 'bofqi-auxiliary-setting-more-list' }, this, 'HTML5播放器'); - - /** Flash播放器 */ - $flash = Element.add('button', { class: 'bofqi-auxiliary-setting-more-list' }, this, 'Flash播放器'); - - constructor() { - super(); - - this.classList.add('bofqi-auxiliary-setting-more'); - this.popover = 'auto'; - - this.$html5.disabled = true; - this.$flash.disabled = true; - - this.$loadFiles.addEventListener('click', () => { - showOpenFilePicker({ - multiple: true, - types: [ - { - description: '所有媒体', - accept: { - 'application/bofqi': ['.mp4', '.json', '.xml', '.vtt', '.gz'], - } - }, - { - description: "MP4", - accept: { - 'video/mp4': ['.mp4'], - } - }, - { - description: "弹幕", - accept: { - 'application/danmaku+json': ['.json'], - 'application/danmaku+xml': ['.xml'], - 'application/danmkau+gzip': ['.gz'], - } - }, - { - description: "字幕", - accept: { - 'text/vtt': ['.vtt'], - 'application/vtt+gzip': ['.gz'], - } - } - ], - }) - .then(d => Promise.all(d.map(d => d.getFile()))) - .then(d => { - ev.trigger(PLAYER_EVENT.LOCAL_FILE_LOAD, d); - }); - }); - } -} \ No newline at end of file diff --git a/src/player/auxiliary/info/number.ts b/src/player/auxiliary/info/number.ts index 010fc4b..f0213f6 100644 --- a/src/player/auxiliary/info/number.ts +++ b/src/player/auxiliary/info/number.ts @@ -1,7 +1,8 @@ +import { Player } from "../.."; import { customElement } from "../../../utils/Decorator/customElement"; -import { PLAYER_EVENT, ev } from "../../event-target"; +import { ev, PLAYER_EVENT } from "../../event"; -/** 弹幕数及播放人数信息 */ +/** 播放器数据 */ @customElement('div') export class Number extends HTMLDivElement { @@ -19,11 +20,8 @@ export class Number extends HTMLDivElement { */ // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} - /** 初始化标记 */ - // #inited = false; - /** 每当元素添加到文档中时调用。 */ - // connectedCallback() {} + // connectedCallback() { } /** 每当元素从文档中移除时调用。 */ // disconnectedCallback() {} @@ -31,38 +29,48 @@ export class Number extends HTMLDivElement { /** 每当元素被移动到新文档中时调用。 */ // adoptedCallback() {} - #count = 0; + #player: Player; - /** 观看人数 */ - set $count(v: number) { - this.dataset.count = `${(this.#count = v) || '-'}`; - } + #watch = this.appendChild(document.createElement('span')); + + #danmaku = this.appendChild(document.createElement('span')); - #danmaku = 0; + #dm = 0; + + /** 设定观看人数 */ + set $watch(v: number | string) { + this.#watch.dataset.num = v || '-'; + } - /** 弹幕数 */ + /** 设定弹幕数 */ set $danmaku(v: number) { - this.dataset.danmaku = `${(this.#danmaku += v) || '-'}条弹幕`; + this.#danmaku.dataset.num = (this.#dm += v) || '-'; } - constructor() { + constructor(player: Player) { super(); - this.classList.add('bofqi-auxiliary-info-number'); - - this.textContent = '人正在观看,'; - this.dataset.count = '-'; - this.dataset.danmaku = '-条弹幕'; + this.#player = player; + this.classList.add('bofqi-info-number'); + this.#watch.textContent = '人正在观看,'; + this.#danmaku.textContent = '条弹幕'; ev.bind(PLAYER_EVENT.DANMAKU_ADD, ({ detail }) => { this.$danmaku = detail.length; }); - ev.bind(PLAYER_EVENT.DANMAKU_IDENTIFY, this.identify); - ev.bind(PLAYER_EVENT.IDENTIFY, this.identify); - } + ev.bind(PLAYER_EVENT.DANMAKU_IDENTIFY, () => { + this.#dm = 0; + this.$danmaku = 0; + }); + ev.bind(PLAYER_EVENT.OTHER_IDENTITY, () => { + this.$watch = 0; + }); + ev.bind(PLAYER_EVENT.ONLINE_NUMBER, ({ detail }) => { + const { count } = detail; + this.$watch = count; + }); - private identify = () => { - this.$count = 0; - this.$danmaku = this.#danmaku = 0; + this.$danmaku = 0; + this.$watch = 0; } } \ No newline at end of file diff --git a/src/player/auxiliary/info/setting.ts b/src/player/auxiliary/info/setting.ts new file mode 100644 index 0000000..1b780a4 --- /dev/null +++ b/src/player/auxiliary/info/setting.ts @@ -0,0 +1,318 @@ +import { Player } from "../.."; +import svg_24setting from "../../../assets/svg/24setting.svg"; +import { toastr } from "../../../toastr"; +import { customElement } from "../../../utils/Decorator/customElement"; +import { Element } from "../../../utils/element"; +import { ev, PLAYER_EVENT } from "../../event"; +import { options } from "../../option"; +import { Checkbox } from "../../widget/checkbox"; +import { Slider } from "../../widget/slider"; + +/** 播放器设置 */ +@customElement('button') +export class Setting extends HTMLButtonElement { + + /** + * 需要监听变动的属性。 + * 与实例方法`attributeChangedCallback`配合使用。 + * 此字符串序列定义了`attributeChangedCallback`回调时的第一个参数的可能值。 + */ + // static observedAttributes = []; + + /** + * 在属性更改、添加、移除或替换时调用。 + * 需要与静态属性`observedAttributes`配合使用。 + * 此回调的第一个参数在`observedAttributes`数组中定义。 + */ + // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} + + /** 每当元素添加到文档中时调用。 */ + connectedCallback() { + this.insertAdjacentElement('afterend', this.#wrap); + } + + /** 每当元素从文档中移除时调用。 */ + disconnectedCallback() { + this.#wrap.remove(); + } + + /** 每当元素被移动到新文档中时调用。 */ + // adoptedCallback() {} + + #player: Player; + + #wrap = Element.add('div', { class: 'bofqi-setting-wrap' }); + + #header = Element.add('header', { appendTo: this.#wrap, innerText: '播放器设置' }); + + $list = Element.add('div', { class: 'bofqi-setting', appendTo: this.#wrap }); + + #danamku = Element.add('div', { class: 'bofqi-setting-title', appendTo: this.$list, innerText: '弹幕设置' }); + + #opacityContent = Element.add('div', { class: 'bofqi-setting-content', appendTo: this.$list, data: { label: '弹幕不透明度' } }); + /** 弹幕不透明度 */ + #opacity = this.#opacityContent.appendChild(new Slider()); + + #speedPlusContent = Element.add('div', { class: 'bofqi-setting-content', appendTo: this.$list, data: { label: '弹幕速度' } }); + /** 弹幕速度 */ + #speedPlus = this.#speedPlusContent.appendChild(new Slider()); + + #danmakuNumberContent = Element.add('div', { class: 'bofqi-setting-content', appendTo: this.$list, data: { label: '同屏弹幕密度' } }); + /** 同屏弹幕密度 */ + #danmakuNumber = this.#danmakuNumberContent.appendChild(new Slider()); + + #fontSizeContent = Element.add('div', { class: 'bofqi-setting-content', appendTo: this.$list, data: { label: '字号缩放' } }); + /** 字号缩放 */ + #fontSize = this.#fontSizeContent.appendChild(new Slider()); + + #fullScreenSyncContent = Element.add('div', { class: 'bofqi-setting-content', appendTo: this.$list }); + /** 弹幕等比缩放 */ + #fullScreenSync = this.#fullScreenSyncContent.appendChild(new Checkbox()); + + #mode7Content = Element.add('div', { class: 'bofqi-setting-content', appendTo: this.$list }); + /** mode7 自适应 */ + #mode7 = this.#mode7Content.appendChild(new Checkbox()); + + #fontBorderSyncContent = Element.add('div', { class: 'bofqi-setting-content', appendTo: this.$list, data: { label: '边框样式' } }); + /** 边框样式 */ + #fontBorder = Element.add('form', { class: 'bofqi-setting-font-border', appendTo: this.#fontBorderSyncContent }); + + #fontFamilySyncContent = Element.add('div', { class: 'bofqi-setting-content', appendTo: this.$list, data: { label: '弹幕字体' } }); + /** 弹幕字体 */ + #fontFamily = Element.add('select', { class: 'bpui-select', appendTo: this.#fontFamilySyncContent }); + + #sameAsPanelContent = Element.add('div', { class: ['bofqi-setting-content', 'compact'], appendTo: this.$list }); + /** 应用到界面字体 */ + #sameAsPanel = this.#sameAsPanelContent.appendChild(new Checkbox()); + + #boldContent = Element.add('div', { class: ['bofqi-setting-content', 'compact'], appendTo: this.$list }); + /** 粗体 */ + #bold = this.#boldContent.appendChild(new Checkbox()); + + #preventShadeContent = Element.add('div', { class: 'bofqi-setting-content', appendTo: this.$list }); + /** 防挡字幕 */ + #preventShade = this.#preventShadeContent.appendChild(new Checkbox()); + + #typeContent = Element.add('div', { class: 'bofqi-setting-content', appendTo: this.$list, data: { label: '渲染类型' } }); + /** 渲染类型 */ + #type = Element.add('select', { class: 'bpui-select', appendTo: this.#typeContent }); + + #speedSyncContent = Element.add('div', { class: 'bofqi-setting-content', appendTo: this.$list }); + /** 弹幕速度同步播放倍速 */ + #speedSync = this.#speedSyncContent.appendChild(new Checkbox()); + + constructor(player: Player) { + super(); + + this.#player = player; + this.classList.add('bofqi-info-setting'); + this.innerHTML = svg_24setting; + + this.#wrap.popover = 'auto'; + this.popoverTargetElement = this.#wrap; + + this.#opacity.$hint = true; + this.#opacity.formatHint(v => { + return Math.floor(+v) + '%'; + }); + this.#speedPlus.$max = 200; + this.#speedPlus.$step = 10; + this.#speedPlus.$hint = true; + this.#speedPlus.formatHint(v => { + return Math.floor(+v) + '%'; + }); + this.#danmakuNumber.$max = 105; + this.#danmakuNumber.$hint = true; + this.#danmakuNumber.formatHint(v => { + switch (+v) { + case 101: return '200'; + case 102: return '300'; + case 103: return '400'; + case 104: return '500'; + case 105: return '无限制'; + default: return v; + } + }); + this.#fontSize.$max = 200; + this.#fontSize.$step = 10; + this.#fontSize.$hint = true; + this.#fontSize.formatHint(v => { + return Math.floor(+v) + '%'; + }); + this.#fullScreenSync.$text = '弹幕等比缩放'; + this.#mode7.$text = '高级弹幕(mode=7)自适应'; + const id = crypto.randomUUID(); + this.#fontBorder.innerHTML = ` + +`; + this.#fontFamily.innerHTML = ` + + + + +`; + this.#sameAsPanel.$text = '应用到界面字体'; + this.#sameAsPanel.$disabled = true; + this.#bold.$text = '粗体'; + this.#preventShade.$text = '防挡字幕'; + this.#type.innerHTML = ``; + this.#type.disabled = true; + this.#speedSync.$text = '弹幕速度同步播放倍速'; + + ev.one(PLAYER_EVENT.OPTINOS_CHANGE, ({ detail }) => { + const { fontFamily, fontFamilyCustom } = detail.danmaku; + if (fontFamilyCustom) { + const [fullName, family] = fontFamilyCustom; + const option = document.createElement('option'); + option.style.fontFamily = family; + option.value = family; + option.text = fullName; + this.#fontFamily.appendChild(option); + this.#fontFamily.value = family; + } + this.#fontFamily.value = fontFamily || '默认'; + }); + ev.bind(PLAYER_EVENT.OPTINOS_CHANGE, ({ detail }) => { + const { opacity, speedPlus, danmakuNumber, fontSize, fullScreenSync, fontBorder, fontFamily, bold, preventShade, speedSync, mode7Scale } = detail.danmaku; + + this.#opacity.$value = Math.max(1, Math.min(100, opacity * 100)); + this.#speedPlus.$value = Math.max(10, Math.min(200, speedPlus * 100)); + switch (danmakuNumber) { + case 0: { + this.#danmakuNumber.$value = 105; + break; + } + case 200: { + this.#danmakuNumber.$value = 101; + break; + } + case 300: { + this.#danmakuNumber.$value = 102; + break; + } + case 400: { + this.#danmakuNumber.$value = 103; + break; + } + case 500: { + this.#danmakuNumber.$value = 104; + break; + } + default: { + this.#danmakuNumber.$value = Math.max(1, Math.min(100, danmakuNumber)); + break; + } + } + this.#fontSize.$value = Math.max(10, Math.min(200, fontSize * 100)); + this.#fullScreenSync.$value = fullScreenSync; + this.#mode7.$value = mode7Scale; + const fb = this.#fontBorder.querySelector(`[value="${fontBorder}"]`); + fb && (fb.checked = true); + this.#fontFamily.value = fontFamily || '默认'; + this.#bold.$value = bold; + this.#preventShade.$value = preventShade; + this.#speedSync.$value = speedSync; + }); + + this.#opacity.addEventListener('change', () => { + options.danmaku.opacity = Math.max(0.01, Math.min(1, +this.#opacity.$value / 100)); + }); + this.#speedPlus.addEventListener('change', () => { + options.danmaku.speedPlus = Math.max(0.1, Math.min(2, +this.#speedPlus.$value / 100)); + }); + this.#danmakuNumber.addEventListener('change', () => { + const value = this.#danmakuNumber.$value; + switch (value) { + case 101: { + options.danmaku.danmakuNumber = 200; + break; + } + case 102: { + options.danmaku.danmakuNumber = 300; + break; + } + case 103: { + options.danmaku.danmakuNumber = 400; + break; + } + case 104: { + options.danmaku.danmakuNumber = 500; + break; + } + case 105: { + options.danmaku.danmakuNumber = 0; + break; + } + default: { + options.danmaku.danmakuNumber = Math.max(1, Math.min(100, +value)); + break; + } + } + }); + this.#fontSize.addEventListener('change', () => { + options.danmaku.fontSize = Math.max(0.1, Math.min(2, +this.#fontSize.$value / 100)); + }); + this.#fullScreenSync.addEventListener('change', () => { + options.danmaku.fullScreenSync = this.#fullScreenSync.$value; + }); + this.#mode7Content.addEventListener('change', () => { + options.danmaku.mode7Scale = this.#mode7.$value; + }); + this.#fontBorder.addEventListener('change', () => { + const form = new FormData(this.#fontBorder); + options.danmaku.fontBorder = +[...form.values()][0]; + }); + this.#fontFamily.addEventListener('change', () => { + // 更改字体 + if (this.#fontFamily.value === '...') { + this.#fontFamily.value = options.danmaku.fontFamily; + queryLocalFonts() + .then(d => { + const popover = document.createElement('div'); + popover.popover = 'auto'; + popover.classList.add('popover-local-font'); + this.append(popover); + d.forEach(({ fullName, family }) => { + if (/[\p{L}&&\p{Script=Han}]/v.test(fullName)) { + const button = document.createElement('button'); + button.textContent = fullName; + button.style.fontFamily = family; + button.addEventListener('click', e => { + e.stopPropagation(); + options.danmaku.fontFamilyCustom = [fullName, family]; + options.danmaku.fontFamily = family; + const option = document.createElement('option'); + option.style.fontFamily = family; + option.value = family; + option.text = fullName; + this.#fontFamily.appendChild(option); + this.#fontFamily.value = family; + popover.hidePopover(); + }) + popover.appendChild(button); + } + }); + popover.addEventListener('toggle', () => { + popover.matches(":popover-open") || popover.remove(); + }); + popover.showPopover(); + }) + .catch(e => { + toastr.error(e); + console.log(e); + }); + } else { + options.danmaku.fontFamily = this.#fontFamily.value; + } + }); + this.#bold.addEventListener('change', () => { + options.danmaku.bold = this.#bold.$value; + }); + this.#preventShade.addEventListener('change', () => { + options.danmaku.preventShade = this.#preventShade.$value; + }); + this.#speedSync.addEventListener('change', () => { + options.danmaku.speedSync = this.#speedSync.$value; + }); + } +} \ No newline at end of file diff --git a/src/player/auxiliary/info/setting/wrap.ts b/src/player/auxiliary/info/setting/wrap.ts deleted file mode 100644 index 8872119..0000000 --- a/src/player/auxiliary/info/setting/wrap.ts +++ /dev/null @@ -1,315 +0,0 @@ -import { DANMAKU } from "../../../../danmaku/block"; -import { customElement } from "../../../../utils/Decorator/customElement"; -import { Element } from "../../../../utils/element"; -import { ev, PLAYER_EVENT } from "../../../event-target"; -import { options } from "../../../option"; -import { POLICY } from "../../../policy"; -import { Checkbox } from "../../../widget/checkbox"; -import { Slider } from "../../../widget/slider"; - -/** 设置面版 */ -@customElement('div') -export class SettingWrap extends HTMLDivElement { - - /** - * 需要监听变动的属性。 - * 与实例方法`attributeChangedCallback`配合使用。 - * 此字符串序列定义了`attributeChangedCallback`回调时的第一个参数的可能值。 - */ - // static observedAttributes = []; - - /** - * 在属性更改、添加、移除或替换时调用。 - * 需要与静态属性`observedAttributes`配合使用。 - * 此回调的第一个参数在`observedAttributes`数组中定义。 - */ - // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} - - /** 初始化标记 */ - // #inited = false; - - /** 每当元素添加到文档中时调用。 */ - // connectedCallback() {} - - /** 每当元素从文档中移除时调用。 */ - // disconnectedCallback() {} - - /** 每当元素被移动到新文档中时调用。 */ - // adoptedCallback() {} - - private $header = Element.add('header', undefined, this, '播放器设置'); - - private $list = Element.add('div', { class: 'bofqi-setting' }, this); - - private $danamku = Element.add('div', { class: 'bofqi-setting-title' }, this.$list, '弹幕设置'); - - private $opacityContent = Element.add('div', { class: 'bofqi-setting-content', 'data-label': '弹幕不透明度' }, this.$list); - /** 弹幕不透明度 */ - private $opacity = this.$opacityContent.appendChild(new Slider()); - - private $speedPlusContent = Element.add('div', { class: 'bofqi-setting-content', 'data-label': '弹幕速度' }, this.$list); - /** 弹幕速度 */ - private $speedPlus = this.$speedPlusContent.appendChild(new Slider()); - - private $danmakuNumberContent = Element.add('div', { class: 'bofqi-setting-content', 'data-label': '同屏弹幕密度' }, this.$list); - /** 同屏弹幕密度 */ - private $danmakuNumber = this.$danmakuNumberContent.appendChild(new Slider()); - - private $fontSizeContent = Element.add('div', { class: 'bofqi-setting-content', 'data-label': '字号缩放' }, this.$list); - /** 字号缩放 */ - private $fontSize = this.$fontSizeContent.appendChild(new Slider()); - - private $fullScreenSyncContent = Element.add('div', { class: 'bofqi-setting-content' }, this.$list); - /** 弹幕等比缩放 */ - private $fullScreenSync = this.$fullScreenSyncContent.appendChild(new Checkbox()); - - private $fontBorderSyncContent = Element.add('div', { class: 'bofqi-setting-content', 'data-label': '边框样式' }, this.$list); - /** 边框样式 */ - private $fontBorder = Element.add('form', { class: 'bofqi-setting-font-border' }, this.$fontBorderSyncContent); - - private $fontFamilySyncContent = Element.add('div', { class: 'bofqi-setting-content', 'data-label': '弹幕字体' }, this.$list); - /** 弹幕字体 */ - private $fontFamily = Element.add('select', { class: 'bpui-select' }, this.$fontFamilySyncContent); - - private $sameAsPanelContent = Element.add('div', { class: 'bofqi-setting-content compact' }, this.$list); - /** 应用到界面字体 */ - private $sameAsPanel = this.$sameAsPanelContent.appendChild(new Checkbox()); - - private $boldContent = Element.add('div', { class: 'bofqi-setting-content compact' }, this.$list); - /** 粗体 */ - private $bold = this.$boldContent.appendChild(new Checkbox()); - - private $preventShadeContent = Element.add('div', { class: 'bofqi-setting-content' }, this.$list); - /** 防挡字幕 */ - private $preventShade = this.$preventShadeContent.appendChild(new Checkbox()); - - private $typeContent = Element.add('div', { class: 'bofqi-setting-content', 'data-label': '渲染类型' }, this.$list); - /** 渲染类型 */ - private $type = Element.add('select', { class: 'bpui-select' }, this.$typeContent); - - private $speedSyncContent = Element.add('div', { class: 'bofqi-setting-content' }, this.$list); - /** 弹幕速度同步播放倍速 */ - private $speedSync = this.$speedSyncContent.appendChild(new Checkbox()); - - private $player = Element.add('div', { class: 'bofqi-setting-title' }, this.$list, '播放器设置'); - - private $policyContent = Element.add('div', { class: 'bofqi-setting-content', 'data-label': '渲染类型' }, this.$list); - /** 渲染类型 */ - private $policy = Element.add('select', { class: 'bpui-select' }, this.$policyContent); - - private $incognitoContent = Element.add('div', { class: 'bofqi-setting-content' }, this.$list); - /** 弹幕速度同步播放倍速 */ - private $incognito = this.$incognitoContent.appendChild(new Checkbox()); - - constructor() { - super(); - - this.classList.add('bofqi-auxiliary-setting'); - this.popover = 'auto'; - - this.$opacity.min = '1'; - this.$opacity.$hint = true; - this.$opacity.formatHint(v => { - return Math.floor(+v) + '%'; - }); - this.$speedPlus.min = '10'; - this.$speedPlus.max = '200'; - this.$speedPlus.step = '10'; - this.$speedPlus.$hint = true; - this.$speedPlus.formatHint(v => { - return Math.floor(+v) + '%'; - }); - this.$danmakuNumber.min = '1'; - this.$danmakuNumber.max = '105'; - this.$danmakuNumber.$hint = true; - this.$danmakuNumber.formatHint(v => { - switch (v) { - case '101': return '200'; - case '102': return '300'; - case '103': return '400'; - case '104': return '500'; - case '105': return '无限制'; - default: return v; - } - }); - this.$fontSize.min = '10'; - this.$fontSize.max = '200'; - this.$fontSize.step = '10'; - this.$fontSize.$hint = true; - this.$fontSize.formatHint(v => { - return Math.floor(+v) + '%'; - }); - this.$fullScreenSync.$text = '弹幕等比缩放'; - const id = crypto.randomUUID(); - this.$fontBorder.innerHTML = ` - -`; - this.$fontFamily.innerHTML = ` - - - - -`; - this.$sameAsPanel.$text = '应用到界面字体'; - this.$sameAsPanel.$disabled = true; - this.$bold.$text = '粗体'; - this.$preventShade.$text = '防挡字幕'; - this.$type.innerHTML = ``; - this.$type.disabled = true; - this.$speedSync.$text = '弹幕速度同步播放倍速'; - this.$player.insertAdjacentHTML('beforebegin', '
    '); - this.$policy.innerHTML = ` - -`; - this.$incognito.$text = '无痕模式'; - - ev.one(PLAYER_EVENT.OPTINOS_CHANGE, ({ detail }) => { - const { fontFamily, fontFamilyCustom } = detail.danmaku; - if (fontFamilyCustom) { - const [fullName, family] = fontFamilyCustom; - Element.add('option', { style: `font-family: ${family}`, value: family }, this.$fontFamily, fullName); - this.$fontFamily.value = family; - } - this.$fontFamily.value = fontFamily || '默认'; - }); - ev.bind(PLAYER_EVENT.OPTINOS_CHANGE, ({ detail }) => { - const { opacity, speedPlus, danmakuNumber, fontSize, fullScreenSync, fontBorder, fontFamily, bold, preventShade, speedSync } = detail.danmaku; - - this.$opacity.$value = Math.max(1, Math.min(100, opacity * 100)); - this.$speedPlus.$value = Math.max(10, Math.min(200, speedPlus * 100)); - switch (danmakuNumber) { - case 0: { - this.$danmakuNumber.$value = '105'; - break; - } - case 200: { - this.$danmakuNumber.$value = '101'; - break; - } - case 300: { - this.$danmakuNumber.$value = '102'; - break; - } - case 400: { - this.$danmakuNumber.$value = '103'; - break; - } - case 500: { - this.$danmakuNumber.$value = '104'; - break; - } - default: { - this.$danmakuNumber.$value = Math.max(1, Math.min(100, danmakuNumber)); - break; - } - } - this.$fontSize.$value = Math.max(10, Math.min(200, fontSize * 100)); - this.$fullScreenSync.$value = fullScreenSync; - const fb = this.$fontBorder.querySelector(`[value="${fontBorder}"]`); - fb && (fb.checked = true); - this.$fontFamily.value = fontFamily || '默认'; - this.$bold.$value = bold; - this.$preventShade.$value = preventShade; - this.$speedSync.$value = speedSync; - - const { policy, incognito } = detail.player; - this.$policy.value = policy; - this.$incognito.$value = incognito; - }); - - this.$opacity.addEventListener('change', () => { - options.danmaku.opacity = Math.max(0.01, Math.min(1, +this.$opacity.$value / 100)); - }); - this.$speedPlus.addEventListener('change', () => { - options.danmaku.speedPlus = Math.max(0.1, Math.min(2, +this.$speedPlus.$value / 100)); - }); - this.$danmakuNumber.addEventListener('change', () => { - const value = this.$danmakuNumber.$value; - switch (value) { - case '101': { - options.danmaku.danmakuNumber = 200; - break; - } - case '102': { - options.danmaku.danmakuNumber = 300; - break; - } - case '103': { - options.danmaku.danmakuNumber = 400; - break; - } - case '104': { - options.danmaku.danmakuNumber = 500; - break; - } - case '105': { - options.danmaku.danmakuNumber = 0; - break; - } - default: { - options.danmaku.danmakuNumber = Math.max(1, Math.min(100, +value)); - break; - } - } - }); - this.$fontSize.addEventListener('change', () => { - options.danmaku.fontSize = Math.max(0.1, Math.min(2, +this.$fontSize.$value / 100)); - }); - this.$fullScreenSync.addEventListener('change', () => { - options.danmaku.fullScreenSync = this.$fullScreenSync.$value; - }); - this.$fontBorder.addEventListener('change', () => { - const form = new FormData(this.$fontBorder); - options.danmaku.fontBorder = +[...form.values()][0]; - }); - this.$fontFamily.addEventListener('change', () => { - // 更改字体 - if (this.$fontFamily.value === '...') { - this.$fontFamily.value = options.danmaku.fontFamily; - queryLocalFonts().then(d => { - const popover = document.createElement('div'); - popover.popover = 'auto'; - popover.classList.add('popover-local-font'); - this.append(popover); - d.forEach(d => { - if (/[\p{L}&&\p{Script=Han}]/v.test(d.fullName)) { - const button = document.createElement('button'); - button.textContent = d.fullName; - button.style.fontFamily = `"${d.family}"`; - button.addEventListener('click', e => { - e.stopPropagation(); - options.danmaku.fontFamilyCustom = [d.fullName, d.family]; - options.danmaku.fontFamily = d.family; - Element.add('option', { style: `font-family: ${d.family}`, value: d.family }, this.$fontFamily, d.fullName); - this.$fontFamily.value = d.family; - popover.hidePopover(); - }) - popover.appendChild(button); - } - }); - popover.addEventListener('toggle', () => { - popover.matches(":popover-open") || popover.remove(); - }); - popover.showPopover(); - }); - } else { - options.danmaku.fontFamily = this.$fontFamily.value; - } - }); - this.$bold.addEventListener('change', () => { - options.danmaku.bold = this.$bold.$value; - }); - this.$preventShade.addEventListener('change', () => { - options.danmaku.preventShade = this.$preventShade.$value; - }); - this.$speedSync.addEventListener('change', () => { - options.danmaku.speedSync = this.$speedSync.$value; - }); - this.$policy.addEventListener('change', () => { - options.player.policy = +this.$policy.value; - }); - this.$incognito.addEventListener('change', () => { - options.player.incognito = this.$incognito.$value; - }); - } -} \ No newline at end of file diff --git a/src/player/style/auxiliary/recommend/index.css b/src/player/auxiliary/recommend/index.css similarity index 89% rename from src/player/style/auxiliary/recommend/index.css rename to src/player/auxiliary/recommend/index.css index a5f8f03..bd06f84 100644 --- a/src/player/style/auxiliary/recommend/index.css +++ b/src/player/auxiliary/recommend/index.css @@ -16,7 +16,7 @@ &::after { content: '暂无相关视频推荐'; - color: black; + color: var(--000); } } @@ -52,8 +52,8 @@ position: absolute; inset-inline-end: anchor(--recommend-cover end); inset-block-end: anchor(--recommend-cover end); - color: white; - background-color: #0006; + color: var(--fff); + background-color: var(--0006); padding-inline: 4px; border-start-start-radius: 4px; border-end-end-radius: 4px; @@ -75,13 +75,13 @@ white-space: pre-wrap; text-overflow: ellipsis; overflow: hidden; - color: #222; + color: var(--222); display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; &:hover { - color: #00a1d6; + color: var(--00a1d6); } } @@ -96,6 +96,8 @@ >svg { padding-inline-end: 6px; + block-size: 1em; + fill: var(--99a2aa); } &:first-child, @@ -112,12 +114,12 @@ content: attr(data-index); position: absolute; position-anchor: --recommend-left; - inset-area: inline-start; + position-area: inline-start; display: flex; align-items: center; justify-content: center; inline-size: 20px; - color: black; + color: var(--000); } &.selected { @@ -125,7 +127,7 @@ &::before { content: "▶"; - color: #00a1d6; + color: var(--00a1d6); } } diff --git a/src/player/auxiliary/recommend/index.ts b/src/player/auxiliary/recommend/index.ts index 568fbf7..b3ac43e 100644 --- a/src/player/auxiliary/recommend/index.ts +++ b/src/player/auxiliary/recommend/index.ts @@ -1,10 +1,10 @@ +import svg_12icondanmu from "../../../assets/svg/12icondanmu.svg"; +import svg_12iconplayed from "../../../assets/svg/12iconplayed.svg"; +import svg_12up from "../../../assets/svg/12up.svg"; import { customElement } from "../../../utils/Decorator/customElement"; import { Format } from "../../../utils/fomat"; import { https } from "../../../utils/https"; -import svg_icon_danmaku from "../../assets/svg/icon-danmaku.svg"; -import svg_icon_played from "../../assets/svg/icon-played.svg"; -import svg_upper from "../../assets/svg/upper.svg"; -import { PLAYER_EVENT, ev } from "../../event-target"; +import { ev, PLAYER_EVENT } from "../../event"; /** 推荐列表 */ @customElement('div') @@ -36,35 +36,34 @@ export class Recommend extends HTMLDivElement { /** 每当元素被移动到新文档中时调用。 */ // adoptedCallback() {} - private $items: IRecommend[] = []; + #items: IRecommend[] = []; - private $startIndex = 0; + #startIndex = 0; - private $seeLength = 0; + #seeLength = 0; constructor() { super(); this.classList.add('bofqi-auxiliary-recommend'); - ev.bind(PLAYER_EVENT.IDENTIFY, this.identify); - + ev.bind(PLAYER_EVENT.OTHER_IDENTITY, this.#identify); new ResizeObserver(this.observeResizeCallback).observe(this); new IntersectionObserver(this.observeIntersectionCallback).observe(this); this.addEventListener('scroll', () => { const { scrollTop } = this; const startindex = Math.floor(scrollTop / 72); - const { length } = this.$items; - if (this.$startIndex < startindex) { + const { length } = this.#items; + if (this.#startIndex < startindex) { // 向下滚动 - for (; (this.$startIndex < startindex && (this.$startIndex + this.$seeLength) < length); this.$startIndex++) { - this.appendChild(this.renderItem(this.$items[this.$startIndex + this.$seeLength], this.$startIndex + this.$seeLength + 1)); + for (; (this.#startIndex < startindex && (this.#startIndex + this.#seeLength) < length); this.#startIndex++) { + this.appendChild(this.renderItem(this.#items[this.#startIndex + this.#seeLength], this.#startIndex + this.#seeLength + 1)); this.firstElementChild?.remove(); } } else { // 向上滚动 - for (; (this.$startIndex > startindex && (this.$startIndex - 1) >= 0); this.$startIndex--) { - this.prepend(this.renderItem(this.$items[this.$startIndex - 1], this.$startIndex)); + for (; (this.#startIndex > startindex && (this.#startIndex - 1) >= 0); this.#startIndex--) { + this.prepend(this.renderItem(this.#items[this.#startIndex - 1], this.#startIndex)); this.lastElementChild?.remove(); } } @@ -79,7 +78,7 @@ export class Recommend extends HTMLDivElement { } if (!isIntersecting) { - this.$startIndex = 0; + this.#startIndex = 0; } } @@ -98,19 +97,19 @@ export class Recommend extends HTMLDivElement { private render(blockSize: number) { this.replaceChildren(); - const { length } = this.$items; + const { length } = this.#items; const max = Math.ceil(blockSize / 72) + 2; - for (let i = 0; (i < max && (this.$startIndex + i) < length); i++) { - this.appendChild(this.renderItem(this.$items[this.$startIndex + i], this.$startIndex + i + 1)); - this.$seeLength = i + 1; + for (let i = 0; (i < max && (this.#startIndex + i) < length); i++) { + this.appendChild(this.renderItem(this.#items[this.#startIndex + i], this.#startIndex + i + 1)); + this.#seeLength = i + 1; } this.marginFix(); } private marginFix() { - const { length } = this.$items; - this.style.setProperty('--margin-block-start', `${this.$startIndex * 72}px`); - this.style.setProperty('--margin-block-end', `${(length - this.$seeLength - this.$startIndex) * 72}px`); + const { length } = this.#items; + this.style.setProperty('--margin-block-start', `${this.#startIndex * 72}px`); + this.style.setProperty('--margin-block-end', `${(length - this.#seeLength - this.#startIndex) * 72}px`); } private renderItem(d: IRecommend, i: number) { @@ -126,23 +125,23 @@ export class Recommend extends HTMLDivElement {
    ${d.title}
    - ${d.view ? `
    ${svg_icon_played + Format.carry(d.view)}
    ` : ''} - ${d.danmaku ? `
    ${svg_icon_danmaku + Format.carry(d.danmaku)}
    ` : ''} - ${d.author ? `
    ${svg_upper + d.author}
    ` : ''} + ${d.view ? `
    ${svg_12iconplayed + Format.carry(d.view)}
    ` : ''} + ${d.danmaku ? `
    ${svg_12icondanmu + Format.carry(d.danmaku)}
    ` : ''} + ${d.author ? `
    ${svg_12up + d.author}
    ` : ''}
    `; d.callback && div.addEventListener('click', d.callback); - return div + return div; } add(items: IRecommend | IRecommend[]) { - this.$items = this.$items.concat(items); + this.#items = this.#items.concat(items); this.hasChildNodes() || (this.offsetHeight && this.render(this.offsetHeight)); } - private identify = () => { - this.$startIndex = 0; - this.$items.length = 0; + #identify = () => { + this.#startIndex = 0; + this.#items.length = 0; this.replaceChildren(); } } diff --git a/src/player/dash.ts b/src/player/dash.ts new file mode 100644 index 0000000..d643bde --- /dev/null +++ b/src/player/dash.ts @@ -0,0 +1,66 @@ +import DashPlayer, { IDashPlayerOption, IMPDJsonData, IQualityChangeRendered, IQualityChangeRequested, IVideoInfoEvent } from "../dash-player"; +import { Video } from "./area/wrap/video"; +import { ev, PLAYER_EVENT } from "./event"; + +/** flv.js播放器内核管理 */ +export class DashAgent { + + dashPlayer: DashPlayer; + + constructor( + video: Video, + mpd: IMPDJsonData, + options?: IDashPlayerOption, + videoQuality = 0, + seekTime = 0, + ) { + ev.trigger(PLAYER_EVENT.VIDEO_DESTORY, void 0); + this.dashPlayer = new DashPlayer(video, { + defaultAudioQuality: 30280, + defaultVideoQuality: 80, + enableHEVC: false, + enableAV1: false, + isAutoPlay: false, + isDynamic: false, + enableMultiAudioTracks: false, + abrStrategy: DashPlayer.STRING.ABR_BOLA, + stableBufferTime: 20, + ...options, + }); + this.dashPlayer.initialize(mpd) + .then(() => { + if (videoQuality === 0) { + this.dashPlayer?.setAutoSwitchQualityFor('audio', false); + this.dashPlayer?.setAutoSwitchQualityFor('video', true); + } + seekTime && video.$seek(seekTime); + }); + ev.one(PLAYER_EVENT.VIDEO_DESTORY, () => { + ev.unbind(PLAYER_EVENT.QUALITY_SET_FOR, this.setQualityFor); + this.dashPlayer?.destroy(); + }); + ev.bind(PLAYER_EVENT.QUALITY_SET_FOR, this.setQualityFor); + this.dashPlayer.on(DashPlayer.EVENTS.QUALITY_CHANGE_REQUESTED, (e: IQualityChangeRequested) => { + ev.trigger(PLAYER_EVENT.QUALITY_CHANGE_REQUESTED, e); + }); + this.dashPlayer.on(DashPlayer.EVENTS.QUALITY_CHANGE_RENDERED, (e: IQualityChangeRendered) => { + ev.trigger(PLAYER_EVENT.QUALITY_CHANGE_RENDERED, e); + }); + this.dashPlayer.on(DashPlayer.EVENTS.VIDEO_INFO, (e: IVideoInfoEvent) => { + ev.trigger(PLAYER_EVENT.VIDEO_INFO_DASH, e); + }); + this.dashPlayer.on(DashPlayer.EVENTS.ERROR, () => { + ev.trigger(PLAYER_EVENT.MEDIA_ERROR, void 0); + }); + + } + + setQualityFor = ({ detail }: CustomEvent) => { + if (detail === 0) { + this.dashPlayer.setAutoSwitchQualityFor('video', true); + } else { + this.dashPlayer.setAutoSwitchQualityFor('video', false); + this.dashPlayer.setQualityFor('video', detail); + } + } +} \ No newline at end of file diff --git a/src/player/event-target.ts b/src/player/event.ts similarity index 64% rename from src/player/event-target.ts rename to src/player/event.ts index 745b8d2..339ba8d 100644 --- a/src/player/event-target.ts +++ b/src/player/event.ts @@ -1,8 +1,8 @@ +import FlvJs from "flv.js"; import { IDanmaku } from "../danmaku"; -import { IQualityChangeRendered, IQualityChangeRequested, IVideoInfoEvent } from "../dash-player"; -import flvjs from "../flv.js"; import { IOptions } from "./option"; -// import { DanmakuSend } from "./area/sendbar/danmaku"; +import { IQualityChangeRendered, IQualityChangeRequested, IVideoInfoEvent } from "../dash-player"; +import { IDanmakuInput } from "./area/sendbar"; class Event extends EventTarget { @@ -56,28 +56,35 @@ export const ev = new Event(); /** 播放器事件 */ export enum PLAYER_EVENT { /** - * 重置播放器 - * 所有有状态的组件都应监听此事件,并在回调中将自身重置为初始状态 + * 销毁当前视频 + * 所有与视频本身直接相关的才使用此事件初始化 */ - IDENTIFY, + VIDEO_DESTORY, /** - * 变更播放器模式 - * 使用二进制位判定 {@link PLAYER_MODE} + * 初始化弹幕相关组件 */ - PLAYER_MODE, + DANMAKU_IDENTIFY, - /** 全屏模式变动 */ - FULLSCREEN_CHANGE, + /** 设置变动 */ + OPTINOS_CHANGE, - /** 画中画模式切换 */ - PICTURE_IN_PICTURE, + /** 播放列表切换 */ + AUXILIARY_FILTER, - /** 加载本地文件 */ - LOCAL_FILE_LOAD, + /** 播放器初始化完成 */ + INITED, - /** 播放出错 */ - MEDIA_ERROR, + /** 添加弹幕 */ + DANMAKU_ADD, + + /** 弹幕选择 */ + DANMAKU_CONTEXT, + + /** + * 视频弹幕之外的组件初始化 + */ + OTHER_IDENTITY, /** DASH视频信息心跳 */ VIDEO_INFO_DASH, @@ -88,75 +95,62 @@ export enum PLAYER_EVENT { /** 原生播放器心跳 */ VIDEO_INFO_NATIVE, + /** 播放视频错误 */ + MEDIA_ERROR, + + /** 加载字幕 */ + VTT_FILE_LOAD, + /** 【Dash】更新画质·响应 */ QUALITY_CHANGE_REQUESTED, /** 【Dash】更新画质·成功 */ QUALITY_CHANGE_RENDERED, - /** - * 请求更新指针位置缩略图 - * [缩略图元素, 指针位置秒数] - */ - PROGRESS_VIDEOSHOT, - - /** 单独重置弹幕相关组件 */ - DANMAKU_IDENTIFY, - - /** 添加弹幕,注意参数为新增弹幕 */ - DANMAKU_ADD, - - /** 设置变动 */ - OPTINOS_CHANGE, - - /** 播放列表切换 */ - AUXILIARY_FILTER, - - /** 播放器初始化完成 */ - INITED, + /** 在线人数 */ + ONLINE_NUMBER, - /** 弹幕发送 */ - DANMAKU_SEND, + /** 切换画质 */ + QUALITY_CHANGE, - /** 弹幕选择 */ - DANMAKU_CONTEXT, + /** 设定画质 */ + QUALITY_SET_FOR, - /** 发布通知 */ - TOAST, + /** 加载本地文件 */ + LOAD_VIDEO_FILE, - /** 点击画质切换 */ - QUALITY_CHANGE, + /** 指针焦点进度条 */ + PROGRESS_HOVER, - /** 播放本地视频 */ - LOCAL_MEDIA_LOAD, + /** 发送弹幕 */ + DANMAKU_INPUT, - /** 获取下一P */ - CALL_NEXT_PAGE, + /** 点击下一P */ + CALL_NEXT_REGISTER, } /** 播放器事件基类 */ -interface IPlayerEvent { +export interface IPlayerEvent { 0: void; - 1: number; - 2: boolean; - 3: void; - 4: File[]; - 5: void; - 6: IVideoInfoEvent; - 7: [flvjs.FlvPlayerMediaInfo, flvjs.FlvPlayerStatisticsInfo]; - 8: [flvjs.NativePlayerMediaInfo, flvjs.NativePlayerStatisticsInfo]; - 9: IQualityChangeRequested; - 10: IQualityChangeRendered; - 11: [HTMLDivElement, number]; - 12: void; - 13: IDanmaku[]; - 14: IOptions; - 15: 0 | 1 | 2; - 16: void; - // 17: DanmakuSend; - 18: IDanmaku; - 19: string; - 20: number; - 21: void; - 22: any; + 1: void; + 2: IOptions; + 3: 0 | 1 | 2; + 4: void; + 5: IDanmaku[]; + 6: IDanmaku; + 7: void; + 8: IVideoInfoEvent; + 9: [FlvJs.FlvPlayerMediaInfo, FlvJs.FlvPlayerStatisticsInfo]; + 10: [FlvJs.NativePlayerMediaInfo, FlvJs.NativePlayerStatisticsInfo]; + 11: void; + 12: File; + 13: IQualityChangeRequested; + 14: IQualityChangeRendered; + 15: { total: string, count: string; }; + 16: number; + 17: number; + 18: void; + 19: number; + 20: IDanmakuInput; + 21?: () => void; } \ No newline at end of file diff --git a/src/player/flv.ts b/src/player/flv.ts new file mode 100644 index 0000000..579c36a --- /dev/null +++ b/src/player/flv.ts @@ -0,0 +1,55 @@ +import FlvJs from "flv.js"; +import { Video } from "./area/wrap/video"; +import { ev, PLAYER_EVENT } from "./event"; + +/** flv.js播放器内核管理 */ +export class FlvAgent { + + flvPlayer: FlvJs.Player + + constructor( + video: Video, + mediaDataSource: FlvJs.MediaDataSource, + config?: FlvJs.Config, + seekTime = 0, + ) { + FlvJs.LoggingControl.forceGlobalTag = true; + FlvJs.LoggingControl.enableVerbose = false; + ev.trigger(PLAYER_EVENT.VIDEO_DESTORY, void 0); + this.flvPlayer = FlvJs.createPlayer(mediaDataSource, { + enableWorker: false, + stashInitialSize: 1024 * 64, + accurateSeek: true, + seekType: 'range', + rangeLoadZeroStart: false, + lazyLoadMaxDuration: 100, + lazyLoadRecoverDuration: 50, + deferLoadAfterSourceOpen: false, + fixAudioTimestampGap: false, + reuseRedirectedURL: true, + ...config, + }); + this.flvPlayer.attachMediaElement(video); + this.flvPlayer.load(); + seekTime && video.$seek(seekTime); + ev.one(PLAYER_EVENT.VIDEO_DESTORY, () => { + this.flvPlayer.destroy(); + }); + this.flvPlayer.on(FlvJs.Events.STATISTICS_INFO, () => { + if (this.flvPlayer.type === 'FlvPlayer') { + ev.trigger(PLAYER_EVENT.VIDEO_INFO_FLV, [ + (this.flvPlayer).mediaInfo, + (this.flvPlayer).statisticsInfo] + ); + } else if (this.flvPlayer.type === 'NativePlayer') { + ev.trigger(PLAYER_EVENT.VIDEO_INFO_NATIVE, [ + (this.flvPlayer).mediaInfo, + (this.flvPlayer).statisticsInfo] + ); + } + }); + this.flvPlayer.on(FlvJs.Events.ERROR, () => { + ev.trigger(PLAYER_EVENT.MEDIA_ERROR, void 0); + }); + } +} \ No newline at end of file diff --git a/src/player/index.css b/src/player/index.css new file mode 100644 index 0000000..5528286 --- /dev/null +++ b/src/player/index.css @@ -0,0 +1,133 @@ +@import url(./area/index.css); +@import url(./auxiliary/index.css); +@import url(./widget/index.css); + +@scope { + :scope { + --e2e2e2: #e2e2e2; + --99a2aa: #99a2aa; + --fff: #fff; + --353535: #353535; + --262626: #262626; + --00a1d6: #00a1d6; + --000: #000; + --f4f5f7: #f4f5f7; + --6d757a: #6d757a; + --fafafa: #fafafa; + --222: #222; + --ccd0d7: #ccd0d7; + --e5e9ef: #e5e9ef; + --017cc3: #017cc3; + --8adced: #8adced; + --6b6b6b: #6b6b6b; + --0006: #0006; + --ffa726: #ffa726; + --47be8d: #47be8d; + --f2509e: #f2509e; + --effef5: #effef5; + --fffde1: #fffde1; + --ffefd8: #ffefd8; + --ff792b: #ff792b; + --ddd: #ddd; + --1c1c1ce6: #1c1c1ce6; + --00000080: #00000080; + --000000cc: #000000cc; + --eee: #eee; + --ffffff33: #ffffff33; + --ffffff1f: #ffffff1f; + --1c1c1ccc: #1c1c1ccc; + --ffa500: #ffa500; + --00b5e5: #00b5e5; + --f5f5f5: #f5f5f5; + + /* 是否启用宽屏 */ + --screen-wide: 0; + /* 是否启用网页全屏 */ + --screen-web: 0; + /* 是否全屏 */ + --fullscreen: 0; + + color-scheme: light dark; + font-family: Arial, Helvetica, Microsoft YaHei, sans-serif; + font-size: 12px; + box-sizing: border-box; + border-radius: 4px; + box-shadow: 0 0 8px var(--e2e2e2); + background-color: var(--fff); + inline-size: 100%; + block-size: 100%; + margin: 0; + margin: 0; + padding: 0; + text-align: start; + container: bofqi / inline-size; + display: flex; + + /* 宽屏模式 */ + &.screen-wide { + --screen-wide: 1; + } + + /* 网页全屏 */ + &.screen-web { + object-fit: contain; + user-select: text; + position: fixed !important; + box-sizing: border-box !important; + min-inline-size: 0px !important; + max-inline-size: none !important; + min-block-size: 0px !important; + max-block-size: none !important; + inline-size: 100% !important; + block-size: 100% !important; + transform: none !important; + inset: 0px !important; + margin: 0px !important; + z-index: 999999; + + --screen-web: 1; + } + + /* 全屏模式 */ + &:fullscreen { + --fullscreen: 1; + } + + @media (display-mode: picture-in-picture) { + & { + object-fit: contain; + user-select: text; + position: fixed !important; + box-sizing: border-box !important; + min-inline-size: 0px !important; + max-inline-size: none !important; + min-block-size: 0px !important; + max-block-size: none !important; + inline-size: 100% !important; + block-size: 100% !important; + transform: none !important; + inset: 0px !important; + margin: 0px !important; + } + } + } + + div { + box-sizing: border-box; + margin: 0; + padding: 0; + border: 0; + user-select: none; + } + + button { + display: flex; + justify-content: center; + align-items: center; + color: inherit; + background-color: transparent; + padding: 0; + border: none; + cursor: pointer; + } +} \ No newline at end of file diff --git a/src/player/index.d.css.ts b/src/player/index.d.css.ts new file mode 100644 index 0000000..eb9bc4f --- /dev/null +++ b/src/player/index.d.css.ts @@ -0,0 +1,2 @@ +declare const stylesheet: CSSStyleSheet; +export default stylesheet; \ No newline at end of file diff --git a/src/player/index.ts b/src/player/index.ts index 23fd337..b5dc3b0 100644 --- a/src/player/index.ts +++ b/src/player/index.ts @@ -1,16 +1,19 @@ -import { Danmaku, DanmakuEvent } from "../danmaku/index.js"; -import DashPlayer, { IDashPlayerOption, IMPDJsonData, IQualityChangeRendered, IQualityChangeRequested, IVideoInfoEvent } from "../dash-player/index.js"; -import flvjs from "../flv.js"; +import { Danmaku } from "../danmaku"; +import { DanmakuEvent } from "../danmaku/event"; +import { toastr } from "../toastr"; +import { CSSStyleSheet2HTMLStyleElement } from "../utils/CSSStyleSheet2HTMLStyleElement"; import { customElement } from "../utils/Decorator/customElement"; +import { Format } from "../utils/fomat"; import { Area } from "./area"; -import { video } from "./area/wrap/video/index.js"; +import { Video } from "./area/wrap/video"; import { Auxiliary } from "./auxiliary"; -import { ev, PLAYER_EVENT } from "./event-target"; -import { options } from "./option.js"; -import { PLAYER_MODE, PLAYER_STATE } from "./state.js"; +import { ev, PLAYER_EVENT } from "./event"; +import { FlvAgent } from "./flv"; +import stylesheet from "./index.css" with {type: 'css'}; +import { options } from "./option"; /** 播放器核心 */ -@customElement('figure') +@customElement(undefined, `bofqi-${Date.now()}`) export class Player extends HTMLElement { /** @@ -28,398 +31,134 @@ export class Player extends HTMLElement { // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} /** 每当元素添加到文档中时调用。 */ - connectedCallback() { - self.addEventListener('keydown', this.onKeyDown); - } + // connectedCallback() {} /** 每当元素从文档中移除时调用。 */ - disconnectedCallback() { - self.removeEventListener('keydown', this.onKeyDown); - } + // disconnectedCallback() {} /** 每当元素被移动到新文档中时调用。 */ // adoptedCallback() {} - /** 播放器左侧面板 */ - $area = this.appendChild(new Area()); + #host = this.attachShadow({ mode: 'closed' }); - /** 播放器右侧面板 */ - $auxiliary = this.appendChild(new Auxiliary()); + /** 视频元素 */ + $video = new Video(this); - /** flv.js实例 */ - flvPlayer?: flvjs.Player; + /** 弹幕组件 */ + $danmaku = new Danmaku(this.$video); - /** dashjs实例 */ - dashPlayer?: DashPlayer; + $area = this.#host.appendChild(new Area(this)); - /** 弹幕组件 */ - danmaku = new Danmaku(video); + $auxiliary = this.#host.appendChild(new Auxiliary(this)); + + /** 是否处于全屏模式 */ + get $isFullScreen() { + return this.matches(':fullscreen'); + } constructor() { super(); - this.insertAdjacentHTML('beforeend', ``); - this.classList.add('video-state-pause'); - this.$area.$wrap.appendChild(this.danmaku); - - ev.bind(PLAYER_EVENT.PLAYER_MODE, ({ detail }) => { - this.style.setProperty('--mode-wide', detail & PLAYER_MODE.WIDE ? '1' : '0'); - this.style.setProperty('--mode-fullscreen', detail & PLAYER_MODE.FULL ? '1' : '0'); - this.classList.toggle('mode-fullscreen-web', Boolean(detail & PLAYER_MODE.WEB)); - detail & PLAYER_MODE.FULL ? this.requestFullscreen() : (document.fullscreenElement === this && document.exitFullscreen()); - }); - ev.bind(PLAYER_EVENT.PICTURE_IN_PICTURE, () => { - if (documentPictureInPicture.window) { } else { - const parrent = this.parentElement!; - const prev = this.nextElementSibling; - documentPictureInPicture.requestWindow({ - width: this.clientWidth, - height: this.clientHeight - }).then(d => { - d.window.document.body.append(this); - // [...document.styleSheets].forEach((styleSheet) => { - // try { - // const cssRules = [...styleSheet.cssRules] - // .map((rule) => rule.cssText) - // .join(""); - // const style = document.createElement("style"); - - // style.textContent = cssRules; - // d.window.document.head.appendChild(style); - // } catch (e) { - // const link = document.createElement("link"); + // this.#host.adoptedStyleSheets = [stylesheet]; // 文档画中画模式会导致构造的样式表丢失,暂时取道 style 元素代替 + this.#host.appendChild(CSSStyleSheet2HTMLStyleElement(stylesheet)); - // link.rel = "stylesheet"; - // link.type = styleSheet.type; - // link.media = styleSheet.media; - // link.href = styleSheet.href; - // d.window.document.head.appendChild(link); - // } - // }); - d.addEventListener('pagehide', () => { - // parrent.appendChild(this); - parrent.insertBefore(this, prev); - documentPictureInPicture.window?.close(); - }, { once: true }); - }); - } - }); - ev.bind(PLAYER_EVENT.LOCAL_FILE_LOAD, ({ detail }) => { - this.fileLoad(detail) - }); - ev.bind(PLAYER_EVENT.DANMAKU_IDENTIFY, this.danmaku.identify); + ev.bind(PLAYER_EVENT.DANMAKU_IDENTIFY, this.$danmaku.$indentify); ev.bind(PLAYER_EVENT.OPTINOS_CHANGE, ({ detail }) => { - const { visible, opacity, preventShade, block, weight, speedPlus, danmakuNumber, fontSize, fullScreenSync, fontBorder, fontFamily, bold, speedSync } = detail.danmaku; - - visible ? this.danmaku.on() : this.danmaku.off(); - this.danmaku.style.setProperty('--opacity', opacity); - this.danmaku.$preventShade = preventShade; - this.danmaku.$block = block; - this.danmaku.$weiget = weight; - this.danmaku.$speedPlus = speedPlus; - this.danmaku.$danmakuNumber = danmakuNumber; - this.danmaku.style.setProperty('--fontSize', fontSize); - fullScreenSync ? this.danmaku.style.setProperty('--full-screen-sync', '1') : this.danmaku.style.removeProperty('--full-screen-sync'); - this.danmaku.style.setProperty('--fontBorder', fontBorder); - this.danmaku.style.setProperty('--font-family', fontFamily); - bold ? this.danmaku.style.removeProperty('--font-weight') : this.danmaku.style.setProperty('--font-weight', 'normal'); - this.danmaku.$speedSync = speedSync; - }); - ev.one(PLAYER_EVENT.INITED, () => { - ev.trigger(PLAYER_EVENT.OPTINOS_CHANGE, options); - }); - - this.danmaku.bind(DanmakuEvent.DANMAKU_ADD, ({ detail }) => { + const { visible, opacity, preventShade, block, weight, speedPlus, danmakuNumber, fontSize, fullScreenSync, fontBorder, fontFamily, bold, speedSync, mode7Scale } = detail.danmaku; + + visible ? this.$danmaku.$on() : this.$danmaku.$off(); + this.$danmaku.style.setProperty('--opacity', opacity); + this.$danmaku.$preventShade = preventShade; + this.$danmaku.$block = block; + this.$danmaku.$weiget = weight; + this.$danmaku.$speedPlus = speedPlus; + this.$danmaku.$danmakuNumber = danmakuNumber; + this.$danmaku.$mode7Scale = mode7Scale; + this.$danmaku.style.setProperty('--fontSize', fontSize); + fullScreenSync ? this.$danmaku.style.setProperty('--full-screen-sync', '1') : this.$danmaku.style.removeProperty('--full-screen-sync'); + this.$danmaku.style.setProperty('--fontBorder', fontBorder); + this.$danmaku.style.setProperty('--font-family', fontFamily); + bold ? this.$danmaku.style.removeProperty('--font-weight') : this.$danmaku.style.setProperty('--font-weight', 'normal'); + this.$danmaku.$speedSync = speedSync; + }); + + this.$danmaku.bind(DanmakuEvent.DANMAKU_ADD, ({ detail }) => { ev.trigger(PLAYER_EVENT.DANMAKU_ADD, detail); }); - video.addEventListener('play', () => { - this.classList.toggle('video-state-pause', video.paused); - this.classList.remove('video-state-buff'); - }); - video.addEventListener('pause', () => { - this.classList.toggle('video-state-pause', video.paused); - }); - video.addEventListener('playing', () => { - this.classList.remove('video-state-buff'); - }); - video.addEventListener('waiting', () => { - this.classList.add('video-state-buff'); - }); - video.addEventListener('ended', () => { - this.classList.toggle('video-state-pause', video.ended); - }); - video.addEventListener('emptied', () => { - this.classList.toggle('video-state-pause', video.paused); - }); - - document.addEventListener('fullscreenchange', () => { - if ((document.fullscreenElement === this) !== Boolean(PLAYER_STATE.mode & PLAYER_MODE.FULL)) { - PLAYER_STATE.mode ^= PLAYER_MODE.FULL; - this.style.setProperty('--mode-fullscreen', PLAYER_STATE.mode & PLAYER_MODE.FULL ? '1' : '0'); - } - ev.trigger(PLAYER_EVENT.FULLSCREEN_CHANGE, document.fullscreenElement === this); - }); - - self.addEventListener('beforeunload', () => { - try { - this.identify(); - } catch { } - }); - - ev.trigger(PLAYER_EVENT.INITED, void 0); + ev.trigger(PLAYER_EVENT.OPTINOS_CHANGE, options); } - /** - * 跳帧播放 - * - * @param t 目标时间:/s - */ - seek = (t: number) => { - video.seek(t); - } - - /** 播放 */ - play = () => { - video.play(); - } - - /** 暂停 */ - pause = () => { - video.pause(); - } - - /** 加载本地文件 */ - private fileLoad(files: File[]) { + /** 本地文件处理 */ + fileHandle = (files: File[]) => { /** 互斥标记,一些类型的文件只加载首项 */ const mutex: string[] = []; for (const file of files) { switch (true) { - case file.name.endsWith('.mp4'): { + case file.name.endsWith('.mp4'): + case file.name.endsWith('.flv'): { if (!mutex.includes('video')) { + ev.trigger(PLAYER_EVENT.VIDEO_DESTORY, void 0); const url = URL.createObjectURL(file); - this.flvjs({ type: 'mp4', url }); + new FlvAgent(this.$video, { type: 'mp4', url }); mutex.push('video'); - ev.trigger(PLAYER_EVENT.LOCAL_MEDIA_LOAD, void 0); + toastr.success('加载视频文件', file.name, `大小:${Format.fileSize(file.size)}`); } break; } case file.name.endsWith('.vtt'): { - this.$area.$control.$closedCaption.load(file); + ev.trigger(PLAYER_EVENT.VTT_FILE_LOAD, file); break; } case file.name.endsWith('.xml'): { ev.trigger(PLAYER_EVENT.DANMAKU_IDENTIFY, void 0); file.text().then(d => { - this.danmaku.fromXML(d); - }) + this.$danmaku.$fromXML(d); + toastr.success('加载弹幕文件', file.name, `大小:${Format.fileSize(file.size)}`); + }).catch(e => { + toastr.error('加载弹幕出错', e); + console.error(e); + }); break; } case file.name.endsWith('.dm.json'): { ev.trigger(PLAYER_EVENT.DANMAKU_IDENTIFY, void 0); file.text().then(d => { - this.danmaku.add(JSON.parse(d)); - }) + this.$danmaku.$add(JSON.parse(d)); + toastr.success('加载弹幕文件', file.name, `大小:${Format.fileSize(file.size)}`); + }).catch(e => { + toastr.error('加载弹幕出错', e); + console.error(e); + }); break; } case file.name.endsWith('.gz'): { + toastr.info(`压缩文件:${file.name}(${file.type}),尝试解压中~`); const date = file.stream().pipeThrough(new DecompressionStream('gzip')); - return new Response(date).blob().then(d => { - this.fileLoad([ + new Response(date).blob().then(d => { + this.fileHandle([ new File([d], file.name.replace(/\.gz$/, ''), { type: file.type, lastModified: file.lastModified })]); - }) + }).catch(e => { + toastr.error('解压文件出错', e); + console.error(e); + }); + break; } default: { - ev.trigger(PLAYER_EVENT.TOAST, `不支持的文件类型:${file.name}(${file.type})`); + toastr.warn(`不支持的文件类型:${file.name}(${file.type})`); } } } } - /** - * 加载flv资源 - * - * @param mediaDataSource flv信息 - * @param config 播放器配置 - * @param seekTime 播放时间戳:/s - */ - flvjs( - mediaDataSource: flvjs.MediaDataSource, - config: flvjs.Config = { - enableWorker: false, - stashInitialSize: 1024 * 64, - accurateSeek: true, - seekType: 'range', - rangeLoadZeroStart: false, - lazyLoadMaxDuration: 100, - lazyLoadRecoverDuration: 50, - deferLoadAfterSourceOpen: false, - fixAudioTimestampGap: false, - reuseRedirectedURL: true, - }, - seekTime = 0, - ) { - this.playerDistroy(); - this.contains(video) || (this.$area.$wrap.insertBefore(video, this.danmaku)); - flvjs.LoggingControl.forceGlobalTag = true; - flvjs.LoggingControl.enableVerbose = false; - this.flvPlayer = flvjs.createPlayer(mediaDataSource, config); - this.flvPlayer.attachMediaElement(video); - this.flvPlayer.load(); - seekTime && this.seek(seekTime); - this.bindFlvPlayer(); - return this.flvPlayer; - } - - /** 监听flvPlayer事件 */ - protected bindFlvPlayer() { - this.flvPlayer?.on(flvjs.Events.STATISTICS_INFO, () => { - if (this.flvPlayer?.type === 'FlvPlayer') { - ev.trigger(PLAYER_EVENT.VIDEO_INFO_FLV, [ - (this.flvPlayer).mediaInfo, - (this.flvPlayer).statisticsInfo] - ); - } else if (this.flvPlayer?.type === 'NativePlayer') { - ev.trigger(PLAYER_EVENT.VIDEO_INFO_NATIVE, [ - (this.flvPlayer).mediaInfo, - (this.flvPlayer).statisticsInfo] - ); - } - }); - this.flvPlayer?.on(flvjs.Events.ERROR, () => { - ev.trigger(PLAYER_EVENT.MEDIA_ERROR, void 0); - }); - } - - /** - * 加载dash资源 - * - * @param mpd dash mpd - * @param options 播放器设置 - * @param videoQuality 当前清晰度 - * @param highAudioQuality Hires或者杜比全景声清晰度 - * @param seekTime 播放时间戳:/s - */ - dashjs( - mpd: IMPDJsonData, - options: IDashPlayerOption = { - defaultAudioQuality: 30280, - defaultVideoQuality: 80, - enableHEVC: false, - enableAV1: false, - isAutoPlay: false, - isDynamic: false, - enableMultiAudioTracks: false, - abrStrategy: DashPlayer.STRING.ABR_BOLA, - stableBufferTime: 20, - }, - videoQuality = 0, - seekTime = 0, - ) { - this.playerDistroy(); - this.contains(video) || (this.$area.$wrap.insertBefore(video, this.danmaku)); - this.dashPlayer = new DashPlayer(video, options); - this.dashPlayer.initialize(mpd) - .then(() => { - if (videoQuality === 0) { - this.dashPlayer?.setAutoSwitchQualityFor('audio', false); - this.dashPlayer?.setAutoSwitchQualityFor('video', true); - } - seekTime && this.seek(seekTime); - }); - this.bindDashPlayer(); - return this.dashPlayer; - } - - /** 监听DashPlayer事件 */ - protected bindDashPlayer() { - this.dashPlayer?.on(DashPlayer.EVENTS.QUALITY_CHANGE_REQUESTED, (e: IQualityChangeRequested) => { - ev.trigger(PLAYER_EVENT.QUALITY_CHANGE_REQUESTED, e); - }); - this.dashPlayer?.on(DashPlayer.EVENTS.QUALITY_CHANGE_RENDERED, (e: IQualityChangeRendered) => { - ev.trigger(PLAYER_EVENT.QUALITY_CHANGE_RENDERED, e); - }); - this.dashPlayer?.on(DashPlayer.EVENTS.VIDEO_INFO, (e: IVideoInfoEvent) => { - ev.trigger(PLAYER_EVENT.VIDEO_INFO_DASH, e); - }); - this.dashPlayer?.on(DashPlayer.EVENTS.ERROR, () => { - ev.trigger(PLAYER_EVENT.MEDIA_ERROR, void 0); - }); - - } - - protected onKeyDown = ({ key, code, shiftKey, ctrlKey, altKey, metaKey, isComposing }: KeyboardEvent) => { - try { - const { activeElement } = document; - if ( - activeElement?.hasAttribute('contenteditable') - || activeElement instanceof HTMLInputElement - || activeElement instanceof HTMLTextAreaElement - ) { } else { - switch (key) { - // 不能区分小键盘,但能识别 Shift 后的值 - case 'F': case 'f': { - // 全屏 - shiftKey || ctrlKey || altKey || metaKey || ev.trigger(PLAYER_EVENT.PLAYER_MODE, PLAYER_STATE.mode ^= PLAYER_MODE.FULL); - break; - } - case 'd': case 'D': { - // 弹幕开关 - shiftKey || ctrlKey || altKey || metaKey || (options.danmaku.visible = !options.danmaku.visible); - break; - } - case 'm': case 'M': { - // 音量开关 - shiftKey || ctrlKey || altKey || metaKey || (video.muted = !video.muted); - break; - } - case ' ': { - // 播放/暂停 - shiftKey || ctrlKey || altKey || metaKey || (PLAYER_STATE.mode & PLAYER_MODE.FULL && video.toggle()); - break; - } - case 'ArrowRight': { - // 快进 5 秒 - shiftKey || ctrlKey || altKey || metaKey || (video.currentTime += 5); - break; - } - case 'ArrowLeft': { - // 快退 5 秒 - shiftKey || ctrlKey || altKey || metaKey || (video.currentTime -= 5); - break; - } - } - // switch (code) { - // // 能区分小键盘,但不识别 Shift 后的值 - // } - } - } catch { } - } - - /** 销毁当前播放器 */ - protected playerDistroy() { - this.flvPlayer?.destroy(); - this.dashPlayer?.destroy(); - delete this.flvPlayer; - delete this.dashPlayer; - } - - /** 初始化播放器 */ + /** 初始化所有组件 */ identify() { - video.pause(); - this.classList.add('video-state-pause'); - this.danmaku.identify(); - this.playerDistroy(); - ev.trigger(PLAYER_EVENT.IDENTIFY, void 0); + ev.trigger(PLAYER_EVENT.VIDEO_DESTORY, void 0); + ev.trigger(PLAYER_EVENT.DANMAKU_IDENTIFY, void 0); + ev.trigger(PLAYER_EVENT.OTHER_IDENTITY, void 0); } -} - -//////////////////////////// 全局增强 //////////////////////////// -declare global { - /** 基于哈希消息认证码的一次性口令的密钥 */ - const __BOFQI_STYLE__: string; } \ No newline at end of file diff --git a/src/player/option.ts b/src/player/option.ts index 9f47c57..12a85f7 100644 --- a/src/player/option.ts +++ b/src/player/option.ts @@ -1,7 +1,5 @@ -// import { DANMAKU } from "../danmaku/block"; import { ProxyHook } from "../utils/hook/Proxy"; -import { PLAYER_EVENT, ev } from "./event-target"; -import { POLICY } from "./policy"; +import { ev, PLAYER_EVENT } from "./event"; /** 默认设置 */ export const OPTIONS: IOptions = { @@ -30,10 +28,7 @@ export const OPTIONS: IOptions = { weight: 0, blockList: false, block: 0, - }, - player: { - policy: POLICY.AVC, - incognito: false, + mode7Scale: false, } } @@ -48,7 +43,7 @@ try { } } catch { } -let timer: number; +let timer: ReturnType; /** * 播放器设置 * 本对象经过多层代理,如果要短时间内多次访问,为提高性能。 @@ -120,12 +115,9 @@ export interface IOptions { * 根据{@link DANMAKU}的二进制位判定 */ block: number; - } - /** 播放器 */ - player: { - /** 播放策略 */ - policy: POLICY, - /** 无痕模式 */ - incognito: boolean, + /** + * mode7 自适应缩放 + */ + mode7Scale: boolean; } }; diff --git a/src/player/state.ts b/src/player/state.ts deleted file mode 100644 index f61b6b6..0000000 --- a/src/player/state.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** 播放器状态 */ -export const PLAYER_STATE = { - /** - * 播放器模式 - * 使用二进制位判定 {@link PLAYER_MODE} - */ - mode: 0, -} - -/** - * 播放器模式 - * 使用二进制位判定 - */ -export enum PLAYER_MODE { - /** 宽屏 */ - WIDE = 0B1, - /** 网页全屏 */ - WEB = 0B10, - /** 全屏 */ - FULL = 0B100, -} \ No newline at end of file diff --git a/src/player/style/area/control/closed-caption.css b/src/player/style/area/control/closed-caption.css deleted file mode 100644 index 282dbff..0000000 --- a/src/player/style/area/control/closed-caption.css +++ /dev/null @@ -1,119 +0,0 @@ -.bofqi-control-closed-caption { - anchor-name: --closed-caption; - --caption-color: #fffff; - - &>svg { - display: none; - font-size: 28px; - } - - &:not(.on)>svg:first-child { - display: block; - } - - &.on>svg:nth-child(2) { - display: block; - } - - .bofqi-control-closed-caption-wrap { - display: none; - position: absolute; - position-anchor: --closed-caption; - inset-area: block-start span-inline-start; - background: var(--background-color); - border: 1px solid #e2e2e2; - border-start-start-radius: 4px; - border-start-end-radius: 4px; - inline-size: 250px; - padding-inline: 10px; - padding-block: 14px; - font-size: 12px; - line-height: 2; - text-align: start; - z-index: 1; - - .bofqi-control-closed-caption-title { - inline-size: 100%; - } - - .bofqi-control-closed-caption-select { - max-inline-size: 75%; - } - - .bpui-slider { - flex-grow: 1; - block-size: 1.5em; - } - - .closed-caption-color { - flex-grow: 1; - block-size: 1.5em; - border: 1px solid #e2e2e2; - border-radius: 4px; - background-color: var(--caption-color); - } - - .bpui-select { - flex-grow: 1; - color: #6d757a; - block-size: 1.5em; - } - - hr { - inline-size: 100%; - block-size: 0; - border: 0; - margin: 0; - } - } - - &:hover { - .bofqi-control-closed-caption-wrap { - display: flex; - flex-wrap: wrap; - align-items: center; - justify-content: space-between; - } - } - - &:disabled { - display: none; - } -} - -[popover].popover-local-font { - inset-inline-start: 50%; - inset-block-start: 50%; - translate: -50% -50%; - display: flex; - flex-direction: row; - flex-wrap: wrap; - justify-content: space-between; - border: 1px solid #ccd0d7; - border-radius: 4px; - padding: 4px; - align-items: center; - max-inline-size: 800px; - max-block-size: 600px; - overflow-y: auto; - scrollbar-width: none; - - &>button { - appearance: none; - outline: none; - border: 1px solid #ccd0d7; - border-radius: 4px; - padding: 4px; - margin: 4px; - inline-size: fit-content; - block-size: fit-content; - background-color: white; - cursor: pointer; - - &:hover { - background-color: white; - background-color: #f4f5f7; - color: #6d757a; - } - } -} \ No newline at end of file diff --git a/src/player/style/area/control/danmaku.css b/src/player/style/area/control/danmaku.css deleted file mode 100644 index 74aa972..0000000 --- a/src/player/style/area/control/danmaku.css +++ /dev/null @@ -1,118 +0,0 @@ -.bofqi-control-danmaku { - anchor-name: --danmaku; - - &>svg { - display: none; - } - - &:not(.off)>svg:first-child { - display: block; - font-size: 14px; - } - - &.off>svg:nth-child(2) { - display: block; - } - - .bofqi-control-danmaku-wrap { - display: none; - position: absolute; - position-anchor: --danmaku; - inset-area: block-start span-inline-start; - background: var(--background-color); - border: 1px solid #e2e2e2; - inline-size: 222px; - line-height: 1.5; - border-start-start-radius: 4px; - border-start-end-radius: 4px; - padding-inline: 22px; - padding-block-start: 14px; - padding-block-end: 24px; - font-size: 12px; - row-gap: .5em; - text-align: start; - z-index: 1; - - .bofqi-control-danmaku-opacity { - min-inline-size: 0; - inline-size: 120px; - } - - .bofqi-control-danmaku-block { - display: flex; - flex-direction: column; - align-items: center; - transition: color .3s; - position: relative; - - &::after { - content: attr(data-label); - display: block; - } - - >svg { - &:not(:last-child) { - font-size: 48px; - fill: #99a2aa; - transition: fill .3s; - anchor-name: --danmaku-block; - } - - &:last-child { - position: absolute; - inset-inline-end: anchor(--danmaku-block end); - inset-block-end: anchor(--danmaku-block end); - font-size: 24px; - background-color: #fff; - border: 3px solid #fff; - fill: #99a2aa; - border-radius: 50%; - box-sizing: border-box; - opacity: 0; - transition: all .3s; - } - } - - &:hover { - - >svg { - &:not(:last-child) { - fill: #6d757a; - } - - &:last-child { - opacity: 1; - fill: #6d757a; - } - } - } - - &.block { - color: #99a2aa; - - >svg { - - &:last-child { - opacity: 1; - fill: #f25d8e; - } - } - } - } - - hr { - inline-size: 100%; - block-size: 0; - border: 0; - } - } - - &:hover { - .bofqi-control-danmaku-wrap { - display: flex; - flex-wrap: wrap; - align-items: center; - justify-content: space-between; - } - } -} \ No newline at end of file diff --git a/src/player/style/area/control/fullscreen-web.css b/src/player/style/area/control/fullscreen-web.css deleted file mode 100644 index 2de0283..0000000 --- a/src/player/style/area/control/fullscreen-web.css +++ /dev/null @@ -1,13 +0,0 @@ -.bofqi-control-fullscreen-web { - position: absolute; - background-color: var(--background-color); - position-anchor: --fullscreen; - inset-area: block-start; - inline-size: anchor-size(inline); - anchor-name: --fullscreen-web; -} - -.bofqi-area-control:not(:has(.bofqi-control-fullscreen:hover), :has(.bofqi-control-fullscreen-web:hover), - :has(.bofqi-control-fullscreen-pip:hover)) .bofqi-control-fullscreen-web { - display: none; -} \ No newline at end of file diff --git a/src/player/style/area/control/fullscreen.css b/src/player/style/area/control/fullscreen.css deleted file mode 100644 index 3d65b75..0000000 --- a/src/player/style/area/control/fullscreen.css +++ /dev/null @@ -1,9 +0,0 @@ -.bofqi-control-fullscreen { - anchor-name: --fullscreen; -} - -@media (display-mode: picture-in-picture) { - .bofqi-control-fullscreen { - display: none; - } -} \ No newline at end of file diff --git a/src/player/style/area/control/index.css b/src/player/style/area/control/index.css deleted file mode 100644 index 87a662d..0000000 --- a/src/player/style/area/control/index.css +++ /dev/null @@ -1,60 +0,0 @@ -@import url(./toggle.css); -@import url(./next.css); -@import url(./progress.css); -@import url(./time.css); -@import url(./volume.css); -@import url(./quality.css); -@import url(./danmaku.css); -@import url(./closed-caption.css); -@import url(./repeat.css); -@import url(./wide.css); -@import url(./fullscreen.css); -@import url(./fullscreen-web.css); -@import url(./pip.css); - -.bofqi-area-control { - block-size: 30px; - flex-shrink: 0; - border-block: 1px solid var(--box-shadow); - display: flex; - transition: all .2s; - background-color: var(--background-color); - z-index: 2; - - &>button { - font-size: 16px; - block-size: inherit; - aspect-ratio: 1; - - --mode-wide: 0; - } - - svg { - block-size: 1em; - aspect-ratio: 1; - fill: #99a2aa; - } -} - -@container bofqi style(--mode-fullscreen:1) { - - .bofqi-area-control { - position: absolute; - inset-inline: 0; - inset-block-end: 0; - anchor-name: --control; - } - - .bofqi-area.hide:not(:has(.bofqi-area-sendbar:hover), :has(.bofqi-area-control:hover)) .bofqi-area-control { - position-anchor: --bofqi; - inset-area: block-end; - opacity: 0; - } -} - -@container bofqi (inline-size < 780px) { - - .bofqi-area-control { - display: none; - } -} \ No newline at end of file diff --git a/src/player/style/area/control/pip.css b/src/player/style/area/control/pip.css deleted file mode 100644 index 4e1431e..0000000 --- a/src/player/style/area/control/pip.css +++ /dev/null @@ -1,14 +0,0 @@ -.bofqi-control-fullscreen-pip { - position: absolute; - background-color: var(--background-color); - position-anchor: --fullscreen-web; - inset-area: block-start; - inline-size: anchor-size(inline); - border-start-start-radius: 4px; - border-start-end-radius: 4px; -} - -.bofqi-area-control:not(:has(.bofqi-control-fullscreen:hover), :has(.bofqi-control-fullscreen-web:hover), - :has(.bofqi-control-fullscreen-pip:hover)) .bofqi-control-fullscreen-pip { - display: none; -} \ No newline at end of file diff --git a/src/player/style/area/control/progress.css b/src/player/style/area/control/progress.css deleted file mode 100644 index bd00646..0000000 --- a/src/player/style/area/control/progress.css +++ /dev/null @@ -1,74 +0,0 @@ -.bofqi-control-progress { - flex-grow: 1; - block-size: inherit; - background-color: transparent; - anchor-name: --bofqi-progress; - - &:hover+.bofqi-control-progress-detail { - display: flex; - flex-direction: column; - align-items: center; - } -} - -.bofqi-control-progress-detail { - --inline-start: 0px; - - position: absolute; - overflow: visible; - inline-size: 160px; - text-align: center; - pointer-events: none; - display: none; - inset-inline-start: calc(anchor(--bofqi-progress start) + var(--inline-start)); - inset-block-end: anchor(--slider-thumb end); - translate: -50%; - z-index: 1; - - .bofqi-control-progress-detail-img { - inline-size: 160px; - block-size: 90px; - background-color: transparent; - translate: 0 12px; - color: #fb7299; - font-size: 36px; - } - - .bofqi-control-progress-detail-time { - background-color: #e5e9ef; - color: #6b6b6b; - padding-block: 3px; - padding-inline: 5px; - border-radius: 4px; - translate: 0 -8px; - } - - .bofqi-control-progress-detail-sign { - display: flex; - flex-direction: column; - align-items: center; - - &::before { - content: " "; - inline-size: 0; - block-size: 0; - border-block-start-width: 4px; - border-inline-width: 4px; - border-style: solid; - border-color: var(--border-hover) transparent transparent; - position: relative; - } - - &::after { - content: " "; - padding-block-start: 2px; - inline-size: 0; - block-size: 0; - border-block-end-width: 4px; - border-inline-width: 4px; - border-style: solid; - border-color: transparent transparent var(--border-hover); - position: relative; - } - } -} \ No newline at end of file diff --git a/src/player/style/area/control/quality.css b/src/player/style/area/control/quality.css deleted file mode 100644 index aac497e..0000000 --- a/src/player/style/area/control/quality.css +++ /dev/null @@ -1,58 +0,0 @@ -.bofqi-control-quality { - anchor-name: --quality; - inline-size: 55px; - display: flex; - align-items: center; - justify-content: center; - - &::before { - content: attr(data-label); - font-size: 12px; - color: #99a2aa; - } - - .bofqi-quality-wrap { - display: none; - position: absolute; - position-anchor: --quality; - inset-area: block-start; - background-color: var(--background-color); - padding: 0; - margin: 0; - border-start-start-radius: 4px; - border-start-end-radius: 4px; - font-size: 12px; - border: 1px solid #e2e2e2; - - >li { - display: flex; - align-items: center; - justify-content: center; - color: #222; - block-size: 32px; - padding-inline: 20px; - transition: background-color .3s; - cursor: pointer; - - &:hover { - background-color: #e5e9ef; - } - - &.selected { - color: #00a1d6; - cursor: default; - pointer-events: none; - } - } - } - - &:hover .bofqi-quality-wrap { - display: flex; - align-items: center; - flex-direction: column-reverse; - } - - &.hidden { - display: none; - } -} \ No newline at end of file diff --git a/src/player/style/area/control/repeat.css b/src/player/style/area/control/repeat.css deleted file mode 100644 index e69de29..0000000 diff --git a/src/player/style/area/control/time.css b/src/player/style/area/control/time.css deleted file mode 100644 index 1df871a..0000000 --- a/src/player/style/area/control/time.css +++ /dev/null @@ -1,48 +0,0 @@ -.bofqi-control-time { - display: flex; - justify-content: center; - block-size: inherit; - align-items: center; - - .bofqi-control-time-seek { - display: none; - inline-size: 60px; - margin-inline: 10px; - padding-inline: 5px; - block-size: 20px; - font-size: 12px; - color: #99a2aa; - text-align: center; - border: 1px solid #e2e2e2; - - &:focus-visible { - outline: none; - } - } - - &.seeking { - .bofqi-control-time-seek { - display: flex; - } - } - - .bofqi-control-time-wrap { - margin-inline: 10px; - block-size: inherit; - display: flex; - align-items: center; - justify-content: center; - white-space: nowrap; - color: #99a2aa; - - .bofqi-control-divider { - padding-inline: 2px; - } - } - - &.seeking { - .bofqi-control-time-wrap { - display: none; - } - } -} \ No newline at end of file diff --git a/src/player/style/area/control/toggle.css b/src/player/style/area/control/toggle.css deleted file mode 100644 index e69de29..0000000 diff --git a/src/player/style/area/control/volume.css b/src/player/style/area/control/volume.css deleted file mode 100644 index aec7b5c..0000000 --- a/src/player/style/area/control/volume.css +++ /dev/null @@ -1,57 +0,0 @@ -.bofqi-control-volume { - anchor-name: --volume; - - &>svg { - display: none; - } - - &:not(.large, .muted)>svg:first-child { - display: block; - } - - &.large:not(.muted)>svg:nth-child(2) { - display: block; - } - - &.muted>svg:nth-child(3) { - display: block; - } - - .bofqi-control-volume-wrap { - display: none; - position: absolute; - position-anchor: --volume; - inset-area: inline-end; - block-size: anchor-size(block); - rotate: -90deg; - transform-origin: -15px; - background-color: var(--background-color); - border: 1px solid #e2e2e2; - border-end-end-radius: 4px; - border-start-end-radius: 4px; - inline-size: 100px; - - &>.bpui-slider { - min-inline-size: 0; - padding-inline-start: 10px; - } - } - - &:hover { - .bofqi-control-volume-wrap { - display: flex; - align-items: center; - - &::after { - content: attr(data-value); - rotate: 90deg; - display: flex; - align-items: center; - justify-content: center; - block-size: 28px; - aspect-ratio: 1; - font-size: 12px; - } - } - } -} \ No newline at end of file diff --git a/src/player/style/area/control/wide.css b/src/player/style/area/control/wide.css deleted file mode 100644 index e69de29..0000000 diff --git a/src/player/style/area/index.css b/src/player/style/area/index.css deleted file mode 100644 index 3833d56..0000000 --- a/src/player/style/area/index.css +++ /dev/null @@ -1,10 +0,0 @@ -@import url(./message/index.css); -@import url(./wrap/index.css); -@import url(./control/index.css); -@import url(./sendbar/index.css); - -.bofqi-area { - flex-grow: 1; - display: flex; - flex-direction: column; -} \ No newline at end of file diff --git a/src/player/style/area/message/index.css b/src/player/style/area/message/index.css deleted file mode 100644 index 786180b..0000000 --- a/src/player/style/area/message/index.css +++ /dev/null @@ -1,55 +0,0 @@ -.bofqi-area-message { - block-size: 48px; - flex-shrink: 0; - background-color: #353535; - display: flex; - padding: 6px; - gap: 12px; - - >.bofqi-message-prev, - >.bofqi-message-next { - flex-shrink: 0; - inline-size: 16px; - border-radius: 3px; - background: #262626; - - >svg { - fill: #fff; - inline-size: 1em; - aspect-ratio: 1; - transition: fill .3s; - } - - &:hover>svg { - fill: #00a1d6; - } - } - - >.bofqi-message-prev { - rotate: 180deg; - } - - >.bofqi-message-panel { - flex-grow: 1; - background: #262626; - display: flex; - align-items: center; - justify-content: center; - font-size: 13px; - color: #fff; - } -} - -@container bofqi (inline-size < 780px) { - - .bofqi-area-message { - display: none; - } -} - -@container bofqi style(--mode-wide: 1) or style(--mode-fullscreen:1) { - - .bofqi-area-message { - display: none; - } -} \ No newline at end of file diff --git a/src/player/style/area/sendbar/choose.css b/src/player/style/area/sendbar/choose.css deleted file mode 100644 index 0cb5e47..0000000 --- a/src/player/style/area/sendbar/choose.css +++ /dev/null @@ -1,165 +0,0 @@ -.bofqi-sendbar-choose { - flex-shrink: 0; - position: relative; - inline-size: 38px; - font-size: 14px; - aspect-ratio: 1; - anchor-name: --sendbar-choose; - - .bofqi-sendbar-choose-wrap { - position: absolute; - position-anchor: --sendbar-choose; - inset-area: block-start span-inline-end; - position-visibility: anchors-visible; - padding-inline: 10px; - padding-block-start: 20px; - border: 1px solid #e2e2e2; - border-radius: 4px; - background-color: var(--background-color); - transition: all .3s ease-in-out; - border-start-start-radius: 4px; - border-start-end-radius: 4px; - - &[popover]:popover-open:not(dialog) { - display: flex; - flex-direction: column; - flex-wrap: nowrap; - justify-content: flex-start; - align-items: flex-start; - cursor: default; - overflow: initial; - } - - .bofqi-sendbar-choose-row { - min-block-size: 24px; - font-size: 12px; - display: flex; - flex-direction: row; - flex-wrap: nowrap; - justify-content: flex-start; - align-items: center; - margin-block: 6px; - - .bofqi-sendbar-choose-label { - inline-size: 30px; - text-align: center; - margin-inline-end: 10px; - } - - .bofqi-sendbar-choose-selection { - display: flex; - flex-direction: row; - flex-wrap: wrap; - align-content: flex-start; - justify-content: flex-start; - align-items: center; - - >div { - border: 1px solid transparent; - padding: 4px 5px; - border-radius: 4px; - transition: all .3s ease-in-out; - cursor: pointer; - - &:not(:last-child) { - margin-inline-end: 10px; - } - - &.bofqi-sendbar-choose-mode { - display: flex; - flex-direction: column; - align-items: center; - transition: color .3s; - position: relative; - inline-size: 64px; - margin-block-end: 16px; - - &::after { - pointer-events: none; - content: attr(data-label); - display: block; - } - - >svg { - pointer-events: none; - - &:not(:last-child) { - font-size: 48px; - fill: #99a2aa; - transition: fill .3s; - anchor-name: --danmaku-select; - } - - &:last-child { - position: absolute; - inset-inline-end: anchor(--danmaku-select end); - inset-block-end: anchor(--danmaku-select end); - font-size: 24px; - background-color: #fff; - border: 3px solid #fff; - fill: #99a2aa; - border-radius: 50%; - box-sizing: border-box; - opacity: 0; - transition: all .3s; - } - } - - &:hover { - - >svg { - &:not(:last-child) { - fill: #6d757a; - } - - &:last-child { - opacity: 1; - fill: #6d757a; - } - } - } - - &.active { - - >svg { - - &:last-child { - opacity: 1; - fill: #00a1d6; - } - } - } - } - - &:hover { - color: #00a1d6; - } - - &.active { - color: #00a1d6; - pointer-events: none; - cursor: default; - - &:not(.bofqi-sendbar-choose-mode) { - border-color: #00a1d6; - } - } - - &.disabled { - color: #99a2aa; - cursor: not-allowed; - } - } - - } - - &:first-child { - margin-block-start: 0; - } - - &:last-child { - margin-block-end: 0; - } - } - } -} \ No newline at end of file diff --git a/src/player/style/area/sendbar/color.css b/src/player/style/area/sendbar/color.css deleted file mode 100644 index 8c4f76d..0000000 --- a/src/player/style/area/sendbar/color.css +++ /dev/null @@ -1,13 +0,0 @@ -.bofqi-sendbar-color { - flex-shrink: 0; - position: relative; - inline-size: 38px; - font-size: 14px; - aspect-ratio: 1; - anchor-name: --bpui-color; - --color-select: #ffffff; - - >svg { - background-color: var(--color-select); - } -} \ No newline at end of file diff --git a/src/player/style/area/sendbar/index.css b/src/player/style/area/sendbar/index.css deleted file mode 100644 index 124b099..0000000 --- a/src/player/style/area/sendbar/index.css +++ /dev/null @@ -1,41 +0,0 @@ -@import url(./choose.css); -@import url(./color.css); -@import url(./input.css); - -.bofqi-area-sendbar { - block-size: 38px; - flex-shrink: 0; - transition: all .2s; - background-color: var(--background-color); - display: flex; - align-items: center; - z-index: 1; - - svg { - block-size: 1em; - aspect-ratio: 1; - fill: #99a2aa; - } -} - -@container bofqi style(--mode-fullscreen:1) { - - .bofqi-area-sendbar { - position: absolute; - inline-size: calc(anchor-size(inline) - 460px); - position-anchor: --control; - inset-area: block-start; - } - - .bofqi-area.hide:not(:has(.bofqi-area-sendbar:hover), :has(.bofqi-area-control:hover)) .bofqi-area-sendbar { - inset-area: block-end; - opacity: 0; - } -} - -@container bofqi (inline-size < 780px) { - - .bofqi-area-sendbar { - display: none; - } -} \ No newline at end of file diff --git a/src/player/style/area/wrap/index.css b/src/player/style/area/wrap/index.css deleted file mode 100644 index 3d55e61..0000000 --- a/src/player/style/area/wrap/index.css +++ /dev/null @@ -1,31 +0,0 @@ -@import url(./record.css); -@import url(./state.css); -@import url(./toast.css); -@import url(./panel.css); -@import url(./video/index.css); - -.bofqi-area-wrap { - flex: 1; - position: relative; - background-color: black; - - >video { - display: block; - position: absolute; - inset: 0; - inline-size: 100%; - block-size: 100%; - - &.mirror { - rotate: y 180deg; - } - } -} - -@container bofqi style(--mode-fullscreen:1) { - @scope (.no-cursor) { - .bofqi-area-wrap * { - cursor: none; - } - } -} \ No newline at end of file diff --git a/src/player/style/area/wrap/panel.css b/src/player/style/area/wrap/panel.css deleted file mode 100644 index 2c8607e..0000000 --- a/src/player/style/area/wrap/panel.css +++ /dev/null @@ -1,41 +0,0 @@ -.bofqi-panel { - position: absolute; - inline-size: 100%; - block-size: 100%; - display: flex; - align-items: center; - justify-content: center; - background-color: #fff; - cursor: pointer; - z-index: 1; - - .bofqi-panel-image { - inline-size: 90px; - block-size: 90px; - background-image: url(../../../assets/image/panel.gif); - background-repeat: no-repeat; - } - - .bofqi-panel-text { - position: absolute; - color: #ccd0d7; - font-size: 12px; - inset-inline-start: 10px; - inset-block-end: 10px; - line-height: 20px; - } - - .bofqi-panel-row { - &:not([data-stage]) { - display: none; - } - - &::after { - content: "[" attr(data-stage) "]"; - } - } - - &:not(.active) { - display: none; - } -} \ No newline at end of file diff --git a/src/player/style/area/wrap/record.css b/src/player/style/area/wrap/record.css deleted file mode 100644 index 546847c..0000000 --- a/src/player/style/area/wrap/record.css +++ /dev/null @@ -1,21 +0,0 @@ -.bofqi-record { - position: absolute; - inset: 0; - pointer-events: none; - user-select: none; - z-index: 1; - - .bofqi-record-item { - position: absolute; - inset-inline: 0; - pointer-events: none; - user-select: none; - display: inline-block; - inset-block-end: 28px; - color: #fff; - text-shadow: #000 1px 0 1px, #000 0 1px 1px, #000 0 -1px 1px, #000 -1px 0 1px; - font-size: 16px; - line-height: 16px; - text-align: center; - } -} \ No newline at end of file diff --git a/src/player/style/area/wrap/toast.css b/src/player/style/area/wrap/toast.css deleted file mode 100644 index 10b3cd5..0000000 --- a/src/player/style/area/wrap/toast.css +++ /dev/null @@ -1,41 +0,0 @@ -.bofqi-toast { - position: absolute; - inset-inline-start: 0; - inset-block-end: 10px; - display: flex; - flex-direction: column-reverse; - font-size: 14px; - color: #fff; - pointer-events: none; - z-index: 1; - - .bofqi-toast-item { - cursor: default; - margin-inline-start: 10px; - padding-inline: 20px; - background-color: #191919e0; - border-radius: 4px; - line-height: 42px; - gap: 10px; - pointer-events: initial; - display: flex; - align-items: center; - - - /* 点击按钮备用 */ - /* &::after { - content: "×"; - padding-inline: 10px; - cursor: pointer; - line-height: 26px; - font-size: 24px; - } */ - } -} - -@container bofqi style(--mode-fullscreen:1) { - - .bofqi-toast { - inset-block-end: 40px; - } -} \ No newline at end of file diff --git a/src/player/style/auxiliary/index.css b/src/player/style/auxiliary/index.css deleted file mode 100644 index fba3eed..0000000 --- a/src/player/style/auxiliary/index.css +++ /dev/null @@ -1,37 +0,0 @@ -@import url(./info/index.css); -@import url(./filter.css); -@import url(./recommend/index.css); -@import url(./danmaku/index.css); -@import url(./block/index.css); - -.bofqi-auxiliary { - flex-shrink: 0; - inline-size: 300px; - border-inline-start: 1px solid var(--box-shadow); - display: flex; - flex-direction: column; - align-items: stretch; - color: #99a2aa; - anchor-name: --bofqi-auxiliary; - - svg { - display: block; - block-size: 1em; - aspect-ratio: 1; - fill: #99a2aa; - } -} - -@container bofqi (inline-size < 780px) { - - .bofqi-auxiliary { - display: none; - } -} - -@container bofqi style(--mode-wide: 1) or style(--mode-fullscreen:1) { - - .bofqi-auxiliary { - display: none; - } -} \ No newline at end of file diff --git a/src/player/style/auxiliary/info/index.css b/src/player/style/auxiliary/info/index.css deleted file mode 100644 index 009e178..0000000 --- a/src/player/style/auxiliary/info/index.css +++ /dev/null @@ -1,11 +0,0 @@ -@import url(./number.css); -@import url(./more/index.css); -@import url(./setting/index.css); - -.bofqi-auxiliary-info { - flex-shrink: 0; - block-size: 60px; - display: flex; - align-items: center; - anchor-name: --bofqi-info; -} \ No newline at end of file diff --git a/src/player/style/auxiliary/info/more/index.css b/src/player/style/auxiliary/info/more/index.css deleted file mode 100644 index 68dfa8f..0000000 --- a/src/player/style/auxiliary/info/more/index.css +++ /dev/null @@ -1,10 +0,0 @@ -@import url(./wrap.css); - -.bofqi-auxiliary-info-more { - position: absolute; - font-size: 24px; - inline-size: 30px; - aspect-ratio: 1; - inset-inline-end: calc(anchor(--bofqi-info end) + 6px); - anchor-name: --bofqi-more; -} \ No newline at end of file diff --git a/src/player/style/auxiliary/info/more/wrap.css b/src/player/style/auxiliary/info/more/wrap.css deleted file mode 100644 index 500a1d7..0000000 --- a/src/player/style/auxiliary/info/more/wrap.css +++ /dev/null @@ -1,52 +0,0 @@ -.bofqi-auxiliary-setting-more { - position: absolute; - position-anchor: --bofqi-more; - inset-area: block-end span-inline-start; - margin-block-start: 5px; - padding-block: 10px; - border: 1px solid #99a2aa; - border-radius: 4px; - box-shadow: 0 2px 1px 0 #e2e2e2; - - &[popover]:popover-open:not(dialog) { - display: flex; - flex-direction: column; - align-items: stretch; - overflow: initial; - } - - &>.bofqi-auxiliary-setting-more-triangle { - position: absolute; - inset-block-start: -4px; - inset-inline-end: 10px; - inline-size: 6px; - block-size: 6px; - border: 1px solid #99a2aa; - rotate: 45deg; - border-block-end-color: #fafafa; - border-inline-end-color: #fafafa; - } - - &>.bofqi-auxiliary-setting-more-list { - block-size: 30px; - padding-block: 0; - padding-inline-end: 14px; - padding-inline-start: 22px; - cursor: pointer; - text-align: start; - color: #222; - transition: background-color .3s; - display: flex; - align-items: center; - justify-content: flex-start; - - &:hover { - background-color: #e5e9ef; - } - - &:disabled { - cursor: not-allowed; - color: #ccd0d7; - } - } -} \ No newline at end of file diff --git a/src/player/style/auxiliary/info/number.css b/src/player/style/auxiliary/info/number.css deleted file mode 100644 index 9659162..0000000 --- a/src/player/style/auxiliary/info/number.css +++ /dev/null @@ -1,14 +0,0 @@ -.bofqi-auxiliary-info-number { - margin-inline-start: 20px; - - &:before { - content: attr(data-count); - font-size: 18px; - font-weight: 700; - color: #222; - } - - &:after { - content: attr(data-danmaku); - } -} \ No newline at end of file diff --git a/src/player/style/auxiliary/info/setting/index.css b/src/player/style/auxiliary/info/setting/index.css deleted file mode 100644 index 4a23607..0000000 --- a/src/player/style/auxiliary/info/setting/index.css +++ /dev/null @@ -1,10 +0,0 @@ -@import url(./wrap.css); - -.bofqi-auxiliary-info-setting { - position: absolute; - font-size: 24px; - inline-size: 30px; - aspect-ratio: 1; - position-anchor: --bofqi-more; - inset-area: inline-start; -} \ No newline at end of file diff --git a/src/player/style/auxiliary/info/setting/wrap.css b/src/player/style/auxiliary/info/setting/wrap.css deleted file mode 100644 index a08e91a..0000000 --- a/src/player/style/auxiliary/info/setting/wrap.css +++ /dev/null @@ -1,102 +0,0 @@ -.bofqi-auxiliary-setting { - position: absolute; - inset-inline-start: anchor(--bofqi-auxiliary start); - inset-block-start: anchor(--bofqi-auxiliary start); - inline-size: anchor-size(--bofqi-auxiliary inline); - block-size: anchor-size(--bofqi-auxiliary block); - scrollbar-width: thin; - - &[popover]:popover-open:not(dialog) { - display: flex; - flex-direction: column; - align-items: stretch; - overflow-x: hidden; - overflow-y: auto; - } - - >header { - flex-shrink: 0; - block-size: 60px; - color: #222; - font-size: 14px; - border-block-end: 1px solid #e2e2e2; - display: flex; - align-items: center; - padding-inline-start: 22px; - } - - >.bofqi-setting { - flex-grow: 1; - display: flex; - flex-direction: column; - align-items: stretch; - padding-inline-start: 22px; - padding-inline-end: 15px; - - >hr { - inline-size: 100%; - border-color: #e2e2e2; - } - - .bofqi-setting-title { - color: #222; - block-size: 50px; - font-weight: 700; - display: flex; - align-items: center; - } - - .bofqi-setting-content { - display: flex; - align-items: center; - block-size: 26px; - - &:not(.compact, :last-child) { - margin-block-end: 14px; - } - - > :last-child { - flex-grow: 1; - } - - &::before { - content: attr(data-label); - flex-shrink: 0; - inline-size: 72px; - color: #99a2aa; - margin-inline-end: 9px; - } - - >.bofqi-setting-font-border { - display: flex; - align-items: center; - - >label { - display: flex; - align-items: center; - block-size: 24px; - margin-inline: 4px; - padding-inline: 6px; - border-radius: 4px; - color: #222; - border: 1px solid transparent; - transition: all .3s; - cursor: pointer; - - &:has(input:checked) { - color: #00a1d6; - border-color: #00a1d6; - } - - >input { - appearance: none; - } - } - } - - >.bpui-select { - block-size: 100%; - } - } - } -} \ No newline at end of file diff --git a/src/player/style/index.css b/src/player/style/index.css deleted file mode 100644 index cd2e4ea..0000000 --- a/src/player/style/index.css +++ /dev/null @@ -1,123 +0,0 @@ -@import url(./area/index.css); -@import url(./auxiliary/index.css); -@import url(./widget/index.css); -@import url(../../danmaku/style/index.css); - -:root { - --box-shadow: #e2e2e2; - --background-color: #fff; - --background-color-hover: #f4f5f7; - --font-color: #99a2aa; - --font-color-hover: #6d757a; - --font-color-disabled: #ccd0d7; - --border-hover: #00a1d6; -} - -@scope { - :scope { - font-family: Arial, Helvetica, Microsoft YaHei, sans-serif; - font-size: 12px; - box-sizing: border-box; - border-radius: 4px; - text-align: start; - box-shadow: 0 0 8px var(--box-shadow); - background-color: var(--background-color); - white-space: nowrap; - margin: 0; - padding: 0; - inline-size: 100%; - block-size: 100%; - display: flex; - container: bofqi / inline-size; - - /* 宽屏模式 */ - --mode-wide: 0; - /* 全屏模式 */ - --mode-fullscreen: 0; - /* 播放状态 */ - --state: 1; - - &.mode-fullscreen-web:not(:fullscreen) { - position: fixed; - translate: initial; - inset-block-start: 0; - inset-inline-start: 0; - inline-size: 100vi; - block-size: 100vb; - z-index: 99999; - - } - - &.video-state-pause { - --state: 0; - } - - &.video-state-buff { - --state: 2; - } - - &:fullscreen { - inset: initial; - translate: initial; - --mode-fullscreen: 1; - } - - @media (display-mode: picture-in-picture) { - & { - inline-size: 100vi !important; - block-size: 100vb !important; - } - } - } - - div, - button { - box-sizing: border-box; - margin: 0; - padding: 0; - border: 0; - user-select: none; - } - - button { - cursor: pointer; - background-color: initial; - display: flex; - justify-content: center; - align-items: center; - transition: background-color .3s; - - &:disabled { - pointer-events: none; - - svg { - fill: #ccd0d7; - } - } - - &:hover { - background-color: #f4f5f7; - color: #6d757a; - - svg { - fill: #6d757a; - } - } - } - - :focus-visible { - outline: none; - } -} - -body:has(.mode-fullscreen-web:not(:fullscreen)) { - overflow: hidden; -} - -@media (display-mode: picture-in-picture) { - body { - margin: 0; - padding: 0; - border: 0; - } -} \ No newline at end of file diff --git a/src/player/style/widget/index.css b/src/player/style/widget/index.css deleted file mode 100644 index 7a96c9b..0000000 --- a/src/player/style/widget/index.css +++ /dev/null @@ -1,6 +0,0 @@ -@import url(./slider.css); -@import url(./checkbox.css); -@import url(./color.css); -@import url(./context.css); -@import url(./button.css); -@import url(./select.css); \ No newline at end of file diff --git a/src/player/style/widget/slider.css b/src/player/style/widget/slider.css deleted file mode 100644 index d740b83..0000000 --- a/src/player/style/widget/slider.css +++ /dev/null @@ -1,51 +0,0 @@ -input[type="range" i].bpui-slider { - position: relative; - appearance: none; - margin-inline: 0; - margin-block: 0; - cursor: pointer; - --linear-gradient: linear-gradient(to right, #e5e9ef 0% 100%); - - &::-webkit-slider-runnable-track { - background-image: var(--linear-gradient); - border-radius: 4px; - block-size: 6px; - } - - &::-webkit-slider-thumb { - appearance: none; - border-radius: 7px; - cursor: pointer; - box-shadow: 0 0 3px #017cc3; - background-color: #fff; - inline-size: 14px; - block-size: 14px; - position: relative; - inset-block-start: -4.5px; - anchor-name: --slider-thumb; - } - - &.hint { - &::after { - content: attr(data-value); - background-color: var(--background-color); - position: absolute; - position-anchor: --slider-thumb; - inset-area: block-start; - transition: all .3s; - opacity: 0; - padding-inline: 4px; - padding-block: 2px; - border-radius: 4px; - - } - - &:hover::after { - opacity: 1; - } - } - - &:disabled { - pointer-events: none; - } -} \ No newline at end of file diff --git a/src/player/tsconfig.json b/src/player/tsconfig.json deleted file mode 100644 index 6d75978..0000000 --- a/src/player/tsconfig.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "extends": "../tsconfig-base.json", - "include": [ - "**/*", - "../utils", - "../interface", - "../flv.js", - "../dash-player", - "../danmaku" - ], - "references": [ - { - "path": "../utils" - }, - { - "path": "../interface" - }, - { - "path": "../flv.js" - }, - { - "path": "../dash-player" - }, - { - "path": "../danmaku" - } - ] -} \ No newline at end of file diff --git a/src/player/style/widget/button.css b/src/player/widget/button.css similarity index 66% rename from src/player/style/widget/button.css rename to src/player/widget/button.css index ab2eae6..0c8126c 100644 --- a/src/player/style/widget/button.css +++ b/src/player/widget/button.css @@ -1,9 +1,9 @@ .bpui-button { - color: #fff; + color: var(--fff); cursor: pointer; border-radius: 4px; - background-color: #00a1d6; - border: 1px solid #00a1d6; + background-color: var(--00a1d6); + border: 1px solid var(--00a1d6); padding-inline: 10px; min-block-size: 26px; transition: .2s; @@ -20,33 +20,33 @@ } &:hover { - background-color: #00b5e5; - border-color: #00b5e5; + background-color: var(--00b5e5); + border-color: var(--00b5e5); } &:disabled { cursor: default; - background-color: #ccd0d7; - border-color: #ccd0d7; + background-color: var(--ccd0d7); + border-color: var(--ccd0d7); } &.small { font-size: 12px; - color: #99a2aa; - border: 1px solid #ccd0d7; + color: var(--99a2aa); + border: 1px solid var(--ccd0d7); border-radius: 4px; - background-color: #fff; + background-color: var(--fff); padding: 0 8px; block-size: 20px; line-height: 20px; &:hover { - color: #222; - border-color: #6d757a; + color: var(--222); + border-color: var(--6d757a); } &:disabled { - color: #ccd0d7; + color: var(--ccd0d7); border-color: #e2e2e2; } } diff --git a/src/player/style/widget/checkbox.css b/src/player/widget/checkbox/index.css similarity index 77% rename from src/player/style/widget/checkbox.css rename to src/player/widget/checkbox/index.css index 98cf1fd..7db983b 100644 --- a/src/player/style/widget/checkbox.css +++ b/src/player/widget/checkbox/index.css @@ -1,3 +1,8 @@ +input[type="checkbox" i] { + margin: 0; + cursor: pointer; +} + label.bpui-checkbox { vertical-align: middle; cursor: pointer; @@ -13,6 +18,6 @@ label.bpui-checkbox { &:has(input:disabled) { cursor: not-allowed; - color: #ccd0d7; + color: var(--ccd0d7); } } \ No newline at end of file diff --git a/src/player/widget/checkbox.ts b/src/player/widget/checkbox/index.ts similarity index 77% rename from src/player/widget/checkbox.ts rename to src/player/widget/checkbox/index.ts index 7a8d1c9..6e1b6cb 100644 --- a/src/player/widget/checkbox.ts +++ b/src/player/widget/checkbox/index.ts @@ -1,5 +1,4 @@ -import { customElement } from "../../utils/Decorator/customElement"; -import { Element } from "../../utils/element"; +import { customElement } from "../../../utils/Decorator/customElement"; /** * 带标签的复选框 @@ -40,10 +39,10 @@ export class Checkbox extends HTMLLabelElement { // adoptedCallback() {} /** 复选框 */ - $input = Element.add('input', { type: 'checkbox' }, this); + #input = this.appendChild(document.createElement('input')); /** 标签 */ - $label = this.appendChild(document.createTextNode('')); + #label = this.appendChild(document.createTextNode('')); #prev = false; @@ -53,36 +52,37 @@ export class Checkbox extends HTMLLabelElement { set $prev(v) { this.#prev = v; - this.insertAdjacentElement(v ? 'beforeend' : 'afterbegin', this.$input); + this.insertAdjacentElement(v ? 'beforeend' : 'afterbegin', this.#input); } get $text() { - return this.$label.textContent; + return this.#label.textContent; } set $text(v) { - this.$label.textContent = v; + this.#label.textContent = v; } get $value() { - return this.$input.checked; + return this.#input.checked; } set $value(v) { - this.$input.checked = v; + this.#input.checked = v; } get $disabled() { - return this.$input.disabled; + return this.#input.disabled; } set $disabled(v) { - this.$input.disabled = v; + this.#input.disabled = v; } constructor() { super(); this.classList.add('bpui-checkbox'); + this.#input.type = 'checkbox'; } } \ No newline at end of file diff --git a/src/player/style/widget/color.css b/src/player/widget/color/index.css similarity index 78% rename from src/player/style/widget/color.css rename to src/player/widget/color/index.css index c38d013..3213fd7 100644 --- a/src/player/style/widget/color.css +++ b/src/player/widget/color/index.css @@ -1,13 +1,12 @@ .bpui-color { - position: absolute; position-anchor: --bpui-color; - inset-area: block-start span-inline-end; + position-area: block-start span-inline-end; position-visibility: anchors-visible; padding-inline: 6px; padding-block-end: 10px; - border: 1px solid #e2e2e2; + border: 1px solid var(--e2e2e2); border-radius: 4px; - background-color: var(--background-color); + background-color: var(--fff); transition: all .3s ease-in-out; border-start-start-radius: 4px; border-start-end-radius: 4px; @@ -47,7 +46,7 @@ aspect-ratio: 1; &:hover { - box-shadow: inset 0 0 0 1px orange, inset 0 0 0 2px white; + box-shadow: inset 0 0 0 1px var(--ffa500), inset 0 0 0 2px var(--fff); } } } @@ -63,13 +62,13 @@ } &:not(:first-child) { - border-block-start: 1px solid #e2e2e2; + border-block-start: 1px solid var(--e2e2e2); } } .bpui-color-eye { padding: 5px 10px; - border-block-start: 1px solid #e2e2e2; + border-block-start: 1px solid var(--e2e2e2); display: flex; gap: 5px; align-items: center; @@ -81,7 +80,7 @@ display: inline-block; inline-size: 20px; aspect-ratio: 1; - background-image: url(../../assets/image/color.png); + background-image: url(../../../assets/color.png); background-size: contain; } @@ -90,14 +89,14 @@ display: inline-block; flex-grow: 1; padding-block: 3px; - color: #6b6b6b; - border: 1px solid #e2e2e2; + color: var(--6b6b6b); + border: 1px solid var(--e2e2e2); border-radius: 4px; } &:hover { - background-color: #f4f5f7; - color: #6d757a; + background-color: var(--f4f5f7); + color: var(--6d757a); } } } \ No newline at end of file diff --git a/src/player/widget/color.ts b/src/player/widget/color/index.ts similarity index 90% rename from src/player/widget/color.ts rename to src/player/widget/color/index.ts index cc9e499..8c922fd 100644 --- a/src/player/widget/color.ts +++ b/src/player/widget/color/index.ts @@ -1,5 +1,6 @@ -import { customElement } from "../../utils/Decorator/customElement"; -import { Element } from "../../utils/element"; +import { toastr } from "../../../toastr"; +import { customElement } from "../../../utils/Decorator/customElement"; +import { Element } from "../../../utils/element"; /** * 颜色选择 @@ -35,11 +36,11 @@ export class Color extends HTMLDivElement { /** 每当元素被移动到新文档中时调用。 */ // adoptedCallback() {} - private $theme = Element.add('div', { class: 'bpui-color-row', 'data-label': '主题色' }, this); + private $theme = Element.add('div', { class: 'bpui-color-row', data: { label: '主题色' }, appendTo: this }); - private $standard = Element.add('div', { class: 'bpui-color-row', 'data-label': '标准色' }, this); + private $standard = Element.add('div', { class: 'bpui-color-row', data: { label: '标准色' }, appendTo: this }); - private $eye = Element.add('div', { class: 'bpui-color-eye', 'data-value': '#ffffff' }, this, '拾色器'); + private $eye = Element.add('div', { class: 'bpui-color-eye', data: { value: '#ffffff' }, appendTo: this, innerText: '拾色器' }); /** 主题色 */ private $themes = [ @@ -157,6 +158,9 @@ export class Color extends HTMLDivElement { new EyeDropper().open().then(({ sRGBHex }) => { this.$value = sRGBHex; this.dispatchEvent(new Event('change')); + }).catch(e => { + toastr.error(e); + console.log(e); }) }); diff --git a/src/player/style/widget/context.css b/src/player/widget/context/index.css similarity index 70% rename from src/player/style/widget/context.css rename to src/player/widget/context/index.css index afef212..91496e5 100644 --- a/src/player/style/widget/context.css +++ b/src/player/widget/context/index.css @@ -1,14 +1,14 @@ [popover].bpui-context { - border: 1px solid #e2e2e2; + border: 1px solid var(--e2e2e2); padding: initial; margin: initial; outline: initial; inset: initial; box-sizing: border-box; - background: #fff; + background: var(--fff); border-radius: 3px; - box-shadow: 0 0 5px #e2e2e2; - text-shadow: 0 0 #e2e2e2; + box-shadow: 0 0 5px var(--e2e2e2); + text-shadow: 0 0 var(--e2e2e2); display: flex; flex-direction: column; justify-content: flex-start; @@ -20,9 +20,9 @@ overflow: visible; &.black { - background-color: rgba(28, 28, 28, .9); + background-color: var(--1c1c1ce6); border: none; - text-shadow: 0 0 2px rgba(0, 0, 0, .5); + text-shadow: 0 0 2px var(--00000080); box-shadow: none; } diff --git a/src/player/widget/context.ts b/src/player/widget/context/index.ts similarity index 97% rename from src/player/widget/context.ts rename to src/player/widget/context/index.ts index fa52bbf..11e8914 100644 --- a/src/player/widget/context.ts +++ b/src/player/widget/context/index.ts @@ -1,4 +1,4 @@ -import { customElement } from "../../utils/Decorator/customElement"; +import { customElement } from "../../../utils/Decorator/customElement"; /** 右键菜单 */ @customElement('ul') diff --git a/src/player/widget/index.css b/src/player/widget/index.css new file mode 100644 index 0000000..1ce3139 --- /dev/null +++ b/src/player/widget/index.css @@ -0,0 +1,6 @@ +@import url(./checkbox/index.css); +@import url(./select.css); +@import url(./context/index.css); +@import url(./color/index.css); +@import url(./button.css); +@import url(./local-font.css); \ No newline at end of file diff --git a/src/player/widget/local-font.css b/src/player/widget/local-font.css new file mode 100644 index 0000000..63fd641 --- /dev/null +++ b/src/player/widget/local-font.css @@ -0,0 +1,34 @@ +[popover].popover-local-font { + inset-inline-start: 50%; + inset-block-start: 50%; + translate: -50% -50%; + border: 1px solid var(--ccd0d7); + border-radius: 4px; + padding: 4px; + align-items: center; + max-inline-size: 800px; + max-block-size: 600px; + overflow-y: auto; + scrollbar-width: none; + display: flex; + flex-wrap: wrap; + gap: .5em; + + &>button { + appearance: none; + outline: none; + border: 1px solid var(--ccd0d7); + border-radius: 4px; + padding: .5em; + inline-size: fit-content; + block-size: fit-content; + background-color: var(--fff); + cursor: pointer; + + &:hover { + background-color: var(--fff); + background-color: var(--ccd0d7); + color: var(--6d757a); + } + } +} \ No newline at end of file diff --git a/src/player/style/widget/select.css b/src/player/widget/select.css similarity index 100% rename from src/player/style/widget/select.css rename to src/player/widget/select.css diff --git a/src/player/widget/slider.ts b/src/player/widget/slider.ts deleted file mode 100644 index 1a069e5..0000000 --- a/src/player/widget/slider.ts +++ /dev/null @@ -1,130 +0,0 @@ -import { customElement } from "../../utils/Decorator/customElement"; -import { Format } from "../../utils/fomat"; - -/** 滚动条 */ -@customElement('input') -export class Slider extends HTMLInputElement { - - /** - * 需要监听变动的属性。 - * 与实例方法`attributeChangedCallback`配合使用。 - * 此字符串序列定义了`attributeChangedCallback`回调时的第一个参数的可能值。 - */ - static observedAttributes = ['min', 'max', 'step']; - - /** - * 在属性更改、添加、移除或替换时调用。 - * 需要与静态属性`observedAttributes`配合使用。 - * 此回调的第一个参数在`observedAttributes`数组中定义。 - */ - attributeChangedCallback(name: 'min' | 'max' | 'step', oldValue: string, newValue: string) { - switch (name) { - case "min": - case "max": - case 'step': { - this.linearGradient(); - break; - } - } - } - - /** 初始化标记 */ - // #inited = false; - - /** 每当元素添加到文档中时调用。 */ - // connectedCallback() {} - - /** 每当元素从文档中移除时调用。 */ - // disconnectedCallback() {} - - /** 每当元素被移动到新文档中时调用。 */ - // adoptedCallback() {} - - /** 进度颜色 */ - $colorProgress = '#00a1d6'; - - /** 背景色 */ - $colorTrack = '#e5e9ef'; - - /** 缓冲色 */ - $colorBuffer = '#8adced'; - - #buffer: [number, number][] = []; - - #formatHint = (value: string) => value; - - /** 设置缓冲区 */ - set $buffer(buffer: [number, number][]) { - this.#buffer = buffer.sort((a, b) => a[0] - b[0] > 0 ? 1 : -1); - this.linearGradient(); - } - - /** 当前值 */ - get $value() { - return this.value; - } - - set $value(v) { - this.dataset.value = this.#formatHint(this.value = v); - this.linearGradient(); - } - - /** 浮动显示当前数值 */ - set $hint(v: boolean) { - this.classList.toggle('hint', v); - } - - constructor() { - super(); - - this.type = 'range'; - this.classList.add('bpui-slider'); - - this.addEventListener('change', () => { - this.$value = this.value; - }); - new IntersectionObserver(this.$intersectionObserver).observe(this); - - this.defaultValue = '0'; - } - - private $intersectionObserver = (entries: IntersectionObserverEntry[]) => { - for (const entry of entries) { - if (entry.isIntersecting) { - this.linearGradient(); - break; - } - } - } - - /** 更新进度或缓冲色 */ - private linearGradient = () => { - const value = Format.fmRange((+this.value - +this.min) / ((+this.max || 100) - +this.min), 0, 1); - const linear = [[this.$colorProgress, 0, Format.toFixed(value * 100) + '%']]; - /** 当前最大值,用于还原背景色 */ - let max = value; - for (const buffer of this.#buffer) { - if (buffer[1] < value) break; - max = buffer[1]; - if (buffer[0] > value) { - linear.push( - [this.$colorTrack, Format.toFixed(value * 100) + '%', Format.toFixed(buffer[0] * 100) + '%'], - [this.$colorBuffer, Format.toFixed(buffer[0] * 100) + '%', Format.toFixed(buffer[1] * 100) + '%'] - ); - } else { - linear.push([this.$colorBuffer, Format.toFixed(value * 100) + '%', Format.toFixed(buffer[1] * 100) + '%']); - } - } - linear.push([this.$colorTrack, Format.toFixed(max * 100) + '%', '100%']); - this.style.setProperty('--linear-gradient', `linear-gradient(to right,${linear.map(d => d.join(' ')).join(',')})`); - } - - /** - * 格式化浮动信息 - * - * @param callback 用于修改浮动信息的回调函数,将原始值传入 - */ - formatHint(callback: (value: string) => string) { - this.#formatHint = callback; - } -} \ No newline at end of file diff --git a/src/player/widget/slider/index.css b/src/player/widget/slider/index.css new file mode 100644 index 0000000..8ec2fdd --- /dev/null +++ b/src/player/widget/slider/index.css @@ -0,0 +1,83 @@ +@scope { + :scope { + flex-grow: 1; + display: flex; + align-items: center; + position: relative; + + >.slider { + appearance: none; + outline: 0; + margin: 0; + border: 0; + cursor: pointer; + position: relative; + inline-size: 100%; + background-color: transparent; + + + &::-webkit-slider-runnable-track { + background-color: transparent; + border-radius: 4px; + block-size: .5em; + } + + &::-webkit-slider-thumb { + appearance: none; + border-radius: 7px; + cursor: pointer; + box-shadow: 0 0 3px var(--017cc3); + background-color: var(--fff); + inline-size: 14px; + block-size: 14px; + position: relative; + inset-block-start: -4.5px; + anchor-name: --slider-thumb; + } + + &.hint { + &::after { + content: attr(data-value); + background-color: var(--fff); + position: absolute; + position-anchor: --slider-thumb; + position-area: block-start; + transition: all .3s; + opacity: 0; + padding-inline: 4px; + padding-block: 2px; + border-radius: 4px; + + } + + &:hover::after { + opacity: 1; + } + } + } + + >progress { + appearance: none; + position: absolute; + inline-size: 100%; + inset-inline: 0; + block-size: .5em; + border-radius: 4px; + vertical-align: unset; + overflow: clip; + pointer-events: none; + + &::-webkit-progress-bar { + background-color: transparent; + } + + &::-webkit-progress-value { + background-color: var(--00a1d6); + } + + &.bar::-webkit-progress-bar { + background-color: var(--e5e9ef); + } + } + } +} \ No newline at end of file diff --git a/src/player/widget/slider/index.d.css.ts b/src/player/widget/slider/index.d.css.ts new file mode 100644 index 0000000..eb9bc4f --- /dev/null +++ b/src/player/widget/slider/index.d.css.ts @@ -0,0 +1,2 @@ +declare const stylesheet: CSSStyleSheet; +export default stylesheet; \ No newline at end of file diff --git a/src/player/widget/slider/index.ts b/src/player/widget/slider/index.ts new file mode 100644 index 0000000..c553f45 --- /dev/null +++ b/src/player/widget/slider/index.ts @@ -0,0 +1,104 @@ +import { CSSStyleSheet2HTMLStyleElement } from "../../../utils/CSSStyleSheet2HTMLStyleElement"; +import { customElement } from "../../../utils/Decorator/customElement"; +import stylesheet from "./index.css" with {type: 'css'}; + +/** 播放器主区域 */ +@customElement('form') +export class Slider extends HTMLFormElement { + + /** + * 需要监听变动的属性。 + * 与实例方法`attributeChangedCallback`配合使用。 + * 此字符串序列定义了`attributeChangedCallback`回调时的第一个参数的可能值。 + */ + // static observedAttributes = []; + + /** + * 在属性更改、添加、移除或替换时调用。 + * 需要与静态属性`observedAttributes`配合使用。 + * 此回调的第一个参数在`observedAttributes`数组中定义。 + */ + // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} + + /** 每当元素添加到文档中时调用。 */ + // connectedCallback() { } + + /** 每当元素从文档中移除时调用。 */ + // disconnectedCallback() {} + + /** 每当元素被移动到新文档中时调用。 */ + // adoptedCallback() {} + + /** 轨道 */ + protected $bar = this.appendChild(document.createElement('progress')); + + /** 进度 */ + #progress = this.appendChild(document.createElement('progress')); + + /** 进度条 */ + #slider = this.appendChild(document.createElement('input')); + + #formatHint = (value: number) => value; + + /** 默认值 */ + set $defaultValue(v: number) { + this.#slider.defaultValue = v; + } + + /** 步进 */ + set $step(v: number | string) { + this.#slider.step = v; + } + + /** 最大值 */ + set $max(v: number) { + this.#slider.max = (this.#progress.max = v); + } + + /** 当前值 */ + set $value(v: number) { + this.#slider.value = (this.#progress.value = v); + this.#slider.dataset.value = this.#formatHint(v); + } + + get $value() { + return +this.#slider.value; + } + + /** 浮动显示当前数值 */ + set $hint(v: boolean) { + this.#slider.classList.toggle('hint', v); + } + + /** 设定浮动显示值 */ + set $hintValue(v: string) { + this.#slider.dataset.value = v; + } + + constructor() { + super(); + + // this.#host.adoptedStyleSheets = [stylesheet]; // 文档画中画模式会导致构造的样式表丢失,暂时取道 style 元素代替 + this.appendChild(CSSStyleSheet2HTMLStyleElement(stylesheet)); + + this.classList.add('bpui-slider'); + this.#slider.type = 'range'; + this.#slider.classList.add('slider'); + this.$bar.classList.add('bar'); + + this.#slider.addEventListener('change', () => { + this.#progress.value = +this.#slider.value; + }); + + this.$max = 100; + } + + /** + * 格式化浮动信息 + * + * @param callback 用于修改浮动信息的回调函数,将原始值传入 + */ + formatHint(callback: (value: number) => string | number) { + this.#formatHint = callback; + } +} \ No newline at end of file diff --git a/src/sidebar/index.css b/src/sidebar/index.css new file mode 100644 index 0000000..4e41b69 --- /dev/null +++ b/src/sidebar/index.css @@ -0,0 +1,3 @@ +body { + margin: 0; +} \ No newline at end of file diff --git a/src/sidebar/index.d.css.ts b/src/sidebar/index.d.css.ts new file mode 100644 index 0000000..eb9bc4f --- /dev/null +++ b/src/sidebar/index.d.css.ts @@ -0,0 +1,2 @@ +declare const stylesheet: CSSStyleSheet; +export default stylesheet; \ No newline at end of file diff --git a/src/sidebar/index.html b/src/sidebar/index.html new file mode 100644 index 0000000..5ffabf3 --- /dev/null +++ b/src/sidebar/index.html @@ -0,0 +1,14 @@ + + + + + + 哔哩哔哩 (゜-゜)つロ 干杯~-bilibili + + + + + + + + \ No newline at end of file diff --git a/src/sidebar/index.ts b/src/sidebar/index.ts new file mode 100644 index 0000000..414cfe5 --- /dev/null +++ b/src/sidebar/index.ts @@ -0,0 +1,3 @@ +import stylesheet from "./index.css" with {type: 'css'}; + +document.adoptedStyleSheets = [stylesheet]; \ No newline at end of file diff --git a/src/sw/GM/escape-header.ts b/src/sw/GM/escape-header.ts deleted file mode 100644 index 05dd7a4..0000000 --- a/src/sw/GM/escape-header.ts +++ /dev/null @@ -1,114 +0,0 @@ -/** 简单请求头 */ -const CORS_safelisted_request_header = [ - "accept", - "accept-language", - "content-language", - "content-type", - "user-agent", - "referer" -]; - -class Headers { - private headers: Record = {} - /** - * 拦截请求头 - * @param headers 要添加的请求头 - */ - constructor(headers?: chrome.declarativeNetRequest.ModifyHeaderInfo | chrome.declarativeNetRequest.ModifyHeaderInfo[]) { - if (headers) { - Array.isArray(headers) || (headers = [headers]); - this.push(...headers); - } - } - /** 添加拦截请求头 */ - push(...headers: chrome.declarativeNetRequest.ModifyHeaderInfo[]) { - headers.forEach(d => { - this.headers[d.header] = d; - }); - } - /** - * 移除请求头 - * @param ids 请求头名 - */ - remove(...ids: string[]) { - ids.forEach(d => { - delete this.headers[d]; - }) - } - /** - * 移除请求头 - * @param headers 请求头数据 - */ - pop(...headers: chrome.declarativeNetRequest.ModifyHeaderInfo[]) { - this.remove(...headers.map(d => d.header)); - } - /** 输出请求头数据 */ - toJSON() { - return Object.values(this.headers); - } - set(obj: Record) { - this.push(...Object.entries(obj).map(d => { - const header: chrome.declarativeNetRequest.ModifyHeaderInfo = { - header: d[0], - operation: d[1] ? 'set' : 'remove' - }; - d[1] && (header.value = d[1]); - return header; - })) - } -} - -/** - * 拦截修改ajax禁止修改表头 - * @param input 目标url - * @param requestHeaders 请求头 - * @returns 请求完成后用于取消对于该请求的拦截 - */ -export function swFetchHeader(input: RequestInfo | URL, requestHeaders?: Record, responseHeaders?: Record) { - if (input instanceof Request) { - input = input.url; - } else if (input instanceof URL) { - input = input.toJSON(); - } - /** 随机互斥ID */ - const id = Math.ceil(Math.random() * 1e8); - /** 预设请求头 */ - const request = new Headers({ - header: 'origin', // origin会暴露拓展名,必须去掉 - operation: 'remove' - }); - /** 预设返回头 */ - const response = new Headers(); - /** 拦截规则 */ - const rule: chrome.declarativeNetRequest.Rule = { - id, - action: { - type: 'modifyHeaders' - }, - condition: { - urlFilter: input, // 精确匹配网址 - resourceTypes: [ - 'main_frame', - 'sub_frame', - 'stylesheet', - 'script', - 'image', - 'font', - 'object', - 'xmlhttprequest', - 'ping', - 'csp_report', - 'media', - 'websocket', - 'other' - ] - } - }; - requestHeaders && request.set(requestHeaders); // 添加请求头 - responseHeaders && response.set(responseHeaders); // 添加返回头 - const reH = request.toJSON(); - const rpH = response.toJSON(); - reH.length && (rule.action.requestHeaders = reH); - rpH.length && (rule.action.responseHeaders = rpH); - return <[chrome.declarativeNetRequest.Rule, number]>[rule, id]; -} \ No newline at end of file diff --git a/src/sw/GM/session-rule.ts b/src/sw/GM/session-rule.ts deleted file mode 100644 index 3f4d422..0000000 --- a/src/sw/GM/session-rule.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * 更新 declarativeNetRequest 会话规则表 - * @param tabId 规则所属标签页 - * @param rules 新增规则,可不传递以清空该标签所有已运用的规则 - * @param tab 是否绑定tabid - */ -export function updateSessionRules(tabId: number, rules: chrome.declarativeNetRequest.Rule[], tab = true) { - // 有规则添加规则 - tab && rules.forEach(d => { - // 为每条规则限定标签ID - d.condition.tabIds = [tabId]; - }); - return chrome.declarativeNetRequest.updateSessionRules({ addRules: rules }); -} \ No newline at end of file diff --git a/src/sw/README.md b/src/sw/README.md deleted file mode 100644 index aafc0be..0000000 --- a/src/sw/README.md +++ /dev/null @@ -1 +0,0 @@ -后台脚本(Service Worker) \ No newline at end of file diff --git a/src/sw/index.ts b/src/sw/index.ts deleted file mode 100644 index e259c54..0000000 --- a/src/sw/index.ts +++ /dev/null @@ -1,52 +0,0 @@ -// 后台脚本 - -import { IGMPostMessage } from "../utils/interface/GM"; -import { swFetchHeader } from "./GM/escape-header"; -import { updateSessionRules } from "./GM/session-rule"; - -// 消息监听 -chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { - if (sender.tab && typeof message === "object") { - switch (message.type) { - /** 跨域版`fetch` */ - case "fetch": { - const [rule, id] = swFetchHeader(message.message.input, message.message.init?.headers, message.message.responseHeaders); - if (Array.isArray(message.message.init?.body)) { - // 数组还原为二进制数据 - message.message.init.body = new Uint8Array(message.message.init.body); - } - chrome.declarativeNetRequest.updateSessionRules({ addRules: [rule] }) - .then(() => fetch(message.message.input, message.message.init)) - .then(async d => { - return { - status: d.status, - statusText: d.statusText, - header: [...d.headers], - url: d.url, - redirected: d.redirected, - type: d.type, - data: Array.from(new Uint8Array(await d.arrayBuffer())) // JSON-serializable - }; - }) - .then(data => sendResponse({ data })) - .catch(err => sendResponse({ err: err.toString() })) - .finally(() => chrome.declarativeNetRequest.updateSessionRules({ removeRuleIds: [id] })); - return true; - } - /** 更新网络拦截规则 */ - case "updateSessionRules": { - updateSessionRules(sender.tab.id!, message.message.rules, message.message.tab) - .then(data => sendResponse({ data })) - .catch(err => sendResponse({ err: err.toString() })); - return true; - } - /** 移除网络规则集 */ - case "removeSessionRules": { - chrome.declarativeNetRequest.updateSessionRules({ - removeRuleIds: message.message.ids - }) - break; - } - } - } -}); \ No newline at end of file diff --git a/src/sw/tsconfig.json b/src/sw/tsconfig.json deleted file mode 100644 index 9bb4b02..0000000 --- a/src/sw/tsconfig.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "extends": "../tsconfig-base.json", - "include": [ - "**/*", - "../utils" - ], - "references": [ - { - "path": "../utils" - } - ], - "compilerOptions": { - "types": [ - "chrome" - ] - } -} \ No newline at end of file diff --git a/src/toastr/README.md b/src/toastr/README.md new file mode 100644 index 0000000..43c1491 --- /dev/null +++ b/src/toastr/README.md @@ -0,0 +1 @@ +参考来源[toastr](https://github.com/CodeSeven/toastr) \ No newline at end of file diff --git a/src/toastr/assets/error.png b/src/toastr/assets/error.png new file mode 100644 index 0000000..90565a9 Binary files /dev/null and b/src/toastr/assets/error.png differ diff --git a/src/toastr/assets/info.png b/src/toastr/assets/info.png new file mode 100644 index 0000000..037ea99 Binary files /dev/null and b/src/toastr/assets/info.png differ diff --git a/src/toastr/assets/success.png b/src/toastr/assets/success.png new file mode 100644 index 0000000..5d9b3cf Binary files /dev/null and b/src/toastr/assets/success.png differ diff --git a/src/toastr/assets/warn.png b/src/toastr/assets/warn.png new file mode 100644 index 0000000..21ac2b2 Binary files /dev/null and b/src/toastr/assets/warn.png differ diff --git a/src/toastr/index.css b/src/toastr/index.css new file mode 100644 index 0000000..615582c --- /dev/null +++ b/src/toastr/index.css @@ -0,0 +1,101 @@ +@scope { + :scope { + inline-size: 100%; + block-size: 100%; + padding: 0; + margin: 0; + border: 0; + font-size: 14px; + text-align: start; + background-color: transparent; + pointer-events: none; + + --delay: 4s; + } +} + +.container { + position: absolute; + inset-block-start: 1em; + inset-inline-end: 1em; + display: flex; + flex-direction: column; + row-gap: .5em; + + >div { + inline-size: 300px; + /* block-size: calc-size(auto); */ + padding-block: 1.25em; + padding-inline-start: 4em; + padding-inline-end: 1.25em; + border-radius: .25em; + background-color: #2f96b4; + background-image: url(./assets/info.png); + background-position: 1.25em center; + background-repeat: no-repeat; + box-shadow: 0 0 12px #999; + color: #fff; + opacity: .8; + box-sizing: border-box; + pointer-events: auto; + overflow: clip; + word-wrap: break-word; + transform-origin: center 0; + cursor: pointer; + transition: all 0.7s allow-discrete; + animation: toastr 0s linear var(--delay); + + + @starting-style { + opacity: 0; + scale: 1 0; + } + + &.success { + background-color: #51a351; + background-image: url(./assets/success.png); + } + + &.warn { + background-color: #f89406; + background-image: url(./assets/warn.png); + } + + &.error { + background-color: #bd362f; + background-image: url(./assets/error.png); + } + + &:hover { + box-shadow: 0 0 12px #000; + opacity: 1; + animation-play-state: paused; + } + + &.hold { + animation-play-state: paused; + } + + &.hide { + opacity: 0; + scale: 1 0; + } + } + + &.top-left { + inset-inline: 1em auto; + } + + &.bottom-left { + flex-direction: column-reverse; + inset-inline: 1em auto; + inset-block: auto 1em; + } + + &.bottom-right { + flex-direction: column-reverse; + inset-block: auto 1em; + } +} + +@keyframes toastr {} \ No newline at end of file diff --git a/src/toastr/index.d.css.ts b/src/toastr/index.d.css.ts new file mode 100644 index 0000000..eb9bc4f --- /dev/null +++ b/src/toastr/index.d.css.ts @@ -0,0 +1,2 @@ +declare const stylesheet: CSSStyleSheet; +export default stylesheet; \ No newline at end of file diff --git a/src/toastr/index.ts b/src/toastr/index.ts new file mode 100644 index 0000000..eff06a6 --- /dev/null +++ b/src/toastr/index.ts @@ -0,0 +1,100 @@ +import { customElement } from "../utils/Decorator/customElement"; +import { Toast } from "./toast"; +import stylesheet from "./index.css" with {type: 'css'}; + +@customElement(undefined, `toastr-${Date.now()}`) +class Toastr extends HTMLElement { + + /** + * 需要监听变动的属性。 + * 与实例方法`attributeChangedCallback`配合使用。 + * 此字符串序列定义了`attributeChangedCallback`回调时的第一个参数的可能值。 + */ + // static observedAttributes = []; + + /** + * 在属性更改、添加、移除或替换时调用。 + * 需要与静态属性`observedAttributes`配合使用。 + * 此回调的第一个参数在`observedAttributes`数组中定义。 + */ + // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} + + /** 每当元素添加到文档中时调用。 */ + connectedCallback() { + this.popover = 'manual'; + this.showPopover(); + } + + /** 每当元素从文档中移除时调用。 */ + // disconnectedCallback() {} + + /** 每当元素被移动到新文档中时调用。 */ + // adoptedCallback() {} + + #host = this.attachShadow({ mode: 'closed' }); + + #container = this.#host.appendChild(document.createElement('div')) + + /** 设定默认通知存在时间:单位/秒(会被通知实例自己设定的时间覆盖) */ + set $delay(v: number) { + v ? this.style.setProperty('--delay', v + 's') : this.style.removeProperty('--delay'); + } + + set $position(v: 'top-left' | 'bottom-left' | 'bottom-right' | '') { + switch (v) { + case "": { + this.#container.classList.remove('top-left'); + this.#container.classList.remove('bottom-left'); + this.#container.classList.remove('bottom-right'); + break; + } + default: { + this.#container.classList.remove('top-left'); + this.#container.classList.remove('bottom-left'); + this.#container.classList.remove('bottom-right'); + this.#container.classList.add(v); + } + } + } + + constructor() { + super(); + + this.#host.adoptedStyleSheets = [stylesheet]; + this.#container.classList.add('container'); + } + + info(...args: string[]) { + document.body.contains(this) || document.body.appendChild(this); + const div = new Toast(...args); + this.#container.appendChild(div); + return div; + } + + success(...args: string[]) { + document.body.contains(this) || document.body.appendChild(this); + const div = new Toast(...args); + div.$type = 'success'; + this.#container.appendChild(div); + return div; + } + + warn(...args: string[]) { + document.body.contains(this) || document.body.appendChild(this); + const div = new Toast(...args); + div.$type = 'warn'; + this.#container.appendChild(div); + return div; + } + + error(...args: string[]) { + document.body.contains(this) || document.body.appendChild(this); + const div = new Toast(...args); + div.$type = 'error'; + this.#container.appendChild(div); + return div; + } +} + +/** 通知组件 */ +export const toastr = new Toastr(); diff --git a/src/toastr/toast.ts b/src/toastr/toast.ts new file mode 100644 index 0000000..00bfc63 --- /dev/null +++ b/src/toastr/toast.ts @@ -0,0 +1,80 @@ +import { customElement } from "../utils/Decorator/customElement"; + +@customElement('div') +export class Toast extends HTMLDivElement { + + /** + * 需要监听变动的属性。 + * 与实例方法`attributeChangedCallback`配合使用。 + * 此字符串序列定义了`attributeChangedCallback`回调时的第一个参数的可能值。 + */ + // static observedAttributes = []; + + /** + * 在属性更改、添加、移除或替换时调用。 + * 需要与静态属性`observedAttributes`配合使用。 + * 此回调的第一个参数在`observedAttributes`数组中定义。 + */ + // attributeChangedCallback(name: IobservedAttributes, oldValue: string, newValue: string) {} + + /** 每当元素添加到文档中时调用。 */ + // connectedCallback() {} + + /** 每当元素从文档中移除时调用。 */ + // disconnectedCallback() {} + + /** 每当元素被移动到新文档中时调用。 */ + // adoptedCallback() {} + + /** 通知类型 */ + set $type(v: 'success' | 'warn' | 'error' | '') { + switch (v) { + case "": { + this.classList.remove('success'); + this.classList.remove('warn'); + this.classList.remove('error'); + break; + } + default: { + this.classList.remove('success'); + this.classList.remove('warn'); + this.classList.remove('error'); + this.classList.add(v); + break; + } + } + } + + /** 设定通知存在时间:单位/秒(为 0 时将永远存在直到重新设定) */ + set $delay(v: number) { + if (v) { + this.style.setProperty('--delay', v + 's'); + this.classList.remove('hold'); + } else { + this.style.removeProperty('--delay'); + this.classList.add('hold'); + } + } + + constructor(...args: string[]) { + super(); + + this.innerText = args.join('\n'); + this.addEventListener('animationend', this.onAnimationEnd) + } + + private onAnimationEnd = ({ animationName }: AnimationEvent) => { + if (animationName === 'toastr') { + this.addEventListener('transitionend', () => { + this.remove(); + }, { once: true }); + this.classList.add('hide'); + this.removeEventListener('animationend', this.onAnimationEnd); + } + } + + /** 补充通知内容 */ + appendText(...args: string[]) { + this.innerText += '\n' + args.join('\n'); + } +} \ No newline at end of file diff --git a/src/tsconfig-base.json b/src/tsconfig-base.json deleted file mode 100644 index f251cda..0000000 --- a/src/tsconfig-base.json +++ /dev/null @@ -1,101 +0,0 @@ -{ - "compilerOptions": { - /* Visit https://aka.ms/tsconfig to read more about this file */ - /* Projects */ - // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ - "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ - // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ - // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ - // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ - // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ - /* Language and Environment */ - "target": "ESNext", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ - // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ - // "jsx": "preserve", /* Specify what JSX code is generated. */ - // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ - // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ - // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ - // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ - // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ - // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ - // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ - // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ - // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ - /* Modules */ - "module": "ESNext", /* Specify what module code is generated. */ - // "rootDir": "./", /* Specify the root folder within your source files. */ - "moduleResolution": "Bundler", /* Specify how TypeScript looks up a file from a given module specifier. */ - // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ - // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ - // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ - // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ - "types": [], /* Specify type package names to be included without being referenced in a source file. */ - // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ - // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ - // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ - // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ - // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ - // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ - "resolveJsonModule": true, /* Enable importing .json files. */ - "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ - // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ - /* JavaScript Support */ - // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ - // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ - // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ - /* Emit */ - "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ - // "declarationMap": true, /* Create sourcemaps for d.ts files. */ - // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ - // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ - // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ - // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ - // "outDir": "./", /* Specify an output folder for all emitted files. */ - // "removeComments": true, /* Disable emitting comments. */ - // "noEmit": true, /* Disable emitting files from a compilation. */ - // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ - // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ - // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ - // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ - // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ - // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ - // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ - // "newLine": "crlf", /* Set the newline character for emitting files. */ - // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ - // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ - // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ - // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ - // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ - // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ - /* Interop Constraints */ - // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ - // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ - // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ - "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ - // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ - "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ - /* Type Checking */ - "strict": true, /* Enable all strict type-checking options. */ - // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ - // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ - // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ - // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ - // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ - // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ - // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ - // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ - // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ - // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ - // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ - // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ - // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ - // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ - // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ - // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ - // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ - // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ - /* Completeness */ - // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ - "skipLibCheck": true /* Skip type checking all .d.ts files. */ - } -} \ No newline at end of file diff --git a/src/tsconfig.json b/src/tsconfig.json index e7ed895..cab64ed 100644 --- a/src/tsconfig.json +++ b/src/tsconfig.json @@ -1,33 +1,100 @@ { - "files": [], - "include": [], - "references": [ - { - "path": "./danmaku" - }, - { - "path": "./dash-player" - }, - { - "path": "./flv.js" - }, - { - "path": "./interface" - }, - { - "path": "./io" - }, - { - "path": "./main" - }, - { - "path": "./player" - }, - { - "path": "./sw" - }, - { - "path": "./utils" - } - ] + "compilerOptions": { + /* Visit https://aka.ms/tsconfig to read more about this file */ + /* Projects */ + // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ + // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ + // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ + // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ + // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ + // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ + /* Language and Environment */ + "target": "ESNext", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + // "jsx": "preserve", /* Specify what JSX code is generated. */ + // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ + // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ + // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ + // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ + // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ + // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ + // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ + // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ + // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ + /* Modules */ + "module": "ESNext", /* Specify what module code is generated. */ + // "rootDir": "./", /* Specify the root folder within your source files. */ + "moduleResolution": "Bundler", /* Specify how TypeScript looks up a file from a given module specifier. */ + // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ + // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ + // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ + // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ + // "types": [], /* Specify type package names to be included without being referenced in a source file. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ + "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ + // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ + // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ + // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ + // "resolveJsonModule": true, /* Enable importing .json files. */ + "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ + // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ + /* JavaScript Support */ + // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ + // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ + // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ + /* Emit */ + // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ + // "declarationMap": true, /* Create sourcemaps for d.ts files. */ + // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ + // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ + // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ + // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ + // "outDir": "./", /* Specify an output folder for all emitted files. */ + // "removeComments": true, /* Disable emitting comments. */ + "noEmit": true, /* Disable emitting files from a compilation. */ + // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ + // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ + // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ + // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ + // "newLine": "crlf", /* Set the newline character for emitting files. */ + // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ + // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ + // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ + // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ + // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ + /* Interop Constraints */ + // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ + // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ + // "isolatedDeclarations": true, /* Require sufficient annotation on exports so other tools can trivially generate declaration files. */ + // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ + "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ + // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ + "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ + /* Type Checking */ + "strict": true, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ + // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ + // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ + // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ + // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ + // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ + // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ + // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ + // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ + // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ + // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ + // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ + // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ + // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ + /* Completeness */ + // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ + } } \ No newline at end of file diff --git a/src/utils/CSSStyleSheet2HTMLStyleElement.ts b/src/utils/CSSStyleSheet2HTMLStyleElement.ts new file mode 100644 index 0000000..a51e337 --- /dev/null +++ b/src/utils/CSSStyleSheet2HTMLStyleElement.ts @@ -0,0 +1,10 @@ +/** + * CSS 样式表转样式节点 + * + * @param stylesheet CSS 样式表 + */ +export function CSSStyleSheet2HTMLStyleElement(stylesheet: CSSStyleSheet) { + const style = document.createElement('style'); + style.textContent = [...stylesheet.cssRules].map(d => d.cssText).join(''); + return style; +} \ No newline at end of file diff --git a/src/utils/TOTP.ts b/src/utils/TOTP.ts deleted file mode 100644 index 0cbebe9..0000000 --- a/src/utils/TOTP.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { crypto } from "./md5"; - -/** 基于哈希消息认证码的一次性口令 */ -export namespace TOTP { - - /** 密码有效秒数 */ - const STEP = 30; - /** 生成密码位数 */ - const DIGIT = 6; - - export function now() { - - const count = Math.floor((Date.now() / 1000) / STEP); - - const stringArray = crypto.sha1.hmac.raw(__TOTP_KEY__, count.toString()); // HSA1加密 - - // Truncate 截断函数 - const offset = stringArray[19] & 15; // 选取最后一个字节的低字节位4位的整数值作为偏移量 - // 从指定偏移位开始,连续截取 4 个字节(32 位),最后返回 32 位中的后面 31 位 - const p = (stringArray[offset] & 127) << 24 | - (stringArray[offset + 1] & 255) << 16 | - (stringArray[offset + 2] & 255) << 8 | - (stringArray[offset + 3] & 255); - const result = (p % Math.pow(10, DIGIT)).toString(); - - return result; - } -} - -//////////////////////////// 全局增强 //////////////////////////// -declare global { - /** 基于哈希消息认证码的一次性口令的密钥 */ - const __TOTP_KEY__: string; -} \ No newline at end of file diff --git a/src/utils/TTOP.ts b/src/utils/TTOP.ts new file mode 100644 index 0000000..f073372 --- /dev/null +++ b/src/utils/TTOP.ts @@ -0,0 +1,31 @@ +import { crypto } from "./md5"; + +/** 密码有效秒数 */ +const STEP = 30; +/** 生成密码位数 */ +const DIGIT = 6; + +/** 基于哈希消息认证码的一次性口令 */ +export function TOTP() { + + const count = Math.floor((Date.now() / 1000) / STEP); + + const stringArray = crypto.sha1.hmac.raw(__TOTP_KEY__, count.toString()); // HSA1加密 + + // Truncate 截断函数 + const offset = stringArray[19] & 15; // 选取最后一个字节的低字节位4位的整数值作为偏移量 + // 从指定偏移位开始,连续截取 4 个字节(32 位),最后返回 32 位中的后面 31 位 + const p = (stringArray[offset] & 127) << 24 | + (stringArray[offset + 1] & 255) << 16 | + (stringArray[offset + 2] & 255) << 8 | + (stringArray[offset + 3] & 255); + const result = (p % Math.pow(10, DIGIT)).toString(); + + return result; +} + +//////////////////////////// 全局增强 //////////////////////////// +declare global { + /** 基于哈希消息认证码的一次性口令的密钥 */ + const __TOTP_KEY__: string; +} \ No newline at end of file diff --git a/src/utils/av.ts b/src/utils/av.ts index 53b3fcb..9fe7416 100644 --- a/src/utils/av.ts +++ b/src/utils/av.ts @@ -4,7 +4,6 @@ export namespace AV { const MASK_CODE = 2251799813685247n; const MAX_AID = 1n << 51n; - const MIN_AID = 1n; const BASE = 58n; const BYTES = ['B', 'V', 1, '', '', '', '', '', '', '', '', '']; diff --git a/src/utils/base64.ts b/src/utils/base64.ts index 518c287..d9e8d67 100644 --- a/src/utils/base64.ts +++ b/src/utils/base64.ts @@ -1,40 +1,43 @@ // @see MDN_Web_Docs {@link https://developer.mozilla.org/en-US/docs/Glossary/Base64} /** Base64编解码工具。 */ -export const base64 = new (class { +export namespace base64 { /** * Base64编码 * @param str 原始字符串 * @returns 编码结果 */ - encode(str: string) { + export function encode(str: string) { return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function (match, p1) { return String.fromCharCode(('0x' + p1)); })); } + /** * Base64解码 * @param str 原始字符串 * @returns 解码结果 */ - decode(str: string) { + export function decode(str: string) { return decodeURIComponent(atob(str).split('').map(function (c) { return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2); }).join('')); } + /** * Base64编码 * @param buffer Uint8Array * @returns 编码结果 */ - encodeFromUint8Array(buffer: Uint8Array) { + export function encodeFromUint8Array(buffer: Uint8Array) { return btoa(String.fromCharCode(...buffer)); } + /** * Base64编码 * @param str * @returns */ - decodeToUint8Array(str: string) { + export function decodeToUint8Array(str: string) { return new Uint8Array(atob(str).split('').map(d => d.charCodeAt(0))); } -})() +} diff --git a/src/utils/cookie.ts b/src/utils/cookie.ts index f070219..b146cad 100644 --- a/src/utils/cookie.ts +++ b/src/utils/cookie.ts @@ -1,5 +1,6 @@ /** cookie工具 */ export namespace cookie { + export function get(cookieName: string) { const cookies = document.cookie.split('; '); const cookie = cookies.reduce((s, d) => { @@ -10,6 +11,7 @@ export namespace cookie { }, >{}); return cookie[cookieName]; } + export function set(name: string, value: string | boolean | number, days = 365) { const exp = new Date(); exp.setTime(exp.getTime() + days * 24 * 60 * 60 * 1000); @@ -17,6 +19,7 @@ export namespace cookie { '' + value, )};expires=${exp.toUTCString()}; path=/; domain=.bilibili.com`; } + export function remove(name: string) { set(name, '', -1); } diff --git a/src/utils/element.ts b/src/utils/element.ts index d1d76cc..a82e7bf 100644 --- a/src/utils/element.ts +++ b/src/utils/element.ts @@ -1,80 +1,58 @@ -/** DOM 操作的封装 */ +/** DOM 操作封装 */ export namespace Element { /** - * 创建HTML节点 + * 快速创建 Element * - * @param tag 节点名称 - * @param attribute 节点属性对象 - * @param parrent 添加到的父节点 - * @param innerHTML 节点的innerHTML - * @param top 是否在父节点中置顶 - * @param replaced 替换节点而不是添加,被替换的节点,将忽略父节点相关参数 + * @param tagName 标签铭 + * @param param1 属性配置 */ export function add( - tag: T, - attribute?: Record, - parrent?: HTMLElement, - innerHTML?: string, - top?: boolean, - replaced?: Element, - ): HTMLElementTagNameMap[T] { - const element = document.createElement(tag); - attribute && (Object.entries(attribute).forEach(d => { - Object.hasOwn(attribute, d[0]) && element.setAttribute(d[0], d[1]) - })); - innerHTML && element.insertAdjacentHTML('beforeend', innerHTML); - replaced - ? replaced.replaceWith(element) - : parrent && (top - ? parrent.insertAdjacentElement('afterbegin', element) - : parrent.insertAdjacentElement('beforeend', element) - ) - return element; - } - - /** - * 生成svg系节点 - * - * @param tag 节点名称 - * @param attribute 节点属性对象 - * @param parrent 添加到的父节点 - */ - export function addSvg( - tag: T, - attribute?: Record, - parrent?: ParentNode, + tagName: T, + option?: IAddOption, ) { - const svg = document.createElementNS("http://www.w3.org/2000/svg", tag); - attribute && (Object.entries(attribute).forEach(d => { - Object.hasOwn(attribute, d[0]) && svg.setAttribute(d[0], d[1]) - })); - parrent && parrent.append(svg); - return svg; + const ele = document.createElement(tagName); + if (option) { + const { insertTo, appendTo, prependTo, style, attribute, class: classList, data, innerText, innerHTML, children } = option; + attribute && Object.entries(attribute).forEach(d => { ele.setAttribute(...d) }); + style && Object.entries(style).forEach(([key, value]) => { (ele).style[key] = value }); + classList && ele.classList.add(...Array.isArray(classList) ? classList : [classList]); + data && Object.entries(data).forEach(([key, value]) => { ele.dataset[key] = value }); + innerText ? (ele.innerText = innerText) : innerHTML ? (ele.innerHTML = innerHTML) : (children && ele.replaceChildren(...Array.isArray(children) ? children : [children])); + if (insertTo) { + insertTo.target.insertAdjacentElement(insertTo.where || 'afterend', ele); + } else { + appendTo ? appendTo.append(ele) : (prependTo?.prepend(ele)); + } + } + return ele; } - /** - * 加载外源脚本, - * 支持加载完成后执行回调函数或者返回Promise - * - * @param src 外源脚本url - * @param onload 加载完成后的回调函数 - */ - export function loadScript(src: string, onload?: () => void) { - return new Promise((r, j) => { - const script = document.createElement("script"); - script.type = "text/javascript"; - script.src = src; - script.addEventListener("load", () => { - script.remove(); - onload && onload(); - r(true); - }); - script.addEventListener('error', () => { - script.remove(); - j(); - }); - (document.body || document.head || document.documentElement || document).appendChild(script); - }); + interface IAddOption { + /** 插入到(比`appendTo`和`prependTo`优先级更高) */ + insertTo?: { + /** 对应节点 */ + target: Element; + /** 位置,默认为目标同级后 */ + where?: InsertPosition; + } + /** 要添加到对应节点的末尾 */ + appendTo?: ParentNode; + /** 要添加到对应节点的末尾 */ + prependTo?: ParentNode; + /** 样式 */ + style?: Partial; + /** 属性 */ + attribute?: Record; + /** 类(禁止含空格) */ + class?: string | string[]; + /** data-* */ + data?: DOMStringMap; + /** 子节点字符串 */ + innerHTML?: string; + /** 子节点文本 */ + innerText?: string; + /** 子节点(优先级在`innerHTML`和`innerText`之后) */ + children?: Node | Node[]; } } \ No newline at end of file diff --git a/src/utils/interface/GM.ts b/src/utils/interface/GM.ts deleted file mode 100644 index ce593ba..0000000 --- a/src/utils/interface/GM.ts +++ /dev/null @@ -1,54 +0,0 @@ -/** 消息类型对应的消息内容 */ -export interface IGMPostMessage { - /** 跨域版`fetch` */ - fetch: { input: RequestInfo | URL, init?: RequestInit }; - /** 更新网络拦截规则 */ - updateSessionRules: { rules: any[], tab: boolean }; - /** 移除网络规则集 */ - removeSessionRules: { ids: number[] }; - /** 写入存储 */ - storageSet: { key: string, value: any }; - /** 读取存储 */ - storageGet: { key: string, def: any }; - /** 删除存储 */ - storageRemove: { key: string | string[] }; - /** 清空存储 */ - storageClear: void; - /** 获取拓展内文件 */ - file: string; -} - -/** 消息类型对应的回调参数内容 */ -export interface IGMOnMessage { - /** 跨域版`fetch` */ - fetch: { - /** HTTP状态码 */ - status: number, - /** HTTP状态 */ - statusText: string, - /** 返回标头 */ - header: [string, string][], - /** 实际URL */ - url: string, - /** 是否重定向 */ - redirected: boolean, - /** 请求类型 */ - type: string, - /** 返回值类型数组转真数组(因为chromebug不支持克隆ArrayBuffer!) */ - data: number[]; - }; - /** 更新网络拦截规则 */ - updateSessionRules: void; - /** 移除网络规则集 */ - removeSessionRules: void; - /** 写入存储 */ - storageSet: void; - /** 读取存储 */ - storageGet: unknown; - /** 删除存储 */ - storageRemove: void; - /** 清空存储 */ - storageClear: void; - /** 获取拓展内文件 */ - file: IGMOnMessage['fetch']; -} \ No newline at end of file diff --git a/src/utils/md5.ts b/src/utils/md5.ts index aca4872..4bda804 100644 --- a/src/utils/md5.ts +++ b/src/utils/md5.ts @@ -199,6 +199,7 @@ export namespace crypto { return str } } + export namespace md5 { /** diff --git a/src/utils/uhash.ts b/src/utils/midHash.ts similarity index 99% rename from src/utils/uhash.ts rename to src/utils/midHash.ts index 2229c7d..dd78bb6 100644 --- a/src/utils/uhash.ts +++ b/src/utils/midHash.ts @@ -3,7 +3,7 @@ // @see esterTion {@link https://github.com/esterTion/BiliBili_crc2mid} // @license GFUL -export namespace Uhash { +export namespace MidHash { const CRCPOLYNOMIAL = 0xEDB88320; const crctable = new Array(256); const index = new Array(4); diff --git a/src/utils/support.ts b/src/utils/support.ts deleted file mode 100644 index 0af98b5..0000000 --- a/src/utils/support.ts +++ /dev/null @@ -1,3 +0,0 @@ -// 运行环境检查 - -CSS.supports('anchor-name: --anchor') || alert('Bilibili 2019:您的浏览器不受支持,请使用chrome 125以上版本!'); \ No newline at end of file diff --git a/src/utils/tsconfig.json b/src/utils/tsconfig.json deleted file mode 100644 index 32bfa04..0000000 --- a/src/utils/tsconfig.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "extends": "../tsconfig-base.json", - "include": [ - "**/*", - "../interface" - ], - "references": [ - { - "path": "../interface" - } - ] -} \ No newline at end of file