@digitalpaws/starr 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (290) hide show
  1. package/README.rst +75 -0
  2. package/data/.nfs.811bbb0d.43c34.4 +0 -0
  3. package/data/.nfs.81ba4cd0.ffff87804.4 +0 -0
  4. package/data/.nfs.82d127f7.fffff5704.4 +0 -0
  5. package/data/.nfs.82d127fe.fffff5704.4 +0 -0
  6. package/data/.nfs.82d159a5.fffff5aa4.4 +0 -0
  7. package/data/.nfs.82d159aa.fffff5aa4.4 +0 -0
  8. package/data/.nfs.82d15a9c.fffff5bb4.4 +0 -0
  9. package/data/.nfs.82d15aa1.fffff5bb4.4 +0 -0
  10. package/data/.nfs.82d15c65.fffff5c24.4 +0 -0
  11. package/data/.nfs.82d1852c.fffff5e24.4 +0 -0
  12. package/data/.nfs.82d18536.fffff5e24.4 +0 -0
  13. package/data/.nfs.82d18dbe.fffff5e44.4 +0 -0
  14. package/data/.nfs.82d18dc6.fffff5e44.4 +0 -0
  15. package/data/.nfs.8309a3f5.fffff82f4.4 +0 -0
  16. package/data/.nfs.8309a3fe.fffff82f4.4 +0 -0
  17. package/data/.nfs.8314122e.fffffb044.4 +0 -0
  18. package/data/.nfs.83141236.fffffb044.4 +0 -0
  19. package/data/.nfs.833c4fe9.fffffbc34.4 +0 -0
  20. package/data/.nfs.833c4ff3.fffffbc34.4 +0 -0
  21. package/data/logs/starr.log +66244 -0
  22. package/data/starr.db +0 -0
  23. package/data/starr.db-shm +0 -0
  24. package/data/starr.db-wal +0 -0
  25. package/data/starr.jwt +1 -0
  26. package/data/starr.key +1 -0
  27. package/dist/auth/auth-service.d.ts +42 -0
  28. package/dist/auth/auth-service.d.ts.map +1 -0
  29. package/dist/auth/auth-service.js +226 -0
  30. package/dist/auth/auth-service.js.map +1 -0
  31. package/dist/auth/http-server.d.ts +16 -0
  32. package/dist/auth/http-server.d.ts.map +1 -0
  33. package/dist/auth/http-server.js +207 -0
  34. package/dist/auth/http-server.js.map +1 -0
  35. package/dist/auth/jwt-secret.d.ts +6 -0
  36. package/dist/auth/jwt-secret.d.ts.map +1 -0
  37. package/dist/auth/jwt-secret.js +51 -0
  38. package/dist/auth/jwt-secret.js.map +1 -0
  39. package/dist/auth/oidc/oidc-flow.d.ts +60 -0
  40. package/dist/auth/oidc/oidc-flow.d.ts.map +1 -0
  41. package/dist/auth/oidc/oidc-flow.js +201 -0
  42. package/dist/auth/oidc/oidc-flow.js.map +1 -0
  43. package/dist/auth/oidc/oidc-provider-service.d.ts +59 -0
  44. package/dist/auth/oidc/oidc-provider-service.d.ts.map +1 -0
  45. package/dist/auth/oidc/oidc-provider-service.js +124 -0
  46. package/dist/auth/oidc/oidc-provider-service.js.map +1 -0
  47. package/dist/auth/oidc/user-service.d.ts +67 -0
  48. package/dist/auth/oidc/user-service.d.ts.map +1 -0
  49. package/dist/auth/oidc/user-service.js +177 -0
  50. package/dist/auth/oidc/user-service.js.map +1 -0
  51. package/dist/auth/rate-limiter.d.ts +31 -0
  52. package/dist/auth/rate-limiter.d.ts.map +1 -0
  53. package/dist/auth/rate-limiter.js +70 -0
  54. package/dist/auth/rate-limiter.js.map +1 -0
  55. package/dist/config/config-service.d.ts +83 -0
  56. package/dist/config/config-service.d.ts.map +1 -0
  57. package/dist/config/config-service.js +309 -0
  58. package/dist/config/config-service.js.map +1 -0
  59. package/dist/config/validate.d.ts +12 -0
  60. package/dist/config/validate.d.ts.map +1 -0
  61. package/dist/config/validate.js +140 -0
  62. package/dist/config/validate.js.map +1 -0
  63. package/dist/content-types/registry.d.ts +75 -0
  64. package/dist/content-types/registry.d.ts.map +1 -0
  65. package/dist/content-types/registry.js +171 -0
  66. package/dist/content-types/registry.js.map +1 -0
  67. package/dist/datastore/db.d.ts +9 -0
  68. package/dist/datastore/db.d.ts.map +1 -0
  69. package/dist/datastore/db.js +27 -0
  70. package/dist/datastore/db.js.map +1 -0
  71. package/dist/datastore/encrypt.d.ts +9 -0
  72. package/dist/datastore/encrypt.d.ts.map +1 -0
  73. package/dist/datastore/encrypt.js +84 -0
  74. package/dist/datastore/encrypt.js.map +1 -0
  75. package/dist/datastore/migrations.d.ts +10 -0
  76. package/dist/datastore/migrations.d.ts.map +1 -0
  77. package/dist/datastore/migrations.js +340 -0
  78. package/dist/datastore/migrations.js.map +1 -0
  79. package/dist/datastore/storage.d.ts +4 -0
  80. package/dist/datastore/storage.d.ts.map +1 -0
  81. package/dist/datastore/storage.js +60 -0
  82. package/dist/datastore/storage.js.map +1 -0
  83. package/dist/events/event-bus.d.ts +15 -0
  84. package/dist/events/event-bus.d.ts.map +1 -0
  85. package/dist/events/event-bus.js +140 -0
  86. package/dist/events/event-bus.js.map +1 -0
  87. package/dist/events/spine.d.ts +31 -0
  88. package/dist/events/spine.d.ts.map +1 -0
  89. package/dist/events/spine.js +68 -0
  90. package/dist/events/spine.js.map +1 -0
  91. package/dist/events/trace.d.ts +24 -0
  92. package/dist/events/trace.d.ts.map +1 -0
  93. package/dist/events/trace.js +33 -0
  94. package/dist/events/trace.js.map +1 -0
  95. package/dist/host.d.ts +65 -0
  96. package/dist/host.d.ts.map +1 -0
  97. package/dist/host.js +94 -0
  98. package/dist/host.js.map +1 -0
  99. package/dist/library/importer.d.ts +29 -0
  100. package/dist/library/importer.d.ts.map +1 -0
  101. package/dist/library/importer.js +145 -0
  102. package/dist/library/importer.js.map +1 -0
  103. package/dist/library/library-cache.d.ts +46 -0
  104. package/dist/library/library-cache.d.ts.map +1 -0
  105. package/dist/library/library-cache.js +152 -0
  106. package/dist/library/library-cache.js.map +1 -0
  107. package/dist/library/library-config.d.ts +12 -0
  108. package/dist/library/library-config.d.ts.map +1 -0
  109. package/dist/library/library-config.js +10 -0
  110. package/dist/library/library-config.js.map +1 -0
  111. package/dist/library/library-scanner.d.ts +62 -0
  112. package/dist/library/library-scanner.d.ts.map +1 -0
  113. package/dist/library/library-scanner.js +336 -0
  114. package/dist/library/library-scanner.js.map +1 -0
  115. package/dist/library/library-service.d.ts +65 -0
  116. package/dist/library/library-service.d.ts.map +1 -0
  117. package/dist/library/library-service.js +197 -0
  118. package/dist/library/library-service.js.map +1 -0
  119. package/dist/library/metadata-matcher.d.ts +56 -0
  120. package/dist/library/metadata-matcher.d.ts.map +1 -0
  121. package/dist/library/metadata-matcher.js +207 -0
  122. package/dist/library/metadata-matcher.js.map +1 -0
  123. package/dist/library/naming.d.ts +8 -0
  124. package/dist/library/naming.d.ts.map +1 -0
  125. package/dist/library/naming.js +47 -0
  126. package/dist/library/naming.js.map +1 -0
  127. package/dist/library/scan-controller.d.ts +95 -0
  128. package/dist/library/scan-controller.d.ts.map +1 -0
  129. package/dist/library/scan-controller.js +379 -0
  130. package/dist/library/scan-controller.js.map +1 -0
  131. package/dist/library/source-resolver.d.ts +17 -0
  132. package/dist/library/source-resolver.d.ts.map +1 -0
  133. package/dist/library/source-resolver.js +75 -0
  134. package/dist/library/source-resolver.js.map +1 -0
  135. package/dist/logging/logger.d.ts +3 -0
  136. package/dist/logging/logger.d.ts.map +1 -0
  137. package/dist/logging/logger.js +107 -0
  138. package/dist/logging/logger.js.map +1 -0
  139. package/dist/main.d.ts +36 -0
  140. package/dist/main.d.ts.map +1 -0
  141. package/dist/main.js +229 -0
  142. package/dist/main.js.map +1 -0
  143. package/dist/plugins/context.d.ts +10 -0
  144. package/dist/plugins/context.d.ts.map +1 -0
  145. package/dist/plugins/context.js +110 -0
  146. package/dist/plugins/context.js.map +1 -0
  147. package/dist/plugins/discovery.d.ts +2 -0
  148. package/dist/plugins/discovery.d.ts.map +1 -0
  149. package/dist/plugins/discovery.js +32 -0
  150. package/dist/plugins/discovery.js.map +1 -0
  151. package/dist/plugins/loader.d.ts +9 -0
  152. package/dist/plugins/loader.d.ts.map +1 -0
  153. package/dist/plugins/loader.js +245 -0
  154. package/dist/plugins/loader.js.map +1 -0
  155. package/dist/state/request-intake.d.ts +26 -0
  156. package/dist/state/request-intake.d.ts.map +1 -0
  157. package/dist/state/request-intake.js +74 -0
  158. package/dist/state/request-intake.js.map +1 -0
  159. package/dist/state/state-store.d.ts +146 -0
  160. package/dist/state/state-store.d.ts.map +1 -0
  161. package/dist/state/state-store.js +465 -0
  162. package/dist/state/state-store.js.map +1 -0
  163. package/dist/state/trace-store.d.ts +12 -0
  164. package/dist/state/trace-store.d.ts.map +1 -0
  165. package/dist/state/trace-store.js +24 -0
  166. package/dist/state/trace-store.js.map +1 -0
  167. package/dist/types/plugin.d.ts +93 -0
  168. package/dist/types/plugin.d.ts.map +1 -0
  169. package/dist/types/plugin.js +4 -0
  170. package/dist/types/plugin.js.map +1 -0
  171. package/dist/web/index.d.ts +10 -0
  172. package/dist/web/index.d.ts.map +1 -0
  173. package/dist/web/index.js +153 -0
  174. package/dist/web/index.js.map +1 -0
  175. package/dist/web/routes/activity.d.ts +6 -0
  176. package/dist/web/routes/activity.d.ts.map +1 -0
  177. package/dist/web/routes/activity.js +31 -0
  178. package/dist/web/routes/activity.js.map +1 -0
  179. package/dist/web/routes/auth.d.ts +6 -0
  180. package/dist/web/routes/auth.d.ts.map +1 -0
  181. package/dist/web/routes/auth.js +142 -0
  182. package/dist/web/routes/auth.js.map +1 -0
  183. package/dist/web/routes/config.d.ts +7 -0
  184. package/dist/web/routes/config.d.ts.map +1 -0
  185. package/dist/web/routes/config.js +204 -0
  186. package/dist/web/routes/config.js.map +1 -0
  187. package/dist/web/routes/content-types.d.ts +6 -0
  188. package/dist/web/routes/content-types.d.ts.map +1 -0
  189. package/dist/web/routes/content-types.js +29 -0
  190. package/dist/web/routes/content-types.js.map +1 -0
  191. package/dist/web/routes/discovery.d.ts +6 -0
  192. package/dist/web/routes/discovery.d.ts.map +1 -0
  193. package/dist/web/routes/discovery.js +111 -0
  194. package/dist/web/routes/discovery.js.map +1 -0
  195. package/dist/web/routes/library-import.d.ts +6 -0
  196. package/dist/web/routes/library-import.d.ts.map +1 -0
  197. package/dist/web/routes/library-import.js +120 -0
  198. package/dist/web/routes/library-import.js.map +1 -0
  199. package/dist/web/routes/library-management.d.ts +6 -0
  200. package/dist/web/routes/library-management.d.ts.map +1 -0
  201. package/dist/web/routes/library-management.js +120 -0
  202. package/dist/web/routes/library-management.js.map +1 -0
  203. package/dist/web/routes/library.d.ts +6 -0
  204. package/dist/web/routes/library.d.ts.map +1 -0
  205. package/dist/web/routes/library.js +28 -0
  206. package/dist/web/routes/library.js.map +1 -0
  207. package/dist/web/routes/oidc.d.ts +9 -0
  208. package/dist/web/routes/oidc.d.ts.map +1 -0
  209. package/dist/web/routes/oidc.js +203 -0
  210. package/dist/web/routes/oidc.js.map +1 -0
  211. package/dist/web/routes/requests.d.ts +6 -0
  212. package/dist/web/routes/requests.d.ts.map +1 -0
  213. package/dist/web/routes/requests.js +183 -0
  214. package/dist/web/routes/requests.js.map +1 -0
  215. package/dist/web/routes/sse.d.ts +6 -0
  216. package/dist/web/routes/sse.d.ts.map +1 -0
  217. package/dist/web/routes/sse.js +54 -0
  218. package/dist/web/routes/sse.js.map +1 -0
  219. package/package.json +33 -0
  220. package/package.json.license +2 -0
  221. package/src/auth/auth-service.ts +333 -0
  222. package/src/auth/http-server.ts +249 -0
  223. package/src/auth/jwt-secret.ts +65 -0
  224. package/src/auth/oidc/oidc-flow.ts +266 -0
  225. package/src/auth/oidc/oidc-provider-service.ts +174 -0
  226. package/src/auth/oidc/user-service.ts +227 -0
  227. package/src/auth/rate-limiter.ts +85 -0
  228. package/src/config/config-service.ts +434 -0
  229. package/src/config/validate.ts +153 -0
  230. package/src/content-types/registry.ts +194 -0
  231. package/src/datastore/db.ts +38 -0
  232. package/src/datastore/encrypt.ts +107 -0
  233. package/src/datastore/migrations.ts +367 -0
  234. package/src/datastore/storage.ts +83 -0
  235. package/src/events/event-bus.ts +180 -0
  236. package/src/events/spine.ts +87 -0
  237. package/src/events/trace.ts +38 -0
  238. package/src/host.ts +130 -0
  239. package/src/library/importer.ts +174 -0
  240. package/src/library/library-cache.ts +212 -0
  241. package/src/library/library-config.ts +22 -0
  242. package/src/library/library-scanner.ts +404 -0
  243. package/src/library/library-service.ts +282 -0
  244. package/src/library/metadata-matcher.ts +285 -0
  245. package/src/library/naming.ts +59 -0
  246. package/src/library/scan-controller.ts +490 -0
  247. package/src/library/source-resolver.ts +92 -0
  248. package/src/logging/logger.ts +115 -0
  249. package/src/main.ts +364 -0
  250. package/src/plugins/context.ts +138 -0
  251. package/src/plugins/discovery.ts +29 -0
  252. package/src/plugins/loader.ts +309 -0
  253. package/src/state/request-intake.ts +101 -0
  254. package/src/state/state-store.ts +621 -0
  255. package/src/state/trace-store.ts +27 -0
  256. package/src/types/plugin.ts +110 -0
  257. package/src/web/index.ts +182 -0
  258. package/src/web/routes/activity.ts +41 -0
  259. package/src/web/routes/auth.ts +170 -0
  260. package/src/web/routes/config.ts +282 -0
  261. package/src/web/routes/content-types.ts +42 -0
  262. package/src/web/routes/discovery.ts +156 -0
  263. package/src/web/routes/library-import.ts +196 -0
  264. package/src/web/routes/library-management.ts +140 -0
  265. package/src/web/routes/library.ts +46 -0
  266. package/src/web/routes/oidc.ts +260 -0
  267. package/src/web/routes/requests.ts +250 -0
  268. package/src/web/routes/sse.ts +92 -0
  269. package/tests/auth.test.ts +510 -0
  270. package/tests/config-route.test.ts +345 -0
  271. package/tests/config.test.ts +612 -0
  272. package/tests/content-types.test.ts +376 -0
  273. package/tests/datastore.test.ts +345 -0
  274. package/tests/e2e.test.ts +430 -0
  275. package/tests/event-bus.test.ts +925 -0
  276. package/tests/host.test.ts +449 -0
  277. package/tests/library-cache.test.ts +171 -0
  278. package/tests/library-scanner.test.ts +177 -0
  279. package/tests/library.test.ts +210 -0
  280. package/tests/loader-context.test.ts +644 -0
  281. package/tests/logger-jsonl.test.ts +154 -0
  282. package/tests/metadata-matcher.test.ts +198 -0
  283. package/tests/oidc.test.ts +218 -0
  284. package/tests/scan-controller.test.ts +328 -0
  285. package/tests/source-resolver.test.ts +100 -0
  286. package/tests/starr-video-integration.test.ts +153 -0
  287. package/tests/state-store.test.ts +700 -0
  288. package/tests/trace-history.test.ts +356 -0
  289. package/tsconfig.json +8 -0
  290. package/tsconfig.json.license +2 -0
@@ -0,0 +1,95 @@
1
+ import type Database from "better-sqlite3";
2
+ import type { EventBus, Logger } from "../types/plugin.js";
3
+ import type { LibraryScanCandidate } from "@digitalpaws/starr-types-core";
4
+ import { LibraryScanner } from "./library-scanner.js";
5
+ import { MetadataMatcher } from "./metadata-matcher.js";
6
+ import { LibraryCache } from "./library-cache.js";
7
+ import type { ContentTypeRegistryImpl } from "../content-types/registry.js";
8
+ import type { Importer } from "./importer.js";
9
+ /**
10
+ * In-memory scan state, keyed by scanId.
11
+ */
12
+ interface ScanState {
13
+ scanId: string;
14
+ status: "scanning" | "matching" | "complete" | "cancelled";
15
+ progress: {
16
+ total: number;
17
+ completed: number;
18
+ matched: number;
19
+ noMatch: number;
20
+ alreadyImported: number;
21
+ };
22
+ candidates: LibraryScanCandidate[];
23
+ }
24
+ export declare class ScanController {
25
+ private scanner;
26
+ private matcher;
27
+ private db;
28
+ private bus;
29
+ private logger;
30
+ private libraryCache;
31
+ private contentTypeRegistry;
32
+ private importer;
33
+ private concurrency;
34
+ constructor(scanner: LibraryScanner, matcher: MetadataMatcher, db: Database.Database, bus: EventBus, logger: Logger, libraryCache: LibraryCache, contentTypeRegistry: ContentTypeRegistryImpl, importer: Importer, concurrency?: number);
35
+ /**
36
+ * Start an async scan. Returns the scanId immediately.
37
+ * The scan runs in the background; progress is polled via getScan().
38
+ *
39
+ * @param libraryId Optional — scan only this library.
40
+ * @param contentTypeIds Optional — scan only roots for these content types.
41
+ */
42
+ startScan(libraryId?: string, contentTypeIds?: string[]): string;
43
+ /**
44
+ * Get the current state of a scan.
45
+ */
46
+ getScan(scanId: string): ScanState | undefined;
47
+ /**
48
+ * Cancel a running scan.
49
+ */
50
+ cancelScan(scanId: string): boolean;
51
+ /**
52
+ * Import accepted candidates into the library.
53
+ * Phase 1: Create tracked_request rows in a transaction (sync).
54
+ * Phase 2: Process each via the Importer (async) so library config
55
+ * (roots, link type, subtitles, naming) is applied correctly.
56
+ */
57
+ importCandidates(scanId: string, candidateIds: string[]): Promise<{
58
+ imported: number;
59
+ failed: number;
60
+ errors: Array<{
61
+ id: string;
62
+ error: string;
63
+ }>;
64
+ }>;
65
+ /**
66
+ * Remove a completed scan from memory.
67
+ */
68
+ cleanupScan(scanId: string): void;
69
+ private runScan;
70
+ /**
71
+ * Run a custom search for a specific candidate and update its matches.
72
+ * Called from the API when the user clicks "Search again".
73
+ */
74
+ searchCandidate(scanId: string, candidateId: string, query: string): Promise<boolean>;
75
+ /**
76
+ * Update the user's selected match index for a candidate.
77
+ */
78
+ selectMatch(scanId: string, candidateId: string, matchIndex: number): boolean;
79
+ private groupToCandidate;
80
+ private emitProgress;
81
+ /**
82
+ * Create a tracked_request row for a scanned candidate.
83
+ * Runs inside a transaction. Returns the data the Importer needs,
84
+ * or null if the candidate has no media files.
85
+ *
86
+ * Unlike the old importSingleCandidate, this does NOT:
87
+ * - Insert into library_paths (the Importer handles that)
88
+ * - Set library_path on tracked_requests (the Importer sets it)
89
+ * - Emit events (the Importer emits them)
90
+ */
91
+ private createTrackedRequest;
92
+ private pickPrimaryFile;
93
+ }
94
+ export {};
95
+ //# sourceMappingURL=scan-controller.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scan-controller.d.ts","sourceRoot":"","sources":["../../src/library/scan-controller.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAC3C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,KAAK,EAAE,oBAAoB,EAAmB,MAAM,+BAA+B,CAAC;AAC3F,OAAO,EAAE,cAAc,EAAwB,MAAM,sBAAsB,CAAC;AAC5E,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AAE5E,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAE9C;;GAEG;AACH,UAAU,SAAS;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,UAAU,GAAG,UAAU,GAAG,UAAU,GAAG,WAAW,CAAC;IAC3D,QAAQ,EAAE;QACR,KAAK,EAAE,MAAM,CAAC;QACd,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,EAAE,MAAM,CAAC;QAChB,eAAe,EAAE,MAAM,CAAC;KACzB,CAAC;IACF,UAAU,EAAE,oBAAoB,EAAE,CAAC;CACpC;AAmCD,qBAAa,cAAc;IACzB,OAAO,CAAC,OAAO,CAAiB;IAChC,OAAO,CAAC,OAAO,CAAkB;IACjC,OAAO,CAAC,EAAE,CAAoB;IAC9B,OAAO,CAAC,GAAG,CAAW;IACtB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,mBAAmB,CAA0B;IACrD,OAAO,CAAC,QAAQ,CAAW;IAC3B,OAAO,CAAC,WAAW,CAAS;gBAG1B,OAAO,EAAE,cAAc,EACvB,OAAO,EAAE,eAAe,EACxB,EAAE,EAAE,QAAQ,CAAC,QAAQ,EACrB,GAAG,EAAE,QAAQ,EACb,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,YAAY,EAC1B,mBAAmB,EAAE,uBAAuB,EAC5C,QAAQ,EAAE,QAAQ,EAClB,WAAW,SAAsB;IAanC;;;;;;OAMG;IACH,SAAS,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,cAAc,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM;IAmBhE;;OAEG;IACH,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS;IAI9C;;OAEG;IACH,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO;IAOnC;;;;;OAKG;IACG,gBAAgB,CACpB,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,EAAE,GACrB,OAAO,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,KAAK,CAAC;YAAE,EAAE,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAA;SAAE,CAAC,CAAA;KAAE,CAAC;IAmE9F;;OAEG;IACH,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;YAMnB,OAAO;IAiFrB;;;OAGG;IACG,eAAe,CACnB,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,OAAO,CAAC;IA4BnB;;OAEG;IACH,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO;IAW7E,OAAO,CAAC,gBAAgB;IAgBxB,OAAO,CAAC,YAAY;IAQpB;;;;;;;;;OASG;IACH,OAAO,CAAC,oBAAoB;IAkF5B,OAAO,CAAC,eAAe;CAiBxB"}
@@ -0,0 +1,379 @@
1
+ // SPDX-License-Identifier: 0BSD
2
+ // SPDX-FileCopyrightText: 2025 Digi <digitalpaws@proton.me>
3
+ import { randomUUID } from "node:crypto";
4
+ import path from "node:path";
5
+ import { computeContentIdentity } from "../content-types/registry.js";
6
+ const activeScans = new Map();
7
+ const DEFAULT_CONCURRENCY = 3;
8
+ /**
9
+ * Simple semaphore for concurrency control.
10
+ */
11
+ class Semaphore {
12
+ max;
13
+ current = 0;
14
+ queue = [];
15
+ constructor(max) {
16
+ this.max = max;
17
+ }
18
+ async acquire() {
19
+ if (this.current < this.max) {
20
+ this.current++;
21
+ return;
22
+ }
23
+ return new Promise((resolve) => {
24
+ this.queue.push(resolve);
25
+ });
26
+ }
27
+ release() {
28
+ if (this.queue.length > 0) {
29
+ const next = this.queue.shift();
30
+ next();
31
+ }
32
+ else {
33
+ this.current--;
34
+ }
35
+ }
36
+ }
37
+ export class ScanController {
38
+ scanner;
39
+ matcher;
40
+ db;
41
+ bus;
42
+ logger;
43
+ libraryCache;
44
+ contentTypeRegistry;
45
+ importer;
46
+ concurrency;
47
+ constructor(scanner, matcher, db, bus, logger, libraryCache, contentTypeRegistry, importer, concurrency = DEFAULT_CONCURRENCY) {
48
+ this.scanner = scanner;
49
+ this.matcher = matcher;
50
+ this.db = db;
51
+ this.bus = bus;
52
+ this.logger = logger.child("scan-controller");
53
+ this.libraryCache = libraryCache;
54
+ this.contentTypeRegistry = contentTypeRegistry;
55
+ this.importer = importer;
56
+ this.concurrency = concurrency;
57
+ }
58
+ /**
59
+ * Start an async scan. Returns the scanId immediately.
60
+ * The scan runs in the background; progress is polled via getScan().
61
+ *
62
+ * @param libraryId Optional — scan only this library.
63
+ * @param contentTypeIds Optional — scan only roots for these content types.
64
+ */
65
+ startScan(libraryId, contentTypeIds) {
66
+ const scanId = randomUUID();
67
+ const state = {
68
+ scanId,
69
+ status: "scanning",
70
+ progress: { total: 0, completed: 0, matched: 0, noMatch: 0, alreadyImported: 0 },
71
+ candidates: [],
72
+ };
73
+ activeScans.set(scanId, state);
74
+ // Run scan in background (not awaited)
75
+ this.runScan(scanId, state, libraryId, contentTypeIds).catch((err) => {
76
+ this.logger.error("scan failed", { scanId, error: String(err) });
77
+ state.status = "complete";
78
+ });
79
+ return scanId;
80
+ }
81
+ /**
82
+ * Get the current state of a scan.
83
+ */
84
+ getScan(scanId) {
85
+ return activeScans.get(scanId);
86
+ }
87
+ /**
88
+ * Cancel a running scan.
89
+ */
90
+ cancelScan(scanId) {
91
+ const state = activeScans.get(scanId);
92
+ if (!state || state.status === "complete")
93
+ return false;
94
+ state.status = "cancelled";
95
+ return true;
96
+ }
97
+ /**
98
+ * Import accepted candidates into the library.
99
+ * Phase 1: Create tracked_request rows in a transaction (sync).
100
+ * Phase 2: Process each via the Importer (async) so library config
101
+ * (roots, link type, subtitles, naming) is applied correctly.
102
+ */
103
+ async importCandidates(scanId, candidateIds) {
104
+ const state = activeScans.get(scanId);
105
+ if (!state) {
106
+ return { imported: 0, failed: 0, errors: [] };
107
+ }
108
+ const errors = [];
109
+ // Phase 1: Create all tracked_request rows in a single transaction.
110
+ // Each entry captures what the Importer needs without doing the actual file I/O.
111
+ const pending = [];
112
+ const txn = this.db.transaction(() => {
113
+ for (const candidateId of candidateIds) {
114
+ const candidate = state.candidates.find((c) => c.id === candidateId);
115
+ if (!candidate) {
116
+ errors.push({ id: candidateId, error: "candidate not found" });
117
+ continue;
118
+ }
119
+ try {
120
+ const result = this.createTrackedRequest(candidate);
121
+ if (result)
122
+ pending.push(result);
123
+ }
124
+ catch (err) {
125
+ errors.push({ id: candidateId, error: err.message });
126
+ }
127
+ }
128
+ });
129
+ txn();
130
+ // Phase 2: Process each candidate through the Importer.
131
+ // The Importer handles file transfer (link type), subtitle importing,
132
+ // naming-rule resolution, library_path insertion, and event emission.
133
+ let imported = 0;
134
+ for (const p of pending) {
135
+ try {
136
+ const result = await this.importer.import(p.requestId, p.primaryFile, p.item, p.contentTypeId, p.extension, p.contentIdentity, p.libraryId);
137
+ if (result.success) {
138
+ imported++;
139
+ }
140
+ else {
141
+ errors.push({ id: p.requestId, error: result.error ?? "importer returned failure" });
142
+ }
143
+ }
144
+ catch (err) {
145
+ errors.push({ id: p.requestId, error: err.message });
146
+ }
147
+ }
148
+ return { imported, failed: errors.length, errors };
149
+ }
150
+ /**
151
+ * Remove a completed scan from memory.
152
+ */
153
+ cleanupScan(scanId) {
154
+ activeScans.delete(scanId);
155
+ }
156
+ // ── Private ──
157
+ async runScan(scanId, state, libraryId, contentTypeIds) {
158
+ // Phase 1: Discover groups
159
+ this.emitProgress(scanId, state);
160
+ const groups = await this.scanner.discoverGroups({ libraryId, contentTypeIds });
161
+ if (state.status === "cancelled")
162
+ return;
163
+ state.candidates = groups.map((g) => this.groupToCandidate(g));
164
+ state.progress.total = groups.length;
165
+ // Find already-imported items
166
+ const dirs = groups.map((g) => g.directory);
167
+ const existing = this.scanner.findExisting(dirs);
168
+ for (const candidate of state.candidates) {
169
+ if (existing.has(candidate.directory)) {
170
+ candidate.alreadyImported = true;
171
+ candidate.status = "already-imported";
172
+ candidate.accepted = false;
173
+ state.progress.alreadyImported++;
174
+ }
175
+ }
176
+ state.status = "matching";
177
+ this.emitProgress(scanId, state);
178
+ // Phase 2: Match metadata for non-imported candidates
179
+ const sem = new Semaphore(this.concurrency);
180
+ const matchTasks = state.candidates
181
+ .filter((c) => !c.alreadyImported)
182
+ .map(async (candidate) => {
183
+ await sem.acquire();
184
+ try {
185
+ if (state.status === "cancelled")
186
+ return;
187
+ const group = groups.find((g) => g.directory === candidate.directory);
188
+ if (!group)
189
+ return;
190
+ const matches = await this.matcher.findMatches(group);
191
+ if (matches.length > 0) {
192
+ candidate.matches = matches;
193
+ candidate.selectedMatchIndex = 0;
194
+ candidate.status = "matched";
195
+ candidate.accepted = true;
196
+ state.progress.matched++;
197
+ }
198
+ else {
199
+ candidate.matches = [];
200
+ candidate.selectedMatchIndex = -1;
201
+ candidate.status = "no-match";
202
+ state.progress.noMatch++;
203
+ }
204
+ state.progress.completed++;
205
+ this.emitProgress(scanId, state);
206
+ }
207
+ catch (err) {
208
+ candidate.status = "error";
209
+ candidate.error = String(err);
210
+ state.progress.completed++;
211
+ this.emitProgress(scanId, state);
212
+ }
213
+ finally {
214
+ sem.release();
215
+ }
216
+ });
217
+ await Promise.all(matchTasks);
218
+ state.status = "complete";
219
+ this.emitProgress(scanId, state);
220
+ this.logger.info("scan complete", {
221
+ scanId,
222
+ total: state.progress.total,
223
+ matched: state.progress.matched,
224
+ noMatch: state.progress.noMatch,
225
+ alreadyImported: state.progress.alreadyImported,
226
+ });
227
+ }
228
+ /**
229
+ * Run a custom search for a specific candidate and update its matches.
230
+ * Called from the API when the user clicks "Search again".
231
+ */
232
+ async searchCandidate(scanId, candidateId, query) {
233
+ const state = activeScans.get(scanId);
234
+ if (!state)
235
+ return false;
236
+ const candidate = state.candidates.find((c) => c.id === candidateId);
237
+ if (!candidate)
238
+ return false;
239
+ try {
240
+ const matches = await this.matcher.searchCustom(candidate.folderName, query, candidate.contentTypeId);
241
+ candidate.matches = matches;
242
+ candidate.selectedMatchIndex = matches.length > 0 ? 0 : -1;
243
+ candidate.status = matches.length > 0 ? "matched" : "no-match";
244
+ candidate.accepted = matches.length > 0;
245
+ return true;
246
+ }
247
+ catch (err) {
248
+ this.logger.error("custom search failed", {
249
+ scanId,
250
+ candidateId,
251
+ error: String(err),
252
+ });
253
+ return false;
254
+ }
255
+ }
256
+ /**
257
+ * Update the user's selected match index for a candidate.
258
+ */
259
+ selectMatch(scanId, candidateId, matchIndex) {
260
+ const state = activeScans.get(scanId);
261
+ if (!state)
262
+ return false;
263
+ const candidate = state.candidates.find((c) => c.id === candidateId);
264
+ if (!candidate)
265
+ return false;
266
+ if (matchIndex < -1 || matchIndex >= candidate.matches.length)
267
+ return false;
268
+ candidate.selectedMatchIndex = matchIndex;
269
+ candidate.accepted = matchIndex >= 0;
270
+ return true;
271
+ }
272
+ groupToCandidate(group) {
273
+ return {
274
+ id: randomUUID(),
275
+ directory: group.directory,
276
+ files: group.files,
277
+ contentTypeId: group.contentTypeId,
278
+ libraryId: group.libraryId,
279
+ folderName: group.folderName,
280
+ matches: [],
281
+ selectedMatchIndex: -1,
282
+ accepted: false,
283
+ alreadyImported: false,
284
+ status: "pending",
285
+ };
286
+ }
287
+ emitProgress(scanId, state) {
288
+ this.bus.emit("library.scan.progress", {
289
+ scanId,
290
+ status: state.status,
291
+ progress: state.progress,
292
+ });
293
+ }
294
+ /**
295
+ * Create a tracked_request row for a scanned candidate.
296
+ * Runs inside a transaction. Returns the data the Importer needs,
297
+ * or null if the candidate has no media files.
298
+ *
299
+ * Unlike the old importSingleCandidate, this does NOT:
300
+ * - Insert into library_paths (the Importer handles that)
301
+ * - Set library_path on tracked_requests (the Importer sets it)
302
+ * - Emit events (the Importer emits them)
303
+ */
304
+ createTrackedRequest(candidate) {
305
+ const requestId = randomUUID();
306
+ // Get the selected match, if any
307
+ const selectedMatch = candidate.selectedMatchIndex >= 0 && candidate.selectedMatchIndex < candidate.matches.length
308
+ ? candidate.matches[candidate.selectedMatchIndex]
309
+ : null;
310
+ // Determine the correct contentTypeId: prefer the match's value when
311
+ // available (metadata sources return the real ID), otherwise fall back
312
+ // to the scanner-assigned ID (which may be "unknown" for default-root items).
313
+ const resolvedContentTypeId = selectedMatch
314
+ ? selectedMatch.item.contentTypeId
315
+ : candidate.contentTypeId;
316
+ // Build the ContentItemBase from selected match or folder name
317
+ const item = selectedMatch
318
+ ? { ...selectedMatch.item, contentTypeId: resolvedContentTypeId }
319
+ : {
320
+ contentTypeId: resolvedContentTypeId,
321
+ title: candidate.folderName,
322
+ externalIds: {},
323
+ };
324
+ // Pick the primary media file
325
+ const primaryFile = this.pickPrimaryFile(candidate.files);
326
+ // Compute content identity using the resolved content type ID
327
+ const contentIdentity = selectedMatch
328
+ ? computeContentIdentity({ contentTypeId: resolvedContentTypeId, externalIds: item.externalIds }, this.contentTypeRegistry)
329
+ : `${resolvedContentTypeId}::local::${candidate.folderName}`;
330
+ // Determine file extension for the Importer
331
+ const extension = path.extname(primaryFile).slice(1).toLowerCase() || "mkv";
332
+ const now = new Date().toISOString();
333
+ // Insert into tracked_requests — no library_path set here.
334
+ // The Importer will update it after the file transfer.
335
+ this.db
336
+ .prepare(`INSERT INTO tracked_requests
337
+ (request_id, content_type_id, content_identity, item,
338
+ owner_user_id, watchers, status, library_id, attempts, created_at, updated_at)
339
+ VALUES
340
+ (@requestId, @contentTypeId, @contentIdentity, @item,
341
+ @ownerUserId, '[]', 'downloaded', @libraryId, 0, @createdAt, @updatedAt)`)
342
+ .run({
343
+ requestId,
344
+ contentTypeId: resolvedContentTypeId,
345
+ contentIdentity,
346
+ item: JSON.stringify(item),
347
+ ownerUserId: "admin",
348
+ libraryId: candidate.libraryId ?? null,
349
+ createdAt: now,
350
+ updatedAt: now,
351
+ });
352
+ return {
353
+ requestId,
354
+ primaryFile,
355
+ item,
356
+ contentTypeId: resolvedContentTypeId,
357
+ extension,
358
+ contentIdentity,
359
+ libraryId: candidate.libraryId,
360
+ };
361
+ }
362
+ pickPrimaryFile(files) {
363
+ if (files.length === 0)
364
+ throw new Error("no media files to import");
365
+ if (files.length === 1)
366
+ return files[0];
367
+ // Prefer the file whose basename best matches the parent folder name
368
+ const dirName = path.basename(path.dirname(files[0]));
369
+ const exactMatch = files.find((f) => {
370
+ const base = path.basename(f, path.extname(f));
371
+ return base.toLowerCase() === dirName.toLowerCase();
372
+ });
373
+ if (exactMatch)
374
+ return exactMatch;
375
+ // Fall back to first file
376
+ return files[0];
377
+ }
378
+ }
379
+ //# sourceMappingURL=scan-controller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scan-controller.js","sourceRoot":"","sources":["../../src/library/scan-controller.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ export interface ResolvedSource {
2
+ /** The directory containing the source files. */
3
+ sourceDir: string;
4
+ /** All media files found at the source location. */
5
+ files: string[];
6
+ }
7
+ /**
8
+ * Resolve the source path from a download.completed event.
9
+ *
10
+ * Resolution order:
11
+ * 1. If location is provided and the stripped path exists:
12
+ * - If it is a file → return it as the only file
13
+ * - If it is a directory → scan for media files
14
+ * 2. If all fail → throw with a descriptive message.
15
+ */
16
+ export declare function resolveSourcePath(location: string | undefined): Promise<ResolvedSource>;
17
+ //# sourceMappingURL=source-resolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"source-resolver.d.ts","sourceRoot":"","sources":["../../src/library/source-resolver.ts"],"names":[],"mappings":"AAWA,MAAM,WAAW,cAAc;IAC7B,iDAAiD;IACjD,SAAS,EAAE,MAAM,CAAC;IAClB,oDAAoD;IACpD,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAgCD;;;;;;;;GAQG;AACH,wBAAsB,iBAAiB,CACrC,QAAQ,EAAE,MAAM,GAAG,SAAS,GAC3B,OAAO,CAAC,cAAc,CAAC,CAgCzB"}
@@ -0,0 +1,75 @@
1
+ // SPDX-License-Identifier: 0BSD
2
+ // SPDX-FileCopyrightText: 2025 Digi <digitalpaws@proton.me>
3
+ import fs from "node:fs/promises";
4
+ import path from "node:path";
5
+ const MEDIA_EXTENSIONS = new Set([
6
+ "mkv", "mp4", "avi", "mov", "wmv", "flv", "webm",
7
+ "m4v", "mpg", "mpeg", "ts", "m2ts",
8
+ ]);
9
+ /**
10
+ * Return true if the file extension looks like a media file.
11
+ */
12
+ function isMediaFile(filePath) {
13
+ const ext = path.extname(filePath).slice(1).toLowerCase();
14
+ return MEDIA_EXTENSIONS.has(ext);
15
+ }
16
+ /**
17
+ * Scan a directory (non-recursive) for media files.
18
+ * Skips hidden directories and common non-content subdirectories.
19
+ */
20
+ async function scanDirectory(dir) {
21
+ const results = [];
22
+ const entries = await fs.readdir(dir, { withFileTypes: true });
23
+ for (const entry of entries) {
24
+ const fullPath = path.join(dir, entry.name);
25
+ if (entry.isDirectory()) {
26
+ // Skip common non-content directories
27
+ if (entry.name.startsWith("."))
28
+ continue;
29
+ if (entry.name === "Extras" || entry.name === "Sample" || entry.name === "samples")
30
+ continue;
31
+ const nested = await scanDirectory(fullPath);
32
+ results.push(...nested);
33
+ }
34
+ else if (entry.isFile() && isMediaFile(fullPath)) {
35
+ results.push(fullPath);
36
+ }
37
+ }
38
+ return results;
39
+ }
40
+ /**
41
+ * Resolve the source path from a download.completed event.
42
+ *
43
+ * Resolution order:
44
+ * 1. If location is provided and the stripped path exists:
45
+ * - If it is a file → return it as the only file
46
+ * - If it is a directory → scan for media files
47
+ * 2. If all fail → throw with a descriptive message.
48
+ */
49
+ export async function resolveSourcePath(location) {
50
+ if (!location) {
51
+ throw new Error("Cannot resolve source path: no location provided on download.completed");
52
+ }
53
+ // Strip protocol prefix (e.g. "qbittorrent:/path" → "/path")
54
+ const colonIdx = location.indexOf(":");
55
+ const strippedPath = colonIdx >= 0 ? location.slice(colonIdx + 1) : location;
56
+ try {
57
+ const stat = await fs.stat(strippedPath);
58
+ if (stat.isFile()) {
59
+ const dir = path.dirname(strippedPath);
60
+ return { sourceDir: dir, files: [strippedPath] };
61
+ }
62
+ if (stat.isDirectory()) {
63
+ const files = await scanDirectory(strippedPath);
64
+ if (files.length === 0) {
65
+ throw new Error(`No media files found in directory: ${strippedPath}`);
66
+ }
67
+ return { sourceDir: strippedPath, files };
68
+ }
69
+ }
70
+ catch (err) {
71
+ throw new Error(`Cannot access source path "${strippedPath}": ${err.message}`, { cause: err });
72
+ }
73
+ throw new Error(`Source path is neither a file nor a directory: ${strippedPath}`);
74
+ }
75
+ //# sourceMappingURL=source-resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"source-resolver.js","sourceRoot":"","sources":["../../src/library/source-resolver.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { Logger } from "../types/plugin.js";
2
+ export declare function createLogger(tag?: string, levelName?: string): Logger;
3
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/logging/logger.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAoBjD,wBAAgB,YAAY,CAAC,GAAG,SAAS,EAAE,SAAS,SAAS,GAAG,MAAM,CAuFrE"}