@psychout98/tadaima 1.0.3 → 1.0.4

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.
@@ -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
@@ -25,8 +25,8 @@ async function main() {
25
25
  break;
26
26
  }
27
27
  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");
28
+ const { AgentWebSocket } = await import("./ws-client-CLD6QAFC.js");
29
+ const { DownloadHandler } = await import("./download-handler-R6AJCG4O.js");
30
30
  const { TUI } = await import("./tui-ZE4672PT.js");
31
31
  const { shouldCheckNow, checkForUpdate, applyUpdate, logUpdateAdvisory } = await import("./updater-HXBNBU36.js");
32
32
  const { writeStatus, removeStatus } = await import("./status-writer-RGH2PULB.js");
@@ -9,9 +9,18 @@ import {
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.4",
4
4
  "description": "CLI download agent for Tadaima — a self-hosted media download orchestrator",
5
5
  "type": "module",
6
6
  "license": "MIT",