PageSourceSearch

https://www.hamienet.com/assets/ChannelPage-DAFNmqL_.js

js hamienet.com collected 2026-09-24 18:59:18 UTC 38,758 bytes, 555 lines download raw bytes

1import { r as reactExports, j as jsxRuntimeExports, R as React } from "./vendor-react-Bay9Y7Lb.js";
2import { a as api } from "./index-D0Bmh-UX.js";
3import { u as usePlayer, V as VideoCard } from "./main-CznOH3wZ.js";
4import { u as useShouldShowAd, A as AdSlot } from "./AdSlot-CPW4Sb2o.js";
5import { U as UserAvatar } from "./UserAvatar-BQN4jlY-.js";
6import { g as getColorsFromHash, t as timeAgo } from "./utils-BYCVoSTN.js";
7import { u as useTranslation } from "./vendor-i18n-R-bMoYp_.js";
8import { b6 as LoaderCircle, dN as Users, bn as Globe, bE as ChevronRight, b7 as Check, bg as Music, ba as Info, bZ as Pause, bU as Play, bA as Eye, cv as LayoutGrid, cw as List, b4 as FolderOpen, dM as Calendar } from "./vendor-dVNaLJx_.js";
9import "./vendor-react-three-MDLA5jog.js";
10import "./synth-BE1TSTD4.js";
11import "./wasm-synth-adapter-BrqgBX1y.js";
12import "./vendor-spessasynth-DH8n6o_V.js";
13import "./vendor-audio-DH3w0BzW.js";
14import "./Waveform-MxtdejK7.js";
15import "./vendor-framer-C8eEgkdL.js";
16import "./labTools-BATZiaPB.js";
17const ChannelPage = ({
18  channelName,
19  onSelectTrack,
20  onToggleWatchLater,
21  watchLaterSet,
22  currentUser,
23  onAuthRequested,
24  onAddToQueue,
25  onAddToPlaylist
26}) => {
27  const { t } = useTranslation();
28  const shouldShowExploreAd = useShouldShowAd("exploreList");
29  const { activeTrack, isPlaying, togglePlay, playTrack } = usePlayer();
30  const [activeTab, setActiveTab] = reactExports.useState("home");
31  const [profile, setProfile] = reactExports.useState(null);
32  const [profileLoading, setProfileLoading] = reactExports.useState(true);
33  const [profileError, setProfileError] = reactExports.useState(null);
34  const [subscribing, setSubscribing] = reactExports.useState(false);
35  const [featuredTrack, setFeaturedTrack] = reactExports.useState(null);
36  const [recentTracks, setRecentTracks] = reactExports.useState([]);
37  const [popularTracks, setPopularTracks] = reactExports.useState([]);
38  const [homeLoading, setHomeLoading] = reactExports.useState(false);
39  const [tracks, setTracks] = reactExports.useState([]);
40  const [sortMode, setSortMode] = reactExports.useState("recent");
41  const [tracksLoading, setTracksLoading] = reactExports.useState(false);
42  const [loadingMore, setLoadingMore] = reactExports.useState(false);
43  const [hasMore, setHasMore] = reactExports.useState(true);
44  const [viewMode, setViewMode] = reactExports.useState("grid");
45  const [playlists, setPlaylists] = reactExports.useState([]);
46  const [playlistsLoading, setPlaylistsLoading] = reactExports.useState(false);
47  const observerRef = reactExports.useRef(null);
48  const sentinelRef = reactExports.useRef(null);
49  reactExports.useEffect(() => {
50    if (!channelName) {
51      setProfile(null);
52      setProfileLoading(false);
53      return;
54    }
55    setProfileLoading(true);
56    setProfileError(null);
57    setActiveTab("home");
58    api.getChannelProfile(channelName).then((res) => {
59      setProfile(res);
60    }).catch((err) => {
61      console.error("[Channel Profile Load Fail]", err);
62      setProfileError(t("channel.notFound", "Channel not found or error loading profile."));
63      setProfile(null);
64    }).finally(() => {
65      setProfileLoading(false);
66    });
67  }, [channelName]);
68  reactExports.useEffect(() => {
69    if (!profile || activeTab !== "home") return;
70    setHomeLoading(true);
71    Promise.all([
72      api.getTracks("", "all", "recent", 6, 0, false, profile.username),
73      api.getTracks("", "all", "popular", 6, 0, false, profile.username)
74    ]).then(([recent, popular]) => {
75      setRecentTracks(recent);
76      setPopularTracks(popular);
77      if (popular.length > 0) {
78        setFeaturedTrack(popular[0]);
79      } else if (recent.length > 0) {
80        setFeaturedTrack(recent[0]);
81      } else {
82        setFeaturedTrack(null);
83      }
84    }).catch((err) => {
85      console.error("[Channel Home Data Load Fail]", err);
86    }).finally(() => {
87      setHomeLoading(false);
88    });
89  }, [profile, activeTab]);
90  reactExports.useEffect(() => {
91    if (!profile || activeTab !== "tracks") return;
92    setTracksLoading(true);
93    setTracks([]);
94    setHasMore(true);
95    api.getTracks("", "all", sortMode === "recent" ? "recent" : "popular", 30, 0, false, profile.username).then((res) => {
96      setTracks(res);
97      if (res.length < 30) {
98        setHasMore(false);
99      }
100    }).catch((err) => {
101      console.error("[Channel Tracks Initial Load Fail]", err);
102    }).finally(() => {
103      setTracksLoading(false);
104    });
105  }, [profile, activeTab, sortMode]);
106  reactExports.useEffect(() => {
107    if (activeTab !== "tracks" || tracksLoading || !hasMore) return;
108    if (observerRef.current) observerRef.current.disconnect();
109    observerRef.current = new IntersectionObserver(
110      (entries) => {
111        if (entries[0].isIntersecting && !loadingMore && hasMore) {
112          loadMoreTracks();
113        }
114      },
115      { threshold: 0.1, rootMargin: "150px" }
116    );
117    if (sentinelRef.current) {
118      observerRef.current.observe(sentinelRef.current);
119    }
120    return () => {
121      if (observerRef.current) observerRef.current.disconnect();
122    };
123  }, [activeTab, tracksLoading, loadingMore, hasMore, tracks.length]);
124  const loadMoreTracks = () => {
125    if (!profile || loadingMore || !hasMore) return;
126    setLoadingMore(true);
127    const skip = tracks.length;
128    api.getTracks("", "all", sortMode === "recent" ? "recent" : "popular", 30, skip, false, profile.username).then((res) => {
129      if (res.length === 0) {
130        setHasMore(false);
131      } else {
132        setTracks((prev) => [...prev, ...res]);
133        if (res.length < 30) {
134          setHasMore(false);
135        }
136      }
137    }).catch((err) => {
138      console.error("[Channel Load More Tracks Fail]", err);
139    }).finally(() => {
140      setLoadingMore(false);
141    });
142  };
143  reactExports.useEffect(() => {
144    if (!profile || activeTab !== "playlists") return;
145    setPlaylistsLoading(true);
146    api.getPlaylists(profile.id).then((res) => {
147      setPlaylists(res);
148    }).catch((err) => {
149      console.error("[Channel Playlists Load Fail]", err);
150    }).finally(() => {
151      setPlaylistsLoading(false);
152    });
153  }, [profile, activeTab]);
154  const handleSubscribe = async () => {
155    if (!currentUser) {
156      onAuthRequested();
157      return;
158    }
159    if (!profile) return;
160    setSubscribing(true);
161    try {
162      await api.toggleSubscription(currentUser.id, profile.id);
163      setProfile((prev) => {
164        if (!prev) return null;
165        const nextSubscribed = !prev.isSubscribed;
166        return {
167          ...prev,
168          isSubscribed: nextSubscribed,
169          subscriberCount: nextSubscribed ? prev.subscriberCount + 1 : Math.max(0, prev.subscriberCount - 1)
170        };
171      });
172    } catch (err) {
173      console.error("[Subscription Toggle Fail]", err);
174    } finally {
175      setSubscribing(false);
176    }
177  };
178  const handlePlayPlaylist = async (pl) => {
179    if (!pl.trackIds || pl.trackIds.length === 0) return;
180    try {
181      const track = await api.getTrack(pl.trackIds[0]);
182      if (track) {
183        onSelectTrack(track);
184      }
185    } catch (e) {
186      console.error("[Play Playlist Track Fail]", e);
187    }
188  };
189  const handlePlayFeatured = () => {
190    if (!featuredTrack) return;
191    if ((activeTrack == null ? void 0 : activeTrack.id) === featuredTrack.id) {
192      togglePlay();
193    } else {
194      playTrack(featuredTrack);
195    }
196  };
197  const getJoinedDate = (dateStr) => {
198    try {
199      return new Date(dateStr).toLocaleDateString(void 0, { year: "numeric", month: "long", day: "numeric" });
200    } catch (e) {
201      return dateStr;
202    }
203  };
204  if (profileLoading) {
205    return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col items-center justify-center py-32 bg-white dark:bg-[#0f0f0f] min-h-[60vh]", children: [
206      /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { className: "w-10 h-10 text-indigo-600 animate-spin mb-4" }),
207      /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-zinc-500 dark:text-zinc-400 text-sm font-medium", children: t("channel.loading", "Loading channel...") })
208    ] });
209  }
210  if (profileError || !profile) {
211    return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col items-center justify-center py-24 text-center px-4 bg-white dark:bg-[#0f0f0f] min-h-[60vh]", children: [
212      /* @__PURE__ */ jsxRuntimeExports.jsx(Users, { size: 48, className: "text-zinc-300 dark:text-zinc-700 mb-4" }),
213      /* @__PURE__ */ jsxRuntimeExports.jsx("h2", { className: "text-xl font-bold text-zinc-900 dark:text-white mb-2", children: t("channel.errorTitle", "Channel Unavailable") }),
214      /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-zinc-500 dark:text-zinc-400 max-w-md mb-6", children: profileError || t("channel.errorDesc", "The requested channel does not exist or could not be loaded.") })
215    ] });
216  }
217  const isOwnChannel = (currentUser == null ? void 0 : currentUser.username) === profile.username;
218  const hashColors = featuredTrack ? getColorsFromHash(featuredTrack.title) : { gradient: "linear-gradient(135deg, #6366f1, #a855f7)" };
219  return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "bg-white dark:bg-[#0f0f0f] text-zinc-950 dark:text-zinc-50 min-h-screen transition-colors duration-200", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "max-w-6xl mx-auto px-4 pt-4 sm:px-6", children: [
220    profile.banner ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-full h-36 sm:h-48 md:h-64 overflow-hidden relative rounded-2xl shadow-sm border border-zinc-200 dark:border-zinc-800", children: /* @__PURE__ */ jsxRuntimeExports.jsx("img", { src: profile.banner, alt: "Channel Banner", className: "w-full h-full object-cover" }) }) : /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "w-full h-36 sm:h-48 md:h-64 rounded-2xl shadow-inner relative overflow-hidden bg-gradient-to-r from-zinc-900 via-[#18181b] to-zinc-900 border border-zinc-200 dark:border-zinc-800", children: [
221      /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute inset-0 opacity-20 mix-blend-overlay pointer-events-none bg-[radial-gradient(ellipse_at_top_right,_var(--tw-gradient-stops))] from-indigo-300 via-fuchsia-500 to-indigo-950" }),
222      /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute -bottom-16 -left-16 w-64 h-64 bg-indigo-500 rounded-full filter blur-3xl opacity-10 animate-pulse" }),
223      /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute -top-16 -right-16 w-64 h-64 bg-fuchsia-500 rounded-full filter blur-3xl opacity-10 animate-pulse delay-1000" })
224    ] }),
225    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col md:flex-row items-center md:items-start gap-6 mt-6 pb-6 border-b border-zinc-200 dark:border-zinc-800", children: [
226      /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "-mt-14 sm:-mt-20 md:-mt-16 relative z-10 shrink-0", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "p-1 rounded-full bg-white dark:bg-[#0f0f0f] shadow-lg", children: /* @__PURE__ */ jsxRuntimeExports.jsx(UserAvatar, { src: profile.avatar, name: profile.name, size: 110, className: "sm:w-[136px] sm:h-[136px] sm:min-w-[136px] sm:min-h-[136px]" }) }) }),
227      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 text-center md:text-left min-w-0", children: [
228        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-wrap items-center justify-center md:justify-start gap-2.5", children: [
229          /* @__PURE__ */ jsxRuntimeExports.jsx("h1", { className: "text-2xl sm:text-3xl font-extrabold text-zinc-900 dark:text-white truncate", children: profile.name }),
230          profile.country && profile.showCountryBadge && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1 text-[10px] font-bold bg-zinc-100 dark:bg-zinc-800 px-2 py-0.5 rounded-full text-zinc-600 dark:text-zinc-300", children: [
231            /* @__PURE__ */ jsxRuntimeExports.jsx(Globe, { size: 11 }),
232            " ",
233            profile.country
234          ] })
235        ] }),
236        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-wrap items-center justify-center md:justify-start gap-1.5 text-sm text-zinc-500 dark:text-zinc-400 font-medium mt-1", children: [
237          /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: profile.handle ? `@${profile.handle}` : `@${profile.username.toLowerCase()}` }),
238          /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "opacity-40", children: "•" }),
239          /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: t("channel.subsCount", "{{count}} subscriber", { count: profile.subscriberCount }) }),
240          /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "opacity-40", children: "•" }),
241          /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: t("channel.tracksCount", "{{count}} MIDI", { count: profile.midisCount }) })
242        ] }),
243        profile.description && /* @__PURE__ */ jsxRuntimeExports.jsxs(
244          "p",
245          {
246            onClick: () => setActiveTab("about"),
247            className: "text-sm text-zinc-600 dark:text-zinc-400 mt-3 max-w-2xl line-clamp-2 md:line-clamp-1 cursor-pointer hover:text-zinc-900 dark:hover:text-zinc-200 transition-colors",
248            children: [
249              profile.description,
250              " ",
251              /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-bold text-indigo-600 dark:text-indigo-400 hover:underline text-xs shrink-0 ml-1 inline-flex items-center", children: [
252                t("channel.more", "more"),
253                " ",
254                /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { size: 12 })
255              ] })
256            ]
257          }
258        ),
259        /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-4 flex flex-wrap gap-2.5 justify-center md:justify-start", children: isOwnChannel ? /* @__PURE__ */ jsxRuntimeExports.jsx(
260          "button",
261          {
262            onClick: () => window.location.href = "/account",
263            className: "px-6 py-2 bg-zinc-100 dark:bg-zinc-800 hover:bg-zinc-200 dark:hover:bg-zinc-700 text-zinc-800 dark:text-white rounded-full text-sm font-bold transition-all transform hover:scale-[1.02]",
264            children: t("channel.editProfile", "Edit profile")
265          }
266        ) : /* @__PURE__ */ jsxRuntimeExports.jsx(
267          "button",
268          {
269            onClick: handleSubscribe,
270            disabled: subscribing,
271            className: `px-6 py-2 rounded-full text-sm font-bold transition-all transform hover:scale-[1.02] flex items-center gap-2 ${profile.isSubscribed ? "bg-zinc-100 hover:bg-zinc-200 dark:bg-zinc-800 dark:hover:bg-zinc-700 text-zinc-800 dark:text-white" : "bg-indigo-600 hover:bg-indigo-700 text-white shadow-md"}`,
272            children: subscribing ? /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { size: 16, className: "animate-spin" }) : profile.isSubscribed ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
273              /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { size: 16 }),
274              t("channel.subscribed", "Subscribed")
275            ] }) : t("channel.subscribe", "Subscribe")
276          }
277        ) })
278      ] })
279    ] }),
280    /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex border-b border-zinc-200 dark:border-zinc-800 gap-6 sm:gap-8 overflow-x-auto scrollbar-none mt-2", children: ["home", "tracks", "playlists", "about"].map((tab) => /* @__PURE__ */ jsxRuntimeExports.jsx(
281      "button",
282      {
283        onClick: () => setActiveTab(tab),
284        className: `py-3.5 px-1 font-bold text-sm border-b-2 relative transition-all uppercase tracking-wider ${activeTab === tab ? "border-indigo-600 dark:border-indigo-500 text-indigo-600 dark:text-indigo-400" : "border-transparent text-zinc-400 hover:text-zinc-700 dark:hover:text-zinc-200"}`,
285        children: t(`channel.tab.${tab}`, tab.charAt(0).toUpperCase() + tab.slice(1))
286      },
287      tab
288    )) }),
289    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "py-8", children: [
290      activeTab === "home" && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-10 animate-in fade-in duration-300", children: homeLoading && recentTracks.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex justify-center py-20", children: /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { className: "w-8 h-8 text-indigo-600 animate-spin" }) }) : recentTracks.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-center py-16 bg-zinc-50 dark:bg-zinc-900/30 rounded-2xl border border-dashed border-zinc-200 dark:border-zinc-800", children: [
291        /* @__PURE__ */ jsxRuntimeExports.jsx(Music, { size: 36, className: "mx-auto text-zinc-300 dark:text-zinc-700 mb-3" }),
292        /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-zinc-500 dark:text-zinc-400 text-sm font-medium", children: t("channel.noUploadsYet", "This channel hasn't uploaded any MIDI files yet.") })
293      ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
294        featuredTrack && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-4", children: [
295          /* @__PURE__ */ jsxRuntimeExports.jsxs("h2", { className: "text-lg font-bold text-zinc-900 dark:text-white flex items-center gap-2", children: [
296            /* @__PURE__ */ jsxRuntimeExports.jsx(Info, { size: 16, className: "text-indigo-500" }),
297            t("channel.featuredTrack", "Featured Upload")
298          ] }),
299          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-1 md:grid-cols-12 gap-6 bg-zinc-50 dark:bg-[#151518] p-5 rounded-2xl border border-zinc-150 dark:border-zinc-800 shadow-sm relative overflow-hidden transition-all duration-300 hover:border-zinc-250 dark:hover:border-zinc-700", children: [
300            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "md:col-span-5 aspect-video bg-zinc-200 dark:bg-zinc-800 rounded-xl overflow-hidden relative group shadow-sm shrink-0", children: [
301              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { background: hashColors.gradient }, className: "w-full h-full flex items-center justify-center relative overflow-hidden group-hover:scale-102 transition-transform duration-500", children: [
302                /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute inset-0 bg-gradient-to-t from-black/45 via-transparent to-transparent" }),
303                /* @__PURE__ */ jsxRuntimeExports.jsx(Music, { size: 32, className: "text-white/60 z-10 drop-shadow-[0_0_8px_rgba(255,255,255,0.3)]" })
304              ] }),
305              /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute bottom-2 right-2 bg-black/80 text-white text-[10px] font-bold px-1.5 py-0.5 rounded shadow-lg backdrop-blur-[2px]", children: featuredTrack.duration }),
306              /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute inset-0 bg-black/30 opacity-0 group-hover:opacity-100 flex items-center justify-center transition-all cursor-pointer", onClick: handlePlayFeatured, children: /* @__PURE__ */ jsxRuntimeExports.jsx("button", { className: "w-14 h-14 bg-indigo-600 rounded-full flex items-center justify-center text-white shadow-xl hover:scale-105 transition-transform", children: (activeTrack == null ? void 0 : activeTrack.id) === featuredTrack.id && isPlaying ? /* @__PURE__ */ jsxRuntimeExports.jsx(Pause, { size: 22 }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Play, { size: 22, fill: "currentColor", className: "ml-1" }) }) })
307            ] }),
308            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "md:col-span-7 flex flex-col justify-between py-1", children: [
309              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
310                /* @__PURE__ */ jsxRuntimeExports.jsx(
311                  "h3",
312                  {
313                    onClick: () => onSelectTrack(featuredTrack),
314                    className: "text-xl font-extrabold text-zinc-900 dark:text-white leading-snug hover:text-indigo-600 dark:hover:text-indigo-400 transition-colors cursor-pointer",
315                    children: featuredTrack.title
316                  }
317                ),
318                /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm font-semibold text-zinc-500 dark:text-zinc-400 mt-1", children: featuredTrack.artist }),
319                /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1.5 text-xs text-zinc-400 mt-3 font-medium", children: [
320                  /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1", children: [
321                    /* @__PURE__ */ jsxRuntimeExports.jsx(Eye, { size: 12 }),
322                    " ",
323                    featuredTrack.views,
324                    " views"
325                  ] }),
326                  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "•" }),
327                  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: timeAgo(featuredTrack.uploadedAt) })
328                ] }),
329                featuredTrack.description && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-zinc-500 dark:text-zinc-400 mt-4 line-clamp-3 leading-relaxed", children: featuredTrack.description })
330              ] }),
331              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-5 flex gap-2", children: [
332                /* @__PURE__ */ jsxRuntimeExports.jsx(
333                  "button",
334                  {
335                    onClick: handlePlayFeatured,
336                    className: "px-5 py-2 bg-indigo-600 hover:bg-indigo-700 text-white rounded-full text-xs font-bold transition-all shadow flex items-center gap-1.5",
337                    children: (activeTrack == null ? void 0 : activeTrack.id) === featuredTrack.id && isPlaying ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
338                      /* @__PURE__ */ jsxRuntimeExports.jsx(Pause, { size: 14 }),
339                      " ",
340                      t("common.pause", "Pause")
341                    ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
342                      /* @__PURE__ */ jsxRuntimeExports.jsx(Play, { size: 14, fill: "currentColor" }),
343                      " ",
344                      t("channel.play", "Play")
345                    ] })
346                  }
347                ),
348                /* @__PURE__ */ jsxRuntimeExports.jsx(
349                  "button",
350                  {
351                    onClick: (e) => onToggleWatchLater(featuredTrack.id, e),
352                    className: "px-4 py-2 border border-zinc-200 dark:border-zinc-800 hover:bg-zinc-50 dark:hover:bg-zinc-900 rounded-full text-xs font-bold transition-colors text-zinc-700 dark:text-zinc-300",
353                    children: watchLaterSet.has(featuredTrack.id) ? t("list.removeWatchLater", "Remove Later") : t("nav.watchLater", "Watch Later")
354                  }
355                )
356              ] })
357            ] })
358          ] })
359        ] }),
360        recentTracks.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-4", children: [
361          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between items-center", children: [
362            /* @__PURE__ */ jsxRuntimeExports.jsx("h2", { className: "text-lg font-bold text-zinc-900 dark:text-white", children: t("channel.recentUploads", "Recent Uploads") }),
363            /* @__PURE__ */ jsxRuntimeExports.jsxs(
364              "button",
365              {
366                onClick: () => {
367                  setActiveTab("tracks");
368                  setSortMode("recent");
369                },
370                className: "text-xs font-bold text-indigo-600 dark:text-indigo-400 hover:underline flex items-center gap-0.5",
371                children: [
372                  t("channel.viewAll", "View All"),
373                  " ",
374                  /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { size: 12 })
375                ]
376              }
377            )
378          ] }),
379          /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "grid grid-cols-2 md:grid-cols-3 lg:grid-cols-6 gap-x-4 gap-y-8", children: recentTracks.map((track) => /* @__PURE__ */ jsxRuntimeExports.jsx(
380            VideoCard,
381            {
382              track,
383              onSelect: (t2) => onSelectTrack(t2, void 0, recentTracks),
384              onToggleWatchLater,
385              isWatchLater: watchLaterSet.has(track.id),
386              variant: "grid",
387              onAddToQueue,
388              onAddToPlaylist,
389              playlist: recentTracks
390            },
391            `recent-${track.id}`
392          )) })
393        ] }),
394        popularTracks.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-4", children: [
395          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between items-center", children: [
396            /* @__PURE__ */ jsxRuntimeExports.jsx("h2", { className: "text-lg font-bold text-zinc-900 dark:text-white", children: t("channel.popularUploads", "Popular Uploads") }),
397            /* @__PURE__ */ jsxRuntimeExports.jsxs(
398              "button",
399              {
400                onClick: () => {
401                  setActiveTab("tracks");
402                  setSortMode("popular");
403                },
404                className: "text-xs font-bold text-indigo-600 dark:text-indigo-400 hover:underline flex items-center gap-0.5",
405                children: [
406                  t("channel.viewAll", "View All"),
407                  " ",
408                  /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { size: 12 })
409                ]
410              }
411            )
412          ] }),
413          /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "grid grid-cols-2 md:grid-cols-3 lg:grid-cols-6 gap-x-4 gap-y-8", children: popularTracks.map((track) => /* @__PURE__ */ jsxRuntimeExports.jsx(
414            VideoCard,
415            {
416              track,
417              onSelect: (t2) => onSelectTrack(t2, void 0, popularTracks),
418              onToggleWatchLater,
419              isWatchLater: watchLaterSet.has(track.id),
420              variant: "grid",
421              onAddToQueue,
422              onAddToPlaylist,
423              playlist: popularTracks
424            },
425            `popular-${track.id}`
426          )) })
427        ] })
428      ] }) }),
429      activeTab === "tracks" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-6 animate-in fade-in duration-300", children: [
430        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-wrap items-center justify-between gap-4 pb-4 border-b border-zinc-150 dark:border-zinc-800", children: [
431          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 bg-zinc-50 dark:bg-zinc-900 p-0.5 rounded-xl border border-zinc-200 dark:border-zinc-800 shadow-inner", children: [
432            /* @__PURE__ */ jsxRuntimeExports.jsx(
433              "button",
434              {
435                onClick: () => setSortMode("recent"),
436                className: `px-4 py-1.5 rounded-lg text-xs font-bold transition-all ${sortMode === "recent" ? "bg-white dark:bg-zinc-800 text-indigo-600 dark:text-indigo-400 shadow-sm" : "text-zinc-400 hover:text-zinc-600 dark:hover:text-zinc-300"}`,
437                children: t("channel.sort.recent", "Latest")
438              }
439            ),
440            /* @__PURE__ */ jsxRuntimeExports.jsx(
441              "button",
442              {
443                onClick: () => setSortMode("popular"),
444                className: `px-4 py-1.5 rounded-lg text-xs font-bold transition-all ${sortMode === "popular" ? "bg-white dark:bg-zinc-800 text-indigo-600 dark:text-indigo-400 shadow-sm" : "text-zinc-400 hover:text-zinc-600 dark:hover:text-zinc-300"}`,
445                children: t("channel.sort.popular", "Popular")
446              }
447            )
448          ] }),
449          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center bg-zinc-50 dark:bg-zinc-900 p-0.5 rounded-xl border border-zinc-200 dark:border-zinc-800 shadow-inner", children: [
450            /* @__PURE__ */ jsxRuntimeExports.jsx(
451              "button",
452              {
453                onClick: () => setViewMode("grid"),
454                className: `p-1.5 rounded-lg transition-all ${viewMode === "grid" ? "bg-white dark:bg-zinc-800 text-indigo-600 dark:text-indigo-400 shadow-sm" : "text-zinc-400 hover:text-zinc-600 dark:hover:text-zinc-300"}`,
455                title: "Grid View",
456                children: /* @__PURE__ */ jsxRuntimeExports.jsx(LayoutGrid, { size: 16 })
457              }
458            ),
459            /* @__PURE__ */ jsxRuntimeExports.jsx(
460              "button",
461              {
462                onClick: () => setViewMode("list"),
463                className: `p-1.5 rounded-lg transition-all ${viewMode === "list" ? "bg-white dark:bg-zinc-800 text-indigo-600 dark:text-indigo-400 shadow-sm" : "text-zinc-400 hover:text-zinc-600 dark:hover:text-zinc-300"}`,
464                title: "List View",
465                children: /* @__PURE__ */ jsxRuntimeExports.jsx(List, { size: 16 })
466              }
467            )
468          ] })
469        ] }),
470        tracksLoading && tracks.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex justify-center py-24", children: /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { className: "w-10 h-10 text-indigo-600 animate-spin" }) }) : tracks.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-center py-20", children: /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-zinc-400 font-medium", children: t("list.empty", "This list is empty.") }) }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
471          /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: viewMode === "grid" ? "grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-6 gap-x-4 gap-y-8" : "flex flex-col gap-3.5", children: tracks.map((track, idx) => {
472            const showAd = (idx + 1) % 12 === 0;
473            return /* @__PURE__ */ jsxRuntimeExports.jsxs(React.Fragment, { children: [
474              /* @__PURE__ */ jsxRuntimeExports.jsx(
475                VideoCard,
476                {
477                  track,
478                  onSelect: (t2) => onSelectTrack(t2, void 0, tracks),
479                  onToggleWatchLater,
480                  isWatchLater: watchLaterSet.has(track.id),
481                  variant: viewMode,
482                  onAddToQueue,
483                  onAddToPlaylist,
484                  playlist: tracks
485                }
486              ),
487              showAd && shouldShowExploreAd && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: viewMode === "grid" ? "col-span-1" : "w-full", children: /* @__PURE__ */ jsxRuntimeExports.jsx(AdSlot, { type: "exploreList", className: "!my-0 h-full min-h-[200px]" }) })
488            ] }, `tracks-${track.id}`);
489          }) }),
490          hasMore && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { ref: sentinelRef, className: "py-12 flex justify-center items-center", children: loadingMore && /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { className: "animate-spin text-indigo-600 w-8 h-8" }) })
491        ] })
492      ] }),
493      activeTab === "playlists" && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "animate-in fade-in duration-300", children: playlistsLoading ? /* @__P
493URE__ */ jsxRuntimeExports.jsx("div", { className: "flex justify-center py-24", children: /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { className: "w-10 h-10 text-indigo-600 animate-spin" }) }) : playlists.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-center py-16 bg-zinc-50 dark:bg-zinc-900/30 rounded-2xl border border-dashed border-zinc-200 dark:border-zinc-800", children: [
494        /* @__PURE__ */ jsxRuntimeExports.jsx(FolderOpen, { size: 36, className: "mx-auto text-zinc-300 dark:text-zinc-700 mb-3" }),
495        /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-zinc-500 dark:text-zinc-400 text-sm font-medium", children: t("channel.noPlaylists", "No public playlists found on this channel.") })
496      ] }) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6", children: playlists.map((pl) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
497        "div",
498        {
499          onClick: () => handlePlayPlaylist(pl),
500          className: "bg-zinc-50 dark:bg-[#151518] border border-zinc-200 dark:border-zinc-800 rounded-xl overflow-hidden hover:border-indigo-500 dark:hover:border-indigo-500 transition-colors group cursor-pointer relative shadow-sm",
501          children: [
502            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "aspect-video bg-zinc-200 dark:bg-zinc-800 relative group-hover:opacity-90 transition-opacity", children: [
503              pl.thumbnail ? /* @__PURE__ */ jsxRuntimeExports.jsx("img", { src: pl.thumbnail, className: "w-full h-full object-cover", alt: pl.title }) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-full h-full flex items-center justify-center text-zinc-400", children: /* @__PURE__ */ jsxRuntimeExports.jsx(FolderOpen, { size: 38, opacity: 0.25 }) }),
504              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "absolute inset-y-0 right-0 w-1/3 bg-black/60 flex flex-col items-center justify-center text-white backdrop-blur-[2px]", children: [
505                /* @__PURE__ */ jsxRuntimeExports.jsx(FolderOpen, { size: 20, className: "mb-1.5" }),
506                /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-sm font-extrabold", children: pl.trackIds.length }),
507                /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[9px] uppercase tracking-wider font-bold opacity-80", children: t("channel.playlistTracks", "Tracks") })
508              ] }),
509              /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute inset-0 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity bg-black/20", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-12 h-12 bg-indigo-600 rounded-full flex items-center justify-center text-white shadow-lg transform scale-90 group-hover:scale-100 transition-transform", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Play, { size: 20, fill: "currentColor", className: "ml-1" }) }) })
510            ] }),
511            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-4", children: [
512              /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "font-extrabold text-zinc-900 dark:text-white truncate text-base", children: pl.title }),
513              /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-xs text-zinc-400 mt-1 font-medium", children: [
514                t("channel.playlistUpdated", "Updated"),
515                " ",
516                new Date(pl.createdAt).toLocaleDateString()
517              ] })
518            ] })
519          ]
520        },
521        pl.id
522      )) }) }),
523      activeTab === "about" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-1 md:grid-cols-12 gap-8 animate-in fade-in duration-300", children: [
524        /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "md:col-span-8 space-y-6", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-3", children: [
525          /* @__PURE__ */ jsxRuntimeExports.jsx("h2", { className: "text-lg font-bold text-zinc-900 dark:text-white", children: t("channel.about.description", "Description") }),
526          profile.description ? /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-zinc-650 dark:text-zinc-350 text-sm leading-relaxed whitespace-pre-wrap", children: profile.description }) : /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-zinc-400 dark:text-zinc-500 text-sm italic", children: t("channel.about.noDescription", "No description provided by this uploader.") })
527        ] }) }),
528        /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "md:col-span-4 space-y-6", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-4 bg-zinc-50 dark:bg-[#151518] p-5 rounded-2xl border border-zinc-150 dark:border-zinc-800 shadow-sm text-sm", children: [
529          /* @__PURE__ */ jsxRuntimeExports.jsx("h2", { className: "text-base font-bold text-zinc-900 dark:text-white border-b border-zinc-200 dark:border-zinc-800 pb-2", children: t("channel.about.stats", "Stats") }),
530          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-3 font-medium", children: [
531            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3 text-zinc-650 dark:text-zinc-350", children: [
532              /* @__PURE__ */ jsxRuntimeExports.jsx(Calendar, { size: 16, className: "text-zinc-400" }),
533              /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: t("channel.about.joined", "Joined {{date}}", { date: getJoinedDate(profile.createdAt) }) })
534            ] }),
535            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3 text-zinc-650 dark:text-zinc-350 border-t border-zinc-200/50 dark:border-zinc-800/50 pt-2.5", children: [
536              /* @__PURE__ */ jsxRuntimeExports.jsx(Eye, { size: 16, className: "text-zinc-400" }),
537              /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: t("channel.about.views", "{{count}} total views", { count: profile.totalViews }) })
538            ] }),
539            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3 text-zinc-650 dark:text-zinc-350 border-t border-zinc-200/50 dark:border-zinc-800/50 pt-2.5", children: [
540              /* @__PURE__ */ jsxRuntimeExports.jsx(Users, { size: 16, className: "text-zinc-400" }),
541              /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: t("channel.about.subs", "{{count}} subscriber", { count: profile.subscriberCount }) })
542            ] }),
543            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3 text-zinc-650 dark:text-zinc-350 border-t border-zinc-200/50 dark:border-zinc-800/50 pt-2.5", children: [
544              /* @__PURE__ */ jsxRuntimeExports.jsx(Music, { size: 16, className: "text-zinc-400" }),
545              /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: t("channel.about.midisCount", "{{count}} uploads", { count: profile.midisCount }) })
546            ] })
547          ] })
548        ] }) })
549      ] })
550    ] })
551  ] }) });
552};
553export {
554  ChannelPage as default
555};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.