Zmiana iframe listy kanałow na pobierane lokalnie i parsowane

This commit is contained in:
dm
2025-11-26 09:15:29 +01:00
parent 284009d411
commit 8bf578e6d9
7 changed files with 129 additions and 96 deletions

View File

@@ -1,20 +1,26 @@
--- ---
import ChannelSwitcher from "../../islands/ChannelSwitcher.jsx"; import ChannelSwitcher from "../../islands/ChannelSwitcher.jsx";
const { section } = Astro.props; const { section } = Astro.props;
--- ---
<section class="fuz-section bg-transparent">
<section class="f-section">
<div class="max-w-7xl mx-auto text-center"> <div class="max-w-7xl mx-auto text-center">
{section.title && ( {section.title && (
<h2 class="fuz-section-title">{section.title}</h2> <h2 class="f-section-title">{section.title}</h2>
)} )}
{section.content && ( {section.content && (
<div class="fuz-markdown mb-10" set:html={section.html} /> <div class="f-markdown mb-10" set:html={section.html} />
)} )}
<ChannelSwitcher client:load sets={section.iframe_sets} title={section.title} /> {section.iframe_sets && section.iframe_sets.length > 0 && (
<ChannelSwitcher
client:load
sets={section.iframe_sets}
title={section.title}
/>
)}
</div> </div>
</section> </section>

View File

@@ -1,19 +1,32 @@
import { useState } from "preact/hooks"; import { useState, useEffect } from "preact/hooks";
export default function ChannelSwitcher({ sets = [], title = "" }) { export default function ChannelSwitcher({ sets = [], title = "" }) {
const [activeId, setActiveId] = useState(sets[0]?.id); const [activeId, setActiveId] = useState(sets[0]?.id);
const [channels, setChannels] = useState([]);
const active = sets.find((x) => x.id === activeId); const active = sets.find((x) => x.id === activeId);
const iframeSrc = `https://www.jambox.pl/iframe-pakiet-logo?p=${active?.p}`;
useEffect(() => {
if (!active) return;
fetch(`/api/jambox/${active.p}`)
.then((r) => r.json())
.then((data) => {
setChannels(data);
})
.catch(() => setChannels([]));
}, [active]);
return ( return (
<div class="w-full"> <div class="w-full">
{/* SWITCHER */}
<div class="flex justify-center mb-10"> <div class="flex justify-center mb-10">
<div class="fuz-switch-group"> <div class="f-switch-group">
{sets.map((s) => ( {sets.map((s) => (
<button <button
type="button" type="button"
class={`fuz-switch ${activeId === s.id ? "active" : ""}`} class={`f-switch ${activeId === s.id ? "active" : ""}`}
onClick={() => setActiveId(s.id)} onClick={() => setActiveId(s.id)}
title={title} title={title}
> >
@@ -23,16 +36,31 @@ export default function ChannelSwitcher({ sets = [], title = "" }) {
</div> </div>
</div> </div>
{/* 🔹 Iframe */} {/* LISTA KANAŁÓW */}
<div class="w-full"> <div class="f-section-channel">
<div class="fuz-iframe-wrapper">
<iframe {channels.length === 0 && (
title="Lista kanałów" <p class="text-center col-span-full py-1">
src={iframeSrc} Ładowanie
class="fuz-iframe" </p>
)}
{channels.map((ch) => (
<div
class="f-channel-box"
>
<img
src={ch.logo}
alt={ch.title}
class="h-14 object-contain "
loading="lazy" loading="lazy"
></iframe> />
<p class="text-center text-sm text-[var(--fuz-text)] mt-2">
{ch.title}
</p>
</div> </div>
))}
</div> </div>
</div> </div>

View File

@@ -0,0 +1,42 @@
import type { APIRoute } from "astro";
import { JSDOM } from "jsdom";
interface Channel {
title: string;
logo: string;
}
const cache = new Map<string, { time: number; data: Channel[] }>();
const CACHE_TIME = 1000 * 60 * 60 * 24 * 30; //miesiąc
export const GET: APIRoute = async ({ params }) => {
const id = params.id!;
const cached = cache.get(id);
if (cached && Date.now() - cached.time < CACHE_TIME) {
return new Response(JSON.stringify(cached.data), {
headers: { "Content-Type": "application/json" }
});
}
const url = `https://www.jambox.pl/iframe-pakiet-logo?p=${id}`;
const resp = await fetch(url, { headers: { "User-Agent": "Mozilla/5.0" } });
const html = await resp.text();
const dom = new JSDOM(html);
const images = [
...dom.window.document.querySelectorAll("img.imagefield-field_logo")
];
const channels = images.map((img) => ({
title: img.getAttribute("alt")?.trim() ?? "",
logo: img.getAttribute("src") ?? "",
}));
cache.set(id, { time: Date.now(), data: channels });
return new Response(JSON.stringify(channels), {
headers: { "Content-Type": "application/json" }
});
};

View File

@@ -1,48 +1,36 @@
@layer components { @layer components {
#cookie-banner { #cookie-banner {
@apply fixed bottom-0 left-0 w-full translate-y-full transition-transform duration-500 ease-in-out; @apply fixed bottom-0 left-0 w-full translate-y-full transition-transform duration-500 ease-in-out;
background: var(--f-background-invert); background: var(--f-cookie-background);
color: var(--f-text-invert); color: var(--f-cookie-text);
border-top: 1px solid rgba(0, 0, 0, 0.1); /* border-top: 1px solid rgba(0, 0, 0, 0.1); */
} }
.f-cookie-panel-inner { .f-cookie-panel-inner {
@apply max-w-4xl mx-auto flex flex-col md:flex-row items-start @apply max-w-5xl mx-auto flex flex-col md:flex-row items-start md:items-center justify-between gap-4 px-6 py-6;
md:items-center justify-between gap-4 px-6 py-6;
} }
.f-cookie-text{ .f-cookie-text {
@apply text-base leading-snug; @apply text-base leading-snug;
} }
.f-cookie-privacy-link { .f-cookie-privacy-link {
@apply ml-3; @apply ml-3;
color: var(--btn-outline-invert); color: var(--f-link-text);
} }
.f-cookie-accept, .f-cookie-accept,
.f-cookie-reject { .f-cookie-reject {
@apply px-4 py-2 rounded-md text-sm font-medium transition-colors; @apply px-8 py-4 rounded-md text-sm font-medium transition-colors;
} }
.f-cookie-accept { .f-cookie-accept {
background: var(--btn-bg-invert); background: var(--f-cookie-accept-background);
color: var(--btn-text-invert); color: var(--f-cookie-accept-text);
}
.f-cookie-accept:hover {
background: var(--fuz-accent-hover);
} }
.f-cookie-reject { .f-cookie-reject {
@apply border; background: var(--f-cookie-reject-background);
border-color: var(--btn-outline-invert); color: var(--f-cookie-reject-text);
color: var(--btn-outline-invert);
background: transparent;
} }
.f-cookie-reject:hover {
background: var(--btn-outline-bg-invert);
}
} }

View File

@@ -1,9 +1,9 @@
.f-switches-wrapper { .f-switches-wrapper {
@apply flex flex-wrap justify-center gap-6 mb-12; @apply flex flex-wrap justify-center gap-6 mb-10;
} }
.f-switch-group { .f-switch-group {
@apply inline-flex overflow-hidden relative bg-[var(--f-background-switch)]; @apply inline-flex overflow-hidden relative bg-[var(--f-background-switch)] mt-8;
} }
.f-switch { .f-switch {

View File

@@ -43,7 +43,15 @@
} }
.fuz-iframe-box { .f-section-channel {
@apply grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-8 gap-2;
}
.f-channel-box {
@apply flex flex-col items-center p-4 rounded-xl bg-[var(--f-background)] border border-[var(--f-offers-border)] shadow-sm hover:shadow-md transition
}
/* .fuz-iframe-box {
@apply bg-white dark:bg-slate-800 p-4 rounded-xl border border-gray-200 dark:border-slate-700 shadow; @apply bg-white dark:bg-slate-800 p-4 rounded-xl border border-gray-200 dark:border-slate-700 shadow;
} }
@@ -64,4 +72,4 @@
.dark .fuz-iframe { .dark .fuz-iframe {
background: transparent !important; background: transparent !important;
} } */

View File

@@ -69,27 +69,12 @@
--f-offers-popular: var(--brand-light); --f-offers-popular: var(--brand-light);
--f-offers-popular-bg: color-mix(in srgb, var(--f-offers-popular) 22%, transparent); --f-offers-popular-bg: color-mix(in srgb, var(--f-offers-popular) 22%, transparent);
/* Invert Color */ --f-cookie-background: var(--text1-light);
/* --f-background-invert: #0d1117; --f-cookie-text: var(--surface2-light);
--f-text-invert: #e6edf3;
--btn-bg-invert: #58a6ff;
--btn-text-invert: #0d1117;
--btn-outline-invert: #58a6ff;
--btn-outline-bg-invert: rgba(88, 166, 255, 0.15); */
/* Links */
/* --fuz-link: #0050c8;
--fuz-link-hover: #003f9a; */
/* Accent (buttons, highlights) */ --f-cookie-accept-background: green;
/* --fuz-accent: #0066ff; /* --f-cookie-accept-text: */
--fuz-accent-hover: #004bcc; --f-cookie-reject-background: var(--text2-light);
--fuz-accent-text: #ffffff;
--btn-ghost-text: var(--f-text);
--btn-ghost-hover-bg: rgba(0, 0, 0, 0.05);
---f-border-color: rgb(209 213 219); */
/* / var(--tw-border-opacity, 1)); */
} }
:root.dark { :root.dark {
@@ -128,36 +113,12 @@
--f-offers-popular: var(--brand-dark); --f-offers-popular: var(--brand-dark);
--f-offers-popular-bg: color-mix(in srgb, var(--f-offers-popular) 22%, transparent); --f-offers-popular-bg: color-mix(in srgb, var(--f-offers-popular) 22%, transparent);
--f-cookie-background: var(--text1-light);
--f-cookie-text: var(--surface2-light);
--f-cookie-accept-background: green;
/* Invert Color */ /* --f-cookie-accept-text: */
--f-background-invert: #ffffff; --f-cookie-reject-background: var(--text2-light);
--f-text-invert: #0d0d0d;
--btn-bg-invert: #0066ff;
--btn-text-invert: #ffffff;
--btn-outline-invert: #0066ff;
/* Links (GitHub Dark palette) */
--fuz-link: var(--brand-dim);
--fuz-link-hover: #79b7ff;
/* Accent */
--fuz-accent: hsl(var(--hue) calc(var(--saturation) / 1.25) calc(var(--lightness) / 1.25));
--fuz-accent-hover: #79b7ff;
--fuz-accent-text: #0d1117;
/* Buttons */
--btn-ghost-text: var(--f-text);
--btn-ghost-hover-bg: rgba(255, 255, 255, 0.08);
--f-border-color: rgb(209 213 219);
/* / var(--tw-border-opacity, 1)) */
} }
/* Body */ /* Body */