@serifpersia/dango 3.3.9 → 3.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/client/dist/assets/{AnimeCard-URCiiy5W.js → AnimeCard-B0oMgXBm.js} +1 -1
- package/client/dist/assets/{AnimeInfo-e3z3er9t.js → AnimeInfo-o0Udf0og.js} +1 -1
- package/client/dist/assets/{AnimeInfoPage-B7Djlmy_.js → AnimeInfoPage-CojMIEuf.js} +2 -2
- package/client/dist/assets/Asmr-2V3yCCiK.js +2 -0
- package/client/dist/assets/AsmrResetProgressModal-C9R7HlNa.js +1 -0
- package/client/dist/assets/AvSyncCalibrator-Bo3820j3.js +10 -0
- package/client/dist/assets/Home-C8uXTzIM.js +1 -0
- package/client/dist/assets/Insights-CRqBfZuK.js +1 -0
- package/client/dist/assets/{LibraryListPage-B0qWqhb_.js → LibraryListPage-B_r51cRi.js} +1 -1
- package/client/dist/assets/{ListeningList-CSqD8j1C.js → ListeningList-BWnwvJTt.js} +1 -1
- package/client/dist/assets/{Manga-Cg2YuObF.js → Manga-CI5cZg3h.js} +1 -1
- package/client/dist/assets/{MangaInfoPage-DcArhEsy.js → MangaInfoPage-BtkOrO34.js} +1 -1
- package/client/dist/assets/MangaReadPage-Uv72AYdY.js +1 -0
- package/client/dist/assets/{MangaResetProgressModal-D_yT3d1v.js → MangaResetProgressModal-CPMEqZyi.js} +1 -1
- package/client/dist/assets/Mature-DXHWvoY5.js +1 -0
- package/client/dist/assets/MediaCard-DzA336CA.js +1 -0
- package/client/dist/assets/Music-C4dM0BQx.js +1 -0
- package/client/dist/assets/OptionListMenu-CpwxAC3J.js +1 -0
- package/client/dist/assets/Player-CpMvWTkH.js +56 -0
- package/client/dist/assets/PlayerSettings-pMU85k7p.js +1 -0
- package/client/dist/assets/QueueOptionsButton-1M7MdKzM.js +1 -0
- package/client/dist/assets/{QueueRail-B7f1-e6i.js → QueueRail-Bpla2xD8.js} +1 -1
- package/client/dist/assets/Radio-DlmTC-qG.js +1 -0
- package/client/dist/assets/{ReadingList-BaIem8H1.js → ReadingList-wJMDUybm.js} +1 -1
- package/client/dist/assets/{ResetProgressModal-DES5Y-Fl.js → ResetProgressModal-CByRM1Fh.js} +1 -1
- package/client/dist/assets/Search-B94D0tRM.js +1 -0
- package/client/dist/assets/Settings-CcU3oNY8.js +21 -0
- package/client/dist/assets/{SkeletonGrid-BxfgjP4i.js → SkeletonGrid-BJyZkg_K.js} +1 -1
- package/client/dist/assets/Trackers-vz-gqxtm.js +2 -0
- package/client/dist/assets/Tv-Rji_lvQo.js +4 -0
- package/client/dist/assets/Tv-ZEA45gXK.css +1 -0
- package/client/dist/assets/TvInfo-Ciujd6os.js +1 -0
- package/client/dist/assets/TvPopup-CqlVRF-Y.js +1 -0
- package/client/dist/assets/{TvSearch-BWDwWnHe.js → TvSearch-BcKXQue9.js} +1 -1
- package/client/dist/assets/{TvWatchlist-D3n5_4MI.js → TvWatchlist-sjsiup2h.js} +1 -1
- package/client/dist/assets/Watchlist-DAMdvt7Z.js +1 -0
- package/client/dist/assets/{hls-BO_L1WoN.js → hls-Do_n10_Y.js} +1 -1
- package/client/dist/assets/index-B5SLDCAi.js +178 -0
- package/client/dist/assets/useAnimeInfoData-DU9X90no.js +1 -0
- package/client/dist/assets/useAsmr-D6cSjbvt.js +1 -0
- package/client/dist/assets/{useAsmrPopup-D_o9-QlR.js → useAsmrPopup-DK1Vx-HV.js} +1 -1
- package/client/dist/assets/useMangaLibrary-DcxPn0RM.js +1 -0
- package/client/dist/assets/{useMangaPopup-TFzkC-tG.js → useMangaPopup-BLwvRZn0.js} +1 -1
- package/client/dist/assets/useProviders-D3L1Chqc.js +1 -0
- package/client/dist/assets/useTv-B546u-Rx.js +1 -0
- package/client/dist/assets/useTvLibrary-C2rPeCVT.js +1 -0
- package/client/dist/assets/{vendor-query-BXRECSI6.js → vendor-query-BUTye89G.js} +1 -1
- package/client/dist/index.html +2 -2
- package/orchestrator.js +1 -2
- package/package.json +6 -6
- package/server/dist/app-auth.js +9 -29
- package/server/dist/app-hono.js +621 -0
- package/server/dist/config.js +0 -1
- package/server/dist/db/drizzle.js +32 -0
- package/server/dist/db/schema-anime.js +64 -0
- package/server/dist/db/schema-asmr.js +25 -0
- package/server/dist/db/schema-manga.js +31 -0
- package/server/dist/db/schema-tv.js +33 -0
- package/server/dist/db.js +75 -4
- package/server/dist/discord-gateway.js +1 -1
- package/server/dist/google.js +0 -7
- package/server/dist/hono/asmr.js +1 -0
- package/server/dist/hono/auth.js +56 -0
- package/server/dist/hono/music.js +419 -0
- package/server/dist/{controllers/proxy.controller.js → hono/proxy.js} +390 -394
- package/server/dist/hono/tv.js +197 -0
- package/server/dist/hono/watchlist.js +733 -0
- package/server/dist/lib/anilist.js +3 -3
- package/server/dist/lib/insights.js +185 -0
- package/server/dist/lib/ipc.js +1 -2
- package/server/dist/lib/kitsu.js +1 -28
- package/server/dist/lib/mal-import.js +291 -0
- package/server/dist/lib/migration.js +20 -34
- package/server/dist/lib/offline-db.js +36 -36
- package/server/dist/lib/tracker/manga-sync.service.js +21 -21
- package/server/dist/lib/tracker/sync.service.js +20 -20
- package/server/dist/lib/translate.js +79 -0
- package/server/dist/lib/ytmusic.js +0 -2
- package/server/dist/providers/title-matching.js +0 -1
- package/server/dist/repositories/asmr.repository.js +90 -87
- package/server/dist/repositories/insights.repository.js +24 -13
- package/server/dist/repositories/library.repository.js +33 -93
- package/server/dist/repositories/manga.repository.js +108 -116
- package/server/dist/repositories/notifications.repository.js +50 -31
- package/server/dist/repositories/queue.repository.js +36 -25
- package/server/dist/repositories/settings.repository.js +18 -8
- package/server/dist/repositories/shows-meta.repository.js +54 -41
- package/server/dist/repositories/temp-show-ids.repository.js +43 -31
- package/server/dist/repositories/tv.repository.js +98 -115
- package/server/dist/repositories/watched-episodes.repository.js +52 -20
- package/server/dist/repositories/watchlist.repository.js +36 -48
- package/server/dist/server.js +59 -151
- package/server/dist/sync.js +41 -4
- package/server/dist/trpc/context.js +7 -0
- package/server/dist/trpc/errors.js +21 -0
- package/server/dist/trpc/index.js +19 -0
- package/server/dist/trpc/router.js +49 -0
- package/server/dist/trpc/routers/asmr-library.js +122 -0
- package/server/dist/trpc/routers/asmr-progress.js +133 -0
- package/server/dist/trpc/routers/asmr.js +87 -0
- package/server/dist/trpc/routers/auth.js +247 -0
- package/server/dist/trpc/routers/continue-watching.js +83 -0
- package/server/dist/trpc/routers/data.js +1039 -0
- package/server/dist/trpc/routers/discord.js +288 -0
- package/server/dist/trpc/routers/discovery.js +15 -0
- package/server/dist/trpc/routers/insights.js +45 -0
- package/server/dist/trpc/routers/manga-library.js +280 -0
- package/server/dist/trpc/routers/manga-progress.js +181 -0
- package/server/dist/trpc/routers/manga.js +201 -0
- package/server/dist/{controllers/music.controller.js → trpc/routers/music.js} +155 -288
- package/server/dist/trpc/routers/notifications.js +80 -0
- package/server/dist/trpc/routers/progress.js +15 -0
- package/server/dist/trpc/routers/queue.js +95 -0
- package/server/dist/{routes/radio.routes.js → trpc/routers/radio.js} +30 -23
- package/server/dist/trpc/routers/settings.js +105 -0
- package/server/dist/trpc/routers/tracker.js +219 -0
- package/server/dist/trpc/routers/tv-library.js +145 -0
- package/server/dist/trpc/routers/tv-progress.js +171 -0
- package/server/dist/trpc/routers/tv.js +565 -0
- package/server/dist/trpc/routers/watchlist.js +564 -0
- package/server/dist/trpc/validation.js +159 -0
- package/server/dist/utils/security.utils.js +0 -18
- package/server/package.json +5 -10
- package/client/dist/assets/Asmr-C28TfSvX.js +0 -2
- package/client/dist/assets/AsmrResetProgressModal-6Xh71AgR.js +0 -1
- package/client/dist/assets/AvSyncCalibrator-BwCtRW0P.js +0 -10
- package/client/dist/assets/Home-BJdbHYja.js +0 -1
- package/client/dist/assets/Insights-CjRy42YE.js +0 -1
- package/client/dist/assets/MangaReadPage-YCETZRte.js +0 -1
- package/client/dist/assets/Mature-BGxRWkV5.js +0 -1
- package/client/dist/assets/MediaCard-CAzoxsRS.js +0 -1
- package/client/dist/assets/Music-Cgo-yQ7E.js +0 -1
- package/client/dist/assets/OptionListMenu--Owe9Jle.js +0 -1
- package/client/dist/assets/Player-yRXG2BrP.js +0 -56
- package/client/dist/assets/PlayerSettings-DrZLa0CC.js +0 -1
- package/client/dist/assets/QueueOptionsButton-BVZyPyKf.js +0 -1
- package/client/dist/assets/Radio-BFjxlzuF.js +0 -1
- package/client/dist/assets/Search-BOBo0Ogh.js +0 -1
- package/client/dist/assets/Settings-DOvNQ0gX.js +0 -21
- package/client/dist/assets/Trackers-DZ9KqOWv.js +0 -2
- package/client/dist/assets/Tv-5mATEU4p.js +0 -4
- package/client/dist/assets/Tv-C2SWcVzN.css +0 -1
- package/client/dist/assets/TvInfo-CF-5C5Fs.js +0 -1
- package/client/dist/assets/TvPopup-Dfqifj3s.js +0 -1
- package/client/dist/assets/Watchlist-B8MHNsAU.js +0 -1
- package/client/dist/assets/index-D2RkUJIN.js +0 -178
- package/client/dist/assets/useAnimeInfoData-BWShDHqE.js +0 -1
- package/client/dist/assets/useAsmr-FyfD9G7b.js +0 -1
- package/client/dist/assets/useMangaLibrary-BJU5x8Vz.js +0 -1
- package/client/dist/assets/useProviders-DDRxa8vl.js +0 -1
- package/client/dist/assets/useTv-Bh6WmNnZ.js +0 -1
- package/client/dist/assets/useTvLibrary-_Pqm8re1.js +0 -1
- package/server/dist/controllers/asmr-library.controller.js +0 -288
- package/server/dist/controllers/auth.controller.js +0 -225
- package/server/dist/controllers/data.controller.js +0 -767
- package/server/dist/controllers/insights.controller.js +0 -203
- package/server/dist/controllers/manga-library.controller.js +0 -350
- package/server/dist/controllers/settings.controller.js +0 -609
- package/server/dist/controllers/tv-library.controller.js +0 -314
- package/server/dist/controllers/watchlist.controller.js +0 -1069
- package/server/dist/request-logger.js +0 -16
- package/server/dist/routes/asmr-library.routes.js +0 -23
- package/server/dist/routes/asmr.routes.js +0 -81
- package/server/dist/routes/auth.routes.js +0 -25
- package/server/dist/routes/data.routes.js +0 -47
- package/server/dist/routes/discord-gateway.routes.js +0 -51
- package/server/dist/routes/insights.routes.js +0 -35
- package/server/dist/routes/lan-auth.routes.js +0 -46
- package/server/dist/routes/manga-library.routes.js +0 -25
- package/server/dist/routes/manga.routes.js +0 -141
- package/server/dist/routes/music.routes.js +0 -20
- package/server/dist/routes/providers.routes.js +0 -17
- package/server/dist/routes/proxy.routes.js +0 -12
- package/server/dist/routes/settings.routes.js +0 -24
- package/server/dist/routes/tracker.routes.js +0 -287
- package/server/dist/routes/translate.routes.js +0 -88
- package/server/dist/routes/tv-library.routes.js +0 -23
- package/server/dist/routes/tv.routes.js +0 -629
- package/server/dist/routes/watchlist.routes.js +0 -341
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import{d as r,A as V,h as U,u as e,q as te,S as ee,T as Te,o as Re,a as De,g as ge,_ as Ie}from"./vendor-core-8R2fFbyN.js";import{d as Pe,B as D,M as G,h as xe,T as F,z as me,D as ie,E as Le,F as Oe,q as de,u as Ae,G as Ue,w as pe,x as Me,H as Ee,I as $e,V as je,J as Ge,K as Ke,L as He}from"./index-D2RkUJIN.js";import{u as ue}from"./vendor-query-BXRECSI6.js";import{I as q}from"./vendor-icons-CDMuNDeL.js";const oe={"title-preference-container":"_title-preference-container_sdqgs_1","toggle-button":"_toggle-button_sdqgs_21","toggle-label":"_toggle-label_sdqgs_51"},ce=["name","nativeName","englishName"],le={name:"Default (Romaji)",nativeName:"Native",englishName:"English"},Fe=()=>{const{titlePreference:t,setTitlePreference:n,loading:o}=Pe(),[i,l]=r(le[t]),[w,b]=r(!1),d=V(null);U(()=>{o||l(le[t])},[t,o]);const s=async()=>{if(w)return;b(!0);const g=(ce.indexOf(t)+1)%ce.length,p=ce[g],h=le[p];d.current&&(d.current.style.transform="translateX(-100%)",d.current.style.opacity="0"),setTimeout(async()=>{l(h),n(p),d.current&&(d.current.style.transition="none",d.current.style.transform="translateX(100%)",d.current.style.opacity="0",d.current.offsetWidth,d.current.style.transition="transform 0.3s ease-out, opacity 0.3s ease-out",d.current.style.transform="translateX(0)",d.current.style.opacity="1");try{await fetch("/api/settings",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({key:"titlePreference",value:p})})}catch(C){console.error("Error saving title preference:",C)}finally{b(!1)}},300)};return o?e("p",{children:"Loading title preference..."}):e("div",{className:oe["title-preference-container"],children:[e("h4",{children:"Anime Title Display Preference"}),e("button",{onClick:s,className:oe["toggle-button"],disabled:w,children:e("span",{ref:d,className:oe["toggle-label"],children:i})})]})},ze="_settingsHeader_14a5d_1",Be="_pageTitle_14a5d_6",We="_pageSubtitle_14a5d_14",qe="_settingsLayout_14a5d_20",Ve="_sidebar_14a5d_27",Je="_sidebarIndicator_14a5d_35",Ye="_sidebarItem_14a5d_50",Qe="_active_14a5d_72",Xe="_mainContent_14a5d_80",Ze="_tabContent_14a5d_85",et="_sectionCard_14a5d_103",tt="_settingItem_14a5d_127",at="_settingRow_14a5d_132",nt="_controls_14a5d_145",st="_status_14a5d_151",m={settingsHeader:ze,pageTitle:Be,pageSubtitle:We,settingsLayout:qe,sidebar:Ve,sidebarIndicator:Je,sidebarItem:Ye,active:Qe,mainContent:Xe,tabContent:Ze,sectionCard:et,settingItem:tt,settingRow:at,controls:nt,status:st},rt="_sectionCard_wt900_12",it="_title_wt900_22",ot="_signIn_wt900_29",ct="_userInfo_wt900_36",lt="_warning_wt900_58",dt="_formGroup_wt900_65",ut="_label_wt900_69",ht="_inputWrapper_wt900_79",gt="_input_wt900_79",mt="_select_wt900_103",pt="_actions_wt900_140",yt="_hr_wt900_148",y={sectionCard:rt,title:it,signIn:ot,userInfo:ct,warning:lt,formGroup:dt,label:ut,inputWrapper:ht,input:gt,select:mt,actions:pt,hr:yt},ft=()=>{const[t,n]=r(!0),[o,i]=r(!1),[l,w]=r(!1),[b,d]=r(null),[s,u]=r({status:"idle"}),[g,p]=r(!1),[h,C]=r(""),[k,f]=r(!1),I=V(null),[T,P]=r({show:!1,message:"",type:"info"}),N=()=>{I.current&&(window.clearInterval(I.current),I.current=null)},L=te(S=>{w(S.authenticated),d(S.user||S.device.user||null),u(S.device),p(!!S.hasCustomClientId)},[]),O=te(async()=>{const M=await(await fetch("/api/auth/github/status")).json();L(M)},[L]),E=async()=>{try{const M=await(await fetch("/api/auth/github/poll")).json();u(M),M.status==="success"?(N(),await O(),P({show:!0,message:"GitHub authentication complete. Sync has been initialized.",type:"success"})):M.status==="error"&&(N(),P({show:!0,message:M.error||"GitHub authentication failed.",type:"error"}))}catch{N(),P({show:!0,message:"Failed to poll GitHub authentication status.",type:"error"})}},_=(S=5)=>{N(),I.current=window.setInterval(E,Math.max(S,5)*1e3)};U(()=>(O().catch(()=>{P({show:!0,message:"Failed to load GitHub sync status.",type:"error"})}).finally(()=>n(!1)),N),[O]);const v=async()=>{i(!0);try{const S=await fetch("/api/auth/github/start",{method:"POST"}),M=await S.json();if(!S.ok)throw new Error("Failed to start GitHub authentication.");u(M),_(M.verification?.interval)}catch(S){P({show:!0,message:S instanceof Error?S.message:"Failed to start GitHub authentication.",type:"error"})}finally{i(!1)}},R=async()=>{try{await fetch("/api/auth/github/logout",{method:"POST"}),w(!1),d(null),u({status:"idle"}),P({show:!0,message:"Signed out of GitHub sync.",type:"success"})}catch{P({show:!0,message:"Failed to sign out of GitHub.",type:"error"})}},$=async()=>{f(!0);try{if(!(await fetch("/api/auth/github/auth",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({clientId:h})})).ok)throw new Error("save failed");C(""),await O(),P({show:!0,message:"Custom GitHub auth saved.",type:"success"})}catch{P({show:!0,message:"Failed to save override.",type:"error"})}finally{f(!1)}},K=async()=>{f(!0);try{await fetch("/api/auth/github/auth",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({clientId:""})}),C(""),await O(),P({show:!0,message:"Custom auth cleared. Using dango defaults.",type:"success"})}catch{P({show:!0,message:"Failed to clear override.",type:"error"})}finally{f(!1)}};return t?e("div",{style:{color:"var(--text-secondary)",padding:"1.5rem"},children:"Loading..."}):e("div",{className:y.sectionCard,children:[e("h3",{className:y.title,children:"GitHub Cloud Sync"}),e("p",{className:y.signIn,children:"Sync local data through a private GitHub repository named dango-sync-data."}),l&&b?e("div",{className:y.userInfo,children:[e("p",{children:["Signed in as: ",e("strong",{children:b.name||b.login})]}),e(D,{variant:"danger",onClick:R,children:"Sign Out"})]}):e("div",{className:y.signIn,children:e(D,{onClick:v,disabled:o||s.status==="pending",children:o||s.status==="pending"?"Waiting for GitHub...":"Sign in with GitHub"})}),s.status==="pending"&&s.verification&&e(ee,{children:[e("hr",{className:y.hr}),e("div",{className:y.formGroup,children:[e("label",{className:y.label,children:"Verification URL"}),e("div",{className:y.inputWrapper,children:[e("input",{className:y.input,value:s.verification.verification_uri,readOnly:!0}),e(D,{onClick:()=>{const S=s.verification?.verification_uri;S&&/^https?:\/\//i.test(S)&&window.open(S,"_blank","noopener,noreferrer")},children:"Open"})]})]}),e("div",{className:y.formGroup,children:[e("label",{className:y.label,children:"Device Code"}),e("input",{className:y.input,value:s.verification.user_code,readOnly:!0})]})]}),s.status==="error"&&e("p",{className:y.warning,children:s.error}),e("details",{style:{marginTop:"1rem",fontSize:"var(--font-size-sm)"},children:[e("summary",{style:{cursor:"pointer",color:"var(--text-secondary)"},children:["Advanced: use your own auth ",g?"(override active)":""]}),e("div",{className:y.formGroup,style:{marginTop:"0.75rem"},children:[e("label",{className:y.label,children:"Client ID (optional)"}),e("input",{className:y.input,value:h,onChange:S=>C(S.currentTarget.value),placeholder:"Your GitHub OAuth app ID"})]}),e("div",{className:y.actions,children:[e(D,{onClick:$,disabled:k,children:"Save override"}),e(D,{variant:"secondary",onClick:K,disabled:k,children:"Use dango defaults"})]})]}),e(G,{isOpen:T.show,onClose:()=>P(S=>({...S,show:!1})),width:"sm",children:[e(G.Body,{children:e("p",{children:T.message})}),e(G.Actions,{children:e(D,{onClick:()=>P(S=>({...S,show:!1})),children:"OK"})})]})]})},wt=()=>{const[t,n]=r(!0),[o,i]=r(null),[l,w]=r(!1),[b,d]=r(!1),[s,u]=r(""),[g,p]=r(""),[h,C]=r(""),[k,f]=r(!1),[I,T]=r({show:!1,message:"",type:"info"}),P=async()=>{try{const R=await(await fetch("/api/auth/user")).json();i(R?.email?R:null)}catch{i(null)}},N=async()=>{try{const[v,R]=await Promise.all([fetch("/api/auth/config-status"),fetch("/api/auth/google-auth")]),$=await v.json(),K=await R.json();w(!!$.hasConfig),d(!!(K.hasCustomWorkerUrl||K.hasCustomClientId||K.hasClientSecret))}catch(v){console.error("Failed to fetch config status",v)}};U(()=>{(async()=>{n(!0),await Promise.all([P(),N()]);try{new URLSearchParams(window.location.search).get("google_auth")==="success"&&(await P(),window.history.replaceState(null,"",window.location.pathname))}catch{}n(!1)})();const R=$=>{$.origin===window.location.origin&&typeof $.data=="object"&&$.data!==null&&$.data.type==="GOOGLE_AUTH_SUCCESS"&&(i($.data.user),window.location.reload())};return window.addEventListener("message",R),()=>window.removeEventListener("message",R)},[]);const L=async()=>{try{const R=await(await fetch("/api/auth/google/login",{method:"POST"})).json();if(R.authenticated){window.location.reload();return}if(R.url){if(typeof window.DangoBridge<"u"){window.location.href=R.url;return}const K=600,S=700,M=window.innerWidth/2-K/2,ae=window.innerHeight/2-S/2;window.open(R.url,"GoogleAuth",`width=${K},height=${S},top=${ae},left=${M}`)}else throw new Error("Auth URL not available")}catch{T({show:!0,message:"Authentication failed. Ensure server is configured correctly.",type:"error"})}},O=async()=>{try{await fetch("/api/auth/logout",{method:"POST"}),i(null),T({show:!0,message:"Successfully signed out.",type:"success"}),window.location.reload()}catch(v){console.error("Sign out failed",v),T({show:!0,message:"Failed to sign out.",type:"error"})}},E=async()=>{f(!0);try{if(!(await fetch("/api/auth/google-auth",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({workerUrl:s,clientId:g,clientSecret:h})})).ok)throw new Error("save failed");u(""),p(""),C(""),await N(),T({show:!0,message:"Custom auth saved. Restart may be required.",type:"success"})}catch{T({show:!0,message:"Failed to save override.",type:"error"})}finally{f(!1)}},_=async()=>{f(!0);try{await fetch("/api/auth/google-auth",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({workerUrl:"",clientId:"",clientSecret:""})}),u(""),p(""),C(""),await N(),T({show:!0,message:"Custom auth cleared. Using dango defaults.",type:"success"})}catch{T({show:!0,message:"Failed to clear override.",type:"error"})}finally{f(!1)}};return t?e("div",{style:{color:"var(--text-secondary)",padding:"1.5rem"},children:"Loading..."}):e("div",{className:y.sectionCard,children:[e("h3",{className:y.title,children:"Google Drive Sync"}),o?e("div",{className:y.userInfo,children:[e("p",{children:["Signed in as: ",e("strong",{children:o.name})," (",o.email,")"]}),e(D,{variant:"danger",onClick:O,children:"Sign Out"})]}):e("div",{className:y.signIn,children:[e("p",{children:"Sign in with your Google account to enable synchronization features."}),e(D,{onClick:L,disabled:!l,children:"Sign in with Google"}),!l&&e("p",{className:y.warning,children:"Google sync is not configured on the server."})]}),e("details",{style:{marginTop:"1rem",fontSize:"var(--font-size-sm)"},children:[e("summary",{style:{cursor:"pointer",color:"var(--text-secondary)"},children:["Advanced: use your own auth ",b?"(override active)":""]}),e("div",{className:y.formGroup,style:{marginTop:"0.75rem"},children:[e("label",{className:y.label,children:"Worker URL (optional)"}),e("input",{className:y.input,value:s,onChange:v=>u(v.currentTarget.value),placeholder:"https://your-worker.workers.dev"})]}),e("div",{className:y.formGroup,children:[e("label",{className:y.label,children:"Client ID (optional)"}),e("input",{className:y.input,value:g,onChange:v=>p(v.currentTarget.value),placeholder:"Your Google OAuth client ID"})]}),e("div",{className:y.formGroup,children:[e("label",{className:y.label,children:"Client Secret (optional)"}),e("input",{type:"password",className:y.input,value:h,onChange:v=>C(v.currentTarget.value),placeholder:"Your Google OAuth secret"})]}),e("div",{className:y.actions,children:[e(D,{onClick:E,disabled:k,children:"Save override"}),e(D,{variant:"secondary",onClick:_,disabled:k,children:"Use dango defaults"})]})]}),e(G,{isOpen:I.show,onClose:()=>T(v=>({...v,show:!1})),width:"sm",children:[e(G.Body,{children:e("p",{children:I.message})}),e(G.Actions,{children:e(D,{onClick:()=>T(v=>({...v,show:!1})),children:"OK"})})]})]})},vt="_sectionCard_1pqo4_1",bt="_settingItem_1pqo4_26",_t="_settingRow_1pqo4_31",kt="_modalOverlay_1pqo4_38",St="_modal_1pqo4_38",Ct="_modalActions_1pqo4_73",Nt="_modalSecondary_1pqo4_79",Tt="_modalPrimary_1pqo4_89",j={sectionCard:vt,settingItem:bt,settingRow:_t,modalOverlay:kt,modal:St,modalActions:Ct,modalSecondary:Nt,modalPrimary:Tt},Rt={anime:{heading:"Watchlist Preferences",blurb:"Customize your experience when managing your watchlist and series entries.",skipKey:"skipRemoveConfirmation",ignoreKey:"ignoreAdultContent",listOnlyKey:"cwWatchlistOnly",skipLabel:"Skip confirmation when removing from watchlist",ignoreLabel:"Hide adult content from continue watching",listOnlyLabel:"Only show continue watching for shows in watchlist",listNoun:"watchlist",continueNoun:"continue watching",adultCountUrl:"/api/continue-watching/adult-count",purgeUrl:"/api/continue-watching/purge-adult",purgeDoneText:t=>`Removed ${t??0} entries from continue watching`,invalidateKeys:[["allContinueWatching"]]},manga:{heading:"Reading List Preferences",blurb:"Customize your experience when managing your reading list and manga entries.",skipKey:"mangaSkipRemoveConfirmation",ignoreKey:"mangaIgnoreAdultContent",listOnlyKey:"mangaCwWatchlistOnly",skipLabel:"Skip confirmation when removing from reading list",ignoreLabel:"Hide adult content from continue reading",listOnlyLabel:"Only show continue reading for manga in reading list",listNoun:"reading list",continueNoun:"continue reading",adultCountUrl:"/api/manga/continue-reading/adult-count",purgeUrl:"/api/manga/continue-reading/purge-adult",purgeDoneText:t=>`Removed ${t??0} entries from continue reading`,invalidateKeys:[["manga-continue-reading"]]},tv:{heading:"TV Watchlist Preferences",blurb:"Customize your experience when managing your TV watchlist and series entries.",skipKey:"tvSkipRemoveConfirmation",ignoreKey:"tvIgnoreAdultContent",listOnlyKey:"tvCwWatchlistOnly",skipLabel:"Skip confirmation when removing from TV watchlist",ignoreLabel:"Hide adult content from continue watching",listOnlyLabel:"Only show continue watching for shows in TV watchlist",listNoun:"TV watchlist",continueNoun:"continue watching",adultCountUrl:"/api/tv/continue-watching/adult-count",purgeUrl:"/api/tv/continue-watching/purge-adult",purgeDoneText:t=>`Removed ${t??0} entries from continue watching`,invalidateKeys:[["tv-continue-watching"]]},asmr:{heading:"Listening List Preferences",blurb:"Customize your experience when managing your listening list and work entries.",skipKey:"asmrSkipRemoveConfirmation",ignoreKey:"asmrIgnoreAdultContent",listOnlyKey:"asmrCwWatchlistOnly",skipLabel:"Skip confirmation when removing from listening list",ignoreLabel:"Hide adult content from continue listening",listOnlyLabel:"Only show continue listening for works in listening list",listNoun:"listening list",continueNoun:"continue listening",adultCountUrl:"/api/asmr/continue-listening/adult-count",purgeUrl:"/api/asmr/continue-listening/purge-adult",purgeDoneText:t=>`Removed ${t??0} entries from continue listening`,invalidateKeys:[["asmr-continue-listening"]]}},Dt=(t,n)=>{if(t==null)return n;const o=String(t);return o==="true"||o==="1"?!0:o==="false"||o==="0"?!1:n},ye=(t,n,o)=>{const[i,l]=r(n),[w,b]=r(!1),d=ue();return U(()=>{(async()=>{try{const p=await(await fetch(`/api/settings?key=${t}`)).json();l(Dt(p.value,n))}catch(g){console.error("Failed to fetch setting",g)}})()},[t,n]),{value:i,setValue:l,isUpdating:w,toggle:async()=>{if(w)return;b(!0);const u=!i;l(u);try{await fetch("/api/settings",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({key:t,value:u})});for(const g of o)d.invalidateQueries({queryKey:g})}catch(g){console.error("Error saving setting:",g),l(!u)}finally{b(!1)}}}},It=()=>{const{contentType:t}=xe(),n=Rt[t],[o,i]=r(!1),[l,w]=r(!0),[b,d]=r(!1),s=ye(n.ignoreKey,!0,n.invalidateKeys),u=ye(n.listOnlyKey,!1,n.invalidateKeys),[g,p]=r(null),[h,C]=r(!1),k=ue();U(()=>{w(!0),(async()=>{try{const O=await(await fetch(`/api/settings?key=${n.skipKey}`)).json();String(O.value)==="true"||String(O.value)==="1"?i(!0):i(!1)}catch(L){console.error("Failed to fetch setting",L)}finally{w(!1)}})()},[n.skipKey]);const f=async()=>{if(b)return;d(!0);const N=!o;i(N);try{await fetch("/api/settings",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({key:n.skipKey,value:N})})}catch(L){console.error("Error saving setting:",L),i(!N)}finally{d(!1)}},I=async()=>{if(!s.isUpdating){if(s.value)try{const L=await(await fetch(n.adultCountUrl)).json();if(typeof L.count=="number"&&L.count>0){p(L.count);return}}catch(N){console.error("Failed to fetch adult count",N)}s.toggle()}},T=()=>{p(null),s.toggle()},P=async()=>{if(!h){C(!0);try{const L=await(await fetch(n.purgeUrl,{method:"POST"})).json();me.success(n.purgeDoneText(L.removed));for(const O of n.invalidateKeys)k.invalidateQueries({queryKey:O})}catch(N){console.error("Failed to purge adult entries",N),me.error("Failed to remove adult entries")}finally{C(!1),p(null)}s.toggle()}};return l?e("div",{children:"Loading settings..."}):e("div",{className:j.sectionCard,children:[e("h3",{children:n.heading}),e("p",{children:n.blurb}),e("div",{className:j.settingItem,children:e("div",{className:j.settingRow,children:[e("label",{htmlFor:"skip-confirmation-toggle",children:n.skipLabel}),e(F,{isChecked:o,onChange:f,id:"skip-confirmation-toggle",disabled:b})]})}),e("div",{className:j.settingItem,children:e("div",{className:j.settingRow,children:[e("label",{htmlFor:"ignore-adult-toggle",children:n.ignoreLabel}),e(F,{isChecked:s.value,onChange:I,id:"ignore-adult-toggle",disabled:s.isUpdating})]})}),e("div",{className:j.settingItem,children:e("div",{className:j.settingRow,children:[e("label",{htmlFor:"cw-watchlist-only-toggle",children:n.listOnlyLabel}),e(F,{isChecked:u.value,onChange:u.toggle,id:"cw-watchlist-only-toggle",disabled:u.isUpdating})]})}),g!==null&&e("div",{className:j.modalOverlay,onClick:()=>p(null),children:e("div",{className:j.modal,onClick:N=>N.stopPropagation(),children:[e("h4",{children:"Show adult entries?"}),e("p",{children:[g," adult ",g===1?"entry":"entries"," not in your"," ",n.listNoun," ",g===1?"is":"are"," currently hidden from"," ",n.continueNoun,". You can purge ",g===1?"it":"them"," from the database instead of showing ",g===1?"it":"them","."]}),e("div",{className:j.modalActions,children:[e("button",{type:"button",className:j.modalSecondary,onClick:T,disabled:h,children:"Show them"}),e("button",{type:"button",className:j.modalPrimary,onClick:P,disabled:h,children:h?"Purging...":`Purge ${g}`})]})]})})]})},Pt="_sectionCard_jkggs_1",xt="_statsGrid_jkggs_26",Lt="_statCard_jkggs_33",Ot="_statLabel_jkggs_43",At="_statValue_jkggs_51",Ut="_statMeta_jkggs_57",Mt="_statusBanner_jkggs_63",Et="_statusSuccess_jkggs_74",$t="_statusUpdating_jkggs_80",jt="_statusError_jkggs_86",Gt="_sha_jkggs_92",Kt="_settingItem_jkggs_99",Ht="_settingRow_jkggs_104",Ft="_settingTitle_jkggs_111",zt="_settingSubtitle_jkggs_117",Bt="_actionsRow_jkggs_124",x={sectionCard:Pt,statsGrid:xt,statCard:Lt,statLabel:Ot,statValue:At,statMeta:Ut,statusBanner:Mt,statusSuccess:Et,statusUpdating:$t,statusError:jt,sha:Gt,settingItem:Kt,settingRow:Ht,settingTitle:Ft,settingSubtitle:zt,actionsRow:Bt},Wt=()=>{const[t,n]=r(null),[o,i]=r(!0),[l,w]=r(!1),[b,d]=r(!1),s=V(null),u=async()=>{try{const k=await fetch("/api/settings/offline-db");if(k.ok){const f=await k.json();return n(f),f}}catch(k){console.error("Failed to fetch offline DB info:",k)}finally{i(!1)}return null};U(()=>(u(),()=>{s.current&&window.clearInterval(s.current)}),[]),U(()=>{t?.isRefreshing?s.current||(s.current=window.setInterval(async()=>{const k=await u();k&&!k.isRefreshing&&s.current&&(window.clearInterval(s.current),s.current=null)},2e3)):s.current&&(window.clearInterval(s.current),s.current=null)},[t?.isRefreshing]);const g=async()=>{if(!t||l)return;const k=!t.autoUpdateEnabled;w(!0),n({...t,autoUpdateEnabled:k});try{(await fetch("/api/settings/offline-db/auto-update",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({enabled:k})})).ok||n({...t,autoUpdateEnabled:!k})}catch(f){console.error("Failed to update auto update setting:",f),n({...t,autoUpdateEnabled:!k})}finally{w(!1)}},p=async()=>{if(!(t?.isRefreshing||b)){d(!0);try{(await fetch("/api/settings/offline-db/update",{method:"POST"})).ok&&n(f=>f?{...f,isRefreshing:!0,lastStatus:"updating",lastMessage:"Downloading database..."}:null)}catch(k){console.error("Failed to trigger offline DB update:",k)}finally{d(!1)}}},h=k=>{if(!k)return"Never";try{return new Date(k).toLocaleString(void 0,{dateStyle:"medium",timeStyle:"short"})}catch{return k}},C=t?.isRefreshing||b;return e("div",{className:x.sectionCard,children:[e("h3",{children:"Offline Metadata Database"}),e("p",{children:"Local MAL to AniList ID map used for instant imports and metadata enrichment without hitting rate-limited APIs. Updates automatically every Saturday in the background."}),e("div",{className:x.statsGrid,children:[e("div",{className:x.statCard,children:[e("span",{className:x.statLabel,children:"MAL Mapped"}),e("span",{className:x.statValue,children:o?"—":(t?.totalMalMapped??0).toLocaleString()})]}),e("div",{className:x.statCard,children:[e("span",{className:x.statLabel,children:"Release"}),e("span",{className:x.statMeta,children:o?"—":t?.releaseTag??"Never"})]}),e("div",{className:x.statCard,children:[e("span",{className:x.statLabel,children:"Last Checked"}),e("span",{className:x.statMeta,children:o?"—":h(t?.lastCheckedAt??null)})]}),e("div",{className:x.statCard,children:[e("span",{className:x.statLabel,children:"Last Updated"}),e("span",{className:x.statMeta,children:o?"—":h(t?.lastUpdatedAt??null)})]})]}),t?.lastMessage&&e("div",{className:`${x.statusBanner} ${t.lastStatus==="failed"?x.statusError:t.lastStatus==="updating"?x.statusUpdating:x.statusSuccess}`,children:[e("span",{children:t.lastMessage}),t.contentSha256&&e("span",{className:x.sha,children:["sha:",t.contentSha256.slice(0,12)]})]}),e("div",{className:x.settingItem,children:e("div",{className:x.settingRow,children:[e("div",{children:[e("span",{className:x.settingTitle,children:"Weekly Automatic Updates (Saturdays)"}),e("div",{className:x.settingSubtitle,children:"Download and index new database releases in the background. Catches up on startup if the server was offline."})]}),e(F,{id:"offline-db-auto-update-toggle",isChecked:t?t.autoUpdateEnabled:!0,onChange:g,disabled:o||l})]})}),e("div",{className:x.actionsRow,children:e(D,{onClick:p,disabled:o||C,children:C?"Updating Database...":"Update Offline Database Now"})})]})},qt="_sectionCard_1g4qx_1",Vt="_dangerCard_1g4qx_26",Jt="_status_1g4qx_34",Yt="_warningText_1g4qx_40",Qt="_ackLabel_1g4qx_44",Z={sectionCard:qt,dangerCard:Vt,status:Jt,warningText:Yt,ackLabel:Qt},Xt=()=>{const t=ue(),[n,o]=r(!1),[i,l]=r(!1),[w,b]=r(!1),[d,s]=r(""),u=()=>{l(!1),s(""),o(!0)},g=()=>{w||(o(!1),l(!1))},p=async()=>{if(!(!i||w)){b(!0),s("");try{const h=await fetch("/api/database/clear",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({confirm:!0})}),C=await h.json();if(!h.ok)throw new Error(C.error||"Failed to clear database");const k=C.deleted,f=C.deletedManga,I=C.deletedTv,T=C.deletedAsmr,P=Object.values(k).reduce((E,_)=>E+(_||0),0)+Object.values(f??{}).reduce((E,_)=>E+(_||0),0)+Object.values(I??{}).reduce((E,_)=>E+(_||0),0)+Object.values(T??{}).reduce((E,_)=>E+(_||0),0),N=(f?.manga_library??0)+(f?.manga_progress??0),L=(I?.tv_library??0)+(I?.tv_progress??0),O=(T?.asmr_library??0)+(T?.asmr_progress??0);s(`Library cleared — ${k.watchlist??0} watchlist entries, ${N} manga rows, ${L} TV rows, ${O} ASMR rows and ${P} total rows removed.`),t.invalidateQueries({queryKey:["watchlist"]}),t.invalidateQueries({queryKey:["allContinueWatching"]}),t.invalidateQueries({queryKey:["manga-library"]}),t.invalidateQueries({queryKey:["manga-library-ids"]}),t.invalidateQueries({queryKey:["manga-continue-reading"]}),t.invalidateQueries({queryKey:["tv-library"]}),t.invalidateQueries({queryKey:["tv-library-ids"]}),t.invalidateQueries({queryKey:["tv-continue-watching"]}),t.invalidateQueries({queryKey:["asmr-library"]}),t.invalidateQueries({queryKey:["asmr-library-ids"]}),t.invalidateQueries({queryKey:["asmr-continue-listening"]}),o(!1)}catch(h){s(h.message)}finally{b(!1),l(!1)}}};return e("div",{className:`${Z.sectionCard} ${Z.dangerCard}`,children:[e("h3",{children:"Danger Zone"}),e("p",{children:"Permanently delete your library (watchlist, watched episodes, queue, cached show metadata, manga reading list and reading progress, TV watchlist and watch progress, ASMR listening list and listening progress) to start fresh. Your settings, sync state and the offline metadata cache are preserved."}),e(D,{variant:"danger",onClick:u,children:"Clear Library Data"}),d&&e("p",{className:Z.status,children:d}),e(G,{isOpen:n,onClose:g,title:"Clear library data?",width:"sm",children:[e(G.Body,{children:[e("p",{className:Z.warningText,children:"This permanently deletes everything in your library. It acts on the active database (dev or prod, per server config) and cannot be undone. Settings, sync state and the offline metadata cache are kept."}),e("label",{className:Z.ackLabel,children:[e("input",{type:"checkbox",checked:i,onChange:h=>l(h.target.checked),disabled:w}),e("span",{children:"I understand this permanently deletes my library"})]})]}),e(G.Actions,{children:[e(D,{variant:"secondary",onClick:g,disabled:w,children:"Cancel"}),e(D,{variant:"danger",onClick:p,disabled:!i||w,children:w?"Clearing...":"Yes, delete everything"})]})]})]})},Zt=()=>{const[t,n]=r(""),[o,i]=r([]),[l,w]=r(null),[b,d]=r(!0),[s,u]=r({show:!1,message:"",type:"info"}),g=async()=>{try{d(!0);const C=await(await fetch("/api/auth/settings/rclone")).json();n(C.remote||""),i(C.availableRemotes||[]),w(C.activeRemote||null)}catch(h){console.error("Failed to fetch Rclone settings",h)}finally{d(!1)}};U(()=>{g()},[]);const p=async()=>{try{if((await fetch("/api/auth/settings/rclone",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({remote:t})})).ok)u({show:!0,message:"Rclone configuration saved successfully. You must restart the server for these changes to take effect.",type:"success"}),g();else throw new Error("Failed to save")}catch{u({show:!0,message:"Failed to save Rclone configuration.",type:"error"})}};return b?e("div",{style:{color:"var(--text-secondary)",padding:"1.5rem"},children:"Loading..."}):e("div",{className:y.sectionCard,children:[e("h3",{className:y.title,children:"Rclone Cloud Sync"}),e("p",{className:y.signIn,children:"Configure which Rclone remote to use for database synchronization."}),l?e("div",{className:y.userInfo,children:e("p",{children:["Current Active Remote: ",e("strong",{children:l})]})}):e("p",{className:y.warning,style:{marginBottom:"1.5rem"},children:"Rclone sync is not currently active. Ensure rclone is installed and a remote is configured."}),e("div",{className:y.formGroup,children:[e("label",{className:y.label,children:"Remote Name"}),e("div",{className:y.inputWrapper,children:e("select",{className:y.select,value:t,onChange:h=>n(h.target.value),children:[e("option",{value:"",children:"(Disabled / None)"}),o.map(h=>e("option",{value:h,children:h},h)),t&&!o.includes(t)&&e("option",{value:t,children:[t," (Manual)"]})]})}),e("p",{className:y.warning,style:{marginTop:"12px"},children:[e("strong",{children:"Note:"})," You can choose which provider is active using the selector at the top of the page."]})]}),e("div",{className:y.actions,children:e(D,{onClick:p,children:"Save Rclone Settings"})}),e(G,{isOpen:s.show,onClose:()=>u(h=>({...h,show:!1})),width:"sm",children:[e(G.Body,{children:e("p",{children:s.message})}),e(G.Actions,{children:e(D,{onClick:()=>u(h=>({...h,show:!1})),children:"OK"})})]})]})},ea=()=>{const[t,n]=r(null),[o,i]=r(!0),l=async()=>{try{const u=await(await fetch("/api/auth/settings/sync")).json();n(u)}catch(s){console.error("Failed to fetch sync settings",s)}finally{i(!1)}};U(()=>{l()},[]);const w=async s=>{const u=s.target.value;try{const g=await fetch("/api/auth/settings/sync",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({provider:u})});if(g.ok){const p=await g.json();n(h=>h?{...h,activeProvider:p.activeProvider}:null),await l(),ie.success(`Sync provider updated to ${u==="default"?"Default Order":u}`)}else ie.error("Failed to update sync provider")}catch{ie.error("Error updating sync provider")}};if(o||!t)return null;const b=[{value:"default",label:"Default (Priority Based)"}];t.authenticatedProviders.github&&b.push({value:"github",label:"GitHub"}),t.authenticatedProviders.google&&b.push({value:"google",label:"Google Drive"}),t.authenticatedProviders.rclone&&b.push({value:"rclone",label:"Rclone"}),b.push({value:"none",label:"None (Local Only)"});const d=s=>s==="none"?"Local Only (No Sync)":s==="google"?"Google Drive":s==="github"?"GitHub":s==="rclone"?"Rclone":s;return e("div",{className:m.sectionCard,children:[e("h3",{children:"Active Sync Provider"}),e("p",{children:'Choose which provider to use for synchronization. "Default" uses the first available provider you are logged into.'}),e("div",{style:{marginBottom:"1.5rem",padding:"0.75rem",background:"rgba(255, 255, 255, 0.03)",borderRadius:"var(--radius-md)",borderLeft:"4px solid var(--accent)",fontSize:"var(--font-size-sm)"},children:["Current Status:"," ",e("strong",{style:{color:"var(--accent-light)",textTransform:"capitalize"},children:d(t.actualActiveProvider)})]}),e("div",{className:m.settingRow,children:[e("label",{htmlFor:"sync-provider-select",children:"Selected Preference"}),e("select",{id:"sync-provider-select",value:t.activeProvider,onChange:w,style:{padding:"0.5rem",borderRadius:"var(--radius-md)",background:"rgba(255, 255, 255, 0.05)",color:"var(--text-primary)",border:"1px solid var(--glass-border)",fontSize:"var(--font-size-sm)",minWidth:"220px",outline:"none",cursor:"pointer"},children:b.map(s=>e("option",{value:s.value,children:s.label},s.value))})]})]})};function ta(){return"javascript:"+encodeURIComponent(`(function(){
|
|
2
|
-
if(!/(^|\\.)discord\\.com$/.test(location.hostname)){alert('Dango: use this bookmarklet while on discord.com');return}
|
|
3
|
-
var t=null;
|
|
4
|
-
try{
|
|
5
|
-
window.webpackChunkdiscord_app.push([[Symbol()],{},function(req){
|
|
6
|
-
if(!req.c)return;
|
|
7
|
-
var ms=Object.values(req.c);
|
|
8
|
-
for(var i=0;i<ms.length;i++){var m=ms[i];
|
|
9
|
-
try{
|
|
10
|
-
if(!m.exports||m.exports===window)continue;
|
|
11
|
-
if(typeof m.exports.getToken==='function'){var x=m.exports.getToken();if(x){t=x;return}}
|
|
12
|
-
for(var k in m.exports){try{var ex=m.exports[k];if(ex&&typeof ex.getToken==='function'&&ex[Symbol.toStringTag]!=='IntlMessagesProxy'){var y=ex.getToken();if(y){t=y;return}}}catch(e1){}}
|
|
13
|
-
}catch(e2){}}
|
|
14
|
-
}]);
|
|
15
|
-
window.webpackChunkdiscord_app.pop();
|
|
16
|
-
}catch(e3){}
|
|
17
|
-
if(!t){alert('Dango: could not grab your token. Make sure you are logged in to Discord in this tab, then run the bookmarklet again.');return}
|
|
18
|
-
function copyFallback(){var a=document.createElement('textarea');a.value=t;a.style.position='fixed';a.style.opacity='0';document.body.appendChild(a);a.select();try{document.execCommand('copy')}catch(e4){}document.body.removeChild(a)}
|
|
19
|
-
var done=function(){window.close();setTimeout(function(){if(!window.closed){alert('Dango: token copied to clipboard! Could not close this tab, so just close it, go back to Dango Settings, paste the token and press Save.')}},250)};
|
|
20
|
-
if(navigator.clipboard&&navigator.clipboard.writeText){navigator.clipboard.writeText(t).then(done,function(){copyFallback();done()})}else{copyFallback();done()}
|
|
21
|
-
})()`)}const aa=()=>{const t=Te(ta,[]);return e("div",{style:{marginTop:"0.9rem",paddingTop:"0.9rem",borderTop:"1px dashed var(--border-color)"},children:[e("h4",{style:{margin:"0 0 0.35rem",fontSize:"0.95rem"},children:"Grab the token automatically (bookmarklet)"}),e("p",{style:{margin:"0 0 0.6rem",fontSize:"0.85rem",color:"var(--text-secondary)"},children:"Set this up once and you never have to dig through DevTools: the bookmarklet copies your Discord token to the clipboard and closes the Discord tab — then you just paste it above and press Save."}),e("ol",{style:{margin:"0 0 0.75rem",paddingLeft:"1.25rem",fontSize:"0.85rem",color:"var(--text-secondary)",display:"grid",gap:"0.3rem"},children:[e("li",{children:["Drag the ",e("strong",{style:{color:"var(--text-primary)"},children:"Dango Token Grabber"})," ","button below onto your bookmarks bar (press ",e("kbd",{children:"Ctrl"}),"+",e("kbd",{children:"Shift"}),"+",e("kbd",{children:"B"})," ","first if the bar is hidden)."]}),e("li",{children:["Click ",e("strong",{style:{color:"var(--text-primary)"},children:"Open Discord"})," and log in (skip this if you are already logged in)."]}),e("li",{children:["On the Discord tab, click the"," ",e("strong",{style:{color:"var(--text-primary)"},children:"Dango Token Grabber"})," bookmark. The token lands in your clipboard and the tab closes on its own — come back here, paste it above and press ",e("strong",{children:"Save"}),"."]})]}),e("div",{style:{display:"flex",gap:"0.5rem",alignItems:"center",flexWrap:"wrap"},children:[e("a",{href:t,onClick:i=>{i.preventDefault(),alert("Drag this button onto your bookmarks bar instead of clicking it, then click it while you are on discord.com.")},title:"Drag me to your bookmarks bar",draggable:!0,style:{display:"inline-flex",alignItems:"center",padding:"0.4rem 0.8rem",borderRadius:"999px",border:"1px dashed var(--accent)",background:"var(--bg-secondary)",color:"var(--text-primary)",fontSize:"0.85rem",textDecoration:"none",cursor:"grab",userSelect:"none"},children:[e(q,{name:"bookmark",style:{marginRight:"0.45rem",color:"var(--accent)"}}),"Dango Token Grabber"]}),e("a",{className:"btn btn-secondary btn-sm",href:"https://discord.com/channels/@me",target:"_blank",rel:"noopener noreferrer",children:"Open Discord"}),e(D,{variant:"ghost",size:"sm",onClick:async()=>{try{await navigator.clipboard.writeText(t),alert('Bookmarklet code copied. Create a new bookmark on your bookmarks bar, name it "Dango Token" and paste the code as its URL.')}catch{alert(`Could not copy automatically. Right-click the "Dango Token Grabber" button and choose "Copy link address" instead, then paste that into a new bookmark's URL.`)}},children:"Copy code"})]}),e("p",{style:{margin:"0.5rem 0 0",fontSize:"0.75rem",color:"var(--text-tertiary)"},children:`"Copy code" is a fallback for browsers that won't drag bookmarklets: create any bookmark, then paste the copied code as its URL.`})]})},na="_sectionCard_1ag4p_1",sa="_settingItem_1ag4p_25",ra="_field_1ag4p_30",ia="_input_1ag4p_43",oa="_actions_1ag4p_63",ca="_status_1ag4p_69",z={sectionCard:na,settingItem:sa,field:ra,input:ia,actions:oa,status:ca},la=()=>{const[t,n]=r(!1),[o,i]=r(""),[l,w]=r(""),[b,d]=r(!0),[s,u]=r(!1),[g,p]=r("");U(()=>{(async()=>{try{const T=await(await fetch("/api/auth/app-status")).json();n(T.hasPassword)}catch{console.error("Failed to fetch LAN auth status")}finally{d(!1)}})()},[]);const h=async()=>{if(p(""),o!==l){p("Passwords do not match");return}if(o.length<4){p("Password must be at least 4 characters");return}u(!0);try{const f=await fetch("/api/auth/app-setup",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({password:o})}),I=await f.json();if(!f.ok){p(I.error||"Failed to update password");return}n(I.hasPassword),i(""),w(""),p(I.hasPassword?"Password set successfully":"Password removed")}catch{p("Network error")}finally{u(!1)}},C=async()=>{u(!0);try{const f=await fetch("/api/auth/app-setup",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({password:""})}),I=await f.json();if(!f.ok){p(I.error||"Failed to remove password");return}n(!1),i(""),w(""),p("Password removed")}catch{p("Network error")}finally{u(!1)}},k=async()=>{u(!0);try{await fetch("/api/auth/app-logout",{method:"POST"}),p("Logged out"),window.location.reload()}catch{p("Logout failed")}finally{u(!1)}};return b?e("div",{className:z.sectionCard,children:e("p",{children:"Loading..."})}):e("div",{className:z.sectionCard,children:[e("h3",{children:"LAN Lock"}),e("p",{children:"Require a password to access this instance from any device, including this one. Sessions persist across server restarts until logout or password change."}),e("div",{className:z.settingItem,children:[e("div",{className:z.field,children:[e("label",{htmlFor:"lan-password",children:"New Password"}),e("input",{id:"lan-password",type:"password",value:o,onChange:f=>i(f.target.value),placeholder:t?"Leave empty to keep current password":"Enter a password...",className:z.input})]}),e("div",{className:z.field,children:[e("label",{htmlFor:"lan-confirm-password",children:"Confirm Password"}),e("input",{id:"lan-confirm-password",type:"password",value:l,onChange:f=>w(f.target.value),placeholder:"Confirm password...",className:z.input})]}),e("div",{className:z.actions,children:[e(D,{variant:"primary",onClick:h,loading:s,disabled:s,children:t?"Update Password":"Set Password"}),t&&e(D,{variant:"danger",onClick:k,loading:s,disabled:s,children:"Logout"}),t&&e(D,{variant:"secondary",onClick:C,loading:s,disabled:s,children:"Remove Password"})]}),g&&e("p",{className:z.status,children:g})]})]})},da=()=>{const{theme:t,setTheme:n,loading:o}=Le();return e("div",{children:[e("h4",{style:{margin:"0 0 0.25rem",fontSize:"1rem"},children:"Palette"}),e("p",{style:{margin:"0 0 0.75rem",fontSize:"0.85rem",color:"var(--text-secondary)"},children:"Taro is the default. Pick a palette that suits you."}),e("div",{role:"radiogroup","aria-label":"Palette",style:{display:"grid",gridTemplateColumns:"repeat(auto-fit, minmax(150px, 1fr))",gap:"0.6rem"},children:Oe.map(i=>{const l=t===i.id;return e("button",{role:"radio","aria-checked":l,disabled:o,onClick:()=>n(i.id),style:{display:"flex",gap:"0.6rem",alignItems:"center",textAlign:"left",padding:"0.65rem 0.75rem",borderRadius:"10px",cursor:"pointer",background:l?"var(--accent-subtle)":"var(--bg-tertiary)",border:l?"1px solid var(--accent)":"1px solid var(--border-primary)",color:"var(--text-primary)"},children:[e("span",{"aria-hidden":!0,style:{width:26,height:26,borderRadius:"50%",flex:"none",background:i.swatch,border:"1px solid rgba(0,0,0,0.35)"}}),e("span",{style:{minWidth:0},children:[e("span",{style:{display:"block",fontSize:"0.85rem",fontWeight:700},children:i.label}),e("span",{style:{display:"block",fontSize:"0.75rem",color:"var(--text-secondary)",whiteSpace:"nowrap",overflow:"hidden",textOverflow:"ellipsis"},children:i.blurb})]})]},i.id)})})]})},ua="_card_13wyw_1",ha="_cardHeader_13wyw_9",ga="_discordBadge_13wyw_16",ma="_cardTitle_13wyw_27",pa="_cardSub_13wyw_34",ya="_description_13wyw_40",fa="_divider_13wyw_47",wa="_statsRow_13wyw_54",va="_statBox_13wyw_61",ba="_statValue_13wyw_71",_a="_statLabel_13wyw_78",ka="_rolePreview_13wyw_86",Sa="_badge_13wyw_93",Ca="_badgeRank_13wyw_100",Na="_badgeDere_13wyw_106",Ta="_progressSection_13wyw_112",Ra="_progressLabel_13wyw_116",Da="_progressTrack_13wyw_124",Ia="_progressFill_13wyw_132",Pa="_tierList_13wyw_139",xa="_dereSection_13wyw_147",La="_sectionHeading_13wyw_152",Oa="_dereGrid_13wyw_161",Aa="_dereCard_13wyw_167",Ua="_dereCardActive_13wyw_178",Ma="_dereName_13wyw_184",Ea="_dereGenre_13wyw_194",$a="_actions_13wyw_199",ja="_btnPrimary_13wyw_205",Ga="_btnSecondary_13wyw_234",Ka="_statusMsg_13wyw_257",Ha="_statusSuccess_13wyw_264",Fa="_statusError_13wyw_270",za="_statusInfo_13wyw_276",Ba="_userRow_13wyw_283",Wa="_avatar_13wyw_294",qa="_avatarPlaceholder_13wyw_302",Va="_userName_13wyw_314",Ja="_userId_13wyw_320",Ya="_notice_13wyw_325",Qa="_shareList_13wyw_332",Xa="_btnUnlink_13wyw_340",c={card:ua,cardHeader:ha,discordBadge:ga,cardTitle:ma,cardSub:pa,description:ya,divider:fa,statsRow:wa,statBox:va,statValue:ba,statLabel:_a,rolePreview:ka,badge:Sa,badgeRank:Ca,badgeDere:Na,progressSection:Ta,progressLabel:Ra,progressTrack:Da,progressFill:Ia,tierList:Pa,dereSection:xa,sectionHeading:La,dereGrid:Oa,dereCard:Aa,dereCardActive:Ua,dereName:Ma,dereGenre:Ea,actions:$a,btnPrimary:ja,btnSecondary:Ga,statusMsg:Ka,statusSuccess:Ha,statusError:Fa,statusInfo:za,userRow:Ba,avatar:Wa,avatarPlaceholder:qa,userName:Va,userId:Ja,notice:Ya,shareList:Qa,btnUnlink:Xa},B=[{label:"F-Rank",hours:0},{label:"E-Rank",hours:5},{label:"D-Rank",hours:20},{label:"C-Rank",hours:50},{label:"B-Rank",hours:100},{label:"A-Rank",hours:200},{label:"S-Rank",hours:400}],fe={fantasy:"Kamidere",action:"Kamidere",isekai:"Kamidere",magic:"Kamidere","super power":"Megadere",adventure:"Himedere","martial arts":"Himedere",family:"Himedere",comedy:"Bakadere",shounen:"Bakadere",parody:"Bakadere",sports:"Undere",music:"Deredere",romance:"Deredere",ecchi:"Deredere",harem:"Deredere",drama:"Dandere","slice of life":"Dandere",yuri:"Dandere","school life":"Dandere","high school":"Tsundere",school:"Tsundere",military:"Tsundere",shoujo:"Tsundere",seinen:"Kuudere","sci-fi":"Kuudere","science fiction":"Kuudere",mystery:"Kuudere",mecha:"Kuudere",josei:"Kuudere",psychological:"Yandere",horror:"Yandere",thriller:"Sadodere",supernatural:"Dorodere",demons:"Dorodere",game:"Oujidere",historical:"Kanedere"};function Za(t){return fe[(t||"").toLowerCase().trim()]||"Tsundere"}function en(t){const n=new Set;for(const o of t){const i=fe[o.toLowerCase().trim()];i&&n.add(i)}return[...n]}const tn=[{name:"Tsundere",genres:"Romance, School, Shoujo, Military, High School"},{name:"Yandere",genres:"Psychological, Horror"},{name:"Kuudere",genres:"Seinen, Sci-Fi, Mystery, Mecha, Josei"},{name:"Dandere",genres:"Drama, Slice of Life, Yuri, School Life"},{name:"Deredere",genres:"Romance, Ecchi, Harem, Music"},{name:"Bakadere",genres:"Comedy, Shounen, Parody"},{name:"Kamidere",genres:"Fantasy, Action, Isekai, Magic"},{name:"Dorodere",genres:"Supernatural, Demons"},{name:"Himedere",genres:"Adventure, Martial Arts, Family"},{name:"Sadodere",genres:"Thriller"},{name:"Undere",genres:"Sports"},{name:"Megadere",genres:"Super Power"},{name:"Oujidere",genres:"Game"},{name:"Kanedere",genres:"Historical"}];function an(t){return t>=400?"S-Rank":t>=200?"A-Rank":t>=100?"B-Rank":t>=50?"C-Rank":t>=20?"D-Rank":t>=5?"E-Rank":"F-Rank"}function nn(t){const n=[...B].reverse().findIndex(u=>t>=u.hours),o=n===-1?0:B.length-1-n,i=B[o].label,l=o<B.length-1?B[o+1]:null;if(!l)return{pct:100,current:i,next:"MAX",nextHours:0};const w=B[o].hours,b=B.length-1,d=(t-w)/(l.hours-w);return{pct:Math.min(100,(o+d)/b*100),current:i,next:l.label,nextHours:l.hours-t}}const se="dango_discord_user";function sn({workerUrl:t}){const[n,o]=r(()=>{try{const _=localStorage.getItem(se);if(_)return JSON.parse(_)}catch{}try{const v=new URLSearchParams(window.location.search).get("discord_user");if(v)return JSON.parse(v)}catch{}return null}),[i,l]=r(null),[w,b]=r(!1),[d,s]=r(null),[u,g]=r(()=>!n);U(()=>{const _=new URLSearchParams(window.location.search),v=_.get("discord_user");if(v){try{const R=JSON.parse(v);R&&R.id&&(o(R),g(!1),localStorage.setItem(se,JSON.stringify(R)),fetch("/api/discord-user",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({user:R})}).catch(()=>{}))}catch{}_.delete("discord_user"),window.history.replaceState(null,"",window.location.pathname+(_.toString()?`?${_.toString()}`:""))}},[]);const p=te(async()=>{if(n){g(!1);return}try{const _=await de("/api/discord-user");_.user&&(o(_.user),localStorage.setItem(se,JSON.stringify(_.user)))}catch{}finally{g(!1)}},[n]),h=te(async()=>{try{const _=await de("/api/insights/discord-sync-stats");l(_)}catch{}},[]);U(()=>{p(),h()},[p,h]);const C=()=>{localStorage.removeItem(se),o(null),s(null),fetch("/api/discord-user",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({user:null})}).catch(()=>{})},k=async()=>{if(!(!n||!i)){b(!0),s(null);try{const v=await(await fetch("/api/discord-sync-now",{method:"POST"})).json();v.success?s({type:"success",text:`✅ ${v.message}`}):s({type:"error",text:`❌ ${v.error||v.message||"Sync failed"}`})}catch{s({type:"error",text:"❌ Network error — check your connection"})}finally{b(!1)}}},f=i?Math.round(i.totalSeconds/3600):0,I=an(f),T=i?Za(i.topGenre):"—",P=i?.dere||(i?.genres?en(i.genres):[T]),{pct:N,next:L,nextHours:O}=nn(f),E=n?.avatar?`https://cdn.discordapp.com/avatars/${n.id}/${n.avatar}.${n.avatar.startsWith("a_")?"gif":"png"}?size=80`:null;return e(ee,{children:[e("div",{className:c.card,children:[e("div",{className:c.cardHeader,children:[e("div",{className:c.discordBadge,children:e("svg",{width:"20",height:"15",viewBox:"0 0 71 55",fill:"white","aria-hidden":"true",children:e("path",{d:"M60.1 4.9A58.5 58.5 0 0 0 45.5.3a.22.22 0 0 0-.23.11 40.8 40.8 0 0 0-1.8 3.7 54 54 0 0 0-16.2 0 37.5 37.5 0 0 0-1.83-3.7.23.23 0 0 0-.23-.11A58.3 58.3 0 0 0 10.9 4.9a.2.2 0 0 0-.1.08C1.58 18.73-1 32.23.31 45.55a.24.24 0 0 0 .09.17 58.8 58.8 0 0 0 17.7 8.95.23.23 0 0 0 .25-.08 42 42 0 0 0 3.61-5.88.22.22 0 0 0-.12-.31 38.7 38.7 0 0 1-5.53-2.64.23.23 0 0 1-.02-.37 29 29 0 0 0 1.1-.86.22.22 0 0 1 .23-.03c11.6 5.3 24.14 5.3 35.6 0a.22.22 0 0 1 .23.03c.36.3.73.58 1.1.86a.23.23 0 0 1-.02.37 36.4 36.4 0 0 1-5.53 2.64.22.22 0 0 0-.12.31 47.1 47.1 0 0 0 3.61 5.88.22.22 0 0 0 .25.08 58.6 58.6 0 0 0 17.72-8.95.23.23 0 0 0 .09-.16c1.47-15.27-2.47-28.65-10.46-40.47a.18.18 0 0 0-.09-.09ZM23.73 37.55c-3.49 0-6.37-3.21-6.37-7.15s2.82-7.15 6.37-7.15c3.58 0 6.42 3.24 6.37 7.15 0 3.94-2.82 7.15-6.37 7.15Zm23.54 0c-3.49 0-6.37-3.21-6.37-7.15s2.82-7.15 6.37-7.15c3.58 0 6.42 3.24 6.37 7.15 0 3.94-2.79 7.15-6.37 7.15Z"})})}),e("div",{children:[e("p",{className:c.cardTitle,children:"Discord Account"}),e("p",{className:c.cardSub,children:"Link your Discord to earn community roles"})]})]}),u?e("p",{className:c.notice,children:"Loading…"}):n?e(ee,{children:[e("div",{className:c.userRow,children:[E?e("img",{className:c.avatar,src:E,alt:n.username}):e("div",{className:c.avatarPlaceholder,children:"👤"}),e("div",{children:[e("p",{className:c.userName,children:n.username}),e("p",{className:c.userId,children:["ID: ",n.id]})]}),e("button",{className:c.btnUnlink,onClick:C,title:"Disconnect your Discord account",children:"Disconnect"})]}),e("p",{className:c.notice,children:"The following data is shared with the Dango community service to assign roles:"}),e("ul",{className:c.shareList,children:[e("li",{children:"Total watch time (hours)"}),e("li",{children:"Top 3 watched genres"}),e("li",{children:"Discord user ID + username"})]})]}):e(ee,{children:[e("p",{className:c.description,children:"Sign in with Discord (OAuth) to link your account. Only your public username and ID are stored — no email or DMs."}),e("div",{className:c.actions,children:[e("button",{className:c.btnPrimary,onClick:()=>{const _=encodeURIComponent(window.location.origin+"/settings?tab=community"),v=`${t}/auth/login?return_to=${_}`;window.location.href=v},children:"Login with Discord"}),e("a",{className:c.btnSecondary,href:"https://discord.gg/2FTSPXCsvn",target:"_blank",rel:"noopener noreferrer",children:"Join Server"})]})]})]}),e("div",{className:c.card,children:[e("div",{className:c.cardHeader,children:e("div",{children:[e("p",{className:c.cardTitle,children:"Your Roles"}),e("p",{className:c.cardSub,children:"Based on your current Dango watch history"})]})}),i?e(ee,{children:[e("div",{className:c.statsRow,children:[e("div",{className:c.statBox,children:[e("span",{className:c.statValue,children:[f,"h"]}),e("span",{className:c.statLabel,children:"Watch Time"})]}),e("div",{className:c.statBox,children:[e("span",{className:c.statValue,children:i.topGenre||"—"}),e("span",{className:c.statLabel,children:"Top Genre"})]})]}),e("div",{className:c.rolePreview,children:[e("span",{className:`${c.badge} ${c.badgeRank}`,children:I}),e("span",{className:`${c.badge} ${c.badgeDere}`,children:T})]}),e("div",{className:c.progressSection,children:[e("div",{className:c.progressLabel,children:[e("span",{children:["Progress to ",L]}),e("span",{children:L==="MAX"?"MAX":`${O}h remaining`})]}),e("div",{className:c.progressTrack,children:e("div",{className:c.progressFill,style:{width:`${N}%`}})}),e("div",{className:c.tierList,children:[e("span",{children:"0h (F)"}),e("span",{children:"5h (E)"}),e("span",{children:"20h (D)"}),e("span",{children:"50h (C)"}),e("span",{children:"100h (B)"}),e("span",{children:"200h (A)"}),e("span",{children:"400h (S)"})]})]}),e("div",{className:c.dereSection,children:[e("p",{className:c.sectionHeading,children:"Personality Archetypes"}),e("div",{className:c.dereGrid,children:tn.map(_=>{const v=P.includes(_.name);return e("div",{className:`${c.dereCard} ${v?c.dereCardActive:""}`,children:[e("span",{className:c.dereName,children:_.name}),e("span",{className:c.dereGenre,children:_.genres})]},_.name)})})]}),e("hr",{className:c.divider}),e("div",{className:c.actions,children:[e("button",{className:c.btnPrimary,onClick:k,disabled:!n||w,id:"discord-roles-sync-btn",children:w?"Syncing…":"Sync Roles"}),!n&&e("span",{style:{fontSize:"0.78rem",color:"var(--text-tertiary)",alignSelf:"center"},children:"Login first to sync"})]}),d&&e("p",{className:`${c.statusMsg} ${d.type==="success"?c.statusSuccess:d.type==="error"?c.statusError:c.statusInfo}`,children:d.text})]}):e("p",{className:c.notice,children:"No watch data yet — start watching to earn roles!"})]}),e("div",{className:c.card,children:[e("div",{className:c.cardHeader,children:e("div",{children:[e("p",{className:c.cardTitle,children:"Dango Discord Server"}),e("p",{className:c.cardSub,children:"Community, announcements and support"})]})}),e("div",{className:c.actions,children:e("a",{className:c.btnPrimary,href:"https://discord.gg/2FTSPXCsvn",target:"_blank",rel:"noopener noreferrer",children:"Join the Server"})})]})]})}function rn({variant:t="info",children:n,className:o=""}){return e("div",{className:`alert alert-${t} ${o}`,children:n})}const un=()=>{const[t,n]=Re(),o=De(),i=t.get("tab"),[l,w]=r(i&&["general","sync","watchlist","database","community"].includes(i)?i:"general"),[b,d]=r(""),s=V(null),u=V(null),g=V(new Map),[p,h]=r({top:0,height:0,left:0,width:0}),{lowEndMode:C,setLowEndMode:k}=Ae(),[f,I]=r(""),[T,P]=r(localStorage.getItem("telemetry_enabled")!=="false"),[N,L]=r(localStorage.getItem("installation_id")||"");U(()=>{fetch("/api/installation-id").then(a=>a.json()).then(a=>{a.id&&(L(a.id),localStorage.setItem("installation_id",a.id))}).catch(()=>{})},[]),U(()=>{de("/api/discord-roles-config").then(a=>{a.workerUrl&&I(a.workerUrl)}).catch(()=>{})},[]);const[O,E]=r(Ue),[_,v]=r(!0),{data:R}=pe("discordRPCEnabled"),$=Me(),[K,S]=r(!0),{data:M}=pe("discordRPCHideMature"),[ae,ne]=r(""),[J,re]=r(null);U(()=>{fetch("/api/discord/gateway/status").then(a=>a.json()).then(a=>{re(a),a.masked&&a.masked!=="none"&&ne(a.masked)}).catch(()=>{})},[]),U(()=>{R!==void 0&&v(R==="true"||R===!0||R===null)},[R]),U(()=>{M!==void 0&&S(M==="true"||M===!0||M===null)},[M]);const we=a=>{v(a),$.mutate({key:"discordRPCEnabled",value:String(a)})},ve=a=>{S(a),$.mutate({key:"discordRPCHideMature",value:String(a)})},be=a=>{P(a),localStorage.setItem("telemetry_enabled",String(a)),a?Ke():He()},_e=a=>{E(a),localStorage.setItem(je,String(a)),window.dispatchEvent(new CustomEvent(Ge))};ge.useEffect(()=>{document.title="Settings - dango"},[]),ge.useEffect(()=>{const a=t.get("tab");a&&["general","sync","watchlist","database","community"].includes(a)&&w(a)},[t]);const Y=a=>{w(a),n(a==="general"?{}:{tab:a})},Q=te(()=>{const a=g.current.get(l),A=u.current;if(a&&A){const W=A.getBoundingClientRect(),H=a.getBoundingClientRect(),X=window.matchMedia("(max-width: 900px)").matches;h(X?{top:H.top-W.top,height:H.height,left:H.left-W.left,width:H.width}:{top:H.top-W.top,height:H.height,left:0,width:0})}},[l]);Ie(()=>{Q()},[l,Q]),U(()=>{const a=()=>Q();window.addEventListener("resize",a);let A=!1;return document.fonts?.ready&&document.fonts.ready.then(()=>{A||Q()}),()=>{A=!0,window.removeEventListener("resize",a)}},[l,Q]);const ke=()=>{d("Exporting databases...");const a=window.DangoBridge;if(a?.downloadFile){a.downloadFile("/api/backup-db","dango-backup.json"),setTimeout(()=>{d("Databases exported successfully!")},1500);return}const A=document.createElement("a");A.href="/api/backup-db",A.download="dango-backup.json",document.body.appendChild(A),A.click(),A.remove(),setTimeout(()=>{d("Databases exported successfully!")},1500)},Se=async a=>{const A=a.target.files?.[0];if(!A)return;d("Importing databases...");const W=new FormData;W.append("dbfile",A);try{const H=await fetch("/api/restore-db",{method:"POST",body:W}),X=await H.json();if(H.ok){const he=Array.isArray(X.restored)?X.restored.join(", "):null;d(he?`Databases imported successfully (${he})!`:"Database imported successfully!"),setTimeout(()=>window.location.reload(),2e3)}else d(`Import failed: ${X.error}`)}catch{d("Import failed: An unexpected error occurred.")}finally{a.target.value=""}},Ce=()=>{s.current?.click()},Ne=()=>{switch(l){case"general":return e("div",{className:m.tabContent,children:e("div",{className:m.sectionCard,children:[e("h3",{children:"Appearance & Preferences"}),e("p",{children:"Configure how titles are displayed and other general preferences."}),e("div",{className:m.settingItem,children:e(Fe,{})}),e("div",{className:m.settingItem,style:{marginTop:"1.5rem"},children:e(da,{})}),e("div",{className:m.settingItem,style:{marginTop:"1.5rem"},children:e("div",{className:m.settingRow,children:[e("div",{style:{minWidth:0},children:[e("h4",{style:{margin:0,fontSize:"1rem"},children:"Low End Mode"}),e("p",{style:{margin:"0.25rem 0 0",fontSize:"0.85rem",color:"var(--text-secondary)"},children:"Disables animations and heavy visual effects for better performance on older hardware."})]}),e(F,{isChecked:C,onChange:a=>k(a.target.checked),id:"low-end-mode"})]})}),e("div",{className:m.settingItem,style:{marginTop:"1.5rem"},children:e("div",{className:m.settingRow,children:[e("div",{style:{minWidth:0},children:[e("h4",{style:{margin:0,fontSize:"1rem"},children:"Virtual Keyboard"}),e("p",{style:{margin:"0.25rem 0 0",fontSize:"0.85rem",color:"var(--text-secondary)"},children:"Shows an on-screen keyboard when text search fields are focused."})]}),e(F,{isChecked:O,onChange:a=>_e(a.target.checked),id:"virtual-keyboard-enabled"})]})}),e("div",{className:m.settingItem,style:{marginTop:"1.5rem"},children:e("div",{className:m.settingRow,children:[e("div",{style:{minWidth:0},children:[e("h4",{style:{margin:0,fontSize:"1rem"},children:"Discord Rich Presence"}),e("p",{style:{margin:"0.25rem 0 0",fontSize:"0.85rem",color:"var(--text-secondary)"},children:"Show your current anime and watch progress on your Discord profile status."})]}),e(F,{isChecked:_,onChange:a=>we(a.target.checked),id:"discord-rpc-enabled"})]})}),e("div",{style:{marginTop:"2rem"},children:e(la,{})}),_&&e("div",{className:m.settingItem,style:{marginTop:"1rem"},children:e("div",{className:m.settingRow,children:[e("div",{style:{minWidth:0},children:[e("h4",{style:{margin:0,fontSize:"1rem"},children:"Hide Mature Content"}),e("p",{style:{margin:"0.25rem 0 0",fontSize:"0.85rem",color:"var(--text-secondary)"},children:"Hide mature content (18+) from Discord Rich Presence."})]}),e(F,{isChecked:K,onChange:a=>ve(a.target.checked),id:"discord-hide-mature"})]})}),e("div",{className:m.settingItem,style:{marginTop:"1.5rem",background:"var(--bg-tertiary)",padding:"1rem",borderRadius:"8px",border:"1px solid var(--border-color)"},children:[e("div",{className:m.settingRow,children:e("div",{style:{minWidth:0},children:[e("h4",{style:{margin:0,fontSize:"1rem"},children:"Discord Mobile Presence (Gateway)"}),e("p",{style:{margin:"0.25rem 0 0.75rem",fontSize:"0.85rem",color:"var(--text-secondary)"},children:"Paste your Discord authentication token to enable presence on mobile devices. If no token is saved, the default desktop Rich Presence is used. The token is stored locally in your configuration and never shared."})]})}),e(rn,{variant:"warning",style:{marginTop:"1.25rem",paddingTop:"1.25rem"},children:[e("strong",{children:"Use at your own risk."})," This feature uses the Discord Gateway API with your user token, which violates Discord's Terms of Service. Discord may detect this usage and take action against your account, including phone number locks, temporary suspensions, or permanent bans. While the risk is generally low, it is not zero. Only use this if you understand and accept the risks."]}),e("div",{style:{marginTop:"0.75rem",display:"flex",gap:"0.5rem",alignItems:"center",flexWrap:"wrap"},children:[e("input",{type:"password",value:ae,onChange:a=>ne(a.target.value),placeholder:"Paste Discord token here...",style:{flex:"1 1 200px",padding:"0.5rem",borderRadius:"4px",border:"1px solid var(--border-color)",background:"var(--bg-primary)",color:"var(--text-primary)",fontFamily:"monospace",minWidth:0}}),e(D,{variant:"primary",size:"sm",onClick:async()=>{const a=await fetch("/api/discord/gateway/save",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({token:ae})}),A=await a.json();a.ok?(re({hasToken:!0,masked:A.masked,enabled:!0}),ne(A.masked)):alert(A.error||"Failed to save token")},children:"Save"}),e(D,{variant:"secondary",size:"sm",onClick:async()=>{await fetch("/api/discord/gateway/remove",{method:"POST"}),ne(""),re({hasToken:!1,masked:null,enabled:!1})},children:"Remove"})]}),J&&e("p",{style:{marginTop:"0.5rem",fontSize:"0.8rem",color:J.hasToken?"green":"var(--text-secondary)"},children:["Status:"," ",J.hasToken?`Token saved (${J.masked})`:"No token saved",J.enabled?" - Gateway active":""]}),e(aa,{})]}),e("div",{className:m.settingItem,style:{marginTop:"1.5rem"},children:[e("div",{className:m.settingRow,children:[e("div",{style:{minWidth:0},children:[e("h4",{style:{margin:0,fontSize:"1rem"},children:"Telemetry Tracking"}),e("p",{style:{margin:"0.25rem 0 0",fontSize:"0.85rem",color:"var(--text-secondary)"},children:"Share anonymous installation data to help track active users. Collected: Browser type and OS (e.g. 'Chrome on Windows'), App Version, First Seen/Last Seen timestamps, and timezone (e.g. 'Europe/Berlin'). No other personal information or usage habits are collected."})]}),e(F,{isChecked:T,onChange:a=>be(a.target.checked),id:"telemetry-enabled"})]}),T&&e("div",{style:{marginTop:"0.75rem",fontSize:"0.8rem",color:"var(--text-secondary)"},children:[e("p",{style:{margin:"0 0 0.5rem 0",fontWeight:"bold"},children:"Data currently being shared:"}),e("div",{style:{background:"var(--bg-tertiary)",padding:"0.5rem",borderRadius:"4px",wordBreak:"break-all",fontFamily:"monospace"},children:[e("p",{style:{margin:"0"},children:[e("strong",{children:"ID:"})," ",N||"Loading..."]}),e("p",{style:{margin:"0"},children:[e("strong",{children:"Version:"})," ",Ee.version]}),e("p",{style:{margin:"0"},children:[e("strong",{children:"Browser:"})," ",$e()]}),e("p",{style:{margin:"0"},children:[e("strong",{children:"Timezone:"})," ",Intl.DateTimeFormat().resolvedOptions().timeZone]})]})]}),e("div",{style:{marginTop:"1rem"},children:e(D,{variant:"secondary",size:"sm",onClick:()=>o("/map"),children:"View User Map"})})]}),localStorage.getItem("agreedToViewMature")==="true"&&e("div",{className:m.settingItem,style:{marginTop:"1.5rem"},children:e("div",{className:m.settingRow,children:[e("div",{style:{minWidth:0},children:[e("h4",{style:{margin:0,fontSize:"1rem"},children:"Mature Content"}),e("p",{style:{margin:"0.25rem 0 0",fontSize:"0.85rem",color:"var(--text-secondary)"},children:"You have previously agreed to view 18+ content. Toggle this off to re-enable the blur gate and filtering."})]}),e(F,{isChecked:!0,onChange:a=>{a.target.checked||(localStorage.removeItem("agreedToViewMature"),window.location.reload())},id:"mature-content-enabled"})]})})]})});case"sync":return e("div",{className:m.tabContent,children:[e(ea,{}),e(ft,{}),e(wt,{}),e(Zt,{})]});case"watchlist":return e("div",{className:m.tabContent,children:e(It,{})});case"database":return e("div",{className:m.tabContent,children:[e("div",{className:m.sectionCard,children:[e("h3",{children:"Database Management"}),e("p",{children:"Download a backup of all databases (anime, manga, TV and ASMR) or restore from a backup file. Legacy single-file anime (.db) backups can still be restored."}),e("div",{className:m.controls,children:[e(D,{onClick:ke,children:"Backup Database"}),e(D,{variant:"secondary",onClick:Ce,children:"Restore Database"})]}),e("input",{type:"file",ref:s,onChange:Se,style:{position:"absolute",width:1,height:1,opacity:0,overflow:"hidden"},accept:".json,.db,application/octet-stream"}),b&&e("p",{className:m.status,children:b})]}),e(Wt,{}),e(Xt,{})]});case"community":return e("div",{className:m.tabContent,children:f?e(sn,{workerUrl:f}):e("div",{className:m.sectionCard,children:[e("h3",{children:"Discord Community Roles"}),e("p",{children:"Discord Roles integration is not configured on this instance."})]})});default:return null}};return e("div",{className:"page-container",children:[e("div",{className:m.settingsHeader,children:[e("h1",{className:m.pageTitle,children:"Settings"}),e("p",{className:m.pageSubtitle,children:"Manage your preferences and data synchronization"})]}),e("div",{className:m.settingsLayout,children:[e("aside",{className:m.sidebar,ref:u,children:[e("div",{className:m.sidebarIndicator,style:{top:p.top,height:p.height,left:p.left,...p.width?{width:p.width}:{}}}),e("button",{ref:a=>{a&&g.current.set("general",a)},className:`${m.sidebarItem} ${l==="general"?m.active:""}`,onClick:()=>Y("general"),children:[e(q,{name:"cog"})," ",e("span",{children:"General"})]}),e("button",{ref:a=>{a&&g.current.set("sync",a)},className:`${m.sidebarItem} ${l==="sync"?m.active:""}`,onClick:()=>Y("sync"),children:[e(q,{name:"cloud"})," ",e("span",{children:"Synchronization"})]}),e("button",{ref:a=>{a&&g.current.set("watchlist",a)},className:`${m.sidebarItem} ${l==="watchlist"?m.active:""}`,onClick:()=>Y("watchlist"),children:[e(q,{name:"list"})," ",e("span",{children:"Watchlist"})]}),e("button",{ref:a=>{a&&g.current.set("database",a)},className:`${m.sidebarItem} ${l==="database"?m.active:""}`,onClick:()=>Y("database"),children:[e(q,{name:"database"})," ",e("span",{children:"Database"})]}),f&&e("button",{ref:a=>{a&&g.current.set("community",a)},className:`${m.sidebarItem} ${l==="community"?m.active:""}`,onClick:()=>Y("community"),children:[e(q,{name:"discord"})," ",e("span",{children:"Community"})]})]}),e("main",{className:m.mainContent,children:e("div",{className:m.tabContent,children:Ne()},l)})]})]})};export{un as default};
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{d as i,h as Fe,g as Ee,A as Be,q as De,u as e,S as d}from"./vendor-core-8R2fFbyN.js";import{u as Ue,b as $e,a as I}from"./vendor-query-BXRECSI6.js";import{z as r,W as en,h as nn,B as re}from"./index-D2RkUJIN.js";import{I as o}from"./vendor-icons-CDMuNDeL.js";function an(){const q=Ue(),[Y,w]=i(!1);return Fe(()=>{const s=new URLSearchParams(window.location.search),f=s.get("anilist");if(f){const m=s.get("user"),b=s.get("reason");window.history.replaceState(null,"",window.location.pathname),f==="success"?(r.success(m?`Connected to AniList as ${decodeURIComponent(m)}`:"Connected to AniList"),q.invalidateQueries({queryKey:["trackerStatus"]})):r.error(b?decodeURIComponent(b):"AniList authentication failed");return}const j=new URLSearchParams(window.location.hash.substring(1)),y=j.get("access_token"),S=s.get("code");if(!y&&!S){const m=j.get("error");m&&(r.error(`AniList error: ${m}`),window.history.replaceState(null,"",window.location.pathname));return}const g=window.location.origin+window.location.pathname;window.history.replaceState(null,"",window.location.pathname),w(!0),r("Connecting to AniList..."),fetch("/api/tracker/anilist/auth",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(y?{token:y}:{code:S,redirectUri:g})}).then(async m=>{const b=await m.json().catch(()=>({}));if(!m.ok)throw new Error(b.error||"Authentication failed");r.success(`Connected to AniList as ${b.user?.name??"AniList user"}`),q.invalidateQueries({queryKey:["trackerStatus"]})}).catch(m=>{r.error(m instanceof Error?m.message:"Failed to authenticate with AniList")}).finally(()=>{w(!1)})},[q]),Y}const tn="_header_j1lbh_1",sn="_fadeIn_j1lbh_1",rn="_pageTitle_j1lbh_6",on="_pageSubtitle_j1lbh_14",ln="_importCard_j1lbh_20",cn="_slideUpIn_j1lbh_1",dn="_cardHeader_j1lbh_37",mn="_uploadArea_j1lbh_55",pn="_fileInputWrapper_j1lbh_59",hn="_fileInput_j1lbh_59",un="_fileDisplay_j1lbh_87",gn="_fileIcon_j1lbh_95",yn="_fileName_j1lbh_100",bn="_browseButton_j1lbh_109",fn="_optionsArea_j1lbh_120",_n="_checkboxLabel_j1lbh_128",wn="_checkbox_j1lbh_128",Nn="_checkboxCustom_j1lbh_141",kn="_optionText_j1lbh_169",vn="_optionTitle_j1lbh_175",In="_optionDesc_j1lbh_181",Sn="_actions_j1lbh_187",xn="_importBtn_j1lbh_195",Cn="_cancelBtn_j1lbh_201",Mn="_statusMessage_j1lbh_215",Ln="_success_j1lbh_225",Tn="_error_j1lbh_231",jn="_progressSection_j1lbh_237",An="_progressBar_j1lbh_242",Pn="_progressFill_j1lbh_250",En="_progressInfo_j1lbh_257",Bn="_progressCount_j1lbh_264",Dn="_progressTitle_j1lbh_271",$n="_sourceBadge_j1lbh_289",On="_anilist_j1lbh_301",Fn="_kitsu_j1lbh_307",Un="_offline_j1lbh_313",qn="_skipped_j1lbh_319",Kn="_cardTitleRow_j1lbh_324",Rn="_anilistIcon_j1lbh_334",Qn="_malIcon_j1lbh_340",Gn="_badgeOnline_j1lbh_346",Hn="_badgeOffline_j1lbh_358",Wn="_connectedProfile_j1lbh_370",zn="_avatar_j1lbh_379",Xn="_connectedInfo_j1lbh_386",Jn="_connectedName_j1lbh_394",Yn="_connectedHint_j1lbh_400",Vn="_loginPrompt_j1lbh_405",Zn="_loginSection_j1lbh_413",ea="_fieldLabel_j1lbh_419",na="_helpText_j1lbh_438",aa="_loginText_j1lbh_455",ta="_primaryBtn_j1lbh_464",sa="_secondaryBtn_j1lbh_465",ia="_syncBtn_j1lbh_466",ra="_divider_j1lbh_518",oa="_actionSection_j1lbh_524",la="_inputGroup_j1lbh_538",ca="_input_j1lbh_538",da="_syncSummary_j1lbh_559",ma="_syncErrors_j1lbh_565",pa="_spin_j1lbh_569",ha="_radioGroup_j1lbh_598",ua="_radioLabel_j1lbh_604",ga="_syncHint_j1lbh_642",ya="_importLoading_j1lbh_649",n={header:tn,fadeIn:sn,pageTitle:rn,pageSubtitle:on,importCard:ln,slideUpIn:cn,cardHeader:dn,uploadArea:mn,fileInputWrapper:pn,fileInput:hn,fileDisplay:un,fileIcon:gn,fileName:yn,browseButton:bn,optionsArea:fn,checkboxLabel:_n,checkbox:wn,checkboxCustom:Nn,optionText:kn,optionTitle:vn,optionDesc:In,actions:Sn,importBtn:xn,cancelBtn:Cn,statusMessage:Mn,success:Ln,error:Tn,progressSection:jn,progressBar:An,progressFill:Pn,progressInfo:En,progressCount:Bn,progressTitle:Dn,sourceBadge:$n,anilist:On,kitsu:Fn,offline:Un,skipped:qn,cardTitleRow:Kn,anilistIcon:Rn,malIcon:Qn,badgeOnline:Gn,badgeOffline:Hn,connectedProfile:Wn,avatar:zn,connectedInfo:Xn,connectedName:Jn,connectedHint:Yn,loginPrompt:Vn,loginSection:Zn,fieldLabel:ea,helpText:na,loginText:aa,primaryBtn:ta,secondaryBtn:sa,syncBtn:ia,divider:ra,actionSection:oa,inputGroup:la,input:ca,syncSummary:da,syncErrors:ma,spin:pa,radioGroup:ha,radioLabel:ua,syncHint:ga,importLoading:ya},oe="tracker_anilist_client_id",Oe="49887".trim(),Na=()=>{const{setIsOpen:q}=en(),{contentType:Y}=nn(),w=Y==="manga",s=Ue();Ee.useEffect(()=>{document.title="Trackers - dango"},[]);const[f,j]=i(""),[y,S]=i("username"),[g,m]=i(null),[b,le]=i(""),[x,ce]=i(!1),qe=an(),{data:de,isLoading:me}=$e({queryKey:["trackerStatus"],queryFn:async()=>{const a=await fetch("/api/tracker/status");if(!a.ok)throw new Error("Failed to load tracker status");return a.json()}}),{data:C}=$e({queryKey:["anilistClientId"],queryFn:async()=>(await(await fetch(`/api/settings?key=${oe}`)).json()).value||""}),pe=Be(!1);Ee.useEffect(()=>{pe.current||C!==void 0&&(C&&le(C),pe.current=!0)},[C]);const N=de?.anilist?.connected??!1;Fe(()=>{N&&S("sync")},[N]);const K=de?.anilist?.user??null,V=!!Oe,Ke=(C||"").trim(),Re=async()=>{const a=b.trim(),t=a||Oe;if(!t){r.error("Enter your AniList client ID first");return}const u=async(v,se)=>{if(!(await fetch("/api/settings",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({key:v,value:se})})).ok)throw new Error(`Failed to save ${v}`)};try{a?a!==C&&await u(oe,a):V&&Ke&&await u(oe,""),s.invalidateQueries({queryKey:["anilistClientId"]})}catch(v){r.error(v.message);return}const l=window.location.origin+window.location.pathname,O=encodeURIComponent(l);window.location.href=`https://anilist.co/api/v2/oauth/authorize?client_id=${encodeURIComponent(t)}&response_type=token&state=${O}`},he=I({mutationFn:async()=>{const a=await fetch("/api/tracker/anilist/disconnect",{method:"POST"});if(!a.ok)throw new Error("Failed to disconnect");return a.json()},onSuccess:()=>{s.invalidateQueries({queryKey:["trackerStatus"]}),r.success("Disconnected from AniList")},onError:a=>r.error(a.message)}),R=I({mutationFn:async()=>{const a=await fetch("/api/tracker/sync",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({provider:"anilist"})}),t=await a.json();if(!a.ok)throw new Error(t.error||"Sync failed");return t.summary},onSuccess:a=>{s.invalidateQueries({queryKey:["watchlist"]}),s.invalidateQueries({queryKey:["allContinueWatching"]}),m(a),r.success(`Sync complete — pushed ${a.pushed}, pulled ${a.pulled}, merged ${a.merged}, unchanged ${a.unchanged}`,{duration:6e3})},onError:a=>r.error(a.message)}),[A,ue]=i(""),[Q,ge]=i("sync"),[Z,ye]=i("two-way"),[c,Qe]=i(null),G=I({mutationFn:async()=>{const a=await fetch("/api/tracker/manga/sync",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({direction:Z})}),t=await a.json();if(!a.ok)throw new Error(t.error||"Manga sync failed");return t.summary},onSuccess:a=>{s.invalidateQueries({queryKey:["manga-library"]}),s.invalidateQueries({queryKey:["manga-library-ids"]}),s.invalidateQueries({queryKey:["manga-continue-reading"]}),Qe(a);const t=(a.warnings?.length??0)>0?`, ${a.warnings.length} warning(s)`:"";r.success(`Manga sync complete — pushed ${a.pushed}, pulled ${a.pulled}, merged ${a.merged}, unchanged ${a.unchanged}${t}`,{duration:6e3})},onError:a=>r.error(a.message)}),M=I({mutationFn:async()=>{if(!A.trim())throw new Error("Please enter a username");const a=await fetch("/api/tracker/manga/import",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({username:A.trim(),erase:x})}),t=await a.json();if(!a.ok)throw new Error(t.error||"Import failed");return t.count},onSuccess:a=>{s.invalidateQueries({queryKey:["manga-library"]}),s.invalidateQueries({queryKey:["manga-library-ids"]}),s.invalidateQueries({queryKey:["manga-continue-reading"]}),r.success(`Imported ${a} manga entries from AniList`),ue("")},onError:a=>r.error(a.message)}),[P,be]=i(""),[H,fe]=i("username"),[ee,_e]=i(null),[W,we]=i(!1),[Ne,ne]=i(""),[ke,Ge]=i(""),_=I({mutationFn:async()=>{if(!P.trim())throw new Error("Please enter a MAL username");const a=await fetch("/api/tracker/manga/mal-import",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({username:P.trim(),erase:x})}),t=await a.json();if(!a.ok)throw new Error(t.error||"Import failed");return{imported:t.imported??0,skipped:t.skipped??0}},onSuccess:({imported:a,skipped:t})=>{s.invalidateQueries({queryKey:["manga-library"]}),s.invalidateQueries({queryKey:["manga-library-ids"]}),s.invalidateQueries({queryKey:["manga-continue-reading"]}),r.success(`Imported ${a} manga entries from MAL${t>0?`, skipped ${t}`:""}`),be("")},onError:a=>r.error(a.message)}),He=De(async()=>{const a=document.getElementById("malMangaFile");if(!a.files||a.files.length===0){ne("Please select a file first.");return}we(!0),ne(""),_e(null);try{const t=new FormData;t.append("xmlfile",a.files[0]),t.append("erase",String(x));const u=await fetch("/api/import/mal-xml-manga",{method:"POST",body:t}),l=await u.json().catch(()=>({}));if(!u.ok)throw new Error(l.error||"Import failed");_e({imported:l.imported??0,skipped:l.skipped??0}),s.invalidateQueries({queryKey:["manga-library"]}),s.invalidateQueries({queryKey:["manga-library-ids"]}),s.invalidateQueries({queryKey:["manga-continue-reading"]})}catch(t){ne(t.message)}finally{we(!1)}},[x,s]),L=I({mutationFn:async()=>{if(!f.trim())throw new Error("Please enter a username");const a=await fetch("/api/tracker/anilist/import",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({username:f.trim(),erase:k})}),t=await a.json();if(!a.ok)throw new Error(t.error||"Import failed");return t.count},onSuccess:a=>{s.invalidateQueries({queryKey:["watchlist"]}),s.invalidateQueries({queryKey:["allContinueWatching"]}),r.success(`Imported ${a} anime entries from AniList`),j("")},onError:a=>r.error(a.message)}),[E,ve]=i(""),h=I({mutationFn:async a=>{if(!E.trim())throw new Error("Please enter a MAL username");const t=await fetch("/api/tracker/mal/import",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({username:E.trim(),erase:a.erase,useOfflineDb:a.useOfflineDb,skipFallback:a.skipFallback})}),u=await t.json();if(!t.ok)throw new Error(u.error||"Import failed");return u.count},onSuccess:a=>{s.invalidateQueries({queryKey:["watchlist"]}),s.invalidateQueries({queryKey:["allContinueWatching"]}),r.success(`Imported ${a} anime entries from MAL`),ve("")},onError:a=>r.error(a.message)}),[k,Ie]=i(!1),[B,We]=i(!0),[D,ze]=i(!1),[Se,xe]=i(""),[T,Ce]=i(!1),[p,Me]=i(null),[ae,Le]=i(null),[Te,z]=i(""),te=Be(null),Xe=a=>{a.target.files&&a.target.files.length>0?xe(a.target.files[0].name):xe("")},Je=De(async()=>{const a=document.getElementById("malFile");if(!a.files||a.files.length===0){z("Please select a file first.");return}const t=a.files[0];Ce(!0),Me(null),Le(null),z("");const u=new AbortController;te.current=u;try{const l=new FormData;l.append("xmlfile",t),l.append("erase",String(k)),l.append("useOfflineDb",String(B)),l.append("skipFallback",String(D));const O=await fetch("/api/import/mal-xml",{method:"POST",body:l,signal:u.signal});if(!O.ok){const ie=await O.text();let F="Failed to import";try{F=JSON.parse(ie).error||F}catch{}throw new Error(F)}const v=O.body?.getReader();if(!v)throw new Error("No response stream");const se=new TextDecoder;let X="",J="";for(;;){const{done:ie,value:F}=await v.read();if(ie)break;X+=se.decode(F,{stream:!0});const Ae=X.split(`
|
|
2
|
-
`);X=Ae.pop()||"";for(const U of Ae)if(U.startsWith("event: "))J=U.slice(7).trim();else if(U.startsWith("data: ")){const Ze=U.slice(6);try{const Pe=JSON.parse(Ze);J==="progress"?Me(Pe):J==="complete"&&Le(Pe)}catch{}}else U===""&&(J="")}s.invalidateQueries({queryKey:["watchlist"]})}catch(l){l instanceof DOMException&&l.name==="AbortError"?z("Import cancelled."):z(l.message)}finally{Ce(!1),te.current=null}},[k,B,D,s]),Ye=()=>{te.current?.abort(),fetch("/api/import/mal-xml/cancel",{method:"POST"}).catch(()=>{})},Ve=p?Math.round(p.current/p.total*100):0,[$,je]=i("username");return e("div",{className:"page-container",children:[e("div",{className:n.header,children:[e("h1",{className:n.pageTitle,children:"Trackers"}),e("p",{className:n.pageSubtitle,children:"Import & sync your anime watchlists and manga reading lists with tracking services"})]}),e("div",{style:{marginBottom:"16px",padding:"10px 14px",borderRadius:"8px",fontSize:"0.85rem",background:"color-mix(in srgb, var(--color-warning) 8%, transparent)",border:"1px solid color-mix(in srgb, var(--color-warning) 25%, transparent)",color:"var(--text-secondary)"},children:"⚠ Syncing writes directly to your AniList entries, and dango may not always handle anime/manga lists correctly — always keep a backup of your lists before syncing."}),!w&&e(d,{children:e("div",{className:n.importCard,children:[e("div",{className:n.cardHeader,children:e("div",{className:n.cardTitleRow,children:[e(o,{name:"anilist",className:n.anilistIcon}),e("h3",{children:"AniList"}),me?null:N?e("span",{className:n.badgeOnline,children:"Connected"}):e("span",{className:n.badgeOffline,children:"Not connected"})]})}),N&&K?e("div",{className:n.connectedProfile,children:[K.avatar&&e("img",{src:K.avatar,alt:"Avatar",className:n.avatar,loading:"lazy",decoding:"async"}),e("div",{className:n.connectedInfo,children:[e("span",{className:n.connectedName,children:K.name}),e("span",{className:n.connectedHint,children:"2-way sync is ready"})]}),e("button",{className:n.secondaryBtn,onClick:()=>he.mutate(),disabled:he.isPending,children:[e(o,{name:"sign-out-alt"})," Disconnect"]})]}):e("div",{className:n.loginSection,children:[e("p",{className:n.loginText,children:["Log in with AniList to enable bidirectional progress & status synchronization.",qe&&e("span",{style:{marginLeft:8,color:"var(--accent)"},children:"Finishing login…"})]}),e("div",{className:n.fieldLabel,children:["AniList client ID — defaults to Dango's app"," ",e("a",{href:"https://anilist.co/settings/developer",target:"_blank",rel:"noopener noreferrer",children:["create your own app ",e(o,{name:"external-link-alt",size:9})]})]}),e("div",{className:n.inputGroup,children:[e("input",{type:"text",placeholder:"Client ID",value:b,onChange:a=>le(a.target.value),className:n.input}),e("button",{className:n.primaryBtn,onClick:Re,disabled:!(b.trim()||V),children:[e(o,{name:"external-link-alt"})," Connect"]})]}),e("p",{className:n.helpText,children:V?e(d,{children:["By default uses Dango's client. To use your own AniList app: create it at"," ",e("a",{href:"https://anilist.co/settings/developer",target:"_blank",rel:"noopener noreferrer",children:"anilist.co/settings/developer"}),", set its ",e("strong",{children:"Redirect URL"})," to"," ",e("code",{children:window.location.port==="5173"?`${window.location.protocol}//${window.location.hostname}:3000/api/tracker/anilist/callback`:`${window.location.origin}/api/tracker/anilist/callback`}),", then paste only the ",e("strong",{children:"Client ID"})," above (leave empty to use Dango's)."]}):e(d,{children:["No shipped client in this build — create your own app at"," ",e("a",{href:"https://anilist.co/settings/developer",target:"_blank",rel:"noopener noreferrer",children:"anilist.co/settings/developer"}),", set ",e("strong",{children:"Redirect URL"})," to"," ",e("code",{children:window.location.port==="5173"?`${window.location.protocol}//${window.location.hostname}:3000/api/tracker/anilist/callback`:`${window.location.origin}/api/tracker/anilist/callback`}),", then paste the ",e("strong",{children:"Client ID"})," above."]})})]}),N&&e(d,{children:[e("hr",{className:n.divider}),e("div",{className:n.radioGroup,children:[e("label",{className:n.radioLabel,children:[e("input",{type:"radio",name:"anilistMode",value:"username",checked:y==="username",onChange:()=>S("username")}),e("span",{children:"Quick Import"})]}),e("label",{className:n.radioLabel,children:[e("input",{type:"radio",name:"anilistMode",value:"sync",checked:y==="sync",onChange:()=>S("sync")}),e("span",{children:"Two-way Sync"})]})]}),y==="username"?e("div",{className:n.inputGroup,children:e("input",{type:"text",placeholder:"Enter AniList username",value:f,onChange:a=>j(a.target.value),className:n.input,disabled:L.isPending,onKeyDown:a=>{a.key==="Enter"&&f.trim()&&L.mutate()}})}):e("p",{className:n.syncHint,children:"Merges progress non-destructively: pushes shows and episodes you watched on dango to AniList, and pulls anything you updated on AniList into dango."}),y==="username"&&e("div",{className:n.optionsArea,children:e("label",{className:n.checkboxLabel,children:[e("input",{type:"checkbox",checked:k,onChange:a=>Ie(a.target.checked),className:n.checkbox,disabled:L.isPending}),e("span",{className:n.checkboxCustom}),e("div",{className:n.optionText,children:[e("span",{className:n.optionTitle,children:"Erase current watchlist"}),e("span",{className:n.optionDesc,children:"Delete existing watchlist before importing."})]})]})}),y==="username"?e("button",{className:n.syncBtn,onClick:()=>L.mutate(),disabled:!f.trim()||L.isPending,children:[e(o,{name:"download"}),L.isPending?"Importing...":"Start Import"]}):e(d,{children:[e("button",{className:n.syncBtn,onClick:()=>R.mutate(),disabled:R.isPending,children:[e(o,{name:"sync-alt",className:R.isPending?n.spin:""}),R.isPending?"Syncing...":"Start Sync"]}),g&&e("div",{className:n.syncSummary,children:["Pushed: ",g.pushed," · Pulled: ",g.pulled," · Merged:"," ",g.merged," · Unchanged: ",g.unchanged,g.errors.length>0&&e("span",{className:n.syncErrors,children:[" ","· ",g.errors.length," error",g.errors.length>1?"s":""]})]})]}),e("p",{style:{fontSize:"0.75rem",color:"var(--text-tertiary)",marginTop:"8px"},children:["AniList ",e("code",{children:"Rewatching (REPEATING)"})," is not tracked — entries with this status are ignored."]})]})]})}),w&&e(d,{children:e("div",{className:n.importCard,children:[e("div",{className:n.cardHeader,children:e("div",{className:n.cardTitleRow,children:[e(o,{name:"anilist",className:n.anilistIcon}),e("h3",{children:"AniList Manga"}),me?null:N?e("span",{className:n.badgeOnline,children:"Connected"}):e("span",{className:n.badgeOffline,children:"Not connected"})]})}),N?e(d,{children:[e("div",{className:n.radioGroup,children:[e("label",{className:n.radioLabel,children:[e("input",{type:"radio",name:"mangaMode",value:"username",checked:Q==="username",onChange:()=>ge("username")}),e("span",{children:"Quick Import"})]}),e("label",{className:n.radioLabel,children:[e("input",{type:"radio",name:"mangaMode",value:"sync",checked:Q==="sync",onChange:()=>ge("sync")}),e("span",{children:"Two-way Sync"})]})]}),Q==="username"?e(d,{children:[e("div",{className:n.inputGroup,children:e("input",{type:"text",placeholder:"Enter AniList username",value:A,onChange:a=>ue(a.target.value),className:n.input,disabled:M.isPending,onKeyDown:a=>{a.key==="Enter"&&A.trim()&&M.mutate()}})}),e("div",{className:n.optionsArea,children:e("label",{className:n.checkboxLabel,children:[e("input",{type:"checkbox",checked:x,onChange:a=>ce(a.target.checked),className:n.checkbox,disabled:M.isPending}),e("span",{className:n.checkboxCustom}),e("div",{className:n.optionText,children:[e("span",{className:n.optionTitle,children:"Replace AniList-linked manga"}),e("span",{className:n.optionDesc,children:"Clears previously imported (anilist:) entries before importing. Your provider bookmarks are never touched."})]})]})})]}):e(d,{children:[e("p",{className:n.syncHint,children:"Merges chapter progress non-destructively: pushes manga and chapters you read on dango to AniList, and pulls anything you updated on AniList into dango. Local titles are linked only on exact title matches, and bookmarks with no chapters read are never pushed."}),e("div",{className:n.radioGroup,children:[e("label",{className:n.radioLabel,children:[e("input",{type:"radio",name:"mangaDirection",value:"two-way",checked:Z==="two-way",onChange:()=>ye("two-way")}),e("span",{children:"Two-way"})]}),e("label",{className:n.radioLabel,children:[e("input",{type:"radio",name:"mangaDirection",value:"pull-only",checked:Z==="pull-only",onChange:()=>ye("pull-only")}),e("span",{children:"Pull only (never writes to AniList)"})]})]})]}),Q==="username"?e("button",{className:n.syncBtn,onClick:()=>M.mutate(),disabled:!A.trim()||M.isPending,children:[e(o,{name:"download"}),M.isPending?"Importing...":"Start Import"]}):e(d,{children:[e("button",{className:n.syncBtn,onClick:()=>G.mutate(),disabled:G.isPending,children:[e(o,{name:"sync-alt",className:G.isPending?n.spin:""}),G.isPending?"Syncing...":"Start Manga Sync"]}),c&&e(d,{children:[e("div",{className:n.syncSummary,children:["Pushed: ",c.pushed," · Pulled: ",c.pulled," · Merged: ",c.merged," · Unchanged:"," ",c.unchanged,(c.skipped??0)>0&&e("span",{children:[" · Skipped: ",c.skipped]}),c.errors.length>0&&e("span",{className:n.syncErrors,children:[" ","· ",c.errors.length," error",c.errors.length>1?"s":""]})]}),(c.warnings?.length??0)>0&&e("div",{className:n.syncErrors,style:{marginTop:"8px"},children:[(c.warnings??[]).slice(0,5).map((a,t)=>e("div",{children:["⚠ ",a]},t)),(c.warnings?.length??0)>5&&e("div",{children:["+",(c.warnings?.length??0)-5," more warning(s)"]})]})]})]}),e("p",{style:{fontSize:"0.75rem",color:"var(--text-tertiary)",marginTop:"8px"},children:["AniList ",e("code",{children:"Rereading (REPEATING)"})," is not tracked — entries with this status are ignored."]})]}):e("p",{className:n.loginText,children:"Connect your AniList account above to sync your manga reading list."})]})}),!w&&e(d,{children:e("div",{className:n.importCard,children:[e("div",{className:n.cardHeader,children:e("div",{className:n.cardTitleRow,children:[e(o,{name:"myanimelist",className:n.malIcon}),e("h3",{children:"MyAnimeList"})]})}),e("div",{className:n.radioGroup,children:[e("label",{className:n.radioLabel,children:[e("input",{type:"radio",name:"malMode",value:"username",checked:$==="username",onChange:()=>je("username")}),e("span",{children:"Username"})]}),e("label",{className:n.radioLabel,children:[e("input",{type:"radio",name:"malMode",value:"xml",checked:$==="xml",onChange:()=>je("xml")}),e("span",{children:"XML File"})]})]}),$==="username"?e(d,{children:[e("div",{className:n.inputGroup,children:e("input",{type:"text",placeholder:"Enter MAL username",value:E,onChange:a=>ve(a.target.value),className:n.input,disabled:h.isPending,onKeyDown:a=>{a.key==="Enter"&&E.trim()&&h.mutate({erase:k,useOfflineDb:B,skipFallback:D})}})}),h.isPending&&e("div",{className:n.importLoading,children:[e(o,{name:"sync-alt",className:n.spin}),e("span",{children:"Importing from MAL..."})]})]}):e("div",{className:n.uploadArea,children:e("div",{className:n.fileInputWrapper,children:[e("input",{type:"file",id:"malFile",accept:".xml,application/xml",className:n.fileInput,onChange:Xe,disabled:T}),e("div",{className:n.fileDisplay,children:[e(o,{name:"file-alt",className:n.fileIcon}),e("span",{className:n.fileName,children:Se||"Choose XML file..."})]}),e("label",{htmlFor:"malFile",className:n.browseButton,children:"Browse"})]})}),e("div",{className:n.optionsArea,children:[e("label",{className:n.checkboxLabel,children:[e("input",{type:"checkbox",checked:k,onChange:a=>Ie(a.target.checked),className:n.checkbox,disabled:T||h.isPending}),e("span",{className:n.checkboxCustom}),e("div",{className:n.optionText,children:[e("span",{className:n.optionTitle,children:"Erase current watchlist"}),e("span",{className:n.optionDesc,children:"Delete existing watchlist before importing."})]})]}),e("label",{className:n.checkboxLabel,children:[e("input",{type:"checkbox",checked:B,onChange:a=>We(a.target.checked),className:n.checkbox,disabled:T||h.isPending}),e("span",{className:n.checkboxCustom}),e("div",{className:n.optionText,children:[e("span",{className:n.optionTitle,children:"Use offline database (fast)"}),e("span",{className:n.optionDesc,children:"Match by MAL ID locally first — instant and avoids AniList rate limits."})]})]}),e("label",{className:n.checkboxLabel,children:[e("input",{type:"checkbox",checked:D,onChange:a=>ze(a.target.checked),className:n.checkbox,disabled:T||h.isPending}),e("span",{className:n.checkboxCustom}),e("div",{className:n.optionText,children:[e("span",{className:n.optionTitle,children:"Skip online fallback"}),e("span",{className:n.optionDesc,children:"Only import offline matches. Unmatched titles are skipped without API calls."})]})]})]}),$==="username"?e("button",{className:n.syncBtn,onClick:()=>h.mutate({erase:k,useOfflineDb:B,skipFallback:D}),disabled:!E.trim()||h.isPending,children:[e(o,{name:"download"}),h.isPending?"Importing...":"Start Import"]}):e("div",{className:n.actions,children:T?e(re,{onClick:Ye,className:n.cancelBtn,children:"Cancel Import"}):e(re,{onClick:Je,className:n.importBtn,disabled:!Se,children:[e(o,{name:"upload"})," Start Import"]})}),$==="username"&&h.isError&&e("div",{className:`${n.statusMessage} ${n.error}`,children:h.error.message}),T&&p&&e("div",{className:n.progressSection,children:[e("div",{className:n.progressBar,children:e("div",{className:n.progressFill,style:{width:`${Ve}%`}})}),e("div",{className:n.progressInfo,children:[e("span",{className:n.progressCount,children:[p.current," / ",p.total]}),e("span",{className:n.progressTitle,children:p.found?e(d,{children:[p.matchedTitle,e("span",{className:`${n.sourceBadge} ${n[p.source||"anilist"]}`,children:p.source==="kitsu"?"Kitsu":p.source==="offline"?"Offline DB":"AniList"})]}):e("span",{className:n.skipped,children:["Skipped: ",p.title]})})]})]}),ae&&e("div",{className:`${n.statusMessage} ${n.success}`,children:["Import complete! Imported: ",ae.imported,", Skipped: ",ae.skipped,"."]}),Te&&e("div",{className:`${n.statusMessage} ${n.error}`,children:Te})]})}),w&&e("div",{className:n.importCard,children:[e("div",{className:n.cardHeader,children:e("div",{className:n.cardTitleRow,children:[e(o,{name:"myanimelist",className:n.malIcon}),e("h3",{children:"MyAnimeList Manga"})]})}),e("div",{className:n.radioGroup,children:[e("label",{className:n.radioLabel,children:[e("input",{type:"radio",name:"malMangaMode",value:"username",checked:H==="username",onChange:()=>fe("username")}),e("span",{children:"Username"})]}),e("label",{className:n.radioLabel,children:[e("input",{type:"radio",name:"malMangaMode",value:"xml",checked:H==="xml",onChange:()=>fe("xml")}),e("span",{children:"XML File"})]})]}),H==="username"?e("div",{className:n.inputGroup,children:e("input",{type:"text",placeholder:"Enter MAL username",value:P,onChange:a=>be(a.target.value),className:n.input,disabled:_.isPending,onKeyDown:a=>{a.key==="Enter"&&P.trim()&&_.mutate()}})}):e("div",{className:n.uploadArea,children:e("div",{className:n.fileInputWrapper,children:[e("input",{type:"file",id:"malMangaFile",accept:".xml,application/xml",className:n.fileInput,onChange:a=>Ge(a.target.files?.[0]?.name??""),disabled:W}),e("div",{className:n.fileDisplay,children:[e(o,{name:"file-alt",className:n.fileIcon}),e("span",{className:n.fileName,children:ke||"Choose XML file..."})]}),e("label",{htmlFor:"malMangaFile",className:n.browseButton,children:"Browse"})]})}),e("div",{className:n.optionsArea,children:e("label",{className:n.checkboxLabel,children:[e("input",{type:"checkbox",checked:x,onChange:a=>ce(a.target.checked),className:n.checkbox,disabled:_.isPending||W}),e("span",{className:n.checkboxCustom}),e("div",{className:n.optionText,children:[e("span",{className:n.optionTitle,children:"Replace AniList-linked manga"}),e("span",{className:n.optionDesc,children:"Clears previously imported (anilist:) entries before importing. Your provider bookmarks are never touched."})]})]})}),H==="username"?e("button",{className:n.syncBtn,onClick:()=>_.mutate(),disabled:!P.trim()||_.isPending,children:[e(o,{name:"download"}),_.isPending?"Importing...":"Start Import"]}):e("div",{className:n.actions,children:e(re,{onClick:He,className:n.importBtn,disabled:W||!ke,children:[e(o,{name:"upload"})," ",W?"Importing...":"Start Import"]})}),_.isError&&e("div",{className:`${n.statusMessage} ${n.error}`,children:_.error.message}),Ne&&e("div",{className:`${n.statusMessage} ${n.error}`,children:Ne}),ee&&e("div",{className:`${n.statusMessage} ${n.success}`,children:["Import complete! Imported: ",ee.imported,", Skipped:"," ",ee.skipped,"."]}),e("p",{style:{fontSize:"0.75rem",color:"var(--text-tertiary)",marginTop:"8px"},children:"MAL manga is matched to AniList by MAL ID with a title fallback. Unmatched titles are skipped. Imports land as AniList-linked entries alongside your provider bookmarks."})]})]})};export{Na as default};
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import{d as v,A as T,h as y,u as t,S as Ve,n as Fr,o as qr,a as zr,T as Se,q as S}from"./vendor-core-8R2fFbyN.js";import{u as Wr}from"./vendor-query-BXRECSI6.js";import{e as An,z as Ur,M as cn,B as _e}from"./index-D2RkUJIN.js";import{I as $}from"./vendor-icons-CDMuNDeL.js";import{M as Hr,t as ur,f as Rn,d as jr,C as Kr,S as Yr,U as Qr,a as Gr,p as Dn,u as Jr,b as Xr,c as Zr,l as es,q as ts,i as Pn,v as ns,w as rs,x as cr,s as Ln,e as ss,g as as,h as os,r as is,j as nt,k as ls,E as cs,A as ds,P as us,m as be,o as ms}from"./AvSyncCalibrator-BwCtRW0P.js";import{O as un,S as hs,a as ps,A as fs}from"./OptionListMenu--Owe9Jle.js";import{u as gs}from"./useMatureConsent-CT7-KfzG.js";import{u as vs}from"./useProviders-DDRxa8vl.js";import{d as bs,g as ys,h as Ss,f as _s,e as Cs,j as Ts,k as ks}from"./useTvLibrary-_Pqm8re1.js";import{l as Es,c as Ns}from"./hls-BO_L1WoN.js";import{u as ws}from"./useIsMobile-Dm_b6Ob3.js";import{S as xs}from"./SynopsisText-BcpwT5iw.js";const Is="_fullscreenFlat_1deq4_1",Bs="_timeDisplay_1deq4_15",Ms="_bottomControls_1deq4_19",As="_leftControls_1deq4_23",Ps="_rightControls_1deq4_24",Ls="_controlBtn_1deq4_28",$s="_controlsOverlay_1deq4_47",Rs="_hidden_1deq4_67",Ds="_topControls_1deq4_73",Os="_backBtn_1deq4_93",Vs="_videoTitleInfo_1deq4_113",Fs="_animeTitle_1deq4_119",qs="_episodeLabel_1deq4_127",zs="_speedBoostBadge_1deq4_134",Ws="_progressBarContainer_1deq4_180",Us="_progressBar_1deq4_180",Hs="_thumb_1deq4_199",js="_bufferedBar_1deq4_217",Ks="_watchedBar_1deq4_227",Ys="_scrubbing_1deq4_252",Qs="_timeBubble_1deq4_256",Gs="_bottomControlsRow_1deq4_270",Js="_nextEpisodeBtn_1deq4_293",Xs="_active_1deq4_346",Zs="_settingsPanel_1deq4_421",ea="_settingsHeader_1deq4_440",ta="_settingsBackBtn_1deq4_448",na="_settingsTitle_1deq4_466",ra="_settingsContent_1deq4_477",sa="_currentValue_1deq4_486",aa="_menuItem_1deq4_501",oa="_menuNote_1deq4_534",a={fullscreenFlat:Is,timeDisplay:Bs,bottomControls:Ms,leftControls:As,rightControls:Ps,controlBtn:Ls,controlsOverlay:$s,hidden:Rs,topControls:Ds,backBtn:Os,videoTitleInfo:Vs,animeTitle:Fs,episodeLabel:qs,speedBoostBadge:zs,progressBarContainer:Ws,progressBar:Us,thumb:Hs,bufferedBar:js,watchedBar:Ks,scrubbing:Ys,timeBubble:Qs,bottomControlsRow:Gs,nextEpisodeBtn:Js,active:Xs,settingsPanel:Zs,settingsHeader:ea,settingsBackBtn:ta,settingsTitle:na,settingsContent:ra,currentValue:sa,menuItem:aa,menuNote:oa},dn=-1e4,$n=1e4,dr=500,ia=({classes:u,delayMs:w,onDelayChange:Z})=>{const[U,q]=v(null),V=U??w,r=T(Z);r.current=Z;const B=T(w);B.current=w;const se=T(null);se.current=U,y(()=>{q(null)},[w]),y(()=>()=>{const H=se.current;H!==null&&H!==B.current&&r.current(H)},[]);const me=H=>{B.current=H,se.current=null,r.current(H),q(null)},l=H=>{const ne=se.current??B.current,Fe=Math.max(dn,Math.min($n,ne+H));me(Fe)};return t(Ve,{children:[t("div",{style:{display:"flex",gap:6,padding:"0.5rem 0.75rem 0.25rem"},children:[t("button",{type:"button",className:u.item,onClick:()=>l(-dr),"aria-label":"Show subtitles 0.5 seconds earlier",style:{flex:1,justifyContent:"center"},children:t("span",{children:"-0.5s"})}),t("button",{type:"button",className:u.item,onClick:()=>l(dr),"aria-label":"Show subtitles 0.5 seconds later",style:{flex:1,justifyContent:"center"},children:t("span",{children:"+0.5s"})}),t("button",{type:"button",className:u.item,onClick:()=>me(0),disabled:V===0,"aria-label":"Reset subtitle timing",style:{flex:1,justifyContent:"center"},children:t("span",{children:"Reset"})})]}),t(Hr,{label:"Subtitle timing",display:ur(V),min:dn,max:$n,step:100,value:V,percent:(V-dn)/($n-dn)*100,onChange:H=>{const ne=Math.round(H);se.current=ne,q(ne)},onCommit:()=>{const H=se.current;H!==null&&me(H)}}),t("div",{className:u.note,children:"Positive delays subtitles (show later). Negative shows them earlier. 0.0s is default."})]})},la=({classes:u,tracks:w,selected:Z,onChange:U})=>t(un,{classes:u,options:w.map((q,V)=>({key:String(V),label:q.label||q.language,selected:V===Z})),onSelect:q=>U(Number(q))}),ca=({player:u,title:w,episodeLabel:Z,audioTracks:U,selectedAudioTrack:q,onAudioTrackChange:V,subtitles:r,selectedSubtitle:B,onSubtitleChange:se,streams:me,qualityIdx:l,onQualityChange:H,onBack:ne,children:Fe,movyServers:g=[],selectedMovyServer:ye="atlanta",onMovyServerSelect:R,isMovySource:$t=!1,videoDelayEnabled:ae=!1,onVideoDelayToggle:qe,videoDelayMs:yt=0,onVideoDelayChange:St,onCalibrateAvSync:Rt,subtitleDelayMs:Dt=0,onSubtitleDelayChange:mn,showNextEpisodeButton:Ot=!1,onNextEpisode:oe})=>{const{state:M,refs:Vt,actions:P}=u,j=Vt.videoRef,{subtitleFontSize:he,subtitlePosition:K,subtitleBgOpacity:rt,subtitleBgColor:st,subtitleTextColor:Ce,subtitleEdge:ze,subtitleBold:pe}=M,[x,D]=v(null),Ie=T(null),[Ae,hn]=v(0);y(()=>{const o=j.current;if(!o)return;const c=()=>hn(o.clientHeight||0);c();const Be=new ResizeObserver(c);return Be.observe(o),()=>Be.disconnect()},[j,me]);const fe=(o,c)=>{try{localStorage.setItem(o,String(c))}catch{}},at=(o,c)=>{switch(o){case"fontSize":typeof c=="number"&&(P.setSubtitleFontSize(c),fe("subtitleFontSize",c));break;case"position":typeof c=="number"&&(P.setSubtitlePosition(c),fe("subtitlePosition",c));break;case"bgOpacity":typeof c=="number"&&(P.setSubtitleBgOpacity(c),fe("subtitleBgOpacity",c));break;case"bgColor":typeof c=="string"&&(P.setSubtitleBgColor(c),fe("subtitleBgColor",c));break;case"textColor":typeof c=="string"&&(P.setSubtitleTextColor(c),fe("subtitleTextColor",c));break;case"edge":(c==="shadow"||c==="outline"||c==="none")&&(P.setSubtitleEdge(c),fe("subtitleEdge",c));break;case"bold":typeof c=="boolean"&&(P.setSubtitleBold(c),fe("subtitleBold",c));break}};y(()=>{const o="tv-player-subtitle-style";let c=document.getElementById(o);c||(c=document.createElement("style"),c.id=o,document.head.appendChild(c));const Be={fontSize:he,position:K,bgOpacity:rt,bgColor:st,textColor:Ce,edge:ze,bold:pe},L=j.current,kt=Rn(he,Ae||L?.clientHeight||720);if(c.textContent=jr({...Be,fontSize:kt}),L){const Et=()=>{const E=Number(K);return isNaN(E)?0:Math.max(0,Math.min(100,E))},Te=(E,Q)=>{try{const A=E;A.snapToLines=!1,A.line=Q}catch{}},vn=()=>{const E=kt*16,Q=L.videoHeight||L.clientHeight||720,A=L.videoWidth||L.clientWidth||1280;return{step:E*1.3/Q*100,chars:Math.max(20,Math.floor(A/(E*.55)))}},ct=E=>{const Q=Math.max(0,Math.min(100,100-Et())),A=Array.from(E.activeCues??[]);if(A.length<=1){A.forEach(Le=>Te(Le,Q));return}const{step:ut,chars:wt}=vn();let je=Q;for(let Le=A.length-1;Le>=0;Le--){const m=String(A[Le].text??"").replace(/<[^>]*>/g,"").split(`
|
|
2
|
-
`).reduce((O,Yt)=>O+Math.max(1,Math.ceil(Yt.length/wt)),0),Ke=je-m*ut;Te(A[Le],Math.max(0,Ke)),je=Ke-ut*.4}},Nt=()=>{const E=Math.max(0,Math.min(100,100-Et()));Array.from(L.textTracks).forEach(Q=>{Q.cues&&(Array.from(Q.cues).forEach(A=>Te(A,E)),Q.mode==="showing"&&ct(Q))})};Nt();const dt=E=>{const Q=E.target;Q.mode==="showing"&&ct(Q)},Ht=()=>{Array.from(L.textTracks).forEach(E=>{E.removeEventListener("cuechange",dt),E.addEventListener("cuechange",dt)}),Nt()};Array.from(L.textTracks).forEach(E=>{E.addEventListener("cuechange",dt)}),L.textTracks.addEventListener("addtrack",Ht);const jt=Array.from(L.querySelectorAll("track")),Kt=()=>Nt();return jt.forEach(E=>E.addEventListener("load",Kt)),()=>{Array.from(L.textTracks).forEach(E=>{E.removeEventListener("cuechange",dt)}),L.textTracks.removeEventListener("addtrack",Ht),jt.forEach(E=>E.removeEventListener("load",Kt))}}},[he,K,rt,st,Ce,ze,pe,B,r,j,Ae]);const ot=o=>{const c=j.current;!c||isNaN(M.duration)||M.duration===0||(c.currentTime=o*M.duration)},pn=()=>{j.current&&(P.setIsScrubbing(!0),P.wasPlayingBeforeScrub.current=!j.current.paused,j.current.pause())},_t=o=>{const c=j.current;c&&M.duration&&(c.currentTime=o*M.duration)},We=()=>{P.setIsScrubbing(!1),P.wasPlayingBeforeScrub.current&&j.current?.play().catch(()=>{})},ee=o=>{const c=j.current;c&&(c.volume=o,c.muted=o===0,localStorage.setItem("playerVolume",o.toString()))},Ft=()=>{P.toggleMute()},Pe=r.length>0,k=B>=0,X=()=>{D("main"),P.setShowControls(!0)},qt=()=>{if(k){se(-1);return}let o=null;try{o=localStorage.getItem("tvLastSubtitle")}catch{}se(Dn(r,{lastKey:o,enabled:!0}))},Ct=()=>{D(null)},Tt=()=>t(Ve,{children:[me.length>1&&t("button",{className:a.menuItem,onClick:()=>D("quality"),children:[t("span",{children:"Quality"}),t("span",{className:a.currentValue,children:me[l]?.quality||"Auto"})]}),$t&&g.length>0&&t("button",{className:a.menuItem,onClick:()=>D("server"),children:[t("span",{style:{display:"flex",alignItems:"center",gap:6},children:[t($,{name:"server",size:12})," Movy Server"]}),t("span",{className:a.currentValue,style:{textTransform:"capitalize"},children:ye})]}),Pe&&t("button",{className:a.menuItem,onClick:()=>D("subtitles"),children:[t("span",{children:"Subtitles"}),t("span",{className:a.currentValue,children:k?r[B]?.label||r[B]?.language:"Off"})]}),Pe&&t("button",{className:a.menuItem,onClick:()=>D("subtitle-style"),children:[t("span",{children:"Subtitle Style"}),t("span",{className:a.currentValue,children:[he.toFixed(1),"x · ",K]})]}),Pe&&t("button",{className:a.menuItem,onClick:()=>D("subtitle-timing"),children:[t("span",{children:"Subtitle Timing"}),t("span",{className:a.currentValue,children:ur(Dt)})]}),U.length>0&&t("button",{className:a.menuItem,onClick:()=>D("audio"),children:[t("span",{children:"Audio Track"}),t("span",{className:a.currentValue,children:U[q]?.label||U[q]?.language})]}),t("button",{className:a.menuItem,onClick:()=>D("av-sync"),children:[t("span",{children:"A/V Sync"}),t("span",{className:a.currentValue,children:ae?`${yt}ms`:"Off"})]})]}),fn=()=>t(un,{classes:{item:a.menuItem,active:a.active},options:me.map((o,c)=>({key:String(c),label:o.quality,selected:c===l})),onSelect:o=>H(Number(o))}),_=()=>t(un,{classes:{item:a.menuItem,active:a.active},options:[{key:"off",label:"Off",selected:!k},...r.map((o,c)=>({key:String(c),label:o.label||o.language,selected:c===B}))],onSelect:o=>se(o==="off"?-1:Number(o))}),d=()=>t(ps,{classes:{item:a.menuItem,active:a.active},values:{fontSize:he,position:K,bgOpacity:rt,bgColor:st,textColor:Ce,edge:ze,bold:pe},onChange:at}),ie=()=>t(ia,{classes:{item:a.menuItem,active:a.active,note:a.menuNote},delayMs:Dt,onDelayChange:o=>mn?.(o)}),le=()=>t(la,{classes:{item:a.menuItem,active:a.active},tracks:U,selected:q,onChange:V}),zt=()=>t(un,{classes:{item:a.menuItem,active:a.active},options:g.map(o=>({key:o,label:t("span",{style:{textTransform:"capitalize"},children:o}),selected:ye===o})),onSelect:o=>R?.(o)}),Wt=()=>t(fs,{classes:{item:a.menuItem,active:a.active,note:a.menuNote},enabled:ae,delayMs:yt,onToggle:o=>qe?.(o),onDelayChange:o=>St?.(o),onCalibrate:()=>{D(null),Rt?.()}}),Ue=M.isMuted?t($,{name:"volume-mute"}):M.volume<.5?t($,{name:"volume-down"}):t($,{name:"volume-up"}),Ut=t("div",{className:`${a.controlsOverlay} ${!M.showControls&&!x?a.hidden:""}`,"data-speed-boost-ignore":"true",style:{pointerEvents:"none",background:"none"},children:t("div",{className:a.topControls,onClick:o=>o.stopPropagation(),children:[t("button",{className:a.backBtn,onClick:ne,title:"Back","aria-label":"Back",children:t($,{name:"chevron-left"})}),t("div",{className:a.videoTitleInfo,children:[t("span",{className:a.animeTitle,children:w}),Z&&t("span",{className:a.episodeLabel,children:Z})]})]})}),He=t(Kr,{isPlaying:M.isPlaying,onTogglePlay:P.togglePlay,onSkipBack:()=>P.seek(-10),onSkipForward:()=>P.seek(10)}),it=t("div",{className:`${a.controlsOverlay} ${!M.showControls&&!x?a.hidden:""}`,"data-speed-boost-ignore":"true",style:{pointerEvents:"none",background:"none",justifyContent:"flex-end"},children:t("div",{className:a.bottomControls,onClick:o=>o.stopPropagation(),children:[t(Yr,{classes:{container:a.progressBarContainer,scrubbing:a.scrubbing,timeBubble:a.timeBubble,bar:a.progressBar,buffered:a.bufferedBar,watched:a.watchedBar,thumb:a.thumb},videoRef:j,duration:M.duration,formatTime:An,isScrubbing:M.isScrubbing,buffered:"full",onSeek:ot,onScrubStart:pn,onScrubMove:_t,onScrubEnd:We,timeLabelRef:Ie}),t("div",{className:a.bottomControlsRow,children:[t("div",{className:a.leftControls,children:[t("button",{className:a.controlBtn,onClick:P.togglePlay,"aria-label":M.isPlaying?"Pause":"Play",children:M.isPlaying?t($,{name:"pause"}):t($,{name:"play"})}),t(Qr,{muted:M.isMuted,volume:M.volume,volumeIcon:Ue,buttonClassName:a.controlBtn,onToggleMute:Ft,onVolumeChange:ee,onExpandedChange:o=>P.setShowVolumeSlider(o)}),t("span",{className:a.timeDisplay,ref:Ie,children:[An(0)," / ",An(M.duration)]})]}),t("div",{className:a.rightControls,children:[Ot&&t("button",{className:a.nextEpisodeBtn,onClick:oe,title:"Play next episode",children:"Next EP"}),Pe&&t("button",{className:`${a.controlBtn} ${k?a.active:""}`,onClick:qt,"aria-label":k?"Turn subtitles off":"Turn subtitles on",children:t($,{name:"closed-captioning"})}),t("button",{className:`${a.controlBtn} ${x?a.active:""}`,onClick:()=>x?Ct():X(),"aria-label":"Settings",children:t($,{name:"cog"})}),t("button",{className:a.controlBtn,onClick:P.toggleFullscreen,"aria-label":M.isFullscreen?"Exit Fullscreen":"Fullscreen",children:M.isFullscreen?t($,{name:"compress"}):t($,{name:"expand"})})]})]})]})}),gn=x?t("div",{"data-speed-boost-ignore":"true",onClick:o=>o.stopPropagation(),children:t(hs,{classes:{panel:a.settingsPanel,header:a.settingsHeader,backBtn:a.settingsBackBtn,title:a.settingsTitle,content:a.settingsContent},title:x==="main"?"Settings":x==="subtitle-style"?"Subtitle Style":x==="subtitle-timing"?"Subtitle Timing":x==="audio"?"Audio Track":x==="server"?"Movy Server":x==="av-sync"?"A/V Sync":x.charAt(0).toUpperCase()+x.slice(1),onBack:()=>x==="main"?Ct():D("main"),children:[x==="main"&&Tt(),x==="quality"&&fn(),x==="subtitles"&&_(),x==="subtitle-style"&&d(),x==="subtitle-timing"&&ie(),x==="audio"&&le(),x==="server"&&zt(),x==="av-sync"&&Wt()]})}):null,lt=M.isSpeedBoostActive?t("div",{className:a.speedBoostBadge,"aria-hidden":"true",children:[t("span",{children:"2x"}),t($,{name:"forward",size:12})]}):null;return t(Gr,{player:u,topBar:Ut,centerControls:He,bottomBar:it,settingsPanel:gn,overlays:lt,isInteracting:!!x,className:M.isFullscreen?a.fullscreenFlat:"",children:Fe})};function da(u,w,Z){const U=()=>{const V=Array.isArray(u.audioTracks)?u.audioTracks.length:0,r=Z.getWant(),B=V>0?Math.min(Math.max(0,r),V-1):r;u.audioTrack=B,B!==r&&Z.onResolve(B)},q=(V,r)=>{const B=Z.getWant(),se=Array.isArray(u.audioTracks)?u.audioTracks.length:0;if(r.id!==B&&B>=0&&B<se){u.audioTrack!==B&&(u.audioTrack=B);return}Z.onResolve(r.id)};return u.on(w.MANIFEST_PARSED,U),u.on(w.AUDIO_TRACK_SWITCHED,q),()=>{u.off(w.MANIFEST_PARSED,U),u.off(w.AUDIO_TRACK_SWITCHED,q)}}const ua="_page_263ma_1",ma="_header_263ma_7",ha="_pageTitle_263ma_11",pa="_searchForm_263ma_21",fa="_searchInput_263ma_27",ga="_searchBtn_263ma_42",va="_suggestions_263ma_58",ba="_suggestionPill_263ma_65",ya="_statusMsg_263ma_81",Sa="_error_263ma_87",_a="_textOnly_263ma_91",Ca="_spinner_263ma_96",Ta="_spin_263ma_96",ka="_grid_263ma_110",Ea="_skeletonCard_263ma_116",Na="_skeletonThumb_263ma_121",wa="_skeletonLine_263ma_127",xa="_shimmer_263ma_134",Ia="_backBtn_263ma_154",Ba="_meta_263ma_172",Ma="_headerActions_263ma_180",Aa="_watchlistBtn_263ma_187",Pa="_active_263ma_205",La="_year_263ma_215",$a="_rating_263ma_220",Ra="_typeBadge_263ma_229",Da="_tv_263ma_237",Oa="_movie_263ma_242",Va="_tvSeries_263ma_247",Fa="_tvMiniSeries_263ma_252",qa="_overview_263ma_257",za="_controls_263ma_264",Wa="_controlLabel_263ma_272",Ua="_select_263ma_282",Ha="_playerSection_263ma_293",ja="_video_263ma_297",Ka="_videoHidden_263ma_319",Ya="_delayCanvas_263ma_324",Qa="_delayCanvasActive_263ma_337",Ga="_subtitleOverlay_263ma_341",Ja="_videoIframe_263ma_360",Xa="_retryButton_263ma_368",Za="_sidebarSeason_263ma_383",eo="_mobileSeasonRow_263ma_399",to="_sidebarPosterCard_263ma_413",no="_sidebarPosterMeta_263ma_428",ro="_providerSelectWrap_263ma_445",so="_genreRow_263ma_457",ao="_genreChip_263ma_464",oo="_metaRow_263ma_474",io="_detailGrid_263ma_483",lo="_detailItem_263ma_490",co="_imdbLink_263ma_512",h={page:ua,header:ma,pageTitle:ha,searchForm:pa,searchInput:fa,searchBtn:ga,suggestions:va,suggestionPill:ba,statusMsg:ya,error:Sa,textOnly:_a,spinner:Ca,spin:Ta,grid:ka,skeletonCard:Ea,skeletonThumb:Na,skeletonLine:wa,shimmer:xa,backBtn:Ia,meta:Ba,headerActions:Ma,watchlistBtn:Aa,active:Pa,year:La,rating:$a,typeBadge:Ra,tv:Da,movie:Oa,tvSeries:Va,tvMiniSeries:Fa,overview:qa,controls:za,controlLabel:Wa,select:Ua,playerSection:Ha,video:ja,videoHidden:Ka,delayCanvas:Ya,delayCanvasActive:Qa,subtitleOverlay:Ga,videoIframe:Ja,retryButton:Xa,sidebarSeason:Za,mobileSeasonRow:eo,sidebarPosterCard:to,sidebarPosterMeta:no,providerSelectWrap:ro,genreRow:so,genreChip:ao,metaRow:oo,detailGrid:io,detailItem:lo,imdbLink:co},uo=[],Eo=()=>{const{id:u}=Fr(),[w]=qr(),Z=T(w);Z.current=w;const U=zr(),q=Wr(),V=w.get("type"),[r,B]=v(null),[se,me]=v(!1),[l,H]=v(()=>parseInt(w.get("s")||"1",10)||1),[ne,Fe]=v([]),[g,ye]=v(()=>parseInt(w.get("e")||"1",10)||1),[R,$t]=v(()=>{try{return localStorage.getItem("tvProvider")||""}catch{return""}}),[ae,qe]=v([]),[yt,St]=v(""),{options:Rt,isFallback:Dt,isLoading:mn}=vs(),Ot=Se(()=>Rt.filter(e=>(e.kind??"tv")==="tv").map(e=>({id:e.value,label:e.label,tier:e.tier==="embed"?"embed":"direct",servers:e.servers})),[Rt]),oe=Dt||Ot.length===0?uo:Ot,M=oe.filter(e=>e.tier==="direct"),Vt=oe.filter(e=>e.tier==="embed"),[P,j]=v(!1),[he,K]=v(""),[rt,st]=v([]),[Ce,ze]=v(0),[pe,x]=v([]),[D,Ie]=v(-1),[Ae,hn]=v("all"),[fe,at]=v(0),[ot,pn]=v(""),[_t,We]=v(""),[ee,Ft]=v(()=>{try{return localStorage.getItem("movyServer")||""}catch{return""}}),Pe=Se(()=>oe.find(e=>e.id===R),[oe,R]),k=Pe?.tier==="embed",X=Se(()=>Pe?.servers??[],[Pe]),qt=T([]);y(()=>{qt.current=ae},[ae]);const Ct=T(Ce);y(()=>{Ct.current=Ce},[Ce]);const Tt=T(D);y(()=>{Tt.current=D},[D]);const fn=Se(()=>[],[]),_=Jr({skipIntervals:fn}),d=_.refs.videoRef,ie=T(null),le=T([]),zt=T(null),Wt=T(null),[Ue,Ut]=v(()=>{try{const e=Number(localStorage.getItem("playerVideoDelayMs"));if(Number.isFinite(e)&&e>=0&&e<=500)return Math.round(e)}catch{}return 180}),[He,it]=v(()=>{try{return localStorage.getItem("playerVideoDelayEnabled")==="true"}catch{return!1}}),[gn,lt]=v(!1),[o,c]=v(!1),Be=T(null),L=T(null),kt=(e,n)=>{try{localStorage.setItem("playerVideoDelayMs",String(e)),localStorage.setItem("playerVideoDelayEnabled",String(n))}catch{}},Et=e=>{const n=Math.max(0,Math.min(500,Math.round(e)));Ut(n);try{localStorage.setItem("playerVideoDelayMs",String(n))}catch{}},[Te,vn]=v(()=>{try{const e=Number(localStorage.getItem("tvSubtitleDelayMs"));if(Number.isFinite(e)&&e>=-1e4&&e<=1e4)return Math.round(e)}catch{}return 0}),ct=T(Te);ct.current=Te;const Nt=S(e=>{const n=Math.max(-1e4,Math.min(1e4,Math.round(e)));if(ct.current!==n){ct.current=n,vn(n);try{localStorage.setItem("tvSubtitleDelayMs",String(n))}catch{}}},[]),dt=()=>{Be.current={enabled:He,ms:Ue},it(!0);try{localStorage.setItem("playerVideoDelayEnabled","true")}catch{}lt(!0)},Ht=()=>{const e=Be.current;Be.current=null,e&&(Ut(e.ms),it(e.enabled),kt(e.ms,e.enabled)),c(!1),lt(!1)},jt=()=>{Be.current=null,it(!0),kt(Ue,!0),c(!1),lt(!1)},Kt=()=>{if(o){c(!1);return}const e=d.current;e&&!isNaN(e.currentTime)&&(L.current=e.currentTime),c(!0)},E=He?Ue:0;Xr({videoRef:d,canvasRef:zt,delayMs:E,enabled:He});const Q=He,A=Q||Te!==0,ut=T(A);ut.current=A;const wt=S(()=>{if(!ut.current)return;const e=d.current;e&&Array.from(e.textTracks).forEach(n=>{n.mode!=="hidden"&&(n.mode="hidden")})},[d]),{hasConsent:je,grant:Le}=gs(),mt=T("");mt.current||(mt.current=`tv-${Date.now()}-${Math.random().toString(36).slice(2)}`);const m=V==="movie",Ke=u?bs(V||"tv",u):"",O=Ke,Yt=ys(),Qt=Ss(),{toggle:On,bookmarkedIds:mr,pending:hr}=_s(),{data:Vn}=Cs(r?Ke:void 0),Gt=Vn?!!Vn.inLibrary:Ke?mr.has(Ke):!1,pr=S(()=>{!r||!u||On({tmdbId:Number(u),mediaType:V||"tv",title:r.title,poster:r.poster,year:r.year,adult:r.adult})},[r,u,V,On]),{data:Ye,isFetched:Fn,isPending:qn}=Ts(O||void 0,l,g),Jt=Se(()=>ne.map(e=>e.episode_number).sort((e,n)=>e-n),[ne]),Qe=Jt.indexOf(g),bn=Qe>0?Jt[Qe-1]:null,N=Qe>=0&&Qe<Jt.length-1?Jt[Qe+1]:null,Xt=Se(()=>(r?.seasons??[]).map(e=>e.season_number).filter(e=>Number.isFinite(e)).sort((e,n)=>e-n),[r]),ge=Se(()=>{if(m||N!=null)return null;const e=Xt.indexOf(l);return e>=0&&e<Xt.length-1?Xt[e+1]:(r?.number_of_seasons??0)>l?l+1:null},[m,N,Xt,l,r?.number_of_seasons]),zn=Ye?.currentTime??0,Wn=Ye?.duration??0,yn=Ye?.completed===!0||Ye?.completed===1||Pn(zn,Wn),[ke,Me]=v(!1),Ee=T(!1),Sn=T(null),xt=T(null);y(()=>{Ee.current=!1,xt.current=null,Me(!1)},[O,l,g]);const[Ne,Zt]=v(!1),[ht,Un]=v(0),[Ge,Hn]=v(!1),[$e,Je]=v(!1),[fr,_n]=v(!1),[gr,jn]=v(""),[en,vr]=v(!1),[br,Cn]=v(!1);Zr(_,(ae.length>0||_t!=="")&&!Ne&&!ke&&!$e);const yr=ws(),[pt,Sr]=v(es),_r=S(e=>{Sr(e),ts(e)},[]),ce=T(!1),Xe=T(null),Tn=T(0),Kn=T(!1),Yn=T(0),te=T(null),It=T(""),Cr=cr(zn,Wn),Bt=!ce.current&&!Ee.current&&(Ne||ke||$e||yn||Cr),ft=T(Bt);ft.current=Bt;const gt=T(!1);y(()=>{gt.current=Ge},[Ge]);const Tr=e=>{const n=Math.floor(e/3600),s=Math.floor(e%3600/60),i=Math.floor(e%60);return n>0?`${n}:${String(s).padStart(2,"0")}:${String(i).padStart(2,"0")}`:`${s}:${String(i).padStart(2,"0")}`},Re=S((e,n)=>{if(!O||!n||n<30)return;const s=Pn(e,n),i=s?n:e;Math.abs(i-Tn.current)<10&&!s||(Tn.current=i,Yt.mutate({mediaId:O,season:l,episode:g,currentTime:Math.floor(i),duration:Math.floor(n),completed:s,title:r?.title??null,poster:r?.poster??null,backdrop:r?.backdrop??null,year:r?.year??null,overview:r?.overview??null,tmdbId:r?.id??null,mediaType:V||"tv",adult:r?.adult?1:0}))},[O,l,g,Yt,r,V]),we=S((e,n)=>{c(!1),lt(!1);const s=new URLSearchParams(w);s.set("type",w.get("type")||"tv"),s.set("s",String(e)),s.set("e",String(n)),U(`${window.location.pathname}?${s.toString()}`)},[w,U]),Mt=S(()=>{const e=d.current;if(!e||m||N==null){_n(!1);return}const n=e.duration||0,s=e.currentTime||0;_n(n>0&&s/n>=.8)},[m,N,d]),tn=S((e,n)=>["tv-sources",R,m?"movie":"tv",u,e,n,ee],[R,m,u,ee]),nn=S(async(e,n)=>{if(!r||!u||!R)return{valid:!1,sources:[]};const s=m?"movie":"tv",i=new URLSearchParams({title:r.title||"",year:r.year||"",season:String(e),episode:String(n),totalSeasons:String(r.number_of_seasons||1),imdbId:r.imdb_id||""});return X.length>0&&ee&&i.set("server",ee),await(await fetch(`/api/tv/sources/${R}/${s}/${u}?${i.toString()}`)).json()},[r,u,R,m,X,ee]),Qn=S(()=>{if(m||!r||!u||!R||k||N==null)return;const e=tn(l,N);q.getQueryState(e)?.status!=="success"&&q.prefetchQuery({queryKey:e,queryFn:()=>nn(l,N),staleTime:300*1e3,gcTime:1800*1e3})},[m,r,u,R,k,N,l,q,tn,nn]),kr=S(()=>{if(o)return;Mt();const e=d.current;!e||e.paused||e.ended||(e.duration>0&&e.duration-e.currentTime<=60&&Qn(),Xe.current&&clearTimeout(Xe.current),Xe.current=setTimeout(()=>{Re(e.currentTime,e.duration||0)},5e3))},[Re,d,o,Mt,Qn]),Er=S(()=>{const e=d.current;if(!e||!r)return;Xe.current&&clearTimeout(Xe.current),Re(e.currentTime,e.duration||0),Kn.current=!0;const n=ns({seekInduced:Date.now()-Yn.current<rs,autoplayEnabled:pt,hasNext:!m&&N!=null,isFinal:m||N==null});n==="advance"&&N!=null?(ye(N),we(l,N)):n==="prompt-next"?(Ee.current=!1,ce.current=!0,Me(!0)):n==="prompt-complete"&&(jn(m?r.title:`${r.title} — Season ${l}`),Je(!0),Ee.current=!0,ce.current=!0)},[m,r,l,N,pt,Re,we,d]),Nr=S(()=>{Kn.current=!1,_.actions.onPlay()},[_.actions]),wr=S(()=>{_.actions.onPause();const e=d.current;e&&!e.ended&&Re(e.currentTime,e.duration||0)},[_.actions,Re,d]),xr=S(()=>{_.actions.onLoadedMetadata();const e=d.current;if(e){try{const n=parseFloat(localStorage.getItem("playerVolume")||"");isNaN(n)||(e.volume=Math.max(0,Math.min(1,n))),e.muted=localStorage.getItem("playerMuted")==="true"}catch{}if(te.current!=null&&te.current>0){try{e.currentTime=te.current}catch{}te.current=null}Mt()}},[_.actions,d,Mt]),vt=S(()=>{const e=d.current;if(e&&te.current!=null&&te.current>0){try{e.currentTime=te.current}catch{}te.current=null}},[d]),Ir=S(()=>{ht>0&&(te.current=ht);const e=d.current;if(e&&ht>0){try{e.currentTime=ht,te.current=null}catch{}e.play().catch(()=>{})}Zt(!1),ce.current=!0},[ht,d]),Gn=S(()=>{te.current=null;const e=d.current;e&&(e.currentTime=0,e.play().catch(()=>{})),Zt(!1),ce.current=!0},[d]),Br=S(()=>{Ee.current=!0,Me(!1),ce.current=!0},[]),Mr=S(()=>{Ee.current=!0,Me(!1),ce.current=!0,te.current=null;const e=d.current;if(e){try{e.currentTime=0}catch{}e.play().catch(()=>{})}},[d]),Jn=S(()=>{N!=null&&(Ee.current=!0,Me(!1),ce.current=!0,ye(N),we(l,N))},[N,l,we]),Xn=S(()=>{ge!=null&&(Ee.current=!0,Me(!1),Je(!1),ce.current=!0,H(ge),ye(1),we(ge,1))},[ge,we]),Ar=S(async()=>{if(O)try{await Qt.mutateAsync({id:O,status:"Completed"}),Je(!1),Me(!1),U("/")}catch{}},[O,Qt,U]);y(()=>{ce.current=!1,Sn.current=null,Zt(!1),_n(!1),Un(0),Hn(!1),gt.current=!1,te.current=null,It.current=`${O}:${l}:${g}`,Tn.current=0},[O,l,g]),y(()=>{Fn&&(Hn(!0),gt.current=!0)},[Fn,O,l,g]),y(()=>{if(!Ye||ce.current)return;const e=`${O}:${l}:${g}`;if(Sn.current===e)return;Sn.current=e;const n=Ye,s=n.currentTime??0,i=n.duration??0;cr(s,i)&&(Un(s),Zt(!0))},[Ye,O,l,g]),y(()=>{if(!Ge||ce.current||Ee.current||Ne||$e||ke)return;const e=`${O}:${l}:${g}`;if(xt.current!==e){if(!yn){qn||(xt.current=e);return}if(m){xt.current=e,Me(!0);return}if(!(!r||Qe<0||ne.length===0)){if(xt.current=e,N==null&&ge==null){r&&ne.length>0&&(jn(`${r.title} — Season ${l}`),Je(!0),Ee.current=!0,ce.current=!0);return}Me(!0)}}},[Ge,yn,qn,Ne,$e,ke,m,N,ge,r,Qe,ne.length,O,l,g]),y(()=>{(Ne||ke)&&d.current&&d.current.pause()},[Ne,ke,d]),y(()=>{const e=d.current;return()=>{Xe.current&&clearTimeout(Xe.current),e&&!e.ended&&Re(e.currentTime,e.duration||0)}},[Re,d]);const kn=S(e=>{let n=null,s=!0;try{n=localStorage.getItem("tvLastSubtitle"),s=localStorage.getItem("tvSubtitlesEnabled")!=="false"}catch{}return Dn(e,{lastKey:n,enabled:s})},[]),En=S(e=>{if(!(e<0))try{localStorage.setItem("tvSubtitlesEnabled","true"),localStorage.setItem("tvSelectedSubtitle",String(e))}catch{}},[]),Zn=S(e=>{if(e.length===0)return 0;let n=null;try{n=localStorage.getItem("tvLastAudio")}catch{}if(n){const s=e.findIndex(p=>Ln(p)===n);if(s>=0)return s;const i=n.indexOf("|||"),f=i>=0?n.slice(0,i):n,G=i>=0?n.slice(i+3):n,b=e.findIndex(p=>p.language===f||p.label===G);if(b>=0)return b}return Dn(e,{lastKey:null,enabled:!0})},[]),{data:er}=ks(O||void 0),tr=Se(()=>(er?.progress??[]).filter(n=>n.season===l&&(n.completed===!0||n.completed===1||Pn(n.currentTime,n.duration))).map(n=>String(n.episode)),[er,l]),nr=Se(()=>ne.map(e=>({id:String(e.episode_number),label:`Episode ${e.episode_number}`,sublabel:e.name||void 0,thumbnail:e.still_path?`https://image.tmdb.org/t/p/w185${e.still_path}`:void 0})),[ne]),rr=Se(()=>(r?.genres??[]).map(e=>e.name).slice(0,5),[r]),Nn=Se(()=>[r?.year,r?.status,r?.number_of_seasons?`${r.number_of_seasons} Season${r.number_of_seasons>1?"s":""}`:null,r?.number_of_episodes?`${r.number_of_episodes} Episodes`:null].filter(Boolean),[r]);y(()=>{if(oe.length!==0&&!oe.some(e=>e.id===R)){const e=oe.find(n=>n.tier==="direct")?.id??oe[0].id;$t(e);try{localStorage.setItem("tvProvider",e)}catch{}}},[oe,R]),y(()=>{if(X.length!==0&&!X.includes(ee)){Ft(X[0]);try{localStorage.setItem("movyServer",X[0])}catch{}}},[X,ee]),y(()=>{if(!u){B(null),qe([]),St(""),K(""),We("");return}const e=Number(u),n=V||"tv";me(!0),B(null),qe([]),St(""),K(""),We("");const s=parseInt(Z.current.get("s")||"",10),i=parseInt(Z.current.get("e")||"",10);H(isNaN(s)?1:s),ye(isNaN(i)?1:i),at(0),fetch(`/api/tv/details/${n}/${e}`).then(f=>f.json()).then(f=>{B(f),me(!1),f.seasons&&f.seasons.length>0&&isNaN(s)&&H(f.seasons[0].season_number)}).catch(()=>{me(!1)})},[u,V]),y(()=>{if(!r||!u)return;if(!(r.seasons!==void 0||r.number_of_seasons!==void 0)){Fe([]);return}const n=parseInt(w.get("e")||"",10);fetch(`/api/tv/episodes/${u}/${l}`).then(s=>s.json()).then(s=>{if(Fe(s.episodes||[]),s.episodes?.length>0){const i=!isNaN(n)&&s.episodes.find(f=>f.episode_number===n)?n:s.episodes[0].episode_number;ye(i)}}).catch(()=>{})},[r,u,l,w]),y(()=>{!r||!u||m||fetch(`/api/tv/episodes/${u}/${l}`).then(e=>e.json()).then(e=>{const n=e.episodes||[];Fe(n),n.length>0&&!n.find(s=>s.episode_number===g)&&ye(n[0].episode_number)}).catch(()=>{})},[l,r,u,m,g]),y(()=>{if(!k||!r||!u)return;fetch(`/api/tv/embed/${R}/${m?"movie":"tv"}/${u}?season=${l}&episode=${g}`).then(n=>n.json()).then(n=>{if(n.url)return We(n.url),fetch(`/api/resolve?url=${encodeURIComponent(n.url)}`).then(s=>s.json()).then(s=>{s.finalUrl&&We(s.finalUrl)})}).catch(()=>{})},[R,r,u,l,g,m,k]);const rn=S(async()=>{if(!r||!u||!R||k||r.adult&&!je)return;const e=`${O}:${l}:${g}`;qt.current.length>0?K(""):(j(!0),K(""),qe([]),at(0),st([]),x([]));try{const s=await q.fetchQuery({queryKey:tn(l,g),queryFn:()=>nn(l,g),staleTime:3e5,gcTime:18e5});if(It.current!==e)return;if(j(!1),!s.valid||!s.sources?.length){K(s.error?`${s.error}`:X.length>0?`No streams from ${ee}. Try another server.`:"No streams available.");return}qe(s.sources),St(e),hn("all"),at(0),s.referer&&pn(s.referer);const i=s.audioTracks||[];st(i),ze(i.length>0?Zn(i):0);const f=s.subtitles||[];if(x(f),f.length>0){const p=kn(f);Ie(p),En(p)}else Ie(-1);fetch(`/api/tv/subtitles/${m?"movie":"tv"}/${u}?season=${l}&episode=${g}`).then(p=>p.json()).then(p=>{if(It.current!==e)return;const re=Array.isArray(p.subtitles)?p.subtitles:[];re.length!==0&&x(ve=>{const F=[...ve,...re.filter(I=>!ve.some(C=>C.url===I.url))];if(ve.length===0){const I=kn(F);Ie(I),En(I)}return F})}).catch(()=>{}),K("")}catch{if(It.current!==e)return;j(!1),K("Failed to load streams.")}},[r,u,R,l,g,m,k,je,X,ee,kn,En,Zn,O,q,tn,nn]),wn=S(e=>{const n=e.toLowerCase();Ft(n);try{localStorage.setItem("movyServer",n)}catch{}K("")},[]),Pr=S(e=>{$t(e);try{localStorage.setItem("tvProvider",e)}catch{}},[]);y(()=>{R&&r&&!k&&rn()},[R,r,rn,k]),y(()=>{if(!d.current||(ie.current&&(ie.current.destroy(),ie.current=null),k))return;let e=!1;if(o){const b=d.current;return b&&(b.pause(),b.removeAttribute("src"),b.load(),b.src="/av-sync-test.mp4",ft.current||b.play().catch(()=>{})),()=>{e=!0}}const n=Ae==="all"?ae:ae.filter(b=>b.type===Ae),s=yt===`${O}:${l}:${g}`&&n[fe]?.url||"";if(!s){try{d.current?.pause()}catch{}return}const i=d.current;if(It.current===`${O}:${l}:${g}`&&te.current==null&&!ft.current)try{!i.ended&&i.currentTime>1&&(te.current=i.currentTime)}catch{}i.pause(),i.removeAttribute("src"),i.load();const f=`/api/tv/stream-proxy?url=${encodeURIComponent(s)}&referer=${encodeURIComponent(ot)}`;n[fe]?.type==="hls"?(async()=>{const b=await Es();if(!e)if(b&&b.isSupported()){const p=new b({enableWorker:!0});ie.current=p,p.on(b.Events.ERROR,(F,I)=>{I.fatal&&(K(`Stream failed (${I.details}). Try another source or reload.`),j(!1),p.destroy(),ie.current=null)}),p.loadSource(f),p.attachMedia(i);const re=()=>{let F=!1,I=0;try{F=localStorage.getItem("tvSubtitlesEnabled")==="true";const C=parseInt(localStorage.getItem("tvSelectedSubtitle")||"0",10);!isNaN(C)&&C>=0&&(I=C)}catch{}return{enabled:F,index:I}},ve=()=>{if(typeof p.subtitleTrack!="number")return;if(le.current.length>0){p.subtitleTrack=-1;return}const F=Array.isArray(p.subtitleTracks)?p.subtitleTracks:[];if(F.length===0){p.subtitleTrack=-1;return}const I=re();if(I.enabled){const C=I.index<F.length?I.index:0;p.subtitleTrack=C,Ie(C),wt()}else p.subtitleTrack=-1};da(p,b.Events,{getWant:()=>Ct.current,onResolve:F=>ze(F)}),p.on(b.Events.MANIFEST_PARSED,()=>{ve(),L.current!=null&&(i.currentTime=L.current,L.current=null),vt(),!ft.current&>.current&&i.play().catch(()=>{})}),p.on(b.Events.SUBTITLE_TRACKS_UPDATED,()=>{ve()}),p.on(b.Events.SUBTITLE_TRACK_SWITCH,(F,I)=>{if(le.current.length>0){if(I.id!==-1)try{p.subtitleTrack=-1}catch{}return}const C=Array.isArray(p.subtitleTracks)?p.subtitleTracks:[];if(C.length!==0){if(I.id===-1){const Y=re();if(Y.enabled){const J=Y.index<C.length?Y.index:0;p.subtitleTrack=J;return}}Ie(I.id),wt()}})}else Ns(i)&&(i.src=f,L.current!=null&&(i.currentTime=L.current,L.current=null),vt(),!ft.current&>.current&&i.play().catch(()=>{K("Failed to play stream. Try another source.")}))})():(i.src=f,L.current!=null&&(i.currentTime=L.current,L.current=null),vt(),!ft.current&>.current&&i.play().catch(()=>{K("Failed to play stream. Try another source.")}));const G=()=>{const re=i.error?.code===4?"Video source not supported or not found":"Failed to load video stream";K(`${re}. The source may be invalid or expired. Try switching source or reloading.`),j(!1)};return i.addEventListener("error",G),()=>{e=!0,i.removeEventListener("error",G)}},[ae,yt,fe,Ae,R,ot,k,d,o,O,l,g,vt,wt]),y(()=>{if(!Ge||Ne||ke||$e||Bt||k||o)return;const e=d.current;!e||!e.paused||e.ended||e.readyState<1||(vt(),e.play().catch(()=>{}))},[Ge,Ne,ke,$e,Bt,k,o,vt,d]),y(()=>{const e=ie.current;!e||typeof e.audioTrack!="number"||(e.audioTrack=Ce)},[Ce]),y(()=>{const e=ie.current;!e||typeof e.subtitleTrack!="number"||!Array.isArray(e.subtitleTracks)||e.subtitleTracks.length===0||le.current.length>0||(D>=0?e.subtitleTrack=D:e.subtitleTrack=-1)},[D]),y(()=>{const e=d.current;if(!(!e||k)&&(e.querySelectorAll("track").forEach(n=>n.remove()),le.current=[],pe.length!==0&&(pe.forEach(n=>{const s=document.createElement("track");s.kind="subtitles",s.label=n.label||n.language||"Unknown",s.srclang=n.language||n.label||"en";const i=`/api/subtitle-proxy?url=${encodeURIComponent(n.url)}&referer=${encodeURIComponent(ot)}`;s.src=i,s.addEventListener("load",()=>{const f=le.current.indexOf(s),G=s.track;if(G&&f>=0){const b=f===Tt.current;G.mode=b&&!ut.current?"showing":"hidden"}}),e.appendChild(s),le.current.push(s)}),pe.length>0)))try{const n=ie.current;n&&typeof n.subtitleTrack=="number"&&n.subtitleTrack!==-1&&(n.subtitleTrack=-1)}catch{}},[pe,ot,k,R,d]),y(()=>{const e=d.current;if(!e||k)return;const n=ie.current;le.current.length>0&&n&&typeof n.subtitleTrack=="number"&&Array.isArray(n.subtitleTracks)&&n.subtitleTracks.length>0&&(n.subtitleTrack=-1);const s=()=>{const G=Rn(_.state.subtitleFontSize,e.clientHeight||720),b=Math.max(0,Math.min(100,100-_.state.subtitlePosition)),p=()=>{const C=G*16,Y=e.videoHeight||e.clientHeight||720,J=e.videoWidth||e.clientWidth||1280;return{step:C*1.3/Y*100,chars:Math.max(20,Math.floor(J/(C*.55)))}},re=(C,Y)=>{const J=C;J.snapToLines=!1,J.line=Y},ve=C=>{if(!C.cues)return;Array.from(C.cues).forEach(de=>re(de,b));const Y=Array.from(C.activeCues??[]);if(Y.length<=1){Y.forEach(de=>re(de,b));return}const{step:J,chars:z}=p();let Ze=b;for(let de=Y.length-1;de>=0;de--){const De=String(Y[de].text??"").replace(/<[^>]*>/g,"").split(`
|
|
3
|
-
`).reduce((on,Pt)=>on+Math.max(1,Math.ceil(Pt.length/z)),0);Ze=Math.max(0,Ze-De*J),re(Y[de],Ze),Ze-=J*.4}},F=le.current;if(F.length>0){F.forEach((C,Y)=>{const J=C.track;if(!J)return;const z=Y===D;J.mode=z&&!A?"showing":"hidden",z&&!A&&ve(J)});return}const I=Array.from(e.textTracks);I.length!==0&&I.forEach((C,Y)=>{const J=Y===D;C.mode=J&&!A?"showing":"hidden",J&&!A&&ve(C)})};s();const i=()=>s();e.textTracks.addEventListener("addtrack",i),e.addEventListener("loadedmetadata",s,{once:!0});const f=window.setTimeout(s,600);return()=>{e.textTracks.removeEventListener("addtrack",i),e.removeEventListener("loadedmetadata",s),window.clearTimeout(f)}},[D,pe,k,R,d,A,_.state.subtitleFontSize,_.state.subtitlePosition]),y(()=>{if(!A)return;const e=d.current,n=Wt.current;if(!e||!n)return;const s=Te/1e3,i=E/1e3,f=new Set;let G=[],b=-1,p=-1,re=-1,ve=-1,F=0,I=0,C=!1;const Y=()=>{const W=Tt.current;if(W<0)return null;const Oe=le.current;return Oe.length>0?Oe[W]?.track??null:Array.from(e.textTracks)[W]??null},J=(W,Oe)=>{const ue=W.cues;if(!ue||ue.length===0)return Array.from(W.activeCues??[]);let et=0,tt=ue.length;for(;et<tt;){const xe=et+tt>>>1;ue[xe].endTime+s<Oe?et=xe+1:tt=xe}const Lt=[];for(let xe=et;xe<ue.length;xe++){const ln=ue[xe];if(ln.startTime+s>Oe)break;ln.endTime+s>=Oe&&Lt.push(ln)}return Lt},z=()=>{const W=Y(),Oe=e.currentTime-i,ue=W?J(W,Oe):[],et=e.clientWidth,tt=e.clientHeight,Lt=e.videoWidth,xe=e.videoHeight;if(ue.length===G.length&&ue.every((In,Bn)=>In===G[Bn])&&et===b&&tt===p&&Lt===re&&xe===ve||(G=ue,b=et,p=tt,re=Lt,ve=xe,n.textContent="",ue.length===0))return;const ir={fontSize:Rn(_.state.subtitleFontSize,tt),position:_.state.subtitlePosition,bgOpacity:_.state.subtitleBgOpacity,bgColor:_.state.subtitleBgColor,textColor:_.state.subtitleTextColor,edge:_.state.subtitleEdge,bold:_.state.subtitleBold},Dr=ss(ir),Or=as(e,ir.position);ue.forEach((In,Bn)=>{const lr=String(In.text??"");if(!os(lr).trim())return;const Mn=document.createElement("div"),Vr=(ue.length-1-Bn)*1.7;Mn.style.cssText=`${Dr}
|
|
4
|
-
bottom: calc(${Or}px + ${Vr}em);`,Mn.innerHTML=is(lr),n.appendChild(Mn)})},Ze=()=>{Array.from(e.textTracks).forEach(W=>{f.has(W)||(f.add(W),W.addEventListener("cuechange",z))})},de=()=>{Ze(),z()},xn=()=>{if(C||I)return;const W=()=>{I=0,!(C||e.paused||e.ended)&&(z(),xn())};I=window.requestAnimationFrame(W)},De=e,on=()=>{F=0,z(),!C&&De.requestVideoFrameCallback&&(F=De.requestVideoFrameCallback(on))},Pt=()=>{z(),De.requestVideoFrameCallback?F||(F=De.requestVideoFrameCallback(on)):xn()};Ze(),e.textTracks.addEventListener("addtrack",de),e.textTracks.addEventListener("removetrack",de),e.addEventListener("timeupdate",z),e.addEventListener("seeked",z),e.addEventListener("loadedmetadata",z),e.addEventListener("play",Pt);const ar=Array.from(e.querySelectorAll("track"));ar.forEach(W=>W.addEventListener("load",z));const or=typeof ResizeObserver>"u"?null:new ResizeObserver(()=>z());return or?.observe(e),z(),Pt(),()=>{C=!0,f.forEach(W=>W.removeEventListener("cuechange",z)),e.textTracks.removeEventListener("addtrack",de),e.textTracks.removeEventListener("removetrack",de),e.removeEventListener("timeupdate",z),e.removeEventListener("seeked",z),e.removeEventListener("loadedmetadata",z),e.removeEventListener("play",Pt),ar.forEach(W=>W.removeEventListener("load",z)),or?.disconnect(),I&&window.cancelAnimationFrame(I),F&&De.cancelVideoFrameCallback&&De.cancelVideoFrameCallback(F),n.textContent=""}},[A,Te,D,pe,E,d,_.state.subtitleFontSize,_.state.subtitlePosition,_.state.subtitleBgOpacity,_.state.subtitleBgColor,_.state.subtitleTextColor,_.state.subtitleEdge,_.state.subtitleBold]);const sn=()=>{B(null),qe([]),We(""),U(-1)},Lr=S(e=>{H(e),ye(1),we(e,1)},[we]),an=!m&&r?.seasons&&r.seasons.length>1?t("label",{className:h.sidebarSeason,children:[t("span",{children:"Season"}),t("select",{value:l,onChange:e=>Lr(parseInt(e.target.value,10)||1),className:h.select,children:r.seasons.map(e=>t("option",{value:e.season_number,children:["Season ",e.season_number," (",e.episode_count," ep)"]},e.season_number))})]}):null,bt=S(e=>{const n=parseInt(e,10);if(!isNaN(n)){try{d.current?.pause()}catch{}ye(n),we(l,n)}Cn(!1)},[l,we,d]);y(()=>{if(m)return;const e=n=>{n.target.closest('input, textarea, button, select, a, [role="button"], [contenteditable="true"]')||n.key.toLowerCase()==="n"&&(N!=null?bt(String(N)):Ur.error("No next episode available"))};return document.addEventListener("keydown",e),()=>{document.removeEventListener("keydown",e)}},[m,N,bt]);const $r=e=>{ze(e);try{const s=rt[e];s&&localStorage.setItem("tvLastAudio",Ln(s))}catch{}const n=ie.current;n&&n.audioTrack!==void 0&&(n.audioTrack=e)},Rr=e=>{Ie(e);try{if(e===-1)localStorage.setItem("tvSubtitlesEnabled","false");else{localStorage.setItem("tvSubtitlesEnabled","true"),localStorage.setItem("tvSelectedSubtitle",String(e));const f=pe[e];f&&localStorage.setItem("tvLastSubtitle",Ln(f))}}catch{}const n=d.current,s=ie.current;if(s&&typeof s.subtitleTrack=="number"&&Array.isArray(s.subtitleTracks)&&s.subtitleTracks.length>0&&(s.subtitleTrack=le.current.length>0||e<0?-1:e),!n)return;const i=le.current;if(i.length>0){i.forEach((f,G)=>{const b=f.track;if(!b)return;const p=G===e;b.mode=p&&!A?"showing":"hidden"});return}Array.from(n.textTracks).forEach((f,G)=>{const b=G===e;f.mode=b&&!A?"showing":"hidden"})},At=S(()=>{if(!r)return;const e=d.current,n=m?"":`S${String(l).padStart(2,"0")}E${String(g).padStart(2,"0")}`;fetch("/api/discord/tv",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({title:r.title,episodeLabel:n,isPlaying:e?!e.paused&&!e.ended:!0,thumbnail:r.poster,currentTime:e?e.currentTime:0,duration:e&&e.duration||0,isAdult:r.adult===!0,sessionId:mt.current})}).catch(()=>{}),fetch("/api/discord/heartbeat",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({sessionId:mt.current})}).catch(()=>{})},[r,m,l,g,d]);y(()=>{if(!r)return;At();const e=window.setInterval(()=>{const n=d.current;(!n||!n.paused)&&At()},15e3);return()=>window.clearInterval(e)},[At,r,d]),y(()=>{if(!r||k||P||he||ae.length===0)return;const e=d.current;if(!e)return;const n=()=>At();return e.addEventListener("play",n),e.addEventListener("pause",n),e.addEventListener("seeked",n),()=>{e.removeEventListener("play",n),e.removeEventListener("pause",n),e.removeEventListener("seeked",n)}},[r,k,P,he,ae,At,d]),y(()=>{const e=mt.current,n=()=>{const i=JSON.stringify({sessionId:e});navigator.sendBeacon?(navigator.sendBeacon("/api/discord/clear",new Blob([i],{type:"application/json"})),navigator.sendBeacon("/api/discord/heartbeat",new Blob([JSON.stringify({sessionId:e,bye:!0})],{type:"application/json"}))):fetch("/api/discord/clear",{method:"POST",headers:{"Content-Type":"application/json"},body:i,keepalive:!0}).catch(()=>{})},s=()=>n();return window.addEventListener("pagehide",s),window.addEventListener("beforeunload",s),()=>{window.removeEventListener("pagehide",s),window.removeEventListener("beforeunload",s),n()}},[]);const sr=Ae==="all"?ae:ae.filter(e=>e.type===Ae);return y(()=>{if(!r){document.title="TV & Movies - dango";return}document.title=m?`${r.title} - dango`:`${r.title} - Season ${l} Episode ${g} - dango`},[r,m,l,g]),y(()=>{if(!r||m)return;const e=new URLSearchParams(w),n=e.get("s"),s=e.get("e"),i=String(l),f=String(g);n===i&&s===f||(e.set("type",w.get("type")||"tv"),e.set("s",i),e.set("e",f),U(`${window.location.pathname}?${e.toString()}`,{replace:!0}))},[r,m,l,g,w,U]),t("div",{className:nt.playerPageLayout,children:[t("aside",{className:nt.episodeSidebar,children:r?m?t("div",{className:h.sidebarPosterCard,children:[r.poster&&t("img",{src:`https://image.tmdb.org/t/p/w342${r.poster}`,alt:r.title,loading:"lazy",decoding:"async"}),t("div",{className:h.sidebarPosterMeta,children:[t("strong",{children:r.title}),t("span",{children:r.year})]})]}):t(ls,{episodes:nr,currentEpisode:String(g),watchedEpisodes:tr,onEpisodeClick:bt,header:an}):m?t("div",{className:h.statusMsg,children:[t($,{name:"spinner",className:h.spinner})," Loading..."]}):t(cs,{variant:"sidebar"})}),t("div",{className:nt.playerMain,children:[r&&t("div",{className:h.playerSection,onContextMenu:e=>e.preventDefault(),style:r.adult&&!je?{filter:"blur(14px)",pointerEvents:"none",userSelect:"none"}:void 0,children:[P&&!k&&t("div",{className:h.statusMsg,children:[t($,{name:"spinner",className:h.spinner})," Loading stream..."]}),k&&_t?t("iframe",{src:_t,className:h.videoIframe,allow:"autoplay; fullscreen",allowFullScreen:!0}):!k&&(sr.length>0||P)?t(Ve,{children:[he&&t("div",{className:`${h.statusMsg} ${h.error}`,style:{flexDirection:"column",alignItems:"stretch",gap:8},children:[t("div",{style:{display:"flex",alignItems:"center",gap:8,flexWrap:"wrap"},children:[t("span",{style:{flex:1},children:he}),t("button",{className:h.retryButton,onClick:rn,children:"Retry"})]}),X.length>0&&t("div",{style:{display:"flex",flexWrap:"wrap",gap:6,alignItems:"center",marginTop:4},children:[t("span",{style:{fontSize:"0.8rem",opacity:.9,fontWeight:600},children:"Servers:"}),X.map(e=>t("button",{onClick:()=>wn(e),className:h.retryButton,style:{padding:"4px 8px",fontSize:"0.75rem",textTransform:"capitalize",background:ee===e?"var(--accent)":void 0,color:ee===e?"white":void 0},children:e},e))]})]}),t(ca,{player:_,title:r.title,episodeLabel:m?void 0:`S${l} E${g}`,audioTracks:rt,selectedAudioTrack:Ce,onAudioTrackChange:$r,subtitles:pe,selectedSubtitle:D,onSubtitleChange:Rr,streams:sr,qualityIdx:fe,onQualityChange:at,onBack:sn,movyServers:X,selectedMovyServer:ee,onMovyServerSelect:wn,isMovySource:X.length>0,videoDelayEnabled:He,onVideoDelayToggle:e=>{it(e);try{localStorage.setItem("playerVideoDelayEnabled",String(e))}catch{}},videoDelayMs:Ue,onVideoDelayChange:Et,onCalibrateAvSync:dt,subtitleDelayMs:Te,onSubtitleDelayChange:Nt,showNextEpisodeButton:fr&&!Ne&&!ke&&!$e,onNextEpisode:Jn,children:[t("video",{ref:d,autoPlay:Ge&&!Bt,playsInline:!0,disablePictureInPicture:!0,className:`${h.video} ${Q?h.videoHidden:""}`,onTimeUpdate:kr,onEnded:()=>{o||Er()},onPlay:Nr,onPause:wr,onSeeked:()=>{Yn.current=Date.now(),Mt()},onLoadedMetadata:xr,onVolumeChange:_.actions.onVolumeChange,onContextMenu:e=>e.preventDefault(),onError:()=>{K("Video failed to load. Try another server or reload."),j(!1)}}),t("canvas",{ref:zt,className:`${h.delayCanvas} ${Q?h.delayCanvasActive:""}`}),A&&t("div",{ref:Wt,className:h.subtitleOverlay}),t(ds,{isOpen:gn,ms:Ue,onChange:Et,onApply:jt,onClose:Ht,testClipActive:o,onTestClip:Kt})]})]}):he&&!k?t("div",{className:`${h.statusMsg} ${h.error}`,style:{flexDirection:"column",alignItems:"stretch",gap:8},children:[t("div",{style:{display:"flex",alignItems:"center",gap:8,flexWrap:"wrap"},children:[t("span",{style:{flex:1},children:he}),t("button",{className:h.retryButton,onClick:rn,children:"Retry"})]}),X.length>0&&t("div",{style:{display:"flex",flexWrap:"wrap",gap:6,alignItems:"center",marginTop:4},children:[t("span",{style:{fontSize:"0.8rem",opacity:.9,fontWeight:600},children:"Servers:"}),X.map(e=>t("button",{onClick:()=>wn(e),className:h.retryButton,style:{padding:"4px 8px",fontSize:"0.75rem",textTransform:"capitalize",background:ee===e?"var(--accent)":void 0,color:ee===e?"white":void 0},children:e},e))]})]}):null]}),r&&t(us,{showTheater:!1}),an&&t("div",{className:h.mobileSeasonRow,children:an}),r&&t("div",{className:h.providerSelectWrap,children:t("label",{className:h.controlLabel,children:["Source",t("select",{value:R,onChange:e=>Pr(e.target.value),className:h.select,disabled:oe.length===0,children:mn?t("option",{value:"",children:"Loading…"}):oe.length===0?t("option",{value:"",children:"No providers available"}):t(Ve,{children:[M.length>0&&t("optgroup",{label:"Direct HLS",children:M.map(e=>t("option",{value:e.id,children:e.label},e.id))}),Vt.length>0&&t("optgroup",{label:"Embeds",children:Vt.map(e=>t("option",{value:e.id,children:e.label},e.id))})]})})]})}),r&&t("div",{className:nt.playerInfoContainer,children:[t("div",{className:nt.playerInfoHeader,children:[t("div",{className:nt.playerAnimeCard,children:r.poster&&t("img",{src:`https://image.tmdb.org/t/p/w342${r.poster}`,alt:r.title,loading:"lazy",decoding:"async"})}),t("div",{className:nt.videoTitleSection,children:[t("div",{className:be.titleContainer,children:[t("h1",{children:r.title}),t("div",{className:h.meta,children:[t("span",{className:h.year,children:r.year}),r.vote_average!=null&&r.vote_average>0&&t("span",{className:h.rating,children:[t($,{name:"star",size:12}),Number(r.vote_average).toFixed(1)]}),t("span",{className:`${h.typeBadge} ${h[m?"movie":"tv"]}`,children:m?"Movie":"TV Show"})]}),rr.length>0&&t("div",{className:h.genreRow,children:rr.map(e=>t("span",{className:h.genreChip,children:e},e))}),Nn.length>0&&t("div",{className:h.metaRow,children:Nn.map((e,n)=>t("span",{children:[e,n<Nn.length-1?" · ":""]},`${e}-${n}`))})]}),t("div",{className:be.controls,children:[t("button",{className:`${be.watchlistBtn} ${Gt?be.inList:""}`,onClick:pr,disabled:hr,type:"button",children:[Gt?t($,{name:"check",size:14}):t($,{name:"plus",size:14}),Gt?"In Watchlist":"Add to Watchlist"]}),!m&&t(Ve,{children:[t("button",{className:`${be.watchlistBtn}`,onClick:()=>bn!=null&&bt(String(bn)),disabled:bn==null,type:"button",children:[t($,{name:"chevron-left",size:14}),"Prev EP"]}),t("button",{className:`${be.watchlistBtn}`,onClick:()=>N!=null&&bt(String(N)),disabled:N==null,type:"button",children:["Next EP",t($,{name:"chevron-right",size:14})]}),!m&&t("button",{className:`${be.watchlistBtn} ${be.modeToggleBtn} ${pt?be.modeToggleActive:""}`,onClick:()=>_r(!pt),type:"button","aria-pressed":pt,children:pt?"AUTOPLAY: ON":"AUTOPLAY: OFF"}),yr&&t("button",{className:`${be.watchlistBtn}`,onClick:()=>Cn(!0),type:"button",children:[t($,{name:"list-ul",size:14}),"Episodes"]})]})]})]})]}),t("div",{className:be.descriptionSection,children:[t("h3",{children:"Synopsis"}),t(xs,{text:r.overview,emptyText:"No description available."})]}),t("button",{className:be.detailsToggleBtn,onClick:()=>vr(!en),type:"button",children:[en?t($,{name:"chevron-up"}):t($,{name:"chevron-down"}),en?"Hide Details":"Show Details"]}),en&&t("div",{className:h.detailGrid,children:[r.first_air_date&&t("div",{className:h.detailItem,children:[t("strong",{children:m?"Release Date":"First Air Date"}),t("span",{children:r.first_air_date})]}),r.last_air_date&&t("div",{className:h.detailItem,children:[t("strong",{children:"Last Air Date"}),t("span",{children:r.last_air_date})]}),r.networks&&r.networks.length>0&&t("div",{className:h.detailItem,children:[t("strong",{children:"Networks"}),t("span",{children:r.networks.map(e=>e.name).join(", ")})]}),r.created_by&&r.created_by.length>0&&t("div",{className:h.detailItem,children:[t("strong",{children:"Created By"}),t("span",{children:r.created_by.map(e=>e.name).join(", ")})]}),r.episode_run_time&&r.episode_run_time.length>0&&t("div",{className:h.detailItem,children:[t("strong",{children:"Episode Runtime"}),t("span",{children:[r.episode_run_time[0]," min"]})]}),r.imdb_id&&t("div",{className:h.detailItem,children:[t("strong",{children:"IMDb"}),t("a",{href:`https://www.imdb.com/title/${r.imdb_id}`,target:"_blank",rel:"noopener noreferrer",className:h.imdbLink,children:"View on IMDb"})]})]})]}),!m&&t(ms,{isOpen:br,onClose:()=>Cn(!1),episodes:nr,currentEpisode:String(g),watchedEpisodes:tr,onEpisodeClick:bt,header:an}),r?.adult&&!je&&t(cn,{isOpen:!0,title:"Content Warning",onClose:sn,children:t("div",{style:{padding:"1rem",textAlign:"center"},children:[t("p",{children:"This title contains mature content intended for adult audiences."}),t("p",{children:["By proceeding, you confirm that you are ",t("strong",{children:"18 years of age or older"})," ","(or the age of majority in your jurisdiction) and wish to view this content."]}),t("div",{style:{marginTop:"1rem",display:"flex",gap:"10px",justifyContent:"center"},children:[t(_e,{variant:"secondary",onClick:sn,children:"Go Back"}),t(_e,{onClick:Le,children:"I'm 18+, Continue"})]})]})}),t(cn,{isOpen:Ne,onClose:Gn,title:"Resume Watching",children:t("div",{style:{padding:"1rem",textAlign:"center"},children:[t("p",{children:["You were at ",t("strong",{children:Tr(ht)})]}),t("div",{style:{marginTop:"1rem",display:"flex",gap:"10px",justifyContent:"center"},children:[t(_e,{variant:"secondary",onClick:Gn,children:"Start from Beginning"}),t(_e,{onClick:Ir,children:"Resume"})]})]})}),t(cn,{isOpen:ke,onClose:Br,title:m?"Movie Completed":"Episode Completed",children:t("div",{style:{padding:"1rem",textAlign:"center"},children:[t("p",{children:m?t(Ve,{children:"You've already watched this movie."}):t(Ve,{children:["You've already watched"," ",t("strong",{children:["Season ",l," Episode ",g]}),". Would you like to continue?"]})}),t("div",{style:{marginTop:"1rem",display:"flex",gap:"10px",justifyContent:"center",flexWrap:"wrap"},children:[t(_e,{variant:"secondary",onClick:Mr,children:"Replay"}),N!=null?t(_e,{onClick:Jn,children:["Watch EP ",N]}):ge!=null?t(_e,{onClick:Xn,children:["Start Season ",ge]}):null]})]})}),t(cn,{isOpen:$e,onClose:()=>Je(!1),title:m?"Movie Completed!":ge!=null?"Season Complete!":"Show Completed!",children:t("div",{style:{padding:"1rem",textAlign:"center"},children:[t($,{name:"check-circle",size:48,style:{color:"var(--accent-lighter)",marginBottom:"0.5rem"}}),t("p",{style:{fontSize:"1.1rem",fontWeight:600},children:gr}),t("p",{style:{color:"var(--text-secondary)"},children:m?"You finished this movie!":ge!=null?`You finished Season ${l}.`:"Congratulations! You finished all available episodes."}),t("div",{style:{marginTop:"1rem",display:"flex",gap:"10px",justifyContent:"center",flexWrap:"wrap"},children:[t(_e,{variant:"secondary",onClick:()=>Je(!1),children:"Close"}),ge!=null?t(_e,{onClick:Xn,children:["Start Season ",ge]}):t(Ve,{children:[t(_e,{onClick:()=>{Je(!1),sn()},children:"Back to Details"}),Gt&&t(_e,{onClick:Ar,disabled:Qt.isPending,children:Qt.isPending?"Saving...":"Move to Completed"})]})]})]})}),!r&&t("div",{className:h.statusMsg,children:[t($,{name:"spinner",className:h.spinner})," Loading..."]})]})]})};export{Eo as default};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
._fullscreenFlat_1deq4_1,._fullscreenFlat_1deq4_1 ._video_1deq4_2,._fullscreenFlat_1deq4_1 ._delayCanvas_1deq4_3,._fullscreenFlat_1deq4_1 ._subtitleOverlay_1deq4_4{border-radius:0}@media(max-width:900px)and (orientation:landscape){._fullscreenFlat_1deq4_1{border-radius:0}}@container (max-width: 26rem){._timeDisplay_1deq4_15{display:none}._bottomControls_1deq4_19{padding:.75rem}._leftControls_1deq4_23,._rightControls_1deq4_24{gap:.2rem}._controlBtn_1deq4_28{padding:5px;min-width:2.125rem;min-height:2.125rem}}._video_1deq4_2{position:absolute;top:0;left:0;width:100%;height:100%;object-fit:contain;display:block;background:#000;border:none}._controlsOverlay_1deq4_47{position:absolute;inset:0;opacity:1;transition:opacity .4s cubic-bezier(.4,0,.2,1);z-index:50;pointer-events:none;display:flex;flex-direction:column;justify-content:space-between;background:radial-gradient(circle,transparent 20%,rgba(0,0,0,.4) 100%)}._controlsOverlay_1deq4_47>*{pointer-events:auto}._controlsOverlay_1deq4_47._hidden_1deq4_67{opacity:0;pointer-events:none;cursor:none}._topControls_1deq4_73{position:absolute;top:0;left:0;right:0;display:flex;align-items:center;gap:var(--space-6);background:linear-gradient(to bottom,rgba(0,0,0,.85) 0%,rgba(0,0,0,.45) 60%,transparent 100%);padding:var(--space-8) var(--space-10);transition:transform .4s cubic-bezier(.2,.8,.2,1);z-index:10;pointer-events:auto}._backBtn_1deq4_93{background:transparent;border:none;color:#ffffffd9;font-size:1.6rem;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all var(--transition-fast);padding:8px;border-radius:50%}._backBtn_1deq4_93:hover{background:#0f0f1199;color:#fff;transform:scale(1.1)}._videoTitleInfo_1deq4_113{display:flex;flex-direction:column;gap:.25rem}._animeTitle_1deq4_119{font-size:1.4rem;font-weight:800;color:#fff;letter-spacing:-.01em;text-shadow:0 2px 4px rgba(0,0,0,.5)}._episodeLabel_1deq4_127{font-size:.85rem;font-weight:600;color:#ffffffbf;text-shadow:0 2px 4px rgba(0,0,0,.5)}._speedBoostBadge_1deq4_134{position:absolute;top:1.5rem;left:50%;transform:translate(-50%);display:flex;align-items:center;gap:.7rem;padding:.65rem 1rem;border:1px solid color-mix(in srgb,var(--accent) 45%,transparent);border-radius:var(--radius-md);background:#121216eb;color:#f5f3ff;font-size:.85rem;font-weight:800;letter-spacing:.06em;z-index:11;pointer-events:none}._speedBoostBadge_1deq4_134 span{line-height:1}._speedBoostBadge_1deq4_134 svg{display:block;flex-shrink:0}._bottomControls_1deq4_19{position:absolute;bottom:0;left:0;right:0;background:linear-gradient(to top,rgba(0,0,0,.85) 0%,rgba(0,0,0,.45) 60%,transparent 100%);padding:clamp(1rem,4cqw,2.5rem) clamp(1rem,4cqw,2.5rem) clamp(.75rem,2.5cqw,1.5rem);z-index:10;pointer-events:auto;transition:transform .4s cubic-bezier(.2,.8,.2,1)}._progressBarContainer_1deq4_180{width:100%;height:20px;display:flex;align-items:center;cursor:pointer;position:relative;touch-action:pan-y}@media(pointer:coarse){._progressBarContainer_1deq4_180{height:28px}._progressBarContainer_1deq4_180 ._progressBar_1deq4_180{height:6px}._progressBarContainer_1deq4_180 ._thumb_1deq4_199{opacity:1}}._progressBar_1deq4_180{width:100%;height:4px;background:#fff3;border-radius:2px;position:relative;transition:height .15s ease}._progressBarContainer_1deq4_180:hover ._progressBar_1deq4_180{height:6px}._bufferedBar_1deq4_217{position:absolute;top:0;left:0;height:100%;background:#ffffff4d;border-radius:2px;pointer-events:none}._watchedBar_1deq4_227{position:absolute;top:0;left:0;height:100%;background:var(--accent);border-radius:2px;pointer-events:none}._thumb_1deq4_199{position:absolute;top:50%;width:12px;height:12px;background:#fff;border-radius:50%;transform:translate(-50%,-50%);box-shadow:0 0 4px #0006;pointer-events:none;opacity:0;transition:opacity .15s ease}._progressBarContainer_1deq4_180:hover ._thumb_1deq4_199,._progressBarContainer_1deq4_180._scrubbing_1deq4_252 ._thumb_1deq4_199{opacity:1}._timeBubble_1deq4_256{position:absolute;bottom:24px;background:#000000d9;color:#fff;padding:4px 8px;border-radius:4px;font-size:.8rem;font-weight:500;transform:translate(-50%);pointer-events:none;white-space:nowrap}._bottomControlsRow_1deq4_270{display:flex;align-items:center;justify-content:space-between;gap:.75rem;margin-top:8px}._leftControls_1deq4_23{display:flex;align-items:center;gap:1rem;gap:clamp(.5rem,2.5cqw,1rem);min-width:0}._rightControls_1deq4_24{display:flex;align-items:center;gap:.75rem;gap:clamp(.5rem,2cqw,.75rem)}._nextEpisodeBtn_1deq4_293{background:#ffffff0f;border:1px solid rgba(255,255,255,.12);border-radius:var(--radius-md);padding:.45rem .8rem;color:#fffc;font-weight:600;font-size:.9rem;display:inline-flex;align-items:center;gap:.5rem;cursor:pointer;transition:all var(--transition-fast);white-space:nowrap}._nextEpisodeBtn_1deq4_293:hover{background:#ffffff1a;color:#fff;transform:translateY(-1px);border-color:#fff3}._controlBtn_1deq4_28{background:transparent;border:none;color:#ffffffd9;font-size:1.1rem;cursor:pointer;display:flex;align-items:center;justify-content:center;padding:8px;border-radius:4px;transition:all var(--transition-fast);min-width:2.125rem;min-height:2.125rem;flex:0 0 auto}@media(pointer:coarse){._controlBtn_1deq4_28{padding:10px;min-width:2.75rem;min-height:2.75rem}}._controlBtn_1deq4_28:hover{color:#fff;background:#ffffff1a}._controlBtn_1deq4_28._active_1deq4_346{color:var(--accent)}._volumeContainer_1deq4_350{display:flex;align-items:center;position:relative}._volumeSlider_1deq4_356{width:0;opacity:0;transform:scaleX(0);transform-origin:left;transition:all .3s cubic-bezier(.4,0,.2,1);cursor:pointer;margin-left:8px;-webkit-appearance:none;appearance:none;background:transparent;height:20px}._volumeContainer_1deq4_350:hover ._volumeSlider_1deq4_356,._volumeSlider_1deq4_356:focus{width:80px;opacity:1;transform:scaleX(1)}._volumeSlider_1deq4_356::-webkit-slider-runnable-track{width:100%;height:4px;background:linear-gradient(to right,var(--accent) 0%,var(--accent) var(--volume-percent, 0%),rgba(255,255,255,.2) var(--volume-percent, 0%),rgba(255,255,255,.2) 100%);border-radius:4px}._volumeSlider_1deq4_356::-moz-range-track{width:100%;height:4px;background:#fff3;border-radius:4px}._volumeSlider_1deq4_356::-moz-range-progress{height:4px;background:var(--accent);border-radius:4px}._volumeSlider_1deq4_356::-webkit-slider-thumb{-webkit-appearance:none;height:12px;width:12px;border-radius:50%;background:#fff;margin-top:-4px;box-shadow:0 0 5px #00000080}._timeDisplay_1deq4_15{color:#fffc;font-size:.85rem;font-weight:500;font-variant-numeric:tabular-nums;min-width:100px}._settingsPanel_1deq4_421{position:absolute;bottom:clamp(6rem,15vh,8rem);right:clamp(1rem,3vw,2rem);background:#141418f2;border:1px solid var(--border-secondary);border-radius:var(--radius-md);width:max-content;min-width:12rem;max-width:min(19rem,calc(100vw - 1.5rem));font-size:.85rem;z-index:60;-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);box-shadow:0 8px 32px #00000080;display:flex;flex-direction:column;overflow:hidden}._settingsHeader_1deq4_440{display:flex;align-items:center;padding:.55rem .75rem;border-bottom:1px solid var(--border-secondary);gap:.625rem}._settingsBackBtn_1deq4_448{background:transparent;border:none;color:#ffffffd9;cursor:pointer;display:flex;align-items:center;justify-content:center;padding:4px;border-radius:4px;transition:all var(--transition-fast)}._settingsBackBtn_1deq4_448:hover{background:#ffffff1a;color:#fff}._settingsTitle_1deq4_466{font-size:.72rem;font-weight:700;text-transform:uppercase;letter-spacing:.05em;color:var(--text-primary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}._settingsContent_1deq4_477{overflow-y:auto;padding:.375rem;display:flex;flex-direction:column;gap:.125rem;max-height:min(56vh,22rem)}._currentValue_1deq4_486{color:var(--text-tertiary);font-size:.8rem;font-weight:500}._settingsLabel_1deq4_492{padding:var(--space-2) var(--space-4);font-size:.75rem;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--text-secondary)}._menuItem_1deq4_501{display:flex;align-items:center;justify-content:space-between;gap:.75rem;width:100%;min-height:2rem;padding:.5rem .75rem;background:transparent;border:none;color:var(--text-primary);font-size:.85rem;cursor:pointer;border-radius:var(--radius-sm);transition:background-color var(--transition-fast);text-align:left;white-space:nowrap}._menuItem_1deq4_501:hover{background:var(--bg-tertiary)}._menuItem_1deq4_501._active_1deq4_346{background:var(--bg-tertiary);color:var(--accent)}._currentValue_1deq4_486{color:var(--text-secondary);font-size:.85rem}._menuNote_1deq4_534{padding:.5rem .75rem;font-size:.7rem;line-height:1.5;color:var(--text-tertiary);white-space:normal;overflow-wrap:break-word}._spin_1deq4_543{animation:_spin_1deq4_543 1s linear infinite}@keyframes _spin_1deq4_543{0%{transform:rotate(0)}to{transform:rotate(360deg)}}@media(pointer:coarse){._settingsPanel_1deq4_421{font-size:.9rem;bottom:calc(env(safe-area-inset-bottom,0px) + 7rem);right:max(.75rem,env(safe-area-inset-right,0px))}._menuItem_1deq4_501{min-height:2.75rem}._settingsContent_1deq4_477{max-height:52vh}}@media(max-width:480px){._settingsPanel_1deq4_421{min-width:9rem;max-width:calc(100vw - 2rem);font-size:.75rem;bottom:calc(env(safe-area-inset-bottom,0px) + 6rem)}._settingsHeader_1deq4_440{padding:.4rem .625rem}._settingsTitle_1deq4_466{font-size:.64rem}._menuItem_1deq4_501{min-height:2.25rem;padding:.45rem .625rem;font-size:.75rem;gap:.5rem}._settingsContent_1deq4_477{max-height:min(40vh,15rem)}}@media(max-height:500px)and (orientation:landscape){._settingsPanel_1deq4_421{bottom:7.25rem;right:2rem;min-width:10rem;max-width:min(15rem,calc(100vw - 1.5rem));font-size:.8rem}._menuItem_1deq4_501{min-height:2.5rem;font-size:.8rem}._settingsContent_1deq4_477{max-height:50vh;overflow-y:auto}}._page_263ma_1{max-width:1200px;margin:0 auto;padding:1.5rem}._header_263ma_7{margin-bottom:1.5rem}._pageTitle_263ma_11{font-size:1.75rem;font-weight:700;margin:0 0 1rem;display:flex;align-items:center;gap:.5rem;color:var(--text-primary)}._searchForm_263ma_21{display:flex;gap:.5rem;max-width:600px}._searchInput_263ma_27{flex:1;padding:.6rem 1rem;border-radius:8px;border:1px solid var(--border-primary);background:var(--bg-elevated);color:var(--text-primary);font-size:.95rem;outline:none}._searchInput_263ma_27:focus{border-color:var(--accent)}._searchBtn_263ma_42{padding:.6rem 1rem;border-radius:8px;border:none;background:var(--accent);color:#fff;cursor:pointer;display:flex;align-items:center;justify-content:center}._searchBtn_263ma_42:hover{opacity:.9}._suggestions_263ma_58{display:flex;flex-wrap:wrap;gap:.5rem;margin-bottom:1.5rem}._suggestionPill_263ma_65{padding:.4rem .8rem;border-radius:20px;border:1px solid var(--border-primary);background:var(--bg-elevated);color:var(--text-secondary);cursor:pointer;font-size:.85rem;transition:all .15s ease}._suggestionPill_263ma_65:hover{border-color:var(--accent);color:var(--text-primary)}._statusMsg_263ma_81{padding:1rem;text-align:center;color:var(--text-secondary)}._statusMsg_263ma_81._error_263ma_87{color:#f87171}._statusMsg_263ma_81._textOnly_263ma_91{background:none;padding:.5rem}._spinner_263ma_96{animation:_spin_263ma_96 1s linear infinite;margin-right:.5rem}@keyframes _spin_263ma_96{0%{transform:rotate(0)}to{transform:rotate(360deg)}}._grid_263ma_110{display:grid;grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:1rem}._skeletonCard_263ma_116{border-radius:8px;overflow:hidden}._skeletonThumb_263ma_121{aspect-ratio:2 / 3;background:var(--bg-secondary);border-radius:8px}._skeletonLine_263ma_127{height:12px;background:var(--bg-secondary);border-radius:4px;margin-top:.5rem}._shimmer_263ma_134{background:linear-gradient(90deg,var(--bg-secondary) 25%,var(--bg-elevated) 50%,var(--bg-secondary) 75%);background-size:200% 100%;animation:_shimmer_263ma_134 1.5s infinite}@keyframes _shimmer_263ma_134{0%{background-position:200% 0}to{background-position:-200% 0}}._backBtn_263ma_154{display:inline-flex;align-items:center;gap:.4rem;padding:.4rem .8rem;border-radius:6px;border:1px solid var(--border-primary);background:var(--bg-elevated);color:var(--text-primary);cursor:pointer;font-size:.85rem;margin-bottom:1rem}._backBtn_263ma_154:hover{border-color:var(--accent)}._meta_263ma_172{display:flex;align-items:center;gap:.75rem;margin:.5rem 0;flex-wrap:wrap}._headerActions_263ma_180{display:flex;gap:.5rem;margin:.75rem 0 0;flex-wrap:wrap}._watchlistBtn_263ma_187{display:inline-flex;align-items:center;gap:.4rem;padding:.5rem 1rem;border-radius:6px;border:1px solid var(--border-primary);background:var(--bg-elevated);color:var(--text-primary);cursor:pointer;font-size:.85rem;font-weight:600}._watchlistBtn_263ma_187:hover:not(:disabled){border-color:var(--accent)}._watchlistBtn_263ma_187._active_263ma_205{border-color:var(--color-success);color:var(--color-success)}._watchlistBtn_263ma_187:disabled{opacity:.6;cursor:default}._year_263ma_215{color:var(--text-secondary);font-size:.9rem}._rating_263ma_220{display:inline-flex;align-items:center;gap:4px;color:var(--accent-lighter);font-weight:600;font-size:.9rem}._typeBadge_263ma_229{padding:2px 10px;border-radius:4px;font-size:.75rem;font-weight:600;text-transform:uppercase}._typeBadge_263ma_229._tv_263ma_237{background:color-mix(in srgb,var(--accent) 20%,transparent);color:var(--accent)}._typeBadge_263ma_229._movie_263ma_242{background:color-mix(in srgb,var(--accent-lighter) 20%,transparent);color:var(--accent-lighter)}._typeBadge_263ma_229._tvSeries_263ma_247,._typeBadge_263ma_229._tvMiniSeries_263ma_252{background:color-mix(in srgb,var(--accent) 20%,transparent);color:var(--accent)}._overview_263ma_257{color:var(--text-secondary);line-height:1.6;margin:.5rem 0 0;max-width:800px}._controls_263ma_264{display:flex;flex-wrap:wrap;gap:1rem;margin:1rem 0;align-items:flex-end}._controlLabel_263ma_272{display:flex;flex-direction:column;gap:.3rem;font-size:.8rem;color:var(--text-secondary);text-transform:uppercase;letter-spacing:.05em}._select_263ma_282{padding:.5rem .75rem;border-radius:6px;border:1px solid var(--border-primary);background:var(--bg-elevated);color:var(--text-primary);font-size:.9rem;cursor:pointer;min-width:160px}._playerSection_263ma_293{margin:1.5rem 0;border-radius:0;overflow:hidden;overflow:clip;isolation:isolate;background:#000;position:relative}._video_263ma_297{position:absolute;top:0;left:0;width:100%;height:100%;object-fit:contain;display:block;background:#000;border:none;border-radius:inherit}._videoHidden_263ma_319{opacity:0;pointer-events:none}._delayCanvas_263ma_324{position:absolute;top:0;left:0;width:100%;height:100%;object-fit:contain;opacity:0;pointer-events:none;z-index:2;border-radius:inherit}._delayCanvasActive_263ma_337{opacity:1}._subtitleOverlay_263ma_341{position:absolute;top:0;left:0;width:100%;height:100%;z-index:3;pointer-events:none;overflow:hidden;border-radius:inherit}@media(max-width:900px)and (orientation:landscape){._playerSection_263ma_293{margin:0;border-radius:0}}._videoIframe_263ma_360{width:100%;aspect-ratio:16 / 9;min-height:400px;border:none;background:#000}._retryButton_263ma_368{margin-left:8px;padding:4px 10px;border-radius:4px;border:1px solid var(--border-primary);background:var(--bg-elevated);color:var(--text-primary);cursor:pointer;font-size:.8rem}._retryButton_263ma_368:hover{border-color:var(--accent)}._sidebarSeason_263ma_383{display:flex;flex-direction:column;gap:.4rem;font-size:.75rem;font-weight:700;color:var(--text-tertiary);text-transform:uppercase;letter-spacing:.1em;margin-bottom:var(--space-4)}._sidebarSeason_263ma_383 ._select_263ma_282{width:100%}._mobileSeasonRow_263ma_399{display:none}@media(max-width:768px){._mobileSeasonRow_263ma_399{display:block}._mobileSeasonRow_263ma_399 ._sidebarSeason_263ma_383{margin-bottom:0}}._sidebarPosterCard_263ma_413{padding:1rem;display:flex;flex-direction:column;gap:.75rem}._sidebarPosterCard_263ma_413 img{width:100%;border-radius:var(--radius-md);aspect-ratio:2 / 3;object-fit:cover;background:#ffffff0f}._sidebarPosterMeta_263ma_428{display:flex;flex-direction:column;gap:.25rem}._sidebarPosterMeta_263ma_428 strong{color:var(--text-primary);font-size:.95rem;line-height:1.3}._sidebarPosterMeta_263ma_428 span{color:var(--text-secondary);font-size:.85rem}._providerSelectWrap_263ma_445{display:flex;align-items:flex-end;gap:1rem;flex-wrap:wrap;min-width:0}._providerSelectWrap_263ma_445 ._backBtn_263ma_154{margin-bottom:0}._genreRow_263ma_457{display:flex;flex-wrap:wrap;gap:.4rem;margin-top:.6rem}._genreChip_263ma_464{padding:.2rem .7rem;border-radius:20px;border:1px solid var(--glass-border);background:#ffffff0d;color:var(--text-secondary);font-size:.78rem;font-weight:600}._metaRow_263ma_474{display:flex;flex-wrap:wrap;gap:.35rem;margin-top:.6rem;color:var(--text-secondary);font-size:.9rem}._detailGrid_263ma_483{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:.75rem;margin-top:1rem}._detailItem_263ma_490{display:flex;flex-direction:column;gap:.2rem;padding:.75rem 1rem;border-radius:var(--radius-md);border:1px solid var(--glass-border);background:#ffffff08}._detailItem_263ma_490 strong{font-size:.75rem;text-transform:uppercase;letter-spacing:.06em;color:var(--text-tertiary)}._detailItem_263ma_490 span{color:var(--text-primary);font-size:.9rem}._imdbLink_263ma_512{color:var(--accent-light);font-size:.9rem;text-decoration:none}._imdbLink_263ma_512:hover{text-decoration:underline}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{n as z,a as E,o as R,d as l,h as C,T,u as e,g as F}from"./vendor-core-8R2fFbyN.js";import{I as a}from"./vendor-icons-CDMuNDeL.js";import{B as w}from"./index-D2RkUJIN.js";import{u as q}from"./useMatureConsent-CT7-KfzG.js";import{n as j,d as U,e as H,f as J,t as I}from"./useTvLibrary-_Pqm8re1.js";import"./vendor-query-BXRECSI6.js";const K="_container_198yg_1",Q="_heroSection_198yg_11",X="_bannerContainer_198yg_17",Y="_banner_198yg_17",Z="_bannerOverlay_198yg_37",ee="_heroContent_198yg_49",ne="_posterContainer_198yg_62",te="_poster_198yg_62",se="_infoGlass_198yg_86",ae="_title_198yg_100",oe="_quickMeta_198yg_110",ie="_metaItem_198yg_118",re="_iconStar_198yg_126",le="_iconTv_198yg_130",ce="_iconType_198yg_134",de="_genres_198yg_139",me="_genre_198yg_139",_e="_metaRow_198yg_156",he="_metaValue_198yg_164",ge="_metaDivider_198yg_168",ve="_synopsisSection_198yg_173",pe="_sectionTitleSmall_198yg_179",ye="_synopsis_198yg_173",ue="_actions_198yg_196",ke="_watchBtn_198yg_202",Ne="_watchlistBtn_198yg_226",be="_active_198yg_250",Se="_seasonPickerLabel_198yg_256",fe="_seasonSelect_198yg_267",Ce="_detailsToggleBtn_198yg_286",Te="_expandedContent_198yg_304",we="_detailGrid_198yg_309",Ie="_detailItem_198yg_315",Be="_imdbLink_198yg_333",De="_heroSkeleton_198yg_343",Me="_skeletonBanner_198yg_347",Le="_skeletonContent_198yg_354",$e="_skeletonPoster_198yg_365",Pe="_skeletonInfo_198yg_373",Ge="_skeletonTitle_198yg_380",xe="_skeletonMeta_198yg_388",Oe="_skeletonDesc_198yg_396",Ae="_skeletonActions_198yg_404",Ve="_errorState_198yg_413",We="_matureOverlay_198yg_434",ze="_matureContent_198yg_444",n={container:K,heroSection:Q,bannerContainer:X,banner:Y,bannerOverlay:Z,heroContent:ee,posterContainer:ne,poster:te,infoGlass:se,title:ae,quickMeta:oe,metaItem:ie,iconStar:re,iconTv:le,iconType:ce,genres:de,genre:me,metaRow:_e,metaValue:he,metaDivider:ge,synopsisSection:ve,sectionTitleSmall:pe,synopsis:ye,actions:ue,watchBtn:ke,watchlistBtn:Ne,active:be,seasonPickerLabel:Se,seasonSelect:fe,detailsToggleBtn:Ce,expandedContent:Te,detailGrid:we,detailItem:Ie,imdbLink:Be,heroSkeleton:De,skeletonBanner:Me,skeletonContent:Le,skeletonPoster:$e,skeletonInfo:Pe,skeletonTitle:Ge,skeletonMeta:xe,skeletonDesc:Oe,skeletonActions:Ae,errorState:Ve,matureOverlay:We,matureContent:ze};function He(){const{id:o}=z(),c=E(),[B]=R(),i=j(B.get("type")),{hasConsent:D}=q(),[t,M]=l(null),[L,m]=l(!0),[p,y]=l(""),[d,$]=l(!1),[_,P]=l(1),G=U(i,o),x=Number(o),{data:O}=H(t?G:void 0),h=!!O?.inLibrary,{toggle:A}=J();C(()=>{o&&(m(!0),y(""),fetch(`/api/tv/details/${i}/${o}`).then(s=>{if(!s.ok)throw new Error("Failed to load details");return s.json()}).then(s=>{M(s),m(!1)}).catch(s=>{y(s.message),m(!1)}))},[o,i]),C(()=>{document.title=t?.title?`${t.title} - dango`:"TV & Movies - dango"},[t?.title]);const V=()=>{!o||!t||c(r?I(i,o):I(i,o,_,1))},W=()=>{t&&A({tmdbId:x,mediaType:i,title:t.title,poster:t.poster,year:t.year,adult:t.adult})},r=i==="movie",g=!r,u=t?.backdrop?`https://image.tmdb.org/t/p/w1280${t.backdrop}`:"",k=t?.poster?`https://image.tmdb.org/t/p/w500${t.poster}`:"",N=T(()=>t?.genres?t.genres.map(s=>s.name).slice(0,5):[],[t?.genres]),b=T(()=>t?.seasons?t.seasons.filter(s=>s.season_number>0):[],[t?.seasons]),v=[t?.year,t?.status,g&&t?.number_of_seasons?`${t.number_of_seasons} Season${t.number_of_seasons>1?"s":""}`:null,g&&t?.number_of_episodes?`${t.number_of_episodes} Episodes`:null].filter(Boolean);if(L)return e("div",{className:n.container,children:e("div",{className:n.heroSkeleton,children:[e("div",{className:n.skeletonBanner}),e("div",{className:n.skeletonContent,children:[e("div",{className:n.skeletonPoster}),e("div",{className:n.skeletonInfo,children:[e("div",{className:n.skeletonTitle}),e("div",{className:n.skeletonMeta}),e("div",{className:n.skeletonDesc}),e("div",{className:n.skeletonActions})]})]})]})});if(p||!t)return e("div",{className:n.container,children:e("div",{className:n.errorState,children:[e(a,{name:"exclamation-triangle",size:48}),e("h2",{children:"Failed to load details"}),e("p",{children:p||"Something went wrong"}),e(w,{onClick:()=>c(-1),children:"Go Back"})]})});const S=t.adult&&!D;return e("div",{className:n.container,style:S?{filter:"blur(14px)",pointerEvents:"none",userSelect:"none"}:void 0,children:[e("div",{className:n.heroSection,children:[e("div",{className:n.bannerContainer,children:[u&&e("div",{className:n.banner,style:{backgroundImage:`url(${u})`}}),e("div",{className:n.bannerOverlay})]}),e("div",{className:n.heroContent,children:[e("div",{className:n.posterContainer,children:k&&e("img",{src:k,alt:t.title,className:n.poster,decoding:"async"})}),e("div",{className:n.infoGlass,children:[e("div",{className:n.topInfo,children:[e("h1",{className:n.title,children:t.title}),e("div",{className:n.quickMeta,children:[t.vote_average!=null&&t.vote_average>0&&e("div",{className:n.metaItem,children:[e(a,{name:"star",className:n.iconStar}),e("span",{children:Number(t.vote_average).toFixed(1)})]}),e("div",{className:n.metaItem,children:[e(a,{name:"tv",className:n.iconTv}),e("span",{children:r?"Movie":"TV Show"})]}),t.vote_count!=null&&e("div",{className:n.metaItem,children:[e(a,{name:"users",className:n.iconType}),e("span",{children:[t.vote_count.toLocaleString()," votes"]})]})]}),N.length>0&&e("div",{className:n.genres,children:N.map(s=>e("span",{className:n.genre,children:s},s))})]}),v.length>0&&e("div",{className:n.metaRow,children:v.map((s,f)=>e(F.Fragment,{children:[e("span",{className:n.metaValue,children:s}),f<v.length-1&&e("span",{className:n.metaDivider,children:"·"})]},f))}),t.overview&&e("div",{className:n.synopsisSection,children:[e("h2",{className:n.sectionTitleSmall,children:"Synopsis"}),e("p",{className:n.synopsis,children:t.overview})]}),e("div",{className:n.actions,children:[e("button",{className:n.watchBtn,onClick:V,children:[e(a,{name:"play",size:14}),r?"Watch Now":`Start S${_}`]}),e("button",{className:`${n.watchlistBtn} ${h?n.active:""}`,onClick:W,children:[h?e(a,{name:"check",size:14}):e(a,{name:"plus",size:14}),h?"In Watchlist":"Add to Watchlist"]})]}),g&&b.length>1&&e("label",{className:n.seasonPickerLabel,children:[e("span",{children:"Season"}),e("select",{value:_,onChange:s=>P(parseInt(s.target.value,10)||1),className:n.seasonSelect,children:b.map(s=>e("option",{value:s.season_number,children:["Season ",s.season_number," (",s.episode_count," Episodes)"]},s.season_number))})]}),e("button",{className:n.detailsToggleBtn,onClick:()=>$(!d),children:[d?e(a,{name:"chevron-up"}):e(a,{name:"chevron-down"}),d?"Hide Details":"Show Details"]}),d&&e("div",{className:n.expandedContent,children:e("div",{className:n.detailGrid,children:[t.first_air_date&&e("div",{className:n.detailItem,children:[e("strong",{children:r?"Release Date":"First Air Date"}),e("span",{children:t.first_air_date})]}),t.last_air_date&&e("div",{className:n.detailItem,children:[e("strong",{children:"Last Air Date"}),e("span",{children:t.last_air_date})]}),t.networks&&t.networks.length>0&&e("div",{className:n.detailItem,children:[e("strong",{children:"Networks"}),e("span",{children:t.networks.map(s=>s.name).join(", ")})]}),t.created_by&&t.created_by.length>0&&e("div",{className:n.detailItem,children:[e("strong",{children:"Created By"}),e("span",{children:t.created_by.map(s=>s.name).join(", ")})]}),t.episode_run_time&&t.episode_run_time.length>0&&e("div",{className:n.detailItem,children:[e("strong",{children:"Episode Runtime"}),e("span",{children:[t.episode_run_time[0]," min"]})]}),t.imdb_id&&e("div",{className:n.detailItem,children:[e("strong",{children:"IMDb"}),e("a",{href:`https://www.imdb.com/title/${t.imdb_id}`,target:"_blank",rel:"noopener noreferrer",className:n.imdbLink,children:"View on IMDb"})]})]})})]})]})]}),S&&e("div",{className:n.matureOverlay,children:e("div",{className:n.matureContent,children:[e(a,{name:"exclamation-triangle",size:48}),e("h2",{children:"Content Warning"}),e("p",{children:"This content may contain mature themes."}),e(w,{onClick:()=>c("/"),children:"Go Back"})]})})]})}export{He as default};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{A as T,d as v,h as P,T as z,u as e,S as W,L as p}from"./vendor-core-8R2fFbyN.js";import{I as i}from"./vendor-icons-CDMuNDeL.js";import{f as B,d as D,c as L,t as M}from"./useTvLibrary-_Pqm8re1.js";import{p as s,a as S}from"./MediaCard-CAzoxsRS.js";const E=({item:a,anchorRect:m,onMouseEnter:u,onMouseLeave:g,onRequestClose:l})=>{const d=T(!1),[n,f]=v(null),[N,c]=v(!0),{toggle:y,bookmarkedIds:_}=B(),w=D(a.type,a.id),o=_.has(w);P(()=>{let t=!1;return c(!0),fetch(`/api/tv/details/${a.type}/${a.id}`).then(r=>{if(!r.ok)throw new Error("Failed");return r.json()}).then(r=>{t||(f({overview:r.overview,genres:r.genres,vote_average:r.vote_average,vote_count:r.vote_count,status:r.status,number_of_seasons:r.number_of_seasons,number_of_episodes:r.number_of_episodes}),c(!1))}).catch(()=>{t||c(!1)}),()=>{t=!0}},[a.id,a.type]);const h=z(()=>n?.genres?n.genres.slice(0,4).map(t=>t.name):[],[n?.genres]),I=L(a.type,a.id),b=M(a.type,a.id),k=t=>{t.preventDefault(),t.stopPropagation(),y({tmdbId:a.id,mediaType:a.type,title:a.title,poster:a.image,year:a.year})};return e(S,{anchorRect:m,onMouseEnter:()=>{d.current=!0,u()},onMouseLeave:()=>{d.current=!1,g()},onRequestClose:l,children:[e("div",{className:s.header,children:e("div",{className:s.title,children:a.title})}),e("div",{className:s.body,children:N?e("div",{className:s.loading,children:[e("div",{className:s.spinner}),e("span",{children:"Fetching details..."})]}):e(W,{children:[e("div",{className:s.metaRow,children:[n?.vote_average!=null&&n.vote_average>0&&e("div",{className:s.metaItem,children:[e(i,{name:"star",className:s.scoreIcon,size:14}),e("span",{children:Number(n.vote_average).toFixed(1)})]}),e("div",{className:s.metaItem,children:[e(i,{name:"tv",size:14}),e("span",{children:a.type==="tv"?"TV Show":"Movie"})]}),a.year&&e("div",{className:s.metaItem,children:e("span",{children:a.year})}),n?.status&&e("div",{className:s.metaItem,children:e("span",{children:n.status})})]}),h.length>0&&e("div",{className:s.genres,children:h.map(t=>e("span",{className:s.genre,children:t},t))}),n?.overview&&e("div",{className:s.synopsis,children:n.overview.length>200?n.overview.slice(0,200)+"...":n.overview})]})}),e("div",{className:s.footer,children:[e("div",{className:s.primaryAction,children:e(p,{to:b,className:s.watchBtn,onClick:l,children:[e(i,{name:"play",size:14}),"Watch now"]})}),e("div",{className:s.secondaryActions,children:[e("button",{className:`${s.watchlistBtn} ${o?s.active:""}`,onClick:k,children:[o?e(i,{name:"check",size:12}):e(i,{name:"plus",size:12}),e("span",{children:o?"Remove":"Watchlist"})]}),e(p,{to:I,className:s.detailsBtn,onClick:l,children:[e(i,{name:"info-circle",size:12}),e("span",{children:"Details"})]})]})]})]})};export{E as T};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{h as pe,n as Ke,a as Ve,o as Qe,d as u,A as ge,T as R,u as t,S as He}from"./vendor-core-8R2fFbyN.js";import{u as Je,a as ve}from"./vendor-query-BXRECSI6.js";import{u as Ze,d as Xe,r as et,t as fe,v as ye,z as tt,q as Se,i as at,w as nt,x as st,y as lt,A as rt,C as it,B as T,M as te}from"./index-D2RkUJIN.js";import{I as p}from"./vendor-icons-CDMuNDeL.js";import{A as ct}from"./AnimeCard-URCiiy5W.js";import{P as be,S as ot}from"./SkeletonGrid-BxfgjP4i.js";import{E as dt}from"./ErrorMessage-z4f4WGgP.js";import{s as a}from"./Watchlist.module-XGtVQr1d.js";import"./useAnimeInfoData-BWShDHqE.js";import"./QueueOptionsButton-BVZyPyKf.js";import"./vendor-ui-CUDCvRS_.js";import"./MediaCard-CAzoxsRS.js";import"./useIsMobile-Dm_b6Ob3.js";const ut=(d,g)=>{const l=new URLSearchParams(g);return l.delete("page"),l.sort(),`${d}|${l.toString()}`},mt=(d,g)=>{const l=new Set(d);return g.forEach(N=>l.add(N)),l},ht=(d,g)=>{const l=new Set(d);return g.forEach(N=>l.delete(N)),l},Ae=["All","Continue Watching","Watching","Completed","On-Hold","Dropped","Planned"],Le=Ae.slice(2),pt=[{value:"ALL",label:"All Types"},{value:"TV",label:"TV Series"},{value:"Movie",label:"Movie"},{value:"OVA",label:"OVA"},{value:"ONA",label:"ONA"},{value:"TV_SHORT",label:"TV Short"},{value:"SPECIAL",label:"Special"},{value:"ADULT",label:"Adult (18+)"}],gt=[{value:"ALL",label:"All Seasons"},{value:"WINTER",label:"Winter"},{value:"SPRING",label:"Spring"},{value:"SUMMER",label:"Summer"},{value:"FALL",label:"Fall"}],Tt=()=>{pe(()=>{document.title="Watchlist - dango"},[]);const{filter:d="All"}=Ke(),g=Ve(),[l,N]=Qe(),ae=Je(),[v,B]=u(parseInt(l.get("page")||"1")),[f,P]=u(l.get("query")||""),[C,q]=u(l.get("sortBy")||"last_added"),[y,W]=u(l.get("type")||"ALL"),[S,I]=u(l.get("season")||"ALL"),[b,$]=u(l.get("year")||"ALL"),[w,O]=u(()=>{const e={},n=l.get("genres")?.split(",").map(r=>r.trim()).filter(Boolean)||[],s=l.get("excludeGenres")?.split(",").map(r=>r.trim()).filter(Boolean)||[];return n.forEach(r=>r&&(e[r]="include")),s.forEach(r=>r&&(e[r]="exclude")),e}),[F,Ne]=u(()=>["type","season","year","genres","excludeGenres"].some(e=>l.has(e))),{lowEndMode:Ce}=Ze(),{titlePreference:M}=Xe(),{data:we}=et(),ne=we?.genres||[],K=ge(null),[c,k]=u(null),[V,D]=u(!1),[se,z]=u(!1),[E,ke]=u(!1),[h,L]=u(new Set),Q=ge(null),i=d==="Continue Watching",G=R(()=>{const e=new URLSearchParams(l);return e.delete("page"),e.set("titlePreference",M),e.toString()},[l,M]),{data:_,isLoading:xe,error:Re}=fe(G,v,14),{data:U,isLoading:Te,error:Be}=ye(d,G,v,14),{data:Pe}=fe(G,v+1,14),{data:We}=ye(d,G,v+1,14),H=R(()=>(i?_?.data:U?.data)||[],[i,_,U]),x=R(()=>(i?_?.total:U?.total)||0,[i,_,U]),Y=i?xe:Te,le=i?Re:Be,re=i?Pe:We;pe(()=>{P(l.get("query")||""),q(l.get("sortBy")||"last_added"),W(l.get("type")||"ALL"),I(l.get("season")||"ALL"),$(l.get("year")||"ALL");const e={},n=l.get("genres")?.split(",").map(o=>o.trim()).filter(Boolean)||[],s=l.get("excludeGenres")?.split(",").map(o=>o.trim()).filter(Boolean)||[];n.forEach(o=>o&&(e[o]="include")),s.forEach(o=>o&&(e[o]="exclude")),O(e),B(parseInt(l.get("page")||"1"));const r=ut(d,l);Q.current!==null&&Q.current!==r&&L(new Set),Q.current=r},[l,d]);const Ie=ve({mutationFn:async({id:e,status:n})=>{await Se("/api/watchlist/status",{method:"POST",body:JSON.stringify({id:e,status:n})})},onSuccess:()=>{ae.invalidateQueries({queryKey:["watchlist"]}),tt.success("Status updated")}}),$e=ve({mutationFn:async e=>{await Se("/api/continue-watching/remove",{method:"POST",body:JSON.stringify({showId:e})})},onSuccess:()=>ae.invalidateQueries({queryKey:["allContinueWatching"]})}),J=at(),{data:ie}=nt("skipRemoveConfirmation"),Oe=st(),Z=R(()=>{const e=n=>n[M]||n.name||"";return[...H].sort((n,s)=>C==="name_asc"?e(n).localeCompare(e(s)):C==="name_desc"?e(s).localeCompare(e(n)):0)},[H,C,M]),X=new Date().getFullYear(),Fe=[{value:"ALL",label:"All Years"},...Array.from({length:X-1980+1},(e,n)=>({value:String(X-n),label:String(X-n)}))],m=(e=C,n=1)=>{const s=new URLSearchParams;f.trim()&&s.set("query",f.trim()),e!=="last_added"&&s.set("sortBy",e),y!=="ALL"&&s.set("type",y),S!=="ALL"&&s.set("season",S),b!=="ALL"&&s.set("year",b);const r=Object.entries(w).filter(([,A])=>A==="include").map(([A])=>A),o=Object.entries(w).filter(([,A])=>A==="exclude").map(([A])=>A);r.length>0&&s.set("genres",r.join(",")),o.length>0&&s.set("excludeGenres",o.join(",")),n>1&&s.set("page",n.toString()),N(s),n!==v&&B(n)},ce=e=>{if(m(C,e),K.current){const n=K.current.getBoundingClientRect().top+window.scrollY-100;window.scrollTo({top:n,behavior:"smooth"})}},Me=()=>{P(""),q("last_added"),W("ALL"),I("ALL"),$("ALL"),O({}),N(new URLSearchParams),B(1)},De=e=>{O(n=>{const s=n[e],r={...n};return s==="include"?r[e]="exclude":s==="exclude"?delete r[e]:r[e]="include",r})},oe=(e,n)=>{if(i){k({id:e,name:n});return}String(ie)==="true"||String(ie)==="1"?J.mutate(e):k({id:e,name:n})},ze=()=>{if(c){if(c.ids){i?(Ee.mutate(c.ids),V&&ue.mutate(c.ids)):ue.mutate(c.ids),L(new Set),k(null),D(!1),z(!1);return}i?($e.mutate(c.id),V&&J.mutate(c.id)):J.mutate(c.id),se&&Oe.mutate({key:"skipRemoveConfirmation",value:!0}),k(null),D(!1),z(!1)}},de=()=>{k(null),D(!1),z(!1)},ue=lt(),Ee=rt(),Ge=it(),_e=()=>{ke(e=>{const n=!e;return n||L(new Set),n})},Ue=e=>{L(n=>{const s=new Set(n);return s.has(e)?s.delete(e):s.add(e),s})},j=Z.map(e=>e.id),ee=j.length>0&&j.every(e=>h.has(e)),Ye=()=>{L(ee?e=>ht(e,j):e=>mt(e,j))},je=e=>{h.size!==0&&(Ge.mutate({ids:[...h],status:e}),L(new Set))},qe=()=>{h.size!==0&&k({ids:[...h]})},me=H.length>=14&&re&&re.data.length>0,he=R(()=>Math.max(1,Math.ceil(x/14)),[x]);return t("div",{className:"page-container",children:[t("header",{className:a.header,children:[t("h2",{className:a.title,children:"My Watchlist"}),t("p",{className:a.subtitle,children:"Track and manage your anime collection"})]}),t("div",{className:a.controls,children:[t("div",{className:a.filters,children:Ae.map(e=>t("button",{className:`${a.filterBtn} ${d===e?a.active:""}`,onClick:()=>{const n=new URLSearchParams(l);n.delete("page"),g({pathname:`/watchlist/${e}`,search:n.toString()}),B(1)},children:e},e))}),t("div",{children:t("select",{className:a.sortSelect,value:C,onChange:e=>{const n=e.currentTarget.value;q(n),m(n,v)},children:[t("option",{value:"last_added",children:"Recently Added"}),t("option",{value:"name_asc",children:"Name (A-Z)"}),t("option",{value:"name_desc",children:"Name (Z-A)"})]})})]}),t("div",{className:a.filterContainer,children:[t("div",{className:a.searchBarWrapper,children:[t("div",{className:a.inputIconWrapper,children:[t(p,{name:"search",className:a.searchIcon}),t("input",{className:a.searchInput,placeholder:"Search your watchlist by title...",value:f,onInput:e=>P(e.currentTarget.value),onKeyDown:e=>e.key==="Enter"&&m()})]}),t("div",{className:a.searchActions,children:[t(T,{onClick:()=>m(),className:a.searchBtn,children:"Search"}),t("button",{className:`${a.filterToggleBtn} ${F?a.active:""}`,onClick:()=>Ne(!F),children:[t(p,{name:"filter",size:14}),t("span",{children:"Filters"}),F?t(p,{name:"chevron-up",size:12}):t(p,{name:"chevron-down",size:12})]})]})]}),t("div",{className:`${a.advancedFilters} ${F?a.show:""}`,children:[t("div",{className:a.filterDivider}),t("div",{className:a.filterGrid,children:[t("div",{className:a.filterItem,children:[t("label",{children:"Type"}),t("select",{value:y,onChange:e=>W(e.currentTarget.value),children:pt.map(e=>t("option",{value:e.value,children:e.label},e.value))})]}),t("div",{className:a.filterItem,children:[t("label",{children:"Season"}),t("select",{value:S,onChange:e=>I(e.currentTarget.value),children:gt.map(e=>t("option",{value:e.value,children:e.label},e.value))})]}),t("div",{className:a.filterItem,children:[t("label",{children:"Year"}),t("select",{value:b,onChange:e=>$(e.currentTarget.value),children:Fe.map(e=>t("option",{value:e.value,children:e.label},e.value))})]})]}),ne.length>0&&t("div",{className:a.genreSection,children:[t("label",{className:a.genreLabel,children:"Genres"}),t("div",{className:a.genreContainer,children:ne.map(e=>t("button",{className:`${a.genreButton} ${a[w[e]||""]}`,onClick:()=>De(e),children:e},e))})]}),t("div",{className:a.filterActions,children:[t(T,{variant:"secondary",onClick:Me,children:"Reset All"}),t(T,{onClick:()=>m(),className:a.applyBtn,children:"Apply Filters"})]})]})]}),t("div",{className:a.resultsHeader,ref:K,children:[t("h3",{className:a.resultsTitle,children:[i?"Continue Watching":d,t("span",{className:a.itemCount,children:["(",x," items)"]})]}),t("div",{className:a.headerActions,children:[t("button",{className:`${a.manageBtn} ${E?a.active:""}`,onClick:_e,children:[t(p,{name:"pencil-alt",size:13}),t("span",{children:"Bulk Manage"})]}),x>0&&t(be,{page:v,totalPages:he,canGoNext:me,onChange:ce,isLoading:Y})]})]}),E&&t("div",{className:a.manageBar,children:[t("button",{className:a.selectAllBtn,onClick:Ye,children:[t("span",{className:`${a.selectAllBox} ${ee?a.selectAllBoxChecked:""}`,"aria-hidden":"true"}),t("span",{children:ee?"Clear Page":"Select All"})]}),t("span",{className:a.manageCount,children:[h.size," selected"]}),t("div",{className:a.manageSpacer}),!i&&t("select",{className:a.manageStatusSelect,value:"",onChange:e=>{e.currentTarget.value&&je(e.currentTarget.value)},disabled:h.size===0,title:"Set status for selected items",children:[t("option",{value:"",children:"Set status…"}),Le.map(e=>t("option",{value:e,children:e},e))]}),t("button",{className:a.manageRemoveBtn,onClick:qe,disabled:h.size===0,children:[t(p,{name:"trash",size:13}),t("span",{children:"Remove Selected"})]})]}),Y?t(ot,{}):le?t(dt,{message:le.message}):t(He,{children:[(y!=="ALL"||S!=="ALL"||b!=="ALL"||Object.keys(w).length>0||f.trim())&&t("div",{style:{display:"flex",flexWrap:"wrap",gap:"8px",alignItems:"center",marginBottom:"1.5rem"},children:[t("span",{style:{fontSize:"0.8rem",color:"var(--text-muted)",fontWeight:600},children:"Active Filters:"}),f.trim()&&t("span",{className:"badge badge-secondary",style:{display:"inline-flex",alignItems:"center",gap:"6px"},children:["Search: ",f,t("button",{onClick:e=>{e.stopPropagation(),e.preventDefault(),P(""),m()},style:{background:"none",border:"none",color:"inherit",cursor:"pointer"},children:"✕"})]}),y!=="ALL"&&t("span",{className:"badge badge-secondary",style:{display:"inline-flex",alignItems:"center",gap:"6px"},children:["Type: ",y,t("button",{onClick:e=>{e.stopPropagation(),e.preventDefault(),W("ALL"),m()},style:{background:"none",border:"none",color:"inherit",cursor:"pointer"},children:"✕"})]}),S!=="ALL"&&t("span",{className:"badge badge-secondary",style:{display:"inline-flex",alignItems:"center",gap:"6px"},children:["Season: ",S,t("button",{onClick:e=>{e.stopPropagation(),e.preventDefault(),I("ALL"),m()},style:{background:"none",border:"none",color:"inherit",cursor:"pointer"},children:"✕"})]}),b!=="ALL"&&t("span",{className:"badge badge-secondary",style:{display:"inline-flex",alignItems:"center",gap:"6px"},children:["Year: ",b,t("button",{onClick:e=>{e.stopPropagation(),e.preventDefault(),$("ALL"),m()},style:{background:"none",border:"none",color:"inherit",cursor:"pointer"},children:"✕"})]}),Object.entries(w).map(([e,n])=>t("span",{className:`badge ${n==="include"?"badge-primary":"badge-danger"}`,style:{display:"inline-flex",alignItems:"center",gap:"6px"},children:[n==="include"?`+ ${e}`:`- ${e}`,t("button",{onClick:s=>{s.stopPropagation(),s.preventDefault(),O(r=>{const o={...r};return delete o[e],o}),m()},style:{background:"none",border:"none",color:"inherit",cursor:"pointer"},children:"✕"})]},e))]}),t("div",{className:`${a.grid} ${Ce?a.lowEnd:""}`,children:Z.map(e=>{const n=h.has(e.id);return t("div",{className:`${a.itemWrapper} ${n?a.selected:""}`,children:[E&&t("div",{className:a.selectOverlay,onClick:()=>Ue(e.id),title:n?"Deselect":"Select",children:t("span",{className:a.selectBadge,children:n?t(p,{name:"check",size:12}):null})}),t(ct,{anime:e,continueWatching:i,onRemove:()=>oe(e.id,e.name),layout:"vertical"}),!i&&!E&&t("div",{className:a.cardActions,children:[t("select",{className:a.statusSelect,value:e.status,onChange:s=>Ie.mutate({id:e.id,status:s.currentTarget.value}),children:Le.map(s=>t("option",{value:s,children:s},s))}),t("button",{className:a.removeBtn,onClick:()=>oe(e.id,e.name),title:"Remove from Watchlist","aria-label":"Remove from Watchlist",children:t(p,{name:"trash",size:12})})]})]},e._id)})})]}),!Y&&Z.length===0&&t("div",{className:a.emptyState,children:[t("h3",{className:a.emptyTitle,children:"Your watchlist is looking a bit lonely"}),t("p",{className:a.emptyText,children:d!=="All"||f||y!=="ALL"||S!=="ALL"||b!=="ALL"||Object.keys(w).length>0?"No anime match these filters. Try adjusting them to see more titles.":"Let's find something to watch!"}),t("button",{className:a.emptyBtn,onClick:()=>g("/search"),children:[t(p,{name:"search",size:14}),t("span",{children:"Browse Anime"})]})]}),x>0&&t("div",{className:a.bottomPagination,children:t(be,{page:v,totalPages:he,canGoNext:me,onChange:ce,isLoading:Y,variant:"labeled"})}),t(te,{isOpen:!!c,onClose:de,title:i?"Reset Progress":"Remove from Watchlist",children:[t(te.Body,{children:[t("p",{children:c?.ids&&c.ids.length>1?i?`Are you sure you want to remove watch progress for ${c.ids.length} items?`:`Are you sure you want to remove ${c.ids.length} items from your watchlist?`:i?`Are you sure you want to remove your watch progress for "${c?.name}"?`:`Are you sure you want to remove "${c?.name}" from your watchlist?`}),i&&t("label",{children:[t("input",{type:"checkbox",checked:V,onChange:e=>D(e.target.checked)}),"Also remove from my watchlist"]}),!i&&!(c?.ids&&c.ids.length>1)&&t("label",{children:[t("input",{type:"checkbox",checked:se,onChange:e=>z(e.target.checked)}),"Remember my choice"]})]}),t(te.Actions,{children:[t(T,{variant:"secondary",onClick:de,children:"No"}),t(T,{variant:"danger",onClick:ze,children:"Yes"})]})]})]})};export{Tt as default};
|