@serifpersia/dango 3.3.8 → 3.4.0

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.
Files changed (180) hide show
  1. package/README.md +1 -1
  2. package/client/dist/assets/{AnimeCard-D4qo4289.js → AnimeCard-CJy1SXH3.js} +1 -1
  3. package/client/dist/assets/{AnimeInfo-BdRRm4So.js → AnimeInfo-BoXlVvZ-.js} +1 -1
  4. package/client/dist/assets/{AnimeInfoPage-DeJNAKMR.js → AnimeInfoPage-BxbA_7AV.js} +2 -2
  5. package/client/dist/assets/Asmr--Zc8G9zv.js +2 -0
  6. package/client/dist/assets/AsmrResetProgressModal-CKYm-8cp.js +1 -0
  7. package/client/dist/assets/AvSyncCalibrator-3yjZS7s6.js +10 -0
  8. package/client/dist/assets/Home-rrhOGQcg.js +1 -0
  9. package/client/dist/assets/Insights-Bay90Yeg.js +1 -0
  10. package/client/dist/assets/{LibraryListPage-D3hTqTRU.js → LibraryListPage-HzuDavbb.js} +1 -1
  11. package/client/dist/assets/{ListeningList-BgWsfu40.js → ListeningList-DYcQnnsG.js} +1 -1
  12. package/client/dist/assets/{Manga-Dn-CYins.js → Manga-D8CT9aO0.js} +1 -1
  13. package/client/dist/assets/{MangaInfoPage-EmQoK7Bv.js → MangaInfoPage-DGK6CYp1.js} +1 -1
  14. package/client/dist/assets/MangaReadPage-B9XoDKF9.js +1 -0
  15. package/client/dist/assets/{MangaResetProgressModal-Dian37PQ.js → MangaResetProgressModal-QjZyIAjl.js} +1 -1
  16. package/client/dist/assets/Mature-CHt_-Hz5.js +1 -0
  17. package/client/dist/assets/MediaCard-B5584Cq9.js +1 -0
  18. package/client/dist/assets/Music-HeLrEZ2d.js +1 -0
  19. package/client/dist/assets/OptionListMenu-CJ90CdwI.js +1 -0
  20. package/client/dist/assets/Player-BYtEax_z.js +56 -0
  21. package/client/dist/assets/PlayerSettings-4-cUAY4B.js +1 -0
  22. package/client/dist/assets/QueueOptionsButton-Ba0dg_82.js +1 -0
  23. package/client/dist/assets/{QueueRail-B7DeSFoA.js → QueueRail-O7f4RqjB.js} +1 -1
  24. package/client/dist/assets/Radio-DPZxQnyD.js +1 -0
  25. package/client/dist/assets/{ReadingList-BT8kUjCB.js → ReadingList-n6rr8ygy.js} +1 -1
  26. package/client/dist/assets/{ResetProgressModal-DWeiO-hs.js → ResetProgressModal-CcStlbhP.js} +1 -1
  27. package/client/dist/assets/Search-C2wrWOdy.js +1 -0
  28. package/client/dist/assets/Settings-CluUYs8U.js +21 -0
  29. package/client/dist/assets/{SkeletonGrid-B5loz7ii.js → SkeletonGrid-CLEPY9nP.js} +1 -1
  30. package/client/dist/assets/Trackers-CTGPxs0V.js +2 -0
  31. package/client/dist/assets/Tv-BwHlDdi8.js +4 -0
  32. package/client/dist/assets/Tv-ZEA45gXK.css +1 -0
  33. package/client/dist/assets/TvInfo-CXnrLekr.js +1 -0
  34. package/client/dist/assets/TvPopup-BekfiNcX.js +1 -0
  35. package/client/dist/assets/{TvSearch-BYwCf5kO.js → TvSearch-DuqR-z3v.js} +1 -1
  36. package/client/dist/assets/{TvWatchlist-BiJU6pKT.js → TvWatchlist-CBvgFrA0.js} +1 -1
  37. package/client/dist/assets/Watchlist-mj8mTOam.js +1 -0
  38. package/client/dist/assets/{hls-C11FOWHj.js → hls-Boje3IOE.js} +1 -1
  39. package/client/dist/assets/index-BXwhFWB_.js +178 -0
  40. package/client/dist/assets/useAnimeInfoData-DJ7A4B4T.js +1 -0
  41. package/client/dist/assets/useAsmr-DIwIsJXF.js +1 -0
  42. package/client/dist/assets/{useAsmrPopup-cT_Uu41e.js → useAsmrPopup-e4zNN9ig.js} +1 -1
  43. package/client/dist/assets/useMangaLibrary-C9Lci5N4.js +1 -0
  44. package/client/dist/assets/{useMangaPopup-PnmSctHu.js → useMangaPopup-B8nnpvyi.js} +1 -1
  45. package/client/dist/assets/useProviders-B5Qr4I1y.js +1 -0
  46. package/client/dist/assets/useTv-Dm8MCYlL.js +1 -0
  47. package/client/dist/assets/useTvLibrary-B7jEk2Q7.js +1 -0
  48. package/client/dist/assets/{vendor-query-BXRECSI6.js → vendor-query-BUTye89G.js} +1 -1
  49. package/client/dist/index.html +2 -2
  50. package/orchestrator.js +1 -2
  51. package/package.json +2 -6
  52. package/server/dist/app-auth.js +9 -29
  53. package/server/dist/app-hono.js +621 -0
  54. package/server/dist/config.js +0 -1
  55. package/server/dist/db/drizzle.js +32 -0
  56. package/server/dist/db/schema-anime.js +64 -0
  57. package/server/dist/db/schema-asmr.js +25 -0
  58. package/server/dist/db/schema-manga.js +31 -0
  59. package/server/dist/db/schema-tv.js +33 -0
  60. package/server/dist/db.js +75 -4
  61. package/server/dist/discord-gateway.js +1 -1
  62. package/server/dist/google.js +0 -7
  63. package/server/dist/hono/asmr.js +1 -0
  64. package/server/dist/hono/auth.js +56 -0
  65. package/server/dist/hono/music.js +419 -0
  66. package/server/dist/{controllers/proxy.controller.js → hono/proxy.js} +390 -394
  67. package/server/dist/hono/tv.js +197 -0
  68. package/server/dist/hono/watchlist.js +733 -0
  69. package/server/dist/lib/anilist.js +3 -3
  70. package/server/dist/lib/insights.js +185 -0
  71. package/server/dist/lib/ipc.js +1 -2
  72. package/server/dist/lib/kitsu.js +1 -28
  73. package/server/dist/lib/mal-import.js +291 -0
  74. package/server/dist/lib/migration.js +20 -34
  75. package/server/dist/lib/offline-db.js +36 -36
  76. package/server/dist/lib/tracker/manga-sync.service.js +21 -21
  77. package/server/dist/lib/tracker/sync.service.js +20 -20
  78. package/server/dist/lib/translate.js +79 -0
  79. package/server/dist/lib/ytmusic.js +15 -4
  80. package/server/dist/providers/title-matching.js +0 -1
  81. package/server/dist/repositories/asmr.repository.js +90 -87
  82. package/server/dist/repositories/insights.repository.js +24 -13
  83. package/server/dist/repositories/library.repository.js +33 -93
  84. package/server/dist/repositories/manga.repository.js +108 -116
  85. package/server/dist/repositories/notifications.repository.js +50 -31
  86. package/server/dist/repositories/queue.repository.js +36 -25
  87. package/server/dist/repositories/settings.repository.js +18 -8
  88. package/server/dist/repositories/shows-meta.repository.js +54 -41
  89. package/server/dist/repositories/temp-show-ids.repository.js +43 -31
  90. package/server/dist/repositories/tv.repository.js +98 -115
  91. package/server/dist/repositories/watched-episodes.repository.js +52 -20
  92. package/server/dist/repositories/watchlist.repository.js +36 -48
  93. package/server/dist/server.js +59 -151
  94. package/server/dist/sync.js +41 -4
  95. package/server/dist/trpc/context.js +7 -0
  96. package/server/dist/trpc/errors.js +21 -0
  97. package/server/dist/trpc/index.js +19 -0
  98. package/server/dist/trpc/router.js +49 -0
  99. package/server/dist/trpc/routers/asmr-library.js +122 -0
  100. package/server/dist/trpc/routers/asmr-progress.js +133 -0
  101. package/server/dist/trpc/routers/asmr.js +87 -0
  102. package/server/dist/trpc/routers/auth.js +247 -0
  103. package/server/dist/trpc/routers/continue-watching.js +83 -0
  104. package/server/dist/trpc/routers/data.js +1039 -0
  105. package/server/dist/trpc/routers/discord.js +288 -0
  106. package/server/dist/trpc/routers/discovery.js +15 -0
  107. package/server/dist/trpc/routers/insights.js +45 -0
  108. package/server/dist/trpc/routers/manga-library.js +280 -0
  109. package/server/dist/trpc/routers/manga-progress.js +181 -0
  110. package/server/dist/trpc/routers/manga.js +201 -0
  111. package/server/dist/{controllers/music.controller.js → trpc/routers/music.js} +235 -295
  112. package/server/dist/trpc/routers/notifications.js +80 -0
  113. package/server/dist/trpc/routers/progress.js +15 -0
  114. package/server/dist/trpc/routers/queue.js +95 -0
  115. package/server/dist/{routes/radio.routes.js → trpc/routers/radio.js} +30 -23
  116. package/server/dist/trpc/routers/settings.js +105 -0
  117. package/server/dist/trpc/routers/tracker.js +219 -0
  118. package/server/dist/trpc/routers/tv-library.js +145 -0
  119. package/server/dist/trpc/routers/tv-progress.js +171 -0
  120. package/server/dist/trpc/routers/tv.js +565 -0
  121. package/server/dist/trpc/routers/watchlist.js +564 -0
  122. package/server/dist/trpc/validation.js +159 -0
  123. package/server/dist/utils/security.utils.js +0 -18
  124. package/server/package.json +5 -10
  125. package/client/dist/assets/Asmr-D0EGRYsv.js +0 -2
  126. package/client/dist/assets/AsmrResetProgressModal-Gs0GmVaT.js +0 -1
  127. package/client/dist/assets/AvSyncCalibrator-D8hqarVB.js +0 -10
  128. package/client/dist/assets/Home-BnBzjFkW.js +0 -1
  129. package/client/dist/assets/Insights-i9QgY3nE.js +0 -1
  130. package/client/dist/assets/MangaReadPage-xJB8K2YU.js +0 -1
  131. package/client/dist/assets/Mature-DQtXKOXc.js +0 -1
  132. package/client/dist/assets/MediaCard-DpKoY-1j.js +0 -1
  133. package/client/dist/assets/Music-BP43Zqmq.js +0 -1
  134. package/client/dist/assets/OptionListMenu-Bj9ebsW3.js +0 -1
  135. package/client/dist/assets/Player-O2Cy2GF1.js +0 -56
  136. package/client/dist/assets/PlayerSettings-DYZ272Yp.js +0 -1
  137. package/client/dist/assets/QueueOptionsButton-lv26-6jp.js +0 -1
  138. package/client/dist/assets/Radio-qQBYnVfY.js +0 -1
  139. package/client/dist/assets/Search-BkvPYN33.js +0 -1
  140. package/client/dist/assets/Settings-CyZEBF6S.js +0 -21
  141. package/client/dist/assets/Trackers-JK-aXBYC.js +0 -2
  142. package/client/dist/assets/Tv-BwXWJxMy.js +0 -4
  143. package/client/dist/assets/Tv-C2SWcVzN.css +0 -1
  144. package/client/dist/assets/TvInfo-Dzyq6ucH.js +0 -1
  145. package/client/dist/assets/TvPopup-PlCU30FO.js +0 -1
  146. package/client/dist/assets/Watchlist-BOEeGXH9.js +0 -1
  147. package/client/dist/assets/index-CtoKRNLc.js +0 -178
  148. package/client/dist/assets/useAnimeInfoData-kr3WOIWH.js +0 -1
  149. package/client/dist/assets/useAsmr-CB9THf0m.js +0 -1
  150. package/client/dist/assets/useMangaLibrary-rM6z3_0E.js +0 -1
  151. package/client/dist/assets/useProviders-B3VB7A9M.js +0 -1
  152. package/client/dist/assets/useTv-BTUKMH7Q.js +0 -1
  153. package/client/dist/assets/useTvLibrary-C0sX-ilZ.js +0 -1
  154. package/server/dist/controllers/asmr-library.controller.js +0 -288
  155. package/server/dist/controllers/auth.controller.js +0 -225
  156. package/server/dist/controllers/data.controller.js +0 -767
  157. package/server/dist/controllers/insights.controller.js +0 -203
  158. package/server/dist/controllers/manga-library.controller.js +0 -350
  159. package/server/dist/controllers/settings.controller.js +0 -609
  160. package/server/dist/controllers/tv-library.controller.js +0 -314
  161. package/server/dist/controllers/watchlist.controller.js +0 -1069
  162. package/server/dist/request-logger.js +0 -16
  163. package/server/dist/routes/asmr-library.routes.js +0 -23
  164. package/server/dist/routes/asmr.routes.js +0 -81
  165. package/server/dist/routes/auth.routes.js +0 -25
  166. package/server/dist/routes/data.routes.js +0 -47
  167. package/server/dist/routes/discord-gateway.routes.js +0 -51
  168. package/server/dist/routes/insights.routes.js +0 -35
  169. package/server/dist/routes/lan-auth.routes.js +0 -46
  170. package/server/dist/routes/manga-library.routes.js +0 -25
  171. package/server/dist/routes/manga.routes.js +0 -141
  172. package/server/dist/routes/music.routes.js +0 -20
  173. package/server/dist/routes/providers.routes.js +0 -17
  174. package/server/dist/routes/proxy.routes.js +0 -12
  175. package/server/dist/routes/settings.routes.js +0 -24
  176. package/server/dist/routes/tracker.routes.js +0 -287
  177. package/server/dist/routes/translate.routes.js +0 -88
  178. package/server/dist/routes/tv-library.routes.js +0 -23
  179. package/server/dist/routes/tv.routes.js +0 -629
  180. 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-CtoKRNLc.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-CtoKRNLc.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-CtoKRNLc.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-D8hqarVB.js";import{O as un,S as hs,a as ps,A as fs}from"./OptionListMenu-Bj9ebsW3.js";import{u as gs}from"./useMatureConsent-CT7-KfzG.js";import{u as vs}from"./useProviders-B3VB7A9M.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-C0sX-ilZ.js";import{l as Es,c as Ns}from"./hls-C11FOWHj.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&&gt.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&&gt.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&&gt.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-CtoKRNLc.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-C0sX-ilZ.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-C0sX-ilZ.js";import{p as s,a as S}from"./MediaCard-DpKoY-1j.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-CtoKRNLc.js";import{I as p}from"./vendor-icons-CDMuNDeL.js";import{A as ct}from"./AnimeCard-D4qo4289.js";import{P as be,S as ot}from"./SkeletonGrid-B5loz7ii.js";import{E as dt}from"./ErrorMessage-z4f4WGgP.js";import{s as a}from"./Watchlist.module-XGtVQr1d.js";import"./useAnimeInfoData-kr3WOIWH.js";import"./QueueOptionsButton-lv26-6jp.js";import"./vendor-ui-CUDCvRS_.js";import"./MediaCard-DpKoY-1j.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};