CoolFace
Apppublic

sdv2500/progettojava

sourceHugging Faceupdated 10mo agoView on Hugging Face
0likes
builder.html1015 linesDownload Raw Back to templates
1<!DOCTYPE html>2<html lang="it">3 4<head>5    <meta charset="UTF-8">6    <meta name="viewport" content="width=device-width, initial-scale=1.0">7    <title>HLS Zenith - Playlist Builder</title>8    <link rel="icon" type="image/x-icon" href="/favicon.ico">9    <style>10        @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');11        @import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500&display=swap');12 13        :root {14            /* Dark Theme Colors */15            --bg-primary: #0f0f23;16            --bg-secondary: #1a1a2e;17            --bg-tertiary: #16213e;18            --bg-card: rgba(26, 26, 46, 0.8);19            --surface: rgba(255, 255, 255, 0.05);20            --surface-glass: rgba(255, 255, 255, 0.08);21            --surface-elevated: rgba(255, 255, 255, 0.12);22 23            /* Modern Dark Gradients */24            --primary-gradient: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);25            --secondary-gradient: linear-gradient(135deg, #06b6d4 0%, #3b82f6 100%);26            --accent-gradient: linear-gradient(135deg, #f59e0b 0%, #f97316 100%);27            --success-gradient: linear-gradient(135deg, #10b981 0%, #059669 100%);28            --danger-gradient: linear-gradient(135deg, #ef4444 0%, #dc2626 100%);29            --neon-gradient: linear-gradient(90deg, #00f5ff, #bf00ff, #ffff00, #bf00ff, #00f5ff);30 31            /* Accent Colors */32            --accent-primary: #6366f1;33            --accent-secondary: #8b5cf6;34            --accent-success: #10b981;35            --accent-warning: #f59e0b;36            --accent-error: #ef4444;37            --accent-info: #06b6d4;38            --neon-blue: #00f5ff;39            --neon-purple: #bf00ff;40            --neon-green: #39ff14;41 42            /* Text Colors */43            --text-primary: #ffffff;44            --text-secondary: #a1a1aa;45            --text-muted: #71717a;46            --text-accent: #e4e4e7;47            --text-inverse: #0f0f23;48 49            /* Input & Form Colors */50            --input-bg: rgba(255, 255, 255, 0.05);51            --input-border: rgba(255, 255, 255, 0.1);52            --input-border-focus: rgba(99, 102, 241, 0.5);53            --input-placeholder: #71717a;54 55            /* Border & Shadow */56            --border-primary: rgba(255, 255, 255, 0.1);57            --border-accent: rgba(255, 255, 255, 0.2);58            --glow-primary: 0 0 20px rgba(99, 102, 241, 0.3);59            --glow-secondary: 0 0 30px rgba(139, 92, 246, 0.4);60            --glow-success: 0 0 20px rgba(16, 185, 129, 0.4);61            --glow-neon: 0 0 40px rgba(0, 245, 255, 0.6);62            --shadow-dark: 0 25px 50px -12px rgba(0, 0, 0, 0.8);63            --shadow-card: 0 8px 32px rgba(0, 0, 0, 0.3);64 65            /* Animation */66            --transition-fast: all 0.15s cubic-bezier(0.4, 0, 0.2, 1);67            --transition-normal: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);68            --transition-slow: all 0.6s cubic-bezier(0.4, 0, 0.2, 1);69 70            /* Measurements */71            --border-radius: 12px;72            --border-radius-large: 20px;73            --spacing-xs: 0.25rem;74            --spacing-sm: 0.5rem;75            --spacing-md: 1rem;76            --spacing-lg: 1.5rem;77            --spacing-xl: 2rem;78            --spacing-2xl: 3rem;79        }80 81        * {82            margin: 0;83            padding: 0;84            box-sizing: border-box;85        }86 87        body {88            font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;89            background: var(--bg-primary);90            color: var(--text-primary);91            min-height: 100vh;92            overflow-x: hidden;93            position: relative;94        }95 96        /* Animated Background */97        body::before {98            content: '';99            position: fixed;100            top: 0;101            left: 0;102            width: 100%;103            height: 100%;104            background:105                radial-gradient(circle at 20% 50%, rgba(99, 102, 241, 0.15) 0%, transparent 50%),106                radial-gradient(circle at 80% 20%, rgba(139, 92, 246, 0.15) 0%, transparent 50%),107                radial-gradient(circle at 40% 80%, rgba(6, 182, 212, 0.15) 0%, transparent 50%);108            animation: backgroundFloat 25s ease-in-out infinite;109            z-index: -1;110        }111 112        @keyframes backgroundFloat {113 114            0%,115            100% {116                transform: translateY(0) rotate(0deg) scale(1);117            }118 119            33% {120                transform: translateY(-20px) rotate(1deg) scale(1.02);121            }122 123            66% {124                transform: translateY(20px) rotate(-1deg) scale(0.98);125            }126        }127 128        .container {129            max-width: 1400px;130            margin: 0 auto;131            padding: var(--spacing-lg);132            min-height: 100vh;133        }134 135        .header {136            text-align: center;137            margin-bottom: var(--spacing-2xl);138            padding: var(--spacing-xl) 0;139        }140 141        .main-title {142            font-size: clamp(2rem, 5vw, 3.5rem);143            font-weight: 700;144            background: var(--neon-gradient);145            background-size: 300% 300%;146            -webkit-background-clip: text;147            -webkit-text-fill-color: transparent;148            background-clip: text;149            margin-bottom: var(--spacing-md);150            animation: gradientShift 4s ease-in-out infinite;151        }152 153        @keyframes gradientShift {154 155            0%,156            100% {157                background-position: 0% 50%;158            }159 160            50% {161                background-position: 100% 50%;162            }163        }164 165        .main-subtitle {166            font-size: 1.125rem;167            color: var(--text-secondary);168            font-weight: 400;169            max-width: 800px;170            margin: 0 auto;171            line-height: 1.6;172        }173 174        .main-content {175            display: grid;176            grid-template-columns: 1fr 1fr;177            gap: var(--spacing-2xl);178            align-items: start;179        }180 181        .left-panel,182        .right-panel {183            background: var(--bg-card);184            border: 1px solid var(--border-primary);185            border-radius: var(--border-radius-large);186            padding: var(--spacing-xl);187            backdrop-filter: blur(20px);188            box-shadow: var(--shadow-card);189            transition: var(--transition-normal);190        }191 192        .left-panel:hover,193        .right-panel:hover {194            border-color: var(--border-accent);195            box-shadow: var(--shadow-card), var(--glow-primary);196        }197 198        .section-title {199            font-size: 1.5rem;200            font-weight: 600;201            margin-bottom: var(--spacing-lg);202            color: var(--text-primary);203            display: flex;204            align-items: center;205            gap: var(--spacing-sm);206        }207 208        .section-icon {209            padding: var(--spacing-sm);210            border-radius: var(--spacing-sm);211            background: var(--primary-gradient);212            font-size: 1rem;213        }214 215        .form-group {216            margin-bottom: var(--spacing-lg);217        }218 219        .form-label {220            display: block;221            font-weight: 500;222            margin-bottom: var(--spacing-sm);223            color: var(--text-secondary);224            font-size: 0.9rem;225            text-transform: uppercase;226            letter-spacing: 0.05em;227        }228 229        .input-group {230            position: relative;231            display: flex;232            gap: var(--spacing-sm);233        }234 235        .form-input {236            flex: 1;237            padding: var(--spacing-md);238            background: var(--input-bg);239            border: 1px solid var(--input-border);240            border-radius: var(--border-radius);241            color: var(--text-primary);242            font-size: 1rem;243            transition: var(--transition-normal);244            backdrop-filter: blur(10px);245        }246 247        .form-input:focus {248            outline: none;249            border-color: var(--input-border-focus);250            box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1), var(--glow-primary);251            background: var(--surface-elevated);252        }253 254        .form-input::placeholder {255            color: var(--input-placeholder);256        }257 258        .btn {259            display: inline-flex;260            align-items: center;261            justify-content: center;262            gap: var(--spacing-sm);263            padding: var(--spacing-md) var(--spacing-lg);264            border: none;265            border-radius: var(--border-radius);266            font-weight: 500;267            font-size: 0.95rem;268            cursor: pointer;269            transition: var(--transition-normal);270            position: relative;271            overflow: hidden;272            text-decoration: none;273            min-width: 120px;274        }275 276        .btn::before {277            content: '';278            position: absolute;279            top: 0;280            left: -100%;281            width: 100%;282            height: 100%;283            background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);284            transition: var(--transition-fast);285        }286 287        .btn:hover::before {288            left: 100%;289        }290 291        .btn-primary {292            background: var(--primary-gradient);293            color: white;294            box-shadow: var(--glow-primary);295        }296 297        .btn-primary:hover {298            transform: translateY(-2px);299            box-shadow: var(--glow-secondary);300        }301 302        .btn-success {303            background: var(--success-gradient);304            color: white;305            box-shadow: var(--glow-success);306        }307 308        .btn-success:hover {309            transform: translateY(-2px);310            box-shadow: 0 0 30px rgba(16, 185, 129, 0.5);311        }312 313        .btn-secondary {314            background: var(--surface-glass);315            color: var(--text-primary);316            border: 1px solid var(--border-primary);317            backdrop-filter: blur(20px);318        }319 320        .btn-secondary:hover {321            background: var(--surface-elevated);322            border-color: var(--border-accent);323            transform: translateY(-2px);324        }325 326        .btn-icon-only {327            min-width: auto;328            width: 44px;329            height: 44px;330            padding: 0;331            border-radius: var(--spacing-sm);332        }333 334        .playlist-item {335            background: var(--surface);336            border: 1px solid var(--border-primary);337            border-radius: var(--border-radius);338            padding: var(--spacing-md);339            margin-bottom: var(--spacing-sm);340            display: flex;341            align-items: center;342            justify-content: space-between;343            transition: var(--transition-normal);344            animation: slideInLeft 0.4s ease-out;345        }346 347        .playlist-item:hover {348            background: var(--surface-elevated);349            border-color: var(--border-accent);350            transform: translateX(4px);351        }352 353        @keyframes slideInLeft {354            from {355                opacity: 0;356                transform: translateX(-20px);357            }358 359            to {360                opacity: 1;361                transform: translateX(0);362            }363        }364 365        .playlist-url {366            font-family: 'JetBrains Mono', monospace;367            font-size: 0.85rem;368            color: var(--text-secondary);369            word-break: break-all;370            margin-right: var(--spacing-sm);371        }372 373        .features-section {374            margin-bottom: var(--spacing-2xl);375        }376 377        .features-grid {378            display: grid;379            grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));380            gap: var(--spacing-md);381            margin-bottom: var(--spacing-xl);382        }383 384        .feature-badge {385            background: var(--surface-glass);386            border: 1px solid var(--border-primary);387            border-radius: var(--border-radius);388            padding: var(--spacing-sm) var(--spacing-md);389            text-align: center;390            font-size: 0.85rem;391            color: var(--text-secondary);392            backdrop-filter: blur(10px);393            transition: var(--transition-normal);394        }395 396        .feature-badge:hover {397            background: var(--surface-elevated);398            color: var(--text-primary);399            transform: scale(1.02);400        }401 402        .examples-section {403            margin-top: var(--spacing-xl);404        }405 406        .examples-list {407            background: var(--surface);408            border: 1px solid var(--border-primary);409            border-radius: var(--border-radius);410            padding: var(--spacing-lg);411            margin-top: var(--spacing-md);412        }413 414        .example-item {415            display: flex;416            align-items: center;417            padding: var(--spacing-sm) 0;418            border-bottom: 1px solid var(--border-primary);419            color: var(--text-secondary);420            font-size: 0.9rem;421        }422 423        .example-item:last-child {424            border-bottom: none;425        }426 427        .example-bullet {428            color: var(--accent-primary);429            margin-right: var(--spacing-sm);430            font-weight: bold;431        }432 433        .output-section {434            position: relative;435        }436 437        .output-content {438            background: var(--bg-tertiary);439            border: 1px solid var(--border-primary);440            border-radius: var(--border-radius);441            padding: 0;442            /* Removed padding to let children control it */443            font-family: 'JetBrains Mono', monospace;444            font-size: 0.9rem;445            color: var(--text-secondary);446            white-space: pre-wrap;447            word-wrap: break-word;448            overflow-y: auto;449            position: relative;450            display: flex;451            flex-direction: column;452            justify-content: center;453        }454 455        .output-placeholder {456            color: var(--text-muted);457            font-style: italic;458            text-align: center;459            padding: var(--spacing-lg);460            display: flex;461            flex-direction: column;462            align-items: center;463            gap: var(--spacing-sm);464        }465 466        .output-placeholder-icon {467            font-size: 2rem;468            opacity: 0.5;469        }470 471        .loading-spinner {472            display: none;473            width: 40px;474            height: 40px;475            border: 3px solid var(--surface);476            border-top: 3px solid var(--accent-primary);477            border-radius: 50%;478            animation: spin 1s linear infinite;479            margin: var(--spacing-xl) auto;480        }481 482        @keyframes spin {483            0% {484                transform: rotate(0deg);485            }486 487            100% {488                transform: rotate(360deg);489            }490        }491 492        .copy-notification {493            position: fixed;494            top: 50%;495            left: 50%;496            transform: translate(-50%, -50%) scale(0);497            background: var(--success-gradient);498            color: white;499            padding: var(--spacing-lg) var(--spacing-xl);500            border-radius: var(--border-radius);501            font-weight: 500;502            box-shadow: var(--shadow-dark);503            z-index: 1000;504            transition: var(--transition-normal);505        }506 507        .copy-notification.show {508            transform: translate(-50%, -50%) scale(1);509        }510 511        .generated-link {512            padding: var(--spacing-md);513            display: flex;514            align-items: center;515            gap: var(--spacing-sm);516        }517 518        .link-text {519            font-family: 'JetBrains Mono', monospace;520            font-size: 0.85rem;521            color: var(--text-primary);522            word-break: break-all;523            flex: 1;524            background: rgba(0, 0, 0, 0.2);525            padding: var(--spacing-sm);526            border-radius: 4px;527            border: 1px solid var(--border-primary);528        }529 530        .link-label {531            color: var(--accent-success);532            font-weight: 600;533            font-size: 0.85rem;534            text-transform: uppercase;535            letter-spacing: 0.05em;536            margin-bottom: var(--spacing-xs);537        }538 539        /* Responsive Design */540        @media (max-width: 1024px) {541            .main-content {542                grid-template-columns: 1fr;543                gap: var(--spacing-xl);544            }545        }546 547        @media (max-width: 768px) {548            .container {549                padding: var(--spacing-md);550            }551 552            .features-grid {553                grid-template-columns: 1fr;554            }555 556            .input-group {557                flex-direction: column;558            }559 560            .btn {561                width: 100%;562                justify-content: center;563            }564 565            .generated-link {566                flex-direction: column;567                align-items: stretch;568            }569        }570 571        /* Scrollbar Styling */572        ::-webkit-scrollbar {573            width: 8px;574        }575 576        ::-webkit-scrollbar-track {577            background: var(--bg-secondary);578            border-radius: 4px;579        }580 581        ::-webkit-scrollbar-thumb {582            background: var(--surface-elevated);583            border-radius: 4px;584        }585 586        ::-webkit-scrollbar-thumb:hover {587            background: var(--border-accent);588        }589 590        /* Custom animations */591        .fade-in {592            animation: fadeIn 0.5s ease-out;593        }594 595        @keyframes fadeIn {596            from {597                opacity: 0;598            }599 600            to {601                opacity: 1;602            }603        }604 605        .pulse-success {606            animation: pulseSuccess 0.6s ease-in-out;607        }608 609        @keyframes pulseSuccess {610            0% {611                transform: scale(1);612            }613 614            50% {615                transform: scale(1.05);616                box-shadow: var(--glow-success);617            }618 619            100% {620                transform: scale(1);621            }622        }623    </style>624</head>625 626<body>627    <div class="container">628        <div class="header">629            <h1 class="main-title">Playlist Builder</h1>630            <p class="main-subtitle">Combina multiple playlist IPTV generando un link unificato per l'endpoint /playlist631            </p>632        </div>633 634        <div class="features-section">635            <div class="features-grid">636                <div class="feature-badge">🔗 Link unificato /playlist</div>637                <div class="feature-badge">🔧 Separazione automatica con ;</div>638                <div class="feature-badge">📺 Formato compatibile con server</div>639                <div class="feature-badge">🎯 Generazione istantanea</div>640                <div class="feature-badge">🌐 Supporto URL multipli</div>641                <div class="feature-badge">📋 Copia con un click</div>642            </div>643        </div>644 645        <div class="main-content">646            <div class="left-panel">647                <h2 class="section-title">648                    <span class="section-icon">📝</span>649                    Configurazione Playlist650                </h2>651 652                <form id="playlistForm">653                    <div class="form-group">654                        <label class="form-label">URL Playlist</label>655                        <div class="input-group">656                            <input type="url" id="urlInput" class="form-input"657                                placeholder="https://provider.com/playlist.m3u8" required>658                            <button type="button" id="addUrl" class="btn btn-primary btn-icon-only">659660                            </button>661                        </div>662                    </div>663                    664                    <div class="form-group">665                        <label class="form-label">API Password (Opzionale)</label>666                        <div class="input-group">667                            <input type="password" id="apiPasswordInput" class="form-input"668                                placeholder="Inserisci la password API se richiesta dal server">669                        </div>670                        <small style="color: var(--text-muted); display: block; margin-top: 5px;">671                            Questa password verrà inclusa nel link generato e usata per autenticare le richieste.672                        </small>673                    </div>674                </form>675 676                <div class="form-group">677                    <label class="form-label">Playlist Aggiunte</label>678                    <div id="playlistList">679                        <div class="output-placeholder">680                            <div class="output-placeholder-icon">📋</div>681                            <p>Nessuna playlist aggiunta</p>682                            <small>Inserisci gli URL delle playlist e clicca ➕ per aggiungerli</small>683                        </div>684                    </div>685                </div>686 687                <div class="form-group">688                    <button id="clearBtn" class="btn btn-secondary" style="width: 100%;">689                        <span>🗑️</span>690                        <span>Pulisci Lista</span>691                    </button>692                </div>693            </div>694 695            <div class="right-panel">696                <div class="output-section">697                    <h2 class="section-title">698                        <span class="section-icon">🔗</span>699                        Link Generato700                        <button id="copyBtn" class="btn btn-secondary btn-icon-only" style="margin-left: auto;"701                            disabled>702                            📋703                        </button>704                    </h2>705 706                    <div class="loading-spinner" id="loadingSpinner"></div>707 708                    <div class="output-content" id="outputContent">709                        <div class="output-placeholder">710                            <div class="output-placeholder-icon">🔗</div>711                            <p>Il link combinato apparirà qui</p>712                            <small>Inserisci gli URL delle playlist per creare il link</small>713                        </div>714                    </div>715                </div>716            </div>717        </div>718    </div>719 720    <div class="copy-notification" id="copyNotification">721        ✅ Link copiato negli appunti!722    </div>723 724    <script>725        class PlaylistBuilder {726            constructor() {727                this.playlists = [];728                this.initializeElements();729                this.attachEventListeners();730            }731 732            initializeElements() {733                this.urlInput = document.getElementById('urlInput');734                this.apiPasswordInput = document.getElementById('apiPasswordInput');735                this.addUrlBtn = document.getElementById('addUrl');736                this.playlistList = document.getElementById('playlistList');737                this.clearBtn = document.getElementById('clearBtn');738                this.outputContent = document.getElementById('outputContent');739                this.copyBtn = document.getElementById('copyBtn');740                this.loadingSpinner = document.getElementById('loadingSpinner');741                this.copyNotification = document.getElementById('copyNotification');742            }743 744            attachEventListeners() {745                this.addUrlBtn.addEventListener('click', () => this.addPlaylist());746                this.urlInput.addEventListener('keypress', (e) => {747                    if (e.key === 'Enter') {748                        e.preventDefault();749                        this.addPlaylist();750                    }751                });752                this.apiPasswordInput.addEventListener('input', () => this.generatePlaylistLink());753                this.clearBtn.addEventListener('click', () => this.clearPlaylists());754                this.copyBtn.addEventListener('click', () => this.copyToClipboard());755            }756 757            addPlaylist() {758                const url = this.urlInput.value.trim();759                if (!url) return;760 761                if (!this.isValidUrl(url)) {762                    this.showNotification('❌ URL non valido', 'error');763                    return;764                }765 766                // Check if URL already exists (check the url property of objects)767                if (this.playlists.some(p => p.url === url)) {768                    this.showNotification('⚠️ URL già presente nella lista', 'warning');769                    return;770                }771 772                this.playlists.push({773                    url: url,774                    sort: false,775                    noProxy: false776                });777 778                this.urlInput.value = '';779                this.updatePlaylistList();780                this.generatePlaylistLink();781 782                // Animazione di successo783                this.addUrlBtn.classList.add('pulse-success');784                setTimeout(() => {785                    this.addUrlBtn.classList.remove('pulse-success');786                }, 600);787            }788 789            isValidUrl(string) {790                try {791                    const url = new URL(string);792                    return url.protocol === 'http:' || url.protocol === 'https:';793                } catch (_) {794                    return false;795                }796            }797 798            updatePlaylistList() {799                if (this.playlists.length === 0) {800                    this.playlistList.innerHTML = `801                        <div class="output-placeholder">802                            <div class="output-placeholder-icon">📋</div>803                            <p>Nessuna playlist aggiunta</p>804                            <small>Inserisci gli URL delle playlist e clicca ➕ per aggiungerli</small>805                        </div>806                    `;807                    return;808                }809 810                this.playlistList.innerHTML = this.playlists.map((item, index) => `811                    <div class="playlist-item">812                        <div style="flex: 1; min-width: 0;">813                            <div class="playlist-url" title="${item.url}">${item.url}</div>814                            <div style="display: flex; gap: 10px; margin-top: 8px;">815                                <label style="display: flex; align-items: center; gap: 4px; font-size: 0.85rem; color: var(--text-secondary); cursor: pointer;">816                                    <input type="checkbox" ${item.sort ? 'checked' : ''} onchange="playlistBuilder.toggleSort(${index})">817                                    Sort A-Z818                                </label>819                                <label style="display: flex; align-items: center; gap: 4px; font-size: 0.85rem; color: var(--text-secondary); cursor: pointer;">820                                    <input type="checkbox" ${item.noProxy ? 'checked' : ''} onchange="playlistBuilder.toggleNoProxy(${index})">821                                    No Zenith822                                </label>823                            </div>824                        </div>825                        <button class="btn btn-secondary btn-icon-only" onclick="playlistBuilder.removePlaylist(${index})" style="margin-left: 10px;">826827                        </button>828                    </div>829                `).join('');830            }831 832            toggleSort(index) {833                this.playlists[index].sort = !this.playlists[index].sort;834                this.generatePlaylistLink();835            }836 837            toggleNoProxy(index) {838                this.playlists[index].noProxy = !this.playlists[index].noProxy;839                this.generatePlaylistLink();840            }841 842            removePlaylist(index) {843                this.playlists.splice(index, 1);844                this.updatePlaylistList();845                this.generatePlaylistLink();846            }847 848            generatePlaylistLink() {849                if (this.playlists.length === 0) {850                    this.resetOutput();851                    return;852                }853 854                const generatedLink = this.createPlaylistLink();855                this.displayResult(generatedLink);856                this.copyBtn.disabled = false;857            }858 859            async simulateLoading() {860                // Breve pausa per UX861                return new Promise(resolve => {862                    setTimeout(resolve, 800);863                });864            }865 866            createPlaylistLink() {867                // Crea il link nel formato /playlist?url=URL1|sort=true|noproxy=false;URL2...868                const encodedDefinitions = this.playlists.map(p => {869                    let def = p.url;870                    if (p.sort) def += '|sort=true';871                    if (p.noProxy) def += '|noproxy=true';872                    return encodeURIComponent(def);873                });874                const urlString = encodedDefinitions.join(';');875                const baseUrl = window.location.origin;876                877                let finalUrl = `${baseUrl}/playlist?url=${urlString}`;878                879                // Aggiungi api_password se presente880                const apiPassword = this.apiPasswordInput.value.trim();881                if (apiPassword) {882                    finalUrl += `&api_password=${encodeURIComponent(apiPassword)}`;883                }884                885                return finalUrl;886            }887 888            displayResult(link) {889                this.outputContent.innerHTML = `890                    <div class="generated-link fade-in">891                        <div class="link-text">${link}</div>892                    </div>893                `;894            }895 896            setLoading(loading) {897                this.loadingSpinner.style.display = loading ? 'block' : 'none';898                if (loading) {899                    this.outputContent.innerHTML = '<div class="output-placeholder"><div class="output-placeholder-icon">⏳</div><p>Generazione link in corso...</p></div>';900                }901            }902 903            async copyToClipboard() {904                const linkElement = document.querySelector('.link-text');905                if (!linkElement) return;906 907                const link = linkElement.textContent;908 909                try {910                    await navigator.clipboard.writeText(link);911                    this.showCopyNotification();912 913                    // Animazione del bottone914                    this.copyBtn.classList.add('pulse-success');915                    setTimeout(() => {916                        this.copyBtn.classList.remove('pulse-success');917                    }, 600);918                } catch (err) {919                    // Fallback per browser che non supportano clipboard API920                    this.fallbackCopyTextToClipboard(link);921                }922            }923 924            fallbackCopyTextToClipboard(text) {925                const textArea = document.createElement('textarea');926                textArea.value = text;927                textArea.style.position = 'fixed';928                textArea.style.left = '-999999px';929                textArea.style.top = '-999999px';930                document.body.appendChild(textArea);931                textArea.focus();932                textArea.select();933 934                try {935                    document.execCommand('copy');936                    this.showCopyNotification();937                } catch (err) {938                    this.showNotification('❌ Impossibile copiare negli appunti', 'error');939                }940 941                document.body.removeChild(textArea);942            }943 944            showCopyNotification() {945                this.copyNotification.classList.add('show');946                setTimeout(() => {947                    this.copyNotification.classList.remove('show');948                }, 2000);949            }950 951            clearPlaylists() {952                this.playlists = [];953                this.updatePlaylistList();954                this.resetOutput();955                this.showNotification('🗑️ Lista playlist pulita', 'info');956            }957 958            resetOutput() {959                this.outputContent.innerHTML = `960                    <div class="output-placeholder">961                        <div class="output-placeholder-icon">🔗</div>962                        <p>Il link combinato apparirà qui</p>963                        <small>Inserisci gli URL delle playlist e clicca "Genera Link" per creare il link unificato</small>964                    </div>965                `;966                this.copyBtn.disabled = true;967            }968 969            showError(message) {970                this.outputContent.innerHTML = `971                    <div class="output-placeholder">972                        <div class="output-placeholder-icon">❌</div>973                        <p>Errore</p>974                        <small>${message}</small>975                    </div>976                `;977                this.showNotification('❌ ' + message, 'error');978            }979 980            showNotification(message, type = 'info') {981                // Implementazione semplificata per ora982                console.log(`${type.toUpperCase()}: ${message}`);983            }984        }985 986        // Inizializzazione987        const playlistBuilder = new PlaylistBuilder();988 989        // Effetti di interattività aggiuntivi990        document.addEventListener('DOMContentLoaded', function () {991            // Effetto hover sulle card992            const cards = document.querySelectorAll('.left-panel, .right-panel');993 994            cards.forEach(card => {995                card.addEventListener('mouseenter', function () {996                    this.style.transform = 'translateY(-4px)';997                });998 999                card.addEventListener('mouseleave', function () {1000                    this.style.transform = 'translateY(0)';1001                });1002            });1003 1004            // Animazione dei badge delle feature1005            const badges = document.querySelectorAll('.feature-badge');1006            badges.forEach((badge, index) => {1007                badge.style.animationDelay = `${index * 0.1}s`;1008                badge.classList.add('fade-in');1009            });1010        });1011    </script>1012</body>1013 1014</html>1015