https://tracker.qu.ax/

제출된 URL:
https://tracker.qu.ax/
보고서 완료:

링크 · 3개 결과

링크텍스트
https://qu.ax/donate.htmlSupport this Tracker
https://qu.ax/qu.ax
https://erdgeist.org/arts/software/opentracker/Opentracker

JavaScript 변수 · 12개 결과

이름유형
onbeforetoggleobject
documentPictureInPictureobject
onscrollendobject
tailwindobject
/template.htmlstring
animateValuefunction
updateStatsfunction
updateStatsImagefunction
fetchWithRetryfunction
fetchStatsfunction

콘솔 로그 메시지 · 1개 결과

유형카테고리로그
warningother
URL
https://cdn.tailwindcss.com/
텍스트
cdn.tailwindcss.com should not be used in production. To use Tailwind CSS in production, install it as a PostCSS plugin or use the Tailwind CLI: https://tailwindcss.com/docs/installation

HTML

<!DOCTYPE html><html lang="en"><head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>qu.ax Tracker Statistics</title>
    <script src="https://cdn.tailwindcss.com"></script>
    <link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@300;400;600&amp;display=swap" rel="stylesheet">
    <style>
        body {
            font-family: 'Open Sans', sans-serif;
            background-color: #151515;
        }
        .gradient-text {
            background: linear-gradient(90deg, #E555A6 0%, #e80860 100%);
            -webkit-background-clip: text;
            -webkit-text-fill-color: transparent;
        }
        .stats-grid {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
            gap: 1rem;
        }
        .stats-card {
            background: rgba(24, 24, 27, 0.6);
            backdrop-filter: blur(10px);
            border: 1px solid #27272a;
        }
        .stats-chart-container {
            width: 100%;
            height: 0;
            padding-bottom: 50%;
            position: relative;
            overflow: hidden;
        }
        .stats-chart-container img {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            object-fit: contain;
            object-position: center;
        }
        @keyframes pulse {
            0%, 100% { opacity: 1; }
            50% { opacity: 0.5; }
        }
        .loading {
            animation: pulse 1.5s ease-in-out infinite;
        }
        @keyframes spin {
            to { transform: rotate(360deg); }
        }
        .spinner {
            animation: spin 1s linear infinite;
        }
        @media (max-width: 768px) {
            .stats-chart-container {
                padding-bottom: 70%;
            }
        }
    </style>
    <script defer="" data-domain="tracker.qu.ax" src="https://plausib.1337.la/js/script.js"></script>
<style>*, ::before, ::after{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgb(59 130 246 / 0.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgb(59 130 246 / 0.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }/* ! tailwindcss v3.4.14 | MIT License | https://tailwindcss.com */*,::after,::before{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}::after,::before{--tw-content:''}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;tab-size:4;font-family:ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.mx-auto{margin-left:auto;margin-right:auto}.mb-12{margin-bottom:3rem}.mb-2{margin-bottom:0.5rem}.mb-8{margin-bottom:2rem}.ml-2{margin-left:0.5rem}.inline-block{display:inline-block}.flex{display:flex}.h-6{height:1.5rem}.min-h-screen{min-height:100vh}.w-6{width:1.5rem}.w-full{width:100%}.max-w-6xl{max-width:72rem}.flex-col{flex-direction:column}.items-center{align-items:center}.justify-start{justify-content:flex-start}.justify-between{justify-content:space-between}.gap-2{gap:0.5rem}.gap-3{gap:0.75rem}.gap-4{gap:1rem}.space-y-6 > :not([hidden]) ~ :not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem * var(--tw-space-y-reverse))}.rounded-lg{border-radius:0.5rem}.border-2{border-width:2px}.border-\[\#dc0060\]{--tw-border-opacity:1;border-color:rgb(220 0 96 / var(--tw-border-opacity))}.bg-\[\#dc0060\]{--tw-bg-opacity:1;background-color:rgb(220 0 96 / var(--tw-bg-opacity))}.p-4{padding:1rem}.p-6{padding:1.5rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.px-8{padding-left:2rem;padding-right:2rem}.py-2{padding-top:0.5rem;padding-bottom:0.5rem}.py-4{padding-top:1rem;padding-bottom:1rem}.pb-8{padding-bottom:2rem}.pt-8{padding-top:2rem}.text-center{text-align:center}.font-mono{font-family:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace}.text-2xl{font-size:1.5rem;line-height:2rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-6xl{font-size:3.75rem;line-height:1}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:0.875rem;line-height:1.25rem}.font-normal{font-weight:400}.font-semibold{font-weight:600}.text-\[\#e80860\]{--tw-text-opacity:1;color:rgb(232 8 96 / var(--tw-text-opacity))}.text-gray-300{--tw-text-opacity:1;color:rgb(209 213 219 / var(--tw-text-opacity))}.text-gray-400{--tw-text-opacity:1;color:rgb(156 163 175 / var(--tw-text-opacity))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255 / var(--tw-text-opacity))}.opacity-25{opacity:0.25}.opacity-75{opacity:0.75}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(0.4, 0, 0.2, 1);transition-duration:150ms}.transition-colors{transition-property:color, background-color, border-color, fill, stroke, -webkit-text-decoration-color;transition-property:color, background-color, border-color, text-decoration-color, fill, stroke;transition-property:color, background-color, border-color, text-decoration-color, fill, stroke, -webkit-text-decoration-color;transition-timing-function:cubic-bezier(0.4, 0, 0.2, 1);transition-duration:150ms}.duration-300{transition-duration:300ms}.hover\:bg-\[\#a20140\]:hover{--tw-bg-opacity:1;background-color:rgb(162 1 64 / var(--tw-bg-opacity))}.hover\:bg-\[\#dc0060\]:hover{--tw-bg-opacity:1;background-color:rgb(220 0 96 / var(--tw-bg-opacity))}.hover\:text-\[\#a20140\]:hover{--tw-text-opacity:1;color:rgb(162 1 64 / var(--tw-text-opacity))}@media (min-width: 640px){.sm\:flex-row{flex-direction:row}}</style></head>
<body class="min-h-screen text-gray-300">
    <div class="min-h-screen flex flex-col items-center justify-start p-4">
        <main class="w-full max-w-6xl mx-auto">
            <header class="text-center mb-12 pt-8">
                <h1 class="text-6xl font-normal mb-2">
                    <span class="gradient-text">qu.ax</span>
                </h1>
                <h2 class="text-2xl text-gray-400 mb-8">Tracker Statistics</h2>
            </header>

            <div class="stats-grid mb-8">
                <div class="stats-card rounded-lg p-6">
                    <h3 class="text-lg text-gray-400 mb-2">Total Torrents</h3>
                    <div class="flex items-center gap-2">
                        <p class="text-3xl font-semibold" id="torrent-total">1,968,380</p>
                    </div>
                </div>
                <div class="stats-card rounded-lg p-6">
                    <h3 class="text-lg text-gray-400 mb-2">Connected Peers</h3>
                    <div class="flex items-center gap-2">
                        <p class="text-3xl font-semibold" id="peer-total">5,049,913</p>
                    </div>
                </div>
                <div class="stats-card rounded-lg p-6">
                    <h3 class="text-lg text-gray-400 mb-2">Connections/sec</h3>
                    <div class="flex items-center gap-2">
                        <p class="text-3xl font-semibold" id="conn-sec">70,034</p>
                    </div>
                </div>
            </div>

            <div class="stats-card rounded-lg p-6 mb-8 border-[#dc0060] border-2">
                <div class="flex flex-col sm:flex-row items-center justify-between gap-4">
                    <div class="text-lg">
                        <span class="text-gray-400">Tracker Address:</span>
                        <span class="font-mono ml-2 text-white" id="tracker-address">udp://tracker.qu.ax:6969/announce</span>
                    </div>
                    <button onclick="copyTrackerAddress()" class="bg-[#dc0060] hover:bg-[#a20140] text-white px-6 py-2 rounded-lg transition-colors duration-300">
                        Copy Address
                    </button>
                </div>
            </div>

            <div class="stats-card rounded-lg p-6 mb-8">
                <div class="stats-chart-container">
                    <img id="stats-chart" src="tracker_stats_chart.png?t=1731439628772" alt="Tracker Statistics Charts">
                </div>
            </div>

            <footer class="text-center pb-8 space-y-6">
                <a href="https://qu.ax/donate.html" class="stats-card inline-block px-8 py-4 rounded-lg border-2 border-[#dc0060] hover:bg-[#dc0060] transition-all duration-300 group">
                    <div class="flex items-center gap-3">
                        <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
                            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z"></path>
                        </svg>
                        <span class="text-lg">Support this Tracker</span>
                    </div>
                </a>
                <div class="text-sm text-gray-400">
                    Powered by <a href="https://qu.ax/" class="text-[#e80860] hover:text-[#a20140]">qu.ax</a> &amp; <a href="https://erdgeist.org/arts/software/opentracker/" class="text-[#e80860] hover:text-[#a20140]">Opentracker</a>
                </div>
            </footer>
        </main>
    </div>

    <script>
        const trackerBaseUrl = 'https://tracker.qu.ax/stats';
        const updateInterval = 30000;
        const maxRetries = 3;
        const retryDelay = 5000;
        
        let currentValues = {
            torrents: 0,
            peers: 0,
            conns: 0
        };

        function animateValue(element, start, end, duration) {
            element.innerHTML = '0';
            
            let startTimestamp = null;
            const step = (timestamp) => {
                if (!startTimestamp) startTimestamp = timestamp;
                const progress = Math.min((timestamp - startTimestamp) / duration, 1);
                
                const easeOutQuart = 1 - Math.pow(1 - progress, 4);
                
                const current = Math.floor(start + (end - start) * easeOutQuart);
                element.textContent = current.toLocaleString();
                
                if (progress < 1) {
                    window.requestAnimationFrame(step);
                }
            };
            window.requestAnimationFrame(step);
        }

        function updateStats(newValues, animate = true) {
            const duration = animate ? 2000 : 0;

            if (animate) {
                animateValue(document.getElementById('torrent-total'), currentValues.torrents, newValues.torrents, duration);
                animateValue(document.getElementById('peer-total'), currentValues.peers, newValues.peers, duration);
                animateValue(document.getElementById('conn-sec'), currentValues.conns, newValues.conns, duration);
            } else {
                document.getElementById('torrent-total').textContent = newValues.torrents.toLocaleString();
                document.getElementById('peer-total').textContent = newValues.peers.toLocaleString();
                document.getElementById('conn-sec').textContent = newValues.conns.toLocaleString();
            }

            currentValues = { ...newValues };
        }

        function updateStatsImage() {
            const img = document.getElementById('stats-chart');
            img.src = `tracker_stats_chart.png?t=${Date.now()}`;
        }

        async function fetchWithRetry(url, retries = maxRetries) {
            try {
                const response = await fetch(url);
                if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
                return response;
            } catch (error) {
                if (retries > 0) {
                    await new Promise(resolve => setTimeout(resolve, retryDelay));
                    return fetchWithRetry(url, retries - 1);
                }
                throw error;
            }
        }

        async function fetchStats(animate = true) {
            try {
                const [torrentResponse, peerResponse, connResponse] = await Promise.all([
                    fetchWithRetry(`${trackerBaseUrl}?mode=torr`),
                    fetchWithRetry(`${trackerBaseUrl}?mode=peer`),
                    fetchWithRetry(`${trackerBaseUrl}?mode=conn`),
                ]);

                const torrentCount = parseInt(await torrentResponse.text());
                const peerCount = parseInt(await peerResponse.text());
                const connText = await connResponse.text();
                const connMatch = connText.match(/(\d+) conns\/s/);
                const connCount = connMatch ? parseInt(connMatch[1]) : 0;

                updateStats({
                    torrents: torrentCount,
                    peers: peerCount,
                    conns: connCount
                }, animate);

                updateStatsImage();

            } catch (error) {
                ['torrent-total', 'peer-total', 'conn-sec'].forEach(id => {
                    document.getElementById(id).textContent = 'Error';
                });
            }
        }

        function copyTrackerAddress() {
            const address = document.getElementById('tracker-address').textContent;
            navigator.clipboard.writeText(address)
                .then(() => {
                    const button = document.querySelector('button');
                    button.textContent = 'Copied!';
                    setTimeout(() => {
                        button.textContent = 'Copy Address';
                    }, 2000);
                })
                .catch(err => {});
        }

        fetchStats(true);
        setInterval(() => fetchStats(true), updateInterval);
    </script>


</body></html>