@psychout98/tadaima 1.0.3 → 1.0.11

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.
File without changes
@@ -13947,7 +13947,9 @@ var cacheCheckSchema = messageEnvelopeSchema.extend({
13947
13947
  });
13948
13948
  var downloadAcceptedPayloadSchema = external_exports.object({
13949
13949
  jobId: external_exports.string(),
13950
- requestId: external_exports.string()
13950
+ requestId: external_exports.string(),
13951
+ title: external_exports.string().optional(),
13952
+ mediaType: external_exports.enum(["movie", "tv"]).optional()
13951
13953
  });
13952
13954
  var downloadAcceptedSchema = messageEnvelopeSchema.extend({
13953
13955
  type: external_exports.literal("download:accepted"),
@@ -13957,6 +13959,8 @@ var downloadProgressPayloadSchema = external_exports.object({
13957
13959
  jobId: external_exports.string(),
13958
13960
  phase: external_exports.string(),
13959
13961
  progress: external_exports.number().min(0).max(100),
13962
+ title: external_exports.string().optional(),
13963
+ mediaType: external_exports.enum(["movie", "tv"]).optional(),
13960
13964
  downloadedBytes: external_exports.number().optional(),
13961
13965
  totalBytes: external_exports.number().optional(),
13962
13966
  speedBps: external_exports.number().optional(),
@@ -13969,6 +13973,7 @@ var downloadProgressSchema = messageEnvelopeSchema.extend({
13969
13973
  var downloadCompletedPayloadSchema = external_exports.object({
13970
13974
  jobId: external_exports.string(),
13971
13975
  filePath: external_exports.string(),
13976
+ filePaths: external_exports.array(external_exports.string()).optional(),
13972
13977
  finalSize: external_exports.number()
13973
13978
  });
13974
13979
  var downloadCompletedSchema = messageEnvelopeSchema.extend({
@@ -13997,7 +14002,9 @@ var downloadQueuedPayloadSchema = external_exports.object({
13997
14002
  queueId: external_exports.string(),
13998
14003
  requestId: external_exports.string(),
13999
14004
  title: external_exports.string(),
14000
- deviceName: external_exports.string()
14005
+ deviceName: external_exports.string(),
14006
+ mediaType: external_exports.enum(["movie", "tv"]).optional(),
14007
+ season: external_exports.number().optional()
14001
14008
  });
14002
14009
  var downloadQueuedSchema = messageEnvelopeSchema.extend({
14003
14010
  type: external_exports.literal("download:queued"),
File without changes
File without changes
File without changes
@@ -4,7 +4,7 @@ import {
4
4
  buildMoviePath,
5
5
  createMessageId,
6
6
  createTimestamp
7
- } from "./chunk-6O3GWKMO.js";
7
+ } from "./chunk-GVIX77PC.js";
8
8
  import {
9
9
  config
10
10
  } from "./chunk-7TPZ4T2V.js";
@@ -200,7 +200,7 @@ async function downloadFile(url, destPath, onProgress, signal, baseDir) {
200
200
 
201
201
  // src/organizer.ts
202
202
  import { mkdir as mkdir2, rename } from "fs/promises";
203
- import { dirname as dirname2, extname, join, resolve as resolve2 } from "path";
203
+ import { basename, dirname as dirname2, extname, join, resolve as resolve2 } from "path";
204
204
  function assertWithinBase(destPath, baseDir) {
205
205
  const resolvedDest = resolve2(destPath);
206
206
  const resolvedBase = resolve2(baseDir);
@@ -225,12 +225,24 @@ async function organizeFile(req) {
225
225
  return destPath;
226
226
  } else {
227
227
  const tvBase = config.get("directories.tv");
228
+ const filename = basename(req.sourcePath);
229
+ let episodeNum = req.episode;
230
+ let epTitle = req.episodeTitle;
231
+ if (episodeNum == null) {
232
+ const match = filename.match(/[Ss](\d{1,2})[Ee](\d{1,2})/);
233
+ if (match) {
234
+ episodeNum = parseInt(match[2], 10);
235
+ }
236
+ }
237
+ if (!epTitle && episodeNum != null) {
238
+ epTitle = `Episode ${episodeNum}`;
239
+ }
228
240
  relativePath = buildEpisodePath(
229
241
  req.title,
230
242
  req.tmdbId,
231
243
  req.season ?? 1,
232
- req.episode ?? 1,
233
- req.episodeTitle ?? `Episode ${req.episode ?? 1}`,
244
+ episodeNum ?? 1,
245
+ epTitle ?? `Episode ${episodeNum ?? 1}`,
234
246
  ext
235
247
  );
236
248
  const destPath = join(tvBase, relativePath.replace(/^TV\//, ""));
@@ -303,7 +315,12 @@ var DownloadHandler = class {
303
315
  };
304
316
  this.activeJobs.set(jobId, job);
305
317
  this.ws.setActiveJobs(this.activeJobs.size);
306
- this.sendMessage("download:accepted", { jobId, requestId });
318
+ this.sendMessage("download:accepted", {
319
+ jobId,
320
+ requestId,
321
+ title: meta.title,
322
+ mediaType: meta.mediaType
323
+ });
307
324
  try {
308
325
  await this.executeDownload(job);
309
326
  } catch (err) {
@@ -345,24 +362,24 @@ var DownloadHandler = class {
345
362
  const { meta, abortController } = job;
346
363
  const signal = abortController.signal;
347
364
  job.phase = "adding";
348
- this.sendProgress(job.jobId, "adding", 0);
365
+ this.sendProgress(job.jobId, "adding", 0, meta);
349
366
  console.log(`[${job.jobId}] Adding magnet to RD...`);
350
367
  const { id: torrentId } = await rdClient.addMagnet(meta.magnet);
351
368
  if (signal.aborted) throw new Error("Cancelled");
352
369
  await rdClient.selectFiles(torrentId);
353
370
  if (signal.aborted) throw new Error("Cancelled");
354
371
  job.phase = "waiting";
355
- this.sendProgress(job.jobId, "waiting", 0);
372
+ this.sendProgress(job.jobId, "waiting", 0, meta);
356
373
  console.log(`[${job.jobId}] Waiting for RD to process...`);
357
374
  const links = await rdClient.pollUntilReady(
358
375
  torrentId,
359
376
  void 0,
360
377
  void 0,
361
- (progress) => this.sendProgress(job.jobId, "waiting", progress),
378
+ (progress) => this.sendProgress(job.jobId, "waiting", progress, meta),
362
379
  signal
363
380
  );
364
381
  job.phase = "unrestricting";
365
- this.sendProgress(job.jobId, "unrestricting", 0);
382
+ this.sendProgress(job.jobId, "unrestricting", 0, meta);
366
383
  console.log(`[${job.jobId}] Unrestricting ${links.length} links...`);
367
384
  const unrestricted = await rdClient.unrestrictAll(links);
368
385
  if (signal.aborted) throw new Error("Cancelled");
@@ -384,6 +401,8 @@ var DownloadHandler = class {
384
401
  jobId: job.jobId,
385
402
  phase: "downloading",
386
403
  progress: pct,
404
+ title: meta.title,
405
+ mediaType: meta.mediaType,
387
406
  downloadedBytes: progress.downloadedBytes,
388
407
  totalBytes: progress.totalBytes,
389
408
  speedBps: progress.speedBps,
@@ -396,33 +415,38 @@ var DownloadHandler = class {
396
415
  totalSize += size;
397
416
  }
398
417
  job.phase = "organizing";
399
- this.sendProgress(job.jobId, "organizing", 0);
418
+ this.sendProgress(job.jobId, "organizing", 0, meta);
400
419
  console.log(`[${job.jobId}] Organizing files...`);
401
- let finalPath = "";
420
+ const organizedPaths = [];
402
421
  for (const filePath of downloadedFiles) {
403
- finalPath = await organizeFile({
422
+ const organized = await organizeFile({
404
423
  title: meta.title,
405
424
  year: meta.year,
406
425
  tmdbId: meta.tmdbId,
407
426
  mediaType: meta.mediaType,
408
427
  season: meta.season,
409
- episode: meta.episode,
410
- episodeTitle: meta.episodeTitle,
411
428
  sourcePath: filePath
412
429
  });
430
+ organizedPaths.push(organized);
413
431
  }
414
432
  await rm(join2(stagingDir, job.jobId), { recursive: true, force: true }).catch(() => {
415
433
  });
416
- console.log(`[${job.jobId}] Complete: ${finalPath}`);
434
+ console.log(`[${job.jobId}] Complete: ${organizedPaths.length} file(s)`);
417
435
  this.sendMessage("download:completed", {
418
436
  jobId: job.jobId,
419
- filePath: finalPath,
437
+ filePath: organizedPaths[organizedPaths.length - 1] ?? "",
438
+ filePaths: organizedPaths,
420
439
  finalSize: totalSize,
421
440
  _meta: meta
422
441
  });
423
442
  }
424
- sendProgress(jobId, phase, progress) {
425
- this.sendMessage("download:progress", { jobId, phase, progress });
443
+ sendProgress(jobId, phase, progress, meta) {
444
+ this.sendMessage("download:progress", {
445
+ jobId,
446
+ phase,
447
+ progress,
448
+ ...meta && { title: meta.title, mediaType: meta.mediaType }
449
+ });
426
450
  }
427
451
  sendMessage(type, payload) {
428
452
  this.ws.send({
package/dist/index.js CHANGED
@@ -1,13 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
- import { readFileSync } from "fs";
5
- import { fileURLToPath } from "url";
6
- import { dirname, join } from "path";
7
- var __dirname = dirname(fileURLToPath(import.meta.url));
8
- var pkg = JSON.parse(
9
- readFileSync(join(__dirname, "..", "package.json"), "utf-8")
10
- );
4
+ var pkg = { version: "1.0.11" };
11
5
  var args = process.argv.slice(2);
12
6
  var command = args[0];
13
7
  async function main() {
@@ -25,10 +19,10 @@ async function main() {
25
19
  break;
26
20
  }
27
21
  console.log(`tadaima-agent v${pkg.version}`);
28
- const { AgentWebSocket } = await import("./ws-client-TPWC6VZR.js");
29
- const { DownloadHandler } = await import("./download-handler-H34V5RNF.js");
22
+ const { AgentWebSocket } = await import("./ws-client-2MSPJRJ6.js");
23
+ const { DownloadHandler } = await import("./download-handler-3RP5BP6P.js");
30
24
  const { TUI } = await import("./tui-ZE4672PT.js");
31
- const { shouldCheckNow, checkForUpdate, applyUpdate, logUpdateAdvisory } = await import("./updater-HXBNBU36.js");
25
+ const { shouldCheckNow, checkForUpdate, applyUpdate, logUpdateAdvisory } = await import("./updater-LSMAQIFU.js");
32
26
  const { writeStatus, removeStatus } = await import("./status-writer-RGH2PULB.js");
33
27
  const ws = new AgentWebSocket();
34
28
  const handler = new DownloadHandler(ws);
@@ -206,13 +200,16 @@ async function main() {
206
200
  break;
207
201
  }
208
202
  case "update": {
209
- const { checkForUpdate, applyUpdate } = await import("./updater-HXBNBU36.js");
203
+ const { checkForUpdate, applyUpdate, logUpdateAdvisory } = await import("./updater-LSMAQIFU.js");
204
+ const isBinaryInstall = !process.argv[1]?.includes("node_modules");
210
205
  console.log(`Current version: v${pkg.version}`);
211
206
  console.log("Checking for updates...");
212
207
  try {
213
208
  const result = await checkForUpdate(pkg.version);
214
209
  if (!result) {
215
210
  console.log("Already up to date.");
211
+ } else if (!isBinaryInstall) {
212
+ logUpdateAdvisory(pkg.version, result.version);
216
213
  } else {
217
214
  console.log(`Update available: v${result.version}`);
218
215
  await applyUpdate(result);
@@ -227,7 +224,7 @@ async function main() {
227
224
  break;
228
225
  }
229
226
  case "rollback": {
230
- const { rollback } = await import("./updater-HXBNBU36.js");
227
+ const { rollback } = await import("./updater-LSMAQIFU.js");
231
228
  rollback();
232
229
  break;
233
230
  }
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  GITHUB_RELEASES_API,
4
4
  getAssetNameForPlatform
5
- } from "./chunk-6O3GWKMO.js";
5
+ } from "./chunk-GVIX77PC.js";
6
6
  import {
7
7
  config
8
8
  } from "./chunk-7TPZ4T2V.js";
@@ -139,7 +139,7 @@ function rollback() {
139
139
  function logUpdateAdvisory(currentVersion, latestVersion) {
140
140
  console.log(`
141
141
  Tadaima v${latestVersion} is available (you have v${currentVersion}).`);
142
- console.log(" npm: npm update -g @tadaima/agent");
142
+ console.log(" npm: npm install -g @psychout98/tadaima@latest");
143
143
  console.log(" Docker: docker compose pull && docker compose up -d\n");
144
144
  }
145
145
  function isNewer(latest, current) {
@@ -2,16 +2,25 @@
2
2
  import {
3
3
  createMessageId,
4
4
  createTimestamp
5
- } from "./chunk-6O3GWKMO.js";
5
+ } from "./chunk-GVIX77PC.js";
6
6
  import {
7
7
  config
8
8
  } from "./chunk-7TPZ4T2V.js";
9
9
 
10
10
  // src/ws-client.ts
11
11
  import WebSocket from "ws";
12
- import { platform, freemem } from "os";
12
+ import { platform } from "os";
13
+ import { statfs } from "fs/promises";
13
14
  var HEARTBEAT_INTERVAL = 3e4;
14
15
  var MAX_BACKOFF = 3e4;
16
+ async function getDiskFreeBytes(dirPath) {
17
+ try {
18
+ const stats = await statfs(dirPath);
19
+ return stats.bavail * stats.bsize;
20
+ } catch {
21
+ return 0;
22
+ }
23
+ }
15
24
  var AgentWebSocket = class {
16
25
  ws = null;
17
26
  backoff = 1e3;
@@ -22,6 +31,10 @@ var AgentWebSocket = class {
22
31
  stopped = false;
23
32
  startTime = Date.now();
24
33
  activeJobs = 0;
34
+ mediaDir;
35
+ constructor() {
36
+ this.mediaDir = config.get("directories.movies") || config.get("directories.tv") || "/";
37
+ }
25
38
  connect() {
26
39
  this.stopped = false;
27
40
  const relayUrl = config.get("relay");
@@ -36,7 +49,8 @@ var AgentWebSocket = class {
36
49
  this.ws.on("open", () => {
37
50
  console.log("Connected to relay");
38
51
  this.backoff = 1e3;
39
- this.sendHello();
52
+ this.sendHello().catch(() => {
53
+ });
40
54
  this.startHeartbeat();
41
55
  this.drainQueue();
42
56
  });
@@ -91,7 +105,7 @@ var AgentWebSocket = class {
91
105
  setActiveJobs(count) {
92
106
  this.activeJobs = count;
93
107
  }
94
- sendHello() {
108
+ async sendHello() {
95
109
  this.send({
96
110
  id: createMessageId(),
97
111
  type: "agent:hello",
@@ -100,18 +114,18 @@ var AgentWebSocket = class {
100
114
  version: "0.0.0",
101
115
  platform: platform(),
102
116
  activeJobs: this.activeJobs,
103
- diskFreeBytes: freemem()
117
+ diskFreeBytes: await getDiskFreeBytes(this.mediaDir)
104
118
  }
105
119
  });
106
120
  }
107
- sendHeartbeat() {
121
+ async sendHeartbeat() {
108
122
  this.send({
109
123
  id: createMessageId(),
110
124
  type: "agent:heartbeat",
111
125
  timestamp: createTimestamp(),
112
126
  payload: {
113
127
  activeJobs: this.activeJobs,
114
- diskFreeBytes: freemem(),
128
+ diskFreeBytes: await getDiskFreeBytes(this.mediaDir),
115
129
  uptimeSeconds: Math.floor((Date.now() - this.startTime) / 1e3)
116
130
  }
117
131
  });
@@ -119,7 +133,10 @@ var AgentWebSocket = class {
119
133
  startHeartbeat() {
120
134
  this.stopHeartbeat();
121
135
  this.heartbeatTimer = setInterval(
122
- () => this.sendHeartbeat(),
136
+ () => {
137
+ this.sendHeartbeat().catch(() => {
138
+ });
139
+ },
123
140
  HEARTBEAT_INTERVAL
124
141
  );
125
142
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@psychout98/tadaima",
3
- "version": "1.0.3",
3
+ "version": "1.0.11",
4
4
  "description": "CLI download agent for Tadaima — a self-hosted media download orchestrator",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -37,21 +37,6 @@
37
37
  "publishConfig": {
38
38
  "access": "public"
39
39
  },
40
- "dependencies": {
41
- "conf": "^15.1.0",
42
- "prompts": "^2.4.2",
43
- "ws": "^8.20.0"
44
- },
45
- "devDependencies": {
46
- "@types/node": "^25.5.2",
47
- "@types/prompts": "^2.4.9",
48
- "@types/ws": "^8.18.1",
49
- "tsup": "^8.0.0",
50
- "tsx": "^4.19.0",
51
- "typescript": "^5.7.0",
52
- "vitest": "^4.1.2",
53
- "@tadaima/shared": "0.0.0"
54
- },
55
40
  "scripts": {
56
41
  "build": "tsup",
57
42
  "dev": "tsx watch src/index.ts",
@@ -66,5 +51,19 @@
66
51
  "compile:linux-x64": "bun build src/index.ts --compile --target=bun-linux-x64 --outfile=dist/tadaima-agent-linux-x64",
67
52
  "compile:all": "pnpm run compile:darwin-arm64 && pnpm run compile:darwin-x64 && pnpm run compile:win-x64 && pnpm run compile:linux-x64",
68
53
  "compile:checksums": "cd dist && shasum -a 256 tadaima-agent-* > checksums.sha256"
54
+ },
55
+ "dependencies": {
56
+ "conf": "^15.1.0",
57
+ "prompts": "^2.4.2",
58
+ "ws": "^8.20.0"
59
+ },
60
+ "devDependencies": {
61
+ "@types/node": "^25.5.2",
62
+ "@types/prompts": "^2.4.9",
63
+ "@types/ws": "^8.18.1",
64
+ "tsup": "^8.0.0",
65
+ "tsx": "^4.19.0",
66
+ "typescript": "^5.7.0",
67
+ "vitest": "^4.1.2"
69
68
  }
70
- }
69
+ }
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 Tadaima Contributors
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.