PageSourceSearch

https://nonstopradio.app/stations/

html nonstopradio.app collected 2026-10-02 01:14:23 UTC 10,095 bytes, 363 lines download raw bytes

1<!doctype html>
2<html lang="en">
3<head>
4  <meta charset="utf-8" />
5  <title>New Stations – Quick Add | Non Stop Radio</title>
6  <link rel="canonical" href="https://nonstopradio.app/stations/" />
7  <meta name="robots" content="noindex, follow" />
8  <meta name="viewport" content="width=device-width, initial-scale=1" />
9  <meta name="description"
10        content="Preview new stations that will be added to Non Stop Radio. Open this page on your Android device and add them straight to your favourites with one tap.">
11
12  <style>
13    :root {
14      color-scheme: dark;
15      --bg: #050c14;
16      --card-bg: #0f2230;
17      --accent: #ffd700;
18      --accent-soft: #ffe680;
19      --text-main: #f5f7fa;
20      --text-muted: #9fb3c8;
21      --border-subtle: #1e3647;
22    }
23
24    * { box-sizing: border-box; }
25
26    body {
27      margin: 0;
28      min-height: 100vh;
29      display: flex;
30      justify-content: center;
31      align-items: center;
32      background: radial-gradient(circle at top, #132a3b 0, #050c14 55%);
33      color: var(--text-main);
34      font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
35    }
36
37    .page {
38      width: 100%;
39      max-width: 980px;
40      padding: 2.5rem 1.5rem 2.75rem;
41    }
42
43    .card {
44      background: rgba(15, 34, 48, 0.96);
45      border-radius: 24px;
46      padding: 2.1rem 1.7rem 1.9rem;
47      border: 1px solid var(--border-subtle);
48      box-shadow: 0 18px 60px rgba(0, 0, 0, 0.45);
49    }
50
51    @media (max-width: 640px) {
52      .card {
53        padding: 1.7rem 1.3rem 1.7rem;
54      }
55    }
56
57    .header {
58      margin-bottom: 1.4rem;
59    }
60
61    .eyebrow {
62      font-size: .78rem;
63      text-transform: uppercase;
64      letter-spacing: .09em;
65      color: var(--accent-soft);
66      opacity: .9;
67    }
68
69    .title {
70      margin: .25rem 0 .45rem;
71      font-size: clamp(1.6rem, 3vw, 1.9rem);
72    }
73
74    .subtitle {
75      margin: 0;
76      font-size: .95rem;
77      color: var(--text-muted);
78      max-width: 48rem;
79    }
80
81    .stations-grid {
82      margin-top: 1.2rem;
83      display: grid;
84      grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
85      gap: 0.9rem 1rem;
86    }
87
88    .station-card {
89      display: flex;
90      align-items: flex-start;
91      gap: .75rem;
92      padding: .85rem .85rem 1rem;
93      border-radius: 16px;
94      background: rgba(4, 14, 24, 0.9);
95      border: 1px solid rgba(30, 54, 71, 0.9);
96    }
97
98    .station-icon {
99      width: 48px;
100      height: 48px;
101      border-radius: 14px;
102      background: #122536;
103      flex-shrink: 0;
104      object-fit: cover;
105      display: block;
106    }
107
108    .station-icon.placeholder {
109      display: flex;
110      align-items: center;
111      justify-content: center;
112      font-weight: 700;
113      font-size: 1.1rem;
114      color: var(--accent-soft);
115      text-transform: uppercase;
116    }
117
118    .station-body {
119      min-width: 0;
120      flex: 1;
121    }
122
123    .station-name {
124      font-size: .98rem;
125      font-weight: 700;
126      margin: 0 0 .1rem;
127    }
128
129    .station-meta {
130      font-size: .78rem;
131      color: var(--text-muted);
132      margin: 0 0 .35rem;
133    }
134
135    .station-meta span + span::before {
136      content: " · ";
137      opacity: .8;
138    }
139
140    .station-homepage {
141      font-size: .8rem;
142      color: var(--accent-soft);
143      text-decoration: none;
144      word-break: break-all;
145    }
146
147    .station-homepage:hover {
148      text-decoration: underline;
149    }
150
151    .station-footer {
152      margin-top: .5rem;
153      display: flex;
154      justify-content: flex-start;
155      align-items: center;
156      gap: .4rem;
157      font-size: .76rem;
158      color: var(--text-muted);
159    }
160
161    .btn {
162      display: inline-flex;
163      align-items: center;
164      justify-content: center;
165      padding: .5rem 1.1rem;
166      border-radius: 999px;
167      border: 1px solid transparent;
168      font-size: .8rem;
169      font-weight: 600;
170      cursor: pointer;
171      background: var(--accent);
172      color: #222;
173      box-shadow: 0 8px 24px rgba(0, 0, 0, 0.6);
174      white-space: nowrap;
175      flex-shrink: 0;
176    }
177
178    .btn:hover {
179      background: #ffe36a;
180    }
181
182    /* ---- Support block (no icon) ---- */
183    .support-card {
184      margin-top: 1rem;
185      padding: .95rem 1rem;
186      border-radius: 18px;
187      background: rgba(4, 14, 24, 0.9);
188      border: 1px solid rgba(30, 54, 71, 0.9);
189      display: flex;
190      gap: .9rem;
191      align-items: center;
192      justify-content: space-between;
193    }
194
195    .support-text {
196      min-width: 0;
197    }
198
199    .support-title {
200      margin: 0 0 .25rem;
201      font-size: .95rem;
202      font-weight: 750;
203    }
204
205    .support-desc {
206      margin: 0;
207      font-size: .85rem;
208      color: var(--text-muted);
209      line-height: 1.35;
210    }
211
212    .support-actions {
213      display: flex;
214      align-items: center;
215      gap: .6rem;
216      flex-shrink: 0;
217    }
218
219    .btn-support {
220      background: transparent;
221      color: var(--accent-soft);
222      border: 1px solid rgba(255, 215, 0, 0.5);
223      box-shadow: none;
224      padding: .55rem 1.05rem;
225    }
226
227    .btn-support:hover {
228      background: rgba(6, 18, 30, 0.9);
229      border-color: var(--accent);
230    }
231    /* ---- End support block ---- */
232
233    .tip {
234      margin-top: 1.2rem;
235      font-size: .78rem;
236      color: var(--text-muted);
237      border-top: 1px dashed var(--border-subtle);
238      padding-top: .7rem;
239    }
240  </style>
241</head>
242<body>
243  <main class="page">
244    <section class="card">
245      <header class="header">
246        <div class="eyebrow">Upcoming stations</div>
247        <h1 class="title">New Stations &amp; Quick Add</h1>
248        <p class="subtitle">
249          These stations will be included in the next Non Stop Radio update.
250          If you want to add any of them right now, open this page on your Android phone
251          where the Non Stop Radio app is installed and tap “Add to favourites”.
252        </p>
253      </header>
254
255      <section id="stations-grid" class="stations-grid" aria-live="polite"></section>
256
257      <!-- Support block (no icon) -->
258      <section class="support-card" aria-label="Optional support">
259        <div class="support-text">
260          <h2 class="support-title">Support NSR (optional)</h2>
261          <p class="support-desc">
262            NSR is free and ad-free. If this page helps you add stations faster, optional support helps cover hosting and development time.
263            No features are unlocked.
264          </p>
265        </div>
266
267        <div class="support-actions">
268          <a class="btn btn-support" href="/support/" aria-label="Support Non Stop Radio">
269            Support NSR
270          </a>
271        </div>
272      </section>
273
274      <p class="tip">
275        Tip: If tapping the button doesn’t open the app, it will try to fall back to Google Play.
276      </p>
277    </section>
278  </main>
279
280  
280<script>
281    (async function () {
282      const grid = document.getElementById('stations-grid');
283      let stations = [];
284
285      try {
286        const res = await fetch('./stations.json', { cache: 'no-store' });
287        stations = await res.json();
288      } catch (e) {
289        console.error('Failed to load stations.json', e);
290        grid.innerHTML = '<p>Could not load stations. Please try again later.</p>';
291        return;
292      }
293
294      function openDeepLink(url) {
295        // Προσπαθεί να ανοίξει την εφαρμογή· αν δεν υπάρχει, πάει Google Play
296        window.location.href = url;
297        setTimeout(() => {
298          window.location.href =
299            'https://play.google.com/store/apps/details?id=com.assistant.greekradio';
300        }, 1200);
301      }
302
303      stations.forEach(st => {
304        const card = document.createElement('article');
305        card.className = 'station-card';
306
307        const name = st.name || 'Unknown station';
308        const country = st.country || '';
309        const tagsText = Array.isArray(st.tags) ? st.tags.join(' · ') : (st.tags || '');
310        const homepage = st.homepage || '';
311        const deeplink = st.deeplink;
312
313        const iconHtml = (st.favicon && String(st.favicon).trim())
314          ? `<img class="station-icon" src="${st.favicon}" alt="${name} logo" loading="lazy">`
315          : `<div class="station-icon placeholder">${name.charAt(0)}</div>`;
316
317        card.innerHTML = `
318          ${iconHtml}
319          <div class="station-body">
320            <h2 class="station-name">${name}</h2>
321            <p class="station-meta">
322              ${country ? `<span>${country}</span>` : ''}
323              ${tagsText ? `<span>${tagsText}</span>` : ''}
324            </p>
325            ${homepage ? `<a class="station-homepage" href="${homepage}" target="_blank" rel="noopener">Homepage</a>` : ''}
326            <div class="station-footer">
327              <button class="btn" type="button">Add to favourites</button>
328            </div>
329          </div>
330        `;
331
332        const btn = card.querySelector('.btn');
333        if (deeplink && btn) {
334          btn.addEventListener('click', () => openDeepLink(deeplink));
335        } else if (btn) {
336          btn.disabled = true;
337          btn.textContent = 'No deeplink';
338        }
339
340        const img = card.querySelector('img.station-icon');
341        if (img) {
342          img.addEventListener('error', () => {
343            img.replaceWith(createPlaceholderIcon(name));
344          });
345        }
346
347        grid.appendChild(card);
348      });
349
350      function createPlaceholderIcon(name) {
351        const div = document.createElement('div');
352        div.className = 'station-icon placeholder';
353        div.textContent = (name || '?').charAt(0);
354        return div;
355      }
356    })();
357  </script>
357
358  
358<script src="/assets/i18n-fr.js"></script>
358
359  
359<script src="/assets/i18n-es.js"></script>
359
360  
360<script src="/assets/i18n-it.js"></script>
360
361  
361<script src="/assets/i18n.js"></script>
361
362</body>
363</html>

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.