@serifpersia/dango 3.4.9 → 3.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/README.md +1 -15
  2. package/client/dist/assets/{AnimeCard-Dnps_uQ4.js → AnimeCard-B2ErWl4t.js} +1 -1
  3. package/client/dist/assets/{AnimeInfo-pfps3FSr.js → AnimeInfo-Dfd4zWTx.js} +1 -1
  4. package/client/dist/assets/AnimeInfoPage-_DAHOnnx.js +2 -0
  5. package/client/dist/assets/{Asmr-D_IQfb8x.js → Asmr-CppKRwf3.js} +2 -2
  6. package/client/dist/assets/{AsmrResetProgressModal-BYtWVQA9.js → AsmrResetProgressModal-DqZ8um_v.js} +1 -1
  7. package/client/dist/assets/{AvSyncCalibrator-jI6WLW45.js → AvSyncCalibrator-DH6KEsbi.js} +1 -1
  8. package/client/dist/assets/{Home-CpaYOxPA.js → Home-bqn1BmP7.js} +1 -1
  9. package/client/dist/assets/Insights-KlshyXuf.js +1 -0
  10. package/client/dist/assets/{LibraryListPage-DW0HqYwj.js → LibraryListPage-Ba7501DJ.js} +1 -1
  11. package/client/dist/assets/{ListeningList-NIRGPFnw.js → ListeningList-CycltWkq.js} +1 -1
  12. package/client/dist/assets/Manga-C3foLd_W.js +1 -0
  13. package/client/dist/assets/{MangaInfoPage-3Ah2UkzA.js → MangaInfoPage-CKgBcwjP.js} +1 -1
  14. package/client/dist/assets/{MangaReadPage-DpLMkSK1.js → MangaReadPage-B0Nr5zbg.js} +1 -1
  15. package/client/dist/assets/{MangaResetProgressModal-CXyMR31_.js → MangaResetProgressModal-C9YlH9oi.js} +1 -1
  16. package/client/dist/assets/{Mature-Ii7NKE5X.js → Mature-DVvlLfAy.js} +1 -1
  17. package/client/dist/assets/{MediaCard-DcB-RhSF.js → MediaCard-Auk42bqx.js} +1 -1
  18. package/client/dist/assets/Music-BeCMeHS7.js +1 -0
  19. package/client/dist/assets/{OptionListMenu-B3xlVT-D.js → OptionListMenu-AHGTsUIM.js} +1 -1
  20. package/client/dist/assets/{Player-CF7briPT.js → Player-Diz4QXSs.js} +6 -6
  21. package/client/dist/assets/{PlayerSettings-g4xFrRiD.js → PlayerSettings-B8CsR3hI.js} +1 -1
  22. package/client/dist/assets/QueueOptionsButton-tpZ79_8s.js +1 -0
  23. package/client/dist/assets/{QueueRail-wmxRrTaD.js → QueueRail-CvrVAD_w.js} +1 -1
  24. package/client/dist/assets/{Radio-DdGONwsq.js → Radio-s_2zeYYn.js} +1 -1
  25. package/client/dist/assets/{ReadingList-BZtJHLAG.js → ReadingList-DLKfiYLy.js} +1 -1
  26. package/client/dist/assets/{ResetProgressModal-CzJaHmde.js → ResetProgressModal-jPtyfyf0.js} +1 -1
  27. package/client/dist/assets/{Search-CUHWC7IZ.js → Search-UfzxfJ8k.js} +1 -1
  28. package/client/dist/assets/Settings-CLP_y7EE.js +21 -0
  29. package/client/dist/assets/{Settings-AuFxQcJF.css → Settings-CwIWLSfY.css} +1 -1
  30. package/client/dist/assets/{SkeletonGrid-BnRGuRMg.js → SkeletonGrid-21to5FX0.js} +1 -1
  31. package/client/dist/assets/ToggleSwitch-BPil0d_k.css +1 -0
  32. package/client/dist/assets/ToggleSwitch-CN_ppEc9.js +1 -0
  33. package/client/dist/assets/{Trackers-m2fBLuJo.js → Trackers-QqnmwIKk.js} +1 -1
  34. package/client/dist/assets/{Tv-C7mRP4gg.js → Tv-D3yVtdDx.js} +4 -4
  35. package/client/dist/assets/{TvInfo-D8zdqjdN.js → TvInfo-gue0nYi0.js} +1 -1
  36. package/client/dist/assets/{TvPopup-_GPNroa1.js → TvPopup-CkXBuHHD.js} +1 -1
  37. package/client/dist/assets/{TvSearch-CiFB0G_Y.js → TvSearch-DLP_3Nf2.js} +1 -1
  38. package/client/dist/assets/{TvWatchlist-6tIsBVyA.js → TvWatchlist-DzgVXuh9.js} +1 -1
  39. package/client/dist/assets/{Watchlist-YyxXLuno.js → Watchlist-DeO4iNhW.js} +1 -1
  40. package/client/dist/assets/{hls-DpOQmcQm.js → hls-D3SWhYIU.js} +1 -1
  41. package/client/dist/assets/index-BGCpLf49.js +178 -0
  42. package/client/dist/assets/index-w_r2lTCe.css +1 -0
  43. package/client/dist/assets/{useAnimeInfoData-DJrFWjlu.js → useAnimeInfoData-CWRGC9fX.js} +1 -1
  44. package/client/dist/assets/{useAsmr-XJn2_7ol.js → useAsmr-5n3wPm5z.js} +1 -1
  45. package/client/dist/assets/{useAsmrPopup-DmZ3Np3r.js → useAsmrPopup-BxGzhLmJ.js} +1 -1
  46. package/client/dist/assets/{useMangaLibrary-COua_Bkn.js → useMangaLibrary-DUteTFgU.js} +1 -1
  47. package/client/dist/assets/{useMangaPopup-o6xoB0uh.js → useMangaPopup-BWU1Tz5e.js} +1 -1
  48. package/client/dist/assets/{useProviders-DIy3z1fe.js → useProviders-mkl-w-e4.js} +1 -1
  49. package/client/dist/assets/{useTv-Y_cF_10L.js → useTv-Dl-E6HBp.js} +1 -1
  50. package/client/dist/assets/{useTvLibrary-DpqGf6Bp.js → useTvLibrary-BVECcmlW.js} +1 -1
  51. package/client/dist/index.html +2 -2
  52. package/package.json +2 -2
  53. package/server/dist/app-hono.js +0 -6
  54. package/server/dist/config.js +0 -1
  55. package/server/dist/db/schema-anime.js +3 -2
  56. package/server/dist/lib/{discord-roles-sync.service.js → recommendations.service.js} +0 -149
  57. package/server/dist/server.js +0 -2
  58. package/server/dist/shipped-defaults.js +0 -1
  59. package/server/dist/sync.js +1 -0
  60. package/server/dist/trpc/routers/discord.js +0 -4
  61. package/server/dist/trpc/routers/insights.js +1 -31
  62. package/server/dist/trpc/routers/settings.js +0 -10
  63. package/server/package.json +1 -1
  64. package/client/dist/assets/AnimeInfoPage-CN99oEZn.js +0 -2
  65. package/client/dist/assets/Insights-07y8ViuK.js +0 -1
  66. package/client/dist/assets/Manga-c2X095B3.js +0 -1
  67. package/client/dist/assets/Map-BtXwpBOC.js +0 -1
  68. package/client/dist/assets/Map-CXFqXmsZ.css +0 -1
  69. package/client/dist/assets/Music-CeiL8cZv.js +0 -1
  70. package/client/dist/assets/QueueOptionsButton-B3cSHa0L.js +0 -1
  71. package/client/dist/assets/Settings-Bxqc5fmz.js +0 -21
  72. package/client/dist/assets/index-Cc1Up_NI.css +0 -1
  73. package/client/dist/assets/index-Dyiy_9L6.js +0 -178
  74. package/server/dist/utils/machine-id.js +0 -39
@@ -1 +1 @@
1
- import{b as u,c as b,u as g,a as c}from"./vendor-query-BBT5_sHx.js";import{t as p,h as i,z as s}from"./index-Dyiy_9L6.js";import{T as Q}from"./vendor-core-DfaOB3Rn.js";const v=300*1e3,M={mangadex:"https://mangadex.org/",mangapill:"https://www.mangapill.com/"},f=(e,a)=>`/api/proxy?url=${encodeURIComponent(a)}&referer=${encodeURIComponent(M[e])}`,T=(e,a)=>a&&(e==="mangapill"?f(e,a):a),k=(e,a=!0)=>{const{provider:r,query:n,page:o,sort:t,status:m,type:d,rating:y,mature:l}=e;return u({queryKey:["mangaBrowse",r,n,o,t,m,d,y,l],queryFn:()=>p.manga.search.query({provider:r,q:n.trim()||void 0,page:o,sort:t||void 0,status:m||void 0,type:d||void 0,rating:y||void 0,mature:l?"1":void 0}),staleTime:v,enabled:a&&!!r})},E=(e,a=14)=>{const{provider:r,query:n,sort:o,status:t,type:m,rating:d,mature:y}=e;return b({queryKey:["mangaBrowseInfinite",r,n,o,t,m,d,y,a],queryFn:({pageParam:l=1})=>p.manga.search.query({provider:r,q:n.trim()||void 0,page:l,limit:a,sort:o||void 0,status:t||void 0,type:m||void 0,rating:d||void 0,mature:y?"1":void 0}),initialPageParam:1,getNextPageParam:(l,q)=>l.hasNext?q.length+1:void 0,staleTime:v,enabled:!!r})},O=(e,a,r="safe",n=!1)=>u({queryKey:["mangaDetail",e,a,r,n],queryFn:()=>p.manga.info.query({provider:e||"",id:a||"",rating:r,mature:n?"1":void 0}),enabled:!!e&&!!a,staleTime:v}),$=(e,a,r)=>u({queryKey:["mangaPages",e,a],queryFn:()=>p.manga.pages.query({provider:e||"",id:a||""}),enabled:!!e&&!!a,staleTime:1800*1e3}),B=()=>{const e=i();return u({...e.manga.trending.queryOptions(),staleTime:600*1e3,retry:1})},I=e=>e.contentRating==="erotica"||e.contentRating==="pornographic"||e.type==="doujinshi";function N(e,a){const r=e.titles??{};return a==="englishName"?r.en||e.title:a==="nativeName"&&(r.native||r.romaji||e.altTitle)||e.title}function A(e){return{englishName:e.titles?.en??e.title,nativeName:e.titles?.native??e.titles?.romaji??e.altTitle??void 0}}const h=/^anilist:ch:(\d+)$/;function w(e){if(!e)return null;const a=h.exec(e.trim());if(!a)return null;const r=Number.parseInt(a[1],10);return Number.isFinite(r)?r:null}function x(e,a){const r=String(a),n=e.find(o=>(o.number??"").trim()===r);return n||(e.find(o=>{const t=Number.parseFloat((o.number??"").trim());return Number.isFinite(t)&&t===a})??null)}const _=["Reading","Completed","On-Hold","Dropped","Planned"],L=(e,a)=>`${e.toLowerCase()}:${a}`,D=(e="All",a=1,r=24)=>{const n=i();return u(n.mangaLibrary.list.queryOptions({status:e,page:a,limit:r}))},P=()=>{const e=i();return u({...e.mangaLibrary.ids.queryOptions(),staleTime:1e3*60})},U=e=>{const a=i();return u({...a.mangaLibrary.check.queryOptions({id:e??""}),enabled:!!e,staleTime:1e3*60})},j=e=>{const a=i();return u({...a.mangaLibrary.entry.queryOptions({id:e??""}),enabled:!!e,staleTime:1e3*60})},H=()=>{const e=i(),a=g();return c(e.mangaLibrary.link.mutationOptions({onSuccess:r=>{s.success(r?.progressMigrated?"Linked — chapter progress carried over":"Linked to AniList entry"),a.invalidateQueries(e.mangaLibrary.pathFilter()),a.invalidateQueries({queryKey:["manga-library"]}),a.invalidateQueries({queryKey:["manga-continue-reading"]})},onError:r=>{s.error(`Failed to link: ${r.message}`)}}))},z=(e=24)=>{const a=i();return u(a.mangaProgress.continueReading.queryOptions({limit:e}))},V=e=>{const a=i();return u({...a.mangaProgress.byManga.queryOptions({mangaId:e??""}),enabled:!!e})},S=()=>{const e=i(),a=g();return c(e.mangaLibrary.add.mutationOptions({onSuccess:(r,n)=>{n.silent||s.success("Bookmarked"),a.invalidateQueries(e.mangaLibrary.pathFilter()),a.invalidateQueries({queryKey:["manga-library"]})},onError:r=>{s.error(`Failed to bookmark: ${r.message}`)}}))},F=()=>{const e=i(),a=g();return c(e.mangaLibrary.remove.mutationOptions({onSuccess:()=>{s.success("Bookmark removed"),a.invalidateQueries(e.mangaLibrary.pathFilter()),a.invalidateQueries({queryKey:["manga-library"]}),a.invalidateQueries({queryKey:["manga-continue-reading"]})},onError:r=>{s.error(`Failed to remove: ${r.message}`)}}))},Y=()=>{const e=i(),a=g();return c(e.mangaLibrary.setStatus.mutationOptions({onSuccess:()=>{s.success("Status updated"),a.invalidateQueries(e.mangaLibrary.pathFilter()),a.invalidateQueries({queryKey:["manga-library"]}),a.invalidateQueries({queryKey:["manga-continue-reading"]})},onError:r=>{s.error(`Failed to update status: ${r.message}`)}}))},G=()=>{const e=i(),a=g();return c(e.mangaProgress.save.mutationOptions({onSuccess:(r,n)=>{a.invalidateQueries({queryKey:["manga-progress",n.mangaId]}),a.invalidateQueries({queryKey:["manga-continue-reading"]}),a.invalidateQueries({queryKey:["manga-library"]}),a.invalidateQueries(e.mangaProgress.pathFilter())}}))},J=()=>{const e=i(),a=g();return c(e.mangaProgress.remove.mutationOptions({onSuccess:()=>{s.success("Progress reset"),a.invalidateQueries({queryKey:["manga-progress"]}),a.invalidateQueries({queryKey:["manga-continue-reading"]}),a.invalidateQueries({queryKey:["manga-library"]}),a.invalidateQueries(e.mangaProgress.pathFilter())},onError:r=>{s.error(`Failed to reset progress: ${r.message}`)}}))},W=()=>{const e=i(),a=g();return c(e.mangaLibrary.batchStatus.mutationOptions({onSuccess:r=>{s.success(`Status updated for ${r.updated??0} items`),a.invalidateQueries(e.mangaLibrary.pathFilter()),a.invalidateQueries({queryKey:["manga-library"]}),a.invalidateQueries({queryKey:["manga-continue-reading"]})},onError:r=>{s.error(`Failed to update statuses: ${r.message}`)}}))},X=()=>{const e=i(),a=g();return c(e.mangaLibrary.removeMany.mutationOptions({onSuccess:r=>{const n=r.removed??0;s.success(`Removed ${n} ${n===1?"item":"items"} from reading list`),a.invalidateQueries(e.mangaLibrary.pathFilter()),a.invalidateQueries({queryKey:["manga-library"]}),a.invalidateQueries({queryKey:["manga-continue-reading"]})},onError:r=>{s.error(`Failed to remove: ${r.message}`)}}))},Z=()=>{const e=i(),a=g();return c(e.mangaProgress.removeMany.mutationOptions({onSuccess:r=>{const n=r.removed??0;s.success(`Reset progress for ${n} ${n===1?"item":"items"}`),a.invalidateQueries({queryKey:["manga-progress"]}),a.invalidateQueries({queryKey:["manga-continue-reading"]}),a.invalidateQueries({queryKey:["manga-library"]}),a.invalidateQueries(e.mangaProgress.pathFilter())},onError:r=>{s.error(`Failed to reset progress: ${r.message}`)}}))},ee=()=>{const e=S(),a=F(),{data:r}=P(),n=Q(()=>new Set(r?.ids||[]),[r]);return{toggle:t=>{const m=L(t.provider,t.id);n.has(m)?a.mutate(m):e.mutate({provider:t.provider,mangaId:t.id,title:t.title,cover:t.cover,altTitle:t.altTitle,contentRating:t.contentRating})},bookmarkedIds:n,pending:e.isPending||a.isPending}};export{Z as A,J as B,_ as M,E as a,T as b,A as c,z as d,B as e,k as f,j as g,S as h,I as i,H as j,O as k,U as l,L as m,V as n,F as o,Y as p,w as q,N as r,x as s,$ as t,ee as u,f as v,G as w,D as x,W as y,X as z};
1
+ import{b as u,c as b,u as g,a as c}from"./vendor-query-BBT5_sHx.js";import{t as p,h as i,z as s}from"./index-BGCpLf49.js";import{T as Q}from"./vendor-core-DfaOB3Rn.js";const v=300*1e3,M={mangadex:"https://mangadex.org/",mangapill:"https://www.mangapill.com/"},f=(e,a)=>`/api/proxy?url=${encodeURIComponent(a)}&referer=${encodeURIComponent(M[e])}`,T=(e,a)=>a&&(e==="mangapill"?f(e,a):a),k=(e,a=!0)=>{const{provider:r,query:n,page:o,sort:t,status:m,type:d,rating:y,mature:l}=e;return u({queryKey:["mangaBrowse",r,n,o,t,m,d,y,l],queryFn:()=>p.manga.search.query({provider:r,q:n.trim()||void 0,page:o,sort:t||void 0,status:m||void 0,type:d||void 0,rating:y||void 0,mature:l?"1":void 0}),staleTime:v,enabled:a&&!!r})},E=(e,a=14)=>{const{provider:r,query:n,sort:o,status:t,type:m,rating:d,mature:y}=e;return b({queryKey:["mangaBrowseInfinite",r,n,o,t,m,d,y,a],queryFn:({pageParam:l=1})=>p.manga.search.query({provider:r,q:n.trim()||void 0,page:l,limit:a,sort:o||void 0,status:t||void 0,type:m||void 0,rating:d||void 0,mature:y?"1":void 0}),initialPageParam:1,getNextPageParam:(l,q)=>l.hasNext?q.length+1:void 0,staleTime:v,enabled:!!r})},O=(e,a,r="safe",n=!1)=>u({queryKey:["mangaDetail",e,a,r,n],queryFn:()=>p.manga.info.query({provider:e||"",id:a||"",rating:r,mature:n?"1":void 0}),enabled:!!e&&!!a,staleTime:v}),$=(e,a,r)=>u({queryKey:["mangaPages",e,a],queryFn:()=>p.manga.pages.query({provider:e||"",id:a||""}),enabled:!!e&&!!a,staleTime:1800*1e3}),B=()=>{const e=i();return u({...e.manga.trending.queryOptions(),staleTime:600*1e3,retry:1})},I=e=>e.contentRating==="erotica"||e.contentRating==="pornographic"||e.type==="doujinshi";function N(e,a){const r=e.titles??{};return a==="englishName"?r.en||e.title:a==="nativeName"&&(r.native||r.romaji||e.altTitle)||e.title}function A(e){return{englishName:e.titles?.en??e.title,nativeName:e.titles?.native??e.titles?.romaji??e.altTitle??void 0}}const h=/^anilist:ch:(\d+)$/;function w(e){if(!e)return null;const a=h.exec(e.trim());if(!a)return null;const r=Number.parseInt(a[1],10);return Number.isFinite(r)?r:null}function x(e,a){const r=String(a),n=e.find(o=>(o.number??"").trim()===r);return n||(e.find(o=>{const t=Number.parseFloat((o.number??"").trim());return Number.isFinite(t)&&t===a})??null)}const _=["Reading","Completed","On-Hold","Dropped","Planned"],L=(e,a)=>`${e.toLowerCase()}:${a}`,D=(e="All",a=1,r=24)=>{const n=i();return u(n.mangaLibrary.list.queryOptions({status:e,page:a,limit:r}))},P=()=>{const e=i();return u({...e.mangaLibrary.ids.queryOptions(),staleTime:1e3*60})},U=e=>{const a=i();return u({...a.mangaLibrary.check.queryOptions({id:e??""}),enabled:!!e,staleTime:1e3*60})},j=e=>{const a=i();return u({...a.mangaLibrary.entry.queryOptions({id:e??""}),enabled:!!e,staleTime:1e3*60})},H=()=>{const e=i(),a=g();return c(e.mangaLibrary.link.mutationOptions({onSuccess:r=>{s.success(r?.progressMigrated?"Linked — chapter progress carried over":"Linked to AniList entry"),a.invalidateQueries(e.mangaLibrary.pathFilter()),a.invalidateQueries({queryKey:["manga-library"]}),a.invalidateQueries({queryKey:["manga-continue-reading"]})},onError:r=>{s.error(`Failed to link: ${r.message}`)}}))},z=(e=24)=>{const a=i();return u(a.mangaProgress.continueReading.queryOptions({limit:e}))},V=e=>{const a=i();return u({...a.mangaProgress.byManga.queryOptions({mangaId:e??""}),enabled:!!e})},S=()=>{const e=i(),a=g();return c(e.mangaLibrary.add.mutationOptions({onSuccess:(r,n)=>{n.silent||s.success("Bookmarked"),a.invalidateQueries(e.mangaLibrary.pathFilter()),a.invalidateQueries({queryKey:["manga-library"]})},onError:r=>{s.error(`Failed to bookmark: ${r.message}`)}}))},F=()=>{const e=i(),a=g();return c(e.mangaLibrary.remove.mutationOptions({onSuccess:()=>{s.success("Bookmark removed"),a.invalidateQueries(e.mangaLibrary.pathFilter()),a.invalidateQueries({queryKey:["manga-library"]}),a.invalidateQueries({queryKey:["manga-continue-reading"]})},onError:r=>{s.error(`Failed to remove: ${r.message}`)}}))},Y=()=>{const e=i(),a=g();return c(e.mangaLibrary.setStatus.mutationOptions({onSuccess:()=>{s.success("Status updated"),a.invalidateQueries(e.mangaLibrary.pathFilter()),a.invalidateQueries({queryKey:["manga-library"]}),a.invalidateQueries({queryKey:["manga-continue-reading"]})},onError:r=>{s.error(`Failed to update status: ${r.message}`)}}))},G=()=>{const e=i(),a=g();return c(e.mangaProgress.save.mutationOptions({onSuccess:(r,n)=>{a.invalidateQueries({queryKey:["manga-progress",n.mangaId]}),a.invalidateQueries({queryKey:["manga-continue-reading"]}),a.invalidateQueries({queryKey:["manga-library"]}),a.invalidateQueries(e.mangaProgress.pathFilter())}}))},J=()=>{const e=i(),a=g();return c(e.mangaProgress.remove.mutationOptions({onSuccess:()=>{s.success("Progress reset"),a.invalidateQueries({queryKey:["manga-progress"]}),a.invalidateQueries({queryKey:["manga-continue-reading"]}),a.invalidateQueries({queryKey:["manga-library"]}),a.invalidateQueries(e.mangaProgress.pathFilter())},onError:r=>{s.error(`Failed to reset progress: ${r.message}`)}}))},W=()=>{const e=i(),a=g();return c(e.mangaLibrary.batchStatus.mutationOptions({onSuccess:r=>{s.success(`Status updated for ${r.updated??0} items`),a.invalidateQueries(e.mangaLibrary.pathFilter()),a.invalidateQueries({queryKey:["manga-library"]}),a.invalidateQueries({queryKey:["manga-continue-reading"]})},onError:r=>{s.error(`Failed to update statuses: ${r.message}`)}}))},X=()=>{const e=i(),a=g();return c(e.mangaLibrary.removeMany.mutationOptions({onSuccess:r=>{const n=r.removed??0;s.success(`Removed ${n} ${n===1?"item":"items"} from reading list`),a.invalidateQueries(e.mangaLibrary.pathFilter()),a.invalidateQueries({queryKey:["manga-library"]}),a.invalidateQueries({queryKey:["manga-continue-reading"]})},onError:r=>{s.error(`Failed to remove: ${r.message}`)}}))},Z=()=>{const e=i(),a=g();return c(e.mangaProgress.removeMany.mutationOptions({onSuccess:r=>{const n=r.removed??0;s.success(`Reset progress for ${n} ${n===1?"item":"items"}`),a.invalidateQueries({queryKey:["manga-progress"]}),a.invalidateQueries({queryKey:["manga-continue-reading"]}),a.invalidateQueries({queryKey:["manga-library"]}),a.invalidateQueries(e.mangaProgress.pathFilter())},onError:r=>{s.error(`Failed to reset progress: ${r.message}`)}}))},ee=()=>{const e=S(),a=F(),{data:r}=P(),n=Q(()=>new Set(r?.ids||[]),[r]);return{toggle:t=>{const m=L(t.provider,t.id);n.has(m)?a.mutate(m):e.mutate({provider:t.provider,mangaId:t.id,title:t.title,cover:t.cover,altTitle:t.altTitle,contentRating:t.contentRating})},bookmarkedIds:n,pending:e.isPending||a.isPending}};export{Z as A,J as B,_ as M,E as a,T as b,A as c,z as d,B as e,k as f,j as g,S as h,I as i,H as j,O as k,U as l,L as m,V as n,F as o,Y as p,w as q,N as r,x as s,$ as t,ee as u,f as v,G as w,D as x,W as y,X as z};
@@ -1 +1 @@
1
- import{u as e,S as f,L as m}from"./vendor-core-DfaOB3Rn.js";import{I as l}from"./vendor-icons-DrybRNxP.js";import{k as y,r as T}from"./useMangaLibrary-COua_Bkn.js";import{d as I}from"./index-Dyiy_9L6.js";import{p as s,a as k}from"./MediaCard-DcB-RhSF.js";import{u as P}from"./usePopup-DS9jMzp6.js";const R=({data:i,anchorRect:d,bookmarked:r,onToggleBookmark:p,onMouseEnter:h,onMouseLeave:v,onRequestClose:g})=>{const{data:a,isLoading:N}=y(i.provider,i.mangaId,i.rating||"safe",i.mature||!1),{titlePreference:u}=I(),t=a?.chapters.length??0,c=[a?.status,a?.type].filter(Boolean).join(" · "),o=T({title:i.title,altTitle:a?.altTitle??i.altTitle},u);return e(k,{anchorRect:d,onMouseEnter:h,onMouseLeave:v,onRequestClose:g,children:N?e("div",{className:s.loading,children:[e("div",{className:s.spinner}),e("span",{children:"Fetching details..."})]}):e(f,{children:[e("div",{className:s.header,children:[e("div",{className:s.title,children:o}),a?.altTitle&&a.altTitle!==o&&e("div",{className:s.synopsis,children:["Also known as: ",a.altTitle]})]}),e("div",{className:s.body,children:[e("div",{className:s.metaRow,children:[c&&e("div",{className:s.metaItem,children:[e(l,{name:"book",size:14}),e("span",{children:c})]}),t>0&&e("div",{className:s.metaItem,children:e("span",{children:[t," chapter",t===1?"":"s"]})}),i.progressLabel&&e("div",{className:s.metaItem,children:[e(l,{name:"bookmark",size:12}),e("span",{children:i.progressLabel})]})]}),e("div",{className:s.synopsis,children:a?.description?a.description:"No synopsis available."}),Array.isArray(a?.genres)&&(a?.genres?.length??0)>0&&e("div",{className:s.details,children:e("div",{className:s.genres,children:a.genres.filter(Boolean).slice(0,4).map(n=>e("span",{className:s.genre,children:n},n))})})]}),e("div",{className:s.footer,children:[e("div",{className:s.primaryAction,children:e(m,{to:i.readTarget,className:s.watchBtn,children:[e(l,{name:"book",size:14}),"Read now"]})}),e("div",{className:s.secondaryActions,children:[e("button",{className:`${s.watchlistBtn} ${r?s.active:""}`,onClick:n=>{n.preventDefault(),n.stopPropagation(),p()},children:[r?e(l,{name:"check",size:12}):e(l,{name:"plus",size:12}),e("span",{children:r?"Remove":"Bookmark"})]}),e(m,{to:`/manga/${i.provider}/${encodeURIComponent(i.mangaId)}`,className:s.detailsBtn,children:"Read more"})]})]})]})})},$=()=>P();export{R as M,$ as u};
1
+ import{u as e,S as f,L as m}from"./vendor-core-DfaOB3Rn.js";import{I as l}from"./vendor-icons-DrybRNxP.js";import{k as y,r as T}from"./useMangaLibrary-DUteTFgU.js";import{d as I}from"./index-BGCpLf49.js";import{p as s,a as k}from"./MediaCard-Auk42bqx.js";import{u as P}from"./usePopup-DS9jMzp6.js";const R=({data:i,anchorRect:d,bookmarked:r,onToggleBookmark:p,onMouseEnter:h,onMouseLeave:v,onRequestClose:g})=>{const{data:a,isLoading:N}=y(i.provider,i.mangaId,i.rating||"safe",i.mature||!1),{titlePreference:u}=I(),t=a?.chapters.length??0,c=[a?.status,a?.type].filter(Boolean).join(" · "),o=T({title:i.title,altTitle:a?.altTitle??i.altTitle},u);return e(k,{anchorRect:d,onMouseEnter:h,onMouseLeave:v,onRequestClose:g,children:N?e("div",{className:s.loading,children:[e("div",{className:s.spinner}),e("span",{children:"Fetching details..."})]}):e(f,{children:[e("div",{className:s.header,children:[e("div",{className:s.title,children:o}),a?.altTitle&&a.altTitle!==o&&e("div",{className:s.synopsis,children:["Also known as: ",a.altTitle]})]}),e("div",{className:s.body,children:[e("div",{className:s.metaRow,children:[c&&e("div",{className:s.metaItem,children:[e(l,{name:"book",size:14}),e("span",{children:c})]}),t>0&&e("div",{className:s.metaItem,children:e("span",{children:[t," chapter",t===1?"":"s"]})}),i.progressLabel&&e("div",{className:s.metaItem,children:[e(l,{name:"bookmark",size:12}),e("span",{children:i.progressLabel})]})]}),e("div",{className:s.synopsis,children:a?.description?a.description:"No synopsis available."}),Array.isArray(a?.genres)&&(a?.genres?.length??0)>0&&e("div",{className:s.details,children:e("div",{className:s.genres,children:a.genres.filter(Boolean).slice(0,4).map(n=>e("span",{className:s.genre,children:n},n))})})]}),e("div",{className:s.footer,children:[e("div",{className:s.primaryAction,children:e(m,{to:i.readTarget,className:s.watchBtn,children:[e(l,{name:"book",size:14}),"Read now"]})}),e("div",{className:s.secondaryActions,children:[e("button",{className:`${s.watchlistBtn} ${r?s.active:""}`,onClick:n=>{n.preventDefault(),n.stopPropagation(),p()},children:[r?e(l,{name:"check",size:12}):e(l,{name:"plus",size:12}),e("span",{children:r?"Remove":"Bookmark"})]}),e(m,{to:`/manga/${i.provider}/${encodeURIComponent(i.mangaId)}`,className:s.detailsBtn,children:"Read more"})]})]})]})})},$=()=>P();export{R as M,$ as u};
@@ -1 +1 @@
1
- import{T as o}from"./vendor-core-DfaOB3Rn.js";import{b as a}from"./vendor-query-BBT5_sHx.js";import{$ as i}from"./index-Dyiy_9L6.js";const n=300*1e3;function u(r){return{value:r.id,label:r.label,mature:r.mature,kind:r.kind,sub:r.sub,tier:r.tier,browse:r.browse,facets:r.facets,servers:r.servers}}const d=()=>{const r=a({queryKey:["providers"],queryFn:()=>i("/api/providers"),staleTime:n,retry:1}),e=Array.isArray(r.data)?r.data:null,t=o(()=>e&&e.length>0?e.filter(s=>s.loaded).map(u):[],[e]);return{...r,options:t,isFallback:!e||e.length===0}};export{d as u};
1
+ import{T as o}from"./vendor-core-DfaOB3Rn.js";import{b as a}from"./vendor-query-BBT5_sHx.js";import{U as i}from"./index-BGCpLf49.js";const n=300*1e3;function u(r){return{value:r.id,label:r.label,mature:r.mature,kind:r.kind,sub:r.sub,tier:r.tier,browse:r.browse,facets:r.facets,servers:r.servers}}const d=()=>{const r=a({queryKey:["providers"],queryFn:()=>i("/api/providers"),staleTime:n,retry:1}),e=Array.isArray(r.data)?r.data:null,t=o(()=>e&&e.length>0?e.filter(s=>s.loaded).map(u):[],[e]);return{...r,options:t,isFallback:!e||e.length===0}};export{d as u};
@@ -1 +1 @@
1
- import{N as c,u as r,S as d}from"./vendor-core-DfaOB3Rn.js";import{I as v}from"./vendor-icons-DrybRNxP.js";import{M as T}from"./MediaCard-DcB-RhSF.js";import{T as g}from"./TvPopup-_GPNroa1.js";import{c as m,i as f}from"./useTvLibrary-DpqGf6Bp.js";import{b as i}from"./vendor-query-BBT5_sHx.js";import{h as p}from"./index-Dyiy_9L6.js";const _=c(({item:e})=>{const o=e.type==="tv"||e.type==="tvSeries"||e.type==="tvMiniSeries",s=m(e.type,e.id);return r(T,{item:{id:String(e.id),title:e.title,thumbnail:e.image,typeBadge:o?"TV":"Movie",isAdult:f(e)},linkTo:s,hoverIcon:"info",showInfoButton:!0,metaRow:r(d,{children:[e.year&&r("span",{style:{opacity:.75},children:e.year}),e.vote_average!=null&&r("span",{style:{display:"inline-flex",alignItems:"center",gap:3},children:[r(v,{name:"star",size:10}),Number(e.vote_average).toFixed(1)]})]}),renderPopup:(a,t)=>r(g,{item:e,anchorRect:a,onMouseEnter:t.onMouseEnter,onMouseLeave:t.onMouseLeave,onRequestClose:t.close})})}),u=1e3*60*5,b=e=>{const o=p(),{query:s,type:a,genre:t,year:n,sort_by:l,page:y}=e;return i({...o.tv.search.queryOptions({q:s?.trim()||"",type:a||"multi",genre:t||"",year:n&&n!=="ALL"?n:"",sortBy:l||"popularity.desc",page:y||1}),staleTime:u,retry:1})},A=(e="all",o="week",s=1,a=!0)=>{const t=p();return i({...t.tv.trending.queryOptions({mediaType:e,timeWindow:o,page:s}),enabled:a,staleTime:u,retry:1})};export{_ as T,b as a,A as u};
1
+ import{N as c,u as r,S as d}from"./vendor-core-DfaOB3Rn.js";import{I as v}from"./vendor-icons-DrybRNxP.js";import{M as T}from"./MediaCard-Auk42bqx.js";import{T as g}from"./TvPopup-CkXBuHHD.js";import{c as m,i as f}from"./useTvLibrary-BVECcmlW.js";import{b as i}from"./vendor-query-BBT5_sHx.js";import{h as p}from"./index-BGCpLf49.js";const _=c(({item:e})=>{const o=e.type==="tv"||e.type==="tvSeries"||e.type==="tvMiniSeries",s=m(e.type,e.id);return r(T,{item:{id:String(e.id),title:e.title,thumbnail:e.image,typeBadge:o?"TV":"Movie",isAdult:f(e)},linkTo:s,hoverIcon:"info",showInfoButton:!0,metaRow:r(d,{children:[e.year&&r("span",{style:{opacity:.75},children:e.year}),e.vote_average!=null&&r("span",{style:{display:"inline-flex",alignItems:"center",gap:3},children:[r(v,{name:"star",size:10}),Number(e.vote_average).toFixed(1)]})]}),renderPopup:(a,t)=>r(g,{item:e,anchorRect:a,onMouseEnter:t.onMouseEnter,onMouseLeave:t.onMouseLeave,onRequestClose:t.close})})}),u=1e3*60*5,b=e=>{const o=p(),{query:s,type:a,genre:t,year:n,sort_by:l,page:y}=e;return i({...o.tv.search.queryOptions({q:s?.trim()||"",type:a||"multi",genre:t||"",year:n&&n!=="ALL"?n:"",sortBy:l||"popularity.desc",page:y||1}),staleTime:u,retry:1})},A=(e="all",o="week",s=1,a=!0)=>{const t=p();return i({...t.tv.trending.queryOptions({mediaType:e,timeWindow:o,page:s}),enabled:a,staleTime:u,retry:1})};export{_ as T,b as a,A as u};
@@ -1 +1 @@
1
- import{T as y}from"./vendor-core-DfaOB3Rn.js";import{b as v,u as o,a as n}from"./vendor-query-BBT5_sHx.js";import{h as a,z as i,t as p}from"./index-Dyiy_9L6.js";const d=t=>String(t).toLowerCase()==="movie"?"movie":"tv",m=(t,e)=>`${d(t)}:${e}`,L=(t,e)=>{const r=d(t);return`/tv/${e}?type=${r}`},S=(t,e,r,s)=>{const u=new URLSearchParams;return u.set("type",d(t)),r!==void 0&&u.set("s",String(r)),s!==void 0&&u.set("e",String(s)),`/tv/${e}/watch?${u.toString()}`},F=t=>t.adult===!0||t.adult===1,K=["Watching","Completed","On-Hold","Dropped","Planned"],g=(t,e)=>m(t,e),$=(t="All",e=1,r=24)=>{const s=a();return v(s.tvLibrary.list.queryOptions({status:t,page:e,limit:r}))},h=()=>{const t=a();return v({...t.tvLibrary.ids.queryOptions(),staleTime:1e3*60})},C=t=>{const e=a();return v({...e.tvLibrary.check.queryOptions({id:t||""}),enabled:!!t,staleTime:1e3*60})},w=(t=24)=>{const e=a();return v(e.tvProgress.continueWatching.queryOptions({limit:t}))},O=t=>{const e=a();return v({...e.tvProgress.getByMedia.queryOptions({mediaId:t||""}),enabled:!!t})},f=(t,e,r)=>{const s=a();return v({...s.tvProgress.latest.queryOptions({mediaId:t||"",season:e,episode:r}),enabled:!!t,staleTime:0})},b=()=>{const t=a(),e=o();return n({mutationFn:async r=>{const{silent:s,...u}=r;return p.tvLibrary.add.mutate(u)},onSuccess:(r,s)=>{s.silent||i.success("Added to TV watchlist"),e.invalidateQueries({queryKey:["tv-library"]}),e.invalidateQueries(t.tvLibrary.pathFilter())},onError:r=>{i.error(`Failed to add: ${r.message}`)}})},Q=()=>{const t=a(),e=o();return n(t.tvLibrary.remove.mutationOptions({onSuccess:()=>{i.success("Removed from TV watchlist"),e.invalidateQueries({queryKey:["tv-library"]}),e.invalidateQueries({queryKey:["tv-continue-watching"]}),e.invalidateQueries(t.tvLibrary.pathFilter()),e.invalidateQueries(t.tvProgress.pathFilter())},onError:r=>{i.error(`Failed to remove: ${r.message}`)}}))},k=()=>{const t=a(),e=o();return n(t.tvLibrary.setStatus.mutationOptions({onSuccess:()=>{i.success("Status updated"),e.invalidateQueries({queryKey:["tv-library"]}),e.invalidateQueries({queryKey:["tv-continue-watching"]}),e.invalidateQueries(t.tvLibrary.pathFilter()),e.invalidateQueries(t.tvProgress.pathFilter())},onError:r=>{i.error(`Failed to update status: ${r.message}`)}}))},R=()=>{const t=a(),e=o();return n(t.tvProgress.save.mutationOptions({onSuccess:(r,s)=>{e.invalidateQueries({queryKey:["tv-progress",s.mediaId]}),e.invalidateQueries({queryKey:["tv-continue-watching"]}),e.invalidateQueries({queryKey:["tv-library"]}),e.invalidateQueries(t.tvLibrary.pathFilter()),e.invalidateQueries(t.tvProgress.pathFilter())}}))},B=()=>{const t=a(),e=o();return n(t.tvProgress.remove.mutationOptions({onSuccess:()=>{i.success("Progress reset"),e.invalidateQueries({queryKey:["tv-progress"]}),e.invalidateQueries({queryKey:["tv-continue-watching"]}),e.invalidateQueries({queryKey:["tv-library"]}),e.invalidateQueries(t.tvLibrary.pathFilter()),e.invalidateQueries(t.tvProgress.pathFilter())},onError:r=>{i.error(`Failed to reset progress: ${r.message}`)}}))},E=()=>{const t=a(),e=o();return n(t.tvLibrary.batchStatus.mutationOptions({onSuccess:r=>{i.success(`Status updated for ${r.updated??0} items`),e.invalidateQueries({queryKey:["tv-library"]}),e.invalidateQueries({queryKey:["tv-continue-watching"]}),e.invalidateQueries(t.tvLibrary.pathFilter()),e.invalidateQueries(t.tvProgress.pathFilter())},onError:r=>{i.error(`Failed to update statuses: ${r.message}`)}}))},I=()=>{const t=a(),e=o();return n(t.tvLibrary.removeMany.mutationOptions({onSuccess:r=>{const s=r.removed??0;i.success(`Removed ${s} ${s===1?"item":"items"} from TV watchlist`),e.invalidateQueries({queryKey:["tv-library"]}),e.invalidateQueries({queryKey:["tv-continue-watching"]}),e.invalidateQueries(t.tvLibrary.pathFilter()),e.invalidateQueries(t.tvProgress.pathFilter())},onError:r=>{i.error(`Failed to remove: ${r.message}`)}}))},A=()=>{const t=a(),e=o();return n(t.tvProgress.removeMany.mutationOptions({onSuccess:r=>{const s=r.removed??0;i.success(`Reset progress for ${s} ${s===1?"item":"items"}`),e.invalidateQueries({queryKey:["tv-progress"]}),e.invalidateQueries({queryKey:["tv-continue-watching"]}),e.invalidateQueries({queryKey:["tv-library"]}),e.invalidateQueries(t.tvLibrary.pathFilter()),e.invalidateQueries(t.tvProgress.pathFilter())},onError:r=>{i.error(`Failed to reset progress: ${r.message}`)}}))},M=()=>{const t=b(),e=Q(),{data:r}=h(),s=y(()=>new Set(r?.ids||[]),[r]);return{toggle:c=>{const l=g(c.mediaType,c.tmdbId);s.has(l)?e.mutate(l):t.mutate({...c,poster:c.poster||""})},bookmarkedIds:s,pending:t.isPending||e.isPending}};export{K as T,Q as a,B as b,L as c,m as d,C as e,M as f,R as g,k as h,F as i,f as j,O as k,$ as l,E as m,d as n,I as o,A as p,S as t,w as u};
1
+ import{T as y}from"./vendor-core-DfaOB3Rn.js";import{b as v,u as o,a as n}from"./vendor-query-BBT5_sHx.js";import{h as a,z as i,t as p}from"./index-BGCpLf49.js";const d=t=>String(t).toLowerCase()==="movie"?"movie":"tv",m=(t,e)=>`${d(t)}:${e}`,L=(t,e)=>{const r=d(t);return`/tv/${e}?type=${r}`},S=(t,e,r,s)=>{const u=new URLSearchParams;return u.set("type",d(t)),r!==void 0&&u.set("s",String(r)),s!==void 0&&u.set("e",String(s)),`/tv/${e}/watch?${u.toString()}`},F=t=>t.adult===!0||t.adult===1,K=["Watching","Completed","On-Hold","Dropped","Planned"],g=(t,e)=>m(t,e),$=(t="All",e=1,r=24)=>{const s=a();return v(s.tvLibrary.list.queryOptions({status:t,page:e,limit:r}))},h=()=>{const t=a();return v({...t.tvLibrary.ids.queryOptions(),staleTime:1e3*60})},C=t=>{const e=a();return v({...e.tvLibrary.check.queryOptions({id:t||""}),enabled:!!t,staleTime:1e3*60})},w=(t=24)=>{const e=a();return v(e.tvProgress.continueWatching.queryOptions({limit:t}))},O=t=>{const e=a();return v({...e.tvProgress.getByMedia.queryOptions({mediaId:t||""}),enabled:!!t})},f=(t,e,r)=>{const s=a();return v({...s.tvProgress.latest.queryOptions({mediaId:t||"",season:e,episode:r}),enabled:!!t,staleTime:0})},b=()=>{const t=a(),e=o();return n({mutationFn:async r=>{const{silent:s,...u}=r;return p.tvLibrary.add.mutate(u)},onSuccess:(r,s)=>{s.silent||i.success("Added to TV watchlist"),e.invalidateQueries({queryKey:["tv-library"]}),e.invalidateQueries(t.tvLibrary.pathFilter())},onError:r=>{i.error(`Failed to add: ${r.message}`)}})},Q=()=>{const t=a(),e=o();return n(t.tvLibrary.remove.mutationOptions({onSuccess:()=>{i.success("Removed from TV watchlist"),e.invalidateQueries({queryKey:["tv-library"]}),e.invalidateQueries({queryKey:["tv-continue-watching"]}),e.invalidateQueries(t.tvLibrary.pathFilter()),e.invalidateQueries(t.tvProgress.pathFilter())},onError:r=>{i.error(`Failed to remove: ${r.message}`)}}))},k=()=>{const t=a(),e=o();return n(t.tvLibrary.setStatus.mutationOptions({onSuccess:()=>{i.success("Status updated"),e.invalidateQueries({queryKey:["tv-library"]}),e.invalidateQueries({queryKey:["tv-continue-watching"]}),e.invalidateQueries(t.tvLibrary.pathFilter()),e.invalidateQueries(t.tvProgress.pathFilter())},onError:r=>{i.error(`Failed to update status: ${r.message}`)}}))},R=()=>{const t=a(),e=o();return n(t.tvProgress.save.mutationOptions({onSuccess:(r,s)=>{e.invalidateQueries({queryKey:["tv-progress",s.mediaId]}),e.invalidateQueries({queryKey:["tv-continue-watching"]}),e.invalidateQueries({queryKey:["tv-library"]}),e.invalidateQueries(t.tvLibrary.pathFilter()),e.invalidateQueries(t.tvProgress.pathFilter())}}))},B=()=>{const t=a(),e=o();return n(t.tvProgress.remove.mutationOptions({onSuccess:()=>{i.success("Progress reset"),e.invalidateQueries({queryKey:["tv-progress"]}),e.invalidateQueries({queryKey:["tv-continue-watching"]}),e.invalidateQueries({queryKey:["tv-library"]}),e.invalidateQueries(t.tvLibrary.pathFilter()),e.invalidateQueries(t.tvProgress.pathFilter())},onError:r=>{i.error(`Failed to reset progress: ${r.message}`)}}))},E=()=>{const t=a(),e=o();return n(t.tvLibrary.batchStatus.mutationOptions({onSuccess:r=>{i.success(`Status updated for ${r.updated??0} items`),e.invalidateQueries({queryKey:["tv-library"]}),e.invalidateQueries({queryKey:["tv-continue-watching"]}),e.invalidateQueries(t.tvLibrary.pathFilter()),e.invalidateQueries(t.tvProgress.pathFilter())},onError:r=>{i.error(`Failed to update statuses: ${r.message}`)}}))},I=()=>{const t=a(),e=o();return n(t.tvLibrary.removeMany.mutationOptions({onSuccess:r=>{const s=r.removed??0;i.success(`Removed ${s} ${s===1?"item":"items"} from TV watchlist`),e.invalidateQueries({queryKey:["tv-library"]}),e.invalidateQueries({queryKey:["tv-continue-watching"]}),e.invalidateQueries(t.tvLibrary.pathFilter()),e.invalidateQueries(t.tvProgress.pathFilter())},onError:r=>{i.error(`Failed to remove: ${r.message}`)}}))},A=()=>{const t=a(),e=o();return n(t.tvProgress.removeMany.mutationOptions({onSuccess:r=>{const s=r.removed??0;i.success(`Reset progress for ${s} ${s===1?"item":"items"}`),e.invalidateQueries({queryKey:["tv-progress"]}),e.invalidateQueries({queryKey:["tv-continue-watching"]}),e.invalidateQueries({queryKey:["tv-library"]}),e.invalidateQueries(t.tvLibrary.pathFilter()),e.invalidateQueries(t.tvProgress.pathFilter())},onError:r=>{i.error(`Failed to reset progress: ${r.message}`)}}))},M=()=>{const t=b(),e=Q(),{data:r}=h(),s=y(()=>new Set(r?.ids||[]),[r]);return{toggle:c=>{const l=g(c.mediaType,c.tmdbId);s.has(l)?e.mutate(l):t.mutate({...c,poster:c.poster||""})},bookmarkedIds:s,pending:t.isPending||e.isPending}};export{K as T,Q as a,B as b,L as c,m as d,C as e,M as f,R as g,k as h,F as i,f as j,O as k,$ as l,E as m,d as n,I as o,A as p,S as t,w as u};
@@ -33,11 +33,11 @@
33
33
  background-color: #0c0d12;
34
34
  }
35
35
  </style>
36
- <script type="module" crossorigin src="/assets/index-Dyiy_9L6.js"></script>
36
+ <script type="module" crossorigin src="/assets/index-BGCpLf49.js"></script>
37
37
  <link rel="modulepreload" crossorigin href="/assets/vendor-core-DfaOB3Rn.js">
38
38
  <link rel="modulepreload" crossorigin href="/assets/vendor-query-BBT5_sHx.js">
39
39
  <link rel="modulepreload" crossorigin href="/assets/vendor-icons-DrybRNxP.js">
40
- <link rel="stylesheet" crossorigin href="/assets/index-Cc1Up_NI.css">
40
+ <link rel="stylesheet" crossorigin href="/assets/index-w_r2lTCe.css">
41
41
  </head>
42
42
 
43
43
  <body>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@serifpersia/dango",
3
- "version": "3.4.9",
3
+ "version": "3.5.1",
4
4
  "description": "A web application for watching anime.",
5
5
  "main": "orchestrator.js",
6
6
  "repository": {
@@ -83,5 +83,5 @@
83
83
  "allowScripts": {
84
84
  "esbuild@0.28.2": true
85
85
  },
86
- "versionDate": "2026-10-03T15:46:33.503Z"
86
+ "versionDate": "2026-10-08T01:33:46.617Z"
87
87
  }
@@ -45,14 +45,8 @@ function mapMalMangaXmlStatus(status) {
45
45
  return 'Planned';
46
46
  }
47
47
  }
48
- const gatewayLog = logger.child({ module: 'DiscordGatewayRoutes' });
49
48
  const TRACKER_TOKEN_KEY = 'tracker_anilist_token';
50
49
  const TRACKER_USER_KEY = 'tracker_anilist_user';
51
- function maskToken(t) {
52
- if (!t)
53
- return 'none';
54
- return t.slice(0, 8) + '...' + t.slice(-4);
55
- }
56
50
  export function getHonoClientIp(c) {
57
51
  try {
58
52
  return getConnInfo(c).remote.address;
@@ -166,7 +166,6 @@ export const CONFIG = {
166
166
  RCLONE_REMOTE: process.env.RCLONE_REMOTE,
167
167
  SYNC_PROVIDER: process.env.SYNC_PROVIDER,
168
168
  DISCORD_CLIENT_ID: process.env.DISCORD_CLIENT_ID || SHIPPED_DEFAULTS.DISCORD_CLIENT_ID || undefined,
169
- DISCORD_ROLES_WORKER_URL: process.env.DISCORD_ROLES_WORKER_URL || SHIPPED_DEFAULTS.DISCORD_ROLES_WORKER_URL || '',
170
169
  APP_PASSWORD_HASH: process.env.APP_PASSWORD_HASH || '',
171
170
  PROVIDER_REPO_URL: process.env.PROVIDER_REPO_URL || SHIPPED_DEFAULTS.PROVIDER_REPO_URL || '',
172
171
  PROVIDER_ENABLED: process.env.PROVIDER_ENABLED || '',
@@ -1,3 +1,4 @@
1
+ import { sql } from 'drizzle-orm';
1
2
  import { integer, primaryKey, real, sqliteTable, text } from 'drizzle-orm/sqlite-core';
2
3
  export const watchedEpisodes = sqliteTable('watched_episodes', {
3
4
  showId: text('showId').notNull(),
@@ -43,12 +44,12 @@ export const queue = sqliteTable('queue', {
43
44
  export const discoveredNotifications = sqliteTable('discovered_notifications', {
44
45
  showId: text('showId').notNull(),
45
46
  episodeNumber: text('episodeNumber').notNull(),
46
- discoveredAt: text('discoveredAt'),
47
+ discoveredAt: text('discoveredAt').default(sql `CURRENT_TIMESTAMP`),
47
48
  }, (t) => [primaryKey({ columns: [t.showId, t.episodeNumber] })]);
48
49
  export const dismissedNotifications = sqliteTable('dismissed_notifications', {
49
50
  showId: text('showId').notNull(),
50
51
  episodeNumber: text('episodeNumber').notNull(),
51
- dismissedAt: text('dismissedAt'),
52
+ dismissedAt: text('dismissedAt').default(sql `CURRENT_TIMESTAMP`),
52
53
  }, (t) => [primaryKey({ columns: [t.showId, t.episodeNumber] })]);
53
54
  export const tempShowIds = sqliteTable('temp_show_ids', {
54
55
  id: text('id').notNull(),
@@ -1,8 +1,5 @@
1
1
  import logger from '../logger.js';
2
- import { CONFIG } from '../config.js';
3
- import { SettingsRepository } from '../repositories/settings.repository.js';
4
2
  import { InsightsRepository } from '../repositories/insights.repository.js';
5
- import { parseJsonBody } from '../utils/http.utils.js';
6
3
  import { anilistRequest } from '../lib/anilist.js';
7
4
  function parseGenres(raw) {
8
5
  if (!raw)
@@ -196,11 +193,6 @@ export async function computeRecCandidates(taste) {
196
193
  };
197
194
  });
198
195
  }
199
- const SETTING_KEY = 'discordLinkedUser';
200
- const SYNC_INTERVAL_MS = 10 * 60 * 1000;
201
- let syncTimer = null;
202
- let lastSyncedSeconds = null;
203
- let lastSyncedTasteHash = null;
204
196
  let recCache = null;
205
197
  function tasteHashOf(taste) {
206
198
  return JSON.stringify([taste.genreComp, taste.genreDrop, taste.knownIds.length]);
@@ -222,144 +214,3 @@ export async function getCachedRecommendations(db) {
222
214
  const taste = await computeTasteProfile(db);
223
215
  return refreshCandidatesIfNeeded(taste, tasteHashOf(taste));
224
216
  }
225
- export async function getLinkedDiscordUser(db) {
226
- const row = await SettingsRepository.getByKey(db, SETTING_KEY);
227
- if (!row?.value)
228
- return null;
229
- try {
230
- return JSON.parse(row.value);
231
- }
232
- catch {
233
- return null;
234
- }
235
- }
236
- export async function setLinkedDiscordUser(db, user) {
237
- if (!user) {
238
- await SettingsRepository.deleteByKey(db, SETTING_KEY);
239
- lastSyncedSeconds = null;
240
- lastSyncedTasteHash = null;
241
- recCache = null;
242
- }
243
- else {
244
- await SettingsRepository.upsert(db, SETTING_KEY, JSON.stringify(user));
245
- }
246
- }
247
- export async function computeDiscordSyncStats(db) {
248
- const [core, watchedShows] = (await Promise.all([
249
- InsightsRepository.getCoreStats(db),
250
- InsightsRepository.getWatchedShowsMeta(db),
251
- ]));
252
- const genreCounts = {};
253
- for (const show of watchedShows) {
254
- if (!show.genres)
255
- continue;
256
- let genres = [];
257
- try {
258
- genres = show.genres.startsWith('[')
259
- ? JSON.parse(show.genres)
260
- : show.genres.split(',').map((g) => g.trim());
261
- }
262
- catch {
263
- // ignore
264
- }
265
- for (const g of genres) {
266
- genreCounts[g] = (genreCounts[g] || 0) + 1;
267
- }
268
- }
269
- const topGenre = Object.entries(genreCounts).sort((a, b) => b[1] - a[1])[0]?.[0] || '';
270
- const genres = Object.entries(genreCounts)
271
- .sort((a, b) => b[1] - a[1])
272
- .slice(0, 3)
273
- .map(([name]) => name);
274
- const completedCount = core?.completedCount || 0;
275
- const totalWatchlist = core?.totalWatchlist || 0;
276
- return {
277
- totalSeconds: core?.totalSeconds || 0,
278
- topGenre,
279
- genres,
280
- totalEpisodes: core?.totalEpisodes || 0,
281
- totalAnime: core?.totalAnime || 0,
282
- completedCount,
283
- completionRate: totalWatchlist > 0 ? Math.round((completedCount / totalWatchlist) * 100) : 0,
284
- };
285
- }
286
- export async function syncDiscordRoles(db, force = false) {
287
- const workerUrl = CONFIG.DISCORD_ROLES_WORKER_URL;
288
- if (!workerUrl) {
289
- return { success: false, message: 'DISCORD_ROLES_WORKER_URL not configured' };
290
- }
291
- const user = await getLinkedDiscordUser(db);
292
- if (!user) {
293
- return { success: false, message: 'No Discord user linked in database' };
294
- }
295
- const { totalSeconds, topGenre, genres, totalEpisodes, totalAnime, completedCount, completionRate, } = await computeDiscordSyncStats(db);
296
- const taste = await computeTasteProfile(db);
297
- const tasteHash = tasteHashOf(taste);
298
- if (!force &&
299
- lastSyncedSeconds !== null &&
300
- totalSeconds === lastSyncedSeconds &&
301
- lastSyncedTasteHash === tasteHash) {
302
- return { success: true, message: 'Watch time unchanged, skipped sync', skipped: true };
303
- }
304
- taste.candidates = await refreshCandidatesIfNeeded(taste, tasteHash);
305
- try {
306
- const res = await fetch(`${workerUrl}/api/sync`, {
307
- method: 'POST',
308
- headers: { 'Content-Type': 'application/json' },
309
- body: JSON.stringify({
310
- discordId: user.id,
311
- username: user.username,
312
- totalSeconds,
313
- topGenre,
314
- genres,
315
- totalEpisodes,
316
- totalAnime,
317
- completedCount,
318
- completionRate,
319
- taste,
320
- force,
321
- }),
322
- });
323
- const data = (await parseJsonBody(res).catch(() => ({})));
324
- if (!res.ok || data.error) {
325
- logger.warn({ error: data.error || res.statusText, code: data.code, failures: data.failures }, 'Discord role sync returned error');
326
- return {
327
- success: false,
328
- message: data.error || 'Worker sync failed',
329
- code: data.code,
330
- failures: data.failures,
331
- };
332
- }
333
- lastSyncedSeconds = totalSeconds;
334
- lastSyncedTasteHash = tasteHash;
335
- logger.info({
336
- user: user.username,
337
- rank: data.rank,
338
- dere: data.dere,
339
- totalHours: Math.round(totalSeconds / 3600),
340
- }, 'Discord roles synced successfully');
341
- return { success: true, message: data.message || 'Roles synced', dere: data.dere };
342
- }
343
- catch (err) {
344
- logger.warn({ err: err?.message }, 'Failed to communicate with Discord roles worker');
345
- return { success: false, message: err?.message || 'Network error' };
346
- }
347
- }
348
- export function initDiscordRolesSync(db) {
349
- const workerUrl = CONFIG.DISCORD_ROLES_WORKER_URL;
350
- if (!workerUrl)
351
- return;
352
- setTimeout(() => {
353
- syncDiscordRoles(db).catch((err) => {
354
- logger.warn({ err: err?.message }, 'Startup Discord roles sync failed');
355
- });
356
- }, 5000).unref();
357
- if (syncTimer)
358
- clearInterval(syncTimer);
359
- syncTimer = setInterval(() => {
360
- syncDiscordRoles(db).catch((err) => {
361
- logger.warn({ err: err?.message }, 'Periodic Discord roles sync failed');
362
- });
363
- }, SYNC_INTERVAL_MS);
364
- syncTimer.unref();
365
- }
@@ -21,7 +21,6 @@ import { createHonoApp } from './app-hono.js';
21
21
  import { startWatchlistDiscovery, stopWatchlistDiscovery } from './hono/watchlist.js';
22
22
  import { checkAnilistStatus } from './lib/anilist.js';
23
23
  import { offlineDb } from './lib/offline-db.js';
24
- import { initDiscordRolesSync } from './lib/discord-roles-sync.service.js';
25
24
  const app = createHonoApp(() => ({ shuttingDown: isShuttingDown, dbReady: !!db }), () => ({
26
25
  shuttingDown: isShuttingDown,
27
26
  dbsReady: !!db && !!mangaDb && !!tvDb && !!asmrDb,
@@ -156,7 +155,6 @@ async function main() {
156
155
  await discordRPCService.setEnabled(isRpcEnabled);
157
156
  discordGatewayService.setEnabled(isRpcEnabled);
158
157
  checkAnilistStatus().catch(() => { });
159
- initDiscordRolesSync(db);
160
158
  if (CONFIG.PROVIDER_REPO_URL.trim()) {
161
159
  setInterval(() => {
162
160
  refreshRemoteProviders().catch((err) => logger.error({ err }, 'periodic providers refresh failed'));
@@ -2,6 +2,5 @@ export const SHIPPED_DEFAULTS = {
2
2
  "GITHUB_CLIENT_ID": "Ov23liT1ZtPk7XtN9PZk",
3
3
  "GOOGLE_AUTH_WORKER_URL": "https://google-auth-client.ramiserifpersia.workers.dev",
4
4
  "DISCORD_CLIENT_ID": "1512253778022367426",
5
- "DISCORD_ROLES_WORKER_URL": "https://dango-discord-roles.ramiserifpersia.workers.dev",
6
5
  "PROVIDER_REPO_URL": "https://raw.githubusercontent.com/serifpersia/dango-providers/main/registry.json"
7
6
  };
@@ -630,6 +630,7 @@ export async function initializeDatabase(dbPath) {
630
630
  db.run('DELETE FROM legacy_id_mapping WHERE legacyId NOT IN (SELECT id FROM watchlist) AND numericId NOT IN (SELECT id FROM watchlist)');
631
631
  db.run('DELETE FROM dismissed_notifications WHERE EXISTS (SELECT 1 FROM watched_episodes we WHERE we.showId = dismissed_notifications.showId AND we.episodeNumber = dismissed_notifications.episodeNumber)');
632
632
  db.run('DELETE FROM discovered_notifications WHERE EXISTS (SELECT 1 FROM watched_episodes we WHERE we.showId = discovered_notifications.showId AND we.episodeNumber = discovered_notifications.episodeNumber)');
633
+ db.run(`UPDATE discovered_notifications SET discoveredAt = datetime('now') WHERE discoveredAt IS NULL`);
633
634
  const addCol = (tbl, col, type) => {
634
635
  const columns = db.all(`PRAGMA table_info(${tbl})`);
635
636
  if (!columns.some((c) => c.name === col))
@@ -4,7 +4,6 @@ import { discordRPCService } from '../../discord-rpc.js';
4
4
  import { discordGatewayService } from '../../discord-gateway.js';
5
5
  import { getDlsitePoster, getMangadexCover } from '../../hono/watchlist.js';
6
6
  import { updateEnvFile } from '../../utils/env.utils.js';
7
- import { CONFIG } from '../../config.js';
8
7
  import logger from '../../logger.js';
9
8
  function maskToken(t) {
10
9
  if (!t)
@@ -283,7 +282,4 @@ export const discordRouter = router({
283
282
  throw failed('Failed to remove token');
284
283
  }
285
284
  }),
286
- rolesConfig: protectedProcedure.query(() => {
287
- return { workerUrl: CONFIG.DISCORD_ROLES_WORKER_URL || null };
288
- }),
289
285
  });
@@ -1,22 +1,6 @@
1
1
  import { protectedProcedure, router } from '../index.js';
2
2
  import { getWatchInsights, getGenreCards } from '../../lib/insights.js';
3
- import { computeDiscordSyncStats, getCachedRecommendations, getLinkedDiscordUser, setLinkedDiscordUser, syncDiscordRoles, } from '../../lib/discord-roles-sync.service.js';
4
- import { defineSchema, optStr, reqObj, reqStr } from '../validation.js';
5
- const discordUserInput = () => defineSchema((value) => {
6
- if (value === undefined || value === null)
7
- return {};
8
- const raw = reqObj(value).user;
9
- if (raw === undefined || raw === null)
10
- return { user: null };
11
- const obj = reqObj(raw);
12
- return {
13
- user: {
14
- id: reqStr(obj, 'id'),
15
- username: optStr(obj, 'username') ?? '',
16
- avatar: optStr(obj, 'avatar') ?? null,
17
- },
18
- };
19
- });
3
+ import { getCachedRecommendations } from '../../lib/recommendations.service.js';
20
4
  export const insightsRouter = router({
21
5
  summary: protectedProcedure.query(async ({ ctx }) => {
22
6
  return getWatchInsights(ctx.db);
@@ -27,18 +11,4 @@ export const insightsRouter = router({
27
11
  recommendations: protectedProcedure.query(async ({ ctx }) => {
28
12
  return { candidates: await getCachedRecommendations(ctx.db) };
29
13
  }),
30
- discordSyncStats: protectedProcedure.query(async ({ ctx }) => {
31
- return computeDiscordSyncStats(ctx.db);
32
- }),
33
- discordUser: protectedProcedure.query(async ({ ctx }) => {
34
- return { user: await getLinkedDiscordUser(ctx.db) };
35
- }),
36
- setDiscordUser: protectedProcedure.input(discordUserInput()).mutation(async ({ ctx, input }) => {
37
- const user = input.user ?? null;
38
- await setLinkedDiscordUser(ctx.db, user);
39
- return { success: true, user };
40
- }),
41
- discordSyncNow: protectedProcedure.mutation(async ({ ctx }) => {
42
- return syncDiscordRoles(ctx.db, true);
43
- }),
44
14
  });
@@ -6,7 +6,6 @@ import { discordRPCService } from '../../discord-rpc.js';
6
6
  import logger from '../../logger.js';
7
7
  import { offlineDb } from '../../lib/offline-db.js';
8
8
  import { translateTexts } from '../../lib/translate.js';
9
- import { getMachineId } from '../../utils/machine-id.js';
10
9
  import { getMalImportStatus, requestMalImportCancel } from '../../lib/mal-import.js';
11
10
  import { defineSchema, reqObj } from '../validation.js';
12
11
  import { keyInput, setSettingInput } from '../validation.js';
@@ -76,15 +75,6 @@ export const settingsRouter = router({
76
75
  });
77
76
  return { message: 'Offline database refresh started' };
78
77
  }),
79
- installationId: protectedProcedure.query(() => {
80
- try {
81
- return { id: getMachineId() };
82
- }
83
- catch (err) {
84
- logger.error({ err }, 'Failed to get machine ID');
85
- throw new TRPCError({ code: 'INTERNAL_SERVER_ERROR', message: 'Failed to get machine ID' });
86
- }
87
- }),
88
78
  translate: protectedProcedure.input(translateInput()).mutation(async ({ input }) => {
89
79
  const texts = input.texts;
90
80
  const source = input.source ?? 'ja';
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dango-server",
3
3
  "private": true,
4
- "version": "3.4.9",
4
+ "version": "3.5.1",
5
5
  "type": "module",
6
6
  "engines": {
7
7
  "node": ">=22.12.0"
@@ -1,2 +0,0 @@
1
- const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/AnimeInfo-pfps3FSr.js","assets/vendor-core-DfaOB3Rn.js","assets/vendor-icons-DrybRNxP.js","assets/useAnimeInfoData-DJrFWjlu.js","assets/vendor-query-BBT5_sHx.js","assets/index-Dyiy_9L6.js","assets/index-Cc1Up_NI.css","assets/QueueOptionsButton-B3cSHa0L.js","assets/vendor-ui-3qrARCPr.js","assets/QueueOptionsButton-CM15uQn8.css","assets/AnimeMetaDetails-BogOi_bM.js","assets/AnimeMetaDetails-BwNQnRCd.css","assets/SynopsisText-B3Sa5pRv.js","assets/useIsMobile-Dvx1uJ82.js","assets/SynopsisText-CYv2W3yR.css","assets/AnimeInfo-BFWtHlPC.css"])))=>i.map(i=>d[i]);
2
- import{a2 as r,_ as t}from"./index-Dyiy_9L6.js";import{u as o,P as e,z as i}from"./vendor-core-DfaOB3Rn.js";import"./vendor-query-BBT5_sHx.js";import"./vendor-icons-DrybRNxP.js";const a=i(()=>t(()=>import("./AnimeInfo-pfps3FSr.js"),__vite__mapDeps([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15])));function p(){return o(e,{fallback:o(r,{}),children:o(a,{})})}export{p as default};
@@ -1 +0,0 @@
1
- import{h as R,T as i,d as W,u as e,L as P}from"./vendor-core-DfaOB3Rn.js";import{b as C,u as O,a as A}from"./vendor-query-BBT5_sHx.js";import{t as p,a1 as F,d as V,z as S,f as k}from"./index-Dyiy_9L6.js";import{I as l}from"./vendor-icons-DrybRNxP.js";const Y="_header_1j07c_1",q="_fadeIn_1j07c_1",H="_personaBadge_1j07c_9",z="_personaInfo_1j07c_37",E="_personaLabel_1j07c_42",U="_personaValue_1j07c_51",K="_statsGrid_1j07c_57",Q="_slideUpIn_1j07c_1",J="_statCard_1j07c_65",X="_statIcon_1j07c_82",Z="_statInfo_1j07c_98",ee="_statValue_1j07c_103",ae="_statLabel_1j07c_112",te="_wideSection_1j07c_120",se="_sectionHeader_1j07c_132",ne="_yearSelect_1j07c_147",re="_heatmapLayout_1j07c_176",ce="_dayLabels_1j07c_183",le="_heatmapWrapper_1j07c_195",oe="_heatmapMonthLabels_1j07c_201",ie="_heatmapGrid_1j07c_212",de="_heatBox_1j07c_220",he="_hiddenBox_1j07c_229",me="_level0_1j07c_240",pe="_level1_1j07c_243",_e="_level2_1j07c_247",ue="_level3_1j07c_251",ge="_level4_1j07c_255",Ne="_heatmapFooter_1j07c_260",ve="_heatLegend_1j07c_267",ye="_chartsContainer_1j07c_277",be="_chartWrapper_1j07c_285",je="_hourlyChart_1j07c_312",fe="_hourlyBarContainer_1j07c_320",Ce="_hourlyBar_1j07c_320",Se="_hourlyLabel_1j07c_346",ke="_chartSubtext_1j07c_353",Le="_popScale_1j07c_365",Be="_popTrack_1j07c_369",we="_popThumb_1j07c_377",Ie="_popLabels_1j07c_389",De="_popDescription_1j07c_399",xe="_seasonalChart_1j07c_414",Te="_seasonalBarContainer_1j07c_422",Me="_seasonalBar_1j07c_422",$e="_seasonalLabel_1j07c_444",Ge="_genreList_1j07c_452",Re="_genreRow_1j07c_459",We="_genreInfo_1j07c_465",Pe="_genreName_1j07c_472",Oe="_genreCount_1j07c_479",Ae="_genreBarBg_1j07c_486",Fe="_genreBar_1j07c_486",Ve="_warningSection_1j07c_499",Ye="_warningHeader_1j07c_510",qe="_droppedGrid_1j07c_526",He="_droppedCard_1j07c_532",ze="_droppedName_1j07c_549",Ee="_droppedDate_1j07c_556",Ue="_genreGrid_1j07c_597",Ke="_genreCard_1j07c_609",Qe="_rankBadge_1j07c_618",Je="_rankNumber_1j07c_631",Xe="_genreTitle_1j07c_637",Ze="_genreStats_1j07c_645",ea="_posterRow_1j07c_653",aa="_miniPoster_1j07c_660",ta="_genreCardSkeleton_1j07c_674",sa="_pulse_1j07c_1",na="_skeletonRank_1j07c_682",ra="_skeletonTitle_1j07c_696",ca="_skeletonStats_1j07c_709",la="_skeletonPosters_1j07c_721",oa="_recSection_1j07c_752",ia="_recSubtext_1j07c_763",da="_recGrid_1j07c_769",ha="_recCard_1j07c_775",ma="_recCover_1j07c_790",pa="_recInfo_1j07c_798",_a="_recTitle_1j07c_805",ua="_recMatch_1j07c_816",ga="_recMeta_1j07c_822",Na="_recWhy_1j07c_827",va="_recNote_1j07c_832",ya="_recAddBtn_1j07c_838",a={header:Y,fadeIn:q,personaBadge:H,personaInfo:z,personaLabel:E,personaValue:U,statsGrid:K,slideUpIn:Q,statCard:J,statIcon:X,statInfo:Z,statValue:ee,statLabel:ae,wideSection:te,sectionHeader:se,yearSelect:ne,heatmapLayout:re,dayLabels:ce,heatmapWrapper:le,heatmapMonthLabels:oe,heatmapGrid:ie,heatBox:de,hiddenBox:he,level0:me,level1:pe,level2:_e,level3:ue,level4:ge,heatmapFooter:Ne,heatLegend:ve,chartsContainer:ye,chartWrapper:be,hourlyChart:je,hourlyBarContainer:fe,hourlyBar:Ce,hourlyLabel:Se,chartSubtext:ke,popScale:Le,popTrack:Be,popThumb:we,popLabels:Ie,popDescription:De,seasonalChart:xe,seasonalBarContainer:Te,seasonalBar:Me,seasonalLabel:$e,genreList:Ge,genreRow:Re,genreInfo:We,genreName:Pe,genreCount:Oe,genreBarBg:Ae,genreBar:Fe,warningSection:Ve,warningHeader:Ye,droppedGrid:qe,droppedCard:He,droppedName:ze,droppedDate:Ee,genreGrid:Ue,genreCard:Ke,rankBadge:Qe,rankNumber:Je,genreTitle:Xe,genreStats:Ze,posterRow:ea,miniPoster:aa,genreCardSkeleton:ta,pulse:sa,skeletonRank:na,skeletonTitle:ra,skeletonStats:ca,skeletonPosters:la,recSection:oa,recSubtext:ia,recGrid:da,recCard:ha,recCover:ma,recInfo:pa,recTitle:_a,recMatch:ua,recMeta:ga,recWhy:Na,recNote:va,recAddBtn:ya},Sa=()=>{R(()=>{document.title="Insights - dango"},[]);const{data:n,isLoading:L,isError:B}=C({queryKey:["insights"],queryFn:async()=>await p.insights.summary.query()}),{data:w,isLoading:_}=F(),{titlePreference:I}=V(),u=O(),{data:g}=C({queryKey:["insights-recommendations"],queryFn:async()=>await p.insights.recommendations.query(),staleTime:300*1e3,retry:1}),N=A({mutationFn:async t=>{await p.watchlist.add.mutate({id:String(t.id),name:t.title,thumbnail:t.cover||"",status:"Plan to Watch",type:t.format})},onSuccess:()=>{u.invalidateQueries({queryKey:["watchlist"]}),u.invalidateQueries({queryKey:["insights-recommendations"]}),S.success("Added to Plan to Watch")},onError:()=>S.error("Could not add to watchlist")}),v=i(()=>[...g?.candidates||[]].sort((t,s)=>s.matchPct-t.matchPct).slice(0,6),[g]),y=t=>{const s=t.name||t.englishName||t.nativeName||"";switch(I){case"nativeName":return t.nativeName||s;case"englishName":return t.englishName||s;default:return s}},[d,D]=W(new Date().getFullYear()),x=i(()=>{if(!n?.activityGrid)return[new Date().getFullYear()];const t=new Set(n.activityGrid.map(s=>parseInt(s.day.split("-")[0])));return t.add(new Date().getFullYear()),Array.from(t).sort((s,r)=>r-s)},[n]),b=i(()=>{if(!n?.activityGrid)return[];const t=new Map(n.activityGrid.map(m=>[m.day,m.count])),s=[],r=new Date(d,0,1),o=new Date(d,11,31),h=r.getDay(),c=new Date(r);for(c.setDate(c.getDate()-h);(c<=o||c.getDay()!==0)&&!(c>o&&c.getDay()===0);){const m=c.getFullYear(),$=String(c.getMonth()+1).padStart(2,"0"),G=String(c.getDate()).padStart(2,"0"),j=`${m}-${$}-${G}`,f=c<r||c>o;s.push({date:j,count:f?0:t.get(j)||0,month:c.getMonth(),isOutOfRange:f}),c.setDate(c.getDate()+1)}return s},[n,d]),T=i(()=>n?.seasonality?.length?Math.max(...n.seasonality.map(t=>t.seconds),1):0,[n]),M=i(()=>n?.hourlyDist?.length?Math.max(...n.hourlyDist.map(t=>t.count),1):0,[n]);return L||_?e("div",{className:a.loading,children:"Analyzing your anime lifestyle..."}):B?e("div",{className:a.error,children:"Could not load insights. Data might be syncing."}):n?e("div",{className:"page-container",children:[e("div",{className:a.header,children:[e("h2",{className:"section-title",children:"Watch Insights"}),e("div",{className:a.personaBadge,children:[e(l,{name:"user-astronaut"}),e("div",{className:a.personaInfo,children:[e("span",{className:a.personaLabel,children:"Your Persona"}),e("span",{className:a.personaValue,children:n.persona})]})]})]}),e("div",{className:a.statsGrid,children:[e("div",{className:a.statCard,children:[e("div",{className:a.statIcon,style:{background:"rgba(138, 79, 255, 0.2)",color:"var(--accent)"},children:e(l,{name:"clock"})}),e("div",{className:a.statInfo,children:[e("span",{className:a.statValue,children:[n.totalHours,"h"]}),e("span",{className:a.statLabel,children:"Watch Time"})]})]}),e("div",{className:a.statCard,children:[e("div",{className:a.statIcon,style:{background:"rgba(167, 139, 250, 0.2)",color:"#a78bfa"},children:e(l,{name:"bookmark"})}),e("div",{className:a.statInfo,children:[e("span",{className:a.statValue,children:n.totalAnime}),e("span",{className:a.statLabel,children:"Total Anime"})]})]}),e("div",{className:a.statCard,children:[e("div",{className:a.statIcon,style:{background:"rgba(34, 197, 94, 0.2)",color:"#22c55e"},children:e(l,{name:"check-circle"})}),e("div",{className:a.statInfo,children:[e("span",{className:a.statValue,children:n.totalEpisodes}),e("span",{className:a.statLabel,children:"Episodes Watched"})]})]}),e("div",{className:a.statCard,children:[e("div",{className:a.statIcon,style:{background:"rgba(34, 197, 94, 0.2)",color:"#22c55e"},children:e(l,{name:"check-circle"})}),e("div",{className:a.statInfo,children:[e("span",{className:a.statValue,children:[n.completionRate,"%"]}),e("span",{className:a.statLabel,children:"Completion Rate"})]})]}),e("div",{className:a.statCard,children:[e("div",{className:a.statIcon,style:{background:"rgba(45, 212, 191, 0.2)",color:"#2dd4bf"},children:e(l,{name:"tv"})}),e("div",{className:a.statInfo,children:[e("span",{className:a.statValue,children:n.completedAnime}),e("span",{className:a.statLabel,children:"Completed Shows"})]})]}),e("div",{className:a.statCard,children:[e("div",{className:a.statIcon,style:{background:"rgba(239, 68, 68, 0.2)",color:"#ef4444"},children:e(l,{name:"fire"})}),e("div",{className:a.statInfo,children:[e("span",{className:a.statValue,children:n.bingeFactor}),e("span",{className:a.statLabel,children:"Max Daily Binge"})]})]}),e("div",{className:a.statCard,children:[e("div",{className:a.statIcon,style:{background:"rgba(59, 130, 246, 0.2)",color:"#3b82f6"},children:e(l,{name:"history"})}),e("div",{className:a.statInfo,children:[e("span",{className:a.statValue,children:[n.avgSessionMinutes,"m"]}),e("span",{className:a.statLabel,children:"Avg. Session"})]})]}),e("div",{className:a.statCard,children:[e("div",{className:a.statIcon,style:{background:"rgba(251, 191, 36, 0.2)",color:"#fbbf24"},children:e(l,{name:"layer-group"})}),e("div",{className:a.statInfo,children:[e("span",{className:a.statValue,children:[n.avgCompletionDays,"d"]}),e("span",{className:a.statLabel,children:"Avg. Speed"})]})]})]}),e("div",{className:a.wideSection,children:[e("div",{className:a.sectionHeader,children:[e("h3",{children:"Activity"}),e("select",{className:a.yearSelect,value:d,onChange:t=>D(Number(t.currentTarget.value)),children:x.map(t=>e("option",{value:t,children:t},t))})]}),e("div",{className:a.heatmapLayout,children:[e("div",{className:a.dayLabels,children:[e("span",{style:{gridRow:2},children:"Mon"}),e("span",{style:{gridRow:4},children:"Wed"}),e("span",{style:{gridRow:6},children:"Fri"})]}),e("div",{className:a.heatmapWrapper,children:[e("div",{className:a.heatmapMonthLabels,children:(()=>{const t=[];let s=-1;return b.forEach((r,o)=>{if(r.month!==s&&!r.isOutOfRange){const h=Math.floor(o/7)+1;h>(t.length>0?t[t.length-1].props.style.gridColumn+2:-1)&&(t.push(e("span",{style:{gridColumn:h,whiteSpace:"nowrap"},children:new Date(0,r.month).toLocaleString("default",{month:"short"})},o)),s=r.month)}}),t})()}),e("div",{className:a.heatmapGrid,children:b.map((t,s)=>{let r=0;return t.count>0&&!t.isOutOfRange&&(r=1),t.count>2&&!t.isOutOfRange&&(r=2),t.count>5&&!t.isOutOfRange&&(r=3),t.count>10&&!t.isOutOfRange&&(r=4),e("div",{className:`${a.heatBox} ${a[`level${r}`]} ${t.isOutOfRange?a.hiddenBox:""}`,title:t.isOutOfRange?"":`${t.count} episodes on ${t.date}`},s)})}),e("div",{className:a.heatmapFooter,children:e("div",{className:a.heatLegend,children:[e("span",{children:"Less"}),e("div",{className:`${a.heatBox} ${a.level0}`}),e("div",{className:`${a.heatBox} ${a.level1}`}),e("div",{className:`${a.heatBox} ${a.level2}`}),e("div",{className:`${a.heatBox} ${a.level3}`}),e("div",{className:`${a.heatBox} ${a.level4}`}),e("span",{children:"More"})]})})]})]})]}),e("div",{className:a.chartsContainer,children:[e("div",{className:a.chartWrapper,children:[e("h3",{children:"Time Distribution (24h)"}),e("div",{className:a.hourlyChart,children:n.hourlyDist?.map((t,s)=>e("div",{className:a.hourlyBarContainer,children:[e("div",{className:a.hourlyBar,style:{height:`${t.count/M*100||2}%`,width:"100%",maxWidth:"12px"},title:`${t.count} watches at ${t.hour}:00`}),e("span",{className:a.hourlyLabel,style:{visibility:s%4===0?"visible":"hidden"},children:t.hour})]},s))}),e("div",{className:a.chartSubtext,children:(()=>{if(!n.hourlyDist?.length)return"Watch more to see your peak hours!";const t=parseInt([...n.hourlyDist].sort((s,r)=>r.count-s.count)[0].hour);return t>=6&&t<19?"You prefer daytime watching ☀️":"You're a confirmed Night Owl 🦉"})()})]}),e("div",{className:a.chartWrapper,children:[e("h3",{children:"Popularity Bias"}),e("div",{className:a.popScale,children:[e("div",{className:a.popTrack,children:e("div",{className:a.popThumb,style:{left:`${Math.max(0,Math.min(100,100-n.popularityScore*10))}%`}})}),e("div",{className:a.popLabels,children:[e("span",{children:"Mainstream"}),e("span",{children:"Underground"})]})]}),e("p",{className:a.popDescription,children:["Your taste score is ",e("strong",{children:[n.popularityScore,"/10"]}),".",n.popularityScore>=5?" You follow the big hits!":" You're a connoisseur of the obscure!"]})]}),e("div",{className:a.chartWrapper,children:[e("h3",{children:"Monthly Activity"}),e("div",{className:a.seasonalChart,children:n.seasonality?.map((t,s)=>e("div",{className:a.seasonalBarContainer,children:[e("div",{className:a.seasonalBar,style:{height:`${t.seconds/T*100||5}%`,width:"100%",maxWidth:"24px"},title:`${Math.round(t.seconds/3600)}h in ${new Date(0,s).toLocaleString("default",{month:"long"})}`}),e("span",{className:a.seasonalLabel,children:new Date(0,s).toLocaleString("default",{month:"short"})})]},s))})]})]}),e("div",{className:a.genreGrid,children:_?Array.from({length:4}).map((t,s)=>e("div",{className:a.genreCardSkeleton,children:[e("div",{className:a.skeletonRank}),e("div",{className:a.skeletonTitle}),e("div",{className:a.skeletonStats}),e("div",{className:a.skeletonPosters})]},s)):w?.map(t=>e("div",{className:a.genreCard,children:[e("div",{className:a.rankBadge,children:e("span",{className:a.rankNumber,children:t.rank})}),e("h3",{className:a.genreTitle,children:t.name}),e("div",{className:a.genreStats,children:[e("span",{children:[t.count," episodes"]}),e("span",{children:t.meanScore>0?`${t.meanScore.toFixed(1)} avg score`:"—"}),e("span",{children:t.timeWatched})]}),e("div",{className:a.posterRow,children:t.topShows.map(s=>e("img",{src:k(s.thumbnail),alt:y(s),className:a.miniPoster,title:y(s),loading:"lazy",decoding:"async"},s.id))})]},t.name))}),v.length>0&&e("div",{className:a.recSection,children:[e("h3",{children:"Recommended For You"}),e("p",{className:a.recSubtext,children:"From your library taste — updates automatically as you watch"}),e("div",{className:a.recGrid,children:v.map(t=>e("div",{className:a.recCard,children:[t.cover&&e("img",{src:k(t.cover),alt:t.title,className:a.recCover,loading:"lazy",decoding:"async"}),e("div",{className:a.recInfo,children:[e(P,{to:`/anime/${t.id}`,className:a.recTitle,children:t.title}),e("span",{className:a.recMatch,children:[t.matchPct,"% match"]}),e("span",{className:a.recMeta,children:[t.year,t.format,t.episodes?`${t.episodes}ep`:null].filter(Boolean).join(" · ")}),e("span",{className:a.recWhy,children:["Shares ",t.sharedGenres.join(", "),t.exemplars.length>0&&` — like ${t.exemplars.join(", ")}`]}),t.kind==="continue"&&e("span",{className:a.recNote,children:"Continue — you watched the earlier part."}),t.kind==="sequel"&&t.prequelTitle&&e("span",{className:a.recNote,children:['Sequel — start with "',t.prequelTitle,'" first.']}),e("button",{className:a.recAddBtn,onClick:()=>N.mutate(t),disabled:N.isPending,children:"+ Plan to Watch"})]})]},t.id))})]}),n.droppedShows?.length>0&&e("div",{className:a.warningSection,children:[e("div",{className:a.warningHeader,children:[e(l,{name:"exclamation-triangle"}),e("h3",{children:"Dusty Watchlist (Inactive 90+ days)"})]}),e("div",{className:a.droppedGrid,children:n.droppedShows.map(t=>e("div",{className:a.droppedCard,children:[e("span",{className:a.droppedName,children:t.name}),e("span",{className:a.droppedDate,children:["Last watched: ",new Date(t.lastActivity).toLocaleDateString()]})]},t.id))})]})]}):null};export{Sa as default};
@@ -1 +0,0 @@
1
- import{p as le,d as C,T as ne,h as T,u as a,m as re,S as se}from"./vendor-core-DfaOB3Rn.js";import{I as M}from"./vendor-icons-DrybRNxP.js";import{T as oe,M as ie,B as D,U as ce}from"./index-Dyiy_9L6.js";import{E as de}from"./ErrorMessage-DAxegITP.js";import{M as ue}from"./MediaCard-DcB-RhSF.js";import{u as ge,M as pe}from"./useMangaPopup-o6xoB0uh.js";import{f as he,u as ve,m as z,i as me,b as be,c as fe}from"./useMangaLibrary-COua_Bkn.js";import{u as Se}from"./useProviders-DIy3z1fe.js";import{s as t}from"./Manga.module-depGEacJ.js";import{p as d}from"./page.module-C1MlZzf3.js";import"./vendor-query-BBT5_sHx.js";import"./vendor-ui-3qrARCPr.js";import"./useIsMobile-Dvx1uJ82.js";import"./usePopup-DS9jMzp6.js";const ye=[{value:"popular",label:"Popular"},{value:"latest",label:"Latest"},{value:"rating",label:"Top Rated"},{value:"followed",label:"Most Followed"}],Ne=[{value:"",label:"Any Status"},{value:"ongoing",label:"Ongoing"},{value:"completed",label:"Completed"},{value:"hiatus",label:"Hiatus"},{value:"cancelled",label:"Cancelled"}],Ce=[{value:"",label:"Any Status"},{value:"publishing",label:"Publishing"},{value:"finished",label:"Finished"},{value:"on hiatus",label:"On Hiatus"},{value:"discontinued",label:"Discontinued"}],Te=[{value:"",label:"All Types"},{value:"manga",label:"Manga"},{value:"novel",label:"Novel"},{value:"one-shot",label:"One-Shot"},{value:"doujinshi",label:"Doujinshi"}],Me=[{value:"safe",label:"Safe"}],we=[{value:"safe",label:"Safe"},{value:"suggestive",label:"Suggestive"},{value:"erotica",label:"Erotica"},{value:"pornographic",label:"Pornographic"}],w="agreedToViewMature";function je(){const[r,I]=le(),[m,P]=C(()=>localStorage.getItem(w)==="true"),[k,b]=C(!1),[L,R]=C(r.get("q")||""),{options:$,isLoading:B,refetch:H}=Se(),u=ne(()=>$.filter(e=>e.kind==="manga").map(e=>({value:e.value,label:e.label})),[$]),f=r.get("provider")||"",i=u.some(e=>e.value===f)?f:u[0]?.value??f,S=r.get("q")||"",g=parseInt(r.get("page")||"1"),E=r.get("sort")||"popular",x=r.get("status")||"",A=r.get("type")||"",p=r.get("rating")||"safe",U=r.get("id")||"";T(()=>{document.title="Manga - dango"},[]),T(()=>{const e=()=>P(localStorage.getItem(w)==="true");return window.addEventListener("focus",e),window.addEventListener("storage",e),()=>{window.removeEventListener("focus",e),window.removeEventListener("storage",e)}},[]);const c=m,y=c?p:"safe",G=c?we:Me,o=(e,l=!1)=>{const n=new URLSearchParams(r);for(const[v,q]of Object.entries(e))q?n.set(v,q):n.delete(v);l&&n.delete("page"),I(n)},h=he({provider:i,query:S,page:g,sort:E,status:x,type:A,rating:y,mature:c}),N=h.data?.items??[],V=h.data?.hasNext??!1,{toggle:Y,bookmarkedIds:K}=ve(),{popup:s,openPopup:W,scheduleClose:F,cancelClose:O,closePopup:X}=ge();if(T(()=>{R(S)},[S]),U){const e=new URLSearchParams;c&&p!=="safe"&&e.set("rating",p);const l=r.get("chapter")||"";l&&e.set("chapter",l);const n=e.toString()?`?${e.toString()}`:"",v=`/manga/${i}/${encodeURIComponent(U)}${l?`/read${n}`:n}`;return a(re,{to:v,replace:!0})}const Q=e=>{e.preventDefault(),ce(),o({q:L},!0)},J=e=>{e.target.checked&&!m&&b(!0)},Z=()=>{localStorage.setItem(w,"true"),P(!0),b(!1),o({},!0)},_=()=>{b(!1)},j=e=>{const l=new URLSearchParams;c&&p!=="safe"&&l.set("rating",p);const n=l.toString()?`?${l.toString()}`:"";return`/manga/${e.provider}/${encodeURIComponent(e.id)}${n}`},ee=i==="mangapill"?Ce:Ne,ae=i==="mangapill",te=i==="mangadex";return a("div",{className:d.page,children:[a("div",{className:d.header,children:[a("h1",{className:d.pageTitle,children:[a(M,{name:"book"})," Manga"]}),a("form",{className:d.searchForm,onSubmit:Q,children:[a("input",{className:d.searchInput,value:L,onChange:e=>R(e.target.value),placeholder:"Search manga titles...","aria-label":"Search manga"}),a("button",{className:d.searchBtn,type:"submit","aria-label":"Search",children:a(M,{name:"search",size:14})})]}),a("select",{className:t.select,value:i,disabled:u.length===0,onChange:e=>{const l=new URLSearchParams(r);l.set("provider",e.target.value),l.delete("page"),l.delete("id"),l.delete("chapter"),l.delete("status"),l.delete("type"),I(l)},"aria-label":"Manga provider",children:u.length===0?a("option",{value:"",children:B?"Loading providers…":"No providers available"}):u.map(e=>a("option",{value:e.value,children:e.label},e.value))}),u.length===0&&!B&&a("button",{className:d.searchBtn,type:"button",onClick:()=>H(),"aria-label":"Retry loading providers",children:a(M,{name:"redo",size:14})})]}),a("div",{className:t.filterRow,children:[i==="mangadex"&&a("select",{className:t.select,value:E,onChange:e=>o({sort:e.target.value},!0),"aria-label":"Sort manga",children:ye.map(e=>a("option",{value:e.value,children:e.label},e.value))}),a("select",{className:t.select,value:x,onChange:e=>o({status:e.target.value},!0),"aria-label":"Filter by status",children:ee.map(e=>a("option",{value:e.value,children:e.label},e.label))}),ae&&a("select",{className:t.select,value:A,onChange:e=>o({type:e.target.value},!0),"aria-label":"Filter by type",children:Te.filter(e=>e.value!=="doujinshi"||c).map(e=>a("option",{value:e.value,children:e.label},e.value))}),te&&a("select",{className:t.select,value:y,onChange:e=>o({rating:e.target.value},!0),"aria-label":"Content rating",children:G.map(e=>a("option",{value:e.value,children:e.label},e.value))}),!m&&a("div",{className:t.matureToggleWrap,children:[a("label",{className:t.matureLabel,htmlFor:"manga-mature-toggle",children:"Mature"}),a(oe,{id:"manga-mature-toggle",isChecked:!1,onChange:J})]})]}),h.isError?a(de,{message:"Failed to load manga. Please try again."}):h.isLoading&&N.length===0?a("div",{className:t.grid,"aria-hidden":!0,children:Array.from({length:12}).map((e,l)=>a("div",{className:t.skeletonCard,children:[a("div",{className:`${t.skeletonThumb} ${t.shimmer}`}),a("div",{className:`${t.skeletonLine} ${t.shimmer}`,style:{width:"88%"}}),a("div",{className:`${t.skeletonLine} ${t.shimmer}`,style:{width:"55%"}})]},l))}):N.length===0?a("p",{className:t.statusMsg,children:"No titles found. Try another search or provider."}):a(se,{children:[a("div",{className:`${t.grid} ${h.isFetching?t.fetching:""}`,children:N.map(e=>{const l=z(e.provider,e.id);return a(ue,{item:{id:l,title:e.title,...fe(e),thumbnail:be(e.provider,e.cover),typeBadge:e.type||void 0,chapterBadge:e.latestChapter?`Ch. ${e.latestChapter}`:null,isAdult:me(e)},linkTo:j(e),hoverIcon:"info",display:{elements:{poster:{typeBadge:!0,chapterBadge:!0,adultBadge:!0},info:{title:!0,mobileBadges:!0,progress:!1,meta:!1}}},onOpenDetails:n=>W(n,{provider:e.provider,mangaId:e.id,title:e.title,altTitle:e.altTitle,cover:e.cover,contentRating:e.contentRating,readTarget:j(e),rating:y,mature:c}),onPopupHoverIntent:n=>n?O():F(),rawThumbnail:!0},`${e.provider}-${e.id}`)})}),a("nav",{className:t.pagination,children:[a("button",{className:t.pageBtn,disabled:g<=1,onClick:()=>o({page:String(g-1)}),children:"Previous"}),a("span",{className:t.pageIndicator,children:["Page ",g]}),a("button",{className:t.pageBtn,disabled:!V,onClick:()=>o({page:String(g+1)}),children:"Next"})]})]}),s&&a(pe,{data:s.data,anchorRect:s.rect,bookmarked:K.has(z(s.data.provider,s.data.mangaId)),onToggleBookmark:()=>Y({id:s.data.mangaId,provider:s.data.provider,title:s.data.title,cover:s.data.cover,altTitle:s.data.altTitle??void 0,contentRating:s.data.contentRating}),onMouseEnter:O,onMouseLeave:F,onRequestClose:X}),k&&a(ie,{isOpen:k,title:"Content Warning",onClose:_,children:a("div",{style:{padding:"var(--space-4)",textAlign:"center"},children:[a("p",{children:"This section contains mature content intended for adult audiences."}),a("p",{children:["By proceeding, you confirm that you are ",a("strong",{children:"18 years of age or older"})," (or the age of majority in your jurisdiction) and wish to view this content."]}),a("p",{style:{fontSize:"var(--font-size-xs)",color:"var(--text-secondary)",marginTop:"var(--space-4)"},children:["You can reset this preference at any time in the ",a("strong",{children:"Settings"})," page."]}),a("div",{style:{marginTop:"var(--space-4)",display:"flex",gap:"var(--space-2-5)",justifyContent:"center"},children:[a(D,{variant:"secondary",onClick:_,children:"Go Back"}),a(D,{onClick:Z,children:"I'm 18+, Continue"})]})]})})]})}export{je as default};