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, commentsTranslate: boolean,
copyFeedsLink: boolean, copyFeedsLink: boolean,
copyCommentLink: boolean, copyCommentLink: boolean,
unfoldFeeds: boolean,
feedsImageExporter: boolean,
latestVersionLink: string, latestVersionLink: string,
currentVersion: 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.css": "CAC8B0DBA8E90B38D31F0811B7B469709052204AC36D5D3F20FE5D0899FDDCCB",
"compact-layout.min.js": "B20609A7CBBDB1845FA0156FB5BE6B1E1A1B8B069EA85F65D16241DD2C12D738", "compact-layout.min.js": "B20609A7CBBDB1845FA0156FB5BE6B1E1A1B8B069EA85F65D16241DD2C12D738",
"copy-comment-link.min.js": "0488EB370A89B79F39120AB5EF7696F9FEF354E12724E7E41197AE8D1A9A6553", "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.css": "1981FD2BF3B17ECF33F98D5DEDAF0D32ACBE9532A51FDB70822286991AB98EF3",
"custom-control-background.min.js": "B9F366B251D1572ACE801FF6ECF15D4D1AAB3EA5A5B04B961B6F4310E2AB1A84", "custom-control-background.min.js": "B9F366B251D1572ACE801FF6ECF15D4D1AAB3EA5A5B04B961B6F4310E2AB1A84",
"custom-navbar.min.css": "96663A9903546E9E69A9E9BD619ED350E738D76DA7AB63C1DF5F1F878DAB272C", "custom-navbar.min.css": "96663A9903546E9E69A9E9BD619ED350E738D76DA7AB63C1DF5F1F878DAB272C",
@ -65,11 +65,13 @@
"danmaku-send-bar.min.css": "52149D46CF3A15CF6EB186CB49F298DE18B0723E52D8693228F66099847EDCCB", "danmaku-send-bar.min.css": "52149D46CF3A15CF6EB186CB49F298DE18B0723E52D8693228F66099847EDCCB",
"danmaku-send-bar.min.js": "7108E72F00C169F26E20409F4AA79F05553310375BABC392EB7E2166224B800A", "danmaku-send-bar.min.js": "7108E72F00C169F26E20409F4AA79F05553310375BABC392EB7E2166224B800A",
"dark.min.css": "7B31789D505589C30EA61AEA4789426D1444490E218CB2A9BFA251F224533A73", "dark.min.css": "7B31789D505589C30EA61AEA4789426D1444490E218CB2A9BFA251F224533A73",
"dark.user.css": "93B67B95FFDBD1FCF0ECD360662B215A8F9062970C3B73E3967798D261A49DAB", "dark.user.css": "F8534277BB8C9F1DCF8BE1E497CCAF2EE3A9CE15B5A46250F842D9DAEC06AD85",
"dark-color-scheme.min.js": "6D353AC738180317ACF9D2E046BFFBA0ACC0FA7A8D22B8E16C51127CDE4FE96B", "dark-color-scheme.min.js": "6D353AC738180317ACF9D2E046BFFBA0ACC0FA7A8D22B8E16C51127CDE4FE96B",
"dark-important.min.css": "546E3C3E0F181EB710CFE486A81CCB5CCC22D61E6C5D442182294CE03D8F4DDF", "dark-important.min.css": "546E3C3E0F181EB710CFE486A81CCB5CCC22D61E6C5D442182294CE03D8F4DDF",
"dark-navbar.min.css": "7FAD547E326B768904B3739C978DF86FFDDD5E5064FB8F490710D197CCC3018B", "dark-navbar.min.css": "7FAD547E326B768904B3739C978DF86FFDDD5E5064FB8F490710D197CCC3018B",
"dark-schedule.min.js": "853C446547603F4F0425F19F09F73335C9EC451A790C1C07E5E5B88A09E9B453", "dark-schedule.min.js": "853C446547603F4F0425F19F09F73335C9EC451A790C1C07E5E5B88A09E9B453",
"dark-slice-1.min.css": "E53F714FB59F62A80537B0715CE29CF0D9D1859CD217C15B647535A2679CE5ED",
"dark-slice-12.min.css": "24AC275EA1A0E74A928498974A0875584D345BE98214B10BFE324CF8CEBEE55B",
"dark-styles.min.js": "77F12E4F159C5139E1FC6030615F88EFD0A02443D34C3C4EDC3DB0B85F081631", "dark-styles.min.js": "77F12E4F159C5139E1FC6030615F88EFD0A02443D34C3C4EDC3DB0B85F081631",
"debounce.min.js": "54D33E1273C1F3FE19550BF1844339C3D54D6B01DF8A39C3162D95B93B079CFA", "debounce.min.js": "54D33E1273C1F3FE19550BF1844339C3D54D6B01DF8A39C3162D95B93B079CFA",
"default-danmaku-settings.min.css": "D9942B184FEDA7B08CFA0C34920E97D7A83B81762DCBF757642EBB60F95FF25D", "default-danmaku-settings.min.css": "D9942B184FEDA7B08CFA0C34920E97D7A83B81762DCBF757642EBB60F95FF25D",
@ -97,9 +99,10 @@
"extend-feeds-live.min.css": "BD0BF6E670808C940B3F3F7D8798B02676A58AA204449AAAE6E162D27EB8598A", "extend-feeds-live.min.css": "BD0BF6E670808C940B3F3F7D8798B02676A58AA204449AAAE6E162D27EB8598A",
"extend-feeds-live.min.js": "AC4C8018F5597536501DC8450F1572D6CF9D417229B84D2FE9A0F32E18942ED6", "extend-feeds-live.min.js": "AC4C8018F5597536501DC8450F1572D6CF9D417229B84D2FE9A0F32E18942ED6",
"favorites-redirect.min.js": "70D6ECCE0402AA76387D2A3288C1148C60CC88D5378B7A2BDC813F3F78E4EE84", "favorites-redirect.min.js": "70D6ECCE0402AA76387D2A3288C1148C60CC88D5378B7A2BDC813F3F78E4EE84",
"feeds-apis.min.js": "1FEB6EAFD6FA6948D6E51F29F5C7B91AF3ADB4C1F20654E9857405F4BD37B722", "feeds-apis.min.js": "0A70658F6A398AC5122C4328F69EC93FC122BD19319F89C3DECF60389DC68C30",
"feeds-filter.min.js": "9E8BC1A669D97992E888A3154FCE64912CE0B7A303D6BE40C3CA0C71A9533B96", "feeds-filter.min.js": "9E8BC1A669D97992E888A3154FCE64912CE0B7A303D6BE40C3CA0C71A9533B96",
"feeds-filter-card.vue.min.js": "D743BEF3C77C8A9AB8AA5DD24E8BD0A7AF7E638319137A103F3A9CF278F66CB4", "feeds-filter-card.vue.min.js": "D743BEF3C77C8A9AB8AA5DD24E8BD0A7AF7E638319137A103F3A9CF278F66CB4",
"feeds-image-exporter.min.js": "22A675B0FD8FFE49394686EDA2F871AE1BE91C856BA4116FE024B22244BBB721",
"feeds-translate.min.css": "54E959EC2AD38451B288E0DC669A269BF916CDB0F6B78497E2C315EB2811EF02", "feeds-translate.min.css": "54E959EC2AD38451B288E0DC669A269BF916CDB0F6B78497E2C315EB2811EF02",
"feeds-translate.min.js": "02C12D7A3382E185FF67C503C6287759ED18D2E9C838C2B94668953272BA09B1", "feeds-translate.min.js": "02C12D7A3382E185FF67C503C6287759ED18D2E9C838C2B94668953272BA09B1",
"feeds-translate-providers.min.js": "DD3B48C051ED1CD89AB79B2563723B2639D8737EF2194136D63ABAA8D4B64F6C", "feeds-translate-providers.min.js": "DD3B48C051ED1CD89AB79B2563723B2639D8737EF2194136D63ABAA8D4B64F6C",
@ -121,7 +124,7 @@
"gbk.min.js": "872D7A4562F13A539C4A9AF1825DE89E610C775F1AD026EA6986A80DD74E4B53", "gbk.min.js": "872D7A4562F13A539C4A9AF1825DE89E610C775F1AD026EA6986A80DD74E4B53",
"get-number.min.js": "7B81A45D0F7D7F68CC6EDE25E02FCD76F8A05B2CF29274F94A99980BB23D8B76", "get-number.min.js": "7B81A45D0F7D7F68CC6EDE25E02FCD76F8A05B2CF29274F94A99980BB23D8B76",
"gui-settings.min.css": "787D487EF39F28C18815C755BB4CA336355B84778861DCC70AA5AC998FDB9CC7", "gui-settings.min.css": "787D487EF39F28C18815C755BB4CA336355B84778861DCC70AA5AC998FDB9CC7",
"gui-settings.min.html": "D223BF882657CA6DE6B1D637687D2EB4F1ABC201423505C92C015C6FCF426FF1", "gui-settings.min.html": "25C8D590B625A310CEE53F685286B252C7DB0B7EE8FB294B8A7A3C9A8E1D1170",
"gui-settings.min.js": "A092181BBAF56712E2F62B46552DE97D582FB1891B626D9D55276E577BC44D7A", "gui-settings.min.js": "A092181BBAF56712E2F62B46552DE97D582FB1891B626D9D55276E577BC44D7A",
"haruna-scale.min.js": "FD079C86741F614FD63E733CF9B7B896A7D20110754815A540F3341D4DC252B2", "haruna-scale.min.js": "FD079C86741F614FD63E733CF9B7B896A7D20110754815A540F3341D4DC252B2",
"hide-bangumi-reviews.min.js": "7265C0CE994FDA926355A9A4F9B207E7E68E749D47332F3C537EF93E97927A23", "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 @namespace Bilibili-Evolved
@homepageURL https://github.com/the1812/Bilibili-Evolved @homepageURL https://github.com/the1812/Bilibili-Evolved
@updateURL https://cdn.jsdelivr.net/gh/the1812/Bilibili-Evolved@preview/min/dark.user.css @updateURL https://cdn.jsdelivr.net/gh/the1812/Bilibili-Evolved@preview/min/dark.user.css
@version 823.28.0 @version 824.25.0
@license MIT @license MIT
@author Grant Howard (https://github.com/the1812), Coulomb-G (https://github.com/Coulomb-G) @author Grant Howard (https://github.com/the1812), Coulomb-G (https://github.com/Coulomb-G)
==/UserStyle== */ ==/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))) { if (urlList.every(url => !document.URL.includes(url))) {
return return
} }
const { forEachFeedsCard } = await import('../feeds-apis') const { forEachFeedsCard, addMenuItem } = await import('../feeds-apis')
const addCopyLinkButton = (card: FeedsCard) => { const addCopyLinkButton = (card: FeedsCard) => {
const morePanel = dq(card.element, '.more-panel') as HTMLElement addMenuItem(card, {
if (!morePanel || dq(morePanel, '.copy-link')) { className: 'copy-link',
return text: '复制链接',
} action: () => {
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', () => {
GM_setClipboard(`https://t.bilibili.com/${card.id}`, { mimetype: 'text/plain' }) GM_setClipboard(`https://t.bilibili.com/${card.id}`, { mimetype: 'text/plain' })
card.element.click() }
}) })
morePanel.appendChild(copyLinkButton)
} }
forEachFeedsCard({ forEachFeedsCard({
added: addCopyLinkButton, added: addCopyLinkButton,

View File

@ -404,7 +404,16 @@ export const getVideoFeeds = async (type: 'video' | 'bangumi' = 'video'): Promis
} }
export const forEachFeedsCard = (callback: FeedsCardCallback) => { 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() const success = await feedsCardsManager.startWatching()
if (!success) { if (!success) {
console.error('feedsCardsManager.startWatching() failed') console.error('feedsCardsManager.startWatching() failed')
@ -419,6 +428,34 @@ export const forEachFeedsCard = (callback: FeedsCardCallback) => {
feedsCardCallbacks.push({ added: none, removed: none, ...callback }) 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 default {
export: { export: {
@ -426,5 +463,6 @@ export default {
feedsCardTypes, feedsCardTypes,
getVideoFeeds, getVideoFeeds,
forEachFeedsCard, 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: '动态反折叠', unfoldFeeds: '动态反折叠',
}, },
}, },
feedsImageExporter: {
path: 'feeds-image-exporter.min.js',
displayNames: {
feedsImageExporter: '动态图片导出',
},
},
} }
export const resourceManifest = Resource.manifest export const resourceManifest = Resource.manifest

View File

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

View File

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