feat(now-playing): ГУСЬ (radiogoose) — now-playing + обложки, починка потоков
Сеть ГУСЬ (16 каналов) на AzuraCast (radiogoose.ru). Потоки в каталоге были многострочными (url1\nurl2) → клиент рвал воспроизведение, health-check метил offline, станции скрывались. Почищены на канонический https://radiogoose.ru/listen/{slug}/play. GooseNowPlayingService (@Interval 30с): AzuraCast API /api/nowplaying/{slug} → now_playing.song {artist,title,art}, ingest + isOnline=true. Исключён из ICY-поллера. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -6636,7 +6636,7 @@
|
|||||||
"name": "Радио Гусь",
|
"name": "Радио Гусь",
|
||||||
"bitrate": "320",
|
"bitrate": "320",
|
||||||
"site": "https://radiogoose.ru/",
|
"site": "https://radiogoose.ru/",
|
||||||
"stream": "http://stream.radiogoose.ru/listen/radiogoose/play\nhttp://stream4.radiogoose.ru/listen/radiogoose/play",
|
"stream": "https://radiogoose.ru/listen/radiogoose/play",
|
||||||
"type": "mp3",
|
"type": "mp3",
|
||||||
"iconText": "",
|
"iconText": "",
|
||||||
"textColor": "#FF834B",
|
"textColor": "#FF834B",
|
||||||
@@ -6651,7 +6651,7 @@
|
|||||||
"name": "ГУСЬ.Бигрум",
|
"name": "ГУСЬ.Бигрум",
|
||||||
"bitrate": "320",
|
"bitrate": "320",
|
||||||
"site": "https://radiogoose.ru/",
|
"site": "https://radiogoose.ru/",
|
||||||
"stream": "http://stream.radiogoose.ru/listen/bigroom/play\nhttp://stream5.radiogoose.ru/listen/bigroom/play",
|
"stream": "https://radiogoose.ru/listen/bigroom/play",
|
||||||
"type": "mp3",
|
"type": "mp3",
|
||||||
"iconText": "ГУСЬ\\Бигрум",
|
"iconText": "ГУСЬ\\Бигрум",
|
||||||
"textColor": "#FF834B",
|
"textColor": "#FF834B",
|
||||||
@@ -6666,7 +6666,7 @@
|
|||||||
"name": "ГУСЬ.Рус",
|
"name": "ГУСЬ.Рус",
|
||||||
"bitrate": "320",
|
"bitrate": "320",
|
||||||
"site": "https://radiogoose.ru/",
|
"site": "https://radiogoose.ru/",
|
||||||
"stream": "http://stream.radiogoose.ru/listen/rus/play\nhttp://stream1.radiogoose.ru/listen/rus/play",
|
"stream": "https://radiogoose.ru/listen/rus/play",
|
||||||
"type": "mp3",
|
"type": "mp3",
|
||||||
"iconText": "ГУСЬ\\Рус",
|
"iconText": "ГУСЬ\\Рус",
|
||||||
"textColor": "#FF834B",
|
"textColor": "#FF834B",
|
||||||
@@ -6681,7 +6681,7 @@
|
|||||||
"name": "ГУСЬ.Олдскул",
|
"name": "ГУСЬ.Олдскул",
|
||||||
"bitrate": "320",
|
"bitrate": "320",
|
||||||
"site": "https://radiogoose.ru/",
|
"site": "https://radiogoose.ru/",
|
||||||
"stream": "http://stream.radiogoose.ru/listen/oldschool/play\nhttp://stream1.radiogoose.ru/listen/oldschool/play",
|
"stream": "https://radiogoose.ru/listen/oldschool/play",
|
||||||
"type": "mp3",
|
"type": "mp3",
|
||||||
"iconText": "ГУСЬ\\Олдскул",
|
"iconText": "ГУСЬ\\Олдскул",
|
||||||
"textColor": "#FF834B",
|
"textColor": "#FF834B",
|
||||||
@@ -6696,7 +6696,7 @@
|
|||||||
"name": "ГУСЬ.Рок",
|
"name": "ГУСЬ.Рок",
|
||||||
"bitrate": "320",
|
"bitrate": "320",
|
||||||
"site": "https://radiogoose.ru/",
|
"site": "https://radiogoose.ru/",
|
||||||
"stream": "http://stream.radiogoose.ru/listen/rock/play\nhttp://stream3.radiogoose.ru/listen/rock/play",
|
"stream": "https://radiogoose.ru/listen/rock/play",
|
||||||
"type": "mp3",
|
"type": "mp3",
|
||||||
"iconText": "ГУСЬ\\Рок",
|
"iconText": "ГУСЬ\\Рок",
|
||||||
"textColor": "#FF834B",
|
"textColor": "#FF834B",
|
||||||
@@ -6711,7 +6711,7 @@
|
|||||||
"name": "ГУСЬ.Мидтемпо",
|
"name": "ГУСЬ.Мидтемпо",
|
||||||
"bitrate": "320",
|
"bitrate": "320",
|
||||||
"site": "https://radiogoose.ru/",
|
"site": "https://radiogoose.ru/",
|
||||||
"stream": "http://stream.radiogoose.ru/listen/midtempo/play\nhttp://stream1.radiogoose.ru/listen/midtempo/play",
|
"stream": "https://radiogoose.ru/listen/midtempo/play",
|
||||||
"type": "mp3",
|
"type": "mp3",
|
||||||
"iconText": "ГУСЬ\\Мидтемпо",
|
"iconText": "ГУСЬ\\Мидтемпо",
|
||||||
"textColor": "#FF834B",
|
"textColor": "#FF834B",
|
||||||
@@ -6726,7 +6726,7 @@
|
|||||||
"name": "ГУСЬ.Транс",
|
"name": "ГУСЬ.Транс",
|
||||||
"bitrate": "320",
|
"bitrate": "320",
|
||||||
"site": "https://radiogoose.ru/",
|
"site": "https://radiogoose.ru/",
|
||||||
"stream": "http://stream.radiogoose.ru/listen/trance/play\nhttp://stream5.radiogoose.ru/listen/trance/play",
|
"stream": "https://radiogoose.ru/listen/trance/play",
|
||||||
"type": "mp3",
|
"type": "mp3",
|
||||||
"iconText": "ГУСЬ\\Транс",
|
"iconText": "ГУСЬ\\Транс",
|
||||||
"textColor": "#FF834B",
|
"textColor": "#FF834B",
|
||||||
@@ -6741,7 +6741,7 @@
|
|||||||
"name": "ГУСЬ.Фонк",
|
"name": "ГУСЬ.Фонк",
|
||||||
"bitrate": "320",
|
"bitrate": "320",
|
||||||
"site": "https://radiogoose.ru/",
|
"site": "https://radiogoose.ru/",
|
||||||
"stream": "http://stream.radiogoose.ru/listen/phonk/play\nhttp://stream3.radiogoose.ru/listen/phonk/play",
|
"stream": "https://radiogoose.ru/listen/phonk/play",
|
||||||
"type": "mp3",
|
"type": "mp3",
|
||||||
"iconText": "ГУСЬ\\Фонк",
|
"iconText": "ГУСЬ\\Фонк",
|
||||||
"textColor": "#FF834B",
|
"textColor": "#FF834B",
|
||||||
@@ -6756,7 +6756,7 @@
|
|||||||
"name": "ГУСЬ.Электро",
|
"name": "ГУСЬ.Электро",
|
||||||
"bitrate": "320",
|
"bitrate": "320",
|
||||||
"site": "https://radiogoose.ru/",
|
"site": "https://radiogoose.ru/",
|
||||||
"stream": "http://stream.radiogoose.ru/listen/electro/play\nhttp://stream4.radiogoose.ru/listen/electro/play",
|
"stream": "https://radiogoose.ru/listen/electro/play",
|
||||||
"type": "mp3",
|
"type": "mp3",
|
||||||
"iconText": "ГУСЬ\\Электро",
|
"iconText": "ГУСЬ\\Электро",
|
||||||
"textColor": "#FF834B",
|
"textColor": "#FF834B",
|
||||||
@@ -6771,7 +6771,7 @@
|
|||||||
"name": "ГУСЬ.Дипхаус",
|
"name": "ГУСЬ.Дипхаус",
|
||||||
"bitrate": "320",
|
"bitrate": "320",
|
||||||
"site": "https://radiogoose.ru/",
|
"site": "https://radiogoose.ru/",
|
||||||
"stream": "http://stream.radiogoose.ru/listen/deep/play\nhttp://stream2.radiogoose.ru/listen/deep/play",
|
"stream": "https://radiogoose.ru/listen/deep/play",
|
||||||
"type": "mp3",
|
"type": "mp3",
|
||||||
"iconText": "ГУСЬ\\Дипхаус",
|
"iconText": "ГУСЬ\\Дипхаус",
|
||||||
"textColor": "#FF834B",
|
"textColor": "#FF834B",
|
||||||
@@ -6786,7 +6786,7 @@
|
|||||||
"name": "ГУСЬ.Драм",
|
"name": "ГУСЬ.Драм",
|
||||||
"bitrate": "320",
|
"bitrate": "320",
|
||||||
"site": "https://radiogoose.ru/",
|
"site": "https://radiogoose.ru/",
|
||||||
"stream": "http://stream.radiogoose.ru/listen/dnb/play\nhttp://stream3.radiogoose.ru/listen/dnb/play",
|
"stream": "https://radiogoose.ru/listen/dnb/play",
|
||||||
"type": "mp3",
|
"type": "mp3",
|
||||||
"iconText": "ГУСЬ\\Драм",
|
"iconText": "ГУСЬ\\Драм",
|
||||||
"textColor": "#FF834B",
|
"textColor": "#FF834B",
|
||||||
@@ -6801,7 +6801,7 @@
|
|||||||
"name": "ГУСЬ.Денскор",
|
"name": "ГУСЬ.Денскор",
|
||||||
"bitrate": "320",
|
"bitrate": "320",
|
||||||
"site": "https://radiogoose.ru/",
|
"site": "https://radiogoose.ru/",
|
||||||
"stream": "http://stream.radiogoose.ru/listen/dancecore/play\nhttp://stream5.radiogoose.ru/listen/dancecore/play",
|
"stream": "https://radiogoose.ru/listen/dancecore/play",
|
||||||
"type": "mp3",
|
"type": "mp3",
|
||||||
"iconText": "ГУСЬ\\Денскор",
|
"iconText": "ГУСЬ\\Денскор",
|
||||||
"textColor": "#FF834B",
|
"textColor": "#FF834B",
|
||||||
@@ -6816,7 +6816,7 @@
|
|||||||
"name": "ГУСЬ.Технорейв",
|
"name": "ГУСЬ.Технорейв",
|
||||||
"bitrate": "320",
|
"bitrate": "320",
|
||||||
"site": "https://radiogoose.ru/",
|
"site": "https://radiogoose.ru/",
|
||||||
"stream": "http://stream.radiogoose.ru/listen/harddance/play\nhttp://stream1.radiogoose.ru/listen/harddance/play",
|
"stream": "https://radiogoose.ru/listen/harddance/play",
|
||||||
"type": "mp3",
|
"type": "mp3",
|
||||||
"iconText": "ГУСЬ\\Технорейв",
|
"iconText": "ГУСЬ\\Технорейв",
|
||||||
"textColor": "#FF834B",
|
"textColor": "#FF834B",
|
||||||
@@ -6831,7 +6831,7 @@
|
|||||||
"name": "ГУСЬ.Хардстайл",
|
"name": "ГУСЬ.Хардстайл",
|
||||||
"bitrate": "320",
|
"bitrate": "320",
|
||||||
"site": "https://radiogoose.ru/",
|
"site": "https://radiogoose.ru/",
|
||||||
"stream": "http://stream.radiogoose.ru/listen/hardstyle/play\nhttp://stream3.radiogoose.ru/listen/hardstyle/play",
|
"stream": "https://radiogoose.ru/listen/hardstyle/play",
|
||||||
"type": "mp3",
|
"type": "mp3",
|
||||||
"iconText": "ГУСЬ\\Хардстайл",
|
"iconText": "ГУСЬ\\Хардстайл",
|
||||||
"textColor": "#FF834B",
|
"textColor": "#FF834B",
|
||||||
@@ -6846,7 +6846,7 @@
|
|||||||
"name": "ГУСЬ.Хардкор",
|
"name": "ГУСЬ.Хардкор",
|
||||||
"bitrate": "320",
|
"bitrate": "320",
|
||||||
"site": "https://radiogoose.ru/",
|
"site": "https://radiogoose.ru/",
|
||||||
"stream": "http://stream.radiogoose.ru/listen/hardcore/play\nhttp://stream3.radiogoose.ru/listen/hardcore/play",
|
"stream": "https://radiogoose.ru/listen/hardcore/play",
|
||||||
"type": "mp3",
|
"type": "mp3",
|
||||||
"iconText": "ГУСЬ\\Хардкор",
|
"iconText": "ГУСЬ\\Хардкор",
|
||||||
"textColor": "#E74E5D",
|
"textColor": "#E74E5D",
|
||||||
@@ -6861,7 +6861,7 @@
|
|||||||
"name": "ГУСЬ.Новогодний",
|
"name": "ГУСЬ.Новогодний",
|
||||||
"bitrate": "320",
|
"bitrate": "320",
|
||||||
"site": "https://radiogoose.ru/",
|
"site": "https://radiogoose.ru/",
|
||||||
"stream": "http://stream.radiogoose.ru/listen/newyear/play\nhttp://stream4.radiogoose.ru/listen/newyear/play",
|
"stream": "https://radiogoose.ru/listen/newyear/play",
|
||||||
"type": "mp3",
|
"type": "mp3",
|
||||||
"iconText": "ГУСЬ\\Новогодний",
|
"iconText": "ГУСЬ\\Новогодний",
|
||||||
"textColor": "#4D8080",
|
"textColor": "#4D8080",
|
||||||
|
|||||||
89
src/now-playing/goose-now-playing.service.ts
Normal file
89
src/now-playing/goose-now-playing.service.ts
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
import { Injectable, Logger } from '@nestjs/common';
|
||||||
|
import { Interval } from '@nestjs/schedule';
|
||||||
|
import { PrismaService } from '../prisma/prisma.service';
|
||||||
|
import { NowPlayingService } from './now-playing.service';
|
||||||
|
|
||||||
|
// Ответ AzuraCast https://radiogoose.ru/api/nowplaying/{slug}
|
||||||
|
interface AzuraNowPlaying {
|
||||||
|
is_online?: boolean;
|
||||||
|
now_playing?: {
|
||||||
|
song?: {
|
||||||
|
artist?: string;
|
||||||
|
title?: string;
|
||||||
|
art?: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Now-playing для сети ГУСЬ (radiogoose.ru) — это AzuraCast, у него штатный
|
||||||
|
* публичный API текущего трека с обложкой:
|
||||||
|
* GET https://radiogoose.ru/api/nowplaying/{slug} → now_playing.song {artist,title,art}.
|
||||||
|
* slug = сегмент потока /listen/{slug}/play. is_online из ответа поправляет
|
||||||
|
* ошибочный offline-флаг (health-check ранее спотыкался о многострочный URL).
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class GooseNowPlayingService {
|
||||||
|
private readonly logger = new Logger(GooseNowPlayingService.name);
|
||||||
|
private readonly headers = { 'User-Agent': 'Mozilla/5.0' };
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private readonly prisma: PrismaService,
|
||||||
|
private readonly nowPlayingService: NowPlayingService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
@Interval(30000)
|
||||||
|
async pollGooseNowPlaying() {
|
||||||
|
const stations = await this.prisma.station.findMany({
|
||||||
|
where: { streamUrl: { contains: 'radiogoose.ru' } },
|
||||||
|
});
|
||||||
|
if (stations.length === 0) return;
|
||||||
|
|
||||||
|
let updated = 0;
|
||||||
|
|
||||||
|
await Promise.allSettled(
|
||||||
|
stations.map(async (station) => {
|
||||||
|
const slug = this.extractSlug(station.streamUrl);
|
||||||
|
if (!slug) return;
|
||||||
|
|
||||||
|
const res = await fetch(
|
||||||
|
`https://radiogoose.ru/api/nowplaying/${slug}`,
|
||||||
|
{ headers: this.headers },
|
||||||
|
);
|
||||||
|
if (!res.ok) return;
|
||||||
|
|
||||||
|
const data = (await res.json()) as AzuraNowPlaying;
|
||||||
|
if (data.is_online === false) return;
|
||||||
|
|
||||||
|
const song = data.now_playing?.song;
|
||||||
|
const artist = (song?.artist ?? '').trim();
|
||||||
|
const title = (song?.title ?? '').trim();
|
||||||
|
if (!artist || !title) return;
|
||||||
|
|
||||||
|
await this.nowPlayingService.ingest({
|
||||||
|
stationDbId: station.id,
|
||||||
|
stationNumericId: station.stationId,
|
||||||
|
artist,
|
||||||
|
song: title,
|
||||||
|
coverUrl: song?.art?.trim() || null,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!station.isOnline) {
|
||||||
|
await this.prisma.station.update({
|
||||||
|
where: { id: station.id },
|
||||||
|
data: { isOnline: true },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
updated++;
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
this.logger.log(`Goose poll: ${updated}/${stations.length} обновлено`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://radiogoose.ru/listen/bigroom/play → bigroom
|
||||||
|
private extractSlug(streamUrl: string): string | null {
|
||||||
|
const m = streamUrl.match(/\/listen\/([a-z0-9]+)\/play/i);
|
||||||
|
return m ? m[1].toLowerCase() : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -38,6 +38,7 @@ export class IcyNowPlayingService {
|
|||||||
'Radio ROKS',
|
'Radio ROKS',
|
||||||
'Unistar',
|
'Unistar',
|
||||||
'Zaicev FM',
|
'Zaicev FM',
|
||||||
|
'Гусь',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
NOT: { streamUrl: { contains: 'emgsound.ru' } },
|
NOT: { streamUrl: { contains: 'emgsound.ru' } },
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import { LoveNowPlayingService } from './love-now-playing.service';
|
|||||||
import { RoksNowPlayingService } from './roks-now-playing.service';
|
import { RoksNowPlayingService } from './roks-now-playing.service';
|
||||||
import { UnistarNowPlayingService } from './unistar-now-playing.service';
|
import { UnistarNowPlayingService } from './unistar-now-playing.service';
|
||||||
import { ZaycevNowPlayingService } from './zaycev-now-playing.service';
|
import { ZaycevNowPlayingService } from './zaycev-now-playing.service';
|
||||||
|
import { GooseNowPlayingService } from './goose-now-playing.service';
|
||||||
import { ChartsModule } from '../charts/charts.module';
|
import { ChartsModule } from '../charts/charts.module';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
@@ -26,6 +27,7 @@ import { ChartsModule } from '../charts/charts.module';
|
|||||||
RoksNowPlayingService,
|
RoksNowPlayingService,
|
||||||
UnistarNowPlayingService,
|
UnistarNowPlayingService,
|
||||||
ZaycevNowPlayingService,
|
ZaycevNowPlayingService,
|
||||||
|
GooseNowPlayingService,
|
||||||
],
|
],
|
||||||
exports: [NowPlayingService],
|
exports: [NowPlayingService],
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user