From 6ea2756f16d7553ddfa6044d62af2f78e59c236b Mon Sep 17 00:00:00 2001 From: tsukumi Date: Sat, 16 Sep 2023 14:07:21 +0000 Subject: [PATCH] =?UTF-8?q?Update:=20[Client][TV/Watch]=20DPlayer=20?= =?UTF-8?q?=E3=81=AE=E6=9B=B4=E6=96=B0=E3=81=AB=E5=90=88=E3=82=8F=E3=81=9B?= =?UTF-8?q?=E3=81=A6=E4=B8=80=E9=83=A8=E3=81=AE=E5=AE=9F=E8=A3=85=E3=82=92?= =?UTF-8?q?=20DPlayer=20=E5=81=B4=E3=81=AB=E5=A7=94=E8=AD=B2=20=E5=BC=B7?= =?UTF-8?q?=E5=BC=95=E3=81=AB=E5=AE=9F=E8=A3=85=E3=81=99=E3=82=8B=E3=82=88?= =?UTF-8?q?=E3=82=8A=20DPlayer=20=E5=81=B4=E3=81=AE=E3=82=AA=E3=83=97?= =?UTF-8?q?=E3=82=B7=E3=83=A7=E3=83=B3=E3=81=A8=E3=81=97=E3=81=A6=E6=A9=9F?= =?UTF-8?q?=E8=83=BD=E8=BF=BD=E5=8A=A0=E3=81=97=E3=81=9F=E6=96=B9=E3=81=8C?= =?UTF-8?q?=E3=82=B9=E3=83=9E=E3=83=BC=E3=83=88=E3=81=AA=E3=81=9F=E3=82=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../player/managers/CaptureManager.ts | 16 ++-- client/src/views/TV/Watch.vue | 87 ++----------------- 2 files changed, 14 insertions(+), 89 deletions(-) diff --git a/client/src/services/player/managers/CaptureManager.ts b/client/src/services/player/managers/CaptureManager.ts index 796aeeee..22e8e353 100644 --- a/client/src/services/player/managers/CaptureManager.ts +++ b/client/src/services/player/managers/CaptureManager.ts @@ -171,17 +171,15 @@ class CaptureManager { // 字幕・文字スーパーの Canvas を取得 // getRawCanvas() で映像と同じ解像度の Canvas が取得できる const aribb24_caption = this.player.plugins.aribb24Caption!; - const aribb24_superimpose = this.player.plugins.aribb24Superimpose!; - const caption_canvas: HTMLCanvasElement = aribb24_caption.getRawCanvas()!; - const superimpose_canvas: HTMLCanvasElement = aribb24_superimpose.getRawCanvas()!; + const aribb24_superimpose = this.player.plugins.aribb24Superimpose ?? null; + const caption_canvas = aribb24_caption.getRawCanvas()!; + const superimpose_canvas = aribb24_superimpose?.getRawCanvas() ?? null; // 字幕が表示されているか - // @ts-ignore - const is_caption_showing = (aribb24_caption.isShowing === true && aribb24_caption.isPresent()); + const is_caption_showing = ((aribb24_caption as any).isShowing === true && aribb24_caption.isPresent()); // 文字スーパーが表示されているか - // @ts-ignore - const is_superimpose_showing = (aribb24_superimpose.isShowing === true && aribb24_superimpose.isPresent()); + const is_superimpose_showing = (aribb24_superimpose && (aribb24_superimpose as any).isShowing === true && aribb24_superimpose.isPresent()); // 字幕が表示されている場合、表示中の字幕のテキストを取得 // 取得した字幕のテキストは、キャプチャに字幕が合成されているかに関わらず、常に EXIF メタデータに書き込まれる @@ -334,7 +332,7 @@ class CaptureManager { // 文字スーパーを描画 (表示されている場合) // 文字スーパー自体が稀だし、文字スーパーなしでキャプチャ撮りたいユースケースはない…はず - if (is_superimpose_showing === true) { + if (superimpose_canvas && is_superimpose_showing === true) { canvas_context.drawImage(superimpose_canvas, 0, 0, canvas.width, canvas.height); } @@ -392,7 +390,7 @@ class CaptureManager { // すでに字幕なしキャプチャを生成する過程でコメントを描画してしまっているため、映像描画からやり直す必要がある if (with_comments === true) { canvas_context.drawImage(image_bitmap, 0, 0, canvas.width, canvas.height); - if (is_superimpose_showing === true) { + if (superimpose_canvas && is_superimpose_showing === true) { canvas_context.drawImage(superimpose_canvas, 0, 0, canvas.width, canvas.height); } } diff --git a/client/src/views/TV/Watch.vue b/client/src/views/TV/Watch.vue index b2503d79..cffd16fe 100644 --- a/client/src/views/TV/Watch.vue +++ b/client/src/views/TV/Watch.vue @@ -883,6 +883,7 @@ export default Vue.extend({ autoplay: true, // 自動再生 hotkey: false, // ショートカットキー(こちらで制御するため無効化) screenshot: false, // スクリーンショット (こちらで制御するため無効化) + crossOrigin: 'anonymous', // CORS を有効化 volume: 1.0, // 音量の初期値 // 映像 video: { @@ -951,6 +952,7 @@ export default Vue.extend({ user: 'KonomiTV', // 便宜上 KonomiTV に固定 speedRate: this.settingsStore.settings.comment_speed_rate, // コメントの流れる速度 fontSize: this.settingsStore.settings.comment_font_size, // コメントのフォントサイズ + closeCommentFormAfterSend: this.settingsStore.settings.close_comment_form_after_sending, // コメント送信後にコメントフォームを閉じる }, // コメント API バックエンド apiBackend: { @@ -996,6 +998,8 @@ export default Vue.extend({ }, // aribb24.js aribb24: { + // 文字スーパーを表示するかどうか + disableSuperimposeRenderer: this.settingsStore.settings.tv_show_superimpose === false, // 描画フォント normalFont: `"${this.settingsStore.settings.caption_font}", "Rounded M+ 1m for ARIB", sans-serif`, // 縁取りする色 @@ -1090,6 +1094,7 @@ export default Vue.extend({ // Enter キーの押下がコメント送信由来のイベントかキャプチャ拡大表示由来のイベントかを判断できない // そこで、コメント入力フォームフォーカス中に Enter キーが押された場合(=コメント送信時)に 0.1 秒間フラグを立てることで、 // ショートカットキーハンドラーがコメント送信由来のイベントであることを判定できるようにしている + // TODO: 普通に KeyboardEvent.target が dplayer-comment-input かで判断できるんじゃね? this.player.template.commentInput.addEventListener('keydown', (event) => { if (event.code === 'Enter') { this.is_comment_send_just_did = true; @@ -1097,49 +1102,6 @@ export default Vue.extend({ } }); - // 「コメント送信後にコメント入力フォームを閉じる」がオフになっている時のために、プレイヤー側のコメント送信関数を上書き - // 上書き部分以外の処理内容は概ね https://github.com/tsukumijima/DPlayer/blob/master/src/js/comment.js に準じる - this.player.comment!.send = () => { - - if (this.player === null) { - return; // 復旧不可能 (発生しないはずだが、書いとかないと TypeScript に怒られる) - } - - // コメント入力フォームへのフォーカスを外す (「コメント送信後にコメント入力フォームを閉じる」がオンのときだけ) - if (this.settingsStore.settings.close_comment_form_after_sending === true) { - this.player.template.commentInput.blur(); - } - - // 空コメントを弾く - if (!this.player.template.commentInput.value.replace(/^\s+|\s+$/g, '')) { - this.player.notice(this.player.tran('Please input danmaku content!'), undefined, undefined, '#FF6F6A'); - return; - } - - // コメントを送信 - this.player.danmaku!.send( - { - text: this.player.template.commentInput.value, - color: this.player.container. - querySelector('.dplayer-comment-setting-color input:checked')!.value, - type: this.player.container. - querySelector('.dplayer-comment-setting-type input:checked')!.value as DPlayerType.DanmakuType, - size: this.player.container. - querySelector('.dplayer-comment-setting-size input:checked')!.value as DPlayerType.DanmakuSize, - }, - // 送信完了後にコメント入力フォームを閉じる ([コメント送信後にコメント入力フォームを閉じる] がオンのときだけ) - () => { - if (this.settingsStore.settings.close_comment_form_after_sending === true) { - this.player !== null && this.player.comment!.hide(); - } - }, - true, - ); - - // 重複送信を防ぐ - this.player.template.commentInput.value = ''; - }; - // ***** 設定パネルのショートカット一覧へのリンクのイベントハンドラー ***** // 設定パネルにショートカット一覧を表示するリンクを動的に追加する @@ -1156,10 +1118,6 @@ export default Vue.extend({ `); - // 設定パネルの高さを再設定 - const settingOriginPanelHeight = this.player.template.settingOriginPanel.scrollHeight; - this.player.template.settingBox.style.clipPath = `inset(calc(100% - ${settingOriginPanelHeight}px) 0 0 round 7px)`; - // 設定パネルのショートカット一覧を表示するリンクがクリックされたときのイベント // リアクティブではないので、手動でやらないといけない… this.$el.querySelector('.dplayer-setting-keyboard-shortcut')!.addEventListener('click', () => { @@ -1281,33 +1239,6 @@ export default Vue.extend({ } }, 30 * 1000)); } - - // ***** 文字スーパーのイベントハンドラー ***** - - (async () => { - - // 文字スーパーが初期化されるまで待つ - if (this.player === null) return; - while (this.player.plugins.aribb24Superimpose === undefined) { - await Utils.sleep(0.1); // 0.1 秒待つ - } - - // 設定で文字スーパーが有効 - // 字幕が非表示の場合でも、文字スーパーは表示する - if (this.settingsStore.settings.tv_show_superimpose === true) { - this.player.plugins.aribb24Superimpose.show(); - this.player.on('subtitle_hide', () => { - this.player?.plugins.aribb24Superimpose!.show(); - }); - // 設定で文字スーパーが無効 - } else { - this.player.plugins.aribb24Superimpose.hide(); - this.player.on('subtitle_show', () => { - this.player?.plugins.aribb24Superimpose!.hide(); - }); - } - - })(); }, // イベントハンドラーを初期化する @@ -1334,10 +1265,6 @@ export default Vue.extend({ // 音量を 0 に設定 this.player.video.volume = 0; - // video 要素の crossOrigin 属性を 'anonymous' に設定 - // これを設定しないと、クロスオリジンの場合にキャプチャができない - this.player.video.crossOrigin = 'anonymous'; - // mpegts.js 再生時のみ、mpegts.js のログハンドラーを設定する if (this.is_mpegts_supported === true && this.player.plugins.mpegts !== undefined) { this.player.plugins.mpegts.on(mpegts.Events.ERROR, async (error_type: mpegts.ErrorTypes, detail: mpegts.ErrorDetails) => { @@ -2207,14 +2134,14 @@ export default Vue.extend({ // キャプチャボタンがクリックされたときのイベント // ショートカットからのキャプチャでも同じイベントがトリガーされる - const capture_button = this.$el.querySelector('.dplayer-icon.dplayer-capture-icon'); + const capture_button = this.$el.querySelector('.dplayer-icon.dplayer-capture-icon')!; capture_button.addEventListener('click', async () => { await this.capture_manager.captureAndSave(false); }); // コメント付きキャプチャボタンがクリックされたときのイベント // ショートカットからのキャプチャでも同じイベントがトリガーされる - const comment_capture_button = this.$el.querySelector('.dplayer-icon.dplayer-comment-capture-icon'); + const comment_capture_button = this.$el.querySelector('.dplayer-icon.dplayer-comment-capture-icon')!; comment_capture_button.addEventListener('click', async () => { await this.capture_manager.captureAndSave(true); });