@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,68 @@
1
+ // SPDX-License-Identifier: 0BSD
2
+ // SPDX-FileCopyrightText: 2025 Digi <digitalpaws@proton.me>
3
+ /**
4
+ * Reserved spine namespace prefixes.
5
+ * The dotted unprefixed namespace is reserved for the core spine.
6
+ */
7
+ export const SPINE_NAMESPACES = [
8
+ "content.",
9
+ "release.",
10
+ "download.",
11
+ "import.",
12
+ "library.",
13
+ "discovery.",
14
+ "item.",
15
+ ];
16
+ /**
17
+ * The v1 spine event catalog (spec §8).
18
+ */
19
+ export const SPINE_EVENTS = {
20
+ "discovery.search": { kind: "request", producers: "core", schemaVersion: 1 },
21
+ "discovery.results": { kind: "reply", producers: "metadata-plugin", schemaVersion: 1 },
22
+ "content.requested": { kind: "request", producers: "core", schemaVersion: 2 },
23
+ "release.found": { kind: "reply", producers: "indexer-plugin", schemaVersion: 1 },
24
+ "release.chosen": { kind: "notification", producers: "selection-plugin", schemaVersion: 1 },
25
+ "download.started": { kind: "notification", producers: "downloader-plugin", schemaVersion: 1 },
26
+ "download.completed": { kind: "notification", producers: "downloader-plugin", schemaVersion: 1 },
27
+ "download.failed": { kind: "notification", producers: "downloader-plugin", schemaVersion: 1 },
28
+ "request.failed": { kind: "notification", producers: "selection-plugin", schemaVersion: 1 },
29
+ "item.updated": { kind: "notification", producers: "core", schemaVersion: 1 },
30
+ "request.deleted": { kind: "notification", producers: "core", schemaVersion: 1 },
31
+ "import.started": { kind: "notification", producers: "core", schemaVersion: 1 },
32
+ "import.completed": { kind: "notification", producers: "core", schemaVersion: 1 },
33
+ "import.failed": { kind: "notification", producers: "core", schemaVersion: 1 },
34
+ "library.file_added": { kind: "notification", producers: "core", schemaVersion: 1 },
35
+ };
36
+ /**
37
+ * Check whether an event type falls within the reserved spine namespace.
38
+ */
39
+ export function isSpineType(type) {
40
+ return SPINE_NAMESPACES.some((ns) => type.startsWith(ns));
41
+ }
42
+ /**
43
+ * Return the schemaVersion for a spine type, or 1 as default for custom events.
44
+ */
45
+ export function getSpineSchemaVersion(type) {
46
+ return SPINE_EVENTS[type]?.schemaVersion ?? 1;
47
+ }
48
+ /**
49
+ * Validate whether a source is allowed to emit the given spine event type.
50
+ *
51
+ * Returns an error message string if the emission is suspect, or null if it is fine.
52
+ * Per EBQ4 resolution, this only logs warnings; it does not reject.
53
+ */
54
+ export function validateSpineEmission(source, type) {
55
+ if (!isSpineType(type))
56
+ return null; // custom event, fine
57
+ const meta = SPINE_EVENTS[type];
58
+ if (!meta) {
59
+ return `Unknown spine event type "${type}" emitted by "${source}"`;
60
+ }
61
+ // For v1 we are lenient: log but don't block.
62
+ // Only flag if core events are emitted by non-core sources.
63
+ if (meta.producers === "core" && source !== "core") {
64
+ return `Spine event "${type}" is reserved for core, but was emitted by "${source}"`;
65
+ }
66
+ return null;
67
+ }
68
+ //# sourceMappingURL=spine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spine.js","sourceRoot":"","sources":["../../src/events/spine.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ import { AsyncLocalStorage } from "node:async_hooks";
2
+ import type { StarrEvent } from "../types/plugin.js";
3
+ /**
4
+ * Singleton AsyncLocalStorage that holds the "current event" during handler dispatch.
5
+ * This is the core trace-propagation mechanism (spec §5 PS2, EBQ5).
6
+ */
7
+ export declare const traceStore: AsyncLocalStorage<StarrEvent<unknown>>;
8
+ /**
9
+ * Run a function within the trace context of the given event.
10
+ * Any emit() calls inside fn will inherit this event's traceId.
11
+ */
12
+ export declare function runInTraceContext<R>(event: StarrEvent, fn: () => R): R;
13
+ /**
14
+ * Return the traceId of the currently handled event, or mint a fresh one
15
+ * if called outside any handler (root emission — spec §5 PS3).
16
+ */
17
+ export declare function getCurrentTraceId(): string;
18
+ /**
19
+ * Return the correlationId of the currently handled event, or undefined.
20
+ * correlationId is NOT automatically propagated by plain emit() —
21
+ * it is only attached via explicit opts or the reply() sugar (§5 PS2, §6.1).
22
+ */
23
+ export declare function getCurrentCorrelationId(): string | undefined;
24
+ //# sourceMappingURL=trace.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trace.d.ts","sourceRoot":"","sources":["../../src/events/trace.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAErD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD;;;GAGG;AACH,eAAO,MAAM,UAAU,wCAAsC,CAAC;AAE9D;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAEtE;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAG1C;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,IAAI,MAAM,GAAG,SAAS,CAE5D"}
@@ -0,0 +1,33 @@
1
+ // SPDX-License-Identifier: 0BSD
2
+ // SPDX-FileCopyrightText: 2025 Digi <digitalpaws@proton.me>
3
+ import { AsyncLocalStorage } from "node:async_hooks";
4
+ import { randomUUID } from "node:crypto";
5
+ /**
6
+ * Singleton AsyncLocalStorage that holds the "current event" during handler dispatch.
7
+ * This is the core trace-propagation mechanism (spec §5 PS2, EBQ5).
8
+ */
9
+ export const traceStore = new AsyncLocalStorage();
10
+ /**
11
+ * Run a function within the trace context of the given event.
12
+ * Any emit() calls inside fn will inherit this event's traceId.
13
+ */
14
+ export function runInTraceContext(event, fn) {
15
+ return traceStore.run(event, fn);
16
+ }
17
+ /**
18
+ * Return the traceId of the currently handled event, or mint a fresh one
19
+ * if called outside any handler (root emission — spec §5 PS3).
20
+ */
21
+ export function getCurrentTraceId() {
22
+ const event = traceStore.getStore();
23
+ return event ? event.traceId : randomUUID();
24
+ }
25
+ /**
26
+ * Return the correlationId of the currently handled event, or undefined.
27
+ * correlationId is NOT automatically propagated by plain emit() —
28
+ * it is only attached via explicit opts or the reply() sugar (§5 PS2, §6.1).
29
+ */
30
+ export function getCurrentCorrelationId() {
31
+ return traceStore.getStore()?.correlationId;
32
+ }
33
+ //# sourceMappingURL=trace.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trace.js","sourceRoot":"","sources":["../../src/events/trace.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,4DAA4D;AAE5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAGzC;;;GAGG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,IAAI,iBAAiB,EAAc,CAAC;AAE9D;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAI,KAAiB,EAAE,EAAW;IACjE,OAAO,UAAU,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AACnC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB;IAC/B,MAAM,KAAK,GAAG,UAAU,CAAC,QAAQ,EAAE,CAAC;IACpC,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC;AAC9C,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,uBAAuB;IACrC,OAAO,UAAU,CAAC,QAAQ,EAAE,EAAE,aAAa,CAAC;AAC9C,CAAC"}
package/dist/host.d.ts ADDED
@@ -0,0 +1,65 @@
1
+ import type { EventBus, Logger, PluginLifecycle, PluginMeta } from "./types/plugin.js";
2
+ import type { Datastore } from "./datastore/db.js";
3
+ import type { ConfigService } from "./config/config-service.js";
4
+ import type { FastifyInstance } from "fastify";
5
+ import type Database from "better-sqlite3";
6
+ import { EventBusImpl } from "./events/event-bus.js";
7
+ import { ContentTypeRegistryImpl } from "./content-types/registry.js";
8
+ import type { StateStore } from "./state/state-store.js";
9
+ import type { Importer } from "./library/importer.js";
10
+ import type { LibraryCache } from "./library/library-cache.js";
11
+ import type { LibraryService } from "./library/library-service.js";
12
+ import type { ScanController } from "./library/scan-controller.js";
13
+ export interface StarrOptions {
14
+ pluginsDir: string;
15
+ apiVersion: string;
16
+ logger: Logger;
17
+ }
18
+ export interface PluginRuntime {
19
+ meta: PluginMeta;
20
+ lifecycle: PluginLifecycle;
21
+ startOrder: number;
22
+ }
23
+ export declare class Starr {
24
+ readonly options: StarrOptions;
25
+ readonly logger: Logger;
26
+ readonly apiVersion: string;
27
+ datastore: Datastore | null;
28
+ private _eventBus;
29
+ /**
30
+ * Public EventBus interface (exposed for wiring context).
31
+ */
32
+ get eventBus(): EventBus;
33
+ /**
34
+ * Internal accessor for EventBusImpl (used by context to create plugin-bound buses).
35
+ */
36
+ get busImpl(): EventBusImpl;
37
+ /**
38
+ * Core event bus with source="core". Convenience for emitting spine events from the host.
39
+ */
40
+ get coreBus(): EventBus;
41
+ contentTypeRegistry: ContentTypeRegistryImpl;
42
+ configService: ConfigService | null;
43
+ stateStore: StateStore | null;
44
+ importer: Importer | null;
45
+ libraryCache: LibraryCache | null;
46
+ libraryService: LibraryService | null;
47
+ scanController: ScanController | null;
48
+ webServer: FastifyInstance | null;
49
+ private pluginRuntimes;
50
+ private running;
51
+ getScanController(): ScanController;
52
+ getLibraryCache(): LibraryCache;
53
+ getLibraryService(): LibraryService;
54
+ getImporter(): Importer;
55
+ /** Convenience: get the state store or throw if not initialized. */
56
+ getStateStore(): StateStore;
57
+ getDatabase(): Database.Database | null;
58
+ constructor(options: StarrOptions);
59
+ markRunning(): void;
60
+ markStopped(): void;
61
+ get isRunning(): boolean;
62
+ setRuntimes(runtimes: PluginRuntime[]): void;
63
+ getRuntimes(): readonly PluginRuntime[];
64
+ }
65
+ //# sourceMappingURL=host.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"host.d.ts","sourceRoot":"","sources":["../src/host.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACvF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AACtE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC/D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAEnE,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,UAAU,CAAC;IACjB,SAAS,EAAE,eAAe,CAAC;IAC3B,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,qBAAa,KAAK;IAChB,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAC;IAC/B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAG5B,SAAS,EAAE,SAAS,GAAG,IAAI,CAAQ;IACnC,OAAO,CAAC,SAAS,CAAe;IAEhC;;OAEG;IACH,IAAI,QAAQ,IAAI,QAAQ,CAEvB;IAED;;OAEG;IACH,IAAI,OAAO,IAAI,YAAY,CAE1B;IAED;;OAEG;IACH,IAAI,OAAO,IAAI,QAAQ,CAEtB;IACD,mBAAmB,EAAE,uBAAuB,CAAiC;IAC7E,aAAa,EAAE,aAAa,GAAG,IAAI,CAAQ;IAC3C,UAAU,EAAE,UAAU,GAAG,IAAI,CAAQ;IACrC,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAAQ;IACjC,YAAY,EAAE,YAAY,GAAG,IAAI,CAAQ;IACzC,cAAc,EAAE,cAAc,GAAG,IAAI,CAAQ;IAC7C,cAAc,EAAE,cAAc,GAAG,IAAI,CAAQ;IAC7C,SAAS,EAAE,eAAe,GAAG,IAAI,CAAQ;IAEzC,OAAO,CAAC,cAAc,CAAuB;IAC7C,OAAO,CAAC,OAAO,CAAS;IAExB,iBAAiB,IAAI,cAAc;IAKnC,eAAe,IAAI,YAAY;IAK/B,iBAAiB,IAAI,cAAc;IAKnC,WAAW,IAAI,QAAQ;IAKvB,oEAAoE;IACpE,aAAa,IAAI,UAAU;IAO3B,WAAW,IAAI,QAAQ,CAAC,QAAQ,GAAG,IAAI;gBAI3B,OAAO,EAAE,YAAY;IAOjC,WAAW,IAAI,IAAI;IAKnB,WAAW,IAAI,IAAI;IAKnB,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED,WAAW,CAAC,QAAQ,EAAE,aAAa,EAAE,GAAG,IAAI;IAI5C,WAAW,IAAI,SAAS,aAAa,EAAE;CAGxC"}
package/dist/host.js ADDED
@@ -0,0 +1,94 @@
1
+ // SPDX-License-Identifier: 0BSD
2
+ // SPDX-FileCopyrightText: 2025 Digi <digitalpaws@proton.me>
3
+ import { EventBusImpl } from "./events/event-bus.js";
4
+ import { ContentTypeRegistryImpl } from "./content-types/registry.js";
5
+ export class Starr {
6
+ options;
7
+ logger;
8
+ apiVersion;
9
+ // Service slots to be wired by later tasks.
10
+ datastore = null;
11
+ _eventBus;
12
+ /**
13
+ * Public EventBus interface (exposed for wiring context).
14
+ */
15
+ get eventBus() {
16
+ return this._eventBus.coreBus();
17
+ }
18
+ /**
19
+ * Internal accessor for EventBusImpl (used by context to create plugin-bound buses).
20
+ */
21
+ get busImpl() {
22
+ return this._eventBus;
23
+ }
24
+ /**
25
+ * Core event bus with source="core". Convenience for emitting spine events from the host.
26
+ */
27
+ get coreBus() {
28
+ return this._eventBus.coreBus();
29
+ }
30
+ contentTypeRegistry = new ContentTypeRegistryImpl();
31
+ configService = null;
32
+ stateStore = null;
33
+ importer = null;
34
+ libraryCache = null;
35
+ libraryService = null;
36
+ scanController = null;
37
+ webServer = null;
38
+ pluginRuntimes = [];
39
+ running = false;
40
+ getScanController() {
41
+ if (!this.scanController)
42
+ throw new Error("scan controller not initialized");
43
+ return this.scanController;
44
+ }
45
+ getLibraryCache() {
46
+ if (!this.libraryCache)
47
+ throw new Error("library cache not initialized");
48
+ return this.libraryCache;
49
+ }
50
+ getLibraryService() {
51
+ if (!this.libraryService)
52
+ throw new Error("library service not initialized");
53
+ return this.libraryService;
54
+ }
55
+ getImporter() {
56
+ if (!this.importer)
57
+ throw new Error("importer not initialized");
58
+ return this.importer;
59
+ }
60
+ /** Convenience: get the state store or throw if not initialized. */
61
+ getStateStore() {
62
+ if (!this.stateStore) {
63
+ throw new Error("state store not initialized");
64
+ }
65
+ return this.stateStore;
66
+ }
67
+ getDatabase() {
68
+ return this.datastore?.db ?? null;
69
+ }
70
+ constructor(options) {
71
+ this.options = options;
72
+ this.logger = options.logger.child("host");
73
+ this.apiVersion = options.apiVersion;
74
+ this._eventBus = new EventBusImpl(this.logger);
75
+ }
76
+ markRunning() {
77
+ this.running = true;
78
+ this.logger.info("running");
79
+ }
80
+ markStopped() {
81
+ this.running = false;
82
+ this.logger.info("stopped");
83
+ }
84
+ get isRunning() {
85
+ return this.running;
86
+ }
87
+ setRuntimes(runtimes) {
88
+ this.pluginRuntimes = runtimes;
89
+ }
90
+ getRuntimes() {
91
+ return this.pluginRuntimes;
92
+ }
93
+ }
94
+ //# sourceMappingURL=host.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"host.js","sourceRoot":"","sources":["../src/host.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,4DAA4D;AAO5D,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AAmBtE,MAAM,OAAO,KAAK;IACP,OAAO,CAAe;IACtB,MAAM,CAAS;IACf,UAAU,CAAS;IAE5B,4CAA4C;IAC5C,SAAS,GAAqB,IAAI,CAAC;IAC3B,SAAS,CAAe;IAEhC;;OAEG;IACH,IAAI,QAAQ;QACV,OAAO,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC;IAClC,CAAC;IAED;;OAEG;IACH,IAAI,OAAO;QACT,OAAO,IAAI,CAAC,SAAS,CAAC;IACxB,CAAC;IAED;;OAEG;IACH,IAAI,OAAO;QACT,OAAO,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC;IAClC,CAAC;IACD,mBAAmB,GAA4B,IAAI,uBAAuB,EAAE,CAAC;IAC7E,aAAa,GAAyB,IAAI,CAAC;IAC3C,UAAU,GAAsB,IAAI,CAAC;IACrC,QAAQ,GAAoB,IAAI,CAAC;IACjC,YAAY,GAAwB,IAAI,CAAC;IACzC,cAAc,GAA0B,IAAI,CAAC;IAC7C,cAAc,GAA0B,IAAI,CAAC;IAC7C,SAAS,GAA2B,IAAI,CAAC;IAEjC,cAAc,GAAoB,EAAE,CAAC;IACrC,OAAO,GAAG,KAAK,CAAC;IAExB,iBAAiB;QACf,IAAI,CAAC,IAAI,CAAC,cAAc;YAAE,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;QAC7E,OAAO,IAAI,CAAC,cAAc,CAAC;IAC7B,CAAC;IAED,eAAe;QACb,IAAI,CAAC,IAAI,CAAC,YAAY;YAAE,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;QACzE,OAAO,IAAI,CAAC,YAAY,CAAC;IAC3B,CAAC;IAED,iBAAiB;QACf,IAAI,CAAC,IAAI,CAAC,cAAc;YAAE,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;QAC7E,OAAO,IAAI,CAAC,cAAc,CAAC;IAC7B,CAAC;IAED,WAAW;QACT,IAAI,CAAC,IAAI,CAAC,QAAQ;YAAE,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;QAChE,OAAO,IAAI,CAAC,QAAQ,CAAC;IACvB,CAAC;IAED,oEAAoE;IACpE,aAAa;QACX,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC;YACrB,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;QACjD,CAAC;QACD,OAAO,IAAI,CAAC,UAAU,CAAC;IACzB,CAAC;IAED,WAAW;QACT,OAAO,IAAI,CAAC,SAAS,EAAE,EAAE,IAAI,IAAI,CAAC;IACpC,CAAC;IAED,YAAY,OAAqB;QAC/B,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAC3C,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;QACrC,IAAI,CAAC,SAAS,GAAG,IAAI,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACjD,CAAC;IAED,WAAW;QACT,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACpB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAC9B,CAAC;IAED,WAAW;QACT,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;QACrB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAC9B,CAAC;IAED,IAAI,SAAS;QACX,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IAED,WAAW,CAAC,QAAyB;QACnC,IAAI,CAAC,cAAc,GAAG,QAAQ,CAAC;IACjC,CAAC;IAED,WAAW;QACT,OAAO,IAAI,CAAC,cAAc,CAAC;IAC7B,CAAC;CACF"}
@@ -0,0 +1,29 @@
1
+ import type Database from "better-sqlite3";
2
+ import type { EventBus, Logger } from "../types/plugin.js";
3
+ import type { ContentItemBase, NamingRule } from "@digitalpaws/starr-types-core";
4
+ import type { LibraryConfig } from "./library-config.js";
5
+ import type { LibraryService } from "./library-service.js";
6
+ export interface ImportResult {
7
+ success: boolean;
8
+ libraryPath?: string;
9
+ error?: string;
10
+ }
11
+ export declare class Importer {
12
+ private db;
13
+ private bus;
14
+ private logger;
15
+ private getConfig;
16
+ private getNamingRule;
17
+ private libraryService?;
18
+ private stmtUpsertPath;
19
+ private stmtUpdateRequestPath;
20
+ constructor(db: Database.Database, bus: EventBus, logger: Logger, getConfig: () => LibraryConfig, getNamingRule: (contentTypeId: string) => NamingRule, libraryService?: LibraryService);
21
+ import(requestId: string, sourcePath: string, item: ContentItemBase, contentTypeId: string, extension: string,
22
+ /** The pre-computed content identity for this item. */
23
+ contentIdentity: string,
24
+ /** Optional library ID for multi-library support. */
25
+ libraryId?: string): Promise<ImportResult>;
26
+ private transferFile;
27
+ private importSubtitles;
28
+ }
29
+ //# sourceMappingURL=importer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"importer.d.ts","sourceRoot":"","sources":["../../src/library/importer.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,eAAe,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAEjF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,QAAQ;IACnB,OAAO,CAAC,EAAE,CAAoB;IAC9B,OAAO,CAAC,GAAG,CAAW;IACtB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,SAAS,CAAsB;IACvC,OAAO,CAAC,aAAa,CAAwC;IAC7D,OAAO,CAAC,cAAc,CAAC,CAAiB;IAExC,OAAO,CAAC,cAAc,CAAqB;IAC3C,OAAO,CAAC,qBAAqB,CAAqB;gBAGhD,EAAE,EAAE,QAAQ,CAAC,QAAQ,EACrB,GAAG,EAAE,QAAQ,EACb,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,aAAa,EAC9B,aAAa,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,UAAU,EACpD,cAAc,CAAC,EAAE,cAAc;IAwB3B,MAAM,CACV,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,eAAe,EACrB,aAAa,EAAE,MAAM,EACrB,SAAS,EAAE,MAAM;IACjB,uDAAuD;IACvD,eAAe,EAAE,MAAM;IACvB,qDAAqD;IACrD,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,YAAY,CAAC;YAoEV,YAAY;YAUZ,eAAe;CA0B9B"}
@@ -0,0 +1,145 @@
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
+ import { resolveNaming } from "./naming.js";
6
+ export class Importer {
7
+ db;
8
+ bus;
9
+ logger;
10
+ getConfig;
11
+ getNamingRule;
12
+ libraryService;
13
+ stmtUpsertPath;
14
+ stmtUpdateRequestPath;
15
+ constructor(db, bus, logger, getConfig, getNamingRule, libraryService) {
16
+ this.db = db;
17
+ this.bus = bus;
18
+ this.logger = logger.child("importer");
19
+ this.getConfig = getConfig;
20
+ this.getNamingRule = getNamingRule;
21
+ this.libraryService = libraryService;
22
+ this.stmtUpsertPath = db.prepare(`
23
+ INSERT INTO library_paths (request_id, library_path, library_root, imported_at, updated_at)
24
+ VALUES (@requestId, @libraryPath, @libraryRoot, datetime('now'), datetime('now'))
25
+ ON CONFLICT(request_id) DO UPDATE SET
26
+ library_path = excluded.library_path,
27
+ updated_at = datetime('now')
28
+ `);
29
+ this.stmtUpdateRequestPath = db.prepare(`
30
+ UPDATE tracked_requests
31
+ SET library_path = @libraryPath, updated_at = datetime('now')
32
+ WHERE request_id = @requestId
33
+ `);
34
+ }
35
+ async import(requestId, sourcePath, item, contentTypeId, extension,
36
+ /** The pre-computed content identity for this item. */
37
+ contentIdentity,
38
+ /** Optional library ID for multi-library support. */
39
+ libraryId) {
40
+ try {
41
+ this.bus.emit("import.started", { requestId });
42
+ // Determine library config
43
+ let libraryRoot;
44
+ let linkType;
45
+ let importSubtitles;
46
+ let subtitleExtensions;
47
+ if (libraryId && this.libraryService) {
48
+ const lib = this.libraryService.get(libraryId);
49
+ if (lib) {
50
+ libraryRoot = lib.roots[contentTypeId] ?? lib.defaultLibraryRoot;
51
+ linkType = lib.linkType;
52
+ importSubtitles = lib.importSubtitles;
53
+ subtitleExtensions = lib.subtitleExtensions;
54
+ }
55
+ else {
56
+ // Fallback to global config
57
+ const cfg = this.getConfig();
58
+ libraryRoot = cfg.roots[contentTypeId] ?? cfg.defaultLibraryRoot;
59
+ linkType = cfg.linkType;
60
+ importSubtitles = cfg.importSubtitles;
61
+ subtitleExtensions = cfg.subtitleExtensions;
62
+ }
63
+ }
64
+ else {
65
+ // Fallback to global config
66
+ const cfg = this.getConfig();
67
+ libraryRoot = cfg.roots[contentTypeId] ?? cfg.defaultLibraryRoot;
68
+ linkType = cfg.linkType;
69
+ importSubtitles = cfg.importSubtitles;
70
+ subtitleExtensions = cfg.subtitleExtensions;
71
+ }
72
+ const namingRule = this.getNamingRule(contentTypeId);
73
+ const relativePath = resolveNaming(namingRule, item, extension);
74
+ const destPath = path.join(libraryRoot, relativePath);
75
+ await fs.mkdir(path.dirname(destPath), { recursive: true });
76
+ await this.transferFile(sourcePath, destPath, linkType);
77
+ if (importSubtitles) {
78
+ await this.importSubtitles(sourcePath, destPath, subtitleExtensions);
79
+ }
80
+ this.stmtUpsertPath.run({
81
+ requestId,
82
+ libraryPath: destPath,
83
+ libraryRoot,
84
+ });
85
+ this.stmtUpdateRequestPath.run({ requestId, libraryPath: destPath });
86
+ this.bus.emit("import.completed", { requestId, libraryPath: destPath });
87
+ this.bus.emit("library.file_added", {
88
+ requestId,
89
+ libraryPath: destPath,
90
+ contentTypeId,
91
+ contentIdentity,
92
+ });
93
+ return { success: true, libraryPath: destPath };
94
+ }
95
+ catch (err) {
96
+ const error = String(err);
97
+ this.bus.emit("import.failed", { requestId, error });
98
+ return { success: false, error };
99
+ }
100
+ }
101
+ async transferFile(src, dest, linkType) {
102
+ switch (linkType) {
103
+ case "move":
104
+ await fs.rename(src, dest);
105
+ break;
106
+ case "hardlink":
107
+ await fs.link(src, dest);
108
+ break;
109
+ case "symlink":
110
+ await fs.symlink(src, dest);
111
+ break;
112
+ case "copy":
113
+ await fs.copyFile(src, dest);
114
+ break;
115
+ default: throw new Error(`Unknown link type: ${linkType}`);
116
+ }
117
+ }
118
+ async importSubtitles(src, dest, subtitleExtensions) {
119
+ const srcDir = path.dirname(src);
120
+ const baseName = path.basename(src, path.extname(src));
121
+ const destDir = path.dirname(dest);
122
+ const destBaseName = path.basename(dest, path.extname(dest));
123
+ const entries = await fs.readdir(srcDir, { withFileTypes: true });
124
+ for (const entry of entries) {
125
+ if (!entry.isFile())
126
+ continue;
127
+ const ext = path.extname(entry.name).slice(1).toLowerCase();
128
+ if (!subtitleExtensions.includes(ext))
129
+ continue;
130
+ const entryBaseName = path.basename(entry.name, path.extname(entry.name));
131
+ if (!entryBaseName.startsWith(baseName))
132
+ continue;
133
+ const srcSub = path.join(srcDir, entry.name);
134
+ const destSubName = entry.name.replace(baseName, destBaseName);
135
+ const destSub = path.join(destDir, destSubName);
136
+ try {
137
+ await this.transferFile(srcSub, destSub, "copy");
138
+ }
139
+ catch (err) {
140
+ this.logger.warn("failed to import subtitle", { src: srcSub, error: String(err) });
141
+ }
142
+ }
143
+ }
144
+ }
145
+ //# sourceMappingURL=importer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"importer.js","sourceRoot":"","sources":["../../src/library/importer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ import type Database from "better-sqlite3";
2
+ import type { EventBus, Logger } from "../types/plugin.js";
3
+ /**
4
+ * A single row in the library_cache table, exposed to the API.
5
+ */
6
+ export interface CachedLibraryItem {
7
+ requestId: string;
8
+ contentTypeId: string;
9
+ title: string;
10
+ sortTitle?: string;
11
+ summary?: string;
12
+ releaseDate?: string;
13
+ images: any[];
14
+ externalIds: Record<string, string>;
15
+ fields?: Record<string, unknown>;
16
+ libraryPath: string;
17
+ fileSize?: number;
18
+ importedAt: string;
19
+ }
20
+ export declare class LibraryCache {
21
+ private db;
22
+ private bus;
23
+ private logger;
24
+ private stmtInsert;
25
+ private stmtDelete;
26
+ private stmtGetAll;
27
+ private stmtGetByType;
28
+ private stmtCount;
29
+ constructor(db: Database.Database, bus: EventBus, logger: Logger);
30
+ /**
31
+ * Populate the cache from tracked_requests on startup.
32
+ */
33
+ refreshFromDb(): void;
34
+ /**
35
+ * Return all cached library items, optionally filtered by content type.
36
+ */
37
+ getItems(contentTypeId?: string): CachedLibraryItem[];
38
+ /**
39
+ * Return total count of cached items.
40
+ */
41
+ count(): number;
42
+ private onFileAdded;
43
+ private onRequestDeleted;
44
+ private rowToItem;
45
+ }
46
+ //# sourceMappingURL=library-cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"library-cache.d.ts","sourceRoot":"","sources":["../../src/library/library-cache.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAC3C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAc,MAAM,oBAAoB,CAAC;AAEvE;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,GAAG,EAAE,CAAC;IACd,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,EAAE,CAAoB;IAC9B,OAAO,CAAC,GAAG,CAAW;IACtB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,UAAU,CAAqB;IACvC,OAAO,CAAC,UAAU,CAAqB;IACvC,OAAO,CAAC,UAAU,CAAqB;IACvC,OAAO,CAAC,aAAa,CAAqB;IAC1C,OAAO,CAAC,SAAS,CAAqB;gBAE1B,EAAE,EAAE,QAAQ,CAAC,QAAQ,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM;IA0ChE;;OAEG;IACH,aAAa,IAAI,IAAI;IAuCrB;;OAEG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,iBAAiB,EAAE;IAQrD;;OAEG;IACH,KAAK,IAAI,MAAM;IAMf,OAAO,CAAC,WAAW;IAkDnB,OAAO,CAAC,gBAAgB;IAOxB,OAAO,CAAC,SAAS;CAgBlB"}