App Store BundleID Viewer

Displays bundle ID & full metadata on App Store pages with advanced features and copy/export buttons

Versión del día 23/05/2025. Echa un vistazo a la versión más reciente.

// ==UserScript==
// @name         App Store BundleID Viewer
// @namespace    http://tampermonkey.net/
// @version      2.0
// @description  Displays bundle ID & full metadata on App Store pages with advanced features and copy/export buttons
// @author       sharmanhall
// @match        https://apps.apple.com/*/app/*/id*
// @match        https://apps.apple.com/app/id*
// @grant        GM_setClipboard
// @grant        GM_download
// @grant        GM_registerMenuCommand
// @license      MIT
// ==/UserScript==

(function () {
    'use strict';

    const settings = {
        displayMode: 'overlay', // or 'inline'
        autoCopy: false
    };

    GM_registerMenuCommand('Toggle Display Mode', () => {
        settings.displayMode = settings.displayMode === 'overlay' ? 'inline' : 'overlay';
        alert(`Switched to: ${settings.displayMode}`);
        location.reload();
    });

    GM_registerMenuCommand('Toggle Auto-Copy', () => {
        settings.autoCopy = !settings.autoCopy;
        alert(`Auto-copy is now ${settings.autoCopy ? 'enabled' : 'disabled'}`);
    });

    function waitForAppId(attempt = 0) {
        const appIdMatch = window.location.href.match(/id(\d+)/);
        if (!appIdMatch && attempt < 10) {
            return setTimeout(() => waitForAppId(attempt + 1), 1000);
        } else if (!appIdMatch) {
            console.error('[BundleID Viewer] App ID not found.');
            return;
        }
        fetchAppData(appIdMatch[1]);
    }

    async function fetchAppData(appId) {
        const lookupUrl = `https://itunes.apple.com/lookup?id=${appId}`;
        try {
            const res = await fetch(lookupUrl);
            const data = await res.json();
            if (!data.results || !data.results.length) throw 'No results';
            showInfo(data.results[0]);
        } catch (err) {
            console.error('[BundleID Viewer] Failed to fetch metadata:', err);
        }
    }

    function formatBytes(bytes) {
        const kb = bytes / 1024;
        if (kb < 1024) return `${Math.round(kb)} KB`;
        return `${(kb / 1024).toFixed(1)} MB`;
    }

    function showInfo(app) {
        const {
            bundleId,
            version,
            minimumOsVersion,
            releaseDate,
            primaryGenreName,
            languageCodesISO2A,
            fileSizeBytes,
            sellerName,
            trackViewUrl
        } = app;

        const country = new URL(window.location.href).pathname.split('/')[1].toUpperCase();
        const lang = languageCodesISO2A?.join(', ') || 'N/A';

        const infoHTML = `
            <div id="bundle-id-widget" style="font-family: monospace; font-size: 13px; line-height: 1.6;">
                <strong>📦 Bundle ID:</strong> ${bundleId}<br>
                <strong>🧑‍💻 Developer:</strong> ${sellerName}<br>
                <strong>📱 Version:</strong> ${version}<br>
                <strong>📆 Release Date:</strong> ${releaseDate?.split('T')[0]}<br>
                <strong>📂 Size:</strong> ${formatBytes(fileSizeBytes)}<br>
                <strong>🧭 Min OS:</strong> ${minimumOsVersion || 'N/A'}<br>
                <strong>🗂 Genre:</strong> ${primaryGenreName}<br>
                <strong>🌍 Country:</strong> ${country}<br>
                <strong>🌐 Language(s):</strong> ${lang}<br><br>
                <button id="copyBtn">📋 Copy Bundle ID</button>
                <button id="jsonBtn">🗄 Export JSON</button>
                <a href="${trackViewUrl}" target="_blank">🔗 View on App Store</a>
            </div>
        `;

        if (settings.displayMode === 'inline') {
            const target = document.querySelector('h1') || document.body;
            const container = document.createElement('div');
            container.innerHTML = infoHTML;
            container.style.background = '#f5f5f7';
            container.style.padding = '12px';
            container.style.marginTop = '20px';
            container.style.border = '1px solid #ccc';
            container.style.borderRadius = '6px';
            target.parentElement.insertBefore(container, target.nextSibling);
        } else {
            const overlay = document.createElement('div');
            overlay.innerHTML = infoHTML;
            Object.assign(overlay.style, {
                position: 'fixed',
                bottom: '20px',
                right: '20px',
                background: '#000',
                color: '#fff',
                padding: '12px',
                borderRadius: '8px',
                zIndex: 9999,
                boxShadow: '0 0 12px rgba(0,0,0,0.5)'
            });
            document.body.appendChild(overlay);
        }

        const copyBtn = document.getElementById('copyBtn');
        copyBtn.onclick = () => {
            (typeof GM_setClipboard === 'function' ? GM_setClipboard : navigator.clipboard.writeText)(bundleId);
            copyBtn.textContent = '✅ Copied!';
            setTimeout(() => (copyBtn.textContent = '📋 Copy Bundle ID'), 1500);
        };

        const jsonBtn = document.getElementById('jsonBtn');
        jsonBtn.onclick = () => {
            const blob = new Blob([JSON.stringify(app, null, 2)], { type: 'application/json' });
            const url = URL.createObjectURL(blob);
            GM_download({
                url: url,
                name: `bundleinfo-${bundleId}.json`
            });
        };

        if (settings.autoCopy) {
            (typeof GM_setClipboard === 'function' ? GM_setClipboard : navigator.clipboard.writeText)(bundleId);
        }
    }

    window.addEventListener('load', waitForAppId);
})();
长期地址
遇到问题?请前往 GitHub 提 Issues。