File size: 15,685 Bytes
e9a7d0a
 
 
dfee44a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e9a7d0a
 
dfee44a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e9a7d0a
dfee44a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e9a7d0a
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Cosmick-Radio</title>
  <style>
    body { margin: 0; font-family: Arial, sans-serif; transition: background-color 0.5s, color 0.5s; }
    .container { display: flex; flex-direction: column; height: 100vh; padding: 20px; background: white; color: #1f2937; }
    .dark-mode { background-color: #1f2937; color: white; }
    .app-bar { display: flex; justify-content: space-between; align-items: center; padding: 10px; border-bottom: 1px solid rgba(156, 163, 175, 0.2); }
    .main-content { display: flex; flex: 1; overflow: hidden; }
    .player { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; }
    .station-box { 
      width: 200px; 
      height: 200px; 
      background: white; 
      border: 3px solid red; 
      border-radius: 10px; 
      box-shadow: 0 10px 20px rgba(0, 0, 0, 0.2); 
      margin-bottom: 20px; 
      display: flex; 
      align-items: center; 
      justify-content: center; 
    }
    .station-name-text { 
      font-size: 24px; 
      font-weight: bold; 
      text-align: center; 
      background: linear-gradient(90deg, red, orange, yellow, green, blue, indigo, violet); 
      -webkit-background-clip: text; 
      -webkit-text-fill-color: transparent; 
    }
    .station-box.playing { animation: pulse 3s infinite; }
    .controls button { padding: 10px; margin: 0 10px; background: #9333ea; color: white; border: none; border-radius: 50%; cursor: pointer; }
    .controls button:hover { background: #7e22ce; }
    .volume { display: flex; align-items: center; margin: 20px 0; }
    .volume input { width: 200px; margin: 0 10px; }
    .actions button { margin: 0 10px; background: none; border: none; cursor: pointer; }
    .liked { color: #ef4444; fill: #ef4444; }
    .panel { width: 300px; background: rgba(0, 0, 0, 0.1); padding: 20px; overflow-y: auto; }
    .station-item { display: flex; align-items: center; padding: 10px; }
    @keyframes pulse { 0% { transform: scale(1); } 50% { transform: scale(1.05); } 100% { transform: scale(1); } }
  </style>
</head>
<body>
  <div id="container" class="container">
    <div class="app-bar">
      <div>
        <svg width="24" height="24" viewBox="0 0 24 24" style="display:inline-block; vertical-align:middle;"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm-1-13h2v6h-2zm0 8h2v2h-2z"/></svg>
        <h1 style="display:inline-block; margin-left:5px;">Cosmick-Radio</h1>
      </div>
      <div>
        <button id="prefs-btn"><svg width="24" height="24" viewBox="0 0 24 24"><path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"/></svg></button>
        <button id="theme-btn"><svg width="24" height="24" viewBox="0 0 24 24"><path d="M12 3v1.5M12 19.5V21M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M3 12h1.5M19.5 12H21M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42M12 7a5 5 0 100 10 5 5 0 000-10z"/></svg></button>
      </div>
    </div>
    <div class="main-content">
      <div class="player">
        <div id="station-box" class="station-box">
          <span id="station-name-text" class="station-name-text">Radio Paradise</span>
        </div>
        <div id="station-info" style="text-align:center;">
          <span id="frequency">N/A</span>
          <span id="status" style="display:inline-block; width:8px; height:8px; border-radius:50%; background:red; margin-left:5px;"></span>
          <h2 id="station-name">Radio Paradise</h2>
          <p id="genre">Eclectic</p>
          <p id="description">Eclectic mix of rock, world, and more</p>
        </div>
        <div class="volume">
          <svg width="20" height="20" viewBox="0 0 24 24"><path d="M18.5 12c0-1.77-.77-3.37-2-4.46V4.46c2.69 1.46 4.5 4.36 4.5 7.54s-1.81 6.08-4.5 7.54v-3.08c1.23-1.09 2-2.69 2-4.46zM3 9v6h4l5 5V4L7 9H3zm11.5 3c0 1.38-.73 2.6-1.85 3.27l1.39 1.39C15.87 15.53 17 13.82 17 12s-1.13-3.53-2.94-4.66l-1.39 1.39c1.12.67 1.83 1.89 1.83 3.27z"/></svg>
          <input type="range" id="volume" min="0" max="100" value="70">
          <span id="volume-value">70%</span>
        </div>
        <div class="controls">
          <button id="prev-btn"><svg width="24" height="24" viewBox="0 0 24 24"><path d="M6 6h2v12H6zm3.5 6l8.5 6V6z"/></svg></button>
          <button id="play-pause-btn"><svg width="28" height="28" viewBox="0 0 24 24"><path d="M8 5v14l11-7z"/></svg></button>
          <button id="next-btn"><svg width="24" height="24" viewBox="0 0 24 24"><path d="M6 18l8.5-6L6 6v12zM16 6v12h2V6h-2z"/></svg></button>
        </div>
        <div class="actions" style="margin-top:20px;">
          <button id="like-btn"><svg width="24" height="24" viewBox="0 0 24 24"><path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/></svg></button>
          <button id="share-btn"><svg width="24" height="24" viewBox="0 0 24 24"><path d="M18 16.08c-.76 0-1.44.3-1.96.77L8.91 12.7c.05-.23.09-.46.09-.7s-.04-.47-.09-.70l7.05-4.11c.54.5 1.25.81 2.04.81 1.66 0 3-1.34 3-3s-1.34-3-3-3-3 1.34-3 3c0 .24.04.47.09.7L8.04 9.81C7.5 9.31 6.79 9 6 9c-1.66 0-3 1.34-3 3s1.34 3 3 3c.79 0 1.5-.31 2.04-.81l7.12 4.16c-.05.21-.08.43-.08.65 0 1.61 1.31 2.92 2.92 2.92 1.61 0 2.92-1.31 2.92-2.92s-1.31-2.92-2.92-2.92z"/></svg></button>
          <button id="recommend-btn"><svg width="24" height="24" viewBox="0 0 24 24"><path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/></svg></button>
        </div>
      </div>
      <div id="recommend-panel" class="panel" style="display:none;">
        <h3>AI Recommendations</h3>
        <button id="close-recommend">×</button>
        <div id="recommend-list"></div>
      </div>
      <div id="prefs-panel" class="panel" style="display:none;">
        <h3>AI Personalization</h3>
        <button id="close-prefs">×</button>
        <p>Preferences coming soon!</p>
      </div>
    </div>
  </div>

  <script src="https://cdn.jsdelivr.net/npm/@huggingface/transformers@latest/dist/transformers.min.js"></script>
  <script>
    const stations = [
      { id: 1, name: "Radio Paradise", genre: "Eclectic", frequency: "N/A", streamUrl: "http://stream.radioparadise.com/mp3-192", description: "Eclectic mix of rock, world, and more" },
      { id: 2, name: "SomaFM Groove Salad", genre: "Ambient", frequency: "N/A", streamUrl: "http://ice1.somafm.com/groovesalad-128-mp3", description: "Chilled beats and ambient vibes" },
      // Classical Stations
      { id: 3, name: "Classical MPR", genre: "Classical", frequency: "N/A", streamUrl: "http://cms.stream.publicradio.org/cms.mp3", description: "Classical music from Minnesota Public Radio" },
      { id: 4, name: "WFMT Classical", genre: "Classical", frequency: "N/A", streamUrl: "http://stream.wfmt.com/main-mp3", description: "Classical music from Chicago" },
      { id: 5, name: "BBC Radio 3", genre: "Classical", frequency: "N/A", streamUrl: "http://bbcmedia.ic.llnwd.net/stream/bbcmedia_radio3_mf_p", description: "Classical music from the BBC" },
      { id: 6, name: "WCRB Classical", genre: "Classical", frequency: "N/A", streamUrl: "http://audio.wgbh.org:8004/stream", description: "Boston’s classical music station" },
      { id: 7, name: "Radio Swiss Classic", genre: "Classical", frequency: "N/A", streamUrl: "http://stream.srg-ssr.ch/m/rsc_de/mp3_128", description: "Swiss classical music" },
      { id: 8, name: "KUSC Classical", genre: "Classical", frequency: "N/A", streamUrl: "http://stream.kusc.org/kusc-128k.mp3", description: "Classical music from Los Angeles" },
      { id: 9, name: "Classical WETA", genre: "Classical", frequency: "N/A", streamUrl: "http://weta.streamguys1.com/classical", description: "Classical music from Washington, DC" },
      { id: 10, name: "France Musique", genre: "Classical", frequency: "N/A", streamUrl: "http://direct.francemusique.fr/live/francemusique-midfi.mp3", description: "French classical music" },
      // Smooth Jazz Stations
      { id: 11, name: "Smooth Jazz Florida", genre: "Smooth Jazz", frequency: "N/A", streamUrl: "http://smoothjazz.cdnstream1.com/2524_128.mp3", description: "Smooth jazz tunes from Florida" },
      { id: 12, name: "Jazz24", genre: "Smooth Jazz", frequency: "N/A", streamUrl: "http://live.wostreaming.net/direct/ppm-jazz24aac256-ibc1", description: "24/7 smooth jazz" },
      { id: 13, name: "WHOV 88.1 Hampton Radio", genre: "Smooth Jazz", frequency: "88.1 FM", streamUrl: "http://whov.streamguys1.com/live", description: "Smooth jazz from Hampton, Virginia" },
      { id: 14, name: "Smooth Jazz Global", genre: "Smooth Jazz", frequency: "N/A", streamUrl: "http://smoothjazzglobal.streamguys1.com/sjglobal", description: "Global smooth jazz hits" },
      { id: 15, name: "The Wave Smooth Jazz", genre: "Smooth Jazz", frequency: "N/A", streamUrl: "http://stream.zeno.fm/xv8nq5v5v5zuv", description: "Relaxing smooth jazz" },
      { id: 16, name: "Jazz FM Smooth", genre: "Smooth Jazz", frequency: "N/A", streamUrl: "http://listen.jazzfm.com/jazzsmooth", description: "Smooth jazz from the UK" },
      { id: 17, name: "Smooth Jazz 247", genre: "Smooth Jazz", frequency: "N/A", streamUrl: "http://stream.smoothjazz247.com/live", description: "Non-stop smooth jazz" },
      // Additional Stations
      { id: 18, name: "KEXP", genre: "Eclectic", frequency: "N/A", streamUrl: "http://live-mp3-128.kexp.org/kexp128.mp3", description: "Eclectic music from Seattle" },
      { id: 19, name: "Hot 108 Jamz", genre: "Hip Hop", frequency: "N/A", streamUrl: "http://108.61.30.179:4010/", description: "Hip hop hits" },
      { id: 20, name: "Virgin Radio UK", genre: "Pop", frequency: "N/A", streamUrl: "http://radio.virginradio.co.uk/stream", description: "Latest pop hits" },
    ];
    let currentStation = 0;
    let isPlaying = false;
    let likedStations = [];
    let isDarkMode = false;
    const audio = new Audio();
    const container = document.getElementById('container');
    const stationBox = document.getElementById('station-box');
    const stationNameText = document.getElementById('station-name-text');
    const frequency = document.getElementById('frequency');
    const status = document.getElementById('status');
    const stationName = document.getElementById('station-name');
    const genre = document.getElementById('genre');
    const description = document.getElementById('description');
    const volume = document.getElementById('volume');
    const volumeValue = document.getElementById('volume-value');
    const playPauseBtn = document.getElementById('play-pause-btn');
    const likeBtn = document.getElementById('like-btn');
    const recommendPanel = document.getElementById('recommend-panel');
    const recommendList = document.getElementById('recommend-list');
    const prefsPanel = document.getElementById('prefs-panel');
    let classifier;

    (async () => {
      classifier = await transformers.pipeline('sentiment-analysis', 'Xenova/distilbert-base-uncased-finetuned-sst-2-english');
      console.log('Sentiment analysis model loaded');
      updateUI();
    })();

    function updateUI() {
      const station = stations[currentStation];
      stationNameText.textContent = station.name;
      frequency.textContent = `${station.frequency}`;
      status.style.background = isPlaying ? '#10b981' : '#ef4444';
      stationName.textContent = station.name;
      genre.textContent = station.genre;
      description.textContent = station.description;
      stationBox.classList.toggle('playing', isPlaying);
      playPauseBtn.innerHTML = isPlaying ? '<svg width="28" height="28" viewBox="0 0 24 24"><path d="M6 4h4v16H6zM14 4h4v16h-4z"/></svg>' : '<svg width="28" height="28" viewBox="0 0 24 24"><path d="M8 5v14l11-7z"/></svg>';
      likeBtn.classList.toggle('liked', likedStations.includes(station.id));
      likeBtn.querySelector('svg').setAttribute('fill', likedStations.includes(station.id) ? '#ef4444' : 'none');
      container.className = `container ${isDarkMode ? 'dark-mode' : ''}`;
    }

    function togglePlay() {
      if (isPlaying) {
        audio.pause();
      } else {
        audio.src = stations[currentStation].streamUrl;
        audio.play().catch(e => console.error('Playback failed:', e));
      }
      isPlaying = !isPlaying;
      updateUI();
    }

    function changeStation(direction) {
      audio.pause();
      currentStation = direction === 'next' 
        ? (currentStation + 1) % stations.length 
        : (currentStation - 1 + stations.length) % stations.length;
      isPlaying = true;
      audio.src = stations[currentStation].streamUrl;
      audio.play().catch(e => console.error('Playback failed:', e));
      updateUI();
    }

    volume.addEventListener('input', () => {
      audio.volume = volume.value / 100;
      volumeValue.textContent = `${volume.value}%`;
    });
    audio.volume = volume.value / 100;

    likeBtn.addEventListener('click', () => {
      const stationId = stations[currentStation].id;
      if (likedStations.includes(stationId)) {
        likedStations = likedStations.filter(id => id !== stationId);
      } else {
        likedStations.push(stationId);
      }
      updateUI();
      if (recommendPanel.style.display === 'block') updateRecommendations();
    });

    document.getElementById('theme-btn').addEventListener('click', () => {
      isDarkMode = !isDarkMode;
      updateUI();
    });

    playPauseBtn.addEventListener('click', togglePlay);
    document.getElementById('prev-btn').addEventListener('click', () => changeStation('prev'));
    document.getElementById('next-btn').addEventListener('click', () => changeStation('next'));

    document.getElementById('recommend-btn').addEventListener('click', () => {
      recommendPanel.style.display = recommendPanel.style.display === 'block' ? 'none' : 'block';
      if (recommendPanel.style.display === 'block') updateRecommendations();
    });

    document.getElementById('close-recommend').addEventListener('click', () => {
      recommendPanel.style.display = 'none';
    });

    async function updateRecommendations() {
      if (!classifier) return;
      recommendList.innerHTML = 'Loading...';
      const listenedStations = stations.filter(s => likedStations.includes(s.id));
      const descriptions = listenedStations.map(s => s.description);
      const sentiments = await classifier(descriptions);
      const recommended = stations.filter(station => 
        !likedStations.includes(station.id) && 
        sentiments.some((s, idx) => s.label === 'POSITIVE' && station.genre === listenedStations[idx].genre)
      );
      recommendList.innerHTML = recommended.length ? recommended.map(station => `
        <div class="station-item">
          <div style="width:50px; height:50px; background:white; border:2px solid red; border-radius:5px; margin-right:10px;"></div>
          <div>
            <h4>${station.name}</h4>
            <p>${station.genre}${station.frequency}</p>
          </div>
        </div>
      `).join('') : 'No recommendations yet. Like some stations!';
    }

    document.getElementById('prefs-btn').addEventListener('click', () => {
      prefsPanel.style.display = prefsPanel.style.display === 'block' ? 'none' : 'block';
    });

    document.getElementById('close-prefs').addEventListener('click', () => {
      prefsPanel.style.display = 'none';
    });
  </script>
</body>
</html>