// ==UserScript== // @name ABEMA Little Tools // @namespace https://greasyfork.org/ja/scripts/465585 // @version 5 // @description ABEMAをちょっとだけ便利にするかもしれない機能をまとめました。 // @match https://abema.tv/* // @grant none // @license MIT License // @noframes // @downloadURL none // ==/UserScript== (() => { 'use strict'; const sid = 'LittleTools', ls = JSON.parse(localStorage.getItem(sid) || '{}') || {}, lsWord = JSON.parse(localStorage.getItem(`${sid}-Word`) || '{}') || {}, lsId = JSON.parse(localStorage.getItem(`${sid}-Id`) || '{}') || {}, data = { blockedUserId: '', commentId: new Set(), commentMouseEnter: false, href: '', ngId: new Set(lsId.ngId), /** @type {string[]} */ ngWordText: [], ngWordRe: [{}], version: 5, videoSource: '', }, interval = { changeTargetQuality: 0, comment: 0, footer: 0, init: 0, navigation: 0, newcomment: 0, notification: 0, resizeVideo: 0, resolution: 0, videoelement: 0, videoskip: 0, videosource: 0, }, selector = { comment: `:is(.com-tv-CommentBlock, .com-comment-CommentItem):has(> div[data-${sid.toLowerCase()}-hidden="false"])`, commentBefore: `:is(.com-tv-CommentBlock, .com-comment-CommentItem):has(> div:not([data-${sid.toLowerCase()}-hidden]))`, commentHidden: `:is(.com-tv-CommentBlock, .com-comment-CommentItem):has(> div[data-${sid.toLowerCase()}-hidden="true"])`, comenntAll: '.com-tv-CommentBlock, .com-comment-CommentItem', commentArea: '.com-tv-CommentArea, .com-comment-CommentContainerView', commentButton: 'button:has(svg[aria-label^="コメント"])', commentContinue: '.com-tv-CommentArea__continue-button, .com-comment-CommentContinueButton', commentForm: '.com-o-CommentForm__opened-textarea,.com-comment-CommentTextarea__textarea', commentInner: '.com-tv-CommentBlock__inner', commentList: '.com-a-OnReachTop > div, .com-comment-CommentList__inner > ul', commentMessage: '.com-tv-CommentBlock__message > span, .com-comment-CommentItem__body', commentReport: `.com-tv-CommentReportForm:not([data-${sid.toLowerCase()}-commentreportform]), .com-comment-CommentReportForm:not([data-${sid.toLowerCase()}-commentreportform])`, commentReportCancel: '.com-tv-CommentReportForm__cancel-button, .com-comment-CommentReportForm__cancel-button', commentReportSubmitTv: 'com-tv-CommentReportForm__submit-button', commentReportSubmitLe: 'com-comment-CommentReportForm__submit-button', commentReportTv: '.com-tv-CommentReportForm', commentReportLe: '.com-comment-CommentReportForm', footer: '.com-tv-LinearFooter,.com-vod-VideoControlBar,.com-live-event-LiveEventVideoController,.com-vod-PayperviewLinearControlBar', footerVisible: '.com-tv-TVScreen__footer-container:not(.com-tv-TVScreen__footer-container--hidden),.com-vod-VODScreen-container:not(.com-vod-VODScreen-container--cursor-hidden),.com-live-event-LiveEventPlayerAreaLayout--controllers-visible', headerMenu: '.com-m-HeaderMenu', inner: '.c-application-DesktopAppContainer__content', main: '#main', mypageMenu: '.com-application-MypageMenu__menu', nextCancel: '.com-vod-VODNextProgramInfo__cancel-button', nextCancelMini: '.com-vod-VODScreenOverlayForMiniPlayer__cancel-next-program-button', notification: '.com-m-NotificationManager', notificationClose: '.com-m-Notification__button[aria-label="閉じる"]', notificationMessage: '.com-m-Notification__message span', sideNavi: '.c-application-SideNavigation', sideNaviColl: 'c-application-SideNavigation--collapsed', sideNaviWrapColl: 'c-application-SideNavigation__wrapper--collapsed', sidePanelClose: '.com-tv-FeedSidePanel__close-button,.com-live-event-LiveEventStatsSidePanel__close,.com-comment-CommentAreaHeader__close-button', video: 'video[src]:not([style*="display: none;"])', videoContainer: '.com-a-Video__container', videoDblclick: '.com-vod-VideoControlBar,.c-vod-EpisodePlayerContainer-ad-container,.c-tv-TimeshiftPlayerContainerView__ad-container,.com-live-event-LiveEventPlayerSectionLayout__player-area', videoMainPlayer: '.com-vod-VODMiniPlayerWrapper__player:not(.com-vod-VODMiniPlayerWrapper__player--bg):not(.com-vod-VODMiniPlayerWrapper__player--mini),.com-live-event-LiveEventPlayerAreaLayout__player', videoSkip: '.com-video_ad-AdSkipButton', videoSkip2: '.com-video_ad-AdSkipButton:not([disabled])', }, setting = { _ngid: [0, 100, 200, 500, 1000, 2000, 5000, 10000, 20000, 50000], closeNotification: ls.closeNotification, closeSidePanel: ls.closeSidePanel, commentFontSize: ls.commentFontSize, commentFontSizeNum: ls.commentFontSizeNum, dblclickScroll: ls.dblclickScroll, enterKey: ls.enterKey, escKey: ls.escKey, headerPosition: ls.headerPosition, hiddenButtonText: ls.hiddenButtonText, hiddenCommentList: ls.hiddenCommentList, hiddenCommentListNum: ls.hiddenCommentListNum, hiddenCommentListNum2: ls.hiddenCommentListNum2, highlightNewComment: ls.highlightNewComment, mouseoverNavigation: ls.mouseoverNavigation, newCommentOneByOne: ls.newCommentOneByOne, nextProgramInfo: ls.nextProgramInfo, ngConsole: ls.ngConsole, ngId: lsId.ngId ? [...lsId.ngId] : [], ngIdEnable: ls.ngIdEnable, ngIdMaxSize: ls.ngIdMaxSize, ngWord: lsWord.ngWord, ngWordEnable: ls.ngWordEnable, overlapSidePanel: ls.overlapSidePanel, pageKey: ls.pageKey, qualityEnable: ls.qualityEnable, reduceCommentSpace: ls.reduceCommentSpace, reduceNavigation: ls.reduceNavigation, scrollNewComment: ls.scrollNewComment, semiTransparent: ls.semiTransparent, sidePanelBackground: ls.sidePanelBackground, sidePanelCloseButton: ls.sidePanelCloseButton, sidePanelSize: ls.sidePanelSize, sidePanelSizeNum: ls.sidePanelSizeNum, showProgramDetail: ls.showProgramDetail, skipVideo: ls.skipVideo, smallFontSize: ls.smallFontSize, stopCommentScroll: ls.stopCommentScroll, targetQuality: ls.targetQuality, videoPadding: ls.videoPadding, videoResolution: ls.videoResolution, }, video = { clientHeight: 0, clientWidth: 0, pixelRatio: 0, src: '', videoHeight: 0, videoWidth: 0, }; /** * NG IDを追加する */ const addNgId = () => { log('addNgId'); clearInterval(interval.newcomment); const blocked = checkBlockedUser(false); if ( blocked >= 100 && setting.ngIdMaxSize && setting._ngid[setting.ngIdMaxSize] > data.ngId.size && data.blockedUserId && !data.ngId.has(data.blockedUserId) ) { setting.ngId.push(data.blockedUserId); lsId.ngId.push(data.blockedUserId); data.ngId.add(data.blockedUserId); log('addNgId', data.blockedUserId, data.ngId.size); saveStorage(); setTimeout(() => { checkBlockedUser(true); }, 1000); } else { log( 'not add NGiD', blocked, setting.ngIdMaxSize, setting._ngid[setting.ngIdMaxSize], data.ngId.size, data.blockedUserId, data.ngId.has(data.blockedUserId) ); } }; /** * スタイルを追加 * @param {string} s */ const addStyle = (s) => { const init = ` .com-tv-CommentBlock:has(> div[data-${sid.toLowerCase()}-hidden="true"]), .com-comment-CommentItem:has(> div[data-${sid.toLowerCase()}-hidden="true"]), .com-tv-CommentBlock:has(> div[data-${sid.toLowerCase()}-ngword]), .com-comment-CommentItem:has(> div[data-${sid.toLowerCase()}-ngword]), .com-tv-CommentBlock:has(> div[data-${sid.toLowerCase()}-ngid]), .com-comment-CommentItem:has(> div[data-${sid.toLowerCase()}-ngid]), .com-tv-CommentBlock:has(> div[data-${sid.toLowerCase()}-duplicate]), .com-comment-CommentItem:has(> div[data-${sid.toLowerCase()}-duplicate]), .${sid}_Settings_hidden, .${sid}_Settings-tab-switch { display: none; } #${sid}_Settings { background-color: #F9FCFF; border: 2px solid #CCCCCC; border-radius: 8px; box-shadow: 4px 4px 16px rgba(0,0,0,0.5); color: black; left: 20px; max-height: calc(100vh - 40px); max-width: calc(100vw - 40px); min-width: 45em; overflow: auto; padding: 8px; position: fixed; top: 20px; user-select: none; width: min-content; z-index: 9900; } #${sid}_Settings label[title] { cursor: help; } #main:has(.c-tv-NowOnAirContainer__side-panel--shown) ~ #${sid}_Settings { max-width: calc(100vw - 460px); } #${sid}_Settings-header { text-align: center; } #${sid}_Settings-main fieldset { border: 1px solid #CCCCCC; margin: 2px 0; padding: 4px 8px; } #${sid}_Settings-main fieldset + label { margin-top: 2px; } #${sid}_Settings-main pre { background-color: #FFFFEE; border: 1px solid #DDDDDD; margin-left: 1em; padding: 4px; user-select: text; width: min-content; } #${sid}_Settings-main :is(label, details):not(.${sid}_Settings-tab-label) { display: inline-block; width: 100%; } #${sid}_Settings-main :is(label, details):not(.${sid}_Settings-tab-label):hover { background-color: #E3ECF6; } #${sid}_Settings-main details { transition: 0.5s; } #${sid}_Settings-main input[type="checkbox"] { position: relative; top: 2px; margin-right: 4px; } #${sid}_Settings-main summary { cursor: pointer; display: list-item; } #${sid}_Settings-main summary::before { background: #88AA88; border-radius: 50%; color: #FFFFFF; content: "?"; display: inline-block; font-size: 85%; font-weight: bold; height: 1.5em; line-height: 1.5; margin-right: 4px; text-align: center; vertical-align: 2px; width: 1.5em; } #${sid}_Settings-main select { appearance: auto; cursor: pointer; margin-top: 8px; padding: 4px 8px; } #${sid}_Settings-main input + input, #${sid}_Settings-main input + input + input, #${sid}_Settings-main legend:has(input), #${sid}_Settings-main legend:has(input) ~ label { color: gray; } #${sid}_Settings-main input:checked + input, #${sid}_Settings-main input:checked + input + input, #${sid}_Settings-main legend:has(input:checked), #${sid}_Settings-main legend:has(input:checked) ~ label { color: black; } #${sid}_Settings-commentFontSizeNum { text-align: center; width: 3.5em; } #${sid}_Settings-hiddenCommentListNum, #${sid}_Settings-hiddenCommentListNum2 { text-align: center; width: 4em; } #${sid}_Settings-sidePanelSizeNum { text-align: center; width: 5em; } #${sid}_Settings input[type="number"], #${sid}_Settings select { margin-left: 8px; margin-right: 2px; } #${sid}_Settings-main input + input, #${sid}_Settings-main input + input + input, legend:has(input) ~ label #${sid}_Settings-targetQuality, legend:has(input) ~ #${sid}_Settings-ngWord, legend:has(input) ~ label #${sid}_Settings-ngIdMaxSize { background-color: #DDDDDD; } #${sid}_Settings-main input:checked + input, #${sid}_Settings-main input:checked + input + input, legend:has(input:checked) ~ label #${sid}_Settings-targetQuality, legend:has(input:checked) ~ #${sid}_Settings-ngWord, legend:has(input:checked) ~ label #${sid}_Settings-ngIdMaxSize { background-color: white; } #${sid}_Settings-ngWord { height: 6em; margin-top: 8px; max-width: calc(100vw - 118px); min-height: 6em; min-width: 40em; width: 100%; } #${sid}_Settings-ngWord-error { display: none; margin-bottom: 4px; } #${sid}_Settings-ngWord-error p { color: red; } #${sid}_Settings-ngWord-error pre { font-weight: bold; margin-top: 4px; padding: 4px 8px; } #${sid}_Settings-ngIdMaxSize { text-align: right; } #${sid}_Settings-ngId-record { margin-left: 1em; } #${sid}_Settings-footer { text-align: right; } #${sid}_Settings-footer button { border: 1px solid gray; border-radius: 4px; margin: 8px; padding: 4px; width: 8em; } #${sid}_Settings-ok { background-color: #EEEEEE; } #${sid}_Settings-cancel { background-color: #EEEEEE; } #${sid}_Settings-main { display: flex; flex-wrap: wrap; margin: 8px 0; } #${sid}_Settings-main:after { background: #8899aa; content: ''; display: block; height: 1px; order: -1; width: 100%; } .${sid}_Settings-tab-label { background: #BCBCBC; border-radius: 4px 4px 0 0; color: White; cursor: pointer; flex: 1; font-weight: bold; order: -1; padding: 2px 4px; position: relative; text-align: center; text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2); white-space: nowrap; z-index: 1; } .${sid}_Settings-tab-label:not(:last-of-type) { margin-right: 5px; } .${sid}_Settings-tab-content { height: 0; opacity: 0; overflow: hidden; width: 100%; } .${sid}_Settings-tab-switch:checked + .${sid}_Settings-tab-label { background: #8899aa; } .${sid}_Settings-tab-switch:checked + .${sid}_Settings-tab-label + .${sid}_Settings-tab-content { box-shadow: 0 0 3px rgba(0, 0, 0, 0.2); height: auto; opacity: 1; overflow: auto; padding: 8px; transition: .5s opacity; } #${sid}_CommentReportForm-NgComment { color: #E6E6E6; font-size: 13px; margin-top: 12px; } #${sid}_CommentReportForm-NgCommentHeader { font-size: 12px; } #${sid}_CommentReportForm-NgCommentList { background-color: #333333; margin-top: 4px; padding: 8px 4px; } #${sid}_CommentReportForm-NgCommentList p + p { margin-top: 0.8em; } #${sid}_VideoResolution { background: linear-gradient(180deg, rgba(0, 0, 0, 0.2), #000000); bottom: 0px; color: #ccc; display: none; font-size: 12px; height: 16px; left: 155px; padding: 0 2px; position: absolute; user-select: none; white-space: nowrap; } `, overlapSidePanel = ` /*右側のサイドパネルを動画に重ねて表示する*/ .c-tv-NowOnAirContainer__screen--with-side-panel { width: 100vw !important; } .com-tv-TVScreen__player { height: 100vh !important; } .com-a-Text--info.com-a-Text--dark, .com-a-Text--info.com-a-Text--light { color: #DDDDDD !important; } .com-tv-CommentBlock, .com-tv-FeedProgramDetailContainerView__contents :is(h2, h3, h2+p, h3+p, dd), .com-tv-FeedProgramDetailContainerView__contents span:not(.com-tv-FeedProgramDetailExternalLink__button-text), .com-tv-FeedSidePanel__close-button-text, .com-comment-CommentItem__body { text-shadow: -1px -1px 1px black, -1px 0px 1px black, -1px 1px 1px black, 0px -1px 1px black, 0px 1px 1px black, 1px -1px 1px black, 1px 0px 1px black, 1px 1px 1px black !important; } .com-tv-CommentBlock__message > span, .com-comment-CommentItem__body { color: white !important; } .c-tv-NowOnAirContainer__side-panel, .com-tv-CommentArea, .com-tv-CommentArea__comment-form, .com-tv-CommentBlock, .com-tv-FeedProgramDetailContainerView__contents, .com-tv-FeedSidePanel__header, .com-comment-CommentContainerView, .com-live-event-LiveEventStatsSidePanel { background-color: rgba(0, 0, 0, 0) !important; } .com-tv-CommentBlock__inner:hover, .com-comment-TwitterSigninButton:hover, .com-comment-CommentItem__inner:hover { background-color: rgba(0, 0, 0, 0.3) !important; } .com-tv-CommentBlock__time { opacity: 0.6 !important; } .c-tv-NowOnAirContainer__screen--with-side-panel .com-tv-TVScreen__footer-container { padding-right: ${ setting.sidePanelSize ? setting.sidePanelSizeNum : '320' }px !important; } .com-o-CommentForm__can-post .com-o-CommentForm__opened-textarea-wrapper, .com-comment-CommentTextarea__textarea { background-color: rgba(255, 255, 255, 0.2) !important; } .com-o-CommentForm__can-post .com-o-CommentForm__opened-textarea:focus, .com-comment-CommentTextarea__textarea:focus { background-color: rgba(255, 255, 255, 0.8) !important; } .com-o-CommentForm__opened-textarea-wrapper { background-color: rgba(0, 0, 0, 0.2) !important; } .com-o-CommentForm__twitter-button, .com-comment-TwitterSigninButton, .com-comment-TwitterSignoutButton, .com-comment-CommentAreaHeader__comment-count { opacity: 0.2 !important; } .com-o-CommentForm__twitter-button:hover, .com-comment-TwitterSigninButton:hover, .com-comment-TwitterSignoutButton:hover, .com-comment-CommentAreaHeader__comment-count:hover { opacity: 1 !important; } .com-tv-FeedSidePanel__contents { height: 98% !important; } .com-a-Button--primary, .com-comment-CommentSubmitButton { background-color: rgba(221, 170, 0, 0.5) !important; } .com-a-Button--primary:hover:not([disabled]), .com-comment-CommentSubmitButton:hover:not([disabled]) { background-color: rgba(221, 170, 0, 1) !important; } .c-tv-NowOnAirContainer__screen--with-side-panel :is( .com-tv-SlotMyListButtonOnPlayerContainerView, .com-tv-TVScreen__ad-link-button ) { bottom: 124px !important; right: ${ setting.sidePanelSize ? setting.sidePanelSizeNum : '320' }px !important; transform: translateX(500px) !important; } .com-tv-SlotMyListButtonOnPlayerContainerView--shown, .com-tv-TVScreen__ad-link-button--shown { transform: translateX(0) !important; } .c-tv-NowOnAirContainer__screen--with-side-panel .c-tv-NowOnAirContainer__remote-controller, .com-question-QuestionContainerView, .com-vod-VODResponsiveMainContent--with-side-panel .com-live-event-LiveEventOverlayControllerLayout__bottom-buttons { right: ${ setting.sidePanelSize ? setting.sidePanelSizeNum : '320' }px !important; } .com-vod-VODResponsiveMainContent--with-side-panel .com-vod-PayperviewLinearControlBar { margin-right: ${ setting.sidePanelSize ? setting.sidePanelSizeNum : '320' }px !important; } .com-tv-FeedProgramDetailCommentCounter, .com-tv-FeedProgramDetailHeader__date, .com-tv-FeedProgramDetailViewCounter { color: #CCCCCC !important; } .com-live-event-LiveEventPlayerSectionLayout__side-panel { height: calc(100vh - 10px); position: absolute; right: 0; } .com-comment-CommentAreaHeader__close-button:hover { color: #FFFFFF; } `, highlightNewComment = ` /*新規コメントと自分のコメントを強調表示する*/ .com-tv-CommentBlock__inner--active, .com-tv-CommentBlock--new, .com-comment-CommentItem[data-${sid.toLowerCase()}-Own] .com-comment-CommentItem__body { text-shadow: -1px -1px 1px black, -1px 0px 1px black, -1px 1px 1px black, 0px -1px 1px black, 0px 1px 1px black, 1px -1px 1px black, 1px 0px 1px black, 1px 1px 1px black, -2px -2px 1px rgba(255,165,0,0.3), -2px 0px 1px rgba(255,165,0,0.3), -2px 2px 1px rgba(255,165,0,0.3), 0px -2px 1px rgba(255,165,0,0.3), 0px 2px 1px rgba(255,165,0,0.3), 2px -2px 1px rgba(255,165,0,0.3), 2px 0px 1px rgba(255,165,0,0.3), 2px 2px 1px rgba(255,165,0,0.3) !important; } .com-tv-CommentBlock__inner--active, .com-comment-CommentItem[data-${sid.toLowerCase()}-Own] { background-color: rgba(128,83,0,0.3) !important; } /* .com-tv-CommentBlock:not(.com-tv-CommentBlock--new) + .com-tv-CommentBlock--new { border-top: 4px dotted rgba(255,165,0,0.4) !important; } */ `, commentFontSize = ` /*コメントの文字サイズを変更する*/ .com-tv-CommentBlock__message > span, .com-comment-CommentItem__body { font-size: ${setting.commentFontSizeNum}px !important; } `, reduceCommentSpace = ` /*コメントの余白を減らす&行間を狭める*/ .com-tv-CommentBlock__inner { padding: 2px 4px !important; } .com-comment-CommentItem__body { padding: 4px 0 !important; } .com-comment-CommentItem__body-sub-wrapper { padding: 0 4px !important; } .com-comment-CommentItem__sub { width: auto !important; } `, sidePanelCloseButton = ` /*サイドパネル上端にマウスカーソルを近づけたとき閉じるボタンを表示*/ .com-tv-FeedSidePanel__close-button { background-color: rgba(64,64,64,0.8) !important; } .com-tv-FeedSidePanel__contents { transform: translateY(10px) !important; } .com-tv-FeedSidePanel__header { position: absolute !important; transform: translateY(-58px) !important; transition: transform 0.2s !important; z-index: 99 !important; } .com-tv-FeedSidePanel__header:hover { transform: translateY(0px) !important; } `, sidePanelSize = ` /*サイドパネルの幅を変更する*/ .c-tv-NowOnAirContainer__side-panel, .com-tv-FeedSidePanel, .com-live-event-LiveEventPlayerSectionLayout__side-panel { width: ${ setting.sidePanelSize ? setting.sidePanelSizeNum : '320' }px !important; } .com-application-Header--shrunk, .c-common-HeaderContainer-header--shrunk { width: calc(100% - ${ setting.sidePanelSize ? setting.sidePanelSizeNum : '320' }px) !important; } `, showProgramDetail = ` /*サイドパネルに記載された番組情報の詳細を常に表示する*/ .com-tv-FeedSidePanel__contents #com-vod-VODDetailsAccordion__details { height: auto !important; } .com-tv-FeedSidePanel__contents .com-vod-VODDetailsAccordion__details--collapsed { visibility: visible !important; } .com-tv-FeedSidePanel__contents .com-vod-VODDetailsAccordion__toggle-collapsed-details-button-paragraph { display: none !important; } `, hiddenButtonText = ` /*最初から見る・番組情報・コメントボタンのテキストを隠す*/ .com-tv-LinearFooterChasePlayButton, .com-tv-LinearFooterProgramDetailButton, .com-tv-LinearFooterCommentButton { transition: all 0.2s !important; width: 44px !important; } .com-tv-LinearFooterChasePlayButton:hover:not(.com-tv-LinearFooterChasePlayButton--shrunk) { width: 132px !important; } .com-tv-LinearFooterProgramDetailButton:hover:not(.com-tv-LinearFooterProgramDetailButton--shrunk), .com-tv-LinearFooterCommentButton:hover:not(.com-tv-LinearFooterCommentButton--shrunk) { width: 100px !important; } .com-tv-LinearFooterChasePlayButton__text, .com-tv-LinearFooterProgramDetailButton__text, .com-tv-LinearFooterCommentButton__text { overflow: hidden; white-space: nowrap; width: 0; } .com-tv-LinearFooterChasePlayButton:hover .com-tv-LinearFooterChasePlayButton__text, .com-tv-LinearFooterProgramDetailButton:hover .com-tv-LinearFooterProgramDetailButton__text, .com-tv-LinearFooterCommentButton:hover .com-tv-LinearFooterCommentButton__text { width: auto; } `, hiddenCommentList = ` /*コメントリストを半透明化*/ .com-a-OnReachTop, .com-comment-CommentList__inner { opacity: ${Number(setting.hiddenCommentListNum) / 100}; } `, hiddenCommentList2 = ` /*コメントリストを半透明化2*/ .com-a-OnReachTop, .com-comment-CommentList__inner { opacity: ${Number(setting.hiddenCommentListNum2) / 100}; } `, videoResolution = ` #${sid}_VideoResolution { display: block; }O `, semiTransparent = ` /*ヘッダーやナビゲーションなどを半透明にする*/ .com-application-Header, .com-application-Header:before { background: linear-gradient(0deg, rgba(0, 0, 0, 0.2), #000000) !important; } .com-InputText__input--dark-strong:not(:focus), .com-live-event-LiveEventOverlayControllerLayout__bottom-buttons .com-a-Button--dark:not(:hover) { background: rgba(33, 33, 33, 0.2) !important; } .c-application-SideNavigation__wrapper { background-color: rgba(0, 0, 0, 0.2) !important; background-image: linear-gradient(270deg,transparent, #000) !important; } .c-application-SideNavigation__footer { background-color: rgba(0, 0, 0, 0) !important; } .com-tv-LinearFooter__button button { background-color: rgba(0, 0, 0, 0.2) !important; } .c-application-SideNavigation__wrapper:hover, .com-tv-LinearFooter__button button:hover { background-color: rgba(0, 0, 0, 0.8) !important; } .com-tv-LinearChannelSwitcher__button { opacity: 0.5 !important; } .com-playback-Volume__icon { opacity: 0.8 !important; } .com-tv-LinearChannelSwitcher__button:hover, .com-playback-Volume__icon:hover { opacity: 1 !important; } .com-tv-RemoteController__button:hover { border: 2px outset #555555 !important; } .com-a-Tooltip { background-color: rgba(33, 33, 33, 0.5) !important; } .com-question-QuestionContainerView .com-question-VoteContent { background-color: rgba(23, 23, 23, 0.6) !important; } .com-question-QuestionContainerView .com-question-VoteContent:hover { background-color: rgba(23, 23, 23, 1) !important; } .com-question-VoteButton:hover:not(.com-question-VoteButton--highest) { background-color: #171717 !important; } `, smallFontSize = ` /*ヘッダーやフッターの一部の文字サイズを小さくする*/ .com-InputText__input { font-size: 14px !important; } .com-tv-LinearFooter__feed-super { font-size: 16px !important; } `, headerPosition = ` /*ヘッダーを追従表示にする*/ body:not(.com-timetable-TimeTable__body-timetable) :is(.c-common-HeaderContainer-header, .com-application-Header:not(.com-application-Header--shrunk)) { position: relative !important; } body:not(.com-timetable-TimeTable__body-timetable) .c-application-SideNavigation__wrapper { padding-top: 0 !important; } .com-a-ResponsiveMainContent { margin-top: 0 !important; } `, videoPadding = ` /*動画周辺の余白を減らす*/ .com-vod-VODResponsiveMainContent { margin: 0 !important; padding: 0 !important; } .com-vod-VODResponsiveMainContent .com-m-BreadcrumbList { padding: 7px 0; } .com-vod-VODResponsiveMainContent__inner { max-width: none !important; } `, mouseoverNavigation = ` /*左端にマウスオーバーしたときだけナビゲーションを表示する*/ .c-application-SideNavigation--collapsed, .c-application-SideNavigation__wrapper--collapsed { width: 8px !important; } .c-application-SideNavigation--collapsed:not(:hover) { opacity: 0 !important; } .com-tv-TVScreen__footer-container--sidenav-collapsed { padding-left: 0 !important; } .com-timetable-DesktopTimeTableWrapper__channel-content-header-wrapper--side-navigation-collapsed, .com-timetable-TimeTableListTimeAxis--is-collapsed-side-navigation { left: 8px !important; } `, sidePanelBackground = ` /*サイドパネルの背景を半透明にする*/ .c-tv-NowOnAirContainer__side-panel, .com-live-event-LiveEventPlayerSectionLayout__side-panel { background-color: rgba(0, 0, 0, 0.5) !important; } `, style = document.createElement('style'); if (s === 'init') { style.textContent = init; } else if (s === 'highlightNewComment') { style.textContent = highlightNewComment; } else if (s === 'overlapSidePanel') { style.textContent = overlapSidePanel; } else if (s === 'commentFontSize') { style.textContent = commentFontSize; } else if (s === 'reduceCommentSpace') { style.textContent = reduceCommentSpace; } else if (s === 'sidePanelCloseButton') { style.textContent = sidePanelCloseButton; } else if (s === 'showProgramDetail') { style.textContent = showProgramDetail; } else if (s === 'sidePanelSize') { style.textContent = sidePanelSize; } else if (s === 'hiddenButtonText') { style.textContent = hiddenButtonText; } else if (s === 'videoResolution') { style.textContent = videoResolution; } else if (s === 'semiTransparent') { style.textContent = semiTransparent; } else if (s === 'smallFontSize') { style.textContent = smallFontSize; } else if (s === 'headerPosition') { style.textContent = headerPosition; } else if (s === 'videoPadding') { style.textContent = videoPadding; } else if (s === 'mouseoverNavigation') { style.textContent = mouseoverNavigation; } else if (s === 'hiddenCommentList') { style.textContent = hiddenCommentList; } else if (s === 'hiddenCommentList2') { style.textContent = hiddenCommentList2; } else if (s === 'sidePanelBackground') { style.textContent = sidePanelBackground; } style.id = `${sid}_style_${s}`; document.head.appendChild(style); }; /** * 動画を構成している要素に変更があったとき */ const changeElements = () => { if (/^https:\/\/abema\.tv\/now-on-air\/.+$/.test(location.href)) { if (setting.closeSidePanel) checkSidePanel(); } else if ( /^https:\/\/abema\.tv\/(?:video\/episode|channels)\/.+$/.test( location.href ) ) { closeNextProgramInfo(); } hasCommentElement(); const inner = document.querySelector(selector.inner), report = document.querySelector(selector.commentReport); if (inner) { setTimeout(() => { hasVideoElement(); checkVisibleFooter(); }, 50); } if (report instanceof HTMLFormElement) { report.dataset[`${sid.toLowerCase()}Commentreportform`] = ''; let uid; if (/^https:\/\/abema\.tv\/now-on-air\/.+$/.test(location.href)) { uid = getCommentProps(report, 'userId'); } else if (/^https:\/\/abema\.tv\/live-event\/.+$/.test(location.href)) { const eProps = report.parentElement?.parentElement; if (eProps instanceof HTMLLIElement) { uid = getCommentProps(eProps, 'userId'); } } log('Comment Report Form: userId', uid); if (uid) { const list = document.querySelectorAll(selector.comenntAll), comments = [], ids = new Set(); for (let i = 0; i < list.length; i++) { const co = list[i]; if (co instanceof HTMLDivElement || co instanceof HTMLLIElement) { if (getCommentProps(co, 'userId') === uid) { const message = co.querySelector(selector.commentMessage), id = getCommentProps(co, 'id'); if (message && !ids.has(id)) { comments.push(message.textContent); ids.add(id); } } } } if (comments.length) { const eWrapper = document.createElement('div'), eHeader = document.createElement('div'), eList = document.createElement('div'); eWrapper.id = `${sid}_CommentReportForm-NgComment`; eHeader.id = `${sid}_CommentReportForm-NgCommentHeader`; eHeader.textContent = 'このユーザーのコメント履歴:'; eList.id = `${sid}_CommentReportForm-NgCommentList`; eWrapper.appendChild(eHeader); for (let i = 0; i < comments.length; i++) { const p = document.createElement('p'); p.textContent = comments[i]; eList.appendChild(p); } eWrapper.appendChild(eList); report.appendChild(eWrapper); } } } }; /** * ページタイトル変更&URL移動したとき */ const changePageTitle = () => { if (data.href !== location.href) { if (/^https:\/\/abema\.tv\/now-on-air\/.+$/.test(location.href)) { removeStyle('headerPosition'); } else reStyle('headerPosition', setting.headerPosition); data.href = location.href; } }; /** * 動画の画質を変更する * @param {Number} n */ const changeTargetQuality = (n) => { const vi = returnVideo(), vt = returnVideoTracks(); if (vt?.qualities) { const qualities = vt.qualities, len = qualities.length, heightList = [0, 180, 240, 360, 480, 720, 1080], targetHeight = heightList[n]; let target = -1; if (qualities.length === 1) { target = 0; } else if (qualities[0].height <= 240) { for (let i = 0; i < len; i++) { if (qualities[i].height <= targetHeight) { target = i; } } } else { for (let i = len - 1; i >= 0; i--) { if (qualities[i].height <= targetHeight) { target = i; } } } log( 'changeTargetQuality', n, len, target, targetHeight, qualities[target]?.height, vi?.videoHeight ); if (target >= 0) { const tqHeight = vt.targetQuality?.height; if (tqHeight !== qualities[target]?.height) { log( 'changeTargetQuality A', vt.activeQuality?.height || 'null', vt.targetQuality?.height || 'null', vi?.videoHeight ); vt.targetQuality = qualities[target]; } } else { if (vt.targetQuality) { log( 'changeTargetQuality B', vt.activeQuality?.height || 'null', vt.targetQuality?.height || 'null', vi?.videoHeight ); vt.targetQuality = null; } } clearInterval(interval.changeTargetQuality); interval.changeTargetQuality = setInterval(() => { if (vt) { if (target >= 0) { const tqHeight = vt.targetQuality?.height; if (tqHeight !== qualities[target]?.height) { log( 'changeTargetQuality C', vt.activeQuality?.height || 'null', vt.targetQuality?.height || 'null', vi?.videoHeight ); vt.targetQuality = qualities[target]; } } else { if (vt.targetQuality) { log( 'changeTargetQuality D', vt.activeQuality?.height || 'null', vt.targetQuality?.height || 'null', vi?.videoHeight ); vt.targetQuality = null; } } } else { clearInterval(interval.changeTargetQuality); } }, 2000); } else if (!/^https:\/\/abema\.tv\/live-event\/.+$/.test(location.href)) { log('changeTargetQuality: not found targetQuality', 'warn'); } }; /** * 動画のソースが切り替わったとき */ const changeVideoSource = () => { clearInterval(interval.videosource); interval.videosource = setInterval(() => { const vi = returnVideo(); if (vi) { clearInterval(interval.videosource); if (vi.hasAttribute('src')) { const src = vi.getAttribute('src'); if (src && src !== data.videoSource) { log('changeVideoSource'); data.videoSource = src; if (setting.qualityEnable) { changeTargetQuality(setting.targetQuality); } if (setting.videoResolution) showVideoResolution(); } } } }, 500); }; /** * ブロックしたユーザー数を確認する * @param {boolean} b trueならdata.ngIdReserveを操作する * @returns {number} ブロックしたユーザー数 */ const checkBlockedUser = (b) => { log('checkBlockedUser', b, data.blockedUserId); const sBui = localStorage.getItem('abm_blockedUserIds'); if (sBui) { const aBui = sBui.split(','); if (b) { if (aBui.length >= 100) { data.blockedUserId = aBui[0]; log('checkBlockedUser', data.blockedUserId, aBui[0], aBui[1]); } else { data.blockedUserId = ''; } } return aBui.length; } log('checkBlockedUser: not found abm_blockedUserIds', 'warn'); return 0; }; /** * クリックしたとき * @param {MouseEvent} e */ const checkClick = (e) => { if ( (/^https:\/\/abema\.tv\/now-on-air\/.+$/.test(location.href) && e.target instanceof HTMLElement && e.target.parentElement?.className === selector.commentReportSubmitTv) || (/^https:\/\/abema\.tv\/live-event\/.+$/.test(location.href) && e.target instanceof HTMLElement && e.target.parentElement?.className === selector.commentReportSubmitLe) ) { addNgId(); } }; /** * ダブルクリックしたとき * @param {MouseEvent} e */ const checkDblclick = (e) => { if ( setting.dblclickScroll && /^https:\/\/abema\.tv\/(?:video\/episode|channels|live-event)\/.+$/.test( location.href ) && e.target instanceof HTMLElement && e.target.closest(selector.videoDblclick) ) { adjustScrollPosition(); } }; /** * キーボードのキーを押したとき * @param {KeyboardEvent} e */ const checkKeyDown = (e) => { const isInput = e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement ? true : false; if (e.shiftKey && e.key === 'O' && (!isInput || (isInput && e.altKey))) { openSettings(); } else if (!isInput && e.key === 'Escape') { const submit = document.querySelectorAll(selector.commentReportCancel); for (let i = 0; i < submit.length; i++) { const button = submit[i]; if (button instanceof HTMLButtonElement) button.click(); } } else if (setting.enterKey && e.key === 'Enter') { if (!isInput) { const ca = document.querySelector(selector.commentArea); if (!ca) { const cb = document.querySelector(selector.commentButton); if (cb instanceof HTMLButtonElement) { cb.click(); } } const ta = document.querySelector(selector.commentForm); if (ta instanceof HTMLTextAreaElement) { ta.focus(); e.preventDefault(); } } const cc = document.querySelector(selector.commentContinue); if (cc instanceof HTMLButtonElement) { const cf = document.querySelector(selector.commentForm); if ( cf instanceof HTMLTextAreaElement && ((isInput && !cf.value) || !isInput) ) { cc.click(); } } } else if ( setting.pageKey && isInput && (e.key === 'PageUp' || e.key === 'PageDown') ) { const cl = document.querySelector(selector.commentList)?.parentElement; if (cl) { const ch = cl.clientHeight; if (ch) { const scroll = e.key === 'PageUp' ? -(ch - 40) : ch - 40; cl.scrollBy({ top: scroll, behavior: 'smooth' }); e.preventDefault(); } } } else if ( setting.sidePanelBackground && e.shiftKey && e.key === 'B' && (!isInput || (isInput && e.altKey)) ) { const style = document.getElementById(`${sid}_style_sidePanelBackground`); if (style) removeStyle('sidePanelBackground'); else addStyle('sidePanelBackground'); } else if ( setting.hiddenCommentList && e.shiftKey && e.key === 'C' && (!isInput || (isInput && e.altKey)) ) { const style1 = document.getElementById(`${sid}_style_hiddenCommentList`), style2 = document.getElementById(`${sid}_style_hiddenCommentList2`); if (!style1 && !style2) { addStyle('hiddenCommentList'); } else if (style1) { removeStyle('hiddenCommentList'); if (setting.hiddenCommentListNum2) addStyle('hiddenCommentList2'); } else if (style2) { removeStyle('hiddenCommentList2'); } } else if ( e.target instanceof HTMLElement && (e.target.closest(selector.commentReportTv) || e.target.closest(selector.commentReportLe)) ) { if ( setting.ngIdEnable && e.target.textContent === 'ブロック' && (e.key === 'Enter' || e.key === ' ') ) { addNgId(); } } }; /** * マウスカーソルをコメントリストに重ねたとき * @param {MouseEvent} e */ const checkMouseEnter = (e) => { const cl = document.querySelector(selector.commentList)?.parentElement; if (cl === e.target) data.commentMouseEnter = true; }; /** * マウスカーソルをコメントリストから外したとき * @param {MouseEvent} e */ const checkMouseLeave = (e) => { const cl = document.querySelector(selector.commentList)?.parentElement; if (cl === e.target) data.commentMouseEnter = false; }; /** * 新規コメントを1行ずつ表示&スクロール表示する */ const checkNewComments = () => { if (setting.newCommentOneByOne) { const service = /^https:\/\/abema\.tv\/now-on-air\/.+$/.test( location.href ) ? 'tv' : /^https:\/\/abema\.tv\/live-event\/.+$/.test(location.href) ? 'le' : ''; if (!service) return; const ca = document.querySelectorAll(selector.comenntAll), /** @type {NodeListOf|null} */ cb = service ? document.querySelectorAll(selector.commentBefore) : null; if (!cb?.length) return; if (ca?.length === cb.length) data.commentId.clear(); const listP = service === 'tv' ? document.querySelector(selector.commentList)?.parentElement : service === 'le' ? document.querySelector(selector.commentList)?.parentElement ?.parentElement : null; if (listP) { for (let i = 0; i < cb.length; i++) { const eMessage = cb[i].querySelector(selector.commentMessage), /** @type {HTMLDivElement|null} */ eInner = cb[i].querySelector(selector.commentInner), message = eMessage?.textContent, cid = getCommentProps(cb[i], 'id'), uid = getCommentProps(cb[i], 'userId'); let ngFlag = false; if (!eInner) continue; //重複コメントの処理 if (data.commentId.has(cid)) { ngFlag = true; if (!(`${sid.toLowerCase()}Duplicate` in eInner.dataset)) { log(`Duplicate: ${uid} / ${cid} / ${message}`); eInner.dataset[`${sid.toLowerCase()}Duplicate`] = ''; } } else data.commentId.add(cid); //NG IDの処理 if (!ngFlag && setting.ngIdEnable && data.ngId.has(uid)) { ngFlag = true; if (!(`${sid.toLowerCase()}Ngid` in eInner.dataset)) { log(`NG ID: ${uid} / ${cid} / ${message}`); eInner.dataset[`${sid.toLowerCase()}Ngid`] = ''; } } //NGワードの処理 if (!ngFlag && setting.ngWordEnable && message) { for (let j = 0; j < data.ngWordText.length; j++) { if (data.ngWordText[j] && message.includes(data.ngWordText[j])) { ngFlag = true; eInner.dataset[`${sid.toLowerCase()}Ngword`] = ''; if (setting.ngConsole) { console.log( `${sid} NG Word: ${data.ngWordText[j]} / Comment: ${message} / UserID: ${uid}` ); } break; } } if (!ngFlag) { for (let j = 0; j < data.ngWordRe.length; j++) { const wr = data.ngWordRe[j], re = new RegExp(wr.r, wr.f); if (data.ngWordRe[j] && re.test(message)) { ngFlag = true; eInner.dataset[`${sid.toLowerCase()}Ngword`] = ''; if (setting.ngConsole) { const ng = re.exec(message); if (ng) { console.log( `${sid} NG Word: ${ng[0]} / Comment: ${ng.input} / UserID: ${uid}` ); } } break; } } } } if (ngFlag) eInner.dataset[`${sid.toLowerCase()}Hidden`] = ''; else eInner.dataset[`${sid.toLowerCase()}Hidden`] = 'true'; if ( /^https:\/\/abema\.tv\/live-event\/.+$/.test(location.href) && getCommentProps(cb[i], 'isOwnComment') ) { eInner.dataset[`${sid.toLowerCase()}Own`] = ''; } } const hidden = document.querySelectorAll(selector.commentHidden), time = service === 'tv' ? hidden.length > 7 ? (6.5 / hidden.length) * 1000 : 920 : service === 'le' ? hidden.length > 5 ? (4.5 / hidden.length) * 1000 : 900 : 1000; clearInterval(interval.newcomment); interval.newcomment = setInterval(() => { /** @type {HTMLDivElement|null} */ const ch = document.querySelector(selector.commentHidden), rf = document.querySelector(selector.commentReport); if (ch && !rf && !data.commentMouseEnter) { /** @type {HTMLDivElement|null} */ const chi = ch.querySelector(selector.commentInner); if (chi) chi.dataset[`${sid.toLowerCase()}Hidden`] = 'false'; if ( listP.scrollHeight - listP.scrollTop - listP.clientHeight < 500 ) { if (setting.scrollNewComment && hidden.length < 30) { listP.scroll({ top: listP.scrollHeight, behavior: 'auto', }); listP.scrollBy({ top: -ch.clientHeight, behavior: 'auto', }); listP.scrollBy({ top: ch.clientHeight + 1, behavior: 'smooth', }); } else { listP.scrollBy({ top: ch.clientHeight + 1, behavior: 'auto', }); } } /** @type {HTMLButtonElement|null} */ const cButton = document.querySelector(selector.commentContinue); if (cButton) { if ( listP.scrollHeight - listP.scrollTop - listP.clientHeight < 500 ) { cButton.click(); } } } else if (!rf && !data.commentMouseEnter) { clearInterval(interval.newcomment); if ( listP.scrollHeight - listP.scrollTop - listP.clientHeight < 500 ) { if (setting.scrollNewComment && hidden.length < 30) { listP.scroll({ top: listP.scrollHeight, behavior: 'smooth', }); } else { listP.scroll({ top: listP.scrollHeight, behavior: 'auto', }); } } } }, time); } } }; /** * サイドパネルが最初から開いているかを調べる */ const checkSidePanel = () => { if (!document.querySelector(`.${sid}_SidePanelCloseed`)) { /** @type {HTMLButtonElement|null} */ const button = document.querySelector(selector.sidePanelClose); button?.classList.add(`${sid}_SidePanelCloseed`); button?.click(); } }; /** * スクリプトとストレージのバージョンを確認 * (現時点ではバージョンを登録するだけ) */ const checkVersion = () => { ls.version = data.version; saveStorage(); }; /** * 動画のフッターが表示されているかを調べる */ const checkVisibleFooter = () => { const cvf = () => { const fo = document.querySelector(selector.footerVisible); if (setting.videoResolution && fo) showVideoResolution(); }; clearInterval(interval.footer); interval.footer = setInterval(cvf, 500); cvf(); }; /** * 「次のエピソード」が表示されたらキャンセルボタンを押す * 可能ならスキップボタンを押す */ const closeNextProgramInfo = () => { const nc = document.querySelector(selector.nextCancel), ncm = document.querySelector(selector.nextCancelMini), vs = document.querySelector(selector.videoSkip); if (setting.nextProgramInfo && nc instanceof HTMLButtonElement) { setTimeout(() => { nc.click(); }, 2000); } if (setting.nextProgramInfo && ncm instanceof HTMLButtonElement) { setTimeout(() => { ncm.click(); }, 2000); } if (setting.skipVideo && vs instanceof HTMLButtonElement) { clearInterval(interval.videoskip); interval.videoskip = setInterval(() => { const vs2 = document.querySelector(selector.videoSkip2); if (vs2 instanceof HTMLButtonElement) { clearInterval(interval.videoskip); vs2.click(); } else if (!vs && !vs2) clearInterval(interval.videoskip); }, 500); } }; /** * 一部の通知を閉じる */ const closeNotificationToast = () => { log('closeNotificationToast'); /** @type {HTMLButtonElement|null} */ const closeButton = document.querySelector(selector.notificationClose), /** @type {HTMLVideoElement|null} */ message = document.querySelector(selector.notificationMessage); if (closeButton && message) { if (/推奨環境外のため/.test(message.innerText)) { closeButton.click(); } } }; /** * 設定欄を閉じる */ const closeSettings = () => { const settings = document.querySelector(`#${sid}_Settings`); settings?.classList.add(`${sid}_Settings_hidden`); }; /** * 記入されたNGワードを処理しやすくするため正規表現用とそれ以外用に分ける * @param {string} t * @returns {string} */ const convertNgword = (t) => { log('convertNgword'); const aWord = t.split(/\r\n|\n|\r/), aText = [], aRe = []; let sError = ''; for (let i = 0, j = aWord.length; i < j; i++) { const str = aWord[i].trim(); if (str.slice(0, 2) !== '//') { if (/^\/.+\/[dgimsuvy]{0,}$/.test(str)) { try { RegExp(str); const re = str.slice(1, str.lastIndexOf('/')), flag = str.slice(str.lastIndexOf('/') + 1) || ''; aRe.push({ r: re, f: flag }); } catch (error) { sError += `${i + 1}行目: ${str}\n`; } } else { aText.push(str); } } } if (!sError) { data.ngWordText = [...aText]; data.ngWordRe = [...aRe]; } return sError; }; /** * 設定欄を作成する */ const createSettings = () => { const sSettings = `
${sid} 設定
全般









テレビ



ビデオ



コメント









[説明]

番組の画質を設定します。ライブイベントには対応していません。

番組を開いた直後やCM直後などから数秒後に設定した画質が反映されます。

設定した画質が用意されていない番組ではそれよりも低い画質で再生します。

[説明]

NGワードに該当するコメントを表示しません。

1行に1つのNGワードを記入してください。
先頭と末尾に / を付けると正規表現として扱います。
正規表現の末尾に i や u などを追記してフラグを使用できます。

先頭が // の行はコメントとして扱うのでNGワードとして使用しません。

記入例:

Aaa
bbb
/Ccc|DDD|eEe/

//大文字小文字を区別しません
/fff|ggg|hhh/i

//10桁以上の数字
/\\d{10,}/

//5文字以上の同じ文字を3回以上繰り返している
//(例:あいうえおあいうえおあいうえお)
/(.{5,})\\1{2,}/

//[ひらがな・カタカナ・漢字・絵文字・全角英数字・一部の全角記号]の
//いずれも含まない(Chromeなど一部のブラウザのみ有効)
/^(?!.*[\\p{scx=Hira}\\p{scx=Kana}\\p{scx=Han}\\p{RGI_Emoji}\\uFF01-\\uFF65]).*$/v

NGワードが多すぎると動作が重くなるのでご注意ください。

エラー:下記の正規表現を修正してください。


      
[説明]

ABEMAではコメント欄からブロックしたユーザーIDをブラウザに100件まで保存していて、それ以上ブロックしたときは古い方から破棄されます。
このNG ID機能ではその破棄されるユーザーIDを別枠で保存しておいてそのユーザーのコメントもブロックします。

現在の保存数よりも少ない保存数に変更した場合は古いほうから溢れた分を破棄します。
0に変更するとすべて破棄します。

`, sMenu = `
${sid}
`, eSettings = document.createElement('div'), eMenuItem = document.createElement('li'); eSettings.id = `${sid}_Settings`; eSettings.className = `${sid}_Settings_hidden`; eSettings.innerHTML = sSettings; eMenuItem.className = 'com-application-SideNavigationSubMenuItem'; eMenuItem.innerHTML = sMenu; eMenuItem.addEventListener('click', openSettings); document.body.appendChild(eSettings); document .getElementById(`${sid}_Settings-overlapSidePanel`) ?.addEventListener('change', () => { const osp = document.getElementById(`${sid}_Settings-overlapSidePanel`), spb = document.getElementById(`${sid}_Settings-sidePanelBackground`); if ( osp instanceof HTMLInputElement && spb instanceof HTMLInputElement ) { spb.disabled = osp.checked ? false : true; } }); document .getElementById(`${sid}_Settings-sidePanelSize`) ?.addEventListener('change', () => { const sps = document.getElementById(`${sid}_Settings-sidePanelSize`), spsn = document.getElementById(`${sid}_Settings-sidePanelSizeNum`); if ( sps instanceof HTMLInputElement && spsn instanceof HTMLInputElement ) { spsn.disabled = sps.checked ? false : true; } }); document .getElementById(`${sid}_Settings-newCommentOneByOne`) ?.addEventListener('change', () => { const snc = document.getElementById(`${sid}_Settings-scrollNewComment`), obo = document.getElementById(`${sid}_Settings-newCommentOneByOne`); if ( snc instanceof HTMLInputElement && obo instanceof HTMLInputElement ) { snc.disabled = obo.checked ? false : true; } }); document .getElementById(`${sid}_Settings-commentFontSize`) ?.addEventListener('change', () => { const cfs = document.getElementById(`${sid}_Settings-commentFontSize`), cfsn = document.getElementById(`${sid}_Settings-commentFontSizeNum`); if ( cfs instanceof HTMLInputElement && cfsn instanceof HTMLInputElement ) { cfsn.disabled = cfs.checked ? false : true; } }); document .getElementById(`${sid}_Settings-hiddenCommentList`) ?.addEventListener('change', () => { const hcl = document.getElementById( `${sid}_Settings-hiddenCommentList` ), hcln = document.getElementById( `${sid}_Settings-hiddenCommentListNum` ), hcln2 = document.getElementById( `${sid}_Settings-hiddenCommentListNum2` ); if ( hcl instanceof HTMLInputElement && hcln instanceof HTMLInputElement && hcln2 instanceof HTMLInputElement ) { hcln.disabled = hcl.checked ? false : true; hcln2.disabled = hcl.checked ? false : true; } }); document .getElementById(`${sid}_Settings-qualityEnable`) ?.addEventListener('change', () => { const qe = document.getElementById(`${sid}_Settings-qualityEnable`), tq = document.getElementById(`${sid}_Settings-targetQuality`); if (qe instanceof HTMLInputElement && tq instanceof HTMLSelectElement) { tq.disabled = qe.checked ? false : true; } }); document .getElementById(`${sid}_Settings-ngWordEnable`) ?.addEventListener('change', () => { const ngwe = document.getElementById(`${sid}_Settings-ngWordEnable`), ngw = document.getElementById(`${sid}_Settings-ngWord`), ngc = document.getElementById(`${sid}_Settings-ngConsole`); if ( ngwe instanceof HTMLInputElement && ngw instanceof HTMLTextAreaElement && ngc instanceof HTMLInputElement ) { ngw.disabled = ngwe.checked ? false : true; ngc.disabled = ngwe.checked ? false : true; } }); document .getElementById(`${sid}_Settings-ngIdEnable`) ?.addEventListener('change', () => { const ngie = document.getElementById(`${sid}_Settings-ngIdEnable`), ngims = document.getElementById(`${sid}_Settings-ngIdMaxSize`); if ( ngie instanceof HTMLInputElement && ngims instanceof HTMLSelectElement ) { ngims.disabled = ngie.checked ? false : true; } }); document .getElementById(`${sid}_Settings-ok`) ?.addEventListener('click', () => { const eWord = document.getElementById(`${sid}_Settings-ngWord`), eWordError = document.getElementById(`${sid}_Settings-ngWord-error`), eWordPre = document.getElementById( `${sid}_Settings-ngWord-error-pre` ); let sError = ''; if (eWord instanceof HTMLTextAreaElement) { const sWord = eWord.value; if (sWord) { sError = convertNgword(sWord); } } if ( eWordError instanceof HTMLDivElement && eWordPre instanceof HTMLPreElement ) { if (sError) { const eTabNg = document.getElementById(`${sid}_Settings-Tab-Ng`); if (eTabNg instanceof HTMLInputElement) { eWordPre.innerText = sError; eWordError.style.display = 'block'; eTabNg.checked = true; } } else { eWordPre.innerText = ''; eWordError.style.display = 'none'; saveSettings(); closeSettings(); } } }); document .getElementById(`${sid}_Settings-cancel`) ?.addEventListener('click', () => { closeSettings(); loadSettings(); }); setTimeout(() => { const mypageMenu = document.querySelector(selector.mypageMenu); if (mypageMenu) { mypageMenu.appendChild(eMenuItem); } else log('createSettings: not found mypageMenu', 'warn'); }, 1000); }; /** * コメントのプロパティを取得する * @param {HTMLDivElement|HTMLFormElement|HTMLLIElement} e 調べる要素 * @param {string} k キー名 * @returns {boolean|string|undefined} */ const getCommentProps = (e, k) => { let flag = false; for (const key in e) { if (key.startsWith('__reactFiber$')) { flag = true; if (k === 'isOwnComment') { if (e[key].return?.pendingProps?.isOwnComment) { return e[key].return?.pendingProps?.isOwnComment; } log('getCommentProps: not found key', k); console.debug(e); return false; } if ( e[key].return?.pendingProps?.comment && e[key].return.pendingProps.comment[`_${k}`] ) { return e[key].return.pendingProps.comment[`_${k}`]; } if ( e[key].return?.pendingProps?.commentItem && e[key].return.pendingProps.commentItem[k] ) { return e[key].return.pendingProps.commentItem[k]; } } } log('getCommentProps: not found key', k, flag); console.debug(e); return undefined; }; /** * コメント欄の要素があるか調べる */ const hasCommentElement = () => { const check = () => { const ca = document.querySelector(selector.commentArea); if (ca) { clearInterval(interval.comment); if (!ca.classList.contains(`${sid}_CommentElement`)) { log('hasCommentElement'); setTimeout(() => { const cl = ca.querySelector(selector.commentList); if (cl) { ca.classList.add(`${sid}_CommentElement`); if (setting.stopCommentScroll) { const cl2 = cl.parentElement; if (cl2) { cl2.addEventListener('mouseenter', checkMouseEnter); cl2.addEventListener('mouseleave', checkMouseLeave); } } if (setting.newCommentOneByOne) { observerC.observe(cl, { childList: true }); checkNewComments(); } } else log('hasCommentElement: Not found element.', 'warn'); }, 1000); } } else { clearInterval(interval.newcomment); } }; clearInterval(interval.comment); interval.comment = setInterval(check, 500); check(); }; /** * 通知の要素があるか調べる */ const hasNotification = () => { clearInterval(interval.notification); interval.notification = setInterval(() => { const noti = document.querySelector(selector.notification); if (noti) { clearInterval(interval.notification); closeNotificationToast(); } }, 1000); }; /** * サイドナビゲーションの要素があるか調べる */ const hasSideNavigation = () => { log('hasSideNavigation'); clearInterval(interval.navigation); interval.navigation = setInterval(() => { const navi = document.querySelector(selector.sideNavi); if (navi) { clearInterval(interval.navigation); if ( !navi.classList.contains(selector.sideNaviColl) && !navi.classList.contains(selector.sideNaviWrapColl) ) { reduceSideNavigation(); } } }, 4000); }; /** * VIDEO要素があるか調べる */ const hasVideoElement = () => { clearInterval(interval.videoelement); interval.videoelement = setInterval(() => { const vi = returnVideo(); if (vi) { clearInterval(interval.videoelement); if (!vi.classList.contains(`${sid}_VideoElement`)) { log('hasVideoElement'); vi.classList.add(`${sid}_VideoElement`); observerV.observe(vi, { attributes: true }); observerR.observe(vi); if (setting.qualityEnable) { changeTargetQuality(setting.targetQuality); } } } }, 500); }; /** * ページを開いたときに実行 */ const init = () => { log('init'); addStyle('init'); checkVersion(); setInitialValue(); if (!document.getElementById(`${sid}_Settings`)) createSettings(); convertNgword(setting.ngWord); checkBlockedUser(true); setTimeout(startFirstObserve, 1000); if (setting.highlightNewComment) addStyle('highlightNewComment'); if (setting.sidePanelCloseButton) addStyle('sidePanelCloseButton'); if (setting.showProgramDetail) addStyle('showProgramDetail'); if (setting.overlapSidePanel) addStyle('overlapSidePanel'); if (setting.sidePanelSize) addStyle('sidePanelSize'); if (setting.hiddenButtonText) addStyle('hiddenButtonText'); if (setting.videoResolution) addStyle('videoResolution'); if (setting.semiTransparent) addStyle('semiTransparent'); if (setting.smallFontSize) addStyle('smallFontSize'); if (setting.commentFontSize) addStyle('commentFontSize'); if (setting.reduceCommentSpace) addStyle('reduceCommentSpace'); if ( setting.headerPosition && !/^https:\/\/abema\.tv\/now-on-air\/.+$/.test(location.href) ) { addStyle('headerPosition'); } if (setting.videoPadding) addStyle('videoPadding'); if (setting.mouseoverNavigation) addStyle('mouseoverNavigation'); if (setting.reduceNavigation) hasSideNavigation(); if (setting.closeNotification) hasNotification(); }; /** * 設定を読み込んで設定欄に反映する */ const loadSettings = () => { /** * 変数aの型がsとは異なる場合trueを返す * @param {any} a 判別したい変数 * @param {string} t 型 * @returns {boolean} */ const notType = (a, t) => Object.prototype.toString.call(a).slice(8, -1) !== t ? true : false; /** * 保存している値を設定欄のチェックボックスに反映する * @param {string} s 変数名 * @param {string} t 型 */ const setCheck = (s, t) => { const e = document.getElementById(`${sid}_Settings-${s}`); if (e instanceof HTMLInputElement && !notType(setting[s], t)) { e.checked = setting[s]; } }; /** * 保存している値を設定欄のセレクトボックスに反映する * @param {string} s 変数名 * @param {string} t 型 */ const setSelect = (s, t) => { const e = document.getElementById(`${sid}_Settings-${s}`); if (e) { if (e instanceof HTMLSelectElement && !notType(setting[s], t)) { e.options.selectedIndex = setting[s]; } } }; /** * 保存している値を設定欄の入力ボックス・テキストエリアに反映する * @param {string} s 変数名 * @param {string} t 型 */ const setValue = (s, t) => { const e = document.getElementById(`${sid}_Settings-${s}`); if (e instanceof HTMLTextAreaElement) { if (s === 'ngWord' && !notType(lsWord[s], t)) e.value = lsWord[s]; } else if (e instanceof HTMLInputElement) { if (!notType(setting[s], t)) e.value = setting[s]; } }; setCheck('reduceNavigation', 'Boolean'); setCheck('hiddenButtonText', 'Boolean'); setCheck('closeNotification', 'Boolean'); setCheck('overlapSidePanel', 'Boolean'); setCheck('videoResolution', 'Boolean'); setCheck('semiTransparent', 'Boolean'); setCheck('smallFontSize', 'Boolean'); setCheck('closeSidePanel', 'Boolean'); setCheck('sidePanelCloseButton', 'Boolean'); setCheck('showProgramDetail', 'Boolean'); setCheck('sidePanelBackground', 'Boolean'); setCheck('sidePanelSize', 'Boolean'); setValue('sidePanelSizeNum', 'String'); setCheck('nextProgramInfo', 'Boolean'); setCheck('skipVideo', 'Boolean'); setCheck('headerPosition', 'Boolean'); setCheck('videoPadding', 'Boolean'); setCheck('dblclickScroll', 'Boolean'); setCheck('mouseoverNavigation', 'Boolean'); setCheck('newCommentOneByOne', 'Boolean'); setCheck('scrollNewComment', 'Boolean'); setCheck('stopCommentScroll', 'Boolean'); setCheck('highlightNewComment', 'Boolean'); setCheck('commentFontSize', 'Boolean'); setValue('commentFontSizeNum', 'String'); setCheck('reduceCommentSpace', 'Boolean'); setCheck('hiddenCommentList', 'Boolean'); setValue('hiddenCommentListNum', 'String'); setValue('hiddenCommentListNum2', 'String'); setCheck('escKey', 'Boolean'); setCheck('enterKey', 'Boolean'); setCheck('pageKey', 'Boolean'); setCheck('qualityEnable', 'Boolean'); setSelect('targetQuality', 'Number'); setCheck('ngWordEnable', 'Boolean'); setValue('ngWord', 'String'); setSelect('ngIdMaxSize', 'Number'); setCheck('ngConsole', 'Boolean'); setCheck('ngIdEnable', 'Boolean'); const spb = document.getElementById(`${sid}_Settings-sidePanelBackground`); if (spb instanceof HTMLInputElement) { spb.disabled = setting.overlapSidePanel ? false : true; } const snc = document.getElementById(`${sid}_Settings-scrollNewComment`); if (snc instanceof HTMLInputElement) { snc.disabled = setting.newCommentOneByOne ? false : true; } const tq = document.getElementById(`${sid}_Settings-targetQuality`); if (tq instanceof HTMLSelectElement) { tq.disabled = setting.qualityEnable ? false : true; } const ngw = document.getElementById(`${sid}_Settings-ngWord`), ngc = document.getElementById(`${sid}_Settings-ngConsole`); if (ngw instanceof HTMLTextAreaElement && ngc instanceof HTMLInputElement) { ngw.disabled = setting.ngWordEnable ? false : true; ngc.disabled = setting.ngWordEnable ? false : true; } const ngims = document.getElementById(`${sid}_Settings-ngIdMaxSize`); if (ngims instanceof HTMLSelectElement) { ngims.disabled = setting.ngIdEnable ? false : true; } const ngwe = document.getElementById(`${sid}_Settings-ngWord-error`), ngwep = document.getElementById(`${sid}_Settings-ngWord-error-pre`); if (ngwe instanceof HTMLDivElement && ngwep instanceof HTMLPreElement) { ngwep.innerText = ''; ngwe.style.display = 'none'; } const record = document.getElementById(`${sid}_Settings-ngId-record`); if (record instanceof HTMLSpanElement) { record.textContent = data.ngId.size ? `(現在の保存数:${data.ngId.size})` : ''; } }; /** * デバッグ用ログ * @param {...any} a */ const log = (...a) => { if (ls.debug) { try { if (/^debug$|^error$|^info$|^warn$/.test(a[a.length - 1])) { const b = a.pop(); console[b](sid, a.join(' ')); } else console.log(sid, a.join(' ')); } catch (e) { if (e instanceof Error) console.error(e.message, ...a); else if (typeof e === 'string') console.error(e, ...a); else console.error('log error', ...a); } } }; /** * 設定欄を開く */ const openSettings = () => { const settings = document.querySelector(`#${sid}_Settings`); if (settings && settings.classList.contains(`${sid}_Settings_hidden`)) { loadSettings(); settings.classList.remove(`${sid}_Settings_hidden`); } }; /** * サイドナビゲーションを縮める */ const reduceSideNavigation = () => { log('reduceSideNavigation'); /** @type {HTMLButtonElement|null} */ const headerMenu = document.querySelector(selector.headerMenu); headerMenu?.click(); }; /** * 動画の大きさが変わったとき */ const resizeVideo = () => { if (setting.qualityEnable) { clearTimeout(interval.resizeVideo); interval.resizeVideo = setTimeout(() => { changeTargetQuality(setting.targetQuality); checkVisibleFooter(); }, 500); } }; /** * スタイルを追加・削除する * @param {string} s スタイルの設定名 * @param {boolean} b trueならスタイルを追加 */ const reStyle = (s, b) => { removeStyle(s); if (b) addStyle(s); }; const removeStyle = (s) => { const e = document.getElementById(`${sid}_style_${s}`); if (e instanceof HTMLStyleElement) e.remove(); }; /** * video要素を返す * @returns {HTMLVideoElement|null} */ const returnVideo = () => { /** @type {HTMLVideoElement|null} */ const vi = document.querySelector(selector.video); return vi ? vi : null; }; /** * 動画のvideoTracksを返す * @returns {object|undefined} */ const returnVideoTracks = () => { const vc = document.querySelector(selector.videoContainer); for (const key in vc) { if ( key.startsWith('__reactFiber$') && vc[key].return?.stateNode?.player?.videoTracks[0] ) { return vc[key].return.stateNode.player.videoTracks[0]; } } return undefined; }; /** * 設定を保存する */ const saveSettings = () => { /** * 設定欄のチェックボックスの値を取得する * @param {string} s 変数名 */ const getCheck = (s) => { const e = document.getElementById(`${sid}_Settings-${s}`); if (e instanceof HTMLInputElement) { setting[s] = e.checked ? true : false; } }; /** * 設定欄のセレクトボックスの値を取得する * @param {string} s 変数名 */ const getSelect = (s) => { const e = document.getElementById(`${sid}_Settings-${s}`); if (e instanceof HTMLSelectElement) { const index = e.options.selectedIndex; if (Number.isInteger(index) && index >= 0) { setting[s] = index; } } }; /** * 設定欄の入力ボックス・テキストエリアの値を取得する * @param {string} s 変数名 */ const getValue = (s) => { const e = document.getElementById(`${sid}_Settings-${s}`); if (e instanceof HTMLInputElement || e instanceof HTMLTextAreaElement) { if (s === 'commentFontSizeNum') { if (!e.value || isNaN(Number(e.value)) || /e/.test(e.value)) { e.value = '13'; } else if (Number(e.value) < 10) e.value = '10'; else if (Number(e.value) > 32) e.value = '32'; } else if ( s === 'hiddenCommentListNum' || s === 'hiddenCommentListNum2' ) { if (!e.value || isNaN(Number(e.value)) || /e/.test(e.value)) { if (s === 'hiddenCommentListNum') e.value = '50'; else if (s === 'hiddenCommentListNum2') e.value = ''; } else if (Number(e.value) < 0) e.value = '0'; else if (Number(e.value) > 100) e.value = '100'; } else if (s === 'sidePanelSizeNum') { if (!e.value || isNaN(Number(e.value)) || /e/.test(e.value)) { e.value = '320'; } else if (Number(e.value) < 100) e.value = '100'; else if (Number(e.value) > 1000) e.value = '1000'; } setting[s] = e.value ? e.value : ''; } }; document.getElementById(`${sid}_Settings-ok`)?.blur(); getCheck('reduceNavigation'); getCheck('hiddenButtonText'); getCheck('closeNotification'); getCheck('overlapSidePanel'); getCheck('videoResolution'); getCheck('semiTransparent'); getCheck('smallFontSize'); getCheck('closeSidePanel'); getCheck('sidePanelBackground'); getCheck('sidePanelCloseButton'); getCheck('showProgramDetail'); getCheck('sidePanelSize'); getValue('sidePanelSizeNum'); getCheck('nextProgramInfo'); getCheck('skipVideo'); getCheck('headerPosition'); getCheck('videoPadding'); getCheck('dblclickScroll'); getCheck('mouseoverNavigation'); getCheck('newCommentOneByOne'); getCheck('scrollNewComment'); getCheck('stopCommentScroll'); getCheck('highlightNewComment'); getCheck('commentFontSize'); getValue('commentFontSizeNum'); getCheck('reduceCommentSpace'); getCheck('hiddenCommentList'); getValue('hiddenCommentListNum'); getValue('hiddenCommentListNum2'); getCheck('escKey'); getCheck('enterKey'); getCheck('pageKey'); getCheck('qualityEnable'); getSelect('targetQuality'); getCheck('ngWordEnable'); getValue('ngWord'); getSelect('ngIdMaxSize'); getCheck('ngConsole'); getCheck('ngIdEnable'); ls.reduceNavigation = setting.reduceNavigation; if (ls.hiddenButtonText !== setting.hiddenButtonText) { reStyle('hiddenButtonText', setting.hiddenButtonText); } ls.hiddenButtonText = setting.hiddenButtonText; ls.closeNotification = setting.closeNotification; if (ls.videoResolution !== setting.videoResolution) { reStyle('videoResolution', setting.videoResolution); } ls.videoResolution = setting.videoResolution; if (ls.semiTransparent !== setting.semiTransparent) { reStyle('semiTransparent', setting.semiTransparent); } ls.semiTransparent = setting.semiTransparent; if (ls.smallFontSize !== setting.smallFontSize) { reStyle('smallFontSize', setting.smallFontSize); } ls.smallFontSize = setting.smallFontSize; ls.closeSidePanel = setting.closeSidePanel; if (ls.overlapSidePanel !== setting.overlapSidePanel) { reStyle('overlapSidePanel', setting.overlapSidePanel); if (setting.highlightNewComment) reStyle('highlightNewComment', true); } ls.overlapSidePanel = setting.overlapSidePanel; ls.sidePanelBackground = setting.sidePanelBackground; if (ls.sidePanelCloseButton !== setting.sidePanelCloseButton) { reStyle('sidePanelCloseButton', setting.sidePanelCloseButton); } ls.sidePanelCloseButton = setting.sidePanelCloseButton; if (ls.showProgramDetail !== setting.showProgramDetail) { reStyle('showProgramDetail', setting.showProgramDetail); } ls.showProgramDetail = setting.showProgramDetail; if (ls.sidePanelSize || ls.sidePanelSize !== setting.sidePanelSize) { reStyle('overlapSidePanel', setting.overlapSidePanel); reStyle('sidePanelSize', setting.sidePanelSize); } ls.sidePanelSize = setting.sidePanelSize; ls.sidePanelSizeNum = setting.sidePanelSizeNum; ls.nextProgramInfo = setting.nextProgramInfo; ls.skipVideo = setting.skipVideo; if (ls.headerPosition !== setting.headerPosition) { reStyle('headerPosition', setting.headerPosition); } if (/^https:\/\/abema\.tv\/now-on-air\/.+$/.test(location.href)) { removeStyle('headerPosition'); } ls.headerPosition = setting.headerPosition; if (ls.videoPadding !== setting.videoPadding) { reStyle('videoPadding', setting.videoPadding); } ls.videoPadding = setting.videoPadding; if (ls.mouseoverNavigation !== setting.mouseoverNavigation) { reStyle('mouseoverNavigation', setting.mouseoverNavigation); } ls.mouseoverNavigation = setting.mouseoverNavigation; ls.newCommentOneByOne = setting.newCommentOneByOne; ls.scrollNewComment = setting.scrollNewComment; ls.stopCommentScroll = setting.stopCommentScroll; if (ls.highlightNewComment !== setting.highlightNewComment) { reStyle('highlightNewComment', setting.highlightNewComment); } ls.highlightNewComment = setting.highlightNewComment; if (ls.commentFontSizeNum !== setting.commentFontSizeNum) { reStyle('commentFontSize', setting.commentFontSize); } ls.commentFontSizeNum = setting.commentFontSizeNum; if (ls.commentFontSize !== setting.commentFontSize) { reStyle('commentFontSize', setting.commentFontSize); } ls.commentFontSize = setting.commentFontSize; if (ls.reduceCommentSpace !== setting.reduceCommentSpace) { reStyle('reduceCommentSpace', setting.reduceCommentSpace); } ls.reduceCommentSpace = setting.reduceCommentSpace; if (ls.hiddenCommentList && !setting.hiddenCommentList) { removeStyle('hiddenCommentList'); removeStyle('hiddenCommentList2'); } ls.hiddenCommentList = setting.hiddenCommentList; if ( setting.hiddenCommentList && ls.hiddenCommentListNum !== setting.hiddenCommentListNum && document.getElementById(`${sid}_style_hiddenCommentList`) ) { reStyle('hiddenCommentList', true); } ls.hiddenCommentListNum = setting.hiddenCommentListNum; if ( setting.hiddenCommentList && ls.hiddenCommentListNum2 !== setting.hiddenCommentListNum2 && document.getElementById(`${sid}_style_hiddenCommentList2`) ) { reStyle('hiddenCommentList2', setting.hiddenCommentListNum2); } ls.hiddenCommentListNum2 = setting.hiddenCommentListNum2; ls.escKey = setting.escKey; ls.enterKey = setting.enterKey; ls.pageKey = setting.pageKey; ls.qualityEnable = setting.qualityEnable; if (setting.qualityEnable && setting.targetQuality !== ls.targetQuality) { changeTargetQuality(setting.targetQuality); } ls.targetQuality = setting.targetQuality; ls.ngWordEnable = setting.ngWordEnable; lsWord.ngWord = setting.ngWord; ls.ngIdEnable = setting.ngIdEnable; lsId.ngId = setting.ngId ? [...setting.ngId] : []; data.ngId = new Set(setting.ngId); if ( ls.ngIdMaxSize < setting.ngIdMaxSize && setting.ngId.length > setting._ngid[ls.ngIdMaxSize] ) { setting.ngId.splice( 0, setting.ngId.length - setting._ngid[ls.ngIdMaxSize] ); data.ngId = new Set(setting.ngId); } ls.ngIdMaxSize = setting.ngIdMaxSize; ls.ngConsole = setting.ngConsole; ls.ngIdEnable = setting.ngIdEnable; saveStorage(); }; /** * ローカルストレージに保存する */ const saveStorage = () => { localStorage.setItem(sid, JSON.stringify(ls)); localStorage.setItem(`${sid}-Word`, JSON.stringify(lsWord)); localStorage.setItem(`${sid}-Id`, JSON.stringify(lsId)); }; /** * 可能であれば動画の上側や左側に隙間がなくなるようにページをスクロールする */ const adjustScrollPosition = () => { log('adjustScrollPosition'); const player = document.querySelector(selector.videoMainPlayer); if (player) { player.scrollIntoView({ behavior: 'smooth', inline: 'start' }); } }; /** * このスクリプトを初めて使うときやローカルストレージを削除したとき初期値を登録する */ const setInitialValue = () => { /** * 変数aの型がsとは異なる場合trueを返す * @param {string} t 型 * @param {any} a 判別したい変数 * @returns {boolean} */ const notType = (t, a) => Object.prototype.toString.call(a).slice(8, -1) !== t ? true : false; /** * 設定用の変数が異なる型の場合は初期値を登録する * @param {string} s 変数名 * @param {string} t 型の先頭3文字 * @param {any} a 初期値 */ const setValue = (s, t, a) => { if (notType(t, setting[s])) { setting[s] = a; if (s === 'ngWord') lsWord[s] = ''; else if (s === 'ngId') lsId[s] = []; else setting[s] = a; } }; if (!lsWord.ngWord) lsWord.ngWord = ''; if (!lsId.ngId) lsId.ngId = []; setValue('reduceNavigation', 'Boolean', true); setValue('hiddenButtonText', 'Boolean', true); setValue('closeNotification', 'Boolean', false); setValue('videoResolution', 'Boolean', true); setValue('semiTransparent', 'Boolean', true); setValue('smallFontSize', 'Boolean', true); setValue('closeSidePanel', 'Boolean', true); setValue('overlapSidePanel', 'Boolean', true); setValue('sidePanelBackground', 'Boolean', true); setValue('sidePanelCloseButton', 'Boolean', true); setValue('showProgramDetail', 'Boolean', true); setValue('sidePanelSize', 'Boolean', false); setValue('sidePanelSizeNum', 'String', '320'); setValue('nextProgramInfo', 'Boolean', true); setValue('skipVideo', 'Boolean', false); setValue('headerPosition', 'Boolean', true); setValue('videoPadding', 'Boolean', true); setValue('dblclickScroll', 'Boolean', true); setValue('mouseoverNavigation', 'Boolean', true); setValue('newCommentOneByOne', 'Boolean', true); setValue('scrollNewComment', 'Boolean', true); setValue('stopCommentScroll', 'Boolean', true); setValue('highlightNewComment', 'Boolean', true); setValue('commentFontSize', 'Boolean', false); setValue('commentFontSizeNum', 'String', '13'); setValue('reduceCommentSpace', 'Boolean', true); setValue('hiddenCommentList', 'Boolean', true); setValue('hiddenCommentListNum', 'String', '50'); setValue('hiddenCommentListNum2', 'String', ''); setValue('escKey', 'Boolean', true); setValue('enterKey', 'Boolean', true); setValue('pageKey', 'Boolean', false); setValue('qualityEnable', 'Boolean', true); setValue('targetQuality', 'Number', 0); setValue('ngWordEnable', 'Boolean', true); setValue('ngWord', 'String', ''); setValue('ngIdMaxSize', 'Number', 0); setValue('ngId', 'Array', ''); setValue('ngConsole', 'Boolean', false); setValue('ngIdEnable', 'Boolean', true); saveStorage(); }; /** * 動画の解像度と表示領域サイズを調べて表示する */ const showVideoResolution = () => { clearTimeout(interval.resolution); interval.resolution = setTimeout(() => { const dpr = window.devicePixelRatio, footer = document.querySelector(selector.footer), vi = returnVideo(), vr = document.getElementById(`${sid}_VideoResolution`), ch = vi?.clientHeight, cw = vi?.clientWidth, vh = vi?.videoHeight, vw = vi?.videoWidth; if (vi && dpr && ch && cw && vh && vw) { if ( video.pixelRatio !== dpr || video.clientHeight !== ch || video.clientWidth !== cw || video.videoHeight !== vh || video.videoWidth !== vw ) { log('showVideoResolution'); let desc = `動画解像度: ${vw}×${vh}`, maxHeight = ''; if ( /^https:\/\/abema\.tv\/(?:now-on-air|live-event)\/.+$/.test( location.href ) ) { const vt = returnVideoTracks(); if (vt?.qualities?.length > 1) { maxHeight = vt.qualities[0].height <= 240 ? vt.qualities[vt.qualities.length - 1].height : vt.qualities[0].height; } if (maxHeight) desc += ` (Max: ${maxHeight}p)`; } desc += ` / 表示領域: ${cw}×${ch}`; if (dpr !== 1) desc += ` * ${dpr}`; if (vr) { vr.innerText = desc; } else { const div = document.createElement('div'); div.id = `${sid}_VideoResolution`; div.innerText = desc; if (footer) footer.appendChild(div); } video.pixelRatio = dpr; video.clientHeight = ch; video.clientWidth = cw; video.videoHeight = vh; video.videoWidth = vw; } } }, 100); }; /** * ページを開いて動画が表示されたら1度だけ実行 */ const startFirstObserve = () => { log('startFirstObserve'); document.addEventListener('keydown', checkKeyDown, true); document.addEventListener('click', checkClick); document.addEventListener('dblclick', checkDblclick); const main = document.querySelector(selector.main); if (main) { observerE.observe(main, { childList: true, subtree: true }); observerT.observe(main, { childList: true }); } else log('startFirstObserve: Not found element.', 'warn'); }; const observerC = new MutationObserver(checkNewComments), observerE = new MutationObserver(changeElements), observerR = new ResizeObserver(resizeVideo), observerT = new MutationObserver(changePageTitle), observerV = new MutationObserver(changeVideoSource); clearInterval(interval.init); interval.init = setInterval(() => { if (document.querySelector(selector.main)) { clearInterval(interval.init); init(); } }, 500); })();