Shotdeck bookmarklet

Bookmarklet Maker

javascript:(function(){
    const slugify = (str) => {
        return str.toString().toLowerCase()
            .replace(/\s+/g, '_')
            .replace(/[^\w-]+/g, '')
            .replace(/_{2,}/g, '_')
            .replace(/^_+|_+$/g, '');
    };

    const rgbToHex = (rgb) => {
        const match = rgb.match(/^rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*\d+\.?\d*)?\)$/);
        if (!match) return rgb;
        const hex = (x) => parseInt(x).toString(16).padStart(2, '0');
        return '#' + hex(match[1]) + hex(match[2]) + hex(match[3]);
    };

    const extractDetails = (html) => {
        const parser = new DOMParser();
        const doc = parser.parseFromString(html, 'text/html');
        const details = {};
        
        // Extract standard details as arrays
        doc.querySelectorAll('.detail-group').forEach(group => {
            const key = group.querySelector('.detail-type').textContent
                .replace(':', '').trim().toLowerCase()
                .replace(/\//g, '_')
                .replace(/\W+/g, '_');
            
            const values = Array.from(group.querySelectorAll('.details a, .details span'))
                .map(el => el.textContent.trim())
                .filter(t => t.length > 0);

            if (values.length > 0) details[key] = values;
        });

        // Extract filming location as string
        const fullLocation = doc.querySelector('.full_filming_location');
        details.filming_location = fullLocation ? 
            fullLocation.textContent.trim().replace(/\s+/g, ' ') : 
            doc.querySelector('.short_filming_location').textContent.trim();

        // Extract color palette as array
        details.color_palette = Array.from(doc.querySelectorAll('.palette a'))
            .map(a => rgbToHex(a.style.backgroundColor).toUpperCase());

        return details;
    };

    const handleClick = async (event) => {
        event.preventDefault();
        event.stopPropagation();
        
        const entry = event.currentTarget;
        const shotId = entry.dataset.shotid;
        
        const titleElement = entry.querySelector('.gallerytitle a');
        const title = titleElement?.textContent.trim() || 'Untitled';
        const year = entry.dataset.titleyear || 'unknown_year';
        const filename = entry.querySelector('a.gallerythumb')?.dataset.filename.split('.')[0] || 'unknown';

        try {
            const response = await fetch(`/browse/shotdetailsajax/image/${shotId}/deck/0`);
            const html = await response.text();
            const details = extractDetails(html);
            
            const result = {
                title: title,
                year: year,
                status: entry.dataset.titleContentStatus,
                filename: filename,
                ...details
            };

            const slugTitle = slugify(title);
            const slugYear = slugify(year);
            const slugFilename = slugify(filename);
            const finalFilename = `${slugYear}_${slugTitle}_${slugFilename}.jsonld`;
            
            const blob = new Blob([JSON.stringify(result, null, 2)], { type: 'application/ld+json' });
            const url = URL.createObjectURL(blob);
            const a = document.createElement('a');
            a.href = url;
            a.download = finalFilename;
            document.body.appendChild(a);
            a.click();
            document.body.removeChild(a);
            URL.revokeObjectURL(url);
        } catch (error) {
            console.error('Error processing entry:', error);
        }
    };

    const activateListeners = () => {
        document.querySelectorAll('.jg-entry.outerimage:not(.bookmarklet-processed)').forEach(entry => {
            entry.classList.add('bookmarklet-processed');
            entry.style.cssText = 'cursor: pointer; outline: 2px solid rgba(0,150,255,0.3); transition: transform 0.2s;';
            entry.addEventListener('click', handleClick);
            entry.addEventListener('mouseover', () => entry.style.transform = 'scale(1.02)');
            entry.addEventListener('mouseout', () => entry.style.transform = 'scale(1)');
        });
    };

    // Add keyboard shortcut (press 'a' to activate listeners)
    document.addEventListener('keydown', (e) => {
        if (e.key.toLowerCase() === 'a') {
            activateListeners();
            // Show temporary notification
            const notification = document.createElement('div');
            notification.style = 'position: fixed; top: 20px; left: 50%; transform: translateX(-50%); padding: 10px 20px; background: #2196F3; color: white; border-radius: 5px; z-index: 9999;';
            notification.textContent = 'Click listeners activated! Click any image to download.';
            document.body.appendChild(notification);
            setTimeout(() => notification.remove(), 2000);
        }
    });

    // Initial notification
    const initialNotification = document.createElement('div');
    initialNotification.style = 'position: fixed; top: 20px; left: 50%; transform: translateX(-50%); padding: 10px 20px; background: #4CAF50; color: white; border-radius: 5px; z-index: 9999;';
    initialNotification.textContent = 'Press "A" to activate image click listeners!';
    document.body.appendChild(initialNotification);
    setTimeout(() => initialNotification.remove(), 3000);
})();