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.