Spaces:
Running
Running
Update index.html
Browse files- index.html +249 -22
index.html
CHANGED
@@ -1,29 +1,256 @@
|
|
1 |
<!DOCTYPE html>
|
2 |
<html lang="en">
|
3 |
-
|
4 |
<head>
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
10 |
</head>
|
11 |
-
|
12 |
<body>
|
13 |
-
|
14 |
-
<
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
<
|
22 |
-
|
23 |
-
|
24 |
-
<
|
25 |
-
|
26 |
-
|
27 |
-
</
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
28 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
29 |
</html>
|
|
|
1 |
<!DOCTYPE html>
|
2 |
<html lang="en">
|
|
|
3 |
<head>
|
4 |
+
<meta charset="UTF-8">
|
5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6 |
+
<title>Cosmick-Radio</title>
|
7 |
+
<style>
|
8 |
+
body { margin: 0; font-family: Arial, sans-serif; transition: background-color 0.5s, color 0.5s; }
|
9 |
+
.container { display: flex; flex-direction: column; height: 100vh; padding: 20px; background: white; color: #1f2937; }
|
10 |
+
.dark-mode { background-color: #1f2937; color: white; }
|
11 |
+
.app-bar { display: flex; justify-content: space-between; align-items: center; padding: 10px; border-bottom: 1px solid rgba(156, 163, 175, 0.2); }
|
12 |
+
.main-content { display: flex; flex: 1; overflow: hidden; }
|
13 |
+
.player { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; }
|
14 |
+
.station-box {
|
15 |
+
width: 200px;
|
16 |
+
height: 200px;
|
17 |
+
background: white;
|
18 |
+
border: 3px solid red;
|
19 |
+
border-radius: 10px;
|
20 |
+
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.2);
|
21 |
+
margin-bottom: 20px;
|
22 |
+
display: flex;
|
23 |
+
align-items: center;
|
24 |
+
justify-content: center;
|
25 |
+
}
|
26 |
+
.station-name-text {
|
27 |
+
font-size: 24px;
|
28 |
+
font-weight: bold;
|
29 |
+
text-align: center;
|
30 |
+
background: linear-gradient(90deg, red, orange, yellow, green, blue, indigo, violet);
|
31 |
+
-webkit-background-clip: text;
|
32 |
+
-webkit-text-fill-color: transparent;
|
33 |
+
}
|
34 |
+
.station-box.playing { animation: pulse 3s infinite; }
|
35 |
+
.controls button { padding: 10px; margin: 0 10px; background: #9333ea; color: white; border: none; border-radius: 50%; cursor: pointer; }
|
36 |
+
.controls button:hover { background: #7e22ce; }
|
37 |
+
.volume { display: flex; align-items: center; margin: 20px 0; }
|
38 |
+
.volume input { width: 200px; margin: 0 10px; }
|
39 |
+
.actions button { margin: 0 10px; background: none; border: none; cursor: pointer; }
|
40 |
+
.liked { color: #ef4444; fill: #ef4444; }
|
41 |
+
.panel { width: 300px; background: rgba(0, 0, 0, 0.1); padding: 20px; overflow-y: auto; }
|
42 |
+
.station-item { display: flex; align-items: center; padding: 10px; }
|
43 |
+
@keyframes pulse { 0% { transform: scale(1); } 50% { transform: scale(1.05); } 100% { transform: scale(1); } }
|
44 |
+
</style>
|
45 |
</head>
|
|
|
46 |
<body>
|
47 |
+
<div id="container" class="container">
|
48 |
+
<div class="app-bar">
|
49 |
+
<div>
|
50 |
+
<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>
|
51 |
+
<h1 style="display:inline-block; margin-left:5px;">Cosmick-Radio</h1>
|
52 |
+
</div>
|
53 |
+
<div>
|
54 |
+
<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>
|
55 |
+
<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>
|
56 |
+
</div>
|
57 |
+
</div>
|
58 |
+
<div class="main-content">
|
59 |
+
<div class="player">
|
60 |
+
<div id="station-box" class="station-box">
|
61 |
+
<span id="station-name-text" class="station-name-text">Radio Paradise</span>
|
62 |
+
</div>
|
63 |
+
<div id="station-info" style="text-align:center;">
|
64 |
+
<span id="frequency">N/A</span>
|
65 |
+
<span id="status" style="display:inline-block; width:8px; height:8px; border-radius:50%; background:red; margin-left:5px;"></span>
|
66 |
+
<h2 id="station-name">Radio Paradise</h2>
|
67 |
+
<p id="genre">Eclectic</p>
|
68 |
+
<p id="description">Eclectic mix of rock, world, and more</p>
|
69 |
+
</div>
|
70 |
+
<div class="volume">
|
71 |
+
<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>
|
72 |
+
<input type="range" id="volume" min="0" max="100" value="70">
|
73 |
+
<span id="volume-value">70%</span>
|
74 |
+
</div>
|
75 |
+
<div class="controls">
|
76 |
+
<button id="prev-btn"><svg width="24" height="24" viewBox="0 0 24 24"><path d="M6 6h2v12H6zm3.5 6l8.5 6V6z"/></svg></button>
|
77 |
+
<button id="play-pause-btn"><svg width="28" height="28" viewBox="0 0 24 24"><path d="M8 5v14l11-7z"/></svg></button>
|
78 |
+
<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>
|
79 |
+
</div>
|
80 |
+
<div class="actions" style="margin-top:20px;">
|
81 |
+
<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>
|
82 |
+
<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>
|
83 |
+
<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>
|
84 |
+
</div>
|
85 |
+
</div>
|
86 |
+
<div id="recommend-panel" class="panel" style="display:none;">
|
87 |
+
<h3>AI Recommendations</h3>
|
88 |
+
<button id="close-recommend">×</button>
|
89 |
+
<div id="recommend-list"></div>
|
90 |
+
</div>
|
91 |
+
<div id="prefs-panel" class="panel" style="display:none;">
|
92 |
+
<h3>AI Personalization</h3>
|
93 |
+
<button id="close-prefs">×</button>
|
94 |
+
<p>Preferences coming soon!</p>
|
95 |
+
</div>
|
96 |
+
</div>
|
97 |
+
</div>
|
98 |
+
|
99 |
+
<script src="https://cdn.jsdelivr.net/npm/@huggingface/transformers@latest/dist/transformers.min.js"></script>
|
100 |
+
<script>
|
101 |
+
const stations = [
|
102 |
+
{ 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" },
|
103 |
+
{ 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" },
|
104 |
+
// Classical Stations
|
105 |
+
{ 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" },
|
106 |
+
{ id: 4, name: "WFMT Classical", genre: "Classical", frequency: "N/A", streamUrl: "http://stream.wfmt.com/main-mp3", description: "Classical music from Chicago" },
|
107 |
+
{ 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" },
|
108 |
+
{ id: 6, name: "WCRB Classical", genre: "Classical", frequency: "N/A", streamUrl: "http://audio.wgbh.org:8004/stream", description: "Boston’s classical music station" },
|
109 |
+
{ 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" },
|
110 |
+
{ id: 8, name: "KUSC Classical", genre: "Classical", frequency: "N/A", streamUrl: "http://stream.kusc.org/kusc-128k.mp3", description: "Classical music from Los Angeles" },
|
111 |
+
{ id: 9, name: "Classical WETA", genre: "Classical", frequency: "N/A", streamUrl: "http://weta.streamguys1.com/classical", description: "Classical music from Washington, DC" },
|
112 |
+
{ id: 10, name: "France Musique", genre: "Classical", frequency: "N/A", streamUrl: "http://direct.francemusique.fr/live/francemusique-midfi.mp3", description: "French classical music" },
|
113 |
+
// Smooth Jazz Stations
|
114 |
+
{ 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" },
|
115 |
+
{ id: 12, name: "Jazz24", genre: "Smooth Jazz", frequency: "N/A", streamUrl: "http://live.wostreaming.net/direct/ppm-jazz24aac256-ibc1", description: "24/7 smooth jazz" },
|
116 |
+
{ 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" },
|
117 |
+
{ id: 14, name: "Smooth Jazz Global", genre: "Smooth Jazz", frequency: "N/A", streamUrl: "http://smoothjazzglobal.streamguys1.com/sjglobal", description: "Global smooth jazz hits" },
|
118 |
+
{ id: 15, name: "The Wave Smooth Jazz", genre: "Smooth Jazz", frequency: "N/A", streamUrl: "http://stream.zeno.fm/xv8nq5v5v5zuv", description: "Relaxing smooth jazz" },
|
119 |
+
{ id: 16, name: "Jazz FM Smooth", genre: "Smooth Jazz", frequency: "N/A", streamUrl: "http://listen.jazzfm.com/jazzsmooth", description: "Smooth jazz from the UK" },
|
120 |
+
{ id: 17, name: "Smooth Jazz 247", genre: "Smooth Jazz", frequency: "N/A", streamUrl: "http://stream.smoothjazz247.com/live", description: "Non-stop smooth jazz" },
|
121 |
+
// Additional Stations
|
122 |
+
{ id: 18, name: "KEXP", genre: "Eclectic", frequency: "N/A", streamUrl: "http://live-mp3-128.kexp.org/kexp128.mp3", description: "Eclectic music from Seattle" },
|
123 |
+
{ id: 19, name: "Hot 108 Jamz", genre: "Hip Hop", frequency: "N/A", streamUrl: "http://108.61.30.179:4010/", description: "Hip hop hits" },
|
124 |
+
{ id: 20, name: "Virgin Radio UK", genre: "Pop", frequency: "N/A", streamUrl: "http://radio.virginradio.co.uk/stream", description: "Latest pop hits" },
|
125 |
+
];
|
126 |
+
let currentStation = 0;
|
127 |
+
let isPlaying = false;
|
128 |
+
let likedStations = [];
|
129 |
+
let isDarkMode = false;
|
130 |
+
const audio = new Audio();
|
131 |
+
const container = document.getElementById('container');
|
132 |
+
const stationBox = document.getElementById('station-box');
|
133 |
+
const stationNameText = document.getElementById('station-name-text');
|
134 |
+
const frequency = document.getElementById('frequency');
|
135 |
+
const status = document.getElementById('status');
|
136 |
+
const stationName = document.getElementById('station-name');
|
137 |
+
const genre = document.getElementById('genre');
|
138 |
+
const description = document.getElementById('description');
|
139 |
+
const volume = document.getElementById('volume');
|
140 |
+
const volumeValue = document.getElementById('volume-value');
|
141 |
+
const playPauseBtn = document.getElementById('play-pause-btn');
|
142 |
+
const likeBtn = document.getElementById('like-btn');
|
143 |
+
const recommendPanel = document.getElementById('recommend-panel');
|
144 |
+
const recommendList = document.getElementById('recommend-list');
|
145 |
+
const prefsPanel = document.getElementById('prefs-panel');
|
146 |
+
let classifier;
|
147 |
+
|
148 |
+
(async () => {
|
149 |
+
classifier = await transformers.pipeline('sentiment-analysis', 'Xenova/distilbert-base-uncased-finetuned-sst-2-english');
|
150 |
+
console.log('Sentiment analysis model loaded');
|
151 |
+
updateUI();
|
152 |
+
})();
|
153 |
+
|
154 |
+
function updateUI() {
|
155 |
+
const station = stations[currentStation];
|
156 |
+
stationNameText.textContent = station.name;
|
157 |
+
frequency.textContent = `${station.frequency}`;
|
158 |
+
status.style.background = isPlaying ? '#10b981' : '#ef4444';
|
159 |
+
stationName.textContent = station.name;
|
160 |
+
genre.textContent = station.genre;
|
161 |
+
description.textContent = station.description;
|
162 |
+
stationBox.classList.toggle('playing', isPlaying);
|
163 |
+
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>';
|
164 |
+
likeBtn.classList.toggle('liked', likedStations.includes(station.id));
|
165 |
+
likeBtn.querySelector('svg').setAttribute('fill', likedStations.includes(station.id) ? '#ef4444' : 'none');
|
166 |
+
container.className = `container ${isDarkMode ? 'dark-mode' : ''}`;
|
167 |
+
}
|
168 |
+
|
169 |
+
function togglePlay() {
|
170 |
+
if (isPlaying) {
|
171 |
+
audio.pause();
|
172 |
+
} else {
|
173 |
+
audio.src = stations[currentStation].streamUrl;
|
174 |
+
audio.play().catch(e => console.error('Playback failed:', e));
|
175 |
+
}
|
176 |
+
isPlaying = !isPlaying;
|
177 |
+
updateUI();
|
178 |
+
}
|
179 |
+
|
180 |
+
function changeStation(direction) {
|
181 |
+
audio.pause();
|
182 |
+
currentStation = direction === 'next'
|
183 |
+
? (currentStation + 1) % stations.length
|
184 |
+
: (currentStation - 1 + stations.length) % stations.length;
|
185 |
+
isPlaying = true;
|
186 |
+
audio.src = stations[currentStation].streamUrl;
|
187 |
+
audio.play().catch(e => console.error('Playback failed:', e));
|
188 |
+
updateUI();
|
189 |
+
}
|
190 |
|
191 |
+
volume.addEventListener('input', () => {
|
192 |
+
audio.volume = volume.value / 100;
|
193 |
+
volumeValue.textContent = `${volume.value}%`;
|
194 |
+
});
|
195 |
+
audio.volume = volume.value / 100;
|
196 |
+
|
197 |
+
likeBtn.addEventListener('click', () => {
|
198 |
+
const stationId = stations[currentStation].id;
|
199 |
+
if (likedStations.includes(stationId)) {
|
200 |
+
likedStations = likedStations.filter(id => id !== stationId);
|
201 |
+
} else {
|
202 |
+
likedStations.push(stationId);
|
203 |
+
}
|
204 |
+
updateUI();
|
205 |
+
if (recommendPanel.style.display === 'block') updateRecommendations();
|
206 |
+
});
|
207 |
+
|
208 |
+
document.getElementById('theme-btn').addEventListener('click', () => {
|
209 |
+
isDarkMode = !isDarkMode;
|
210 |
+
updateUI();
|
211 |
+
});
|
212 |
+
|
213 |
+
playPauseBtn.addEventListener('click', togglePlay);
|
214 |
+
document.getElementById('prev-btn').addEventListener('click', () => changeStation('prev'));
|
215 |
+
document.getElementById('next-btn').addEventListener('click', () => changeStation('next'));
|
216 |
+
|
217 |
+
document.getElementById('recommend-btn').addEventListener('click', () => {
|
218 |
+
recommendPanel.style.display = recommendPanel.style.display === 'block' ? 'none' : 'block';
|
219 |
+
if (recommendPanel.style.display === 'block') updateRecommendations();
|
220 |
+
});
|
221 |
+
|
222 |
+
document.getElementById('close-recommend').addEventListener('click', () => {
|
223 |
+
recommendPanel.style.display = 'none';
|
224 |
+
});
|
225 |
+
|
226 |
+
async function updateRecommendations() {
|
227 |
+
if (!classifier) return;
|
228 |
+
recommendList.innerHTML = 'Loading...';
|
229 |
+
const listenedStations = stations.filter(s => likedStations.includes(s.id));
|
230 |
+
const descriptions = listenedStations.map(s => s.description);
|
231 |
+
const sentiments = await classifier(descriptions);
|
232 |
+
const recommended = stations.filter(station =>
|
233 |
+
!likedStations.includes(station.id) &&
|
234 |
+
sentiments.some((s, idx) => s.label === 'POSITIVE' && station.genre === listenedStations[idx].genre)
|
235 |
+
);
|
236 |
+
recommendList.innerHTML = recommended.length ? recommended.map(station => `
|
237 |
+
<div class="station-item">
|
238 |
+
<div style="width:50px; height:50px; background:white; border:2px solid red; border-radius:5px; margin-right:10px;"></div>
|
239 |
+
<div>
|
240 |
+
<h4>${station.name}</h4>
|
241 |
+
<p>${station.genre} • ${station.frequency}</p>
|
242 |
+
</div>
|
243 |
+
</div>
|
244 |
+
`).join('') : 'No recommendations yet. Like some stations!';
|
245 |
+
}
|
246 |
+
|
247 |
+
document.getElementById('prefs-btn').addEventListener('click', () => {
|
248 |
+
prefsPanel.style.display = prefsPanel.style.display === 'block' ? 'none' : 'block';
|
249 |
+
});
|
250 |
+
|
251 |
+
document.getElementById('close-prefs').addEventListener('click', () => {
|
252 |
+
prefsPanel.style.display = 'none';
|
253 |
+
});
|
254 |
+
</script>
|
255 |
+
</body>
|
256 |
</html>
|