Add feeds image exporter

This commit is contained in:
the1812 2020-10-17 21:47:12 +08:00
parent 84a47147d4
commit d1d024b319
18 changed files with 176 additions and 47 deletions

View File

@ -498,6 +498,8 @@ declare global {
commentsTranslate: boolean,
copyFeedsLink: boolean,
copyCommentLink: boolean,
unfoldFeeds: boolean,
feedsImageExporter: boolean,
latestVersionLink: string,
currentVersion: string,
}

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

@ -37,7 +37,7 @@
"compact-layout.min.css": "CAC8B0DBA8E90B38D31F0811B7B469709052204AC36D5D3F20FE5D0899FDDCCB",
"compact-layout.min.js": "B20609A7CBBDB1845FA0156FB5BE6B1E1A1B8B069EA85F65D16241DD2C12D738",
"copy-comment-link.min.js": "0488EB370A89B79F39120AB5EF7696F9FEF354E12724E7E41197AE8D1A9A6553",
"copy-feeds-link.min.js": "579381349AA2A54A7B8A9AA67A7D35E83DBB456E0A73081E80D4C8DB404604A1",
"copy-feeds-link.min.js": "E13B09593E00520336D9DB97383437ACE66F8E49CD408E336A536C22538EA98A",
"custom-control-background.min.css": "1981FD2BF3B17ECF33F98D5DEDAF0D32ACBE9532A51FDB70822286991AB98EF3",
"custom-control-background.min.js": "B9F366B251D1572ACE801FF6ECF15D4D1AAB3EA5A5B04B961B6F4310E2AB1A84",
"custom-navbar.min.css": "96663A9903546E9E69A9E9BD619ED350E738D76DA7AB63C1DF5F1F878DAB272C",
@ -65,11 +65,13 @@
"danmaku-send-bar.min.css": "52149D46CF3A15CF6EB186CB49F298DE18B0723E52D8693228F66099847EDCCB",
"danmaku-send-bar.min.js": "7108E72F00C169F26E20409F4AA79F05553310375BABC392EB7E2166224B800A",
"dark.min.css": "7B31789D505589C30EA61AEA4789426D1444490E218CB2A9BFA251F224533A73",
"dark.user.css": "93B67B95FFDBD1FCF0ECD360662B215A8F9062970C3B73E3967798D261A49DAB",
"dark.user.css": "F8534277BB8C9F1DCF8BE1E497CCAF2EE3A9CE15B5A46250F842D9DAEC06AD85",
"dark-color-scheme.min.js": "6D353AC738180317ACF9D2E046BFFBA0ACC0FA7A8D22B8E16C51127CDE4FE96B",
"dark-important.min.css": "546E3C3E0F181EB710CFE486A81CCB5CCC22D61E6C5D442182294CE03D8F4DDF",
"dark-navbar.min.css": "7FAD547E326B768904B3739C978DF86FFDDD5E5064FB8F490710D197CCC3018B",
"dark-schedule.min.js": "853C446547603F4F0425F19F09F73335C9EC451A790C1C07E5E5B88A09E9B453",
"dark-slice-1.min.css": "E53F714FB59F62A80537B0715CE29CF0D9D1859CD217C15B647535A2679CE5ED",
"dark-slice-12.min.css": "24AC275EA1A0E74A928498974A0875584D345BE98214B10BFE324CF8CEBEE55B",
"dark-styles.min.js": "77F12E4F159C5139E1FC6030615F88EFD0A02443D34C3C4EDC3DB0B85F081631",
"debounce.min.js": "54D33E1273C1F3FE19550BF1844339C3D54D6B01DF8A39C3162D95B93B079CFA",
"default-danmaku-settings.min.css": "D9942B184FEDA7B08CFA0C34920E97D7A83B81762DCBF757642EBB60F95FF25D",
@ -97,9 +99,10 @@
"extend-feeds-live.min.css": "BD0BF6E670808C940B3F3F7D8798B02676A58AA204449AAAE6E162D27EB8598A",
"extend-feeds-live.min.js": "AC4C8018F5597536501DC8450F1572D6CF9D417229B84D2FE9A0F32E18942ED6",
"favorites-redirect.min.js": "70D6ECCE0402AA76387D2A3288C1148C60CC88D5378B7A2BDC813F3F78E4EE84",
"feeds-apis.min.js": "1FEB6EAFD6FA6948D6E51F29F5C7B91AF3ADB4C1F20654E9857405F4BD37B722",
"feeds-apis.min.js": "0A70658F6A398AC5122C4328F69EC93FC122BD19319F89C3DECF60389DC68C30",
"feeds-filter.min.js": "9E8BC1A669D97992E888A3154FCE64912CE0B7A303D6BE40C3CA0C71A9533B96",
"feeds-filter-card.vue.min.js": "D743BEF3C77C8A9AB8AA5DD24E8BD0A7AF7E638319137A103F3A9CF278F66CB4",
"feeds-image-exporter.min.js": "22A675B0FD8FFE49394686EDA2F871AE1BE91C856BA4116FE024B22244BBB721",
"feeds-translate.min.css": "54E959EC2AD38451B288E0DC669A269BF916CDB0F6B78497E2C315EB2811EF02",
"feeds-translate.min.js": "02C12D7A3382E185FF67C503C6287759ED18D2E9C838C2B94668953272BA09B1",
"feeds-translate-providers.min.js": "DD3B48C051ED1CD89AB79B2563723B2639D8737EF2194136D63ABAA8D4B64F6C",
@ -121,7 +124,7 @@
"gbk.min.js": "872D7A4562F13A539C4A9AF1825DE89E610C775F1AD026EA6986A80DD74E4B53",
"get-number.min.js": "7B81A45D0F7D7F68CC6EDE25E02FCD76F8A05B2CF29274F94A99980BB23D8B76",
"gui-settings.min.css": "787D487EF39F28C18815C755BB4CA336355B84778861DCC70AA5AC998FDB9CC7",
"gui-settings.min.html": "D223BF882657CA6DE6B1D637687D2EB4F1ABC201423505C92C015C6FCF426FF1",
"gui-settings.min.html": "25C8D590B625A310CEE53F685286B252C7DB0B7EE8FB294B8A7A3C9A8E1D1170",
"gui-settings.min.js": "A092181BBAF56712E2F62B46552DE97D582FB1891B626D9D55276E577BC44D7A",
"haruna-scale.min.js": "FD079C86741F614FD63E733CF9B7B896A7D20110754815A540F3341D4DC252B2",
"hide-bangumi-reviews.min.js": "7265C0CE994FDA926355A9A4F9B207E7E68E749D47332F3C537EF93E97927A23",

Binary file not shown.

View File

@ -1 +1 @@
(()=>{return(t,e)=>{(async()=>{const t=["https://t.bilibili.com/","https://space.bilibili.com/","https://live.bilibili.com/"];if(t.every(t=>!document.URL.includes(t))){return}const{forEachFeedsCard:i}=await e.importAsync("feeds-apis");const n=t=>{const e=dq(t.element,".more-panel");if(!e||dq(e,".copy-link")){return}const i=document.createElement("p");i.classList.add("child-button","c-pointer","copy-link");i.textContent="复制链接";const n=[...new Set([...e.children].map(t=>{return t.getAttributeNames().filter(t=>t.startsWith("data-v-"))}).flat())];n.forEach(t=>i.setAttribute(t,""));i.addEventListener("click",()=>{GM_setClipboard(`https://t.bilibili.com/${t.id}`,{mimetype:"text/plain"});t.element.click()});e.appendChild(i)};i({added:n})})()}})();
(()=>{return(i,t)=>{(async()=>{const i=["https://t.bilibili.com/","https://space.bilibili.com/","https://live.bilibili.com/"];if(i.every(i=>!document.URL.includes(i))){return}const{forEachFeedsCard:e,addMenuItem:s}=await t.importAsync("feeds-apis");const c=i=>{s(i,{className:"copy-link",text:"复制链接",action:()=>{GM_setClipboard(`https://t.bilibili.com/${i.id}`,{mimetype:"text/plain"})}})};e({added:c})})()}})();

View File

@ -3,7 +3,7 @@
@namespace Bilibili-Evolved
@homepageURL https://github.com/the1812/Bilibili-Evolved
@updateURL https://cdn.jsdelivr.net/gh/the1812/Bilibili-Evolved@preview/min/dark.user.css
@version 823.28.0
@version 824.25.0
@license MIT
@author Grant Howard (https://github.com/the1812), Coulomb-G (https://github.com/Coulomb-G)
==/UserStyle== */

File diff suppressed because one or more lines are too long

1
min/feeds-image-exporter.min.js vendored Normal file
View File

@ -0,0 +1 @@
(()=>{return(e,a)=>{(async()=>{const{forEachFeedsCard:e,addMenuItem:t}=await a.importAsync("feeds-apis");const n=e=>{t(e,{className:"image-export",text:"导出图片",action:async()=>{const t=[];dqa(e.element,"img, .img-content").forEach(e=>{let a;if(e instanceof HTMLImageElement){a=e.src}else{const t=e.style.backgroundImage;if(!t){return}const n=t.match(/url\("(.+)"\)/);if(!n){return}a=n[1]}const n=a.match(/^(.+)(\..+?)(@.+)$/);if(!n){return}t.push({url:n[1]+n[2],extension:n[2]})});const{DownloadVideoPackage:n}=await a.importAsync("download-video-package");const o=await Promise.all(t.map(({url:e})=>Ajax.getBlob(e)));const s=new n;o.forEach((a,n)=>s.add(`${e.username}-${e.id}-${n}${t[n].extension}`,a));await s.emit(`${e.username}-${e.id}.zip`)}})};e({added:n})})()}})();

File diff suppressed because one or more lines are too long

View File

@ -9,24 +9,15 @@ import { FeedsCard } from '../feeds-apis'
if (urlList.every(url => !document.URL.includes(url))) {
return
}
const { forEachFeedsCard } = await import('../feeds-apis')
const { forEachFeedsCard, addMenuItem } = await import('../feeds-apis')
const addCopyLinkButton = (card: FeedsCard) => {
const morePanel = dq(card.element, '.more-panel') as HTMLElement
if (!morePanel || dq(morePanel, '.copy-link')) {
return
}
const copyLinkButton = document.createElement('p')
copyLinkButton.classList.add('child-button', 'c-pointer', 'copy-link')
copyLinkButton.textContent = '复制链接'
const vueScopeAttributes = [...new Set([...morePanel.children].map((element: HTMLElement) => {
return element.getAttributeNames().filter(it => it.startsWith('data-v-'))
}).flat())]
vueScopeAttributes.forEach(attr => copyLinkButton.setAttribute(attr, ''))
copyLinkButton.addEventListener('click', () => {
addMenuItem(card, {
className: 'copy-link',
text: '复制链接',
action: () => {
GM_setClipboard(`https://t.bilibili.com/${card.id}`, { mimetype: 'text/plain' })
card.element.click()
}
})
morePanel.appendChild(copyLinkButton)
}
forEachFeedsCard({
added: addCopyLinkButton,

View File

@ -404,7 +404,16 @@ export const getVideoFeeds = async (type: 'video' | 'bangumi' = 'video'): Promis
}
export const forEachFeedsCard = (callback: FeedsCardCallback) => {
(async () => {
const feedsUrls = [
/^https:\/\/t\.bilibili\.com\/$/,
/^https:\/\/space\.bilibili\.com\//,
/^https:\/\/live\.bilibili\.com\/(blanc\/)?[\d]+/,
/^https:\/\/t\.bilibili\.com\//,
]
if (feedsUrls.every(url => !url.test(document.URL))) {
return
}
;(async () => {
const success = await feedsCardsManager.startWatching()
if (!success) {
console.error('feedsCardsManager.startWatching() failed')
@ -419,6 +428,34 @@ export const forEachFeedsCard = (callback: FeedsCardCallback) => {
feedsCardCallbacks.push({ added: none, removed: none, ...callback })
})()
}
/**
*
* @param card
* @param config
*/
export const addMenuItem = (card: FeedsCard, config: {
className: string
text: string
action: (e: MouseEvent) => void
}) => {
const morePanel = dq(card.element, '.more-panel') as HTMLElement
const { className, text, action } = config
if (!morePanel || dq(morePanel, `.${className}`)) {
return
}
const menuItem = document.createElement('p')
menuItem.classList.add('child-button', 'c-pointer', className)
menuItem.textContent = text
const vueScopeAttributes = [...new Set([...morePanel.children].map((element: HTMLElement) => {
return element.getAttributeNames().filter(it => it.startsWith('data-v-'))
}).flat())]
vueScopeAttributes.forEach(attr => menuItem.setAttribute(attr, ''))
menuItem.addEventListener('click', e => {
action(e)
card.element.click()
})
morePanel.appendChild(menuItem)
}
export default {
export: {
@ -426,5 +463,6 @@ export default {
feedsCardTypes,
getVideoFeeds,
forEachFeedsCard,
addMenuItem,
},
}

View File

@ -0,0 +1,46 @@
import { FeedsCard } from './feeds-apis'
(async () => {
const { forEachFeedsCard, addMenuItem } = await import('./feeds-apis')
const addExportButton = (card: FeedsCard) => {
addMenuItem(card, {
className: 'image-export',
text: '导出图片',
action: async () => {
const imageUrls: { url: string; extension: string }[] = []
dqa(card.element, 'img, .img-content').forEach((img: HTMLImageElement | HTMLDivElement) => {
let url: string
if (img instanceof HTMLImageElement) {
url = img.src
} else {
const backgroundImage = img.style.backgroundImage
if (!backgroundImage) {
return
}
const match = backgroundImage.match(/url\("(.+)"\)/)
if (!match) {
return
}
url = match[1]
}
const thumbMatch = url.match(/^(.+)(\..+?)(@.+)$/)
if (!thumbMatch) {
return
}
imageUrls.push({
url: thumbMatch[1] + thumbMatch[2],
extension: thumbMatch[2],
})
})
const { DownloadVideoPackage } = await import('../video/download-video/download-video-package')
const imageBlobs = await Promise.all(imageUrls.map(({ url }) => Ajax.getBlob(url)))
const pack = new DownloadVideoPackage()
imageBlobs.forEach((blob, index) => pack.add(`${card.username}-${card.id}-${index}${imageUrls[index].extension}`, blob))
await pack.emit(`${card.username}-${card.id}.zip`)
},
})
}
forEachFeedsCard({
added: addExportButton,
})
})()

View File

@ -967,5 +967,11 @@ Resource.manifest = {
unfoldFeeds: '动态反折叠',
},
},
feedsImageExporter: {
path: 'feeds-image-exporter.min.js',
displayNames: {
feedsImageExporter: '动态图片导出',
},
},
}
export const resourceManifest = Resource.manifest

View File

@ -246,6 +246,7 @@ export const settings = {
copyFeedsLink: false,
copyCommentLink: false,
unfoldFeeds: true,
feedsImageExporter: false,
cache: {},
}
const fixedSettings = {

View File

@ -131,6 +131,7 @@
<checkbox indent="0" key="disableFeedsDetails" dependencies=""></checkbox>
<checkbox indent="0" key="copyFeedsLink" dependencies=""></checkbox>
<checkbox indent="0" key="unfoldFeeds" dependencies=""></checkbox>
<checkbox indent="0" key="feedsImageExporter" dependencies=""></checkbox>
<category icon="video-outline">直播</category>
<checkbox indent="0" key="useDefaultLiveQuality" dependencies=""></checkbox>
<dropdown indent="1" key="defaultLiveQuality" dependencies="useDefaultLiveQuality"></dropdown>