// ==UserScript==
// @name Bilibili视频详情页追加视频封面链接
// @name:zh-CN Bilibili视频详情页追加视频封面链接
// @name:en Add video cover link to Bilibili video detail page
// @name:ar إضافة رابط صورة غلاف الفيديو إلى صفحة تفاصيل فيديو Bilibili
// @description:ar يعرض رابطًا مباشرًا لصورة غلاف الفيديو في صفحة تفاصيل الفيديو على Bilibili.
// @name:bg Добавяне на връзка към обложката на видеото към страницата с подробности за видеоклипа в Bilibili
// @description:bg Показва директна връзка към обложката на видеото на страницата с подробности за видеоклипа в Bilibili.
// @name:cs Přidat odkaz na obálku videa na stránku s podrobnostmi o videu Bilibili
// @description:cs Zobrazí přímý odkaz na obálku videa na stránce s podrobnostmi o videu na Bilibili.
// @name:da Tilføj videocoverlink til Bilibili-videodetaljeside
// @description:da Viser et direkte link til videocoveret på Bilibilis videodetaljeside.
// @name:de Video-Cover-Link zur Bilibili-Videodetailseite hinzufügen
// @description:de Zeigt einen direkten Link zum Video-Cover auf der Bilibili-Videodetailseite an.
// @name:el Προσθήκη συνδέσμου εξωφύλλου βίντεο στη σελίδα λεπτομερειών βίντεο Bilibili
// @description:el Εμφανίζει έναν άμεσο σύνδεσμο προς το εξώφυλλο βίντεο στη σελίδα λεπτομερειών βίντεο του Bilibili.
// @name:eo Aldoni videokovrilan ligilon al Bilibili-videa detala paĝo
// @description:eo Montras rektan ligilon al la videokovrilo sur la Bilibili-videa detala paĝo.
// @name:es Agregar enlace de portada de video a la página de detalles del video de Bilibili
// @description:es Muestra un enlace directo a la portada del video en la página de detalles del video de Bilibili.
// @name:fi Lisää videon kansikuvalinkki Bilibilin videotietosivulle
// @description:fi Näyttää suoran linkin videon kansikuvaan Bilibilin videotietosivulla.
// @name:fr Ajouter un lien de couverture vidéo à la page de détails de la vidéo Bilibili
// @description:fr Affiche un lien direct vers la couverture de la vidéo sur la page de détails de la vidéo Bilibili.
// @name:fr-CA Ajouter un lien de couverture vidéo à la page de détails de la vidéo Bilibili
// @description:fr-CA Affiche un lien direct vers la couverture de la vidéo sur la page de détails de la vidéo Bilibili.
// @name:he הוסף קישור לעטיפת וידאו לדף הפרטים של סרטון Bilibili
// @description:he מציג קישור ישיר לעטיפת הוידאו בדף הפירוט של סרטון בביליבילי.
// @name:hr Dodaj vezu naslovnice videa na stranicu s detaljima videozapisa Bilibili
// @description:hr Prikazuje izravnu vezu na naslovnicu videa na stranici s detaljima videozapisa na Bilibili.
// @name:hu Videóborító link hozzáadása a Bilibili videó részletező oldalához
// @description:hu Közvetlen linket jelenít meg a videó borítójához a Bilibili videó részletező oldalán.
// @name:id Tambahkan tautan sampul video ke halaman detail video Bilibili
// @description:id Menampilkan tautan langsung ke sampul video di halaman detail video Bilibili.
// @name:it Aggiungi link di copertina video alla pagina dei dettagli del video di Bilibili
// @description:it Mostra un link diretto alla copertina del video nella pagina dei dettagli del video di Bilibili.
// @name:ja Bilibiliビデオ詳細ページにビデオカバーリンクを追加
// @description:ja Bilibiliのビデオ詳細ページにビデオカバーへの直接リンクを表示します。
// @name:ka Bilibili ვიდეოს დეტალური გვერდზე ვიდეოს ყდის ბმულის დამატება
// @description:ka აჩვენებს ვიდეოს ყდის პირდაპირ ბმულს Bilibili-ის ვიდეოს დეტალურ გვერდზე.
// @name:ko Bilibili 비디오 세부 정보 페이지에 비디오 커버 링크 추가
// @description:ko Bilibili 비디오 세부 정보 페이지에서 비디오 커버에 대한 직접 링크를 표시합니다.
// @name:nb Legg til videocoverlenke til Bilibili videodetaljside
// @description:nb Viser en direkte lenke til videocoveret på Bilibili videodetaljside.
// @name:nl Voeg een video-coverlink toe aan de Bilibili-videodetailpagina
// @description:nl Toont een directe link naar de video-cover op de Bilibili-videodetailpagina.
// @name:pl Dodaj link do okładki wideo na stronie szczegółów filmu Bilibili
// @description:pl Wyświetla bezpośredni link do okładki wideo na stronie szczegółów filmu Bilibili.
// @name:pt-BR Adicionar link da capa do vídeo à página de detalhes do vídeo Bilibili
// @description:pt-BR Exibe um link direto para a capa do vídeo na página de detalhes do vídeo no Bilibili.
// @name:ro Adăugați un link de copertă video la pagina de detalii video Bilibili
// @description:ro Afișează un link direct către coperta video pe pagina de detalii video Bilibili.
// @name:ru Добавить ссылку на обложку видео на страницу сведений о видео Bilibili
// @description:ru Отображает прямую ссылку на обложку видео на странице сведений о видео Bilibili.
// @name:sk Pridať odkaz na obálku videa na stránku s podrobnosťami o videu Bilibili
// @description:sk Zobrazuje priamy odkaz na obálku videa na stránke s podrobnosťami o videu na Bilibili.
// @name:sr Додај везу омота видео снимка на страницу са детаљима видео снимка Bilibili
// @description:sr Приказује директну везу до омота видео снимка на страници са детаљима видео снимка на Bilibili.
// @name:sv Lägg till videocoverlänk till Bilibili videodetaljsida
// @description:sv Visar en direktlänk till videocoveret på Bilibili videodetaljsida.
// @name:th เพิ่มลิงก์หน้าปกวิดีโอไปยังหน้าข้อมูลวิดีโอ Bilibili
// @description:th แสดงลิงก์ตรงไปยังหน้าปกวิดีโอบนหน้าข้อมูลวิดีโอของ Bilibili
// @name:tr Bilibili video ayrıntı sayfasına video kapak bağlantısı ekle
// @description:tr Bilibili video ayrıntı sayfasında video kapağına doğrudan bağlantı görüntüler.
// @name:ug Bilibili سىن تەپسىلىي بېتىگە سىن مۇقاۋىسى ئۇلىنىشى قوشۇڭ
// @description:ug Bilibili سىن تەپسىلىي بېتىدە سىن مۇقاۋىسىغا بىۋاسىتە ئۇلىنىش كۆرسىتىدۇ.
// @name:uk Додати посилання на обкладинку відео на сторінку відомостей про відео Bilibili
// @description:uk Відображає пряме посилання на обкладинку відео на сторінці відомостей про відео Bilibili.
// @name:vi Thêm liên kết bìa video vào trang chi tiết video Bilibili
// @description:vi Hiển thị một liên kết trực tiếp đến ảnh bìa video trên trang chi tiết video Bilibili.
// @name:zh Bilibili视频详情页追加视频封面链接
// @description:zh 在Bilibili视频详情页面显示视频封面图片的直接链接。
// @name:zh-CN Bilibili视频详情页追加视频封面链接
// @description:zh-CN 在Bilibili视频详情页面显示视频封面图片的直接链接。
// @name:zh-HK Bilibili視頻詳情頁追加視頻封面鏈接
// @description:zh-HK 在Bilibili視頻詳情頁面顯示視頻封面圖片的直接鏈接。
// @name:zh-SG Bilibili视频详情页追加视频封面链接
// @description:zh-SG 在Bilibili视频详情页面显示视频封面图片的直接链接。
// @name:zh-TW Bilibili視頻詳情頁追加視頻封面鏈接
// @description:zh-TW 在Bilibili視頻詳情頁面顯示視頻封面圖片的直接鏈接。
// @namespace http://tampermonkey.net/
// @version 0.0.2
// @description 在视频详情页追加视频封面链接
// @description:en Add video cover link to video detail page
// @author aspen138
// @match *://www.bilibili.com/video/*
// @match *://www.bilibili.com/*
// @match *://www.bilibili.com
// @match *://search.bilibili.com/*
// @icon https://www.bilibili.com/favicon.ico
// @grant GM_registerMenuCommand
// @grant GM_unregisterMenuCommand
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_notification
// @grant GM_info
// @grant window.onurlchange
// @license MIT
// @sandbox JavaScript
// ==/UserScript==
// ↓↓↓↓↓↓↓↓↓模板,建议直接复制 //
// 自定义 urlchange 事件(用来监听 URL 变化)
function addUrlChangeEvent() {
history.pushState = ( f => function pushState(){
var ret = f.apply(this, arguments);
window.dispatchEvent(new Event('pushstate'));
window.dispatchEvent(new Event('urlchange'));
return ret;
})(history.pushState);
history.replaceState = ( f => function replaceState(){
var ret = f.apply(this, arguments);
window.dispatchEvent(new Event('replacestate'));
window.dispatchEvent(new Event('urlchange'));
return ret;
})(history.replaceState);
window.addEventListener('popstate',()=>{
window.dispatchEvent(new Event('urlchange'))
});
}
var menu_ALL = [
['menu_isEnableAppendCoverLink', '默认追加视频封面链接', '默认追加视频封面链接', false]
], menu_ID = [];
for (let i=0;i<menu_ALL.length;i++){ // 如果读取到的值为 null 就写入默认值
if (GM_getValue(menu_ALL[i][0]) == null){GM_setValue(menu_ALL[i][0], menu_ALL[i][3])};
}
// 注册(不可用)脚本菜单
function registerMenuCommand() {
if (menu_ID.length >= menu_ALL.length){ // 如果菜单ID数组长度大于等于菜单数组长度,说明不是首次添加菜单,需要卸载所有脚本菜单
for (let i=0;i<menu_ID.length;i++){
GM_unregisterMenuCommand(menu_ID[i]);
}
}
for (let i=0;i<menu_ALL.length;i++){ // 循环注册(不可用)脚本菜单
menu_ALL[i][3] = GM_getValue(menu_ALL[i][0]);
menu_ID[i] = GM_registerMenuCommand(`${menu_ALL[i][3]?'✅':'❌'} ${menu_ALL[i][1]}`, function(){menu_switch(`${menu_ALL[i][3]}`,`${menu_ALL[i][0]}`,`${menu_ALL[i][2]}`)});
}
}
// 菜单开关
function menu_switch(menu_status, Name, Tips) {
if (menu_status == 'true'){
GM_setValue(`${Name}`, false);
GM_notification({text: `已关闭 [${Tips}] 功能\n(点击刷新网页后生效)`, timeout: 3500, onclick: function(){location.reload();}});
}else{
GM_setValue(`${Name}`, true);
GM_notification({text: `已开启 [${Tips}] 功能\n(点击刷新网页后生效)`, timeout: 3500, onclick: function(){location.reload();}});
}
registerMenuCommand(); // 重新注册(不可用)脚本菜单
};
// 返回菜单值
function menu_value(menuName) {
for (let menu of menu_ALL) {
if (menu[0] == menuName) {
return menu[3]
}
}
}
for (let i=0;i<menu_ALL.length;i++){ // 如果读取到的值为 null 就写入默认值
if (GM_getValue(menu_ALL[i][0]) == null){GM_setValue(menu_ALL[i][0], menu_ALL[i][3])};
}
registerMenuCommand();
if (window.onurlchange === undefined) {addUrlChangeEvent();} // Tampermonkey v4.11 版本添加的 onurlchange 事件 grant,可以监控 pjax 等网页的 URL 变化
// ↑↑↑↑↑↑↑↑↑↑↑↑模板,建议直接复制 //
function appendCoverLink() {
let BVid = '';
const match1 = location.pathname.match(/BV[^/]*/);
if (match1) {
BVid = match1[0];
} else {
// console.error("No match found");
}
// Select the parent container where the new element will be appended
const parentContainer = document.querySelector(`.video-info-detail-list.video-info-detail-content:not(.${BVid}modified)`);
if (!parentContainer) {
console.warn('Parent container not found. Ensure the selector is correct.');
return;
}
// Ensure the link is only appended once
const existingCoverLink = parentContainer.querySelector(' .cover-link');
if (existingCoverLink) {
existingCoverLink.remove();
}
// Extract the cover image URL from the document head
const imageMetaTag = document.head.querySelector('[itemprop="image"]');
if (!imageMetaTag) {
console.warn('Image meta tag with [itemprop="image"] not found.');
return;
}
// Create the new container div with appropriate classes
const coverItem = document.createElement('div');
coverItem.classList.add('cover-item', 'item');
// Create the SVG icon (optional)
const coverIcon = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
coverIcon.setAttribute('class', 'cover-icon');
coverIcon.setAttribute('style', 'width:20px;height:20px;');
coverIcon.setAttribute('viewBox', '0 0 20 20');
coverIcon.setAttribute('width', '20');
coverIcon.setAttribute('height', '20');
// Example SVG path
const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
path.setAttribute('d', 'M10 0 L20 20 L0 20 Z');
path.setAttribute('fill', 'currentColor');
coverIcon.appendChild(path);
// Create the link element
const coverLink = document.createElement('a');
coverLink.setAttribute('target', '_blank');
coverLink.setAttribute('rel', 'noopener noreferrer');
coverLink.classList.add('cover-link');
coverLink.setAttribute('title', 'You may need F5 refresh to get consistent cover');
// Create the text node
const linkText = document.createElement('span');
linkText.classList.add('cover-text');
linkText.textContent = 'Cover';
// Assemble the link
coverLink.appendChild(coverIcon);
coverLink.appendChild(linkText);
// Append the link to the new item container
coverItem.appendChild(coverLink);
setTimeout(() => {
BVid = 'default';
const match = location.pathname.match(/BV[^/]*/);
if (match) {
BVid = match[0];
} else {
// console.error("No match found");
}
// Mark this container as modified
parentContainer.classList.add(BVid + 'modified');
// Replace 'http' with 'https' if necessary
const coverImgUrl =
'https://' + imageMetaTag.getAttribute('content').replace('http', 'https').split('@')[0];
console.log("coverImgUrl=", coverImgUrl);
coverLink.setAttribute('href', coverImgUrl);
// Ensure the link is only appended once
const existingCoverLink = parentContainer.querySelector('.cover-link');
if (existingCoverLink) {
existingCoverLink.remove();
}
parentContainer.append(coverLink);
console.log('Cover link appended successfully.');
}, 2*1000);
}
(function () {
// Retrieve the current setting, default to false
let isEnableAppendCoverLink = GM_getValue('menu_isEnableAppendCoverLink', false);
if (isEnableAppendCoverLink){
const checkIntervalAppendCoverLink = setInterval(()=>appendCoverLink(), 300);
// Cleanup after 30 seconds
setTimeout(() => {
clearInterval(checkIntervalAppendCoverLink);
observer.disconnect(); // Optionally disconnect the observer after cleanup
}, 30*1000);
}
})();