Displays bundle ID & full metadata on App Store pages with advanced features, copy/export buttons, and improved UI
// ==UserScript==
// @name App Store Metadata Viewer
// @namespace http://tampermonkey.net/
// @version 2.3
// @description Displays bundle ID & full metadata on App Store pages with advanced features, copy/export buttons, and improved UI
// @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
// @icon https://www.google.com/s2/favicons?sz=64&domain=apple.com
// ==/UserScript==
(function () {
'use strict';
const settings = {
displayMode: 'overlay',
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('[Metadata 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('[Metadata 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: 'Segoe UI', Roboto, monospace; font-size: 13px; line-height: 1.8;">
<div style="margin-bottom: 8px;"><strong>📦 Bundle ID:</strong> ${bundleId}</div>
<div><strong>👨💻 Developer:</strong> ${sellerName}</div>
<div><strong>📱 Version:</strong> ${version}</div>
<div><strong>📅 Release Date:</strong> ${releaseDate?.split('T')[0]}</div>
<div><strong>📂 Size:</strong> ${formatBytes(fileSizeBytes)}</div>
<div><strong>🧭 Min OS:</strong> ${minimumOsVersion || 'N/A'}</div>
<div><strong>🗂 Genre:</strong> ${primaryGenreName}</div>
<div><strong>🌍 Country:</strong> ${country}</div>
<div style="margin-bottom: 12px;"><strong>🗣️ Language(s):</strong> ${lang}</div>
<div style="display: flex; gap: 10px; flex-wrap: wrap;">
<button id="copyBtn" style="background:#1E88E5;color:white;border:none;padding:6px 10px;border-radius:5px;cursor:pointer;">📋 Copy Bundle ID</button>
<button id="jsonBtn" style="background:#333;color:white;border:none;padding:6px 10px;border-radius:5px;cursor:pointer;">🗄 Export JSON</button>
<a href="${trackViewUrl}" target="_blank" style="color:#42A5F5;text-decoration:none;padding:6px 10px;border-radius:5px;background:#222;display:inline-block;">🔗 View on App Store</a>
</div>
</div>
`;
const container = document.createElement('div');
container.innerHTML = infoHTML;
if (settings.displayMode === 'inline') {
container.style.background = '#f9f9f9';
container.style.padding = '14px';
container.style.border = '1px solid #ccc';
container.style.borderRadius = '8px';
container.style.marginTop = '20px';
const target = document.querySelector('h1') || document.body;
target.parentElement.insertBefore(container, target.nextSibling);
} else {
Object.assign(container.style, {
position: 'fixed',
bottom: '20px',
right: '20px',
background: '#111',
color: '#fff',
padding: '16px',
borderRadius: '10px',
boxShadow: '0 0 16px rgba(0,0,0,0.6)',
zIndex: 99999,
maxWidth: '300px',
opacity: '85%'
});
document.body.appendChild(container);
}
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, name: `bundleinfo-${bundleId}.json` });
};
if (settings.autoCopy) {
(typeof GM_setClipboard === 'function' ? GM_setClipboard : navigator.clipboard.writeText)(bundleId);
}
}
window.addEventListener('load', waitForAppId);
})();