Merge remote-tracking branch 'upstream/preview' into dark-style

This commit is contained in:
JLoeve 2021-02-01 14:34:19 +08:00
commit 2eade3a29b
52 changed files with 438 additions and 270 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -105,7 +105,7 @@
}
```
可以通过 <kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>Alt</kbd> + <kbd>?</kbd> 快捷键清除当前视频的倍数记忆状态.
可以通过 <kbd>Shift</kbd> + <kbd>:</kbd> 快捷键清除当前视频的倍数记忆状态.
</details>
@ -152,7 +152,7 @@
<details>
<summary><strong>启用逐帧调整</strong></summary>
在播放器的时间右边增加两个按钮, 用于**较**精细调整视频时间. 支持键盘快捷键`Shift + 左/右方向键`. (旧版播放器只能用键盘快捷键, 不会显示按钮)
在播放器的时间右边增加两个按钮, 用于**较**精细调整视频时间.
注: `视频的实际播放帧率`跟`视频本身的帧率`和`显示器的刷新率`有关, 很难计算一个精准的数值, 部分视频仍然会有暂停不到那种一闪而过的图的情况.
@ -166,9 +166,9 @@
<details>
<summary><strong>启用视频截图</strong></summary>
在播放器的时间右边增加截图按钮, 点击可以截取视频画面, 不会包含暂停标志和弹幕. 截取的图片将在网页右侧显示(非全屏或网页全屏模式), 可以单独保存或丢弃, 也可以截取一定数量后一次性保存. 支持键盘快捷键`Ctrl + Alt + C`. (旧版播放器只能用键盘快捷键, 不会显示按钮)
在播放器的时间右边增加截图按钮, 点击可以截取视频画面, 不会包含暂停标志和弹幕. 截取的图片将在网页右侧显示(非全屏或网页全屏模式), 可以单独保存或丢弃, 也可以截取一定数量后一次性保存.
如果弹幕渲染类型选择了Canvas, 则可以再按住`Shift`键来截取带弹幕的截图. 也就是鼠标操作为"按住`Shift`点击截图按钮", 键盘操作为"`Ctrl + Shift + Alt + C`".
如果弹幕渲染类型选择了Canvas, 则可以再按住`Shift`键来截取带弹幕的截图.
<img height="100" alt="时间右边的按钮" src="https://cdn.jsdelivr.net/gh/the1812/Bilibili-Evolved@preview/images/compressed/control-enhance-buttons.jpg">

View File

@ -43,7 +43,7 @@
"custom-control-background.min.js": "A2699D3CBCB0857C3D0E5F6744964DF705C35C740E59448AFEE62508B7EE8848",
"custom-navbar.min.css": "ACE2D981B7132A2FD1BDA8AC04A58E48359E1A5C407522C68349D550EF4D44C0",
"custom-navbar.min.html": "B063DEEFF36B29C115483DD0061EF6C62E83085F3D1D9320DE94010A039D8738",
"custom-navbar.min.js": "A0C1A4E01C54DB8FE1AEC2EB090824119DE24CCC52AE131D928CB7758F2D354D",
"custom-navbar.min.js": "BB169AD738E8EA5C566399A4897C393671E0687F2194615238DD33E693996301",
"custom-navbar-activities.min.js": "4AEB94FA06E543E3D0BA9E3337798DA3BC0AD194974A981710CE7CDAC9F6DD98",
"custom-navbar-blank.min.js": "E8F08DA7CD2C67F9D064B959A64CFC6658103DBCBE2B9B58D978A1742E02D829",
"custom-navbar-category.min.js": "A024B4418A6521EDDD9590F2F0FE5EB7FB0556A23468E8CBFF9609B935805348",
@ -54,7 +54,7 @@
"custom-navbar-iframe.min.js": "521D95AFDD0F496BD2B9AFA229D5D0E5DD57C518BAE8337D9753887EC1926C87",
"custom-navbar-logo.min.js": "CE70A2E60EBDB145FC07D97A1DDABD1077DAA66CE83574B97788216D788BA4C5",
"custom-navbar-messages.min.js": "700BD1AD47250B5F4726F547BEA02FAD1D7EF82A8E9BEDB1D3619F2E495E6B70",
"custom-navbar-search-box.min.js": "D28DBE5F895FDD7CA54CE39E701D81CEC19C0593C33243A1B0C826EAE279AABB",
"custom-navbar-search-box.min.js": "5DA8605A9755A2C75DD9B8D3EDA371A33C3922803A25FE0AFF44D6A09D1B3576",
"custom-navbar-settings.min.js": "AFC11C00AF125225609F5A1C81D40324B07CA930B689C14A52E275BBBE3FDA55",
"custom-navbar-simple-link.min.js": "51E2FDD06832E585E86884890D1DC6321A802D43332D7E7B5758A20BC1AB1E03",
"custom-navbar-subscriptions.min.js": "D99A25DE4ABB104FA95166F51EB4FEE9DFDC4AE162CF189ECBCF99CA2E5E0E6C",
@ -66,8 +66,8 @@
"danmaku-segment.min.js": "43CC3236AFABA89BD0AE24D640809BE333772824E3994541AFC3CDDD9F881613",
"danmaku-send-bar.min.css": "186013DAAC3E9E8C0B408CFA3D5C3F5BF702F25D435A386B384ECF3B5FBBBE18",
"danmaku-send-bar.min.js": "670A37F301982F628B6663BB850EF9D9BF87983845CB080E603FC34582CFAD56",
"dark.min.css": "B36488B7B6D7D06E6C7846DBC18C5DCA07C21B66F70DC800E503C7A34425E845",
"dark.user.css": "DC8C9A2B8E65C9F5B92207AFC096240EB4A8BD9A76341FF8D03F685C73213773",
"dark.min.css": "326A85B234DFCACD51633B094DFFFCB4279771E4C337688FDC5D56818973DC6E",
"dark.user.css": "A1C5E947DA9C0867861CB22D832ED6717B30353A2447A7AB30C51A4E086202DD",
"dark-color-scheme.min.js": "C83A38770DCEC5842E49617B07FFF97BCD6691982AFE40788ADC6A449C6474CB",
"dark-important.min.css": "5902E037E040A6C364391FE27885A118A8FD8B30A4B5E8C737C5FE5CFFB67F42",
"dark-navbar.min.css": "7FAD547E326B768904B3739C978DF86FFDDD5E5064FB8F490710D197CCC3018B",
@ -125,7 +125,7 @@
"gbk.min.js": "0EDFCB94F519365A8CCA9BF3AECE57DC6DB273EF3F41545095B8F03625782F0D",
"get-number.min.js": "E644267B8A2DCEF2577BB9270F5C738BCE89C1C128071EBD3645903A930E08E0",
"gui-settings.min.css": "F5D09B1F2D6FB5C9923A87EA087BB50450F4B912360F6EF578D90F35D8D86D36",
"gui-settings.min.html": "E9F1477C563605A0158C9025A22E1165D16AF8D4A5DD179FF9501FEEF1A169BB",
"gui-settings.min.html": "67FEEBC728AB02A85CCF55529A3C2BD8289D3E2F05D8CCC798812F11905AFC8A",
"gui-settings.min.js": "202F382794C122D384F85555DE053D87066AEB167A47C71FE937CA270C9B204C",
"haruna-scale.min.js": "7342274238E3012AD4FBCA753DA64DB8DA54F9E6860DF9689F20852DAEFD7CF8",
"hide-bangumi-reviews.min.js": "0E8D100A27D523008041BAF95B7809855B505F4C02D4EE32CC62917528E46DBE",
@ -157,7 +157,7 @@
"image-viewer.min.html": "5F1EEE9FBFD9D2EB2E2F73A57CB3DB2448A60E3D3480A297A7FC88038F60DF21",
"index.min.html": "94B83D9EBB9005C1286A7E0759A7683932F8DADA20D07E5E9D8FF867B04D4B95",
"key-bindings.min.js": "B896FECF782C96090607EB47B865F19ADE7D269B029D4E61D02E8671618752FD",
"keymap.min.js": "39543583160F098ED78BD6A6B91253B0CFED88989822A050B6439B417C398FD1",
"keymap.min.js": "50A6190D87FB848FB59428F7C35E03968ADFD31C9524C4F63B2F80CDC4A640A4",
"live-pip.min.js": "48FF137D4CF1DEF16395184360981915FF716978455AAD6462C20EDE8A677819",
"live-socket.min.js": "8AB690D1979FB64E7BA6126417700C0DD96C0E5C43712543590753B8344996C8",
"magic-grid.min.js": "1CDDA7436511423934EBA3E9CCE3CD1BC913C9B8DAFE09C06939935B4F02DD3D",
@ -165,6 +165,7 @@
"medal-helper.min.css": "BFB833B7389BE97FE818968789BB13F6576B2023D75F20521E0F55567187EF69",
"medal-helper.min.html": "5D7057259368BE97DED3375DD904695B245AA2BF338C5E18CB3CF61DD913617C",
"medal-helper.min.js": "08D05F7BCF47F676A8A4AABF26E5CCF089511D0F6E846000AAE3C14E6B21C094",
"menu-repeat-video.min.js": "A8A8CE04FC9B8B4118A2358488DE1BF5A9F576A24A657861426696C565EC7197",
"minimal-home.vue.min.js": "DFA2E873F7948766950084DB5640A1299F63A6E45BAC943A3D1B9CB0E4A2E19F",
"minimal-home-search.vue.min.js": "83ACE262CBA7CB68260746B25DD3009D12BA351220F5732BD74EF0B87CBB5D3A",
"mini-player-touch-move.min.css": "285AE33BA492FFE4CE0AE4E079C5C0C6661D70C13010FEBE05A1DA133AF7A7B7",
@ -178,22 +179,24 @@
"old.min.css": "4C8C918BEBE59E9EE19D5E383234767EE2A2F1DB72E86F0C9F9CE01F26DD193C",
"old-tweets.min.js": "AAF995BE7F62E1B57C360246F43BCA8B9ADC850DD828190114F6DCADC9A54C06",
"online-info-row.vue.min.js": "48D42BADAD3F52BAFE20E2810567ED404A82DA78C8B7ECEB97C0D3E76CC61138",
"outer-watchlater.min.css": "B40E94BADA1A9BC96422777B33150780330763098B539921DA05F9D3BA487424",
"outer-watchlater.min.js": "D29860BA24F94DDAC33D057741BC140BA8DDA3364145FBC4EC7AF95F8F818C3E",
"outer-watchlater.min.css": "D33D11E6267DA0F5CD51F8F1CDBD94AC67E4B6F7DABEA531D6EA6B36504A1C26",
"outer-watchlater.min.js": "1661FC4157EEB842FF5266FB78036209C8C84662305EAE9DFD968AEC3F30892B",
"override-navbar.min.css": "E5AA612841281169CA367A238FB934F807900A385CE2298B7352FF3CA4623757",
"override-navbar.min.js": "0FF50371A02F47EE384515544EB87D34CEE2DB449B9B864B5627A9645F921DAB",
"pako-inflate.min.js": "1B89766B342D4A561B0CF7D5A68B2900B6D1772E507EEB1AF56FA7CABE40BCEC",
"player-context-menu.min.js": "D84439ABC219CBCDF4C431DAA73E799265485F3C5C07AE57F9A251C7CF967E4A",
"player-focus.min.js": "9A290D6749E68A4A88563983413A296BFA9B5C18B978F9CF80799196AEC0F9EE",
"player-on-top.min.js": "0D9AA8561E97A6FFCEABDA206B6A57EB87C1B3EF5D0D53EDABA3ED5EFB0DF687",
"player-ready.min.js": "1E2CF36AD9B3D506BD89F7E0BBFB5B4DCE2FDAFB8E22FDC988FA5015B739B413",
"player-shadow.min.js": "206E787488805F285A6A2A5A542569F52A25BD673E44EE881319932385CC7D61",
"player-shadow.min.js": "2B68ADB744DA70E62A7C4E1B659EAA1994F8C167C260051C1115258C0AB67775",
"quality-errors.min.js": "A691A407FC4F80C6B2D243CBFADC636FCFFC4DC35B9E305AEF4184AD3ECFC56B",
"quick-favorite.min.css": "F14B5ED3F95E944E55B06FEAFF0AC04306146FE9F9223B2C49CE3D507849BE6B",
"quick-favorite.min.js": "57263C99EEB16416D25ED6CE5944D818AB7F4136F50082FEFE39FB1863903F9B",
"quick-favorite.min.css": "9D97291211AD43AA618A6755F37BB77A1500F6585D3B25E82FD2B6BBC9010540",
"quick-favorite.min.js": "4B4004FF561B057A755528D11787AD16D01E78CF77CC74A670D45227785CD6FA",
"rank-list.vue.min.js": "23879CFE7674EA072D579BDE665883FC18A536B06094D1C27DC9C8ECD881BF03",
"record-live-danmaku.min.js": "CF1C55683CB20688FAA39B9150144F5C907D8150BF42C1AD2729FB44AB06A604",
"record-live-danmaku.vue.min.js": "CFBB560D1C9C1D3510A4217E141FEDD4A7F227F3825859BB7F729544A1B86DAC",
"remember-video-speed.min.js": "207AB95275A06977CE46AE37C5892FB7823A696C67DC40B4B13EAE82DED330C6",
"remove-guide-popup.min.js": "42D2C3F2E71EF511A82CD846591F046B37EF5F70FF66BAEB9A509CB8344297A8",
"remove-promotions.min.css": "5B2E7D7F4030150D30635537F2BA547DAC702574BCFBA80F514D53E1FEC88A30",
"remove-promotions.min.js": "C45270389F28BEFD963797665DCBC1D4B5D39401932B34834828549A9B33F2E1",
"remove-top-mask.min.js": "D0A554FC1C04B082D01585CE944E322F4F4A0CA25F1C856ADD59883B72DDD52C",
@ -212,9 +215,9 @@
"settings-tooltip.en-US.min.js": "8B559944CF30766F1A86729A0620E3D13D3857FC67273BB1C874A4E0F24B234A",
"settings-tooltip.ja-JP.min.js": "B3F99881B1F4DB293DB80BFFA92793E1834EF3096C536E107C52B116037228B9",
"settings-tooltip.loader.min.js": "477DE4B69F477D67BCBC72CE33C91986EB2A5620D6F2D1D8367BBC388C5E3E5C",
"settings-tooltip.min.css": "3E06D516A95E676B9B0832B16B2522017741BC551D6F541EF3F5A3FBA7761AD3",
"settings-tooltip.min.css": "031B4E11744977C54496A3DB0FAB794401DC1FED2E53D9F729DA4218CD98E8D5",
"settings-tooltip.min.js": "C7F9A79EB623BF58CB81DE4A9E74FC6C2EDB2613403EFE6D09B36FB460159A13",
"settings-tooltip.zh-CN.min.js": "23AD522F4FC083B49FF9864B610C5F63B7EF39D9635151E3AF86E5A18981A7F6",
"settings-tooltip.zh-CN.min.js": "A1C425F8C62F6A9986CA4BFFE24B8CAF0547B333EB7265D40166312FB26E6585",
"show-cover-before-play.min.css": "E4050D66F8014029270CB5A4DEFDE2F847B095BA9ED4274BFED3E131B737B06D",
"show-cover-before-play.min.js": "5614929DD8CEB3B11D184F037235D91B741AD167B08A4F5B7341C6A50A0838FF",
"show-dead-video-title.min.js": "5B968442E71F947F70C57C9FDA083935200CBA9B1038131F1587E70A2B723798",
@ -244,12 +247,12 @@
"touch-move.min.js": "A962710FE0A970E098A1CB1B5C1F63DBA824386F0CE444621555E62CC4542E1A",
"touch-navbar.min.js": "A97A7E536A45DE1DF3F7CC174CF95D855FA6034FB9F7D98B818A7363BADDFE48",
"touch-player.min.css": "F75D1C72B1EFBE1D3DAB5E93A413796BCBE3FD5131E62C57356CC00E17DB28A5",
"touch-player.min.js": "168371E67A6FADDE44DD52A3279E1AAA75BA8CFD86F036CDE02D50D10572FC41",
"touch-player.min.js": "FDFCB93C336C5FA9C8EE133B8F7CC697DBAD5803BBBDC3D7651802ED4E0A4FC4",
"trending-videos.min.js": "F69B8025B8A78354425534096765E25ED533D5F3809639808EF196E56A4767CD",
"trending-videos.vue.min.js": "4BCA996946D6D042183DACD110917463B97DB16A28A61B05B4E6E9473766FCA8",
"tweets.min.css": "9015C1F165C91B9205ABBE4E8A3B5FA83DB1EB4B5CF2442846AF61E44FF178A8",
"unfold-feeds.min.js": "586FFB95BF24CA22E68ACE4CC4ABF25F5A2CE84C70D657D0CD62E666C4589B5B",
"url-params-clean.min.js": "843D56915EE37352925D5F7F73A8521EB2744240A6441022C783A49EC8F1855C",
"url-params-clean.min.js": "3A2B2A671609AF278B5A783702BF53C5F6B9554F5C41CFF91F6776C3B83CE53B",
"v-checkbox.vue.min.js": "D630823220562A011DDF7164A8D12E775548C6B81907EB6525B3211B822590B1",
"v-dropdown.vue.min.js": "3D1D4DC0D8DD06C58C88A530A04DC1D5BEE6690119709B809CD3FA24C8C51C83",
"video-card.vue.min.js": "D24618E924A9D039E24B518361B385AB3155188F8B6EEB8CFBB7F9C332422512",

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

2
min/dark.min.css vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

2
min/keymap.min.js vendored

File diff suppressed because one or more lines are too long

1
min/menu-repeat-video.min.js vendored Normal file
View File

@ -0,0 +1 @@
(()=>(e,n)=>{(async()=>{if(!await videoCondition()){return}const{addMenuItem:e}=await n.importAsync("player-context-menu");const t=document.createElement("div");t.addEventListener("click",(()=>console.log("click!")));t.innerHTML="test";e(t)})()})();

View File

@ -1 +1 @@
.ops .watchlater{margin-right:28px!important;position:relative}.ops .watchlater .tip{position:absolute;top:calc(100% + 8px);left:50%;transform:translateX(-50%);z-index:1000;background:#000d;padding:4px 8px;border-radius:4px;color:#eee;transition:.2s ease-out;opacity:0;pointer-events:none}.watchlater .tip.show{opacity:1;pointer-events:initial}.ops .watchlater>i{display:inline-block;font-style:normal;vertical-align:baseline;text-align:center;text-transform:none;line-height:1;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased}.more-ops-list>ul>li:nth-child(2){display:none!important}
.ops .watchlater{margin-right:28px!important;position:relative;width:auto!important}@media screen and (max-width:1320px),(max-height:750px){.ops .watchlater{margin-right:max(calc(11 * 1vw - 117.2px),6px)!important}.ops .watchlater .text{display:none}}.ops .watchlater .tip{position:absolute;top:calc(100% + 8px);left:50%;transform:translateX(-50%);z-index:1000;background:#000d;padding:4px 8px;border-radius:4px;color:#eee;transition:.2s ease-out;opacity:0;pointer-events:none}.ops .watchlater .tip.show{opacity:1;pointer-events:initial}.ops .watchlater>i{display:inline-block;font-style:normal;vertical-align:baseline;text-align:center;text-transform:none;line-height:1;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased}.more-ops-list>ul>li:nth-child(2){display:none!important}

View File

@ -1 +1 @@
(()=>(t,a)=>{(async()=>{if(!document.URL.startsWith("https://www.bilibili.com/video/")||!getUID()){return}const{playerReady:t}=await a.importAsync("player-ready");await t();const e=dq(".video-toolbar .ops .collect");if(!e){return}a.applyStyle("outerWatchlaterStyle");e.insertAdjacentHTML("afterend",`\n<span title='稍后再看' class='watchlater' :class="{on: isInWatchlater}" @click="toggle()">\n<i class='mdi mdi-timetable'></i>\n 稍后再看\n<div class='tip' :class="{show: tipShowing}">{{tipText}}</div>\n</span>\n`.trim());const i=dq(".ops .watchlater");if(!i){return}const s=new Vue({el:i,store:store,data:{aid:unsafeWindow.aid,tipText:"",tipShowing:false,tipHandle:0},computed:{...Vuex.mapState(["watchlaterList"]),isInWatchlater(){return this.watchlaterList.includes(parseInt(this.aid))}},methods:{...Vuex.mapActions(["toggleWatchlater"]),showTip(t){this.tipText=t;this.tipShowing=true;if(this.tipHandle){clearTimeout(this.tipHandle)}this.tipHandle=setTimeout((()=>{this.tipShowing=false}),2e3)},async toggle(){await this.toggleWatchlater(this.aid);this.showTip(this.isInWatchlater?"已添加至稍后再看":"已从稍后再看移除")}}});Observer.videoChange((()=>{s.aid=unsafeWindow.aid}))})();return{unload:()=>{dqa(".ops .watchlater").forEach((t=>t.style.display="none"))},reload:()=>{dqa(".ops .watchlater").forEach((t=>t.style.display="inline-block"))}}})();
(()=>(t,a)=>{(async()=>{if(!document.URL.startsWith("https://www.bilibili.com/video/")||!getUID()){return}const{playerReady:t}=await a.importAsync("player-ready");await t();const e=dq(".video-toolbar .ops .collect");if(!e){return}a.applyStyle("outerWatchlaterStyle");e.insertAdjacentHTML("afterend",`\n<span title='稍后再看' class='watchlater' :class="{ on: isInWatchlater }" @click="toggle()">\n<i class='mdi mdi-timetable'></i>\n<span class="text">稍后再看</span>\n<div class='tip' :class="{ show: tipShowing }">{{ tipText }}</div>\n</span>\n`.trim());const i=dq(".ops .watchlater");if(!i){return}const s=new Vue({el:i,store:store,data:{aid:unsafeWindow.aid,tipText:"",tipShowing:false,tipHandle:0},computed:{...Vuex.mapState(["watchlaterList"]),isInWatchlater(){return this.watchlaterList.includes(parseInt(this.aid))}},methods:{...Vuex.mapActions(["toggleWatchlater"]),showTip(t){this.tipText=t;this.tipShowing=true;if(this.tipHandle){clearTimeout(this.tipHandle)}this.tipHandle=setTimeout((()=>{this.tipShowing=false}),2e3)},async toggle(){await this.toggleWatchlater(this.aid);this.showTip(this.isInWatchlater?"已添加至稍后再看":"已从稍后再看移除")}}});Observer.videoChange((()=>{s.aid=unsafeWindow.aid}))})();return{unload:()=>{dqa(".ops .watchlater").forEach((t=>t.style.display="none"))},reload:()=>{dqa(".ops .watchlater").forEach((t=>t.style.display="inline-block"))}}})();

1
min/player-context-menu.min.js vendored Normal file
View File

@ -0,0 +1 @@
(()=>(e,t)=>{const n=async e=>{const{open:t,close:n}=e;const i=await videoCondition();if(!i){return}const s=await SpinQuery.select(".bilibili-player");if(!s){return}const o=Observer.childList(s,(()=>{const e=dq(".bilibili-player-context-menu-origin");if(e){o.stop();const i=dq(e,"ul");const s={containerElement:e,listElement:i,get itemElements(){return dqa(e,".context-menu-function")},get isOpen(){return e.classList.contains("active")}};Observer.childList(i,(()=>{if(s.isOpen){t===null||t===void 0?void 0:t(s)}else{n===null||n===void 0?void 0:n(s)}}))}}))};const i=async(e,t)=>n({open:t=>{if(t.listElement.contains(e)){return}const n=document.createElement("li");n.classList.add("context-line","context-menu-function");n.setAttribute("data-append","1");const i=document.createElement("a");i.classList.add("context-menu-a","js-action");i.href="javascript:void(0);";i.appendChild(e);n.addEventListener("mouseover",(()=>n.classList.add("hover")));n.addEventListener("mouseout",(()=>n.classList.remove("hover")));n.appendChild(i);t.listElement.appendChild(n)},close:e=>t===null||t===void 0?void 0:t(e)});return{export:{forEachContextMenu:n,addMenuItem:i}}})();

View File

@ -1 +1 @@
(()=>(e,l)=>{const r=`<style id="player-shadow-style">\n #bilibiliPlayer,\n .bilibili-player.mini-player::before\n {\n box-shadow: 0px 2px 8px 0px var(--theme-color-30) !important;\n }\n body.dark #bilibiliPlayer,\n body.dark .bilibili-player.mini-player::before\n {\n box-shadow: 0px 2px 8px 0px var(--theme-color-20) !important;\n }\n</style>`;l.applyStyleFromText(r);return{reload:()=>l.applyStyleFromText(r),unload:()=>document.getElementById("player-shadow-style").remove()}})();
(()=>(i,l)=>{const r=`\n#bilibiliPlayer,\n#bilibili-player.mini-player:before\n{\n box-shadow: 0px 2px 8px 0px var(--theme-color-30) !important;\n}\nbody.dark #bilibiliPlayer,\nbody.dark #bilibili-player.mini-player::before\n{\n box-shadow: 0px 2px 8px 0px var(--theme-color-20) !important;\n}\n`.trim();return l.toggleStyle(r,"player-shadow-style")})();

View File

@ -1 +1 @@
@font-face{font-family:quick-favorite;src:url(data:application/x-font-ttf;base64,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)}.ops .quick-favorite{margin-right:28px!important;position:relative}.ops .quick-favorite>i{font-family:quick-favorite!important;display:inline-block;font-style:normal;text-align:center;text-transform:none;line-height:1;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased}.ops .quick-favorite>i:after{content:""}.ops .quick-favorite .lists-tip,.ops .quick-favorite .select-list,.ops .quick-favorite .tip{line-height:normal;position:absolute;top:calc(100% + 8px);left:50%;transform:translateX(-50%);z-index:1000;background:#000d;padding:4px 8px;border-radius:4px;color:#eee;transition:.2s ease-out;opacity:0;pointer-events:none}.ops .quick-favorite .lists-tip.show,.ops .quick-favorite .select-list.show,.ops .quick-favorite .tip.show{opacity:1;pointer-events:initial}.ops .quick-favorite .tip{padding:8px}.ops .quick-favorite .lists-tip{top:calc(100% + 8px + 42px);color:#aaa;font-size:12px;z-index:100}.ops .quick-favorite .select-list{display:flex;align-items:center}.ops .quick-favorite .select-list>*{white-space:nowrap}.ops .quick-favorite .select-list .lists-loading{padding:4px 32px}.ops .quick-favorite .select-list .lists{margin-left:8px}.ops .quick-favorite .select-list .v-dropdown{color:#000}body.dark .ops .quick-favorite .select-list .v-dropdown{color:#eee}.ops .quick-favorite .select-list .v-dropdown .mdi-chevron-down{color:inherit!important;margin:0!important;transition:.2s ease-out;display:flex;align-items:center;justify-content:center;font-size:16pt!important}
@font-face{font-family:quick-favorite;src:url(data:application/x-font-ttf;base64,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)}.ops .quick-favorite{margin-right:28px!important;position:relative;width:auto!important}.ops .quick-favorite .text{display:inline}@media screen and (max-width:1320px),(max-height:750px){.ops .quick-favorite{margin-right:max(calc(11 * 1vw - 117.2px),6px)!important}.ops .quick-favorite .text{display:none}}.ops .quick-favorite>i{font-family:quick-favorite!important;display:inline-block;font-style:normal;text-align:center;text-transform:none;line-height:1;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased}.ops .quick-favorite>i:after{content:""}.ops .quick-favorite .lists-tip,.ops .quick-favorite .select-list,.ops .quick-favorite .tip{line-height:normal;position:absolute;top:calc(100% + 8px);left:50%;transform:translateX(-50%);z-index:1000;background:#000d;padding:4px 8px;border-radius:4px;color:#eee;transition:.2s ease-out;opacity:0;pointer-events:none}.ops .quick-favorite .lists-tip.show,.ops .quick-favorite .select-list.show,.ops .quick-favorite .tip.show{opacity:1;pointer-events:initial}.ops .quick-favorite .tip{padding:8px}.ops .quick-favorite .lists-tip{top:calc(100% + 8px + 42px);color:#aaa;font-size:12px;z-index:100}.ops .quick-favorite .select-list{display:flex;align-items:center}.ops .quick-favorite .select-list>*{white-space:nowrap}.ops .quick-favorite .select-list .lists-loading{padding:4px 32px}.ops .quick-favorite .select-list .lists{margin-left:8px}.ops .quick-favorite .select-list .v-dropdown{color:#000}body.dark .ops .quick-favorite .select-list .v-dropdown{color:#eee}.ops .quick-favorite .select-list .v-dropdown .mdi-chevron-down{color:inherit!important;margin:0!important;transition:.2s ease-out;display:flex;align-items:center;justify-content:center;font-size:16pt!important}

View File

@ -1 +1 @@
(()=>(i,t)=>{(async()=>{if(!document.URL.startsWith("https://www.bilibili.com/video/")||!getUID()){return}const{playerReady:e}=await t.importAsync("player-ready");await e();const s=dq(".video-toolbar .ops .collect");if(!s){return}t.applyStyle("quickFavoriteStyle");const a=`\n<span title="快速收藏" @click.left.self="toggle()" @click.right.prevent.self="listShowing = !listShowing" class="quick-favorite" :class="{ on: isFavorite }">\n<i @click.left="toggle()" @click.right.prevent="listShowing = !listShowing"></i>\n<div style="display: inline" @click.left="toggle()" @click.right.prevent="listShowing = !listShowing">快速收藏</div>\n</template>\n<div class="select-list" ref="selectList" :class="{ show: listShowing }">\n 选择快速收藏夹:\n<div v-if="lists.length > 0" class="lists">\n<v-dropdown :items="lists.map(it => it.title)" :value.sync="selectedFavorite"></v-dropdown>\n</div>\n<div class="lists-loading" v-else>加载中...</div>\n</div>\n<div class="lists-tip" :class="{ show: listShowing }">右键点击快速收藏可再次打开</div>\n<div class="tip" :class="{ show: tipShowing }">{{tipText}}</div>\n</span>\n`.trim();if(i.outerWatchlater){const i=await SpinQuery.select(".ops .watchlater");if(i!==null){i.insertAdjacentHTML("beforebegin",a)}else{s.insertAdjacentHTML("afterend",a)}}else{s.insertAdjacentHTML("afterend",a)}const o=dq(".ops .quick-favorite");if(!o){return}const l=new Vue({el:o,store:store,components:{VDropdown:()=>t.importAsync("v-dropdown.vue")},data:{aid:unsafeWindow.aid,favoriteTitle:"",isFavorite:false,tipText:"",tipShowing:false,tipHandle:0,lists:[],selectedFavorite:"",listShowing:false},async created(){this.syncFavoriteState()},watch:{selectedFavorite(t){if(this.lists.length===0){return}const e=this.lists;const s=e.find((i=>i.title===t));if(s){i.quickFavoriteID=s.id;this.syncFavoriteState()}else{console.error("list not found in selectedFavorite(value)")}},async listShowing(i){if(i){document.addEventListener("click",(i=>{const t=this.$el;const e=i.target;if(e!==t&&!t.contains(e)){this.listShowing=false}}));if(this.lists.length===0){try{const i=await Ajax.getJsonWithCredentials(`https://api.bilibili.com/medialist/gateway/base/created?pn=1&ps=100&up_mid=${getUID()}&is_space=0`);if(i.code!==0){throw new Error(`获取收藏夹列表失败: ${i.message}`)}this.lists=_.get(i,"data.list",[])}catch(i){logError(i)}}}}},methods:{async syncFavoriteState(){if(i.quickFavoriteID===0){return}try{const t=await Ajax.getJsonWithCredentials(`https://api.bilibili.com/x/v3/fav/folder/created/list-all?type=2&rid=${this.aid}&up_mid=${getUID()}`);if(t.code!==0){throw new Error(`获取收藏状态失败: ${t.message}`)}const e=_.get(t,"data.list",[]);const s=e.find((t=>t.id===i.quickFavoriteID));if(s===undefined){i.quickFavoriteID=0;return}this.isFavorite=Boolean(s.fav_state);this.selectedFavorite=this.favoriteTitle=s.title}catch(i){logError(i)}},showTip(i){this.tipText=i;this.tipShowing=true;if(this.tipHandle){clearTimeout(this.tipHandle)}this.tipHandle=setTimeout((()=>{this.tipShowing=false}),2e3)},async toggle(){if(i.quickFavoriteID===0){this.listShowing=true;return}const t={rid:this.aid,type:2,add_media_ids:"",del_media_ids:"",csrf:getCsrf()};t[this.isFavorite?"del_media_ids":"add_media_ids"]=i.quickFavoriteID.toString();try{await Ajax.postTextWithCredentials(`https://api.bilibili.com/x/v3/fav/resource/deal`,Object.entries(t).map((([i,t])=>`${i}=${t}`)).join("&"));this.isFavorite=!this.isFavorite;this.showTip(this.isFavorite?`已添加至收藏夹: ${this.favoriteTitle}`:`已移出收藏夹: ${this.favoriteTitle}`)}catch(i){Toast.error(`快速收藏失败: ${i.message}`,"快速收藏");console.error(i)}}}});Observer.videoChange((()=>{l.aid=unsafeWindow.aid;l.syncFavoriteState()}))})();return{unload:()=>{dqa(".ops .quick-favorite").forEach((i=>i.style.display="none"))},reload:()=>{dqa(".ops .quick-favorite").forEach((i=>i.style.display="inline-block"))}}})();
(()=>(t,i)=>{(async()=>{if(!document.URL.startsWith("https://www.bilibili.com/video/")||!getUID()){return}const{playerReady:e}=await i.importAsync("player-ready");await e();const s=dq(".video-toolbar .ops .collect");if(!s){return}i.applyStyle("quickFavoriteStyle");const a=`\n<span title="快速收藏" @click.left.self="toggle()" @click.right.prevent.self="listShowing = !listShowing" class="quick-favorite" :class="{ on: isFavorite }">\n<i @click.left="toggle()" @click.right.prevent="listShowing = !listShowing"></i>\n<div class="text" @click.left="toggle()" @click.right.prevent="listShowing = !listShowing">快速收藏</div>\n</template>\n<div class="select-list" ref="selectList" :class="{ show: listShowing }">\n 选择快速收藏夹:\n<div v-if="lists.length > 0" class="lists">\n<v-dropdown :items="lists.map(it => it.title)" :value.sync="selectedFavorite"></v-dropdown>\n</div>\n<div class="lists-loading" v-else>加载中...</div>\n</div>\n<div class="lists-tip" :class="{ show: listShowing }">右键点击快速收藏可再次打开</div>\n<div class="tip" :class="{ show: tipShowing }">{{tipText}}</div>\n</span>\n`.trim();if(t.outerWatchlater){const t=await SpinQuery.select(".ops .watchlater");if(t!==null){t.insertAdjacentHTML("beforebegin",a)}else{s.insertAdjacentHTML("afterend",a)}}else{s.insertAdjacentHTML("afterend",a)}const o=dq(".ops .quick-favorite");if(!o){return}const l=new Vue({el:o,store:store,components:{VDropdown:()=>i.importAsync("v-dropdown.vue")},data:{aid:unsafeWindow.aid,favoriteTitle:"",isFavorite:false,tipText:"",tipShowing:false,tipHandle:0,lists:[],selectedFavorite:"",listShowing:false},async created(){this.syncFavoriteState()},watch:{selectedFavorite(i){if(this.lists.length===0){return}const e=this.lists;const s=e.find((t=>t.title===i));if(s){t.quickFavoriteID=s.id;this.syncFavoriteState()}else{console.error("list not found in selectedFavorite(value)")}},async listShowing(t){if(t){document.addEventListener("click",(t=>{const i=this.$el;const e=t.target;if(e!==i&&!i.contains(e)){this.listShowing=false}}));if(this.lists.length===0){try{const t=await Ajax.getJsonWithCredentials(`https://api.bilibili.com/medialist/gateway/base/created?pn=1&ps=100&up_mid=${getUID()}&is_space=0`);if(t.code!==0){throw new Error(`获取收藏夹列表失败: ${t.message}`)}this.lists=_.get(t,"data.list",[])}catch(t){logError(t)}}}}},methods:{async syncFavoriteState(){if(t.quickFavoriteID===0){return}try{const i=await Ajax.getJsonWithCredentials(`https://api.bilibili.com/x/v3/fav/folder/created/list-all?type=2&rid=${this.aid}&up_mid=${getUID()}`);if(i.code!==0){throw new Error(`获取收藏状态失败: ${i.message}`)}const e=_.get(i,"data.list",[]);const s=e.find((i=>i.id===t.quickFavoriteID));if(s===undefined){t.quickFavoriteID=0;return}this.isFavorite=Boolean(s.fav_state);this.selectedFavorite=this.favoriteTitle=s.title}catch(t){logError(t)}},showTip(t){this.tipText=t;this.tipShowing=true;if(this.tipHandle){clearTimeout(this.tipHandle)}this.tipHandle=setTimeout((()=>{this.tipShowing=false}),2e3)},async toggle(){if(t.quickFavoriteID===0){this.listShowing=true;return}const i={rid:this.aid,type:2,add_media_ids:"",del_media_ids:"",csrf:getCsrf()};i[this.isFavorite?"del_media_ids":"add_media_ids"]=t.quickFavoriteID.toString();try{await Ajax.postTextWithCredentials(`https://api.bilibili.com/x/v3/fav/resource/deal`,Object.entries(i).map((([t,i])=>`${t}=${i}`)).join("&"));this.isFavorite=!this.isFavorite;this.showTip(this.isFavorite?`已添加至收藏夹: ${this.favoriteTitle}`:`已移出收藏夹: ${this.favoriteTitle}`)}catch(t){Toast.error(`快速收藏失败: ${t.message}`,"快速收藏");console.error(t)}}}});Observer.videoChange((()=>{l.aid=unsafeWindow.aid;l.syncFavoriteState()}))})();return{unload:()=>{dqa(".ops .quick-favorite").forEach((t=>t.style.display="none"))},reload:()=>{dqa(".ops .quick-favorite").forEach((t=>t.style.display="inline-block"))}}})();

1
min/remove-guide-popup.min.js vendored Normal file
View File

@ -0,0 +1 @@
(()=>(e,i)=>i.toggleStyle(`\n .bilibili-player-video-popup-guide-attention { display: none !important; }\n`.trim(),"remove-guide-popup-style"))();

View File

@ -1 +1 @@
.gui-settings-tooltip{position:fixed;left:440px;color:#fff;background:#0008;border:1px solid #333;padding:14px 14px 8px;font-size:12pt;top:50%;transform:translateY(-50%);transition:.2s ease-out;opacity:0;min-width:100%;max-height:90%;line-height:1.8;white-space:pre-wrap;pointer-events:none;user-select:text;border-radius:var(--large-corner-radius);overflow:auto;scrollbar-width:none!important}.gui-settings-tooltip::-webkit-scrollbar{height:0!important;width:0!important}.gui-settings-tooltip.show{pointer-events:initial;opacity:1}.gui-settings-box:not(.opened) .gui-settings-tooltip,.gui-settings-tooltip:empty{pointer-events:none;opacity:0}.gui-settings-tooltip .tooltip-title{font-weight:700;font-size:large;line-height:normal;padding-bottom:12px;margin-bottom:6px;border-bottom:1px solid #333}.gui-settings-tooltip span{display:inline-flex;padding:4px;margin:0 2px;line-height:1.2;background-color:#222;border:1px solid #333;border-radius:4px}.gui-settings-tooltip a,.gui-settings-tooltip a:active,.gui-settings-tooltip a:hover{color:var(--theme-color)}
.gui-settings-tooltip{position:fixed;left:440px;color:#fff;background:#0008;border:1px solid #333;padding:14px 14px 8px;font-size:12pt;top:50%;transform:translateY(-50%);transition:.2s ease-out;min-width:100%;max-height:90%;line-height:1.8;white-space:pre-wrap;user-select:text;border-radius:var(--large-corner-radius);overflow:auto;scrollbar-width:none!important}.gui-settings-tooltip::-webkit-scrollbar{height:0!important;width:0!important}.gui-settings-box:not(.opened) .gui-settings-tooltip,.gui-settings-tooltip:empty{pointer-events:none;opacity:0}.gui-settings-tooltip .tooltip-title{font-weight:700;font-size:large;line-height:normal;padding-bottom:12px;margin-bottom:6px;border-bottom:1px solid #333}.gui-settings-tooltip span{display:inline-flex;padding:4px;margin:0 2px;line-height:1.2;background-color:#222;border:1px solid #333;border-radius:4px}.gui-settings-tooltip a,.gui-settings-tooltip a:active,.gui-settings-tooltip a:hover{color:var(--theme-color)}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
(()=>(t,e)=>{const o=["spm_id_from","from_source","from_spmid","from","seid","share_source","share_medium","share_plat","share_tag","bbid","ts","timestamp","unique_k","rt","tdsourcetag","accept_quality","broadcast_type","current_qn","current_quality","playurl_h264","playurl_h265","quality_description","network","network_status","platform_network_status","p2p_type","referfrom","visit_id","bsource"];const s=[{match:/\/\/www\.bilibili\.com\/audio\/(au[\d]+|mycollection)/,param:"type"},{match:/\/\/live\.bilibili\.com\//,param:"session_id"},{match:/\/\/www\.bilibili\.com\/bangumi\//,param:"theme"}];const i=[/game\.bilibili\.com\/fgo/];const r=t=>t;const a=["videocard_series"];const c=()=>{const t=location.search.substring(1).split("&");if(t.some((t=>a.some((e=>t.includes(e)))))){return}const e=t.filter((t=>{if(o.some((e=>t.startsWith(`${e}=`)))){return false}if(s.some((({match:e,param:o})=>document.URL.match(e)&&t.startsWith(`${o}=`)))){return false}return true}));const i=e.join("&");const c=r(document.URL.replace(location.search,""));const n=i?"?"+i:"";const m=c+n;if(m!==document.URL){console.log("[URL params clean]",document.URL,m);history.replaceState({},document.title,m)}};fullyLoaded((()=>{if(document.contentType==="text/html"){c();Observer.videoChange((()=>c()))}}))})();
(()=>(t,e)=>{const o=["spm_id_from","from_source","from_spmid","from","seid","share_source","share_medium","share_plat","share_tag","bbid","ts","timestamp","unique_k","rt","tdsourcetag","accept_quality","broadcast_type","current_qn","current_quality","playurl_h264","playurl_h265","quality_description","network","network_status","platform_network_status","p2p_type","referfrom","visit_id","bsource","spm","hotRank"];const i=[{match:/\/\/www\.bilibili\.com\/audio\/(au[\d]+|mycollection)/,param:"type"},{match:/\/\/live\.bilibili\.com\//,param:"session_id"},{match:/\/\/www\.bilibili\.com\/bangumi\//,param:"theme"},{match:/\/\/t\.bilibili\.com\/[\d]+/,param:"type"}];const s=[/game\.bilibili\.com\/fgo/];const a=t=>t;const r=["videocard_series"];const c=()=>{const t=location.search.substring(1).split("&");if(t.some((t=>r.some((e=>t.includes(e)))))){return}const e=t.filter((t=>{if(o.some((e=>t.startsWith(`${e}=`)))){return false}if(i.some((({match:e,param:o})=>document.URL.match(e)&&t.startsWith(`${o}=`)))){return false}return true}));const s=e.join("&");const c=a(document.URL.replace(location.search,""));const m=s?"?"+s:"";const n=c+m;if(n!==document.URL){console.log("[URL params clean]",document.URL,n);history.replaceState({},document.title,n)}};fullyLoaded((()=>{if(document.contentType==="text/html"){c();Observer.videoChange((()=>c()))}}))})();

View File

@ -1,6 +1,6 @@
// ==UserScript==
// @name Bilibili Evolved (Preview)
// @version 1.11.14
// @version 1.11.15
// @description Bilibili Evolved 的预览版, 可以抢先体验新功能.
// @author Grant Howard, Coulomb-G
// @copyright 2021, Grant Howard (https://github.com/the1812) & Coulomb-G (https://github.com/Coulomb-G)
@ -126,7 +126,7 @@ window.EventTarget = class EventTarget {
}
// Safari EventTarget polyfill end
import { logError, raiseEvent, loadLazyPanel, contentLoaded, fixed, isOffline, getUID, scriptVersion, getCsrf, formatCount, escapeFilename } from './utils'
import { logError, raiseEvent, loadLazyPanel, loadDanmakuSettingsPanel, loadLazyPlayerSettingsPanel, loadSubtitleSettingsPanel, contentLoaded, fixed, isOffline, getUID, scriptVersion, getCsrf, formatCount, escapeFilename, fullyLoaded, formatDuration, formatFileSize, getDpiSourceSet, videoCondition, matchPattern, formData, retrieveImageUrl, isTyping, getAid } from './utils'
import { settings, loadSettings, settingsChangeHandlers } from './settings'
import { Ajax, setupAjaxHook } from './ajax'
import { loadResources } from './resource-loader'
@ -253,7 +253,11 @@ import { store } from './store'
logError,
raiseEvent,
loadLazyPanel,
loadLazyPlayerSettingsPanel,
loadDanmakuSettingsPanel,
loadSubtitleSettingsPanel,
contentLoaded,
fullyLoaded,
fixed,
settings,
settingsChangeHandlers,
@ -278,6 +282,12 @@ import { store } from './store'
getCsrf,
formatCount,
escapeFilename,
videoCondition,
matchPattern,
formData,
retrieveImageUrl,
isTyping,
getAid,
installStyle,
uninstallStyle,
toggleStyle,

View File

@ -985,6 +985,19 @@ Resource.manifest = {
displayNames: {
extendVideoSpeed: '扩展视频倍数菜单'
}
}
},
menuRepeatVideo: {
path: 'menu-repeat-video.min.js',
displayNames: {
menuRepeatVideo: '视频右键菜单循环播放',
},
},
removeGuidePopup: {
path: 'remove-guide-popup.min.js',
displayNames: {
removeGuidePopup: '删除视频弹窗',
},
reloadable: true,
},
}
export const resourceManifest = Resource.manifest

View File

@ -251,6 +251,8 @@ export const settings = {
extendVideoSpeedList: [2.5, 3],
customKeyBindings: {},
alwaysShowDuration: false,
menuRepeatVideo: false,
removeGuidePopup: true,
cache: {},
}
const fixedSettings = {

2
src/global.d.ts vendored
View File

@ -529,6 +529,8 @@ declare global {
extendVideoSpeedList: number[],
customKeyBindings: { [actionName: string]: string },
alwaysShowDuration: boolean,
menuRepeatVideo: boolean,
removeGuidePopup: boolean,
}
const GM_info: MonkeyInfo
function GM_xmlhttpRequest(details: MonkeyXhrDetails): { abort: () => void }

View File

@ -29,7 +29,11 @@ const loadSettings = () => {
addSettingsListener('customNavbarBlurOpacity', value => {
document.documentElement.style.setProperty('--navbar-blur-opacity', value)
})
if (!document.URL.startsWith('https://space.bilibili.com')) {
const fixedNotSupported = [
// 'https://search.bilibili.com/all',
'https://space.bilibili.com',
]
if (!fixedNotSupported.some(url => document.URL.startsWith(url))) {
addSettingsListener('customNavbarGlobalFixed', value => {
document.body.classList.toggle('fixed-navbar', value)
}, true)

View File

@ -3,46 +3,52 @@ interface SuggestItem {
value: string
html: string
}
const originalHistory = new class {
getAll(): SearchHistoryItem[] {
const history = localStorage.getItem('search_history')
if (!history) {
return []
}
return (JSON.parse(history) as any[]).map(it => {
return {
keyword: it.value,
date: new Date(it.timestamp).toJSON(),
count: 1,
}
})
interface HistoryItem {
value: string
isHistory: number
timestamp: number
}
const SearchHistoryKey = 'be_search_history'
const SearchHistoryMaxItems = 10
const getHistoryItems = () => {
const historyText = localStorage.getItem(SearchHistoryKey)
const historyItems: HistoryItem[] = historyText ? JSON.parse(historyText) : []
return historyItems
}
const clearHistoryItems = () => localStorage.setItem(SearchHistoryKey, '[]')
const addHistoryItem = (item: HistoryItem) => {
localStorage.setItem(SearchHistoryKey, JSON.stringify(
_.sortBy(_.uniqBy(getHistoryItems().concat(item), h => h.value), h => h.timestamp)
.reverse()
.slice(0, SearchHistoryMaxItems)
))
}
const deleteHistoryItem = (keyword: string) => {
const items = getHistoryItems()
const index = items.findIndex(it => it.value === keyword)
if (index !== -1) {
items.splice(index, 1)
localStorage.setItem(SearchHistoryKey, JSON.stringify(items))
}
saveAll(items: SearchHistoryItem[]) {
localStorage.setItem('search_history', JSON.stringify(items))
}
clear() {
localStorage.setItem('search_history', '[]')
}
merge(items: SearchHistoryItem[]) {
const originalItems = this.getAll()
return _.uniqBy(items.concat(originalItems), it => it.keyword).slice(0, 10)
}
add(item: SearchHistoryItem) {
const items = this.getAll()
const existingItem = items.find(it => it.keyword === item.keyword)
if (existingItem) {
Object.assign(existingItem, item)
} else {
items.push(item)
}
this.saveAll(items)
}
remove(item: SearchHistoryItem) {
const items = this.getAll()
const index = items.findIndex(it => it.keyword === item.keyword)
if (index > -1) {
items.splice(index, 1)
this.saveAll(items)
}
const migrateOldHistory = () => {
if (settings.searchHistory.length > 0) {
try {
const currentHistory = getHistoryItems()
const oldHistory: HistoryItem[] = settings.searchHistory.map(it => {
return {
value: it.keyword,
isHistory: 1,
timestamp: Number(new Date(it.date)),
}
})
const newHistory = _.sortBy(_.uniqBy(oldHistory.concat(currentHistory), h => h.value), h => h.timestamp)
.reverse()
.slice(0, SearchHistoryMaxItems)
localStorage.setItem(SearchHistoryKey, JSON.stringify(newHistory))
settings.searchHistory = []
} catch (error) {
console.error(error)
}
}
}
@ -134,6 +140,11 @@ export class SearchBox extends NavbarComponent {
async init() {
const form = await SpinQuery.select('#custom-navbar-search') as HTMLFormElement
const keywordInput = form.querySelector("input[name='keyword']") as HTMLInputElement
migrateOldHistory()
if (document.URL.startsWith('https://search.bilibili.com/all')) {
const keyword = window.location.search.match(/keyword=([^&]+)/)?.[1] || ''
keywordInput.value = decodeURIComponent(keyword)
}
form.addEventListener('submit', e => {
if (keywordInput.value === '') {
if (!settings.hideTopSearch) {
@ -150,21 +161,17 @@ export class SearchBox extends NavbarComponent {
e.preventDefault()
return false
}
const historyItem = settings.searchHistory.find(item => item.keyword === keywordInput.value)
const historyItem = getHistoryItems().find(item => item.value === keywordInput.value)
if (historyItem) {
historyItem.count++
historyItem.date = new Date().toJSON()
// originalHistory.add(historyItem)
historyItem.timestamp = Number(new Date())
} else {
const newItem = {
count: 1,
keyword: keywordInput.value,
date: new Date().toJSON(),
const newItem: HistoryItem = {
value: keywordInput.value,
isHistory: 1,
timestamp: Number(new Date()),
}
settings.searchHistory.unshift(newItem)
// originalHistory.add(newItem)
addHistoryItem(newItem)
}
settings.searchHistory = settings.searchHistory.slice(0, 10) // save history
return true
})
if (!settings.hideTopSearch) {
@ -228,15 +235,11 @@ export class SearchBox extends NavbarComponent {
if (keywordInput.value !== '') {
keywordInput.value = ''
}
const historyIndex = settings.searchHistory.findIndex(it => it.keyword === item.value)
const [historyItem] = settings.searchHistory.splice(historyIndex, 1)
// originalHistory.remove(historyItem)
settings.searchHistory = settings.searchHistory
deleteHistoryItem(item.value)
this.items.splice(index, 1)
},
clearSearchHistory() {
// originalHistory.clear()
settings.searchHistory = []
clearHistoryItems()
this.items = []
}
},
@ -247,19 +250,14 @@ export class SearchBox extends NavbarComponent {
searchList.isHistory = text === ''
const idJump = getIdJump(text)
if (searchList.isHistory) {
// searchList.items = originalHistory.merge(settings.searchHistory)
searchList.items = settings.searchHistory
.sort((a, b) => {
const aDate = a.date ? new Date(a.date) : new Date(0)
const bDate = b.date ? new Date(b.date) : new Date(0)
return Number(bDate) - Number(aDate)
})
searchList.items = getHistoryItems()
.sort((a, b) => b.timestamp - a.timestamp)
.map(item => {
return {
value: item.keyword,
html: item.keyword,
value: item.value,
html: item.value,
}
}).slice(0, 10)
}).slice(0, SearchHistoryMaxItems)
} else if (idJump.success) {
searchList.items = []
const url = idJump.aid ? `https://api.bilibili.com/x/web-interface/view?aid=${idJump.aid}` : `https://api.bilibili.com/x/web-interface/view?bvid=${idJump.bvid}`
@ -294,6 +292,10 @@ export class SearchBox extends NavbarComponent {
}
}
} else {
if (location.host === 'search.bilibili.com') {
// s.search.bilibili.com 禁止 search.bilibili.com 访问, 迷惑
return
}
const url = `https://s.search.bilibili.com/main/suggest?func=suggest&suggest_type=accurate&sub_type=tag&main_ver=v1&highlight=&userid=${getUID()}&bangumi_acc_num=1&special_acc_num=1&topic_acc_num=1&upuser_acc_num=3&tag_num=10&special_num=10&bangumi_num=10&upuser_num=3&term=${text}`
lastQueuedRequest = url
const json = await Ajax.getJson(url)

View File

@ -1673,6 +1673,7 @@ div.drag-bar,
{
box-shadow: 0px 1px 10px 0 #0003 !important;
}
#bilibili-player.mini-player::before,
.bilibili-player.mini-player::before
{
box-shadow: 0 4px 8px 0 #0004 !important;

View File

@ -957,8 +957,13 @@ html {
.article-action .more .more-ops-list ul li:hover {
@include theme-color();
}
.bilibili-player-context-menu-container ul > li a {
@include color("e");
.bilibili-player-context-menu-container ul > li {
a {
@include color("e");
&:hover {
@include background-color();
}
}
}
.coin-dialog-mask .like-checkbox {
@include color("e");

View File

@ -61,3 +61,6 @@
@include background-color("4");
}
}
.filter-wrap .filter-wrap_bottom {
@include background-color("4");
}

View File

@ -116,8 +116,6 @@ a.bp_box_bot_click,
.paging-box-big .next,
.paging-box-big .prev,
.bilibili-player-ending-panel-box-functions .bui-button.bui-button-disabled,
.bilibili-player-context-menu-container.black ul > li.hover,
.bilibili-player-context-menu-container.black ul > li a:hover,
.intimacy-bar,
.tab-box,
.live-module .tab-item .r-item .number,

View File

@ -1,17 +0,0 @@
const style = `<style id="player-shadow-style">
#bilibiliPlayer,
.bilibili-player.mini-player::before
{
box-shadow: 0px 2px 8px 0px var(--theme-color-30) !important;
}
body.dark #bilibiliPlayer,
body.dark .bilibili-player.mini-player::before
{
box-shadow: 0px 2px 8px 0px var(--theme-color-20) !important;
}
</style>`;
resources.applyStyleFromText(style);
export default {
reload: () => resources.applyStyleFromText(style),
unload: () => document.getElementById("player-shadow-style").remove(),
};

View File

@ -0,0 +1,13 @@
const style = `
#bilibiliPlayer,
#bilibili-player.mini-player:before
{
box-shadow: 0px 2px 8px 0px var(--theme-color-30) !important;
}
body.dark #bilibiliPlayer,
body.dark #bilibili-player.mini-player::before
{
box-shadow: 0px 2px 8px 0px var(--theme-color-20) !important;
}
`.trim()
export default resources.toggleStyle(style, 'player-shadow-style')

View File

@ -226,47 +226,46 @@ class VideoShot {
this.cidData = null
this.supportWebp = VideoShot.supportWebp
}
getVideoshot (currentTime, done) {
async getStyle(currentTime) {
const data = await this.cidData
const indexData = data.index
let shotIndex = 0
for (let index = 0; index < indexData.length - 2; index++) {
if (currentTime >= indexData[index] &&
currentTime < indexData[index + 1]) {
shotIndex = index
break
}
}
let imageData = data.image
if (!imageData) {
return {}
}
if (this.supportWebp) {
imageData = imageData.map(url => url.replace('.jpg', '.jpg@.webp'))
}
const xLength = parseInt(data.pv_x_len) || 10
const yLength = parseInt(data.pv_y_len) || 10
const xSize = parseInt(data.pv_x_size) || 160
const ySize = parseInt(data.pv_y_size) || 90
const x = -(shotIndex % 100 % xLength) * xSize
const y = -Math.floor(shotIndex % 100 / yLength) * ySize
return {
width: xSize,
height: ySize,
backgroundImage: `url(${imageData[Math.floor(shotIndex / 100)]})`,
backgroundPosition: `${x}px ${y}px`
}
}
async getVideoshot (currentTime) {
if (!(this.aid && this.cid)) {
return
return {}
}
if (!this.cidData) {
Ajax.getText(`https://api.bilibili.com/x/player/videoshot?aid=${this.aid}&cid=${this.cid}&index=1`).then(response => {
this.cidData = JSON.parse(response).data
this.getVideoshot(currentTime, done)
})
} else {
const data = this.cidData
const indexData = data.index
let shotIndex = 0
for (let index = 0; index < indexData.length - 2; index++) {
if (currentTime >= indexData[index] &&
currentTime < indexData[index + 1]) {
shotIndex = index
break
}
}
let imageData = data.image
if (imageData === null) {
return
}
if (this.supportWebp) {
imageData = imageData.map(url => url.replace('.jpg', '.jpg@.webp'))
}
const xLength = parseInt(data.pv_x_len) || 10
const yLength = parseInt(data.pv_y_len) || 10
const xSize = parseInt(data.pv_x_size) || 160
const ySize = parseInt(data.pv_y_size) || 90
const x = -(shotIndex % 100 % xLength) * xSize
const y = -Math.floor(shotIndex % 100 / yLength) * ySize
done({
width: xSize,
height: ySize,
backgroundImage: `url(${imageData[Math.floor(shotIndex / 100)]})`,
backgroundPosition: `${x}px ${y}px`
})
this.cidData = Ajax.getText(`https://api.bilibili.com/x/player/videoshot?aid=${this.aid}&cid=${this.cid}&index=1`).then(response => JSON.parse(response).data)
}
return this.getStyle(currentTime)
}
static get supportWebp () {
try {
@ -381,7 +380,7 @@ function setupTouchPlayer (player) {
text.querySelector('.touch-speed').innerHTML = `${speed}`
text.querySelector('.touch-info').innerHTML = `进度: ${sec > 0 ? '+' : '-'}${secondsToTime(change)}`
text.querySelector('.touch-result').innerHTML = result
videoshot.getVideoshot(finalTime, style => $('.videoshot').css(style))
videoshot.getVideoshot(finalTime).then(style => $('.videoshot').css(style))
$('.touch-progress').css('width', `${finalPercent}%`)
}
}

View File

@ -76,6 +76,7 @@
<checkbox indent="0" key="doubleClickFullscreen" dependencies=""></checkbox>
<checkbox indent="0" key="showCoverBeforePlay" dependencies=""></checkbox>
<checkbox indent="0" key="seoJump" dependencies=""></checkbox>
<checkbox indent="0" key="removeGuidePopup" dependencies=""></checkbox>
<category icon="style">样式</category>
<li class="indent-0 folded">
<label class="gui-settings-textbox-container">
@ -196,5 +197,5 @@
<a class="release-notes" target="_blank" href="https://github.com/the1812/Bilibili-Evolved/releases">更新日志</a>
</div>
</div>
<div class="gui-settings-tooltip show"></div>
<div class="gui-settings-tooltip"></div>
</div>

View File

@ -10,21 +10,15 @@
top: 50%;
transform: translateY(-50%);
transition: all 0.2s ease-out;
opacity: 0;
min-width: 100%;
max-height: 90%;
line-height: 1.8;
white-space: pre-wrap;
pointer-events: none;
user-select: text;
border-radius: var(--large-corner-radius);
overflow: auto;
@include no-scrollbar();
&.show {
pointer-events: initial;
opacity: 1;
}
.gui-settings-box:not(.opened) &,
&:empty {
pointer-events: none;

View File

@ -20,7 +20,13 @@ export const toolTips = new Map<keyof BilibiliEvolvedSettings, string>([
["autoContinue", /*html*/`播放视频时如果检测到历史记录信息(<span>上次看到...</span>消息), 则自动跳转到相应的时间播放.`],
["airborne", /*html*/`当弹幕出现视频时间点时用下划线标记,点击即可空降到相应时间点.`],
["skipChargeList", /*html*/`自动跳过视频结尾的充电鸣谢.`],
["framePlayback", /*html*/`在播放器的时间右边增加两个按钮, 用于<span>较</span>精细调整视频时间. 支持键盘快捷键<kbd>Shift</kbd>+<kbd>←</kbd>/<kbd>→</kbd>. (旧版播放器只能用键盘快捷键, 不会显示按钮)`],
["framePlayback", /*html*/`在播放器的时间右边增加两个按钮, 用于<span>较</span>精细调整视频时间.
: 视频的实际播放帧率跟视频本身的帧率和显示器的刷新率有关, , .
:
- <span>1080P60</span>/<span>720P60</span>: 1001 / 60000 (59.94006 fps)
- <span></span>: 1001 / 30000 (29.97003 fps)
`],
["playerFocus", /*html*/`进入视频/番剧页面时, 自动定位到播放器.`],
["playerFocusOffset", /*html*/`定位时的竖直偏移量, 单位为像素(px).`],
["customStyleColor", /*html*/`设定顶栏(自定义顶栏启用时)和夜间模式使用的主题色, 可以点击颜色预览的圆圈打开色板, 其中含有预定义的16种主题色, 也可以在右侧的文本框直接输入任何有效的16进制颜色值(<span>#rrggbb</span>或<span>#rgb</span>).`],
@ -117,8 +123,9 @@ export const toolTips = new Map<keyof BilibiliEvolvedSettings, string>([
["hideBanner", /*html*/`隐藏首页顶部横幅.`],
["allowJumpContinue", /*html*/`当历史记录的集数与当前打开的不一致时, 仍然自动跳转.`],
["hideBangumiReviews", /*html*/`隐藏番剧播放页面里的点评板块.`],
["videoScreenshot", /*html*/`启用视频快速截图, 将在播放器的时间右边增加一个截图按钮. 支持键盘快捷键<kbd>Ctrl</kbd>+<kbd>Alt</kbd>+<kbd>C</kbd>. (旧版播放器只能用键盘快捷键, 不会显示按钮)
Canvas, <kbd>Shift</kbd>. "按住<kbd>Shift</kbd>点击截图按钮", "<kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>Alt</kbd>+<kbd>C</kbd>".`],
["videoScreenshot", /*html*/`在播放器的时间右边增加截图按钮, 点击可以截取视频画面, 不会包含暂停标志和弹幕. 截取的图片将在网页右侧显示(非全屏或网页全屏模式), 可以单独保存或丢弃, 也可以截取一定数量后一次性保存.
Canvas, <kbd>Shift</kbd> .`],
["filenameFormat", /*html*/`自定义文件命名格式, 作用于<span>下载弹幕</span>, <span>下载视频</span>, <span>视频截图</span>, <span>查看封面</span>.
使:
- <span>title</span>: /
@ -156,12 +163,14 @@ export const toolTips = new Map<keyof BilibiliEvolvedSettings, string>([
- <kbd>\`</kbd> 打开播放器菜单
- <kbd>Shift + j</kbd> 退85
- <kbd>Shift + w</kbd>
- <kbd>Shift + s</kbd>
- <kbd>Shift + s</kbd> ()
- <kbd>Shift + /</kbd> / <kbd>Shift + ,/.</kbd>
- <kbd>Shift + /</kbd> 1.0x
- <kbd>Ctrl + Shift + /</kbd>
- <kbd>Ctrl + Shift + Alt + /</kbd>
- <kbd>Shift + /</kbd>
- <kbd>Shift + :</kbd>
- <kbd>0</kbd>
- <kbd>Shift</kbd>+<kbd></kbd>/<kbd></kbd> ()
- <kbd>Ctrl</kbd>+<kbd>Alt</kbd>+<kbd>C</kbd> ()
Canvas, <kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>Alt</kbd>+<kbd>C</kbd> .
/<a href="https://github.com/the1812/Bilibili-Evolved/blob/preview/src/video/keymap/key-bindings.md" target="__blank"></a>.
`],

View File

@ -28,6 +28,8 @@ const blockParams = [
'referfrom',
'visit_id',
'bsource',
'spm',
'hotRank',
]
const siteSpecifiedParams = [
{
@ -42,6 +44,10 @@ const siteSpecifiedParams = [
match: /\/\/www\.bilibili\.com\/bangumi\//,
param: 'theme',
},
{
match: /\/\/t\.bilibili\.com\/[\d]+/,
param: 'type',
},
]
const noNormalizes = [
/game\.bilibili\.com\/fgo/,

View File

@ -0,0 +1,84 @@
/** 播放器右键菜单 */
export interface PlayerContextMenu {
/** 总体容器元素, 不是里面那个 <ul> */
containerElement: HTMLElement
/** 里面的 <ul> 列表元素 */
listElement: HTMLElement
/** 所有菜单项元素 */
itemElements: HTMLElement[]
/** 是否已打开 */
isOpen: boolean
}
/** 在播放器菜单打开/关闭时执行特定操作 */
export const forEachContextMenu = async (action: {
open?: (menu: PlayerContextMenu) => void,
close?: (menu: PlayerContextMenu) => void,
}) => {
const { open, close } = action
const hasVideo = await videoCondition()
if (!hasVideo) {
return
}
const player = await SpinQuery.select('.bilibili-player') as HTMLElement
if (!player) {
return
}
const playerObserver = Observer.childList(player, () => {
const container = dq('.bilibili-player-context-menu-origin') as HTMLElement
if (container) {
playerObserver.stop()
const ul = dq(container, 'ul') as HTMLElement
const menu: PlayerContextMenu = {
containerElement: container,
listElement: ul,
get itemElements() {
return dqa(container, '.context-menu-function') as HTMLElement[]
},
get isOpen() {
return container.classList.contains('active')
}
}
Observer.childList(ul, () => {
if (menu.isOpen) { // 打开菜单
open?.(menu)
} else { // 关闭菜单
close?.(menu)
}
})
}
})
}
/** 添加播放器菜单项 */
export const addMenuItem = async (
element: HTMLElement,
cleanUp?: (menu: PlayerContextMenu) => void,
) =>{
return forEachContextMenu({
open: menu => {
if (menu.listElement.contains(element)) {
return
}
// <li class="context-line context-menu-function" data-append="1">
// <a class="context-menu-a js-action" href="javascript:void(0);"></a>
// </li>
const menuItem = document.createElement('li')
menuItem.classList.add('context-line', 'context-menu-function')
menuItem.setAttribute('data-append', '1')
const linkWrapper = document.createElement('a')
linkWrapper.classList.add('context-menu-a', 'js-action')
linkWrapper.href = 'javascript:void(0);'
linkWrapper.appendChild(element)
menuItem.addEventListener('mouseover', () => menuItem.classList.add('hover'))
menuItem.addEventListener('mouseout', () => menuItem.classList.remove('hover'))
menuItem.appendChild(linkWrapper)
menu.listElement.appendChild(menuItem)
},
close: menu => cleanUp?.(menu),
})
}
export default {
export: {
forEachContextMenu,
addMenuItem,
}
}

View File

@ -0,0 +1,10 @@
(async () => {
if (!(await videoCondition())) {
return
}
const { addMenuItem } = await import('../player-context-menu')
const div = document.createElement('div')
div.addEventListener('click', () => console.log('click!'))
div.innerHTML = 'test'
addMenuItem(div)
})()

View File

@ -134,40 +134,14 @@ if (supportedUrls.some(url => document.URL.startsWith(url))) {
like: (() => {
/** 长按`L`三连使用的记忆变量 */
let likeClick = true
/** 在稍后再看页面里, 记录当前视频是否赞过 */
let liked = false
const listenWatchlaterVideoChange = _.once(() => {
Observer.videoChange(() => {
Ajax.getJsonWithCredentials(`https://api.bilibili.com/x/web-interface/archive/has/like?aid=${unsafeWindow.aid}`).then(json => {
liked = Boolean(json.data)
})
})
})
return (({ isWatchlater, isMediaList, event }) => {
if (isWatchlater) {
listenWatchlaterVideoChange()
const formData = {
aid: unsafeWindow.aid,
/** `1`点赞; `2`取消赞 */
like: liked ? 2 : 1,
csrf: getCsrf(),
}
Ajax.postTextWithCredentials(`https://api.bilibili.com/x/web-interface/archive/like`, Object.entries(formData).map(([k, v]) => `${k}=${v}`).join('&')).then(() => {
liked = !liked
if (liked) {
Toast.success(`已点赞`, `快捷键扩展`, 1000)
} else {
Toast.success(`已取消点赞`, `快捷键扩展`, 1000)
}
})
} else if (isMediaList) {
if (isMediaList || isWatchlater) {
const likeButton = dq('.play-options-ul > li:first-child') as HTMLLIElement
if (likeButton) {
likeButton.click()
}
} else {
const likeButton = dq('.video-toolbar .like') as HTMLSpanElement
const likeButton = dq('.video-toolbar .like, .tool-bar .like-info') as HTMLSpanElement
event.preventDefault()
const fireEvent = (name: string, args: Event) => {
const event = new CustomEvent(name, args)
@ -225,14 +199,11 @@ if (supportedUrls.some(url => document.URL.startsWith(url))) {
controller.setVideoSpeed([...rates].reverse().find(it => it < controller.playbackRate) || rates[0])
}),
videoSpeedReset: videoSpeed((controller) => {
controller.reset()
controller.toggleVideoSpeed()
}),
videoSpeedForget: videoSpeed((controller) => {
controller.reset(true)
}),
videoSpeedToggle: videoSpeed((controller) => {
controller.toggleVideoSpeed()
}),
takeScreenshot: clickElement('.video-take-screenshot'),
previousFrame: clickElement('.prev-frame'),
nextFrame: clickElement('.next-frame'),
@ -273,8 +244,7 @@ if (supportedUrls.some(url => document.URL.startsWith(url))) {
videoSpeedIncrease: 'shift > 》 arrowUp',
videoSpeedDecrease: 'shift < 《 arrowDown',
videoSpeedReset: 'shift ? ',
videoSpeedForget: 'ctrl shift alt ?',
videoSpeedToggle: 'ctrl shift ?',
videoSpeedForget: 'shift :',
takeScreenshot: 'ctrl alt c',
previousFrame: 'shift arrowLeft',
nextFrame: 'shift arrowRight',

View File

@ -1,10 +1,14 @@
.ops .watchlater
{
margin-right: 28px !important;
position: relative;
}
.ops .watchlater .tip
{
.ops .watchlater {
margin-right: 28px !important;
position: relative;
width: auto !important;
@media screen and (max-width: 1320px), (max-height: 750px) {
margin-right: max(calc(11 * 1vw - 117.2px), 6px) !important;
.text {
display: none;
}
}
.tip {
position: absolute;
top: calc(100% + 8px);
left: 50%;
@ -14,17 +18,15 @@
padding: 4px 8px;
border-radius: 4px;
color: #eee;
transition: all .2s ease-out;
transition: all 0.2s ease-out;
opacity: 0;
pointer-events: none;
}
.watchlater .tip.show
{
opacity: 1;
pointer-events: initial;
}
.ops .watchlater>i
{
&.show {
opacity: 1;
pointer-events: initial;
}
}
> i {
display: inline-block;
font-style: normal;
vertical-align: baseline;
@ -33,8 +35,8 @@
line-height: 1;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
}
}
.more-ops-list > ul > li:nth-child(2) {
display: none !important;
}
.more-ops-list>ul>li:nth-child(2)
{
display: none !important;
}

View File

@ -10,10 +10,10 @@
}
resources.applyStyle('outerWatchlaterStyle')
favoriteButton.insertAdjacentHTML('afterend', /*html*/`
<span title='稍后再看' class='watchlater' :class="{on: isInWatchlater}" @click="toggle()">
<span title='稍后再看' class='watchlater' :class="{ on: isInWatchlater }" @click="toggle()">
<i class='mdi mdi-timetable'></i>
<div class='tip' :class="{show: tipShowing}">{{tipText}}</div>
<span class="text"></span>
<div class='tip' :class="{ show: tipShowing }">{{ tipText }}</div>
</span>
`.trim())
const watchlaterButton = dq('.ops .watchlater')

View File

@ -6,6 +6,16 @@
.quick-favorite {
margin-right: 28px !important;
position: relative;
width: auto !important;
.text {
display: inline;
}
@media screen and (max-width: 1320px), (max-height: 750px) {
margin-right: max(calc(11 * 1vw - 117.2px), 6px) !important;
.text {
display: none;
}
}
> i {
font-family: "quick-favorite" !important;
display: inline-block;

View File

@ -12,7 +12,7 @@
const html = /*html*/`
<span title="快速收藏" @click.left.self="toggle()" @click.right.prevent.self="listShowing = !listShowing" class="quick-favorite" :class="{ on: isFavorite }">
<i @click.left="toggle()" @click.right.prevent="listShowing = !listShowing"></i>
<div style="display: inline" @click.left="toggle()" @click.right.prevent="listShowing = !listShowing"></div>
<div class="text" @click.left="toggle()" @click.right.prevent="listShowing = !listShowing"></div>
</template>
<div class="select-list" ref="selectList" :class="{ show: listShowing }">
:

View File

@ -0,0 +1,3 @@
export default resources.toggleStyle(`
.bilibili-player-video-popup-guide-attention { display: none !important; }
`.trim(), 'remove-guide-popup-style')

View File

@ -92,7 +92,7 @@ export class VideoSpeedController {
settings.rememberVideoSpeedList = settings.rememberVideoSpeedList
}
static async getInstance(previousSpeed?: number) {
static async getInstance(previousSpeed?: number, nativeSpeed?: number) {
const containerElement = await SpinQuery.select(`.${VideoSpeedController.classNameMap.speedContainer}`)
const videoElement = await SpinQuery.select(`.${VideoSpeedController.classNameMap.video} video`) as HTMLVideoElement
@ -103,19 +103,21 @@ export class VideoSpeedController {
throw "video element not found!"
}
return new VideoSpeedController(containerElement, videoElement, previousSpeed)
return new VideoSpeedController(containerElement, videoElement, previousSpeed, nativeSpeed)
}
static init = _.once(() => {
// 分 P 切换时共享同一个倍数
let sharedSpeed = 1
// 分 P 切换时共享同一个倍数,这里指定初始倍数可以是 undefined不需要是 1
let sharedSpeed: number | undefined = undefined
// 分 P 切换时共享同一个原生倍速值,初始值设置为 1
let sharedNativeSpeed = 1
// 持有菜单容器元素的引用videoChange 时更换(以前缓存的 VideoController 就没有意义了)
let containerElement: HTMLElement
Observer.videoChange(async () => {
const { getExtraSpeedMenuItemElements } = await import("./extend-video-speed")
// 有必要传递之前的 nativeSpeedVal,跨分 P 时原生倍数将保持一样
const controller = await VideoSpeedController.getInstance(sharedSpeed)
// 有必要传递之前的 nativeSpeed,跨分 P 时原生倍数将保持一样
const controller = await VideoSpeedController.getInstance(sharedSpeed, sharedNativeSpeed)
controller.observe();
if (settings.extendVideoSpeed) {
controller._menuListElement.prepend(...await getExtraSpeedMenuItemElements())
@ -136,10 +138,13 @@ export class VideoSpeedController {
}
})
}
// 理论上这里的 controller 一定是非缓存的,因此不用担心事件监听注册重复
// 理论上这里的 controller 一定是非缓存的(换 P 的时候 containerElement 是会发生改变的),因此不用担心重复注册事件监听
({ containerElement } = controller)
containerElement.addEventListener("changed", ({ detail: { speed } }: CustomEvent) => {
containerElement.addEventListener("changed", ({ detail: { speed, isNativeSpeed } }: CustomEvent) => {
sharedSpeed = speed
if (isNativeSpeed) {
sharedNativeSpeed = speed
}
})
// 首次加载可能会遇到意外情况,导致内部强制更新失效,因此延时 100 ms 再触发速度设置
setTimeout(() => {
@ -154,21 +159,19 @@ export class VideoSpeedController {
private _videoElement: HTMLVideoElement
// 这个值模拟原生内部记录的速度倍数,它不应该被赋值成扩展倍数的值
private _nativeSpeedVal: number
private _previousSpeedVal: number
// 这个值用于表示上一次上一P的倍数值如果是首次播放第一P则为 undefined
private _previousSpeedVal?: number
constructor(containerElement: HTMLElement, videoElement: HTMLVideoElement, previousSpeed?: number) {
constructor(containerElement: HTMLElement, videoElement: HTMLVideoElement, previousSpeed?: number, nativeSpeed?: number) {
const controller = VideoSpeedController.instanceMap.get(containerElement)
if (controller && (!previousSpeed || controller.playbackRate === previousSpeed)) {
if (controller && (!previousSpeed || controller._previousSpeedVal === previousSpeed) && (!nativeSpeed || controller._nativeSpeedVal === nativeSpeed)) {
return controller
}
this._videoElement = videoElement
previousSpeed = previousSpeed ?? this.playbackRate
this._containerElement = containerElement
this._previousSpeedVal = previousSpeed
this._nativeSpeedVal = VideoSpeedController.nativeSupportedRates.includes(previousSpeed) ? previousSpeed : 1
this._nativeSpeedVal = nativeSpeed ?? (previousSpeed && VideoSpeedController.nativeSupportedRates.includes(previousSpeed) ? previousSpeed : 1)
this._nameBtn = this._containerElement.querySelector(`.${VideoSpeedController.classNameMap.speedNameBtn}`) as HTMLButtonElement
this._menuListElement = this._containerElement.querySelector(`.${VideoSpeedController.classNameMap.speedMenuList}`) as HTMLElement
@ -213,11 +216,13 @@ export class VideoSpeedController {
currentSpeed = parseFloat(selectedSpeedOption.dataset.value ?? '1')
this._containerElement.dispatchEvent(new CustomEvent("changed", { detail: { speed: currentSpeed, previousSpeed: this._previousSpeedVal } }))
let isNativeSpeed = false
if (VideoSpeedController.nativeSupportedRates.includes(currentSpeed)) {
this._nativeSpeedVal = currentSpeed
isNativeSpeed = true
}
this._containerElement.dispatchEvent(new CustomEvent("changed", { detail: { speed: currentSpeed, isNativeSpeed, previousSpeed: this._previousSpeedVal } }))
// 原生支持倍数的应用后,有必要清除扩展倍数选项上的样式
if (settings.extendVideoSpeed && VideoSpeedController.nativeSupportedRates.includes(currentSpeed)) {
this._menuListElement.querySelector(`.${VideoSpeedController.classNameMap.speedMenuItem}.extended.${VideoSpeedController.classNameMap.active}`)?.classList.remove(VideoSpeedController.classNameMap.active)
@ -234,30 +239,63 @@ export class VideoSpeedController {
}
})
// 刷新 this._previousSpeedVal
// 用户可以通过倍数菜单或者重置倍数快捷键造成类似 2.0x 1.0x 1.0x ... 这样的倍数设定序列
// 我们不希望在第二个 1.0x 的时候刷新 this._previousSpeedVal这样会比较死板
// 判定依据在于 previousSpeed !== currentSpeed
// - 用户可以通过倍数菜单或者倍数快捷键造成类似 1.5x 2.0x 2.0x... 这样的倍数设定序列
// 我们不希望在第二个 2.0x 的时候刷新 this._previousSpeedVal这样会比较死板
// 判定依据在于 previousSpeed !== currentSpeed
if (previousSpeed && previousSpeed !== currentSpeed) {
this._previousSpeedVal = previousSpeed
}
})
}
toggleVideoSpeed() {
this.setVideoSpeed(this._previousSpeedVal)
/**
*
*
* `mode`
*
* - `mode === "smart"` 1.0x 1.0x
* - `mode === "classic"`
*
* `reset()` `forget`
*
* @param forget
*/
toggleVideoSpeed(mode: "smart" | "classic" = "smart", forget = false) {
switch (mode) {
case "smart":
this.playbackRate === 1 ? this.setVideoSpeed(this._previousSpeedVal) : this.reset(forget)
break
case "classic":
this.setVideoSpeed(this._previousSpeedVal)
break
default:
break
}
}
/**
*
*
* @param forget
*/
reset(forget = false) {
if (forget) {
const fallbackVideoSpeed = VideoSpeedController.fallbackVideoSpeed
// 如果 fallbackVideoSpeed 是 undefined那么意味着没有开启记忆倍数功能
// 考虑到与清除视频级别的倍数记忆功能的相关性,这里会忽略设定
// 简单地说,如果没有开启记忆倍数的功能,就无法清除视频级别的倍数记忆
if (!fallbackVideoSpeed) {
return
}
VideoSpeedController.forgetSpeed()
this.setVideoSpeed(VideoSpeedController.fallbackVideoSpeed || 1)
this.setVideoSpeed(VideoSpeedController.fallbackVideoSpeed)
} else {
this.setVideoSpeed(1)
}
}
setVideoSpeed(speed: number) {
this.getSpeedMenuItem(speed).click()
setVideoSpeed(speed?: number) {
speed && this.getSpeedMenuItem(speed).click()
}
private setExtendedVideoSpeed(speed: number) {

View File

@ -1 +1 @@
1.11.14
1.11.15