Improve sync feedback and dashboard layout
Add request counters to Radio 357 backfill logs, clarify RNŚ warnings, and refine responsive dashboard metrics and configuration layout.
This commit is contained in:
@@ -240,12 +240,12 @@ class Radio357Scraper:
|
|||||||
if self.requests_made >= self.hard_limit or backfill_count >= self.backfill_limit:
|
if self.requests_made >= self.hard_limit or backfill_count >= self.backfill_limit:
|
||||||
break
|
break
|
||||||
|
|
||||||
self.logger(f"R357: Pobieram URL dla {ep.title[:30]}...")
|
|
||||||
gateway_url = f"https://gateway.r357.eu/api/content/podcast/{ep.ep_id}/url"
|
gateway_url = f"https://gateway.r357.eu/api/content/podcast/{ep.ep_id}/url"
|
||||||
|
|
||||||
self.requests_made += 1
|
self.requests_made += 1
|
||||||
if self.progress_callback: self.progress_callback(self.requests_made, self.hard_limit)
|
if self.progress_callback: self.progress_callback(self.requests_made, self.hard_limit)
|
||||||
backfill_count += 1
|
backfill_count += 1
|
||||||
|
self.logger(f"R357: [{self.requests_made}/{self.hard_limit}] Pobieram URL dla {ep.title[:30]}...")
|
||||||
try:
|
try:
|
||||||
r = requests.get(gateway_url, headers={
|
r = requests.get(gateway_url, headers={
|
||||||
"Authorization": f"Bearer {token}",
|
"Authorization": f"Bearer {token}",
|
||||||
@@ -259,22 +259,22 @@ class Radio357Scraper:
|
|||||||
ep.url = audio_url
|
ep.url = audio_url
|
||||||
fetched += 1
|
fetched += 1
|
||||||
elif r.status_code == 401 or r.status_code == 403:
|
elif r.status_code == 401 or r.status_code == 403:
|
||||||
self.logger("R357: Token odrzucony przez gateway. Wymagam reloginu.")
|
self.logger(f"R357: [{self.requests_made}/{self.hard_limit}] Token odrzucony przez gateway. Wymagam reloginu.")
|
||||||
# Usuwamy token, zeby przy nastepnym runie pobral nowy
|
# Usuwamy token, zeby przy nastepnym runie pobral nowy
|
||||||
self.db.query(Config).filter_by(key="r357_token").delete()
|
self.db.query(Config).filter_by(key="r357_token").delete()
|
||||||
self.db.commit()
|
self.db.commit()
|
||||||
raise Exception("R357 Token Expired")
|
raise Exception("R357 Token Expired")
|
||||||
else:
|
else:
|
||||||
self.logger(f"R357 Błąd HTTP {r.status_code} podczas pobierania audio: {r.text[:100]}")
|
self.logger(f"R357: [{self.requests_made}/{self.hard_limit}] Błąd HTTP {r.status_code} podczas pobierania audio: {r.text[:100]}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger(f"R357: Błąd URL dla odcinka {ep.ep_id}: {e}")
|
self.logger(f"R357: [{self.requests_made}/{self.hard_limit}] Błąd URL dla odcinka {ep.ep_id}: {e}")
|
||||||
if "Token Expired" in str(e):
|
if "Token Expired" in str(e):
|
||||||
raise
|
raise
|
||||||
|
|
||||||
time.sleep(0.5)
|
time.sleep(0.5)
|
||||||
|
|
||||||
self.db.commit()
|
self.db.commit()
|
||||||
self.logger(f"R357: 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 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)
|
||||||
|
|||||||
@@ -253,6 +253,7 @@ class RNScraper:
|
|||||||
|
|
||||||
new_found = 0
|
new_found = 0
|
||||||
missing_player_count = 0
|
missing_player_count = 0
|
||||||
|
missing_player_titles = []
|
||||||
valid_cards = 0
|
valid_cards = 0
|
||||||
|
|
||||||
for card in cards:
|
for card in cards:
|
||||||
@@ -266,6 +267,8 @@ class RNScraper:
|
|||||||
|
|
||||||
if not player_box:
|
if not player_box:
|
||||||
missing_player_count += 1
|
missing_player_count += 1
|
||||||
|
if title_el := card.find("p", class_="rns-post-title"):
|
||||||
|
missing_player_titles.append(title_el.get_text(strip=True))
|
||||||
|
|
||||||
if audio_url and not audio_url.startswith("http"):
|
if audio_url and not audio_url.startswith("http"):
|
||||||
audio_url = f"{BASE_URL}/{audio_url.lstrip('/')}"
|
audio_url = f"{BASE_URL}/{audio_url.lstrip('/')}"
|
||||||
@@ -315,7 +318,10 @@ class RNScraper:
|
|||||||
invalid_response("rns", "HTML_SCHEMA_CHANGED", "Znaleziono karty podcastów bez oczekiwanych identyfikatorów", url, content=html)
|
invalid_response("rns", "HTML_SCHEMA_CHANGED", "Znaleziono karty podcastów bez oczekiwanych identyfikatorów", url, content=html)
|
||||||
|
|
||||||
if missing_player_count:
|
if missing_player_count:
|
||||||
warning = f"{missing_player_count} odcinków bez dostępnego playera audio; pomijam ich URL-e."
|
titles = ", ".join(missing_player_titles[:3])
|
||||||
|
warning = f"Audycja {program_slug}: {missing_player_count} odcinków bez dostępnego playera audio; pomijam ich URL-e."
|
||||||
|
if titles:
|
||||||
|
warning += f" Odcinki: {titles}."
|
||||||
if self.warning_callback:
|
if self.warning_callback:
|
||||||
self.warning_callback(warning)
|
self.warning_callback(warning)
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -69,6 +69,9 @@
|
|||||||
.episode-title { font-size: 1.05rem; }
|
.episode-title { font-size: 1.05rem; }
|
||||||
.page-toolbar { border-bottom: 1px solid var(--line); padding-bottom: 1rem; margin-bottom: 1.25rem; }
|
.page-toolbar { border-bottom: 1px solid var(--line); padding-bottom: 1rem; margin-bottom: 1.25rem; }
|
||||||
.empty-state { color: var(--muted); padding: 2rem 0; }
|
.empty-state { color: var(--muted); padding: 2rem 0; }
|
||||||
|
.config-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: .5rem; }
|
||||||
|
.config-grid > div { min-width: 0; }
|
||||||
|
.config-grid .form-label { display: block; min-height: 2.5em; line-height: 1.35; }
|
||||||
@media (max-width: 767.98px) {
|
@media (max-width: 767.98px) {
|
||||||
.container { padding-left: 18px; padding-right: 18px; }
|
.container { padding-left: 18px; padding-right: 18px; }
|
||||||
.navbar .container { padding-left: 18px; padding-right: 18px; }
|
.navbar .container { padding-left: 18px; padding-right: 18px; }
|
||||||
|
|||||||
+19
-20
@@ -60,25 +60,20 @@
|
|||||||
<button class="btn btn-sm btn-outline-warning w-100" title="Wymuś nowe logowanie z API" onclick="return confirm('Czy na pewno chcesz wymusić nowe logowanie?')">Relogin</button>
|
<button class="btn btn-sm btn-outline-warning w-100" title="Wymuś nowe logowanie z API" onclick="return confirm('Czy na pewno chcesz wymusić nowe logowanie?')">Relogin</button>
|
||||||
</form>
|
</form>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<form method="POST" action="/force-sync" class="m-0">
|
|
||||||
<input type="hidden" name="csrf" value="{{ csrf_token }}">
|
|
||||||
<input type="hidden" name="station" value="{{ st }}">
|
|
||||||
<button class="btn btn-sm btn-light w-100" {% if status[st].status == 'W trakcie' %}disabled{% endif %}>
|
|
||||||
{% if status[st].status == 'W trakcie' %}
|
|
||||||
Pobieranie...
|
|
||||||
{% else %}
|
|
||||||
Sync
|
|
||||||
{% endif %}
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
{% if status[st].status == 'W trakcie' %}
|
{% if status[st].status == 'W trakcie' %}
|
||||||
<form method="POST" action="/stop-sync" class="m-0">
|
<form method="POST" action="/stop-sync" class="m-0">
|
||||||
<input type="hidden" name="csrf" value="{{ csrf_token }}">
|
<input type="hidden" name="csrf" value="{{ csrf_token }}">
|
||||||
<input type="hidden" name="station" value="{{ st }}">
|
<input type="hidden" name="station" value="{{ st }}">
|
||||||
<button class="btn btn-sm btn-outline-danger w-100" onclick="return confirm('Zatrzymać synchronizację?')">
|
<button class="btn btn-sm btn-outline-danger w-100" onclick="return confirm('Zatrzymać synchronizację?')">
|
||||||
⏹ Stop
|
<span aria-hidden="true">■</span> Stop
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
{% else %}
|
||||||
|
<form method="POST" action="/force-sync" class="m-0">
|
||||||
|
<input type="hidden" name="csrf" value="{{ csrf_token }}">
|
||||||
|
<input type="hidden" name="station" value="{{ st }}">
|
||||||
|
<button class="btn btn-sm btn-light w-100">Sync</button>
|
||||||
|
</form>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -100,6 +95,10 @@
|
|||||||
<small>{{ error.last_seen.strftime('%Y-%m-%d %H:%M') }}</small>
|
<small>{{ error.last_seen.strftime('%Y-%m-%d %H:%M') }}</small>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-1">{{ error.message }}</div>
|
<div class="mt-1">{{ error.message }}</div>
|
||||||
|
{% if error.source == 'missing_player' and error.message.startswith('Audycja ') %}
|
||||||
|
{% set program_slug = error.message.split(':', 1)[0].replace('Audycja ', '') %}
|
||||||
|
<a class="small text-info" href="/episodes/rns/{{ program_slug }}">Otwórz odcinki audycji</a>
|
||||||
|
{% endif %}
|
||||||
<div class="d-flex justify-content-between align-items-center mt-2">
|
<div class="d-flex justify-content-between align-items-center mt-2">
|
||||||
<small class="text-warning">Powtórzenia: {{ error.occurrences }}</small>
|
<small class="text-warning">Powtórzenia: {{ error.occurrences }}</small>
|
||||||
<form method="POST" action="/errors/{{ error.id }}/acknowledge" class="m-0">
|
<form method="POST" action="/errors/{{ error.id }}/acknowledge" class="m-0">
|
||||||
@@ -157,16 +156,16 @@
|
|||||||
<div class="mb-2">
|
<div class="mb-2">
|
||||||
<input type="password" name="r357_password" class="form-control form-control-sm" placeholder="Hasło (zostaw puste aby nie zmieniać)">
|
<input type="password" name="r357_password" class="form-control form-control-sm" placeholder="Hasło (zostaw puste aby nie zmieniać)">
|
||||||
</div>
|
</div>
|
||||||
<div class="row g-2 mb-4">
|
<div class="config-grid mb-4">
|
||||||
<div class="col-4">
|
<div>
|
||||||
<label class="form-label small text-muted m-0">Sync co (godzin)</label>
|
<label class="form-label small text-muted m-0">Sync co (godzin)</label>
|
||||||
<input type="number" name="r357_interval" class="form-control form-control-sm" value="{{ config.r357_interval or '6' }}">
|
<input type="number" name="r357_interval" class="form-control form-control-sm" value="{{ config.r357_interval or '6' }}">
|
||||||
</div>
|
</div>
|
||||||
<div class="col-4">
|
<div>
|
||||||
<label class="form-label small text-muted m-0">Max odcinków backfill</label>
|
<label class="form-label small text-muted m-0">Max odcinków backfill</label>
|
||||||
<input type="number" name="r357_limit" class="form-control form-control-sm" value="{{ config.r357_limit or '600' }}">
|
<input type="number" name="r357_limit" class="form-control form-control-sm" value="{{ config.r357_limit or '600' }}">
|
||||||
</div>
|
</div>
|
||||||
<div class="col-4">
|
<div>
|
||||||
<label class="form-label small text-muted m-0">Hard limit (all)</label>
|
<label class="form-label small text-muted m-0">Hard limit (all)</label>
|
||||||
<input type="number" name="r357_hard_limit" class="form-control form-control-sm" value="{{ config.r357_hard_limit or '1000' }}">
|
<input type="number" name="r357_hard_limit" class="form-control form-control-sm" value="{{ config.r357_hard_limit or '1000' }}">
|
||||||
</div>
|
</div>
|
||||||
@@ -179,16 +178,16 @@
|
|||||||
<div class="mb-2">
|
<div class="mb-2">
|
||||||
<input type="password" name="rns_password" class="form-control form-control-sm" placeholder="Hasło (zostaw puste aby nie zmieniać)">
|
<input type="password" name="rns_password" class="form-control form-control-sm" placeholder="Hasło (zostaw puste aby nie zmieniać)">
|
||||||
</div>
|
</div>
|
||||||
<div class="row g-2 mb-4">
|
<div class="config-grid mb-4">
|
||||||
<div class="col-4">
|
<div>
|
||||||
<label class="form-label small text-muted m-0">Sync co (godzin)</label>
|
<label class="form-label small text-muted m-0">Sync co (godzin)</label>
|
||||||
<input type="number" name="rns_interval" class="form-control form-control-sm" value="{{ config.rns_interval or '6' }}">
|
<input type="number" name="rns_interval" class="form-control form-control-sm" value="{{ config.rns_interval or '6' }}">
|
||||||
</div>
|
</div>
|
||||||
<div class="col-4">
|
<div>
|
||||||
<label class="form-label small text-muted m-0">Max stron backfill</label>
|
<label class="form-label small text-muted m-0">Max stron backfill</label>
|
||||||
<input type="number" name="rns_limit" class="form-control form-control-sm" value="{{ config.rns_limit or '50' }}">
|
<input type="number" name="rns_limit" class="form-control form-control-sm" value="{{ config.rns_limit or '50' }}">
|
||||||
</div>
|
</div>
|
||||||
<div class="col-4">
|
<div>
|
||||||
<label class="form-label small text-muted m-0">Hard limit (all)</label>
|
<label class="form-label small text-muted m-0">Hard limit (all)</label>
|
||||||
<input type="number" name="rns_hard_limit" class="form-control form-control-sm" value="{{ config.rns_hard_limit or '500' }}">
|
<input type="number" name="rns_hard_limit" class="form-control form-control-sm" value="{{ config.rns_hard_limit or '500' }}">
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user