@serifpersia/dango 3.5.0 → 3.5.2
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 -15
- package/client/dist/assets/{AnimeCard-CpMObnqU.js → AnimeCard-BVnAhrys.js} +1 -1
- package/client/dist/assets/{AnimeInfo-DkDW8o1k.js → AnimeInfo-DYBLIXmE.js} +1 -1
- package/client/dist/assets/AnimeInfoPage-DnXV4EMC.js +2 -0
- package/client/dist/assets/{Asmr-B08Aimrk.js → Asmr-Dwlv-Af8.js} +2 -2
- package/client/dist/assets/{AsmrResetProgressModal-CZMbyJZz.js → AsmrResetProgressModal-CnF9ZTJQ.js} +1 -1
- package/client/dist/assets/{AvSyncCalibrator-VoOEtoX9.js → AvSyncCalibrator-Cr9n1euF.js} +1 -1
- package/client/dist/assets/Home-B_q2l74h.css +1 -0
- package/client/dist/assets/{Home-C3Vck_99.js → Home-CUZPz_wy.js} +1 -1
- package/client/dist/assets/Insights-hvaO1jbY.js +1 -0
- package/client/dist/assets/{LibraryListPage-CUzEUs5k.js → LibraryListPage-BeYz_I1y.js} +1 -1
- package/client/dist/assets/{ListeningList-CoeQ17lz.js → ListeningList-D-D-W7JK.js} +1 -1
- package/client/dist/assets/Manga-DqIR2B6F.js +1 -0
- package/client/dist/assets/{MangaInfoPage-wqYZN_sT.js → MangaInfoPage-CWqxWbb3.js} +1 -1
- package/client/dist/assets/{MangaReadPage-BbyExwBN.js → MangaReadPage-BlyJ-OdY.js} +1 -1
- package/client/dist/assets/{MangaResetProgressModal-Dg0xhJQI.js → MangaResetProgressModal-gvz9_X7z.js} +1 -1
- package/client/dist/assets/{Mature-ZKR5vMIv.js → Mature-B1Cuj67a.js} +1 -1
- package/client/dist/assets/MediaCard-GwFTnacn.js +1 -0
- package/client/dist/assets/Music-BEmRDuSo.js +1 -0
- package/client/dist/assets/{OptionListMenu-DcHg1QsF.js → OptionListMenu-Dlk9eHM7.js} +1 -1
- package/client/dist/assets/{Player-V9IueAzT.js → Player-khTFa_o1.js} +6 -6
- package/client/dist/assets/{PlayerSettings-BZtjBWWq.js → PlayerSettings-B16FnAO_.js} +1 -1
- package/client/dist/assets/QueueOptionsButton-DjBOibpt.js +1 -0
- package/client/dist/assets/{QueueRail-CO2ocUNx.js → QueueRail-DcLtEU46.js} +1 -1
- package/client/dist/assets/{Radio-CJ-ra4vb.js → Radio-rpaFuI72.js} +1 -1
- package/client/dist/assets/{ReadingList-CSvUUtZ3.js → ReadingList-DTnI5411.js} +1 -1
- package/client/dist/assets/{ResetProgressModal-5FOWMuqi.js → ResetProgressModal-Bcv92v7c.js} +1 -1
- package/client/dist/assets/{Search-q0GUdNMS.js → Search-DNxPl_On.js} +1 -1
- package/client/dist/assets/Settings-CQ-ykTG3.js +21 -0
- package/client/dist/assets/{Settings-AuFxQcJF.css → Settings-CwIWLSfY.css} +1 -1
- package/client/dist/assets/{SkeletonGrid-DY_7CXpX.js → SkeletonGrid-ZN-kS9re.js} +1 -1
- package/client/dist/assets/ToggleSwitch-BPil0d_k.css +1 -0
- package/client/dist/assets/ToggleSwitch-CN_ppEc9.js +1 -0
- package/client/dist/assets/{Trackers-Dn7P6AfI.js → Trackers-EN1_ol5G.js} +1 -1
- package/client/dist/assets/{Tv-BfaWoR72.js → Tv-C-4wd-BS.js} +4 -4
- package/client/dist/assets/{TvInfo-CgEVN57T.js → TvInfo-Mpzun3PF.js} +1 -1
- package/client/dist/assets/{TvPopup-B-vYyDQF.js → TvPopup-YYgg9-7r.js} +1 -1
- package/client/dist/assets/{TvSearch-CnnVkms6.js → TvSearch-CdL9EA-S.js} +1 -1
- package/client/dist/assets/{TvWatchlist-BLVF_row.js → TvWatchlist-DD6nRESU.js} +1 -1
- package/client/dist/assets/{Watchlist-wd9cCDOK.js → Watchlist-CD8UqukO.js} +1 -1
- package/client/dist/assets/{hls-Bej65gvf.js → hls-E94cMCjR.js} +1 -1
- package/client/dist/assets/index-CZW1But9.js +178 -0
- package/client/dist/assets/index-w_r2lTCe.css +1 -0
- package/client/dist/assets/{useAnimeInfoData-ZmMruFBq.js → useAnimeInfoData-C9tOyy6E.js} +1 -1
- package/client/dist/assets/{useAsmr-z-txLq1s.js → useAsmr-B4HAapZD.js} +1 -1
- package/client/dist/assets/{useAsmrPopup-DU55UhPN.js → useAsmrPopup-trIV7nUr.js} +1 -1
- package/client/dist/assets/{useMangaLibrary-D92WQbf5.js → useMangaLibrary-CZ2Gke8p.js} +1 -1
- package/client/dist/assets/{useMangaPopup-TJOA229J.js → useMangaPopup-80Daa4y-.js} +1 -1
- package/client/dist/assets/{useProviders-fmZrOyDC.js → useProviders-CqMq_6ue.js} +1 -1
- package/client/dist/assets/{useTv-CmfxzpS-.js → useTv-CHDhiKWu.js} +1 -1
- package/client/dist/assets/{useTvLibrary-0y9NZnsk.js → useTvLibrary-DWUGDtld.js} +1 -1
- package/client/dist/index.html +2 -2
- package/package.json +2 -2
- package/server/dist/app-hono.js +2 -10
- package/server/dist/config.js +0 -1
- package/server/dist/db/schema-anime.js +0 -4
- package/server/dist/db/schema-asmr.js +0 -12
- package/server/dist/db/schema-tv.js +0 -15
- package/server/dist/discord-gateway.js +0 -6
- package/server/dist/discord-rpc.js +0 -14
- package/server/dist/github-sync.js +1 -5
- package/server/dist/hono/proxy.js +19 -2
- package/server/dist/hono/tv.js +1 -1
- package/server/dist/hono/watchlist.js +8 -13
- package/server/dist/lib/anilist.js +2 -26
- package/server/dist/lib/anischedule.js +0 -3
- package/server/dist/lib/mal.js +0 -9
- package/server/dist/lib/{discord-roles-sync.service.js → recommendations.service.js} +0 -149
- package/server/dist/lib/tracker/anilist-tracker.js +0 -3
- package/server/dist/lib/tracker/sync.service.js +0 -1
- package/server/dist/providers/manga/manga.types.js +1 -7
- package/server/dist/repositories/mal-cache.repository.js +0 -3
- package/server/dist/server.js +0 -2
- package/server/dist/shipped-defaults.js +0 -1
- package/server/dist/sync.js +0 -1
- package/server/dist/trpc/errors.js +0 -6
- package/server/dist/trpc/index.js +0 -1
- package/server/dist/trpc/routers/discord.js +0 -4
- package/server/dist/trpc/routers/discovery.js +1 -5
- package/server/dist/trpc/routers/insights.js +1 -31
- package/server/dist/trpc/routers/settings.js +0 -10
- package/server/dist/trpc/routers/watchlist.js +3 -3
- package/server/package.json +1 -1
- package/client/dist/assets/AnimeInfoPage-l0wIHS7j.js +0 -2
- package/client/dist/assets/Home-B6bte0tw.css +0 -1
- package/client/dist/assets/Insights-nW34Wbg1.js +0 -1
- package/client/dist/assets/Manga-CeSb5K2w.js +0 -1
- package/client/dist/assets/Map-BtXwpBOC.js +0 -1
- package/client/dist/assets/Map-CXFqXmsZ.css +0 -1
- package/client/dist/assets/MediaCard-9NGAjN3E.js +0 -1
- package/client/dist/assets/Music-CljVJFCj.js +0 -1
- package/client/dist/assets/QueueOptionsButton-CzzisJ3N.js +0 -1
- package/client/dist/assets/Settings-CyT9Lyhz.js +0 -21
- package/client/dist/assets/index-Cc1Up_NI.css +0 -1
- package/client/dist/assets/index-Dk0ASzEz.js +0 -178
- package/server/dist/sync-config.js +0 -23
- 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-
|
|
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-CZW1But9.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-
|
|
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-CZ2Gke8p.js";import{d as I}from"./index-CZW1But9.js";import{p as s,a as k}from"./MediaCard-GwFTnacn.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{
|
|
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-CZW1But9.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-
|
|
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-GwFTnacn.js";import{T as g}from"./TvPopup-YYgg9-7r.js";import{c as m,i as f}from"./useTvLibrary-DWUGDtld.js";import{b as i}from"./vendor-query-BBT5_sHx.js";import{h as p}from"./index-CZW1But9.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-
|
|
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-CZW1But9.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};
|
package/client/dist/index.html
CHANGED
|
@@ -33,11 +33,11 @@
|
|
|
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-CZW1But9.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-
|
|
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.5.
|
|
3
|
+
"version": "3.5.2",
|
|
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-
|
|
86
|
+
"versionDate": "2026-10-08T09:03:59.763Z"
|
|
87
87
|
}
|
package/server/dist/app-hono.js
CHANGED
|
@@ -13,7 +13,7 @@ import { importMangaFromMalXmlItems, } from './lib/tracker/manga-sync.service.js
|
|
|
13
13
|
import { LibraryRepository } from './repositories/library.repository.js';
|
|
14
14
|
import { performAsmrWriteTransactionAsync, performMangaWriteTransactionAsync, performTvWriteTransactionAsync, performWriteTransactionAsync, KIND_SYNC, setKindManifestVersion, } from './sync.js';
|
|
15
15
|
import { ANIME_SYNC_TABLES, ASMR_SYNC_TABLES, MANGA_SYNC_TABLES, TV_SYNC_TABLES, exportTables, importTables, normalizePayload, readPayloadVersion, } from './sync-payload.js';
|
|
16
|
-
import { getMalImportStatus, prepareMalImport, executeMalImport
|
|
16
|
+
import { getMalImportStatus, prepareMalImport, executeMalImport } from './lib/mal-import.js';
|
|
17
17
|
import { registerMusic } from './hono/music.js';
|
|
18
18
|
import { registerAuth } from './hono/auth.js';
|
|
19
19
|
import { registerTv } from './hono/tv.js';
|
|
@@ -45,14 +45,6 @@ function mapMalMangaXmlStatus(status) {
|
|
|
45
45
|
return 'Planned';
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
|
-
const gatewayLog = logger.child({ module: 'DiscordGatewayRoutes' });
|
|
49
|
-
const TRACKER_TOKEN_KEY = 'tracker_anilist_token';
|
|
50
|
-
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
48
|
export function getHonoClientIp(c) {
|
|
57
49
|
try {
|
|
58
50
|
return getConnInfo(c).remote.address;
|
|
@@ -590,7 +582,7 @@ export function createHonoApp(getState, getGuardState, getDbs, dbAdmin, provider
|
|
|
590
582
|
});
|
|
591
583
|
registerMusic(app);
|
|
592
584
|
registerAuth(app, getDbs, runSync);
|
|
593
|
-
registerTv(app, media.
|
|
585
|
+
registerTv(app, media.getTvProvider);
|
|
594
586
|
registerProxy(app);
|
|
595
587
|
app.onError((err, c) => {
|
|
596
588
|
logger.error({ err, url: c.req.path, method: c.req.method }, 'Unhandled error');
|
package/server/dist/config.js
CHANGED
|
@@ -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 || '',
|
|
@@ -59,7 +59,3 @@ export const tempShowIds = sqliteTable('temp_show_ids', {
|
|
|
59
59
|
thumbnail: text('thumbnail'),
|
|
60
60
|
createdAt: integer('createdAt').notNull(),
|
|
61
61
|
}, (t) => [primaryKey({ columns: [t.id] })]);
|
|
62
|
-
export const legacyIdMapping = sqliteTable('legacy_id_mapping', {
|
|
63
|
-
legacyId: text('legacyId').notNull(),
|
|
64
|
-
numericId: text('numericId'),
|
|
65
|
-
}, (t) => [primaryKey({ columns: [t.legacyId] })]);
|
|
@@ -1,16 +1,4 @@
|
|
|
1
1
|
import { integer, primaryKey, real, sqliteTable, text } from 'drizzle-orm/sqlite-core';
|
|
2
|
-
export const asmrLibrary = sqliteTable('asmr_library', {
|
|
3
|
-
id: text('id').notNull(),
|
|
4
|
-
rjCode: text('rjCode').notNull(),
|
|
5
|
-
title: text('title'),
|
|
6
|
-
thumbnail: text('thumbnail'),
|
|
7
|
-
status: text('status'),
|
|
8
|
-
isAdult: integer('isAdult'),
|
|
9
|
-
lastTrackIndex: integer('lastTrackIndex'),
|
|
10
|
-
lastTrackLabel: text('lastTrackLabel'),
|
|
11
|
-
lastPosition: real('lastPosition'),
|
|
12
|
-
updatedAt: integer('updatedAt'),
|
|
13
|
-
}, (t) => [primaryKey({ columns: [t.id] })]);
|
|
14
2
|
export const asmrProgress = sqliteTable('asmr_progress', {
|
|
15
3
|
workId: text('workId').notNull(),
|
|
16
4
|
trackIndex: integer('trackIndex').notNull(),
|
|
@@ -1,19 +1,4 @@
|
|
|
1
1
|
import { integer, primaryKey, real, sqliteTable, text } from 'drizzle-orm/sqlite-core';
|
|
2
|
-
export const tvLibrary = sqliteTable('tv_library', {
|
|
3
|
-
id: text('id').notNull(),
|
|
4
|
-
tmdbId: integer('tmdbId').notNull(),
|
|
5
|
-
mediaType: text('mediaType').notNull(),
|
|
6
|
-
title: text('title'),
|
|
7
|
-
poster: text('poster'),
|
|
8
|
-
backdrop: text('backdrop'),
|
|
9
|
-
year: text('year'),
|
|
10
|
-
overview: text('overview'),
|
|
11
|
-
status: text('status'),
|
|
12
|
-
adult: integer('adult'),
|
|
13
|
-
lastSeason: integer('lastSeason'),
|
|
14
|
-
lastEpisode: integer('lastEpisode'),
|
|
15
|
-
updatedAt: integer('updatedAt'),
|
|
16
|
-
}, (t) => [primaryKey({ columns: [t.id] })]);
|
|
17
2
|
export const tvProgress = sqliteTable('tv_progress', {
|
|
18
3
|
mediaId: text('mediaId').notNull(),
|
|
19
4
|
season: integer('season').notNull(),
|
|
@@ -7,7 +7,6 @@ class DiscordGatewayService {
|
|
|
7
7
|
gateway = null;
|
|
8
8
|
heartbeat = null;
|
|
9
9
|
token = null;
|
|
10
|
-
isConnected = false;
|
|
11
10
|
reconnectTimeout = null;
|
|
12
11
|
lastActivity = null;
|
|
13
12
|
isEnabled = false;
|
|
@@ -276,7 +275,6 @@ class DiscordGatewayService {
|
|
|
276
275
|
clearInterval(this.heartbeat);
|
|
277
276
|
this.heartbeat = null;
|
|
278
277
|
}
|
|
279
|
-
this.isConnected = false;
|
|
280
278
|
}
|
|
281
279
|
connect() {
|
|
282
280
|
if (!this.isEnabled || !this.token || this.gateway)
|
|
@@ -285,7 +283,6 @@ class DiscordGatewayService {
|
|
|
285
283
|
log.debug('Connecting Discord gateway');
|
|
286
284
|
this.gateway = new WebSocket(wsUrl);
|
|
287
285
|
let heartbeatInterval = 41250;
|
|
288
|
-
let responded = false;
|
|
289
286
|
this.gateway.on('message', (data) => {
|
|
290
287
|
try {
|
|
291
288
|
const msg = JSON.parse(data.toString());
|
|
@@ -343,8 +340,6 @@ class DiscordGatewayService {
|
|
|
343
340
|
}
|
|
344
341
|
else if (msg.op === 0) {
|
|
345
342
|
if (msg.t === 'READY') {
|
|
346
|
-
responded = true;
|
|
347
|
-
this.isConnected = true;
|
|
348
343
|
log.info(`Gateway READY as ${msg.d.user?.username || 'unknown'}`);
|
|
349
344
|
if (this.lastActivity) {
|
|
350
345
|
void this.updatePresence(this.lastActivity).catch((err) => log.warn({ err }, 'failed to restore presence after READY'));
|
|
@@ -374,7 +369,6 @@ class DiscordGatewayService {
|
|
|
374
369
|
});
|
|
375
370
|
}
|
|
376
371
|
handleDisconnect() {
|
|
377
|
-
this.isConnected = false;
|
|
378
372
|
if (this.heartbeat) {
|
|
379
373
|
clearInterval(this.heartbeat);
|
|
380
374
|
this.heartbeat = null;
|
|
@@ -213,20 +213,6 @@ class DiscordRPCService {
|
|
|
213
213
|
}
|
|
214
214
|
discordGatewayService.disconnect();
|
|
215
215
|
}
|
|
216
|
-
formatTime(seconds) {
|
|
217
|
-
if (isNaN(seconds) || seconds <= 0)
|
|
218
|
-
return '00:00';
|
|
219
|
-
const h = Math.floor(seconds / 3600);
|
|
220
|
-
const m = Math.floor((seconds % 3600) / 60);
|
|
221
|
-
const s = Math.floor(seconds % 60);
|
|
222
|
-
const mm = String(m).padStart(2, '0');
|
|
223
|
-
const ss = String(s).padStart(2, '0');
|
|
224
|
-
if (h > 0) {
|
|
225
|
-
const hh = String(h).padStart(2, '0');
|
|
226
|
-
return `${hh}:${mm}:${ss}`;
|
|
227
|
-
}
|
|
228
|
-
return `${mm}:${ss}`;
|
|
229
|
-
}
|
|
230
216
|
async updatePresence(data) {
|
|
231
217
|
this.lastActivity = data;
|
|
232
218
|
if (data.sessionId) {
|
|
@@ -84,7 +84,6 @@ function normalizePayload(input) {
|
|
|
84
84
|
}
|
|
85
85
|
class GitHubSyncService {
|
|
86
86
|
deviceState = { status: 'idle' };
|
|
87
|
-
devicePromise = null;
|
|
88
87
|
isAuthenticated() {
|
|
89
88
|
return !!process.env.GITHUB_TOKEN;
|
|
90
89
|
}
|
|
@@ -170,7 +169,7 @@ class GitHubSyncService {
|
|
|
170
169
|
const verificationReady = new Promise((resolve) => {
|
|
171
170
|
resolveVerification = resolve;
|
|
172
171
|
});
|
|
173
|
-
|
|
172
|
+
void this.runDeviceAuth(db, runSyncSequence, resolveVerification);
|
|
174
173
|
await verificationReady;
|
|
175
174
|
return this.deviceState;
|
|
176
175
|
}
|
|
@@ -364,9 +363,6 @@ class GitHubSyncService {
|
|
|
364
363
|
resolveVerification();
|
|
365
364
|
log.error({ err }, 'GitHub device authentication failed');
|
|
366
365
|
}
|
|
367
|
-
finally {
|
|
368
|
-
this.devicePromise = null;
|
|
369
|
-
}
|
|
370
366
|
}
|
|
371
367
|
async getOctokit() {
|
|
372
368
|
if (!process.env.GITHUB_TOKEN) {
|
|
@@ -9,6 +9,7 @@ import { isSafeExternalUrl } from '../utils/security.utils.js';
|
|
|
9
9
|
import { fetchWithRetry, linkAbort, setProxyHeaders } from '../utils/http.utils.js';
|
|
10
10
|
import { MEGAPLAY_ORIGIN, isNexabloomMasterUrl, signNexabloomMasterUrl, } from '../utils/megaplay.utils.js';
|
|
11
11
|
const proxyCache = new AppCache({ ttlSeconds: 30, maxKeys: 500 });
|
|
12
|
+
const imageCache = new AppCache({ ttlSeconds: 86400, maxKeys: 3000 });
|
|
12
13
|
function assTimeToVtt(value) {
|
|
13
14
|
const m = value.trim().match(/^(-?\d+):(\d{2}):(\d{2})[.:](\d{2,3})$/);
|
|
14
15
|
if (!m)
|
|
@@ -784,6 +785,7 @@ export function registerProxy(app) {
|
|
|
784
785
|
const targetUrl = url;
|
|
785
786
|
const cookie = c.req.query('cookie') || '';
|
|
786
787
|
const ua = c.req.query('ua') || '';
|
|
788
|
+
const strict = c.req.query('strict') === '1';
|
|
787
789
|
const abort = new AbortController();
|
|
788
790
|
linkAbort(c.req.raw.signal, abort);
|
|
789
791
|
let refererValue = 'https://anilist.co/';
|
|
@@ -791,6 +793,16 @@ export function registerProxy(app) {
|
|
|
791
793
|
'User-Agent': ua ||
|
|
792
794
|
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
|
|
793
795
|
};
|
|
796
|
+
const cacheKey = `${targetUrl}|${cookie}|${ua}`;
|
|
797
|
+
const cached = imageCache.get(cacheKey);
|
|
798
|
+
if (cached) {
|
|
799
|
+
const hit = new Headers();
|
|
800
|
+
hit.set('Cache-Control', 'public, max-age=604800, immutable');
|
|
801
|
+
hit.set('Content-Type', cached.contentType);
|
|
802
|
+
hit.set('Access-Control-Allow-Origin', '*');
|
|
803
|
+
hit.set('X-Dango-Cache', 'hit');
|
|
804
|
+
return new Response(cached.body, { headers: hit });
|
|
805
|
+
}
|
|
794
806
|
try {
|
|
795
807
|
if (targetUrl.includes('animepahe')) {
|
|
796
808
|
refererValue = 'https://animepahe.pw/';
|
|
@@ -818,7 +830,6 @@ export function registerProxy(app) {
|
|
|
818
830
|
headers['User-Agent'] = uaParam;
|
|
819
831
|
}
|
|
820
832
|
headers['Referer'] = refererValue;
|
|
821
|
-
let lastStatus = 0;
|
|
822
833
|
let body = null;
|
|
823
834
|
let contentType = 'image/webp';
|
|
824
835
|
for (let attempt = 0; attempt < 3 && !body; attempt++) {
|
|
@@ -831,7 +842,6 @@ export function registerProxy(app) {
|
|
|
831
842
|
throwHttpErrors: false,
|
|
832
843
|
timeout: { request: 30000 },
|
|
833
844
|
});
|
|
834
|
-
lastStatus = resp.statusCode;
|
|
835
845
|
if (resp.statusCode === 200 && resp.rawBody?.length) {
|
|
836
846
|
body = Buffer.from(resp.rawBody);
|
|
837
847
|
contentType = String(resp.headers['content-type'] || 'image/webp');
|
|
@@ -842,12 +852,17 @@ export function registerProxy(app) {
|
|
|
842
852
|
}
|
|
843
853
|
}
|
|
844
854
|
if (body) {
|
|
855
|
+
imageCache.set(cacheKey, { body, contentType });
|
|
845
856
|
const outHeaders = new Headers();
|
|
846
857
|
outHeaders.set('Cache-Control', 'public, max-age=604800, immutable');
|
|
847
858
|
outHeaders.set('Content-Type', contentType);
|
|
848
859
|
outHeaders.set('Access-Control-Allow-Origin', '*');
|
|
849
860
|
return new Response(body, { headers: outHeaders });
|
|
850
861
|
}
|
|
862
|
+
// strict=1 callers retry another candidate instead of settling for the
|
|
863
|
+
// placeholder, which would otherwise read as a successful load.
|
|
864
|
+
if (strict)
|
|
865
|
+
return c.text('Not Found', 404);
|
|
851
866
|
const placeholder = placeholderSvg();
|
|
852
867
|
if (placeholder)
|
|
853
868
|
return placeholder;
|
|
@@ -856,6 +871,8 @@ export function registerProxy(app) {
|
|
|
856
871
|
catch {
|
|
857
872
|
if (abort.signal.aborted)
|
|
858
873
|
return new Response(null, { status: 499 });
|
|
874
|
+
if (strict)
|
|
875
|
+
return c.text('Not Found', 502);
|
|
859
876
|
const placeholder = placeholderSvg();
|
|
860
877
|
if (placeholder)
|
|
861
878
|
return placeholder;
|
package/server/dist/hono/tv.js
CHANGED
|
@@ -32,7 +32,7 @@ function isPlaylistBody(body) {
|
|
|
32
32
|
.trimStart()
|
|
33
33
|
.startsWith('#EXTM3U'));
|
|
34
34
|
}
|
|
35
|
-
export function registerTv(app,
|
|
35
|
+
export function registerTv(app, getTvProvider) {
|
|
36
36
|
app.get('/api/tv/embed/:provider/:type/:tmdbId', async (c) => {
|
|
37
37
|
const provider = c.req.param('provider');
|
|
38
38
|
const type = c.req.param('type');
|
|
@@ -20,8 +20,8 @@ let discoveryTotal = 0;
|
|
|
20
20
|
let discoveryDone = 0;
|
|
21
21
|
let discoveryStopped = false;
|
|
22
22
|
let triggerImpl = null;
|
|
23
|
-
export function triggerWatchlistDiscovery(
|
|
24
|
-
return triggerImpl?.(
|
|
23
|
+
export function triggerWatchlistDiscovery() {
|
|
24
|
+
return triggerImpl?.() ?? false;
|
|
25
25
|
}
|
|
26
26
|
export function stopWatchlistDiscovery() {
|
|
27
27
|
discoveryStopped = true;
|
|
@@ -41,7 +41,7 @@ export function getWatchlistDiscoveryStatus() {
|
|
|
41
41
|
}
|
|
42
42
|
export function startWatchlistDiscovery(getDb) {
|
|
43
43
|
const anilistIdCache = new Map();
|
|
44
|
-
const getAnilistId = async (showId
|
|
44
|
+
const getAnilistId = async (showId) => {
|
|
45
45
|
if (anilistIdCache.has(showId)) {
|
|
46
46
|
return anilistIdCache.get(showId) || null;
|
|
47
47
|
}
|
|
@@ -90,7 +90,7 @@ export function startWatchlistDiscovery(getDb) {
|
|
|
90
90
|
return;
|
|
91
91
|
const show = watchingShows[nextIndex];
|
|
92
92
|
nextIndex += 1;
|
|
93
|
-
const id = await getAnilistId(show.id
|
|
93
|
+
const id = await getAnilistId(show.id);
|
|
94
94
|
discoveryDone += 1;
|
|
95
95
|
anilistResults.push({ show, id });
|
|
96
96
|
}
|
|
@@ -177,13 +177,11 @@ export function startWatchlistDiscovery(getDb) {
|
|
|
177
177
|
]);
|
|
178
178
|
const watchedSet = new Set(watchedEps.map((e) => e.toString()));
|
|
179
179
|
const dismissedSet = new Set(dismissedEps.map((e) => e.episodeNumber.toString()));
|
|
180
|
-
let inserted = false;
|
|
181
180
|
for (const { episodeKey, airingAt } of episodes) {
|
|
182
181
|
if (nowUnix - airingAt > 30 * 24 * 60 * 60)
|
|
183
182
|
continue;
|
|
184
183
|
if (!watchedSet.has(episodeKey) && !dismissedSet.has(episodeKey)) {
|
|
185
184
|
await NotificationsRepository.addDiscovered(db, watchlistId, episodeKey);
|
|
186
|
-
inserted = true;
|
|
187
185
|
}
|
|
188
186
|
}
|
|
189
187
|
}
|
|
@@ -214,11 +212,9 @@ export function startWatchlistDiscovery(getDb) {
|
|
|
214
212
|
]);
|
|
215
213
|
const watchedSet = new Set(watchedEps.map((e) => e.toString()));
|
|
216
214
|
const dismissedSet = new Set(dismissedEps.map((e) => e.episodeNumber.toString()));
|
|
217
|
-
let inserted = false;
|
|
218
215
|
for (const { episodeKey } of episodes) {
|
|
219
216
|
if (!watchedSet.has(episodeKey) && !dismissedSet.has(episodeKey)) {
|
|
220
217
|
await NotificationsRepository.addDiscovered(db, watchlistId, episodeKey);
|
|
221
|
-
inserted = true;
|
|
222
218
|
}
|
|
223
219
|
}
|
|
224
220
|
}
|
|
@@ -241,14 +237,13 @@ export function startWatchlistDiscovery(getDb) {
|
|
|
241
237
|
}
|
|
242
238
|
}
|
|
243
239
|
};
|
|
244
|
-
triggerImpl = (
|
|
240
|
+
triggerImpl = () => {
|
|
245
241
|
if (discoveryStopped || discoveryBusy)
|
|
246
242
|
return false;
|
|
247
|
-
|
|
248
|
-
if (!force && now - lastExternalDiscoveryAt < NUDGE_THROTTLE_MS)
|
|
243
|
+
if (Date.now() - lastExternalDiscoveryAt < NUDGE_THROTTLE_MS)
|
|
249
244
|
return false;
|
|
250
|
-
lastExternalDiscoveryAt = now;
|
|
251
|
-
runDiscovery(
|
|
245
|
+
lastExternalDiscoveryAt = Date.now();
|
|
246
|
+
runDiscovery(true);
|
|
252
247
|
return true;
|
|
253
248
|
};
|
|
254
249
|
discoveryIntervalId = setInterval(() => {
|
|
@@ -24,9 +24,6 @@ export function anilistUnavailable() {
|
|
|
24
24
|
export function isAnilistRateLimited() {
|
|
25
25
|
return anilistRateLimited && anilistCooldownUntil > Date.now();
|
|
26
26
|
}
|
|
27
|
-
export function markAnilistDownAtBoot() {
|
|
28
|
-
anilistWasDownAtBoot = anilistUnavailable();
|
|
29
|
-
}
|
|
30
27
|
export async function checkAnilistStatus() {
|
|
31
28
|
const available = await performAnilistStatusCheck();
|
|
32
29
|
if (!available) {
|
|
@@ -995,7 +992,7 @@ export async function batchGetShowStatuses(ids) {
|
|
|
995
992
|
for (let i = 0; i < ids.length; i += BATCH) {
|
|
996
993
|
const batch = ids.slice(i, i + BATCH);
|
|
997
994
|
const aliases = batch
|
|
998
|
-
.map((
|
|
995
|
+
.map((_id, idx) => `a${idx}: Media(id: $id${idx}, type: ANIME) { id status }`)
|
|
999
996
|
.join('\n');
|
|
1000
997
|
const variables = {};
|
|
1001
998
|
batch.forEach((id, idx) => {
|
|
@@ -1253,7 +1250,7 @@ export async function getAiredEpisodesForShows(ids, startDate, endDate) {
|
|
|
1253
1250
|
for (let i = 0; i < ids.length; i += BATCH) {
|
|
1254
1251
|
const batch = ids.slice(i, i + BATCH);
|
|
1255
1252
|
const aliases = batch
|
|
1256
|
-
.map((
|
|
1253
|
+
.map((_id, idx) => `a${idx}: Media(id: $id${idx}, type: ANIME) { id airingSchedule(perPage: 100) { nodes { episode airingAt } } }`)
|
|
1257
1254
|
.join('\n');
|
|
1258
1255
|
const variables = {};
|
|
1259
1256
|
batch.forEach((id, idx) => {
|
|
@@ -1279,27 +1276,6 @@ export async function getAiredEpisodesForShows(ids, startDate, endDate) {
|
|
|
1279
1276
|
setCachedAiredEpisodes(cacheKey, results);
|
|
1280
1277
|
return results;
|
|
1281
1278
|
}
|
|
1282
|
-
export async function getAiredEpisodesForShow(id, startDate, endDate) {
|
|
1283
|
-
const dayStart = Math.floor(startDate.getTime() / 1000);
|
|
1284
|
-
const dayEnd = Math.floor(endDate.getTime() / 1000);
|
|
1285
|
-
const query = `
|
|
1286
|
-
query ($id: Int, $dayStart: Int, $dayEnd: Int) {
|
|
1287
|
-
Media(id: $id, type: ANIME) {
|
|
1288
|
-
id
|
|
1289
|
-
airingSchedule(airingAt_greater: $dayStart, airingAt_lesser: $dayEnd) {
|
|
1290
|
-
episode
|
|
1291
|
-
airingAt
|
|
1292
|
-
}
|
|
1293
|
-
}
|
|
1294
|
-
}
|
|
1295
|
-
`;
|
|
1296
|
-
const result = await anilistRequest(query, { id, dayStart, dayEnd });
|
|
1297
|
-
const schedules = result?.data?.Media?.airingSchedule;
|
|
1298
|
-
if (!schedules || schedules.length === 0)
|
|
1299
|
-
return [];
|
|
1300
|
-
const now = Math.floor(Date.now() / 1000);
|
|
1301
|
-
return schedules.filter((s) => s.airingAt <= now).map((s) => s.episode);
|
|
1302
|
-
}
|
|
1303
1279
|
export async function getSchedule(date, format, adult = false) {
|
|
1304
1280
|
if (format === 'ADULT') {
|
|
1305
1281
|
adult = true;
|
package/server/dist/lib/mal.js
CHANGED
|
@@ -269,14 +269,6 @@ const MAL_STATUS_IDS = {
|
|
|
269
269
|
cancelled: undefined,
|
|
270
270
|
not_yet_released: '3',
|
|
271
271
|
};
|
|
272
|
-
const MAL_RATING_IDS = {
|
|
273
|
-
g: '1',
|
|
274
|
-
pg: '2',
|
|
275
|
-
pg13: '3',
|
|
276
|
-
r17: '4',
|
|
277
|
-
r: '5',
|
|
278
|
-
rx: '6',
|
|
279
|
-
};
|
|
280
272
|
const MAL_ORDER_IDS = {
|
|
281
273
|
start_date: '2',
|
|
282
274
|
score: '3',
|
|
@@ -617,7 +609,6 @@ export async function malSearchMedia(store, options = {}, enrichTitles = true) {
|
|
|
617
609
|
}
|
|
618
610
|
const url = `https://myanimelist.net/anime.php?${params.toString()}`;
|
|
619
611
|
const key = `mal:search:${params.toString()}:page:${page}`;
|
|
620
|
-
const parseHtml = (html) => parseSearchResults(html).map(toAnilistSearchMedia);
|
|
621
612
|
const finish = (list) => enrichTitles ? enrichCardTitles(store, list) : Promise.resolve(list);
|
|
622
613
|
let serverSorted = false;
|
|
623
614
|
let html;
|