今日阅读:

今日软件:

  • 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 存储桶。