今日阅读:
- EDATE函数,月份计算,会计一定要学会
小结:在搞 Apple one 家庭计划收租表格的时候突然想到,我用日期加 90 天来计算一个季度的方式并不准确,查了一下果然有专门处理月份增加的函数。 - 抗战·装备①|日军手枪为何叫王八盒子?三八大盖又是什么?
小结:南部十四年式8毫米半自动手枪原来还是改进版,那没改进的版本不得烂死了。 - AZPR EvBoard
小结:可爱的基板少女,好想要自己弄一个。这个作者一开始我以为只是普通的同人作者,结果他们出的书还在人民邮电出过中文版《CPU自制入门》。
今日软件:
- whereto.stream
小结:统计各大影视在各大流媒体平台不同地区的上架情况,有点像一个影视圈的 steamdb,意外的有用。
今日代码:
提取会员表情的脚本。
难点在于会员表情的信息不在任何一个官方 Api 中,而是在网页里的 ytInitialData 这个由 JS
动态获取的返回值里。且不同的频道对应的层级关系还不一样。我针对 Hololive 的会员频道写了一版,不太满意。后续还是得从 JS 逆向那边去入手。
// ==UserScript==
// @name 下载Youtube会员表情贴纸
// @namespace http://tampermonkey.net/
// @version 1.2
// @description Add a floating button to trigger custom emoji downloads from YouTube
// @author NBXX
// @match *://*.youtube.com/*
// @grant GM_download
// ==/UserScript==
(function() {
'use strict';
function extractEmojis() {
const scripts = Array.from(document.getElementsByTagName('script'));
const targetScript = scripts.find(s => s.textContent.includes('ytInitialData'));
if (!targetScript) {
console.error('ytInitialData not found.');
return [];
}
const ytInitialDataMatch = /var ytInitialData = (.*);<\/script>/.exec(targetScript.outerHTML);
if (!ytInitialDataMatch || !ytInitialDataMatch[1]) {
console.error('Failed to extract ytInitialData.');
return [];
}
let ytData;
try {
ytData = JSON.parse(ytInitialDataMatch[1]);
} catch (e) {
console.error('Error parsing ytInitialData:', e);
return [];
}
try {
const perkRenderer = ytData['contents']['twoColumnBrowseResultsRenderer']['tabs'][7]['tabRenderer']['content']['sectionListRenderer']['contents'][0]['sponsorshipsExpandablePerksRenderer']['expandableItems'][1]['sponsorshipsPerkRenderer'];
const images = perkRenderer.images || [];
return images.map(image => ({
url: image.thumbnails[0].url.split('=w')[0],
label: image.accessibility.accessibilityData.label.replace(/[^\w]/g, '_') + '.png'
}));
} catch (error) {
console.error('Error navigating data structure:', error);
return [];
}
}
function createFloatingButton() {
const button = document.createElement('button');
button.textContent = '⬇️'; // Using a simple emoji as the download icon
button.style = `position: fixed; right: 20px; top: 50%; transform: translateY(-50%); font-size: 24px; border: none;
border-radius: 50%; width: 50px; height: 50px; background-color: #ff0000; color: white; cursor: pointer;
box-shadow: 0 4px 8px rgba(0,0,0,0.2); z-index: 1000;`;
document.body.appendChild(button);
button.addEventListener('click', function() {
const images = extractEmojis();
if (images.length === 0) {
console.log('No images available for download.');
return;
}
images.forEach(img => {
GM_download(img.url, img.label);
});
});
}
window.addEventListener('load', createFloatingButton);
})();
今日见闻:
动漫流媒体 Crunchyroll 订阅价格即将上涨。
今日废话:
原来 cloudflare 的 pages 最大支持 25mb 的存储,那我的博客岂不是快要到极限了?
需要尽快将图片转存 R2 存储桶。