feat: implement caching and redirect endpoints for premium live streams

This commit is contained in:
2026-09-18 22:21:03 +02:00
parent 03505eb16f
commit 60a3267932
4 changed files with 70 additions and 1 deletions
+23
View File
@@ -276,7 +276,30 @@ class Radio357Scraper:
self.db.commit() self.db.commit()
self.logger(f"R357: [{self.requests_made}/{self.hard_limit}] Pomyślnie pobrano {fetched} nowych linków.") self.logger(f"R357: [{self.requests_made}/{self.hard_limit}] Pomyślnie pobrano {fetched} nowych linków.")
def update_live_stream_url(self):
token = self.get_token()
if not token:
return
self.logger("R357: Aktualizacja adresu live stream...")
url = "https://stream.radio357.pl/?s=www"
try:
r = requests.get(url, headers={"User-Agent": "Mozilla/5.0"}, cookies={"token": token}, allow_redirects=False, timeout=5)
if r.status_code in (301, 302, 307) and "Location" in r.headers:
target_url = r.headers["Location"]
config_url = self.db.query(Config).filter_by(key="r357_live_url").first()
if not config_url:
self.db.add(Config(key="r357_live_url", value=target_url))
else:
config_url.value = target_url
self.db.commit()
self.logger(f"R357: Zapisano nowy strumień: {target_url}")
except Exception as e:
self.logger(f"R357 Błąd podczas pobierania streamu premium: {e}")
def run_full_sync(self, specific_program_slug=None): def run_full_sync(self, specific_program_slug=None):
self.sync_episodes(specific_program_slug) self.sync_episodes(specific_program_slug)
self.backfill_urls(specific_program_slug) self.backfill_urls(specific_program_slug)
if not specific_program_slug:
self.update_live_stream_url()
self.logger(f"R357: Koniec. Wysłano zapytania: {self.requests_made}/{self.hard_limit} (w tym backfill ograniczony do {self.backfill_limit}).") self.logger(f"R357: Koniec. Wysłano zapytania: {self.requests_made}/{self.hard_limit} (w tym backfill ograniczony do {self.backfill_limit}).")
+31
View File
@@ -501,6 +501,34 @@ class RNScraper:
self.db.commit() self.db.commit()
time.sleep(0.5) time.sleep(0.5)
def update_live_stream_url(self):
self.logger("RNŚ: Aktualizacja adresu live stream...")
try:
html = self._get_html(f"{BASE_URL}/")
if html:
soup = BeautifulSoup(html, "html.parser")
target_url = None
for btn in soup.find_all(class_="rns-stream-tile__btn"):
src = btn.get("data-neo-player-src")
parent = btn.parent
if parent and src:
text = parent.get_text(" ", strip=True).lower()
if "256" in text or "wysoka" in text:
target_url = src
break
if target_url:
if not target_url.startswith("http"): target_url = f"{BASE_URL}/{target_url.lstrip('/')}"
config_url = self.db.query(Config).filter_by(key="rns_live_url").first()
if not config_url:
self.db.add(Config(key="rns_live_url", value=target_url))
else:
config_url.value = target_url
self.db.commit()
self.logger(f"RNS: Zaktualizowano adres streamu na {target_url}")
except Exception as e:
self.logger(f"RNS: Błąd aktualizacji streamu: {e}")
def run_full_sync(self, specific_program_slug=None): def run_full_sync(self, specific_program_slug=None):
if not self.ensure_auth(): if not self.ensure_auth():
self.logger("RNŚ: Błąd logowania przed startem!") self.logger("RNŚ: Błąd logowania przed startem!")
@@ -511,4 +539,7 @@ class RNScraper:
if self.requests_made < self.hard_limit: if self.requests_made < self.hard_limit:
self.phase_2_backfill(specific_program_slug) self.phase_2_backfill(specific_program_slug)
if not specific_program_slug:
self.update_live_stream_url()
self.logger(f"RNŚ: Koniec. Wysłano zapytania: {self.requests_made}/{self.hard_limit} (w tym backfill ograniczony do {self.backfill_limit}).") self.logger(f"RNŚ: Koniec. Wysłano zapytania: {self.requests_made}/{self.hard_limit} (w tym backfill ograniczony do {self.backfill_limit}).")
+13
View File
@@ -611,3 +611,16 @@ def podcast_rss(station: str, slug_with_ext: str, request: Request, db: Session
if not xml: if not xml:
return Response(status_code=404) return Response(status_code=404)
return Response(content=xml, media_type="application/xml") return Response(content=xml, media_type="application/xml")
@app.get("/live/{station}")
def live_stream_redirect(station: str, db: Session = Depends(db_session)):
if station == "radio357":
config_url = db.query(Config).filter_by(key="r357_live_url").first()
target_url = config_url.value if config_url else "https://stream.rcs.revma.com/ye5kghkgcm0uv"
return RedirectResponse(url=target_url)
elif station == "rns":
config_url = db.query(Config).filter_by(key="rns_live_url").first()
target_url = config_url.value if config_url else "https://stream.rcs.revma.com/4md4m0a0fs8uv"
return RedirectResponse(url=target_url)
else:
raise HTTPException(status_code=404, detail="Brak streamu dla podanej stacji")
+3 -1
View File
@@ -92,7 +92,9 @@
<li class="nav-item"><a class="nav-link" href="/programs/rns">Nowy Świat</a></li> <li class="nav-item"><a class="nav-link" href="/programs/rns">Nowy Świat</a></li>
<li class="nav-item"><a class="nav-link" href="/programs/jazz">Jazz FM</a></li> <li class="nav-item"><a class="nav-link" href="/programs/jazz">Jazz FM</a></li>
</ul> </ul>
<div class="d-flex"> <div class="d-flex gap-2">
<a href="/live/radio357" class="btn btn-outline-info btn-sm" target="_blank" title="Pełny redirect do transmisji. Obie stacje oferują alternatywny link premium dla patronów (bez reklam i z lepszą jakością).">Stream 357</a>
<a href="/live/rns" class="btn btn-outline-info btn-sm" target="_blank" title="Pełny redirect do transmisji. Obie stacje oferują alternatywny link premium dla patronów (bez reklam i z lepszą jakością).">Stream RNŚ</a>
<a href="/feeds/Podcasts.opml" class="btn btn-outline-warning btn-sm" target="_blank">Master OPML</a> <a href="/feeds/Podcasts.opml" class="btn btn-outline-warning btn-sm" target="_blank">Master OPML</a>
</div> </div>
</div> </div>