PractiScore Match Data Exporter (Tampermonkey/Chrome)

Extracts matchDef and scores variables from PractiScore results pages and saves them as a ZIP file

Bu betiği kurabilmeniz için Tampermonkey, Greasemonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği kurabilmeniz için Tampermonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği kurabilmeniz için Tampermonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği kurabilmeniz için Tampermonkey ya da Userscripts gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

You will need to install an extension such as Tampermonkey to install this script.

Bu komut dosyasını yüklemek için bir kullanıcı komut dosyası yöneticisi uzantısı yüklemeniz gerekecek.

(Zaten bir kullanıcı komut dosyası yöneticim var, kurmama izin verin!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(Zateb bir user-style yöneticim var, yükleyeyim!)

// ==UserScript==
// @name         PractiScore Match Data Exporter (Tampermonkey/Chrome)
// @namespace    punctapracticaliberanda.example.com
// @version      1.0
// @description  Extracts matchDef and scores variables from PractiScore results pages and saves them as a ZIP file
// @match        https://practiscore.com/results/new/*
// @match        http://practiscore.com/results/new/*
// @grant        none
// @license      MIT
// @require      https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js
// ==/UserScript==

(function() {
    'use strict';

    // Extract the match ID from the URL
    function getMatchId() {
        const pathParts = window.location.pathname.split('/');
        const idIndex = pathParts.indexOf('new') + 1;
        return pathParts[idIndex] || 'unknown';
    }

    // Get JSZip from various possible locations (Tampermonkey compatibility)
    function getJSZip() {
        if (typeof JSZip !== 'undefined') {
            return JSZip;
        }
        if (typeof window !== 'undefined' && window.JSZip) {
            return window.JSZip;
        }
        return null;
    }

    // Wait for JSZip to be available
    function waitForJSZip(callback, maxAttempts = 50) {
        let attempts = 0;
        const checkInterval = setInterval(() => {
            attempts++;
            const JSZipLib = getJSZip();
            if (JSZipLib) {
                console.log('JSZip library loaded.');
                clearInterval(checkInterval);
                callback();
            } else if (attempts >= maxAttempts) {
                clearInterval(checkInterval);
                console.error('JSZip library not found after waiting.');
                alert('JSZip library failed to load. Please refresh the page.');
            }
        }, 100);
    }

    // Wait for the page to load and variables to be available
    function waitForVariables(callback, maxAttempts = 500) {
        let attempts = 0;
        const checkInterval = setInterval(() => {
            attempts++;
            if (window.matchDef && window.scores) {
                console.log('matchDef and scores found on this page.');
                clearInterval(checkInterval);
                callback();
            } else if (attempts >= maxAttempts) {
                clearInterval(checkInterval);
                let foundMatchDef = window.matchDef !== undefined;
                let foundScores = window.scores !== undefined;
                let errorMessage = `matchDef: ${foundMatchDef}, scores: ${foundScores}`;
                console.error(`matchDef or scores not found after waiting: ${errorMessage}`);
                alert(`Could not find matchDef or scores variables on this page: ${errorMessage}`);
            }
        }, 100);
    }

    // Create ZIP file blob from match data
    function createZipBlob() {
        return new Promise(function(resolve, reject) {
            try {
                console.log('createZipBlob: Starting...');
                const JSZipLib = getJSZip();
                if (!JSZipLib) {
                    console.error('createZipBlob: JSZip library is not available.');
                    reject(new Error('JSZip library is not available.'));
                    return;
                }
                console.log('createZipBlob: JSZip library found.');

                const matchDef = window.matchDef;
                const scores = window.scores;
                const matchId = getMatchId();

                console.log('createZipBlob: matchDef:', !!matchDef, 'scores:', !!scores, 'matchId:', matchId);

                if(!matchDef) {
                    console.error('createZipBlob: matchDef not found.');
                    reject(new Error('matchDef not found on this page.'));
                    return;
                }

                if(!scores) {
                    console.error('createZipBlob: scores not found.');
                    reject(new Error('scores not found on this page.'));
                    return;
                }

                if(!matchId) {
                    console.error('createZipBlob: matchId not found.');
                    reject(new Error('matchId not found on this page.'));
                    return;
                }

                console.log('createZipBlob: Creating ZIP instance...');
                console.log('createZipBlob: JSZipLib constructor:', typeof JSZipLib);
                // Create a new ZIP file
                let zip;
                try {
                    zip = new JSZipLib();
                    console.log('createZipBlob: ZIP instance created:', !!zip);
                } catch (e) {
                    console.error('createZipBlob: Error creating ZIP instance:', e);
                    reject(e);
                    return;
                }

                try {
                    zip.file('match_def.json', JSON.stringify(matchDef, null, 2));
                    zip.file('scores.json', JSON.stringify(scores, null, 2));
                    console.log('createZipBlob: Files added to ZIP, generating blob...');
                } catch (e) {
                    console.error('createZipBlob: Error adding files to ZIP:', e);
                    reject(e);
                    return;
                }

                // Generate the ZIP file as a blob
                console.log('createZipBlob: Calling generateAsync with arraybuffer...');
                if (typeof zip.generateAsync !== 'function') {
                    console.error('createZipBlob: generateAsync is not a function:', typeof zip.generateAsync);
                    reject(new Error('generateAsync is not available on ZIP instance'));
                    return;
                }

                // Add timeout to detect if promise hangs
                const timeoutId = setTimeout(function() {
                    console.error('createZipBlob: generateAsync timed out after 10 seconds');
                    reject(new Error('ZIP generation timed out'));
                }, 10000);

                console.log('createZipBlob: Starting generateAsync promise with arraybuffer...');

                // Use setTimeout to defer execution - this can help promises resolve in Tampermonkey
                setTimeout(function() {
                    try {
                        // Use arraybuffer and convert to blob - this often works better in Tampermonkey
                        const generatePromise = zip.generateAsync({ type: 'arraybuffer' });
                        console.log('createZipBlob: generateAsync promise created:', !!generatePromise);

                        if (!generatePromise || typeof generatePromise.then !== 'function') {
                            clearTimeout(timeoutId);
                            console.error('createZipBlob: generateAsync did not return a promise');
                            reject(new Error('generateAsync did not return a valid promise'));
                            return;
                        }

                        generatePromise.then(function(arrayBuffer) {
                            clearTimeout(timeoutId);
                            console.log('createZipBlob: ArrayBuffer generated, size:', arrayBuffer.byteLength);
                            try {
                                const blob = new Blob([arrayBuffer], { type: 'application/zip' });
                                console.log('createZipBlob: Blob created from ArrayBuffer, size:', blob.size);
                                resolve({ blob: blob, filename: `${matchId}.zip` });
                            } catch (e) {
                                console.error('createZipBlob: Error creating blob from ArrayBuffer:', e);
                                reject(e);
                            }
                        }).catch(function(error) {
                            clearTimeout(timeoutId);
                            console.error('createZipBlob: Error in generateAsync:', error);
                            console.error('createZipBlob: Error stack:', error.stack);
                            reject(error);
                        });
                    } catch (error) {
                        clearTimeout(timeoutId);
                        console.error('createZipBlob: Exception in setTimeout:', error);
                        reject(error);
                    }
                }, 0);
            } catch (error) {
                console.error('createZipBlob: Exception caught:', error);
                reject(error);
            }
        });
    }

    // Create download button with anchor link
    function createDownloadButton() {
        // Check if button already exists
        if (document.getElementById('psa-download-link')) {
            console.log('Download button already exists.');
            return;
        }

        console.log('Creating download button...');

        // Ensure JSZip is available
        const JSZipLib = getJSZip();
        if (!JSZipLib) {
            console.error('JSZip is not available when creating button.');
            alert('JSZip library is not available. Please refresh the page.');
            return;
        }

        // Create ZIP blob first
        console.log('About to call createZipBlob()...');
        createZipBlob().then(function(result) {
            console.log('ZIP blob created successfully.');
            const blob = result.blob;
            const filename = result.filename;
            console.log('Creating blob URL...');
            const blobUrl = URL.createObjectURL(blob);
            console.log('Blob URL created:', blobUrl);

            const linkStyle = `
                padding: 10px 20px;
                color: white;
                border: none;
                border-radius: 5px;
                cursor: pointer;
                font-size: 14px;
                font-weight: bold;
                box-shadow: 0 2px 5px rgba(0,0,0,0.2);
                min-width: 150px;
                text-decoration: none;
                display: inline-block;
                text-align: center;
                position: fixed;
                bottom: 10px;
                right: 10px;
                z-index: 10000;
                background-color: #4CAF50;
            `;

            // Download link (left-click downloads, right-click allows Save As)
            const downloadLink = document.createElement('a');
            downloadLink.id = 'psa-download-link';
            downloadLink.textContent = 'Download';
            downloadLink.href = blobUrl;
            downloadLink.download = filename;
            downloadLink.style.cssText = linkStyle;
            downloadLink.addEventListener('mouseenter', function() {
                this.style.backgroundColor = '#45a049';
            });
            downloadLink.addEventListener('mouseleave', function() {
                this.style.backgroundColor = '#4CAF50';
            });

            // Ensure body exists before appending
            if (document.body) {
                document.body.appendChild(downloadLink);
                console.log('Download button added to page.');
            } else {
                console.error('document.body is not available.');
                // Wait a bit and try again
                setTimeout(function() {
                    if (document.body) {
                        document.body.appendChild(downloadLink);
                        console.log('Download button added to page (delayed).');
                    } else {
                        alert('Could not add download button: document.body not available.');
                    }
                }, 500);
            }
        }).catch(function(error) {
            console.error('Error creating ZIP file:', error);
            alert('Error creating ZIP file: ' + error.message);
        });
    }

    // Wait for page to load, then wait for JSZip, then variables, then create button
    function init() {
        waitForJSZip(function() {
            waitForVariables(createDownloadButton);
        });
    }

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        init();
    }
})();