// ==UserScript==
// @name Wider Bilibili
// @namespace https://greasyfork.org/users/1125570
// @version 0.4.8
// @author posthumz
// @description 哔哩哔哩宽屏体验
// @license MIT
// @icon https://www.bilibili.com/favicon.ico
// @supportURL https://github.com/posthumz/wider-bilibili/issues
// @match http*://*.bilibili.com/*
// @grant GM_addStyle
// @grant GM_addValueChangeListener
// @grant GM_deleteValue
// @grant GM_deleteValues
// @grant GM_getValue
// @grant GM_registerMenuCommand
// @grant GM_setValue
// @run-at document-start
// @compatible firefox 117+
// @compatible chrome 120+
// @compatible edge 120+
// @noframes
// @downloadURL none
// ==/UserScript==
(async function () {
'use strict';
const styles = {
video: `/* 播放器 */
:root {
--upper-nav: 0;
--title-height: 0px;
--reserve-height: calc(var(--upper-nav) * var(--navbar-height) + var(--title-height));
--player-height: calc(100vh - var(--reserve-height));
}
/* 播放器定位 */
#playerWrap.player-wrap,
#bilibili-player-wrap {
position: absolute;
left: 0;
right: 0;
top: 0;
height: auto;
/* 番剧页加载时播放器会有右填充 */
padding-right: 0;
}
#bilibili-player {
/* 播放器适应宽高 */
height: auto !important;
width: auto !important;
box-shadow: none !important;
.bpx-player-container {
box-shadow: none;
}
/* Bilibili Evolved 夜间模式样式的优先级很高,所以嵌套在#bilibili-player里面 */
.bpx-player-video-info {
color: hsla(0, 0%, 100%, .9) !important;
margin-right: 10px;
/* 某些页面莫名其妙加了width */
width: auto !important;
}
.bpx-player-video-btn-dm,
.bpx-player-dm-setting,
.bpx-player-dm-switch {
fill: hsla(0, 0%, 100%, .9) !important;
}
.bpx-player-dm-hint>a {
color: hsla(0, 0%, 100%, .6) !important;
fill: hsla(0, 0%, 100%, .6) !important;
}
}
/* 限制高度上限100vh - 预留高度 */
.bpx-player-container:not(:fullscreen) .bpx-player-video-wrap>video {
max-height: calc(100vh - var(--reserve-height));
}
/* 小窗时仍然保持播放器容器高度 */
.bpx-docker:has(>.bpx-player-container[data-screen="mini"]) {
height: var(--player-height);
}
/* 加载时 */
.bpx-player-container:not([data-screen="mini"]) .bpx-player-video-area:has(>.bpx-state-loading) video,
/* 换源时 */
.bpx-player-video-wrap>video:not([src]) {
/* 宽高比不详,强制占用全高 */
height: 100vh;
}
/* 这啥?加载时会导致屏幕超出 */
.bpx-player-cmd-dm-wrap {
position: absolute;
}
/* 加载动画强制显示 */
.bpx-player-loading-panel-blur {
display: flex !important;
}
/* 强制显示播放器控件 */
.bpx-player-top-left-title,
.bpx-player-top-left-music,
.bpx-player-top-mask {
display: block !important;
}
/* 不然会鬼畜 */
.bpx-player-top-mask {
transition-property: none !important;
}
/* 原弹幕发送区域不显示 */
.bpx-player-sending-area,
/* 原宽屏/网页全屏按钮不显示 */
.bpx-player-ctrl-wide,
.bpx-player-ctrl-web {
display: none;
}
/* 以防窗口过窄 */
#app {
width: 100vw !important;
max-width: 100% !important;
}
/* 自定义顶栏加载前 */
body>.custom-navbar {
z-index: 0 !important;
}
/* 自定义顶栏弹出菜单样式修复 */
.custom-navbar .popup[data-popper-placement="top"] {
inset: auto auto var(--navbar-height) 0px !important;
}
/* 使用 static 才能让播放器的 absolute 正确定位 */
/* 视频、番剧、收藏/稍后再看页 */
.video-container-v1,
.left-container,
.main-container,
.playlist-container--left {
position: static !important;
}
/* 视频页、番剧页、收藏/稍后再看页的下方容器 */
.video-container-v1,
.main-container,
.playlist-container {
padding: 0 var(--layout-padding) !important;
max-width: none !important;
min-width: auto !important;
}
.left-container,
.plp-l,
.playlist-container--left {
flex: 1;
width: auto;
}
.plp-r {
/* 番剧页加载时不会先使用sticky */
position: sticky !important;
padding-top: 0 !important;
}
/* 以防播放器挡住一些浮窗 */
.playlist-container--left,
.bilibili-player-wrap {
z-index: 1 !important;
}
/* 番剧页下方容器 */
.main-container {
width: auto !important;
box-sizing: border-box;
display: flex;
/* 番剧页弹窗 */
>:nth-last-child(2)[class^=dialogcoin_coin_dialog_mask] {
z-index: 100001;
}
/* 右下方浮动按钮位置 */
>:last-child[class^=navTools_floatNavExp] {
z-index: 2 !important;
}
}
/* 特殊页面 */
.special>.special-cover {
max-height: calc(100vh - var(--title-height));
}
.player-left-components {
padding-right: 30px !important;
}
.toolbar {
padding-top: 0;
}
/* 视频标题自动高度 */
#viewbox_report,
.video-info-container {
height: auto;
}
/* 视频标题换行显示 */
.video-title {
white-space: normal !important;
}
/* bgm浮窗 */
#bgm-entry {
z-index: 114514 !important;
left: 0 !important;
}
/* 笔记浮窗 */
.note-pc {
z-index: 114514 !important;
}
.fixed-sidenav-storage {
z-index: initial !important;
}
/* Bilibili Evolved侧栏 */
.be-settings .sidebar {
z-index: 114514 !important;
}`,
t: `#app {
/* 单个动态 */
>.bg+.content {
width: auto;
margin: 10px 0;
>.card {
margin: 0 var(--layout-padding)
}
>.sidebar-wrap {
right: 58px;
margin-right: var(--layout-padding);
}
}
/* 动态页 */
>[class^=bili-dyn-home] {
margin: 0 var(--layout-padding);
.left {
.bili-dyn-live-users {
margin-bottom: 10px;
position: initial !important;
transform: none !important;
}
}
.right {
width: initial;
.bili-dyn-banner {
display: none;
}
.bili-dyn-topic-box {
transform: none !important;
}
}
main {
flex: 1
}
.bili-dyn-sidebar {
right: var(--layout-padding);
transform: none;
}
}
}`,
space: `/* 新版空间页 */
#app .space-main,
.nav-bar__main,
.header .header-upinfo {
--side-padding: var(--layout-padding) !important;
}
/* 空间页 */
#app,
.header-upinfo,
.nav-bar__main,
.space-main,
.bili-dyn-list {
max-width: none !important;
}
#app {
margin: 0 var(--layout-padding);
}
#biliMainHeader {
height: initial !important;
}
div.wrapper,
.search-page {
width: auto !important;
margin: 0;
}
/* 主页 */
#page-index {
>div.col-1 {
/* 以防不支持round */
max-width: calc(100% - 400px);
width: round(down, calc(100% - 400px), 180px);
.section {
>.content {
width: auto;
}
/* 投稿、投币、点赞 */
&.video>.content,
&.coin>.content,
.channel-video {
margin-left: -10px;
overflow: auto !important;
scroll-snap-type: both mandatory;
>.small-item {
padding: 10px !important;
scroll-snap-align: start;
}
&::after {
content: none;
}
}
/* 收藏 */
&.fav>.content {
margin-top: -14px;
margin-left: -10px;
>.fav-item {
margin: 14px 10px;
}
}
/* 番剧 */
&.bangumi>.content>.large-item {
margin-right: 0;
}
}
.article-content {
width: calc(100% - 135px);
}
}
}
/* 动态 */
#page-dynamic>div.col-1 {
width: calc(100% - 360px);
}
/* 投稿, 搜索 */
#page-video {
width: 100% !important;
>.col-full {
display: flex;
>.main-content {
flex: 1;
.cube-list {
width: auto !important;
display: flex;
flex-wrap: wrap;
justify-content: center;
}
}
}
}
/* 合集 */
.channel-index {
width: auto !important;
.channel-list {
gap: 20px;
&::before,
&::after {
content: none;
}
.channel-item {
margin: 0 !important;
}
}
}
/* 收藏夹, 关注 */
#page-fav,
#page-follows {
.col-full {
display: flex !important;
.fav-main,
.follow-main {
flex: 1;
}
}
.fav-content>.fav-video-list {
margin: 10px;
>.small-item {
margin: 10px !important;
}
}
}
/* 追番 */
#page-bangumi,
#page-pgc {
.section>.content {
width: initial;
.pgc-space-follow-page {
padding-left: 0;
}
}
}`,
search: `/* 搜索页 */
.i_wrapper {
padding: 0 var(--layout-padding) !important;
}`,
read: `/* 阅读页 */
#app {
margin: 0 var(--layout-padding);
>.article-detail {
width: auto;
.article-up-info {
width: auto;
margin: 0 80px 20px;
}
.right-side-bar {
right: 0;
}
}
}`,
panel: `/* popover兼容性检测 */
@supports not (selector(:popover-open)) {
.wb-button-group::before {
content: '浏览器内核版本不完全适配脚本,请考虑升级';
color: red;
}
}
.wb-button-group::before {
/* CSS Nesting兼容性检测 */
content: '浏览器内核版本不完全适配脚本,请考虑升级';
color: red;
}
#wider-bilibili {
--wb-bg: var(--Wh0, #FFF);
--wb-fg: var(--Ga10, #18191C);
--wb-white: rgb(255, 255, 255);
--wb-blue: 0, 174, 236;
--wb-pink: 255, 102, 153;
--wb-red: 248, 90, 84;
position: fixed;
top: 0;
bottom: 0;
height: fit-content;
max-height: 80vh;
left: 0;
right: 0;
width: fit-content;
max-width: 80vw;
z-index: 114514;
padding: 10px;
border-radius: 10px;
margin: auto;
box-sizing: border-box;
overflow: auto;
flex-direction: column;
gap: 10px;
border: 2px solid rgba(var(--wb-blue), 0.8);
background-color: var(--wb-bg);
color: var(--wb-fg);
font-size: 20px;
opacity: 0.9;
&:popover-open {
display: flex;
}
&:hover {
opacity: 1
}
>header {
position: sticky;
z-index: 2;
top: -10px;
display: flex;
justify-content: space-between;
margin: -10px;
background-color: var(--wb-bg);
font-weight: bold;
&::before {
content: "Wider Bilibili 选项";
align-self: center;
margin-left: 10px;
margin-right: auto;
}
}
.wb-button-group {
display: flex;
margin-bottom: 10px;
>* {
height: 100%;
}
}
div.wb-button-group::before {
display: none;
}
a,
#wb-close {
padding: 4px;
color: var(--wb-fg);
display: flex;
font-size: 16px;
text-wrap: nowrap;
transition: opacity .1s;
cursor: pointer;
&:hover {
opacity: 0.75;
}
&:active {
opacity: 0.5;
}
>svg {
width: 20px;
height: 20px;
fill: currentColor;
fill-rule: evenodd;
clip-rule: evenodd;
}
}
#wb-close {
width: fit-content;
height: fit-content;
opacity: 1;
border-radius: 0;
outline: none;
box-shadow: none;
&:hover {
background-color: rgb(var(--wb-red));
}
&:active {
background-color: rgba(var(--wb-red), 0.75);
}
}
>fieldset {
border: none;
border-radius: 10px;
padding: 10px;
margin: 0;
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px 15px;
background-color: rgba(127, 127, 127, 0.1);
&::before {
content: attr(name);
border-radius: 4px 4px 0 0;
border-bottom: 2px solid rgba(127, 127, 127, 0.1);
grid-column: 1 / -1;
}
}
[data-option]::after {
content: attr(data-option);
text-wrap: nowrap;
}
[data-hint]:hover::before {
position: absolute;
bottom: 110%;
left: 0;
right: 0;
margin: 0 auto;
width: fit-content;
padding: 3px 5px;
border-radius: 5px;
content: attr(data-hint);
font-size: 12px;
background-color: rgb(var(--wb-blue));
color: var(--wb-white);
white-space: pre-line;
}
label {
display: inline-flex;
gap: 10px;
place-items: center;
position: relative;
}
input,
button {
box-sizing: content-box;
margin: 0;
padding: 4px;
height: 20px;
font-size: 16px;
line-height: 1;
transition: .2s;
&:hover {
box-shadow: 0 0 8px rgb(var(--wb-blue));
}
&:active {
opacity: 0.5;
}
}
input {
&[type=checkbox] {
box-sizing: content-box;
border-radius: 20px;
min-width: 40px;
background-color: #ccc;
appearance: none;
cursor: pointer;
&::before {
content: "";
position: relative;
display: block;
transition: 0.3s;
height: 100%;
aspect-ratio: 1/1;
border-radius: 50%;
background-color: #FFF;
}
&:checked {
background-color: rgb(var(--wb-blue));
}
&:checked::before {
transform: translateX(20px);
}
}
&[type=number] {
width: 40px;
border: none;
border-radius: 5px;
outline: 2px solid rgb(var(--wb-blue));
background: none;
color: var(--wb-fg);
appearance: textfield;
&::-webkit-inner-spin-button {
appearance: none;
}
}
}
button {
border: none;
background: none;
border-radius: 5px;
outline: 1px solid #C9CCD0;
&:hover {
outline: 2px solid rgb(var(--wb-blue));
}
}
}`,
opus: `div.opus-detail {
width: initial;
margin: 0 var(--layout-padding);
}
.right-sidebar-wrap {
margin-left: 0;
right: 0;
}`,
message: `#message-navbar {
display: none;
}
.container {
max-width: none !important;
width: auto !important;
}
.space-right-top {
padding-top: 0 !important;
}`,
home: `/* 首页 */
div#i_cecream {
max-width: none;
}
.feed-roll-btn {
left: calc(100% - var(--layout-padding)) !important;
.roll-btn {
aspect-ratio: 1/1;
>span {
display: none
}
>svg {
margin-bottom: 0 !important
}
}
}
.feed-card,
.floor-single-card,
.bili-video-card {
margin-top: 0px !important;
}
.palette-button-wrap {
left: initial !important;
right: 30px;
}`,
common: `/* This overrides :root style */
html {
--layout-padding: 30px;
--navbar-height: 64px;
}
/* 导航栏 */
#biliMainHeader {
height: auto !important;
min-height: auto !important;
margin-top: var(--player-height);
margin-bottom: 0;
position: initial;
visibility: initial !important;
>.bili-header {
min-width: auto !important;
max-width: none !important;
min-height: auto !important;
>.bili-header__bar {
position: relative !important;
height: var(--navbar-height);
max-width: none !important;
}
}
/* BiliBili Evolved自定义顶栏加载前,强制显示原生顶栏 */
&:not(:has(>.custom-navbar)) .bili-header__bar {
display: flex !important;
}
/* 自定义顶栏加载后 */
>.custom-navbar {
position: relative;
z-index: 3 !important;
}
}
/* 搜索栏 */
.center-search-container {
min-width: 0;
}`,
upperNavigation: `/* 导航栏上置 (默认下置) */
:root {
--upper-nav: 1;
}
#biliMainHeader {
margin-top: 0;
margin-bottom: var(--player-height);
/* 播放器的 z-index 是100000 */
z-index: 114514;
}
#playerWrap.player-wrap,
#bilibili-player-wrap {
top: var(--navbar-height);
}`,
stickyHeader: `#biliMainHeader {
position: sticky;
top: 0;
/* 其他元素 z-index 基本是<100 */
z-index: 100;
}`,
stickyAside: `#app .left {
height: fit-content;
position: sticky;
top: 72px;
}`,
reserveTitleBar: `:root {
--title-height: calc(100px + (1 - var(--upper-nav)) * var(--navbar-height));
}`,
pauseShowControls: `/* 暂停显示控件 */
.bpx-player-container.bpx-state-paused {
.bpx-player-top-wrap,
.bpx-player-control-top,
.bpx-player-control-bottom,
.bpx-player-control-mask {
opacity: 1 !important;
visibility: visible !important;
}
div.bpx-player-shadow-progress-area {
visibility: hidden !important;
}
.bpx-player-pbp {
bottom: 100% !important;
margin-bottom: 5px;
opacity: 1 !important;
left: 0;
right: 0;
width: auto !important;
.bpx-player-pbp-pin {
opacity: 1 !important;
}
}
}`,
mini: `/* 小窗 */
.bpx-player-container {
--mini-width: 320px;
/* 最小宽度,以防不可见 */
min-width: 180px;
&[data-screen="mini"] {
max-width: var(--mini-width) !important;
width: auto !important;
height: auto !important;
/* 以防竖屏视频超出:留出导航栏高度+16px */
.bpx-player-video-wrap>video {
max-height: calc(100vh - 16px - var(--navbar-height));
}
}
}
.bpx-player-mini-resizer {
position: absolute;
left: 0;
width: 10px;
height: 100%;
cursor: ew-resize;
}`,
hideControls: `.bpx-player-control-mask,
.bpx-player-control-top,
.bpx-player-control-bottom,
.bpx-player-pbp {
opacity: 0 !important;
}
.bpx-player-top-wrap:hover {
opacity: 1 !important;
}
.bpx-player-control-wrap:not(:hover) {
.bpx-player-shadow-progress-area {
opacity: 1 !important;
visibility: visible !important;
}
}
.bpx-player-control-wrap:hover {
.bpx-player-control-mask,
.bpx-player-control-top,
.bpx-player-control-bottom,
.bpx-player-pbp {
opacity: 1 !important;
}
}`,
fixHeight: `.bpx-player-container:not([data-screen="mini"]) .bpx-player-video-wrap>video {
height: 100vh;
}`,
compactControls: `/* 播放器控件 */
.bpx-player-control-bottom {
padding: 0 20px !important;
}
.bpx-player-control-bottom>* {
flex: initial !important;
}
/* 控件区域 */
.bpx-player-control-bottom-left,
.bpx-player-control-bottom-right {
min-width: auto !important;
gap: 8px;
}
/* Bilibili Evolved 自定义控件区域 */
.bpx-player-control-bottom-left>.be-video-control-bar-extend {
gap: 8px;
}
/* 减少中间填充空间 */
.bpx-player-control-bottom-center {
flex: 1 !important;
padding: 0 20px !important;
}
/* 防止选集/倍速按钮错位 */
.bpx-player-control-bottom-right>.bpx-player-ctrl-btn:hover {
padding: 0;
}
/* 所有按钮控件 */
.bpx-player-ctrl-btn {
margin: 0 !important;
width: fit-content !important;
}
.bpx-player-ctrl-time {
width: 130px !important;
}
/* 时间控件 */
.bpx-player-ctrl-time-seek {
width: 100% !important;
padding: 0 !important;
left: 0 !important;
}
.bpx-player-ctrl-time-label {
text-align: center !important;
text-indent: 0 !important;
}
/* 弹幕发送框区域 */
.bpx-player-sending-bar {
background-color: transparent !important;
max-width: 90% !important;
}
.bpx-player-video-inputbar {
max-width: none !important;
}
.bpx-player-video-inputbar-wrap {
width: auto;
}`
};
const waitFor = (loaded, desc = "页面加载", retry = 100, interval = 100) => new Promise((resolve, reject) => {
const intervalID = setInterval((res = loaded()) => {
if (res) {
clearInterval(intervalID);
console.info(`${desc}已加载`);
return resolve(res);
}
if (--retry === 0) {
clearInterval(intervalID);
return reject(new Error(`${desc}加载超时`));
}
if (retry % 10 === 0) {
console.debug(`${desc}等待加载`);
}
}, interval);
});
const observeFor = (className, parent) => new Promise((resolve) => {
const elem = parent.getElementsByClassName(className)[0];
if (elem) {
return resolve(elem);
}
new MutationObserver((mutations, observer) => {
for (const mutation of mutations) {
for (const node of mutation.addedNodes) {
if (node instanceof HTMLElement && node.classList.contains(className)) {
observer.disconnect();
return resolve(node);
}
}
}
}).observe(parent, { childList: true });
});
const waitReady = () => new Promise((resolve) => {
document.readyState === "loading" ? window.addEventListener("DOMContentLoaded", () => resolve(), { once: true }) : resolve();
});
const html = `