@serifpersia/dango 3.3.5 → 3.3.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/client/dist/assets/{AnimeCard-CXx2EIN7.js → AnimeCard-WlhU88fn.js} +1 -1
- package/client/dist/assets/{AnimeInfo-C6ehrfYW.js → AnimeInfo-BCLagpbu.js} +1 -1
- package/client/dist/assets/{AnimeInfoPage-pgnMg0On.js → AnimeInfoPage-o0U_OXKP.js} +2 -2
- package/client/dist/assets/{Asmr-DfA8xB-U.js → Asmr-ieGJ_BcQ.js} +1 -1
- package/client/dist/assets/{AsmrResetProgressModal-QUNnEh9C.js → AsmrResetProgressModal-BwaoVski.js} +1 -1
- package/client/dist/assets/{AvSyncCalibrator-C1_iBSyL.css → AvSyncCalibrator-BPa6KaGC.css} +1 -1
- package/client/dist/assets/AvSyncCalibrator-CXvBuY4l.js +10 -0
- package/client/dist/assets/Home-DjLpvsp8.js +1 -0
- package/client/dist/assets/Insights-Dc8xLmsr.css +1 -0
- package/client/dist/assets/Insights-lQcQoeES.js +1 -0
- package/client/dist/assets/LibraryListPage-B2nhFFrc.js +1 -0
- package/client/dist/assets/{ListeningList-BzbQwmh0.js → ListeningList-okIpsKCr.js} +1 -1
- package/client/dist/assets/{Manga-223panCD.js → Manga-3u9nVNZC.js} +1 -1
- package/client/dist/assets/{MangaInfoPage-DBqX-Ord.js → MangaInfoPage-Cat-B487.js} +1 -1
- package/client/dist/assets/{MangaReadPage-DuXdVyTd.js → MangaReadPage-CyLRxzvl.js} +1 -1
- package/client/dist/assets/{MangaResetProgressModal-6C3iaR6g.js → MangaResetProgressModal-Ch8RjwbO.js} +1 -1
- package/client/dist/assets/Mature-pINwl5wj.js +1 -0
- package/client/dist/assets/{MediaCard-xfJv6irD.js → MediaCard-DDiYEAm8.js} +1 -1
- package/client/dist/assets/{Music-DkIeCRq1.js → Music-B1swlVLI.js} +1 -1
- package/client/dist/assets/{OptionListMenu-JQ_--ggT.js → OptionListMenu-CzH2VSPh.js} +1 -1
- package/client/dist/assets/Player-SKPWb-o8.js +56 -0
- package/client/dist/assets/PlayerSettings-jrmY95-i.js +1 -0
- package/client/dist/assets/{QueueOptionsButton-BKNDfpCl.js → QueueOptionsButton-CcVP7Vi6.js} +1 -1
- package/client/dist/assets/{QueueRail-Cgljhm64.js → QueueRail-Cj-PU9EV.js} +1 -1
- package/client/dist/assets/{Radio-wbxhaiqy.js → Radio-CRlbaxQt.js} +1 -1
- package/client/dist/assets/{ReadingList-DNJAx_LW.js → ReadingList-wRtHGNo8.js} +1 -1
- package/client/dist/assets/{ResetProgressModal-By-iSYwL.js → ResetProgressModal-BokfoYFB.js} +1 -1
- package/client/dist/assets/Search-BOOBeTVA.js +1 -0
- package/client/dist/assets/{Settings-BeS0llCE.js → Settings-BgrcswJD.js} +1 -1
- package/client/dist/assets/SkeletonGrid-Bge3aRl2.css +1 -0
- package/client/dist/assets/SkeletonGrid-C6S7OYHn.js +1 -0
- package/client/dist/assets/{Trackers-ACFHfLXZ.js → Trackers-DLcTEJJW.js} +1 -1
- package/client/dist/assets/Tv-ByVckozq.js +4 -0
- package/client/dist/assets/Tv-C2SWcVzN.css +1 -0
- package/client/dist/assets/{TvInfo-fFNHV1RJ.js → TvInfo-fJ9SFMK8.js} +1 -1
- package/client/dist/assets/{TvPopup-DYKTHRrD.js → TvPopup-yfH7p0y0.js} +1 -1
- package/client/dist/assets/TvSearch-CBNWDByW.js +1 -0
- package/client/dist/assets/{TvWatchlist-CTUZAvfa.js → TvWatchlist-BqjZ2-4A.js} +1 -1
- package/client/dist/assets/Watchlist-B_Q2hEiJ.js +1 -0
- package/client/dist/assets/{hls-Cbou9Eb-.js → hls-kIF7bMpF.js} +1 -1
- package/client/dist/assets/{index-CfvzoNYQ.js → index-C65i4HWv.js} +2 -2
- package/client/dist/assets/{useAnimeInfoData-D-vEehTG.js → useAnimeInfoData-BKSrS8eN.js} +1 -1
- package/client/dist/assets/{useAsmr-B-3EYd-B.js → useAsmr-Byj8Qiwp.js} +1 -1
- package/client/dist/assets/{useAsmrPopup-D9BxuTDg.js → useAsmrPopup-CLcWCCXf.js} +1 -1
- package/client/dist/assets/{useMangaLibrary-DtRKgk2x.js → useMangaLibrary-U7tUo8nx.js} +1 -1
- package/client/dist/assets/{useMangaPopup-BNWJ5pR2.js → useMangaPopup-ZonOP5_c.js} +1 -1
- package/client/dist/assets/{useProviders-jxh6fvMH.js → useProviders-CymA42ZB.js} +1 -1
- package/client/dist/assets/{useTv-CP4mofvR.js → useTv-CPj6Cf42.js} +1 -1
- package/client/dist/assets/{useTvLibrary-DeGkJuvp.js → useTvLibrary-COsyN8yu.js} +1 -1
- package/client/dist/index.html +1 -1
- package/package.json +2 -2
- package/server/dist/controllers/insights.controller.js +5 -1
- package/server/dist/controllers/proxy.controller.js +94 -23
- package/server/dist/controllers/watchlist.controller.js +42 -0
- package/server/dist/lib/discord-roles-sync.service.js +237 -2
- package/server/dist/repositories/insights.repository.js +10 -0
- package/server/dist/repositories/shows-meta.repository.js +4 -1
- package/server/dist/repositories/watchlist.repository.js +1 -0
- package/server/dist/routes/insights.routes.js +1 -0
- package/server/package.json +1 -1
- package/client/dist/assets/AvSyncCalibrator-Bs4iejCG.js +0 -10
- package/client/dist/assets/Home-Csx6zT32.js +0 -1
- package/client/dist/assets/Insights-DKtoVe6L.css +0 -1
- package/client/dist/assets/Insights-MW8oRqeX.js +0 -1
- package/client/dist/assets/LibraryListPage-VYaHXgXt.js +0 -1
- package/client/dist/assets/Mature-3VlywJMR.js +0 -1
- package/client/dist/assets/Player-Czg_D7FN.js +0 -56
- package/client/dist/assets/PlayerSettings-KrFECely.js +0 -1
- package/client/dist/assets/Search-63TcSgOa.js +0 -1
- package/client/dist/assets/SkeletonGrid-CnE_aoEU.js +0 -1
- package/client/dist/assets/SkeletonGrid-DhPYefXr.css +0 -1
- package/client/dist/assets/Tv-BCmNLUsp.js +0 -4
- package/client/dist/assets/Tv-CuEl3Iol.css +0 -1
- package/client/dist/assets/TvSearch-CpenLEBz.js +0 -1
- package/client/dist/assets/Watchlist-B10pIG1F.js +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
import{u as e,S as y,L as g,d as k,A as L,q as r}from"./vendor-core-8R2fFbyN.js";import{I as a}from"./vendor-icons-CDMuNDeL.js";import{c as P}from"./useAsmr-
|
|
1
|
+
import{u as e,S as y,L as g,d as k,A as L,q as r}from"./vendor-core-8R2fFbyN.js";import{I as a}from"./vendor-icons-CDMuNDeL.js";import{c as P}from"./useAsmr-Byj8Qiwp.js";import{p as s,a as f}from"./MediaCard-DDiYEAm8.js";import{a as A}from"./Asmr.module-ChUXDZKx.js";const B=({data:n,anchorRect:l,bookmarked:i,onToggleBookmark:c,onMouseEnter:o,onMouseLeave:m,onRequestClose:p})=>{const{data:t,isLoading:d}=P(n.rjCode||null),u=t?.tracks.length??0,N=u>0,h=!d;return e(f,{anchorRect:l,onMouseEnter:o,onMouseLeave:m,onRequestClose:p,children:[e("div",{className:s.header,children:[n.thumbnail&&e("img",{src:n.thumbnail,alt:n.title,loading:"lazy",decoding:"async",className:A.popupCover}),e("div",{className:s.title,children:n.title}),e("div",{className:s.synopsis,children:n.rjCode})]}),e("div",{className:s.body,children:[e("div",{className:s.metaRow,children:[e("div",{className:s.metaItem,children:[e(a,{name:"headphones",size:14}),e("span",{children:h?N?e(y,{children:[u," track",u===1?"":"s"]}):"No tracks":"Loading tracks…"})]}),n.rating&&e("div",{className:s.metaItem,children:e("span",{children:n.rating.toUpperCase()})}),n.isAdult&&e("div",{className:s.metaItem,children:e("span",{children:"18+"})}),n.progressLabel&&e("div",{className:s.metaItem,children:[e(a,{name:"bookmark",size:12}),e("span",{children:n.progressLabel})]})]}),e("div",{className:s.synopsis,children:!h&&!t?.description?"Loading description…":t?.description??"No description available."})]}),e("div",{className:s.footer,children:[e("div",{className:s.primaryAction,children:N?e(g,{to:n.listenTarget,className:s.watchBtn,children:[e(a,{name:"play",size:14}),"Listen now"]}):e("span",{className:s.watchBtn,"aria-disabled":"true",style:{opacity:.5,pointerEvents:"none"},children:[e(a,{name:"play",size:14}),h?"No tracks":"Loading…"]})}),e("div",{className:s.secondaryActions,children:[e("button",{className:`${s.watchlistBtn} ${i?s.active:""}`,onClick:v=>{v.preventDefault(),v.stopPropagation(),c()},children:[i?e(a,{name:"check",size:12}):e(a,{name:"plus",size:12}),e("span",{children:i?"Remove":"Bookmark"})]}),e(g,{to:n.listenTarget,className:s.detailsBtn,children:"Details"})]})]})]})},C=300;function D(){const[n,l]=k(null),i=L(null),c=r(()=>{i.current&&(clearTimeout(i.current),i.current=null)},[]),o=r((t,d)=>{c(),l({data:d,rect:t})},[c]),m=r(()=>{c(),i.current=setTimeout(()=>{i.current=null,l(null)},C)},[c]),p=r(()=>{c(),l(null)},[c]);return{popup:n,openPopup:o,scheduleClose:m,cancelClose:c,closePopup:p}}export{B as A,D as u};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{b as y,c as b,u as m,a as d}from"./vendor-query-BXRECSI6.js";import{q as s,z as o}from"./index-
|
|
1
|
+
import{b as y,c as b,u as m,a as d}from"./vendor-query-BXRECSI6.js";import{q as s,z as o}from"./index-C65i4HWv.js";import{T as S}from"./vendor-core-8R2fFbyN.js";const q=300*1e3,f={mangadex:"https://mangadex.org/",mangapill:"https://www.mangapill.com/"},K=(e,a)=>`/api/proxy?url=${encodeURIComponent(a)}&referer=${encodeURIComponent(f[e])}`,M=(e,a)=>a&&(e==="mangapill"?K(e,a):a),$=(e,a=!0)=>{const{provider:n,query:t,page:u,sort:i,status:g,type:c,rating:l,mature:p}=e;return y({queryKey:["mangaBrowse",n,t,u,i,g,c,l,p],queryFn:()=>{const r=new URLSearchParams;return r.set("provider",n),t.trim()&&r.set("q",t.trim()),r.set("page",String(u)),i&&r.set("sort",i),g&&r.set("status",g),c&&r.set("type",c),l&&r.set("rating",l),p&&r.set("mature","1"),s(`/api/manga/search?${r.toString()}`)},staleTime:q,enabled:a&&!!n})},k=(e,a=14)=>{const{provider:n,query:t,sort:u,status:i,type:g,rating:c,mature:l}=e;return b({queryKey:["mangaBrowseInfinite",n,t,u,i,g,c,l,a],queryFn:({pageParam:p=1})=>{const r=new URLSearchParams;return r.set("provider",n),t.trim()&&r.set("q",t.trim()),r.set("page",String(p)),r.set("limit",String(a)),u&&r.set("sort",u),i&&r.set("status",i),g&&r.set("type",g),c&&r.set("rating",c),l&&r.set("mature","1"),s(`/api/manga/search?${r.toString()}`)},initialPageParam:1,getNextPageParam:(p,r)=>p.hasNext?r.length+1:void 0,staleTime:q,enabled:!!n})},C=(e,a,n="safe",t=!1)=>y({queryKey:["mangaDetail",e,a,n,t],queryFn:()=>{const u=new URLSearchParams;return u.set("provider",e||""),u.set("id",a||""),n&&u.set("rating",n),t&&u.set("mature","1"),s(`/api/manga/info?${u.toString()}`)},enabled:!!e&&!!a,staleTime:q}),w=(e,a,n)=>y({queryKey:["mangaPages",e,a],queryFn:()=>s(`/api/manga/pages?provider=${e}&id=${encodeURIComponent(a||"")}`),enabled:!!e&&!!a,staleTime:1800*1e3}),O=()=>y({queryKey:["mangaTrending"],queryFn:()=>s("/api/manga/trending"),staleTime:600*1e3}),N=e=>e.contentRating==="erotica"||e.contentRating==="pornographic"||e.type==="doujinshi";function I(e,a){const n=e.titles??{};return a==="englishName"?n.en||e.title:a==="nativeName"&&(n.native||n.romaji||e.altTitle)||e.title}function B(e){return{englishName:e.titles?.en??e.title,nativeName:e.titles?.native??e.titles?.romaji??e.altTitle??void 0}}const E=["Reading","Completed","On-Hold","Dropped","Planned"],v=(e,a)=>`${e.toLowerCase()}:${a}`,U=(e="All",a=1,n=24)=>y({queryKey:["manga-library",e,a,n],queryFn:()=>{const t=new URLSearchParams;return t.set("status",e),t.set("page",String(a)),t.set("limit",String(n)),s(`/api/manga/library?${t.toString()}`)}}),h=()=>y({queryKey:["manga-library-ids"],queryFn:()=>s("/api/manga/library/ids"),staleTime:1e3*60}),L=e=>y({queryKey:["manga-library-check",e],queryFn:()=>s(`/api/manga/library/check/${encodeURIComponent(e||"")}`),enabled:!!e,staleTime:1e3*60}),A=(e=24)=>y({queryKey:["manga-continue-reading",e],queryFn:()=>s(`/api/manga/continue-reading?limit=${e}`)}),_=e=>y({queryKey:["manga-progress",e],queryFn:()=>s(`/api/manga/progress/${encodeURIComponent(e||"")}`),enabled:!!e}),P=()=>{const e=m();return d({mutationFn:async a=>{const{silent:n,...t}=a;return s("/api/manga/library/add",{method:"POST",body:JSON.stringify(t)})},onSuccess:(a,n)=>{n.silent||o.success("Bookmarked"),e.invalidateQueries({queryKey:["manga-library"]}),e.invalidateQueries({queryKey:["manga-library-ids"]}),e.invalidateQueries({queryKey:["manga-library-check",v(n.provider,n.mangaId)]})},onError:a=>{o.error(`Failed to bookmark: ${a.message}`)}})},R=()=>{const e=m();return d({mutationFn:async a=>{await s("/api/manga/library/remove",{method:"POST",body:JSON.stringify({id:a})})},onSuccess:(a,n)=>{o.success("Bookmark removed"),e.invalidateQueries({queryKey:["manga-library"]}),e.invalidateQueries({queryKey:["manga-library-ids"]}),e.invalidateQueries({queryKey:["manga-continue-reading"]}),e.invalidateQueries({queryKey:["manga-library-check",n]})},onError:a=>{o.error(`Failed to remove: ${a.message}`)}})},J=()=>{const e=m();return d({mutationFn:async({id:a,status:n})=>{await s("/api/manga/library/status",{method:"POST",body:JSON.stringify({id:a,status:n})})},onSuccess:()=>{o.success("Status updated"),e.invalidateQueries({queryKey:["manga-library"]}),e.invalidateQueries({queryKey:["manga-continue-reading"]})},onError:a=>{o.error(`Failed to update status: ${a.message}`)}})},x=()=>{const e=m();return d({mutationFn:async a=>{await s("/api/manga/progress",{method:"POST",body:JSON.stringify(a)})},onSuccess:(a,n)=>{e.invalidateQueries({queryKey:["manga-progress",n.mangaId]}),e.invalidateQueries({queryKey:["manga-continue-reading"]}),e.invalidateQueries({queryKey:["manga-library"]}),e.invalidateQueries({queryKey:["manga-library-ids"]})}})},D=()=>{const e=m();return d({mutationFn:async({mangaId:a,chapterId:n})=>{await s("/api/manga/progress/remove",{method:"POST",body:JSON.stringify({mangaId:a,chapterId:n})})},onSuccess:()=>{o.success("Progress reset"),e.invalidateQueries({queryKey:["manga-progress"]}),e.invalidateQueries({queryKey:["manga-continue-reading"]}),e.invalidateQueries({queryKey:["manga-library"]})},onError:a=>{o.error(`Failed to reset progress: ${a.message}`)}})},j=()=>{const e=m();return d({mutationFn:async({ids:a,status:n})=>s("/api/manga/library/batch-status",{method:"POST",body:JSON.stringify({ids:a,status:n})}),onSuccess:a=>{o.success(`Status updated for ${a.updated??0} items`),e.invalidateQueries({queryKey:["manga-library"]}),e.invalidateQueries({queryKey:["manga-continue-reading"]})},onError:a=>{o.error(`Failed to update statuses: ${a.message}`)}})},V=()=>{const e=m();return d({mutationFn:async a=>s("/api/manga/library/remove-many",{method:"POST",body:JSON.stringify({ids:a})}),onSuccess:a=>{const n=a.removed??0;o.success(`Removed ${n} ${n===1?"item":"items"} from reading list`),e.invalidateQueries({queryKey:["manga-library"]}),e.invalidateQueries({queryKey:["manga-library-ids"]}),e.invalidateQueries({queryKey:["manga-continue-reading"]})},onError:a=>{o.error(`Failed to remove: ${a.message}`)}})},z=()=>{const e=m();return d({mutationFn:async a=>s("/api/manga/progress/remove-many",{method:"POST",body:JSON.stringify({ids:a})}),onSuccess:a=>{const n=a.removed??0;o.success(`Reset progress for ${n} ${n===1?"item":"items"}`),e.invalidateQueries({queryKey:["manga-progress"]}),e.invalidateQueries({queryKey:["manga-continue-reading"]}),e.invalidateQueries({queryKey:["manga-library"]})},onError:a=>{o.error(`Failed to reset progress: ${a.message}`)}})},G=()=>{const e=P(),a=R(),{data:n}=h(),t=S(()=>new Set(n?.ids||[]),[n]);return{toggle:i=>{const g=v(i.provider,i.id);t.has(g)?a.mutate(g):e.mutate({provider:i.provider,mangaId:i.id,title:i.title,cover:i.cover,altTitle:i.altTitle,contentRating:i.contentRating})},bookmarkedIds:t,pending:e.isPending||a.isPending}};export{E as M,k as a,M as b,B as c,A as d,O as e,$ as f,C as g,L as h,N as i,_ as j,P as k,R as l,v as m,J as n,w as o,K as p,x as q,I as r,U as s,j as t,G as u,V as v,z as w,D as x};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{u as e,S as T,L as N,d as P,A as y,q as o}from"./vendor-core-8R2fFbyN.js";import{I as c}from"./vendor-icons-CDMuNDeL.js";import{g as I,r as A}from"./useMangaLibrary-
|
|
1
|
+
import{u as e,S as T,L as N,d as P,A as y,q as o}from"./vendor-core-8R2fFbyN.js";import{I as c}from"./vendor-icons-CDMuNDeL.js";import{g as I,r as A}from"./useMangaLibrary-U7tUo8nx.js";import{d as L}from"./index-C65i4HWv.js";import{p as s,a as M}from"./MediaCard-DDiYEAm8.js";const S=({data:l,anchorRect:t,bookmarked:a,onToggleBookmark:i,onMouseEnter:d,onMouseLeave:m,onRequestClose:p})=>{const{data:n,isLoading:h}=I(l.provider,l.mangaId,l.rating||"safe",l.mature||!1),{titlePreference:f}=L(),u=n?.chapters.length??0,v=[n?.status,n?.type].filter(Boolean).join(" · "),g=A({title:l.title,altTitle:n?.altTitle??l.altTitle},f);return e(M,{anchorRect:t,onMouseEnter:d,onMouseLeave:m,onRequestClose:p,children:h?e("div",{className:s.loading,children:[e("div",{className:s.spinner}),e("span",{children:"Fetching details..."})]}):e(T,{children:[e("div",{className:s.header,children:[e("div",{className:s.title,children:g}),n?.altTitle&&n.altTitle!==g&&e("div",{className:s.synopsis,children:["Also known as: ",n.altTitle]})]}),e("div",{className:s.body,children:[e("div",{className:s.metaRow,children:[v&&e("div",{className:s.metaItem,children:[e(c,{name:"book",size:14}),e("span",{children:v})]}),u>0&&e("div",{className:s.metaItem,children:e("span",{children:[u," chapter",u===1?"":"s"]})}),l.progressLabel&&e("div",{className:s.metaItem,children:[e(c,{name:"bookmark",size:12}),e("span",{children:l.progressLabel})]})]}),e("div",{className:s.synopsis,children:n?.description?n.description:"No synopsis available."}),Array.isArray(n?.genres)&&(n?.genres?.length??0)>0&&e("div",{className:s.details,children:e("div",{className:s.genres,children:n.genres.filter(Boolean).slice(0,4).map(r=>e("span",{className:s.genre,children:r},r))})})]}),e("div",{className:s.footer,children:[e("div",{className:s.primaryAction,children:e(N,{to:l.readTarget,className:s.watchBtn,children:[e(c,{name:"book",size:14}),"Read now"]})}),e("div",{className:s.secondaryActions,children:[e("button",{className:`${s.watchlistBtn} ${a?s.active:""}`,onClick:r=>{r.preventDefault(),r.stopPropagation(),i()},children:[a?e(c,{name:"check",size:12}):e(c,{name:"plus",size:12}),e("span",{children:a?"Remove":"Bookmark"})]}),e(N,{to:`/manga/${l.provider}/${encodeURIComponent(l.mangaId)}`,className:s.detailsBtn,children:"Read more"})]})]})]})})},b=300;function $(){const[l,t]=P(null),a=y(null),i=o(()=>{a.current&&(clearTimeout(a.current),a.current=null)},[]),d=o((n,h)=>{i(),t({data:h,rect:n})},[i]),m=o(()=>{i(),a.current=setTimeout(()=>{a.current=null,t(null)},b)},[i]),p=o(()=>{i(),t(null)},[i]);return{popup:l,openPopup:d,scheduleClose:m,cancelClose:i,closePopup:p}}export{S as M,$ as u};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{T as o}from"./vendor-core-8R2fFbyN.js";import{b as a}from"./vendor-query-BXRECSI6.js";import{q as i}from"./index-
|
|
1
|
+
import{T as o}from"./vendor-core-8R2fFbyN.js";import{b as a}from"./vendor-query-BXRECSI6.js";import{q as i}from"./index-C65i4HWv.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 d,u as o,S as v}from"./vendor-core-8R2fFbyN.js";import{I as g}from"./vendor-icons-CDMuNDeL.js";import{M as f}from"./MediaCard-
|
|
1
|
+
import{N as d,u as o,S as v}from"./vendor-core-8R2fFbyN.js";import{I as g}from"./vendor-icons-CDMuNDeL.js";import{M as f}from"./MediaCard-DDiYEAm8.js";import{T as m}from"./TvPopup-yfH7p0y0.js";import{c as T,i as h}from"./useTvLibrary-COsyN8yu.js";import{b as u}from"./vendor-query-BXRECSI6.js";import{q as y}from"./index-C65i4HWv.js";const b=d(({item:e})=>{const r=e.type==="tv"||e.type==="tvSeries"||e.type==="tvMiniSeries",t=T(e.type,e.id);return o(f,{item:{id:String(e.id),title:e.title,thumbnail:e.image,typeBadge:r?"TV":"Movie",isAdult:h(e)},linkTo:t,hoverIcon:"info",showInfoButton:!0,metaRow:o(v,{children:[e.year&&o("span",{style:{opacity:.75},children:e.year}),e.vote_average!=null&&o("span",{style:{display:"inline-flex",alignItems:"center",gap:3},children:[o(g,{name:"star",size:10}),Number(e.vote_average).toFixed(1)]})]}),renderPopup:(n,a)=>o(m,{item:e,anchorRect:n,onMouseEnter:a.onMouseEnter,onMouseLeave:a.onMouseLeave,onRequestClose:a.close})})}),c=1e3*60*5,w=e=>{const{query:r,type:t,genre:n,year:a,sort_by:i,page:l}=e,s=new URLSearchParams;r?.trim()&&s.set("q",r.trim()),t&&t!=="multi"&&s.set("type",t),n&&s.set("genre",n),a&&a!=="ALL"&&s.set("year",a),i&&i!=="popularity.desc"&&s.set("sort_by",i),s.set("page",String(l||1));const p=s.toString();return u({queryKey:["tv-search-paginated",p],queryFn:()=>y(`/api/tv/search?${p}`),staleTime:c,retry:1})},A=(e="all",r="week",t=1,n=!0)=>u({queryKey:["tv-trending",e,r,t],queryFn:()=>y(`/api/tv/trending?media_type=${e}&time_window=${r}&page=${t}`),enabled:n,staleTime:c,retry:1});export{b as T,w as a,A as u};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{T as m}from"./vendor-core-8R2fFbyN.js";import{b as y,u as o,a as u}from"./vendor-query-BXRECSI6.js";import{q as i,z as a}from"./index-
|
|
1
|
+
import{T as m}from"./vendor-core-8R2fFbyN.js";import{b as y,u as o,a as u}from"./vendor-query-BXRECSI6.js";import{q as i,z as a}from"./index-C65i4HWv.js";const d=e=>String(e).toLowerCase()==="movie"?"movie":"tv",g=(e,t)=>`${d(e)}:${t}`,K=(e,t)=>{const r=d(e);return`/tv/${t}?type=${r}`},Q=(e,t,r,s)=>{const n=new URLSearchParams;return n.set("type",d(e)),r!==void 0&&n.set("s",String(r)),s!==void 0&&n.set("e",String(s)),`/tv/${t}/watch?${n.toString()}`},$=e=>e.adult===!0||e.adult===1,f=["Watching","Completed","On-Hold","Dropped","Planned"],l=(e,t)=>g(e,t),w=(e="All",t=1,r=24)=>y({queryKey:["tv-library",e,t,r],queryFn:()=>{const s=new URLSearchParams;return s.set("status",e),s.set("page",String(t)),s.set("limit",String(r)),i(`/api/tv/library?${s.toString()}`)}}),p=()=>y({queryKey:["tv-library-ids"],queryFn:()=>i("/api/tv/library/ids"),staleTime:1e3*60}),P=e=>y({queryKey:["tv-library-check",e],queryFn:()=>i(`/api/tv/library/check/${encodeURIComponent(e||"")}`),enabled:!!e,staleTime:1e3*60}),F=(e=24)=>y({queryKey:["tv-continue-watching",e],queryFn:()=>i(`/api/tv/continue-watching?limit=${e}`)}),O=e=>y({queryKey:["tv-progress",e],queryFn:()=>i(`/api/tv/progress/${encodeURIComponent(e||"")}`),enabled:!!e}),C=(e,t,r)=>y({queryKey:["tv-progress-latest",e,t,r],queryFn:()=>{const s=new URLSearchParams;t!==void 0&&s.set("season",String(t)),r!==void 0&&s.set("episode",String(r));const n=s.toString();return i(`/api/tv/progress/${encodeURIComponent(e||"")}/latest${n?`?${n}`:""}`)},enabled:!!e,staleTime:0}),q=()=>{const e=o();return u({mutationFn:async t=>{const{silent:r,...s}=t;return i("/api/tv/library/add",{method:"POST",body:JSON.stringify(s)})},onSuccess:(t,r)=>{r.silent||a.success("Added to TV watchlist"),e.invalidateQueries({queryKey:["tv-library"]}),e.invalidateQueries({queryKey:["tv-library-ids"]}),e.invalidateQueries({queryKey:["tv-library-check",l(r.mediaType,r.tmdbId)]})},onError:t=>{a.error(`Failed to add: ${t.message}`)}})},b=()=>{const e=o();return u({mutationFn:async t=>{await i("/api/tv/library/remove",{method:"POST",body:JSON.stringify({id:t})})},onSuccess:(t,r)=>{a.success("Removed from TV watchlist"),e.invalidateQueries({queryKey:["tv-library"]}),e.invalidateQueries({queryKey:["tv-library-ids"]}),e.invalidateQueries({queryKey:["tv-continue-watching"]}),e.invalidateQueries({queryKey:["tv-library-check",r]})},onError:t=>{a.error(`Failed to remove: ${t.message}`)}})},R=()=>{const e=o();return u({mutationFn:async({id:t,status:r})=>{await i("/api/tv/library/status",{method:"POST",body:JSON.stringify({id:t,status:r})})},onSuccess:()=>{a.success("Status updated"),e.invalidateQueries({queryKey:["tv-library"]}),e.invalidateQueries({queryKey:["tv-continue-watching"]})},onError:t=>{a.error(`Failed to update status: ${t.message}`)}})},k=()=>{const e=o();return u({mutationFn:async t=>{await i("/api/tv/progress",{method:"POST",body:JSON.stringify(t)})},onSuccess:(t,r)=>{e.invalidateQueries({queryKey:["tv-progress",r.mediaId]}),e.invalidateQueries({queryKey:["tv-progress-latest",r.mediaId]}),e.invalidateQueries({queryKey:["tv-continue-watching"]}),e.invalidateQueries({queryKey:["tv-library"]}),e.invalidateQueries({queryKey:["tv-library-ids"]})}})},L=()=>{const e=o();return u({mutationFn:async({mediaId:t,season:r,episode:s})=>{await i("/api/tv/progress/remove",{method:"POST",body:JSON.stringify({mediaId:t,season:r,episode:s})})},onSuccess:()=>{a.success("Progress reset"),e.invalidateQueries({queryKey:["tv-progress"]}),e.invalidateQueries({queryKey:["tv-continue-watching"]}),e.invalidateQueries({queryKey:["tv-library"]})},onError:t=>{a.error(`Failed to reset progress: ${t.message}`)}})},I=()=>{const e=o();return u({mutationFn:async({ids:t,status:r})=>i("/api/tv/library/batch-status",{method:"POST",body:JSON.stringify({ids:t,status:r})}),onSuccess:t=>{a.success(`Status updated for ${t.updated??0} items`),e.invalidateQueries({queryKey:["tv-library"]}),e.invalidateQueries({queryKey:["tv-continue-watching"]})},onError:t=>{a.error(`Failed to update statuses: ${t.message}`)}})},U=()=>{const e=o();return u({mutationFn:async t=>i("/api/tv/library/remove-many",{method:"POST",body:JSON.stringify({ids:t})}),onSuccess:t=>{const r=t.removed??0;a.success(`Removed ${r} ${r===1?"item":"items"} from TV watchlist`),e.invalidateQueries({queryKey:["tv-library"]}),e.invalidateQueries({queryKey:["tv-library-ids"]}),e.invalidateQueries({queryKey:["tv-continue-watching"]})},onError:t=>{a.error(`Failed to remove: ${t.message}`)}})},E=()=>{const e=o();return u({mutationFn:async t=>i("/api/tv/progress/remove-many",{method:"POST",body:JSON.stringify({ids:t})}),onSuccess:t=>{const r=t.removed??0;a.success(`Reset progress for ${r} ${r===1?"item":"items"}`),e.invalidateQueries({queryKey:["tv-progress"]}),e.invalidateQueries({queryKey:["tv-continue-watching"]}),e.invalidateQueries({queryKey:["tv-library"]})},onError:t=>{a.error(`Failed to reset progress: ${t.message}`)}})},J=()=>{const e=q(),t=b(),{data:r}=p(),s=m(()=>new Set(r?.ids||[]),[r]);return{toggle:c=>{const v=l(c.mediaType,c.tmdbId);s.has(v)?t.mutate(v):e.mutate({...c,poster:c.poster||""})},bookmarkedIds:s,pending:e.isPending||t.isPending}};export{f as T,b as a,L as b,K as c,g as d,P as e,J as f,k as g,R as h,$ as i,C as j,O as k,w as l,I as m,d as n,U as o,E as p,Q as t,F as u};
|
package/client/dist/index.html
CHANGED
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
background-color: #0c0d12;
|
|
34
34
|
}
|
|
35
35
|
</style>
|
|
36
|
-
<script type="module" crossorigin src="/assets/index-
|
|
36
|
+
<script type="module" crossorigin src="/assets/index-C65i4HWv.js"></script>
|
|
37
37
|
<link rel="modulepreload" crossorigin href="/assets/vendor-core-8R2fFbyN.js">
|
|
38
38
|
<link rel="modulepreload" crossorigin href="/assets/vendor-query-BXRECSI6.js">
|
|
39
39
|
<link rel="modulepreload" crossorigin href="/assets/vendor-icons-CDMuNDeL.js">
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@serifpersia/dango",
|
|
3
|
-
"version": "3.3.
|
|
3
|
+
"version": "3.3.7",
|
|
4
4
|
"description": "A web application for watching anime.",
|
|
5
5
|
"main": "orchestrator.js",
|
|
6
6
|
"repository": {
|
|
@@ -81,5 +81,5 @@
|
|
|
81
81
|
"allowScripts": {
|
|
82
82
|
"esbuild@0.28.2": true
|
|
83
83
|
},
|
|
84
|
-
"versionDate": "2026-09-
|
|
84
|
+
"versionDate": "2026-09-25T14:47:03.564Z"
|
|
85
85
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import logger from '../logger.js';
|
|
2
2
|
import { InsightsRepository } from '../repositories/insights.repository.js';
|
|
3
|
-
import { computeDiscordSyncStats } from '../lib/discord-roles-sync.service.js';
|
|
3
|
+
import { computeDiscordSyncStats, getCachedRecommendations, } from '../lib/discord-roles-sync.service.js';
|
|
4
4
|
export class InsightsController {
|
|
5
5
|
getWatchInsights = async (req, res) => {
|
|
6
6
|
const db = req.db;
|
|
@@ -195,4 +195,8 @@ export class InsightsController {
|
|
|
195
195
|
const db = req.db;
|
|
196
196
|
res.json(await computeDiscordSyncStats(db));
|
|
197
197
|
};
|
|
198
|
+
getRecommendations = async (req, res) => {
|
|
199
|
+
const db = req.db;
|
|
200
|
+
res.json({ candidates: await getCachedRecommendations(db) });
|
|
201
|
+
};
|
|
198
202
|
}
|
|
@@ -128,6 +128,40 @@ export class ProxyController {
|
|
|
128
128
|
'vibeplayer.site',
|
|
129
129
|
'aniwatchtv.site',
|
|
130
130
|
];
|
|
131
|
+
static NEXABLOOM_FAMILY_SUFFIXES = [
|
|
132
|
+
'.quavex.top',
|
|
133
|
+
'.nexabloom.top',
|
|
134
|
+
'.hiddenvertex.top',
|
|
135
|
+
'.ironhorizon.top',
|
|
136
|
+
'.solarhaven.top',
|
|
137
|
+
];
|
|
138
|
+
static NEXABLOOM_FALLBACK_HOSTS = ['fetch.nexabloom.top'];
|
|
139
|
+
static nexabloomFallbackUrls(urlStr) {
|
|
140
|
+
let host = '';
|
|
141
|
+
try {
|
|
142
|
+
host = new URL(urlStr).hostname.toLowerCase();
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
return [urlStr];
|
|
146
|
+
}
|
|
147
|
+
if (!ProxyController.NEXABLOOM_FAMILY_SUFFIXES.some((s) => host.endsWith(s))) {
|
|
148
|
+
return [urlStr];
|
|
149
|
+
}
|
|
150
|
+
const urls = [urlStr];
|
|
151
|
+
for (const fallback of ProxyController.NEXABLOOM_FALLBACK_HOSTS) {
|
|
152
|
+
if (fallback === host)
|
|
153
|
+
continue;
|
|
154
|
+
try {
|
|
155
|
+
const candidate = new URL(urlStr);
|
|
156
|
+
candidate.hostname = fallback;
|
|
157
|
+
urls.push(candidate.href);
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
// ignore
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return urls;
|
|
164
|
+
}
|
|
131
165
|
static isGotScrapingHost(urlStr) {
|
|
132
166
|
try {
|
|
133
167
|
const host = new URL(urlStr).hostname.toLowerCase();
|
|
@@ -259,17 +293,26 @@ export class ProxyController {
|
|
|
259
293
|
.set('Access-Control-Allow-Origin', '*')
|
|
260
294
|
.send(cached);
|
|
261
295
|
}
|
|
262
|
-
const resp = await
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
296
|
+
const resp = await (async () => {
|
|
297
|
+
let last = null;
|
|
298
|
+
for (const candidate of ProxyController.nexabloomFallbackUrls(urlStr)) {
|
|
299
|
+
const attempt = await gotScraping({
|
|
300
|
+
url: isNexabloomMasterUrl(candidate) ? signNexabloomMasterUrl(candidate) : candidate,
|
|
301
|
+
method: 'GET',
|
|
302
|
+
headers,
|
|
303
|
+
responseType: 'text',
|
|
304
|
+
timeout: { request: 30000 },
|
|
305
|
+
followRedirect: true,
|
|
306
|
+
throwHttpErrors: false,
|
|
307
|
+
});
|
|
308
|
+
last = attempt;
|
|
309
|
+
if (attempt.statusCode === 200 || attempt.statusCode === 206)
|
|
310
|
+
return attempt;
|
|
311
|
+
}
|
|
312
|
+
return last;
|
|
313
|
+
})();
|
|
314
|
+
if (!resp || (resp.statusCode !== 200 && resp.statusCode !== 206)) {
|
|
315
|
+
return res.status(resp?.statusCode ?? 502).send('Upstream error');
|
|
273
316
|
}
|
|
274
317
|
const finalUrl = resp.url || urlStr;
|
|
275
318
|
const baseUrl = new URL(finalUrl);
|
|
@@ -314,17 +357,23 @@ export class ProxyController {
|
|
|
314
357
|
if (ProxyController.isGotScrapingHost(urlStr)) {
|
|
315
358
|
if (req.headers.range)
|
|
316
359
|
headers['Range'] = req.headers.range;
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
360
|
+
let resp = null;
|
|
361
|
+
for (const candidate of ProxyController.nexabloomFallbackUrls(urlStr)) {
|
|
362
|
+
const attempt = await gotScraping({
|
|
363
|
+
url: candidate,
|
|
364
|
+
method: 'GET',
|
|
365
|
+
headers,
|
|
366
|
+
responseType: 'buffer',
|
|
367
|
+
timeout: { request: 30000 },
|
|
368
|
+
followRedirect: true,
|
|
369
|
+
throwHttpErrors: false,
|
|
370
|
+
});
|
|
371
|
+
resp = attempt;
|
|
372
|
+
if (attempt.statusCode === 200 || attempt.statusCode === 206)
|
|
373
|
+
break;
|
|
374
|
+
}
|
|
375
|
+
if (!resp || (resp.statusCode !== 200 && resp.statusCode !== 206)) {
|
|
376
|
+
return res.status(resp?.statusCode ?? 502).send('Upstream error');
|
|
328
377
|
}
|
|
329
378
|
const ct = resp.headers['content-type'];
|
|
330
379
|
const isVtt = urlStr.endsWith('.vtt') || urlStr.includes('.vtt?');
|
|
@@ -342,7 +391,29 @@ export class ProxyController {
|
|
|
342
391
|
res.send(resp.body);
|
|
343
392
|
}
|
|
344
393
|
else {
|
|
345
|
-
|
|
394
|
+
let upstream = null;
|
|
395
|
+
for (const candidate of ProxyController.nexabloomFallbackUrls(urlStr)) {
|
|
396
|
+
if (abortController.signal.aborted)
|
|
397
|
+
break;
|
|
398
|
+
try {
|
|
399
|
+
const attempt = await fetchWithRetry(candidate, { method: 'GET', headers }, { retries: 3, timeoutMs: 30000, signal: abortController.signal });
|
|
400
|
+
upstream = attempt;
|
|
401
|
+
if (attempt.status === 200 || attempt.status === 206)
|
|
402
|
+
break;
|
|
403
|
+
try {
|
|
404
|
+
await attempt.body?.cancel();
|
|
405
|
+
}
|
|
406
|
+
catch {
|
|
407
|
+
// ignore
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
catch {
|
|
411
|
+
// ignore
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
if (!upstream) {
|
|
415
|
+
return res.status(502).send('Upstream error');
|
|
416
|
+
}
|
|
346
417
|
const status = upstream.status;
|
|
347
418
|
if (status !== 200 && status !== 206) {
|
|
348
419
|
try {
|
|
@@ -11,6 +11,7 @@ import { dbAll, dbGet } from '../utils/db-utils.js';
|
|
|
11
11
|
import { searchAnilist, searchAnilistByTitle, getAiredEpisodesForShows, getAnilistEpisodes, getShowMetaById, isAnilistRateLimited, batchGetShowStatuses, } from '../lib/anilist.js';
|
|
12
12
|
import { kitsuSearchAnime } from '../lib/kitsu.js';
|
|
13
13
|
import { getMigratedId } from '../lib/migration.js';
|
|
14
|
+
import { offlineDb } from '../lib/offline-db.js';
|
|
14
15
|
const BACKGROUND_DISCOVERY_INTERVAL_MS = 5 * 60 * 1000;
|
|
15
16
|
const NUDGE_THROTTLE_MS = 120 * 1000;
|
|
16
17
|
const SLOW_MAX_RUN_MS = 5 * 60 * 1000;
|
|
@@ -466,6 +467,47 @@ export class WatchlistController {
|
|
|
466
467
|
}
|
|
467
468
|
}));
|
|
468
469
|
db.scheduleSave();
|
|
470
|
+
await this.sweepOfflinePosters(db);
|
|
471
|
+
}
|
|
472
|
+
async sweepOfflinePosters(db) {
|
|
473
|
+
const [wlMissing, metaMissing] = await Promise.all([
|
|
474
|
+
WatchlistRepository.getMissingThumbnails(db),
|
|
475
|
+
ShowsMetaRepository.getShowIdsMissingMeta(db),
|
|
476
|
+
]);
|
|
477
|
+
const ids = [
|
|
478
|
+
...new Set([...wlMissing.map((r) => r.id), ...metaMissing.map((r) => r.id)]),
|
|
479
|
+
].slice(0, 500);
|
|
480
|
+
if (ids.length === 0)
|
|
481
|
+
return;
|
|
482
|
+
for (const id of ids) {
|
|
483
|
+
const poster = this.resolveOfflinePoster(id);
|
|
484
|
+
if (poster)
|
|
485
|
+
await this.applyPoster(db, id, poster);
|
|
486
|
+
}
|
|
487
|
+
db.scheduleSave();
|
|
488
|
+
}
|
|
489
|
+
resolveOfflinePoster(id) {
|
|
490
|
+
if (/^\d+$/.test(id)) {
|
|
491
|
+
return offlineDb.getByAnilistId(Number(id))?.thumbnail?.trim() || null;
|
|
492
|
+
}
|
|
493
|
+
const mal = /^mal-(\d+)$/i.exec(id)?.[1];
|
|
494
|
+
if (mal)
|
|
495
|
+
return offlineDb.getByMalId(Number(mal))?.thumbnail?.trim() || null;
|
|
496
|
+
return null;
|
|
497
|
+
}
|
|
498
|
+
async applyPoster(db, id, poster) {
|
|
499
|
+
try {
|
|
500
|
+
ShowsMetaRepository.upsert(db, { id, thumbnail: poster });
|
|
501
|
+
}
|
|
502
|
+
catch {
|
|
503
|
+
// ignore
|
|
504
|
+
}
|
|
505
|
+
try {
|
|
506
|
+
await WatchlistRepository.updateThumbnail(db, id, poster);
|
|
507
|
+
}
|
|
508
|
+
catch {
|
|
509
|
+
// ignore
|
|
510
|
+
}
|
|
469
511
|
}
|
|
470
512
|
async getContinueWatchingData(req, limit) {
|
|
471
513
|
const rows = await WatchedEpisodesRepository.getContinueWatching(req.db, limit);
|