feat(now-playing): Love Radio через api.loveradio.ru (ICY шлёт мусор onlinestop56k)
ICY-потоки Love Radio отдают 'onlinestop56k' вместо трека. Берём текущий трек из их API (player/history/list?musicStreamId=N&limit=1, data[0]). Статичный маппинг наших станций -> musicStreamId. ICY-поллер исключает genre='Love Radio'. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -29,7 +29,7 @@ export class IcyNowPlayingService {
|
|||||||
const where = {
|
const where = {
|
||||||
recordStationId: null,
|
recordStationId: null,
|
||||||
isOnline: true,
|
isOnline: true,
|
||||||
genre: { not: 'DFM' },
|
genre: { notIn: ['DFM', 'Love Radio'] },
|
||||||
NOT: { streamUrl: { contains: 'emgsound.ru' } },
|
NOT: { streamUrl: { contains: 'emgsound.ru' } },
|
||||||
};
|
};
|
||||||
const total = await this.prisma.station.count({ where });
|
const total = await this.prisma.station.count({ where });
|
||||||
|
|||||||
89
src/now-playing/love-now-playing.service.ts
Normal file
89
src/now-playing/love-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';
|
||||||
|
|
||||||
|
interface LoveHistoryItem {
|
||||||
|
artistTitle?: string | null;
|
||||||
|
songTitle?: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Now-playing для Love Radio. В ICY-потоках вместо трека приходит «onlinestop56k»
|
||||||
|
* (имя мута), поэтому берём текущий трек из их API:
|
||||||
|
* api.loveradio.ru/api/v1/love-radio/player/history/list?musicStreamId={id}&limit=1
|
||||||
|
* (data[0] = последний/текущий). Обложек API не даёт — их подставит наше обогащение.
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class LoveNowPlayingService {
|
||||||
|
private readonly logger = new Logger(LoveNowPlayingService.name);
|
||||||
|
private readonly headers = {
|
||||||
|
'User-Agent': 'Mozilla/5.0',
|
||||||
|
Origin: 'https://www.loveradio.ru',
|
||||||
|
Referer: 'https://www.loveradio.ru/',
|
||||||
|
};
|
||||||
|
|
||||||
|
// Имя нашей станции -> musicStreamId в API Love Radio
|
||||||
|
private readonly streamId: Record<string, number> = {
|
||||||
|
'Love Radio': 28,
|
||||||
|
'Love RnB': 2,
|
||||||
|
'Love Top40': 3,
|
||||||
|
'Love Dance': 4,
|
||||||
|
'Love Chill': 5,
|
||||||
|
'Love Gold': 6,
|
||||||
|
'Love Russian': 7,
|
||||||
|
'Love KPOP': 10,
|
||||||
|
'Love Power': 11,
|
||||||
|
'Love Summer': 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private readonly prisma: PrismaService,
|
||||||
|
private readonly nowPlayingService: NowPlayingService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
@Interval(30000)
|
||||||
|
async pollLoveNowPlaying() {
|
||||||
|
const stations = await this.prisma.station.findMany({
|
||||||
|
where: { genre: 'Love Radio' },
|
||||||
|
});
|
||||||
|
if (stations.length === 0) return;
|
||||||
|
|
||||||
|
let updated = 0;
|
||||||
|
await Promise.allSettled(
|
||||||
|
stations.map(async (station) => {
|
||||||
|
const id = this.streamId[station.name];
|
||||||
|
if (!id) return;
|
||||||
|
|
||||||
|
const url =
|
||||||
|
`https://api.loveradio.ru/api/v1/love-radio/player/history/list` +
|
||||||
|
`?musicStreamId=${id}&limit=1`;
|
||||||
|
const res = await fetch(url, { headers: this.headers });
|
||||||
|
if (!res.ok) return;
|
||||||
|
|
||||||
|
const json = (await res.json()) as { data?: LoveHistoryItem[] };
|
||||||
|
const cur = json.data?.[0];
|
||||||
|
const artist = (cur?.artistTitle ?? '').trim();
|
||||||
|
const song = (cur?.songTitle ?? '').trim();
|
||||||
|
if (!artist || !song) return;
|
||||||
|
|
||||||
|
await this.nowPlayingService.ingest({
|
||||||
|
stationDbId: station.id,
|
||||||
|
stationNumericId: station.stationId,
|
||||||
|
artist,
|
||||||
|
song,
|
||||||
|
coverUrl: null,
|
||||||
|
});
|
||||||
|
if (!station.isOnline) {
|
||||||
|
await this.prisma.station.update({
|
||||||
|
where: { id: station.id },
|
||||||
|
data: { isOnline: true },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
updated++;
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
this.logger.log(`Love poll: ${updated}/${stations.length} обновлено`);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ import { RecordStationSyncService } from './record-station-sync.service';
|
|||||||
import { IcyNowPlayingService } from './icy-now-playing.service';
|
import { IcyNowPlayingService } from './icy-now-playing.service';
|
||||||
import { EmgNowPlayingService } from './emg-now-playing.service';
|
import { EmgNowPlayingService } from './emg-now-playing.service';
|
||||||
import { DfmNowPlayingService } from './dfm-now-playing.service';
|
import { DfmNowPlayingService } from './dfm-now-playing.service';
|
||||||
|
import { LoveNowPlayingService } from './love-now-playing.service';
|
||||||
import { ChartsModule } from '../charts/charts.module';
|
import { ChartsModule } from '../charts/charts.module';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
@@ -18,6 +19,7 @@ import { ChartsModule } from '../charts/charts.module';
|
|||||||
IcyNowPlayingService,
|
IcyNowPlayingService,
|
||||||
EmgNowPlayingService,
|
EmgNowPlayingService,
|
||||||
DfmNowPlayingService,
|
DfmNowPlayingService,
|
||||||
|
LoveNowPlayingService,
|
||||||
],
|
],
|
||||||
exports: [NowPlayingService],
|
exports: [NowPlayingService],
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user