@serifpersia/dango 3.6.7 → 3.6.8
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-C3P9zWi9.js → AnimeCard-DraA_TF9.js} +1 -1
- package/client/dist/assets/{AnimeInfo-Et6687Tl.js → AnimeInfo-BJ7pzs-P.js} +1 -1
- package/client/dist/assets/{AnimeInfoPage-DX4eNnlH.js → AnimeInfoPage-B_esUr5C.js} +2 -2
- package/client/dist/assets/{Asmr-CCdEWrFi.js → Asmr-Ckuh2xfW.js} +1 -1
- package/client/dist/assets/{AsmrResetProgressModal-DgEWBJOw.js → AsmrResetProgressModal-BS6UFJN4.js} +1 -1
- package/client/dist/assets/{AvSyncCalibrator-CQ2fjf2E.js → AvSyncCalibrator-DwHCd9yB.js} +1 -1
- package/client/dist/assets/{Home-DUkUTTTy.js → Home-DuM1EuvN.js} +1 -1
- package/client/dist/assets/{Insights-QCh4xW12.js → Insights-D3Pd9c9V.js} +1 -1
- package/client/dist/assets/{LibraryListPage-BoTxRqHz.js → LibraryListPage-Dm2yVn0-.js} +1 -1
- package/client/dist/assets/{ListeningList-DguKjI_t.js → ListeningList-D-zt6EXZ.js} +1 -1
- package/client/dist/assets/{Manga-HydR-L7o.js → Manga-JwprMKBk.js} +1 -1
- package/client/dist/assets/{MangaInfoPage-CBakEI9a.js → MangaInfoPage-DaeyJl2p.js} +1 -1
- package/client/dist/assets/{MangaReadPage-DwAR3iPy.js → MangaReadPage-COg0fHI9.js} +1 -1
- package/client/dist/assets/{MangaResetProgressModal-DGzl8vsO.js → MangaResetProgressModal-kguXv0_M.js} +1 -1
- package/client/dist/assets/{Mature-Pi0TxKuF.js → Mature-IHDaasrs.js} +1 -1
- package/client/dist/assets/{MediaCard--cuFxOCS.js → MediaCard-CpwsrXo5.js} +1 -1
- package/client/dist/assets/{Music-CQpyXTLH.js → Music-Dks4tPeK.js} +1 -1
- package/client/dist/assets/{OptionListMenu-B3D39dGS.js → OptionListMenu-11CcAsy8.js} +1 -1
- package/client/dist/assets/{Player-CsH4x_I_.js → Player-DQoEQkHL.js} +2 -2
- package/client/dist/assets/{PlayerSettings-DGxBsi3F.js → PlayerSettings-D4Ud4kv4.js} +1 -1
- package/client/dist/assets/{QueueOptionsButton-Djq4Cpex.js → QueueOptionsButton-B8PlPKjC.js} +1 -1
- package/client/dist/assets/{QueueRail-DOrYgNVX.js → QueueRail-B4Dok7U3.js} +1 -1
- package/client/dist/assets/{Radio-DF6g6odd.js → Radio-BHK6LSk4.js} +1 -1
- package/client/dist/assets/{ReadingList-DwYoBrNk.js → ReadingList-BCsxIz57.js} +1 -1
- package/client/dist/assets/{ResetProgressModal-CMF86mX6.js → ResetProgressModal-BXHxSfy-.js} +1 -1
- package/client/dist/assets/{Search-DFkhuk-x.js → Search-PK1ds07O.js} +1 -1
- package/client/dist/assets/{Settings-DBB2r0VQ.js → Settings-Bxk_x55l.js} +1 -1
- package/client/dist/assets/{SkeletonGrid-DJelJgAv.js → SkeletonGrid-CpJCZSRN.js} +1 -1
- package/client/dist/assets/{Trackers-DGYXFhYZ.js → Trackers-RieCh_84.js} +1 -1
- package/client/dist/assets/{Tv-C5MDGD2Z.js → Tv-DLytPJUj.js} +1 -1
- package/client/dist/assets/{TvInfo-DrKuK13u.js → TvInfo-C8djKebR.js} +1 -1
- package/client/dist/assets/{TvPopup-DtHilRDT.js → TvPopup-40JGGJDk.js} +1 -1
- package/client/dist/assets/{TvSearch-CzNxdu_w.js → TvSearch-A3hbospf.js} +1 -1
- package/client/dist/assets/{TvWatchlist-D7p4br6S.js → TvWatchlist-DuXjqmZM.js} +1 -1
- package/client/dist/assets/{Watchlist-DrOzswl2.js → Watchlist-B9wGBh0u.js} +1 -1
- package/client/dist/assets/{hls-BGCX7mJy.js → hls-CZ5X3Ya-.js} +1 -1
- package/client/dist/assets/{index-CnfZwsds.js → index-dRhc9-ol.js} +2 -2
- package/client/dist/assets/{useAnimeInfoData-DYXWBPF-.js → useAnimeInfoData-ZzDtLfvD.js} +1 -1
- package/client/dist/assets/{useAsmr-BbhQ59bk.js → useAsmr-CKOdHgol.js} +1 -1
- package/client/dist/assets/{useAsmrPopup-SymV1J-b.js → useAsmrPopup-SFHPhpcl.js} +1 -1
- package/client/dist/assets/{useMangaLibrary-DuesWQaE.js → useMangaLibrary-CKATe31P.js} +1 -1
- package/client/dist/assets/{useMangaPopup-D57OVGUr.js → useMangaPopup-C75QuYgY.js} +1 -1
- package/client/dist/assets/{useProviders-mpQb_dix.js → useProviders-CMtvZbEi.js} +1 -1
- package/client/dist/assets/{useTv-DGflCsSI.js → useTv-DUX4zYMr.js} +1 -1
- package/client/dist/assets/{useTvLibrary-BqeSmyc4.js → useTvLibrary-By4Gt-R_.js} +1 -1
- package/client/dist/index.html +1 -1
- package/package.json +2 -2
- package/server/dist/app-auth.js +1 -157
- package/server/dist/app-hono.js +7 -557
- package/server/dist/config.js +1 -174
- package/server/dist/db/drizzle.js +1 -16
- package/server/dist/db/schema-anime.js +1 -61
- package/server/dist/db/schema-asmr.js +1 -13
- package/server/dist/db/schema-manga.js +1 -31
- package/server/dist/db/schema-tv.js +1 -18
- package/server/dist/db.js +1 -217
- package/server/dist/discord-gateway.js +1 -385
- package/server/dist/discord-rpc.js +1 -413
- package/server/dist/github-sync.js +1 -508
- package/server/dist/google.js +1 -741
- package/server/dist/hono/asmr.js +1 -10
- package/server/dist/hono/auth.js +3 -26
- package/server/dist/hono/music.js +1 -209
- package/server/dist/hono/proxy.js +36 -783
- package/server/dist/hono/tv.js +5 -200
- package/server/dist/hono/watchlist.js +2 -338
- package/server/dist/lib/anilist.js +26 -1458
- package/server/dist/lib/anischedule.js +1 -191
- package/server/dist/lib/insights.js +1 -185
- package/server/dist/lib/ipc.js +1 -23
- package/server/dist/lib/kitsu.js +1 -517
- package/server/dist/lib/mal-import.js +1 -291
- package/server/dist/lib/mal.js +1 -957
- package/server/dist/lib/migration.js +1 -310
- package/server/dist/lib/offline-db.js +4 -384
- package/server/dist/lib/potoken.js +1 -135
- package/server/dist/lib/recommendations.service.js +1 -216
- package/server/dist/lib/temp-ids.js +1 -3
- package/server/dist/lib/tmdb.js +1 -72
- package/server/dist/lib/tracker/anilist-tracker.js +8 -245
- package/server/dist/lib/tracker/manga-sync.service.js +1 -773
- package/server/dist/lib/tracker/sync-state.js +1 -21
- package/server/dist/lib/tracker/sync.service.js +1 -476
- package/server/dist/lib/translate.js +3 -79
- package/server/dist/lib/ump.js +1 -106
- package/server/dist/lib/ytmusic.js +1 -162
- package/server/dist/logger.js +1 -16
- package/server/dist/providers/manga/manga.types.js +0 -1
- package/server/dist/providers/provider.interface.js +0 -1
- package/server/dist/providers/remote-loader.js +2 -614
- package/server/dist/providers/remote-types.js +1 -159
- package/server/dist/providers/title-matching.js +1 -226
- package/server/dist/providers/tv.types.js +0 -1
- package/server/dist/rclone.js +2 -120
- package/server/dist/repositories/asmr.repository.js +11 -46
- package/server/dist/repositories/insights.repository.js +11 -25
- package/server/dist/repositories/library.repository.js +3 -94
- package/server/dist/repositories/mal-cache.repository.js +1 -52
- package/server/dist/repositories/manga.repository.js +14 -58
- package/server/dist/repositories/notifications.repository.js +6 -52
- package/server/dist/repositories/queue.repository.js +5 -58
- package/server/dist/repositories/settings.repository.js +2 -29
- package/server/dist/repositories/shows-meta.repository.js +3 -56
- package/server/dist/repositories/temp-show-ids.repository.js +3 -63
- package/server/dist/repositories/tv.repository.js +11 -54
- package/server/dist/repositories/watched-episodes.repository.js +6 -55
- package/server/dist/repositories/watchlist.repository.js +3 -54
- package/server/dist/scripts/backfill-episode-duration.js +1 -42
- package/server/dist/server.js +1 -310
- package/server/dist/shipped-defaults.js +1 -6
- package/server/dist/sync-payload.js +1 -91
- package/server/dist/sync.js +3 -647
- package/server/dist/trpc/context.js +1 -7
- package/server/dist/trpc/errors.js +1 -15
- package/server/dist/trpc/index.js +1 -18
- package/server/dist/trpc/router.js +1 -49
- package/server/dist/trpc/routers/asmr-library.js +1 -112
- package/server/dist/trpc/routers/asmr-progress.js +1 -121
- package/server/dist/trpc/routers/asmr.js +1 -87
- package/server/dist/trpc/routers/auth.js +1 -194
- package/server/dist/trpc/routers/continue-watching.js +2 -58
- package/server/dist/trpc/routers/data.js +1 -1026
- package/server/dist/trpc/routers/discord.js +1 -285
- package/server/dist/trpc/routers/discovery.js +1 -11
- package/server/dist/trpc/routers/insights.js +1 -14
- package/server/dist/trpc/routers/manga-library.js +1 -263
- package/server/dist/trpc/routers/manga-progress.js +1 -169
- package/server/dist/trpc/routers/manga.js +1 -201
- package/server/dist/trpc/routers/music.js +1 -655
- package/server/dist/trpc/routers/notifications.js +1 -80
- package/server/dist/trpc/routers/progress.js +1 -109
- package/server/dist/trpc/routers/queue.js +1 -93
- package/server/dist/trpc/routers/radio.js +1 -145
- package/server/dist/trpc/routers/settings.js +1 -96
- package/server/dist/trpc/routers/tracker.js +1 -213
- package/server/dist/trpc/routers/tv-library.js +1 -135
- package/server/dist/trpc/routers/tv-progress.js +1 -159
- package/server/dist/trpc/routers/tv.js +1 -551
- package/server/dist/trpc/routers/watchlist.js +2 -549
- package/server/dist/trpc/validation.js +1 -258
- package/server/dist/utils/cache.utils.js +1 -25
- package/server/dist/utils/cookie.utils.js +1 -13
- package/server/dist/utils/db-utils.js +5 -17
- package/server/dist/utils/env.utils.js +5 -77
- package/server/dist/utils/http.utils.js +1 -82
- package/server/dist/utils/request-context.js +1 -2
- package/server/dist/utils/security.utils.js +1 -76
- package/server/package.json +3 -2
|
@@ -1,7 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
export function buildTrpcContext(deps, authorization, cookie) {
|
|
3
|
-
return {
|
|
4
|
-
...deps,
|
|
5
|
-
lanAuthed: !hasAppPassword() || validateLanSession(getTokenFromHeaders(authorization, cookie)),
|
|
6
|
-
};
|
|
7
|
-
}
|
|
1
|
+
import{getTokenFromHeaders as t,hasAppPassword as o,validateLanSession as p}from"../app-auth.js";function n(e,r,a){return{...e,lanAuthed:!o()||p(t(r,a))}}export{n as buildTrpcContext};
|
|
@@ -1,15 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
export function authRequiredError(provider) {
|
|
3
|
-
return new TRPCError({
|
|
4
|
-
code: 'FORBIDDEN',
|
|
5
|
-
message: 'AUTH_REQUIRED',
|
|
6
|
-
cause: { code: 'AUTH_REQUIRED', provider },
|
|
7
|
-
});
|
|
8
|
-
}
|
|
9
|
-
export function lanAuthRequiredError() {
|
|
10
|
-
return new TRPCError({
|
|
11
|
-
code: 'UNAUTHORIZED',
|
|
12
|
-
message: 'LAN_AUTH_REQUIRED',
|
|
13
|
-
cause: { code: 'LAN_AUTH_REQUIRED' },
|
|
14
|
-
});
|
|
15
|
-
}
|
|
1
|
+
import{TRPCError as r}from"@trpc/server";function E(e){return new r({code:"FORBIDDEN",message:"AUTH_REQUIRED",cause:{code:"AUTH_REQUIRED",provider:e}})}function R(){return new r({code:"UNAUTHORIZED",message:"LAN_AUTH_REQUIRED",cause:{code:"LAN_AUTH_REQUIRED"}})}export{E as authRequiredError,R as lanAuthRequiredError};
|
|
@@ -1,18 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { lanAuthRequiredError } from './errors.js';
|
|
3
|
-
const t = initTRPC.context().create({
|
|
4
|
-
errorFormatter: ({ shape, error }) => ({
|
|
5
|
-
...shape,
|
|
6
|
-
data: {
|
|
7
|
-
...shape.data,
|
|
8
|
-
cause: error.cause ?? null,
|
|
9
|
-
},
|
|
10
|
-
}),
|
|
11
|
-
});
|
|
12
|
-
export const router = t.router;
|
|
13
|
-
const lanGuard = t.middleware(({ ctx, next }) => {
|
|
14
|
-
if (!ctx.lanAuthed)
|
|
15
|
-
throw lanAuthRequiredError();
|
|
16
|
-
return next({ ctx });
|
|
17
|
-
});
|
|
18
|
-
export const protectedProcedure = t.procedure.use(lanGuard);
|
|
1
|
+
import{initTRPC as o}from"@trpc/server";import{lanAuthRequiredError as n}from"./errors.js";const e=o.context().create({errorFormatter:({shape:r,error:t})=>({...r,data:{...r.data,cause:t.cause??null}})}),d=e.router,c=e.middleware(({ctx:r,next:t})=>{if(!r.lanAuthed)throw n();return t({ctx:r})}),p=e.procedure.use(c);export{p as protectedProcedure,d as router};
|
|
@@ -1,49 +1 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { settingsRouter } from './routers/settings.js';
|
|
3
|
-
import { queueRouter } from './routers/queue.js';
|
|
4
|
-
import { watchlistRouter } from './routers/watchlist.js';
|
|
5
|
-
import { progressRouter } from './routers/progress.js';
|
|
6
|
-
import { notificationsRouter } from './routers/notifications.js';
|
|
7
|
-
import { continueWatchingRouter } from './routers/continue-watching.js';
|
|
8
|
-
import { discoveryRouter } from './routers/discovery.js';
|
|
9
|
-
import { mangaLibraryRouter } from './routers/manga-library.js';
|
|
10
|
-
import { mangaProgressRouter } from './routers/manga-progress.js';
|
|
11
|
-
import { asmrLibraryRouter } from './routers/asmr-library.js';
|
|
12
|
-
import { asmrProgressRouter } from './routers/asmr-progress.js';
|
|
13
|
-
import { tvLibraryRouter } from './routers/tv-library.js';
|
|
14
|
-
import { tvProgressRouter } from './routers/tv-progress.js';
|
|
15
|
-
import { tvRouter } from './routers/tv.js';
|
|
16
|
-
import { mangaRouter } from './routers/manga.js';
|
|
17
|
-
import { asmrRouter } from './routers/asmr.js';
|
|
18
|
-
import { musicRouter } from './routers/music.js';
|
|
19
|
-
import { radioRouter } from './routers/radio.js';
|
|
20
|
-
import { trackerRouter } from './routers/tracker.js';
|
|
21
|
-
import { insightsRouter } from './routers/insights.js';
|
|
22
|
-
import { authRouter } from './routers/auth.js';
|
|
23
|
-
import { dataRouter } from './routers/data.js';
|
|
24
|
-
import { discordRouter } from './routers/discord.js';
|
|
25
|
-
export const appRouter = router({
|
|
26
|
-
settings: settingsRouter,
|
|
27
|
-
queue: queueRouter,
|
|
28
|
-
watchlist: watchlistRouter,
|
|
29
|
-
progress: progressRouter,
|
|
30
|
-
notifications: notificationsRouter,
|
|
31
|
-
continueWatching: continueWatchingRouter,
|
|
32
|
-
discovery: discoveryRouter,
|
|
33
|
-
mangaLibrary: mangaLibraryRouter,
|
|
34
|
-
mangaProgress: mangaProgressRouter,
|
|
35
|
-
asmrLibrary: asmrLibraryRouter,
|
|
36
|
-
asmrProgress: asmrProgressRouter,
|
|
37
|
-
tvLibrary: tvLibraryRouter,
|
|
38
|
-
tvProgress: tvProgressRouter,
|
|
39
|
-
tv: tvRouter,
|
|
40
|
-
manga: mangaRouter,
|
|
41
|
-
asmr: asmrRouter,
|
|
42
|
-
music: musicRouter,
|
|
43
|
-
radio: radioRouter,
|
|
44
|
-
tracker: trackerRouter,
|
|
45
|
-
insights: insightsRouter,
|
|
46
|
-
auth: authRouter,
|
|
47
|
-
data: dataRouter,
|
|
48
|
-
discord: discordRouter,
|
|
49
|
-
});
|
|
1
|
+
import{router as r}from"./index.js";import{settingsRouter as o}from"./routers/settings.js";import{queueRouter as t}from"./routers/queue.js";import{watchlistRouter as m}from"./routers/watchlist.js";import{progressRouter as i}from"./routers/progress.js";import{notificationsRouter as e}from"./routers/notifications.js";import{continueWatchingRouter as a}from"./routers/continue-watching.js";import{discoveryRouter as s}from"./routers/discovery.js";import{mangaLibraryRouter as u}from"./routers/manga-library.js";import{mangaProgressRouter as p}from"./routers/manga-progress.js";import{asmrLibraryRouter as f}from"./routers/asmr-library.js";import{asmrProgressRouter as R}from"./routers/asmr-progress.js";import{tvLibraryRouter as n}from"./routers/tv-library.js";import{tvProgressRouter as g}from"./routers/tv-progress.js";import{tvRouter as c}from"./routers/tv.js";import{mangaRouter as d}from"./routers/manga.js";import{asmrRouter as y}from"./routers/asmr.js";import{musicRouter as h}from"./routers/music.js";import{radioRouter as v}from"./routers/radio.js";import{trackerRouter as b}from"./routers/tracker.js";import{insightsRouter as L}from"./routers/insights.js";import{authRouter as P}from"./routers/auth.js";import{dataRouter as k}from"./routers/data.js";import{discordRouter as l}from"./routers/discord.js";const V=r({settings:o,queue:t,watchlist:m,progress:i,notifications:e,continueWatching:a,discovery:s,mangaLibrary:u,mangaProgress:p,asmrLibrary:f,asmrProgress:R,tvLibrary:n,tvProgress:g,tv:c,manga:d,asmr:y,music:h,radio:v,tracker:b,insights:L,auth:P,data:k,discord:l});export{V as appRouter};
|
|
@@ -1,112 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { protectedProcedure, router } from '../index.js';
|
|
3
|
-
import { performAsmrWriteTransactionAsync } from '../../sync.js';
|
|
4
|
-
import { buildAsmrId, ASMR_STATUSES, AsmrLibraryRepository, AsmrProgressRepository, } from '../../repositories/asmr.repository.js';
|
|
5
|
-
import { defineSchema, reqObj, reqStr, optStr, reqStrArray, readPageLimit } from '../validation.js';
|
|
6
|
-
const asmrLibraryListInput = () => defineSchema((value) => {
|
|
7
|
-
const obj = reqObj(value);
|
|
8
|
-
const out = { ...readPageLimit(obj) };
|
|
9
|
-
const status = optStr(obj, 'status');
|
|
10
|
-
if (status !== undefined)
|
|
11
|
-
out.status = status;
|
|
12
|
-
return out;
|
|
13
|
-
});
|
|
14
|
-
const asmrLibraryIdInput = () => defineSchema((value) => ({
|
|
15
|
-
id: reqStr(reqObj(value), 'id'),
|
|
16
|
-
}));
|
|
17
|
-
const asmrLibraryAddInput = () => defineSchema((value) => {
|
|
18
|
-
const obj = reqObj(value);
|
|
19
|
-
return {
|
|
20
|
-
rjCode: optStr(obj, 'rjCode'),
|
|
21
|
-
id: optStr(obj, 'id'),
|
|
22
|
-
title: optStr(obj, 'title'),
|
|
23
|
-
thumbnail: optStr(obj, 'thumbnail'),
|
|
24
|
-
status: optStr(obj, 'status'),
|
|
25
|
-
isAdult: obj.isAdult,
|
|
26
|
-
};
|
|
27
|
-
});
|
|
28
|
-
const asmrLibraryStatusInput = () => defineSchema((value) => {
|
|
29
|
-
const obj = reqObj(value);
|
|
30
|
-
return { id: reqStr(obj, 'id'), status: reqStr(obj, 'status') };
|
|
31
|
-
});
|
|
32
|
-
const asmrLibraryBatchStatusInput = () => defineSchema((value) => {
|
|
33
|
-
const obj = reqObj(value);
|
|
34
|
-
return { ids: reqStrArray(obj, 'ids', true), status: reqStr(obj, 'status') };
|
|
35
|
-
});
|
|
36
|
-
const asmrLibraryIdsInput = () => defineSchema((value) => ({
|
|
37
|
-
ids: reqStrArray(reqObj(value), 'ids', true),
|
|
38
|
-
}));
|
|
39
|
-
export const asmrLibraryRouter = router({
|
|
40
|
-
list: protectedProcedure.input(asmrLibraryListInput()).query(async ({ ctx, input }) => {
|
|
41
|
-
const page = Math.max(parseInt(String(input.page ?? 1)) || 1, 1);
|
|
42
|
-
const limit = Math.min(Math.max(parseInt(String(input.limit ?? 24)) || 24, 1), 100);
|
|
43
|
-
const offset = (page - 1) * limit;
|
|
44
|
-
const rows = await AsmrLibraryRepository.getAll(ctx.asmrDb, input.status, limit, offset);
|
|
45
|
-
const total = await AsmrLibraryRepository.getCount(ctx.asmrDb, input.status);
|
|
46
|
-
return { data: rows, total, page, limit };
|
|
47
|
-
}),
|
|
48
|
-
ids: protectedProcedure.query(async ({ ctx }) => {
|
|
49
|
-
return { ids: await AsmrLibraryRepository.getIds(ctx.asmrDb) };
|
|
50
|
-
}),
|
|
51
|
-
check: protectedProcedure.input(asmrLibraryIdInput()).query(async ({ ctx, input }) => {
|
|
52
|
-
const item = await AsmrLibraryRepository.getById(ctx.asmrDb, input.id);
|
|
53
|
-
return { inLibrary: !!item, status: item?.status ?? null };
|
|
54
|
-
}),
|
|
55
|
-
add: protectedProcedure.input(asmrLibraryAddInput()).mutation(async ({ ctx, input }) => {
|
|
56
|
-
const rawCode = String(input.rjCode || input.id || '').trim();
|
|
57
|
-
if (!rawCode) {
|
|
58
|
-
throw new TRPCError({ code: 'BAD_REQUEST', message: 'rjCode is required' });
|
|
59
|
-
}
|
|
60
|
-
const id = input.id && input.id.trim() ? buildAsmrId(input.id) : buildAsmrId(rawCode);
|
|
61
|
-
const cleanStatus = typeof input.status === 'string' && ASMR_STATUSES.includes(input.status)
|
|
62
|
-
? input.status
|
|
63
|
-
: 'Listening';
|
|
64
|
-
await performAsmrWriteTransactionAsync(ctx.asmrDb, async (tx) => {
|
|
65
|
-
await AsmrLibraryRepository.upsert(tx, {
|
|
66
|
-
id,
|
|
67
|
-
rjCode: buildAsmrId(rawCode),
|
|
68
|
-
title: String(input.title || ''),
|
|
69
|
-
thumbnail: String(input.thumbnail || ''),
|
|
70
|
-
status: cleanStatus,
|
|
71
|
-
isAdult: input.isAdult === true || input.isAdult === 1,
|
|
72
|
-
});
|
|
73
|
-
});
|
|
74
|
-
return { success: true, id };
|
|
75
|
-
}),
|
|
76
|
-
remove: protectedProcedure.input(asmrLibraryIdInput()).mutation(async ({ ctx, input }) => {
|
|
77
|
-
await performAsmrWriteTransactionAsync(ctx.asmrDb, async (tx) => {
|
|
78
|
-
await AsmrLibraryRepository.delete(tx, String(input.id));
|
|
79
|
-
await AsmrProgressRepository.deleteByWork(tx, String(input.id));
|
|
80
|
-
});
|
|
81
|
-
return { success: true };
|
|
82
|
-
}),
|
|
83
|
-
setStatus: protectedProcedure.input(asmrLibraryStatusInput()).mutation(async ({ ctx, input }) => {
|
|
84
|
-
if (!input.id || !ASMR_STATUSES.includes(input.status)) {
|
|
85
|
-
throw new TRPCError({ code: 'BAD_REQUEST', message: 'id and a valid status are required' });
|
|
86
|
-
}
|
|
87
|
-
await performAsmrWriteTransactionAsync(ctx.asmrDb, async (tx) => {
|
|
88
|
-
await AsmrLibraryRepository.updateStatus(tx, String(input.id), String(input.status));
|
|
89
|
-
});
|
|
90
|
-
return { success: true };
|
|
91
|
-
}),
|
|
92
|
-
batchStatus: protectedProcedure
|
|
93
|
-
.input(asmrLibraryBatchStatusInput())
|
|
94
|
-
.mutation(async ({ ctx, input }) => {
|
|
95
|
-
if (!input.status || !ASMR_STATUSES.includes(input.status)) {
|
|
96
|
-
throw new TRPCError({ code: 'BAD_REQUEST', message: 'a valid status is required' });
|
|
97
|
-
}
|
|
98
|
-
const ids = input.ids.map((id) => String(id));
|
|
99
|
-
await performAsmrWriteTransactionAsync(ctx.asmrDb, async (tx) => {
|
|
100
|
-
await AsmrLibraryRepository.updateStatusMany(tx, ids, String(input.status));
|
|
101
|
-
});
|
|
102
|
-
return { success: true, updated: ids.length };
|
|
103
|
-
}),
|
|
104
|
-
removeMany: protectedProcedure.input(asmrLibraryIdsInput()).mutation(async ({ ctx, input }) => {
|
|
105
|
-
const ids = input.ids.map((id) => String(id));
|
|
106
|
-
await performAsmrWriteTransactionAsync(ctx.asmrDb, async (tx) => {
|
|
107
|
-
await AsmrLibraryRepository.deleteMany(tx, ids);
|
|
108
|
-
await AsmrProgressRepository.deleteMany(tx, ids);
|
|
109
|
-
});
|
|
110
|
-
return { success: true, removed: ids.length };
|
|
111
|
-
}),
|
|
112
|
-
});
|
|
1
|
+
import{TRPCError as b}from"@trpc/server";import{protectedProcedure as e,router as L}from"../index.js";import{performAsmrWriteTransactionAsync as o}from"../../sync.js";import{buildAsmrId as p,ASMR_STATUSES as A,AsmrLibraryRepository as i,AsmrProgressRepository as g}from"../../repositories/asmr.repository.js";import{defineSchema as n,reqObj as u,reqStr as m,optStr as d,reqStrArray as l,readPageLimit as w}from"../validation.js";const h=()=>n(r=>{const t=u(r),s={...w(t)},a=d(t,"status");return a!==void 0&&(s.status=a),s}),I=()=>n(r=>({id:m(u(r),"id")})),f=()=>n(r=>{const t=u(r);return{rjCode:d(t,"rjCode"),id:d(t,"id"),title:d(t,"title"),thumbnail:d(t,"thumbnail"),status:d(t,"status"),isAdult:t.isAdult}}),D=()=>n(r=>{const t=u(r);return{id:m(t,"id"),status:m(t,"status")}}),j=()=>n(r=>{const t=u(r);return{ids:l(t,"ids",!0),status:m(t,"status")}}),q=()=>n(r=>({ids:l(u(r),"ids",!0)})),T=L({list:e.input(h()).query(async({ctx:r,input:t})=>{const s=Math.max(parseInt(String(t.page??1))||1,1),a=Math.min(Math.max(parseInt(String(t.limit??24))||24,1),100),c=(s-1)*a,y=await i.getAll(r.asmrDb,t.status,a,c),S=await i.getCount(r.asmrDb,t.status);return{data:y,total:S,page:s,limit:a}}),ids:e.query(async({ctx:r})=>({ids:await i.getIds(r.asmrDb)})),check:e.input(I()).query(async({ctx:r,input:t})=>{const s=await i.getById(r.asmrDb,t.id);return{inLibrary:!!s,status:s?.status??null}}),add:e.input(f()).mutation(async({ctx:r,input:t})=>{const s=String(t.rjCode||t.id||"").trim();if(!s)throw new b({code:"BAD_REQUEST",message:"rjCode is required"});const a=t.id&&t.id.trim()?p(t.id):p(s),c=typeof t.status=="string"&&A.includes(t.status)?t.status:"Listening";return await o(r.asmrDb,async y=>{await i.upsert(y,{id:a,rjCode:p(s),title:String(t.title||""),thumbnail:String(t.thumbnail||""),status:c,isAdult:t.isAdult===!0||t.isAdult===1})}),{success:!0,id:a}}),remove:e.input(I()).mutation(async({ctx:r,input:t})=>(await o(r.asmrDb,async s=>{await i.delete(s,String(t.id)),await g.deleteByWork(s,String(t.id))}),{success:!0})),setStatus:e.input(D()).mutation(async({ctx:r,input:t})=>{if(!t.id||!A.includes(t.status))throw new b({code:"BAD_REQUEST",message:"id and a valid status are required"});return await o(r.asmrDb,async s=>{await i.updateStatus(s,String(t.id),String(t.status))}),{success:!0}}),batchStatus:e.input(j()).mutation(async({ctx:r,input:t})=>{if(!t.status||!A.includes(t.status))throw new b({code:"BAD_REQUEST",message:"a valid status is required"});const s=t.ids.map(a=>String(a));return await o(r.asmrDb,async a=>{await i.updateStatusMany(a,s,String(t.status))}),{success:!0,updated:s.length}}),removeMany:e.input(q()).mutation(async({ctx:r,input:t})=>{const s=t.ids.map(a=>String(a));return await o(r.asmrDb,async a=>{await i.deleteMany(a,s),await g.deleteMany(a,s)}),{success:!0,removed:s.length}})});export{T as asmrLibraryRouter};
|
|
@@ -1,121 +1 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { performAsmrWriteTransactionAsync } from '../../sync.js';
|
|
3
|
-
import { adultNonListIds } from '../../utils/db-utils.js';
|
|
4
|
-
import { SettingsRepository } from '../../repositories/settings.repository.js';
|
|
5
|
-
import { AsmrLibraryRepository, AsmrProgressRepository, } from '../../repositories/asmr.repository.js';
|
|
6
|
-
import { defineSchema, reqObj, reqStr, reqStrArray, strOrNull } from '../validation.js';
|
|
7
|
-
const getAdultNonListWorkIds = (db) => adultNonListIds(db, 'asmr_progress', 'asmr_library', 'workId', 'COALESCE(l.isAdult, p.isAdult) = 1');
|
|
8
|
-
const asmrProgressIdsInput = () => defineSchema((value) => ({
|
|
9
|
-
ids: reqStrArray(reqObj(value), 'ids', true),
|
|
10
|
-
}));
|
|
11
|
-
const asmrProgressWorkInput = () => defineSchema((value) => ({
|
|
12
|
-
workId: reqStr(reqObj(value), 'workId'),
|
|
13
|
-
}));
|
|
14
|
-
const asmrProgressSaveInput = () => defineSchema((value) => {
|
|
15
|
-
const obj = reqObj(value);
|
|
16
|
-
return {
|
|
17
|
-
workId: reqStr(obj, 'workId'),
|
|
18
|
-
trackIndex: obj.trackIndex,
|
|
19
|
-
trackLabel: obj.trackLabel,
|
|
20
|
-
currentTime: obj.currentTime,
|
|
21
|
-
duration: obj.duration,
|
|
22
|
-
title: strOrNull(obj, 'title'),
|
|
23
|
-
thumbnail: strOrNull(obj, 'thumbnail'),
|
|
24
|
-
rjCode: strOrNull(obj, 'rjCode'),
|
|
25
|
-
isAdult: obj.isAdult ?? null,
|
|
26
|
-
};
|
|
27
|
-
});
|
|
28
|
-
const asmrProgressRemoveInput = () => defineSchema((value) => {
|
|
29
|
-
const obj = reqObj(value);
|
|
30
|
-
return { workId: reqStr(obj, 'workId'), trackIndex: obj.trackIndex ?? undefined };
|
|
31
|
-
});
|
|
32
|
-
const asmrContinueListeningInput = () => defineSchema((value) => {
|
|
33
|
-
const obj = reqObj(value);
|
|
34
|
-
const out = {};
|
|
35
|
-
if (obj.limit !== undefined && obj.limit !== null && obj.limit !== '') {
|
|
36
|
-
const limit = Number(obj.limit);
|
|
37
|
-
if (Number.isFinite(limit))
|
|
38
|
-
out.limit = limit;
|
|
39
|
-
}
|
|
40
|
-
return out;
|
|
41
|
-
});
|
|
42
|
-
export const asmrProgressRouter = router({
|
|
43
|
-
getByWork: protectedProcedure.input(asmrProgressWorkInput()).query(async ({ ctx, input }) => {
|
|
44
|
-
const rows = await AsmrProgressRepository.getByWork(ctx.asmrDb, input.workId);
|
|
45
|
-
return { progress: rows };
|
|
46
|
-
}),
|
|
47
|
-
latest: protectedProcedure.input(asmrProgressWorkInput()).query(async ({ ctx, input }) => {
|
|
48
|
-
const row = await AsmrProgressRepository.getLatest(ctx.asmrDb, input.workId);
|
|
49
|
-
return row || { currentTime: 0, duration: 0 };
|
|
50
|
-
}),
|
|
51
|
-
save: protectedProcedure.input(asmrProgressSaveInput()).mutation(async ({ ctx, input }) => {
|
|
52
|
-
const trackIndexNum = Math.max(Math.round(Number(input.trackIndex)) || 0, 0);
|
|
53
|
-
const timeNum = Math.max(Number(input.currentTime) || 0, 0);
|
|
54
|
-
const durationNum = Math.max(Number(input.duration) || 0, 0);
|
|
55
|
-
await performAsmrWriteTransactionAsync(ctx.asmrDb, async (tx) => {
|
|
56
|
-
await AsmrProgressRepository.upsert(tx, {
|
|
57
|
-
workId: String(input.workId),
|
|
58
|
-
trackIndex: trackIndexNum,
|
|
59
|
-
trackLabel: String(input.trackLabel || ''),
|
|
60
|
-
currentTime: timeNum,
|
|
61
|
-
duration: durationNum,
|
|
62
|
-
title: input.title ?? null,
|
|
63
|
-
thumbnail: input.thumbnail ?? null,
|
|
64
|
-
rjCode: input.rjCode ?? null,
|
|
65
|
-
isAdult: input.isAdult != null ? Number(input.isAdult) : null,
|
|
66
|
-
});
|
|
67
|
-
await AsmrLibraryRepository.touchProgress(tx, String(input.workId), {
|
|
68
|
-
trackIndex: trackIndexNum,
|
|
69
|
-
trackLabel: String(input.trackLabel || ''),
|
|
70
|
-
position: timeNum,
|
|
71
|
-
});
|
|
72
|
-
});
|
|
73
|
-
return { success: true };
|
|
74
|
-
}),
|
|
75
|
-
remove: protectedProcedure.input(asmrProgressRemoveInput()).mutation(async ({ ctx, input }) => {
|
|
76
|
-
await performAsmrWriteTransactionAsync(ctx.asmrDb, async (tx) => {
|
|
77
|
-
if (input.trackIndex !== undefined && input.trackIndex !== null) {
|
|
78
|
-
await AsmrProgressRepository.deleteTrack(tx, String(input.workId), Number(input.trackIndex));
|
|
79
|
-
}
|
|
80
|
-
else {
|
|
81
|
-
await AsmrProgressRepository.deleteByWork(tx, String(input.workId));
|
|
82
|
-
}
|
|
83
|
-
});
|
|
84
|
-
return { success: true };
|
|
85
|
-
}),
|
|
86
|
-
removeMany: protectedProcedure.input(asmrProgressIdsInput()).mutation(async ({ ctx, input }) => {
|
|
87
|
-
const ids = input.ids.map((id) => String(id));
|
|
88
|
-
await performAsmrWriteTransactionAsync(ctx.asmrDb, async (tx) => {
|
|
89
|
-
await AsmrProgressRepository.deleteMany(tx, ids);
|
|
90
|
-
});
|
|
91
|
-
return { success: true, removed: ids.length };
|
|
92
|
-
}),
|
|
93
|
-
continueListening: protectedProcedure
|
|
94
|
-
.input(asmrContinueListeningInput())
|
|
95
|
-
.query(async ({ ctx, input }) => {
|
|
96
|
-
const limit = Math.min(Math.max(parseInt(String(input.limit ?? 24)) || 24, 1), 100);
|
|
97
|
-
const { ignoreAdult, listOnly } = await SettingsRepository.readContinueFlags(ctx.db, 'asmrIgnoreAdultContent', 'asmrCwWatchlistOnly');
|
|
98
|
-
const rows = (await AsmrProgressRepository.getContinueListening(ctx.asmrDb, limit)).filter((row) => {
|
|
99
|
-
if (ignoreAdult && row.isAdult === 1)
|
|
100
|
-
return false;
|
|
101
|
-
if (listOnly && row.watchlistStatus !== 'Listening')
|
|
102
|
-
return false;
|
|
103
|
-
return true;
|
|
104
|
-
});
|
|
105
|
-
return { data: rows, total: rows.length };
|
|
106
|
-
}),
|
|
107
|
-
adultCount: protectedProcedure.query(async ({ ctx }) => {
|
|
108
|
-
const ids = await getAdultNonListWorkIds(ctx.asmrDb);
|
|
109
|
-
return { count: ids.length };
|
|
110
|
-
}),
|
|
111
|
-
purgeAdult: protectedProcedure.mutation(async ({ ctx }) => {
|
|
112
|
-
const ids = await getAdultNonListWorkIds(ctx.asmrDb);
|
|
113
|
-
if (ids.length > 0) {
|
|
114
|
-
await performAsmrWriteTransactionAsync(ctx.asmrDb, async (tx) => {
|
|
115
|
-
for (const id of ids)
|
|
116
|
-
await AsmrProgressRepository.deleteByWork(tx, id);
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
return { success: true, removed: ids.length };
|
|
120
|
-
}),
|
|
121
|
-
});
|
|
1
|
+
import{protectedProcedure as s,router as p}from"../index.js";import{performAsmrWriteTransactionAsync as m}from"../../sync.js";import{adultNonListIds as w}from"../../utils/db-utils.js";import{SettingsRepository as b}from"../../repositories/settings.repository.js";import{AsmrLibraryRepository as y,AsmrProgressRepository as o}from"../../repositories/asmr.repository.js";import{defineSchema as i,reqObj as u,reqStr as l,reqStrArray as A,strOrNull as c}from"../validation.js";const I=t=>w(t,"asmr_progress","asmr_library","workId","COALESCE(l.isAdult, p.isAdult) = 1"),f=()=>i(t=>({ids:A(u(t),"ids",!0)})),k=()=>i(t=>({workId:l(u(t),"workId")})),h=()=>i(t=>{const r=u(t);return{workId:l(r,"workId"),trackIndex:r.trackIndex,trackLabel:r.trackLabel,currentTime:r.currentTime,duration:r.duration,title:c(r,"title"),thumbnail:c(r,"thumbnail"),rjCode:c(r,"rjCode"),isAdult:r.isAdult??null}}),L=()=>i(t=>{const r=u(t);return{workId:l(r,"workId"),trackIndex:r.trackIndex??void 0}}),P=()=>i(t=>{const r=u(t),e={};if(r.limit!==void 0&&r.limit!==null&&r.limit!==""){const n=Number(r.limit);Number.isFinite(n)&&(e.limit=n)}return e}),D=p({getByWork:s.input(k()).query(async({ctx:t,input:r})=>({progress:await o.getByWork(t.asmrDb,r.workId)})),latest:s.input(k()).query(async({ctx:t,input:r})=>await o.getLatest(t.asmrDb,r.workId)||{currentTime:0,duration:0}),save:s.input(h()).mutation(async({ctx:t,input:r})=>{const e=Math.max(Math.round(Number(r.trackIndex))||0,0),n=Math.max(Number(r.currentTime)||0,0),d=Math.max(Number(r.duration)||0,0);return await m(t.asmrDb,async a=>{await o.upsert(a,{workId:String(r.workId),trackIndex:e,trackLabel:String(r.trackLabel||""),currentTime:n,duration:d,title:r.title??null,thumbnail:r.thumbnail??null,rjCode:r.rjCode??null,isAdult:r.isAdult!=null?Number(r.isAdult):null}),await y.touchProgress(a,String(r.workId),{trackIndex:e,trackLabel:String(r.trackLabel||""),position:n})}),{success:!0}}),remove:s.input(L()).mutation(async({ctx:t,input:r})=>(await m(t.asmrDb,async e=>{r.trackIndex!==void 0&&r.trackIndex!==null?await o.deleteTrack(e,String(r.workId),Number(r.trackIndex)):await o.deleteByWork(e,String(r.workId))}),{success:!0})),removeMany:s.input(f()).mutation(async({ctx:t,input:r})=>{const e=r.ids.map(n=>String(n));return await m(t.asmrDb,async n=>{await o.deleteMany(n,e)}),{success:!0,removed:e.length}}),continueListening:s.input(P()).query(async({ctx:t,input:r})=>{const e=Math.min(Math.max(parseInt(String(r.limit??24))||24,1),100),{ignoreAdult:n,listOnly:d}=await b.readContinueFlags(t.db,"asmrIgnoreAdultContent","asmrCwWatchlistOnly"),a=(await o.getContinueListening(t.asmrDb,e)).filter(g=>!(n&&g.isAdult===1||d&&g.watchlistStatus!=="Listening"));return{data:a,total:a.length}}),adultCount:s.query(async({ctx:t})=>({count:(await I(t.asmrDb)).length})),purgeAdult:s.mutation(async({ctx:t})=>{const r=await I(t.asmrDb);return r.length>0&&await m(t.asmrDb,async e=>{for(const n of r)await o.deleteByWork(e,n)}),{success:!0,removed:r.length}})});export{D as asmrProgressRouter};
|
|
@@ -1,87 +1 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { defineSchema, optStr, reqObj, reqStr } from '../validation.js';
|
|
3
|
-
import { authRequiredError } from '../errors.js';
|
|
4
|
-
import logger from '../../logger.js';
|
|
5
|
-
async function cached(ctx, key, ttl, produce) {
|
|
6
|
-
const hit = ctx.apiCache.get(key);
|
|
7
|
-
if (hit)
|
|
8
|
-
return hit;
|
|
9
|
-
const body = await produce();
|
|
10
|
-
if (ttl !== undefined)
|
|
11
|
-
ctx.apiCache.set(key, body, ttl);
|
|
12
|
-
else
|
|
13
|
-
ctx.apiCache.set(key, body);
|
|
14
|
-
return body;
|
|
15
|
-
}
|
|
16
|
-
const asmrBrowseInput = () => defineSchema((value) => {
|
|
17
|
-
if (value === undefined || value === null)
|
|
18
|
-
return {};
|
|
19
|
-
const obj = reqObj(value);
|
|
20
|
-
const out = {};
|
|
21
|
-
const q = optStr(obj, 'q');
|
|
22
|
-
if (q !== undefined)
|
|
23
|
-
out.q = q;
|
|
24
|
-
if (obj.page !== undefined)
|
|
25
|
-
out.page = parseInt(String(obj.page), 10) || 1;
|
|
26
|
-
const sort = optStr(obj, 'sort');
|
|
27
|
-
if (sort !== undefined)
|
|
28
|
-
out.sort = sort;
|
|
29
|
-
const rating = optStr(obj, 'rating');
|
|
30
|
-
if (rating !== undefined)
|
|
31
|
-
out.rating = rating;
|
|
32
|
-
return out;
|
|
33
|
-
});
|
|
34
|
-
const asmrWorkInput = () => defineSchema((value) => ({ rj: reqStr(reqObj(value), 'rj') }));
|
|
35
|
-
export const asmrRouter = router({
|
|
36
|
-
browse: protectedProcedure.input(asmrBrowseInput()).query(async ({ ctx, input }) => {
|
|
37
|
-
const q = input.q || '';
|
|
38
|
-
const page = input.page || 1;
|
|
39
|
-
const sort = input.sort || '';
|
|
40
|
-
const rating = input.rating || '';
|
|
41
|
-
return cached(ctx, `route-asmr-browse-${q}-${page}-${sort}-${rating}-${sort === 'random' ? Date.now() : ''}`, 300, async () => {
|
|
42
|
-
try {
|
|
43
|
-
const provider = ctx.getJasmr();
|
|
44
|
-
if (!provider)
|
|
45
|
-
return { shows: [], hasNext: false };
|
|
46
|
-
return await provider.browse({ query: q, page, sort, rating });
|
|
47
|
-
}
|
|
48
|
-
catch (err) {
|
|
49
|
-
if (err.message === 'AUTH_REQUIRED')
|
|
50
|
-
throw authRequiredError('jasmr');
|
|
51
|
-
logger.error({ err }, '[Asmr] browse failed');
|
|
52
|
-
return { shows: [], hasNext: false };
|
|
53
|
-
}
|
|
54
|
-
});
|
|
55
|
-
}),
|
|
56
|
-
work: protectedProcedure.input(asmrWorkInput()).query(async ({ ctx, input }) => {
|
|
57
|
-
const rj = input.rj;
|
|
58
|
-
return cached(ctx, `route-asmr-work-${rj}`, 1800, async () => {
|
|
59
|
-
try {
|
|
60
|
-
const provider = ctx.getJasmr();
|
|
61
|
-
if (!provider) {
|
|
62
|
-
return { rjCode: rj, description: '', tracks: [], images: [], chapters: [] };
|
|
63
|
-
}
|
|
64
|
-
const rjCode = String(rj).trim().toUpperCase();
|
|
65
|
-
const episodes = await provider.getEpisodes(rjCode);
|
|
66
|
-
const [streams, images, chapters] = await Promise.all([
|
|
67
|
-
provider.getStreamUrls(rjCode, '1'),
|
|
68
|
-
provider.getImages(rjCode),
|
|
69
|
-
provider.getChapters(rjCode),
|
|
70
|
-
]);
|
|
71
|
-
return {
|
|
72
|
-
rjCode,
|
|
73
|
-
description: episodes?.description || '',
|
|
74
|
-
tracks: streams?.[0]?.links || [],
|
|
75
|
-
images,
|
|
76
|
-
chapters,
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
|
-
catch (err) {
|
|
80
|
-
if (err.message === 'AUTH_REQUIRED')
|
|
81
|
-
throw authRequiredError('jasmr');
|
|
82
|
-
logger.error({ err, rj }, '[Asmr] work fetch failed');
|
|
83
|
-
return { rjCode: rj, description: '', tracks: [], images: [], chapters: [] };
|
|
84
|
-
}
|
|
85
|
-
});
|
|
86
|
-
}),
|
|
87
|
-
});
|
|
1
|
+
import{protectedProcedure as c,router as h}from"../index.js";import{defineSchema as p,optStr as i,reqObj as m,reqStr as j}from"../validation.js";import{authRequiredError as u}from"../errors.js";import d from"../../logger.js";async function g(s,e,r,t){const o=s.apiCache.get(e);if(o)return o;const n=await t();return r!==void 0?s.apiCache.set(e,n,r):s.apiCache.set(e,n),n}const I=()=>p(s=>{if(s==null)return{};const e=m(s),r={},t=i(e,"q");t!==void 0&&(r.q=t),e.page!==void 0&&(r.page=parseInt(String(e.page),10)||1);const o=i(e,"sort");o!==void 0&&(r.sort=o);const n=i(e,"rating");return n!==void 0&&(r.rating=n),r}),q=()=>p(s=>({rj:j(m(s),"rj")})),k=h({browse:c.input(I()).query(async({ctx:s,input:e})=>{const r=e.q||"",t=e.page||1,o=e.sort||"",n=e.rating||"";return g(s,`route-asmr-browse-${r}-${t}-${o}-${n}-${o==="random"?Date.now():""}`,300,async()=>{try{const a=s.getJasmr();return a?await a.browse({query:r,page:t,sort:o,rating:n}):{shows:[],hasNext:!1}}catch(a){if(a.message==="AUTH_REQUIRED")throw u("jasmr");return d.error({err:a},"[Asmr] browse failed"),{shows:[],hasNext:!1}}})}),work:c.input(q()).query(async({ctx:s,input:e})=>{const r=e.rj;return g(s,`route-asmr-work-${r}`,1800,async()=>{try{const t=s.getJasmr();if(!t)return{rjCode:r,description:"",tracks:[],images:[],chapters:[]};const o=String(r).trim().toUpperCase(),n=await t.getEpisodes(o),[a,f,w]=await Promise.all([t.getStreamUrls(o,"1"),t.getImages(o),t.getChapters(o)]);return{rjCode:o,description:n?.description||"",tracks:a?.[0]?.links||[],images:f,chapters:w}}catch(t){if(t.message==="AUTH_REQUIRED")throw u("jasmr");return d.error({err:t,rj:r},"[Asmr] work fetch failed"),{rjCode:r,description:"",tracks:[],images:[],chapters:[]}}})})});export{k as asmrRouter};
|
|
@@ -1,194 +1 @@
|
|
|
1
|
-
import logger from
|
|
2
|
-
import { TRPCError } from '@trpc/server';
|
|
3
|
-
import { googleDriveService } from '../../google.js';
|
|
4
|
-
import { githubSyncService } from '../../github-sync.js';
|
|
5
|
-
import { initSyncProvider, getActiveProvider, runFullSyncSequence } from '../../sync.js';
|
|
6
|
-
import { CONFIG } from '../../config.js';
|
|
7
|
-
import { rcloneService } from '../../rclone.js';
|
|
8
|
-
import { updateEnvFile } from '../../utils/env.utils.js';
|
|
9
|
-
import { protectedProcedure, router } from '../index.js';
|
|
10
|
-
import { defineSchema, reqObj } from '../validation.js';
|
|
11
|
-
async function runSyncFromCtx(ctx, database, preferredProvider) {
|
|
12
|
-
const remoteFolder = CONFIG.IS_DEV ? CONFIG.REMOTE_FOLDER_DEV : CONFIG.REMOTE_FOLDER_PROD;
|
|
13
|
-
await runFullSyncSequence({ db: database, mangaDb: ctx.mangaDb, tvDb: ctx.tvDb, asmrDb: ctx.asmrDb }, { remoteFolder, preferredProvider });
|
|
14
|
-
}
|
|
15
|
-
const googleAuthSaveInput = () => defineSchema((value) => {
|
|
16
|
-
const obj = reqObj(value);
|
|
17
|
-
return { clientId: obj.clientId, clientSecret: obj.clientSecret, workerUrl: obj.workerUrl };
|
|
18
|
-
});
|
|
19
|
-
const githubAuthSaveInput = () => defineSchema((value) => {
|
|
20
|
-
const obj = reqObj(value);
|
|
21
|
-
if (typeof obj.clientId !== 'string')
|
|
22
|
-
throw new Error('clientId required');
|
|
23
|
-
return { clientId: obj.clientId };
|
|
24
|
-
});
|
|
25
|
-
const syncProviderInput = () => defineSchema((value) => ({
|
|
26
|
-
provider: reqObj(value).provider,
|
|
27
|
-
}));
|
|
28
|
-
const rcloneSaveInput = () => defineSchema((value) => ({
|
|
29
|
-
remote: reqObj(value).remote,
|
|
30
|
-
}));
|
|
31
|
-
const serverPortInput = () => defineSchema((value) => ({
|
|
32
|
-
port: reqObj(value).port,
|
|
33
|
-
}));
|
|
34
|
-
export const authRouter = router({
|
|
35
|
-
configStatus: protectedProcedure.query(async () => {
|
|
36
|
-
const useWorker = !!CONFIG.GOOGLE_AUTH_WORKER_URL;
|
|
37
|
-
const hasLegacyConfig = !!process.env.GOOGLE_CLIENT_ID && !!process.env.GOOGLE_CLIENT_SECRET;
|
|
38
|
-
return { hasConfig: useWorker || hasLegacyConfig, useWorker };
|
|
39
|
-
}),
|
|
40
|
-
googleAuth: protectedProcedure.query(async () => {
|
|
41
|
-
return {
|
|
42
|
-
useWorker: !!CONFIG.GOOGLE_AUTH_WORKER_URL,
|
|
43
|
-
hasCustomWorkerUrl: !!process.env.GOOGLE_AUTH_WORKER_URL,
|
|
44
|
-
hasCustomClientId: !!process.env.GOOGLE_CLIENT_ID,
|
|
45
|
-
hasClientSecret: !!process.env.GOOGLE_CLIENT_SECRET,
|
|
46
|
-
};
|
|
47
|
-
}),
|
|
48
|
-
saveGoogleAuth: protectedProcedure.input(googleAuthSaveInput()).mutation(async ({ input }) => {
|
|
49
|
-
const { clientId, clientSecret, workerUrl } = input;
|
|
50
|
-
const updates = {};
|
|
51
|
-
if (typeof clientId === 'string') {
|
|
52
|
-
updates.GOOGLE_CLIENT_ID = clientId;
|
|
53
|
-
}
|
|
54
|
-
if (typeof clientSecret === 'string') {
|
|
55
|
-
updates.GOOGLE_CLIENT_SECRET = clientSecret;
|
|
56
|
-
}
|
|
57
|
-
if (typeof workerUrl === 'string') {
|
|
58
|
-
updates.GOOGLE_AUTH_WORKER_URL = workerUrl;
|
|
59
|
-
}
|
|
60
|
-
await updateEnvFile(updates);
|
|
61
|
-
if (typeof workerUrl === 'string') {
|
|
62
|
-
;
|
|
63
|
-
CONFIG.GOOGLE_AUTH_WORKER_URL = workerUrl;
|
|
64
|
-
}
|
|
65
|
-
if (typeof clientId === 'string') {
|
|
66
|
-
;
|
|
67
|
-
CONFIG.GOOGLE_CLIENT_ID = clientId || undefined;
|
|
68
|
-
}
|
|
69
|
-
if (typeof clientSecret === 'string') {
|
|
70
|
-
;
|
|
71
|
-
CONFIG.GOOGLE_CLIENT_SECRET =
|
|
72
|
-
clientSecret || undefined;
|
|
73
|
-
}
|
|
74
|
-
await initSyncProvider();
|
|
75
|
-
return { success: true };
|
|
76
|
-
}),
|
|
77
|
-
githubAuth: protectedProcedure.query(async () => {
|
|
78
|
-
return { hasCustomClientId: !!process.env.GITHUB_CLIENT_ID };
|
|
79
|
-
}),
|
|
80
|
-
saveGithubAuth: protectedProcedure.input(githubAuthSaveInput()).mutation(async ({ input }) => {
|
|
81
|
-
await updateEnvFile({ GITHUB_CLIENT_ID: input.clientId });
|
|
82
|
-
return { success: true };
|
|
83
|
-
}),
|
|
84
|
-
rcloneSettings: protectedProcedure.query(async () => {
|
|
85
|
-
const remotes = await rcloneService.listRemotes();
|
|
86
|
-
return {
|
|
87
|
-
remote: CONFIG.RCLONE_REMOTE || '',
|
|
88
|
-
availableRemotes: remotes,
|
|
89
|
-
activeRemote: rcloneService.isActive() ? rcloneService.getRemoteName() : null,
|
|
90
|
-
};
|
|
91
|
-
}),
|
|
92
|
-
syncSettings: protectedProcedure.query(async () => {
|
|
93
|
-
return {
|
|
94
|
-
activeProvider: process.env.SYNC_PROVIDER || 'default',
|
|
95
|
-
actualActiveProvider: getActiveProvider(),
|
|
96
|
-
authenticatedProviders: {
|
|
97
|
-
github: githubSyncService.isAuthenticated(),
|
|
98
|
-
google: googleDriveService.isAuthenticated(),
|
|
99
|
-
rclone: rcloneService.isActive(),
|
|
100
|
-
},
|
|
101
|
-
};
|
|
102
|
-
}),
|
|
103
|
-
saveSyncProvider: protectedProcedure.input(syncProviderInput()).mutation(async ({ input }) => {
|
|
104
|
-
const { provider } = input;
|
|
105
|
-
const value = provider === 'default' ? '' : provider;
|
|
106
|
-
await updateEnvFile({ SYNC_PROVIDER: value });
|
|
107
|
-
await initSyncProvider();
|
|
108
|
-
return { success: true, activeProvider: process.env.SYNC_PROVIDER || 'default' };
|
|
109
|
-
}),
|
|
110
|
-
githubStatus: protectedProcedure.query(async () => {
|
|
111
|
-
try {
|
|
112
|
-
const user = await githubSyncService.getUserProfile();
|
|
113
|
-
return {
|
|
114
|
-
authenticated: !!user,
|
|
115
|
-
user,
|
|
116
|
-
device: githubSyncService.getDeviceState(),
|
|
117
|
-
hasCustomClientId: !!process.env.GITHUB_CLIENT_ID,
|
|
118
|
-
};
|
|
119
|
-
}
|
|
120
|
-
catch (error) {
|
|
121
|
-
logger.error({ err: error }, 'Failed to fetch GitHub auth status');
|
|
122
|
-
return {
|
|
123
|
-
authenticated: false,
|
|
124
|
-
user: null,
|
|
125
|
-
device: githubSyncService.getDeviceState(),
|
|
126
|
-
hasCustomClientId: !!process.env.GITHUB_CLIENT_ID,
|
|
127
|
-
};
|
|
128
|
-
}
|
|
129
|
-
}),
|
|
130
|
-
githubStart: protectedProcedure.mutation(async ({ ctx }) => {
|
|
131
|
-
const state = await githubSyncService.startDeviceAuth(ctx.db, (db, provider) => runSyncFromCtx(ctx, db, provider));
|
|
132
|
-
return state;
|
|
133
|
-
}),
|
|
134
|
-
githubPoll: protectedProcedure.query(async () => {
|
|
135
|
-
return githubSyncService.getDeviceState();
|
|
136
|
-
}),
|
|
137
|
-
githubLogout: protectedProcedure.mutation(async () => {
|
|
138
|
-
await githubSyncService.logout();
|
|
139
|
-
await updateEnvFile({ SYNC_PROVIDER: '' });
|
|
140
|
-
await initSyncProvider();
|
|
141
|
-
return { success: true };
|
|
142
|
-
}),
|
|
143
|
-
saveRclone: protectedProcedure.input(rcloneSaveInput()).mutation(async ({ ctx, input }) => {
|
|
144
|
-
const { remote } = input;
|
|
145
|
-
await updateEnvFile({
|
|
146
|
-
RCLONE_REMOTE: remote,
|
|
147
|
-
SYNC_PROVIDER: 'rclone',
|
|
148
|
-
});
|
|
149
|
-
await runSyncFromCtx(ctx, ctx.db, 'rclone');
|
|
150
|
-
return { success: true };
|
|
151
|
-
}),
|
|
152
|
-
setServerPort: protectedProcedure.input(serverPortInput()).mutation(async ({ input }) => {
|
|
153
|
-
const port = Number(input.port);
|
|
154
|
-
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
|
155
|
-
throw new TRPCError({
|
|
156
|
-
code: 'BAD_REQUEST',
|
|
157
|
-
message: 'Port must be an integer between 1 and 65535',
|
|
158
|
-
});
|
|
159
|
-
}
|
|
160
|
-
await updateEnvFile({ PORT: String(port) });
|
|
161
|
-
logger.info({ port }, 'Server port saved. Restart required to apply.');
|
|
162
|
-
return { success: true, port };
|
|
163
|
-
}),
|
|
164
|
-
googleAuthUrl: protectedProcedure.query(async () => {
|
|
165
|
-
const url = await googleDriveService.getAuthUrl();
|
|
166
|
-
return { url };
|
|
167
|
-
}),
|
|
168
|
-
googleLogin: protectedProcedure.mutation(async ({ ctx }) => {
|
|
169
|
-
if (googleDriveService.isAuthenticated()) {
|
|
170
|
-
const user = await googleDriveService.getUserProfile();
|
|
171
|
-
if (user) {
|
|
172
|
-
await updateEnvFile({ SYNC_PROVIDER: 'google' });
|
|
173
|
-
await runSyncFromCtx(ctx, ctx.db, 'google');
|
|
174
|
-
return { url: null, authenticated: true };
|
|
175
|
-
}
|
|
176
|
-
else {
|
|
177
|
-
logger.warn('Google tokens found but invalid. Clearing and requesting new auth.');
|
|
178
|
-
await googleDriveService.logout();
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
const url = await googleDriveService.getAuthUrl();
|
|
182
|
-
return { url, authenticated: false };
|
|
183
|
-
}),
|
|
184
|
-
user: protectedProcedure.query(async () => {
|
|
185
|
-
const user = await googleDriveService.getUserProfile();
|
|
186
|
-
return user;
|
|
187
|
-
}),
|
|
188
|
-
logout: protectedProcedure.mutation(async () => {
|
|
189
|
-
await googleDriveService.logout();
|
|
190
|
-
await updateEnvFile({ SYNC_PROVIDER: '' });
|
|
191
|
-
await initSyncProvider();
|
|
192
|
-
return { success: true };
|
|
193
|
-
}),
|
|
194
|
-
});
|
|
1
|
+
import p from"../../logger.js";import{TRPCError as _}from"@trpc/server";import{googleDriveService as s}from"../../google.js";import{githubSyncService as u}from"../../github-sync.js";import{initSyncProvider as E,getActiveProvider as v,runFullSyncSequence as I}from"../../sync.js";import{CONFIG as o}from"../../config.js";import{rcloneService as d}from"../../rclone.js";import{updateEnvFile as a}from"../../utils/env.utils.js";import{protectedProcedure as r,router as O}from"../index.js";import{defineSchema as c,reqObj as l}from"../validation.js";async function m(e,t,n){const i=o.IS_DEV?o.REMOTE_FOLDER_DEV:o.REMOTE_FOLDER_PROD;await I({db:t,mangaDb:e.mangaDb,tvDb:e.tvDb,asmrDb:e.asmrDb},{remoteFolder:i,preferredProvider:n})}const R=()=>c(e=>{const t=l(e);return{clientId:t.clientId,clientSecret:t.clientSecret,workerUrl:t.workerUrl}}),y=()=>c(e=>{const t=l(e);if(typeof t.clientId!="string")throw new Error("clientId required");return{clientId:t.clientId}}),w=()=>c(e=>({provider:l(e).provider})),f=()=>c(e=>({remote:l(e).remote})),C=()=>c(e=>({port:l(e).port})),A=O({configStatus:r.query(async()=>{const e=!!o.GOOGLE_AUTH_WORKER_URL,t=!!process.env.GOOGLE_CLIENT_ID&&!!process.env.GOOGLE_CLIENT_SECRET;return{hasConfig:e||t,useWorker:e}}),googleAuth:r.query(async()=>({useWorker:!!o.GOOGLE_AUTH_WORKER_URL,hasCustomWorkerUrl:!!process.env.GOOGLE_AUTH_WORKER_URL,hasCustomClientId:!!process.env.GOOGLE_CLIENT_ID,hasClientSecret:!!process.env.GOOGLE_CLIENT_SECRET})),saveGoogleAuth:r.input(R()).mutation(async({input:e})=>{const{clientId:t,clientSecret:n,workerUrl:i}=e,g={};return typeof t=="string"&&(g.GOOGLE_CLIENT_ID=t),typeof n=="string"&&(g.GOOGLE_CLIENT_SECRET=n),typeof i=="string"&&(g.GOOGLE_AUTH_WORKER_URL=i),await a(g),typeof i=="string"&&(o.GOOGLE_AUTH_WORKER_URL=i),typeof t=="string"&&(o.GOOGLE_CLIENT_ID=t||void 0),typeof n=="string"&&(o.GOOGLE_CLIENT_SECRET=n||void 0),await E(),{success:!0}}),githubAuth:r.query(async()=>({hasCustomClientId:!!process.env.GITHUB_CLIENT_ID})),saveGithubAuth:r.input(y()).mutation(async({input:e})=>(await a({GITHUB_CLIENT_ID:e.clientId}),{success:!0})),rcloneSettings:r.query(async()=>{const e=await d.listRemotes();return{remote:o.RCLONE_REMOTE||"",availableRemotes:e,activeRemote:d.isActive()?d.getRemoteName():null}}),syncSettings:r.query(async()=>({activeProvider:process.env.SYNC_PROVIDER||"default",actualActiveProvider:v(),authenticatedProviders:{github:u.isAuthenticated(),google:s.isAuthenticated(),rclone:d.isActive()}})),saveSyncProvider:r.input(w()).mutation(async({input:e})=>{const{provider:t}=e;return await a({SYNC_PROVIDER:t==="default"?"":t}),await E(),{success:!0,activeProvider:process.env.SYNC_PROVIDER||"default"}}),githubStatus:r.query(async()=>{try{const e=await u.getUserProfile();return{authenticated:!!e,user:e,device:u.getDeviceState(),hasCustomClientId:!!process.env.GITHUB_CLIENT_ID}}catch(e){return p.error({err:e},"Failed to fetch GitHub auth status"),{authenticated:!1,user:null,device:u.getDeviceState(),hasCustomClientId:!!process.env.GITHUB_CLIENT_ID}}}),githubStart:r.mutation(async({ctx:e})=>await u.startDeviceAuth(e.db,(n,i)=>m(e,n,i))),githubPoll:r.query(async()=>u.getDeviceState()),githubLogout:r.mutation(async()=>(await u.logout(),await a({SYNC_PROVIDER:""}),await E(),{success:!0})),saveRclone:r.input(f()).mutation(async({ctx:e,input:t})=>{const{remote:n}=t;return await a({RCLONE_REMOTE:n,SYNC_PROVIDER:"rclone"}),await m(e,e.db,"rclone"),{success:!0}}),setServerPort:r.input(C()).mutation(async({input:e})=>{const t=Number(e.port);if(!Number.isInteger(t)||t<1||t>65535)throw new _({code:"BAD_REQUEST",message:"Port must be an integer between 1 and 65535"});return await a({PORT:String(t)}),p.info({port:t},"Server port saved. Restart required to apply."),{success:!0,port:t}}),googleAuthUrl:r.query(async()=>({url:await s.getAuthUrl()})),googleLogin:r.mutation(async({ctx:e})=>{if(s.isAuthenticated()){if(await s.getUserProfile())return await a({SYNC_PROVIDER:"google"}),await m(e,e.db,"google"),{url:null,authenticated:!0};p.warn("Google tokens found but invalid. Clearing and requesting new auth."),await s.logout()}return{url:await s.getAuthUrl(),authenticated:!1}}),user:r.query(async()=>await s.getUserProfile()),logout:r.mutation(async()=>(await s.logout(),await a({SYNC_PROVIDER:""}),await E(),{success:!0}))});export{A as authRouter};
|