@adbjs/sdk 0.1.1 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -66,30 +66,35 @@ var BaseResource = class {
66
66
  }
67
67
  /**
68
68
  * Make a GET request
69
- * @param T - The generated response type (e.g., GetLinkUnlockResponse)
69
+ * @template T - The generated response type (e.g., GetLinkUnlockResponse)
70
+ * @param path - API endpoint path
71
+ * @param params - Optional query parameters
70
72
  * @returns The extracted data from the response (without the { status, data } wrapper)
71
73
  */
72
74
  async get(path, params) {
73
- const clientAny = this.client;
74
- return clientAny.get(path, params);
75
+ return this.client.get(path, params);
75
76
  }
76
77
  /**
77
78
  * Make a POST request
78
- * @param T - The generated response type
79
+ * @template T - The generated response type
80
+ * @param path - API endpoint path
81
+ * @param body - Request body
82
+ * @param params - Optional query parameters
79
83
  * @returns The extracted data from the response (without the { status, data } wrapper)
80
84
  */
81
85
  async post(path, body, params) {
82
- const clientAny = this.client;
83
- return clientAny.post(path, body, params);
86
+ return this.client.post(path, body, params);
84
87
  }
85
88
  /**
86
89
  * Make a POST request with FormData (multipart/form-data)
87
- * @param T - The generated response type
90
+ * @template T - The generated response type
91
+ * @param path - API endpoint path
92
+ * @param formData - Form data to send
93
+ * @param params - Optional query parameters
88
94
  * @returns The extracted data from the response (without the { status, data } wrapper)
89
95
  */
90
96
  async postFormData(path, formData, params) {
91
- const clientAny = this.client;
92
- return clientAny.postFormData(path, formData, params);
97
+ return this.client.postFormData(path, formData, params);
93
98
  }
94
99
  };
95
100
 
@@ -144,19 +149,24 @@ var LinkResource = class extends BaseResource {
144
149
  * Get information about one or more links
145
150
  *
146
151
  * @param links - Single link or array of links to get info for
152
+ * @param password - Optional password for password-protected links
147
153
  *
148
154
  * @example
149
155
  * ```ts
150
- * const info = await client.link.infos('https://example.com/file.zip')
151
- * console.log(info)
156
+ * const data = await client.link.infos('https://example.com/file.zip')
157
+ * console.log(data.infos)
158
+ *
159
+ * // With password
160
+ * const protectedData = await client.link.infos(
161
+ * 'https://example.com/protected.zip',
162
+ * 'mypassword'
163
+ * )
152
164
  * ```
153
165
  */
154
- async infos(links) {
166
+ async infos(links, password) {
155
167
  const linksArray = Array.isArray(links) ? links : [links];
156
- const data = await this.get("/link/infos", {
157
- link: linksArray
158
- });
159
- return data?.infos;
168
+ const params = password ? { password } : void 0;
169
+ return this.post("/link/infos", { "link[]": linksArray }, params);
160
170
  }
161
171
  /**
162
172
  * Extract links from redirectors/link protectors
@@ -165,19 +175,20 @@ var LinkResource = class extends BaseResource {
165
175
  *
166
176
  * @example
167
177
  * ```ts
168
- * const links = await client.link.redirector('https://linkprotector.com/abc123')
178
+ * const data = await client.link.redirector('https://linkprotector.com/abc123')
179
+ * console.log(data.links)
169
180
  * ```
170
181
  */
171
182
  async redirector(link) {
172
- const data = await this.get("/link/redirector", {
183
+ return this.post("/link/redirector", {
173
184
  link
174
185
  });
175
- return data?.links;
176
186
  }
177
187
  /**
178
188
  * Unlock a download link
179
189
  *
180
190
  * @param link - The link to unlock
191
+ * @param password - Optional password for password-protected links
181
192
  *
182
193
  * @example
183
194
  * ```ts
@@ -188,12 +199,17 @@ var LinkResource = class extends BaseResource {
188
199
  * // Handle delayed generation
189
200
  * const delayedResult = await client.link.delayed(result.delayed)
190
201
  * }
202
+ *
203
+ * // With password
204
+ * const protectedResult = await client.link.unlock(
205
+ * 'https://example.com/protected.zip',
206
+ * 'mypassword'
207
+ * )
191
208
  * ```
192
209
  */
193
- async unlock(link) {
194
- return this.get("/link/unlock", {
195
- link
196
- });
210
+ async unlock(link, password) {
211
+ const params = password ? { password } : void 0;
212
+ return this.post("/link/unlock", { link }, params);
197
213
  }
198
214
  /**
199
215
  * Unlock a link and automatically poll if delayed
@@ -222,9 +238,10 @@ var LinkResource = class extends BaseResource {
222
238
  * const streams = await client.link.streaming('abc123')
223
239
  * ```
224
240
  */
225
- async streaming(id) {
226
- return this.get("/link/streaming", {
227
- id
241
+ async streaming(id, stream) {
242
+ return this.post("/link/streaming", {
243
+ id,
244
+ stream
228
245
  });
229
246
  }
230
247
  /**
@@ -238,7 +255,7 @@ var LinkResource = class extends BaseResource {
238
255
  * ```
239
256
  */
240
257
  async delayed(id) {
241
- return this.get("/link/delayed", {
258
+ return this.post("/link/delayed", {
242
259
  id
243
260
  });
244
261
  }
@@ -259,7 +276,7 @@ var MagnetResource = class extends BaseResource {
259
276
  */
260
277
  async upload(magnets) {
261
278
  const magnetsArray = Array.isArray(magnets) ? magnets : [magnets];
262
- return this.get("/magnet/upload", {
279
+ return this.post("/magnet/upload", {
263
280
  magnets: magnetsArray
264
281
  });
265
282
  }
@@ -286,23 +303,22 @@ var MagnetResource = class extends BaseResource {
286
303
  return this.postFormData("/magnet/upload/file", formData);
287
304
  }
288
305
  /**
289
- * Get the status of one or more magnets
306
+ * Get the status of a specific magnet by ID
290
307
  *
291
- * @param id - Optional magnet ID to get status for a specific magnet
308
+ * @param id - Magnet ID to get status for
292
309
  *
293
310
  * @example
294
311
  * ```ts
295
- * // Get all magnets status
296
- * const allStatus = await client.magnet.status()
297
- *
298
- * // Get specific magnet status
299
- * const status = await client.magnet.status('123')
312
+ * const status = await client.magnet.status(123)
300
313
  * console.log(status.magnets[0]?.status) // 'Downloading', 'Ready', etc.
301
314
  * ```
315
+ *
316
+ * @remarks
317
+ * This endpoint uses AllDebrid API v4.1 (POST method)
318
+ * Migrated from v4 GET endpoint which was deprecated on 2024-10-16
302
319
  */
303
320
  async status(id) {
304
- const params = id ? { id } : void 0;
305
- const data = await this.get("/magnet/status", params);
321
+ const data = await this.post("/magnet/status", { id });
306
322
  if (data?.magnets && !Array.isArray(data.magnets)) {
307
323
  return {
308
324
  ...data,
@@ -311,71 +327,215 @@ var MagnetResource = class extends BaseResource {
311
327
  }
312
328
  return data;
313
329
  }
330
+ /**
331
+ * Get list of magnets with optional status filter
332
+ *
333
+ * @param statusFilter - Optional filter by status: 'active', 'ready', 'expired', or 'error'
334
+ *
335
+ * @example
336
+ * ```ts
337
+ * // Get all magnets
338
+ * const allMagnets = await client.magnet.statusList()
339
+ *
340
+ * // Get only active magnets
341
+ * const activeMagnets = await client.magnet.statusList('active')
342
+ *
343
+ * // Get only ready magnets
344
+ * const readyMagnets = await client.magnet.statusList('ready')
345
+ * ```
346
+ *
347
+ * @remarks
348
+ * This endpoint uses AllDebrid API v4.1 (POST method)
349
+ */
350
+ async statusList(statusFilter) {
351
+ const body = statusFilter ? { status: statusFilter } : void 0;
352
+ return this.post("/magnet/status", body);
353
+ }
354
+ /**
355
+ * Get magnet status using live mode for bandwidth-optimized delta synchronization
356
+ *
357
+ * Live mode is designed for monitoring multiple magnets efficiently by only transmitting
358
+ * changes between polling intervals, drastically reducing bandwidth usage for dashboards
359
+ * and real-time monitoring applications.
360
+ *
361
+ * ## How it works
362
+ *
363
+ * 1. **Session initialization**: Generate a random session ID and start with counter = 0
364
+ * 2. **First call (fullsync)**: Returns ALL magnets with `fullsync: true`
365
+ * 3. **Update counter**: Use the `counter` value returned by the API for the next call
366
+ * 4. **Subsequent calls (delta)**: Returns ONLY magnets that changed since last call
367
+ * 5. **Repeat**: Keep calling with updated counter to receive only deltas
368
+ *
369
+ * ## When to use
370
+ *
371
+ * - ✅ Monitoring multiple active magnets simultaneously
372
+ * - ✅ Building real-time dashboards
373
+ * - ✅ High-frequency polling scenarios (every few seconds)
374
+ * - ❌ Watching a single specific magnet (use `watch()` instead)
375
+ *
376
+ * ## Important notes
377
+ *
378
+ * - **Don't use the `id` parameter**: Passing an ID defeats the purpose of live mode
379
+ * as it disables delta sync and behaves like a regular `status()` call
380
+ * - **Session persistence**: Keep the same session ID for the entire monitoring session
381
+ * - **Counter tracking**: Always update the counter with the value returned by the API
382
+ * - **Empty deltas**: When no magnets changed, `magnets` will be an empty array
383
+ *
384
+ * @param options - Live mode session options
385
+ * @param options.session - Unique session ID (generate once: `Math.floor(Math.random() * 1000000)`)
386
+ * @param options.counter - Sync counter (start at 0, then use value from previous API response)
387
+ *
388
+ * @example
389
+ * ```ts
390
+ * // Initialize session
391
+ * const session = Math.floor(Math.random() * 1000000)
392
+ * let counter = 0
393
+ *
394
+ * // First call - returns all magnets (fullsync: true)
395
+ * const firstCall = await client.magnet.statusLive({ session, counter })
396
+ * console.log('Full sync:', firstCall.fullsync) // true
397
+ * console.log('All magnets:', firstCall.magnets) // Array of all magnets
398
+ * counter = firstCall.counter // Update counter for next call
399
+ *
400
+ * // Second call - returns only magnets that changed
401
+ * await new Promise(resolve => setTimeout(resolve, 3000)) // Wait 3 seconds
402
+ * const secondCall = await client.magnet.statusLive({ session, counter })
403
+ * console.log('Delta sync:', secondCall.magnets) // Only changed magnets
404
+ * counter = secondCall.counter
405
+ *
406
+ * // Example: Monitor all magnets until none are active
407
+ * const activeMagnets = new Map()
408
+ *
409
+ * while (true) {
410
+ * const response = await client.magnet.statusLive({ session, counter })
411
+ * counter = response.counter ?? counter
412
+ *
413
+ * // Update our local state with changes
414
+ * if (response.fullsync) {
415
+ * activeMagnets.clear()
416
+ * response.magnets?.forEach(m => activeMagnets.set(m.id, m))
417
+ * } else {
418
+ * response.magnets?.forEach(m => {
419
+ * if (m.status === 'Ready' || m.status === 'Error' || m.status === 'Expired') {
420
+ * activeMagnets.delete(m.id)
421
+ * } else {
422
+ * activeMagnets.set(m.id, m)
423
+ * }
424
+ * })
425
+ * }
426
+ *
427
+ * // Display current state
428
+ * console.log(`Active downloads: ${activeMagnets.size}`)
429
+ * activeMagnets.forEach(m => {
430
+ * console.log(` ${m.filename}: ${m.status} - ${m.downloaded}/${m.size} bytes`)
431
+ * })
432
+ *
433
+ * // Stop when no more active magnets
434
+ * if (activeMagnets.size === 0) {
435
+ * console.log('All downloads completed!')
436
+ * break
437
+ * }
438
+ *
439
+ * await new Promise(resolve => setTimeout(resolve, 3000))
440
+ * }
441
+ * ```
442
+ *
443
+ * @remarks
444
+ * This method is ideal for scenarios where you're monitoring multiple magnets and want
445
+ * to minimize bandwidth. For simple single-magnet monitoring, use `watch()` instead.
446
+ */
447
+ async statusLive(options) {
448
+ const body = {
449
+ session: options.session,
450
+ counter: options.counter
451
+ };
452
+ return this.post("/magnet/status", body);
453
+ }
314
454
  /**
315
455
  * Delete a magnet
316
456
  *
317
- * @param id - The magnet ID to delete (string)
457
+ * @param id - The magnet ID to delete
318
458
  *
319
459
  * @example
320
460
  * ```ts
321
- * await client.magnet.delete('123')
461
+ * await client.magnet.delete(123)
322
462
  * ```
323
463
  */
324
464
  async delete(id) {
325
- return this.get("/magnet/delete", {
465
+ return this.post("/magnet/delete", {
326
466
  id
327
467
  });
328
468
  }
329
469
  /**
330
470
  * Restart one or more failed magnets
331
471
  *
332
- * @param ids - Single magnet ID or array of magnet IDs to restart (strings)
472
+ * @param ids - Single magnet ID or array of magnet IDs to restart (numbers)
333
473
  *
334
474
  * @example
335
475
  * ```ts
336
476
  * // Restart single magnet
337
- * await client.magnet.restart('123')
477
+ * await client.magnet.restart(123)
338
478
  *
339
479
  * // Restart multiple magnets
340
- * await client.magnet.restart(['123', '456'])
480
+ * await client.magnet.restart([123, 456])
341
481
  * ```
342
482
  */
343
483
  async restart(ids) {
344
484
  const idsArray = Array.isArray(ids) ? ids : [ids];
345
- return this.get("/magnet/restart", {
485
+ return this.post("/magnet/restart", {
346
486
  ids: idsArray
347
487
  });
348
488
  }
349
489
  /**
350
- * Check instant availability of magnets
490
+ * Get files for a completed magnet
351
491
  *
352
- * @param magnets - Single magnet hash or array of hashes
492
+ * @param ids - The magnet ID or IDs to get files for
353
493
  *
354
494
  * @example
355
495
  * ```ts
356
- * const available = await client.magnet.instant(['hash1', 'hash2'])
357
- * console.log(available.magnets)
496
+ * const data = await client.magnet.files(123)
497
+ * data?.magnets?.forEach(magnet => {
498
+ * console.log(magnet.filename, magnet.files)
499
+ * })
358
500
  * ```
501
+ *
502
+ * @remarks
503
+ * Files are now retrieved separately from magnet status (since v4.1)
504
+ * Only available for magnets with status 'Ready'
359
505
  */
360
- async instant(magnets) {
361
- const magnetsArray = Array.isArray(magnets) ? magnets : [magnets];
362
- return this.get("/magnet/instant", {
363
- magnets: magnetsArray
364
- });
506
+ async files(ids) {
507
+ const formData = new FormData();
508
+ const idsArray = Array.isArray(ids) ? ids : [ids];
509
+ for (const id of idsArray) {
510
+ formData.append("id[]", String(id));
511
+ }
512
+ return this.postFormData("/magnet/files", formData);
365
513
  }
366
514
  /**
367
515
  * Watch a magnet's status with automatic polling
368
516
  *
369
- * @param id - The magnet ID to watch (string)
517
+ * This is a simple helper that polls the status of a specific magnet until it reaches
518
+ * a target status (default: 'Ready'). For advanced use cases with multiple magnets
519
+ * or bandwidth optimization, use `statusLive()` directly instead.
520
+ *
521
+ * @param id - The magnet ID to watch
370
522
  * @param options - Watch options
523
+ * @param options.interval - Polling interval in milliseconds (default: 3000)
524
+ * @param options.maxAttempts - Maximum polling attempts, 0 for infinite (default: 0)
525
+ * @param options.onUpdate - Callback called on each status update
526
+ * @param options.stopOnStatus - Stop when magnet reaches this status (default: 'Ready')
371
527
  *
372
528
  * @example
373
529
  * ```ts
374
- * await client.magnet.watch('123', {
530
+ * await client.magnet.watch(123, {
375
531
  * onUpdate: (status) => console.log('Status:', status.magnets[0]?.status),
376
532
  * stopOnStatus: 'Ready'
377
533
  * })
378
534
  * ```
535
+ *
536
+ * @remarks
537
+ * For monitoring multiple magnets efficiently, use `statusLive()` directly.
538
+ * See the `statusLive()` documentation for details on delta synchronization.
379
539
  */
380
540
  async watch(id, options = {}) {
381
541
  const { interval = 3e3, maxAttempts = 0, onUpdate, stopOnStatus = "Ready" } = options;
@@ -396,6 +556,111 @@ var MagnetResource = class extends BaseResource {
396
556
  }
397
557
  };
398
558
 
559
+ // src/resources/pin.ts
560
+ var PinResource = class extends BaseResource {
561
+ /**
562
+ * Generate a new PIN code for authentication
563
+ *
564
+ * This initiates the PIN authentication flow. The user should visit the
565
+ * returned URL to authorize the application.
566
+ *
567
+ * @example
568
+ * ```ts
569
+ * const pinData = await client.pin.generate()
570
+ * console.log('Visit:', pinData.user_url)
571
+ * console.log('PIN:', pinData.pin)
572
+ *
573
+ * // Poll the check endpoint until user authorizes
574
+ * const auth = await client.pin.check(pinData.check, pinData.pin)
575
+ * if (auth.activated) {
576
+ * console.log('API Key:', auth.apikey)
577
+ * }
578
+ * ```
579
+ *
580
+ * @returns PIN code and authorization URL
581
+ */
582
+ async generate() {
583
+ return super.get("/pin/get");
584
+ }
585
+ /**
586
+ * Check the status of a PIN authentication
587
+ *
588
+ * Poll this endpoint to check if the user has authorized the application.
589
+ * Once authorized, the response will include the API key.
590
+ *
591
+ * @param check - Check ID from /pin/get
592
+ * @param pin - PIN code from /pin/get
593
+ *
594
+ * @example
595
+ * ```ts
596
+ * const pinData = await client.pin.generate()
597
+ *
598
+ * // Poll every few seconds until activated
599
+ * const checkStatus = async () => {
600
+ * const result = await client.pin.check(pinData.check, pinData.pin)
601
+ * if (result?.activated && result?.apikey) {
602
+ * console.log('Authorized! API Key:', result.apikey)
603
+ * return result.apikey
604
+ * }
605
+ * // Wait and try again
606
+ * await new Promise(resolve => setTimeout(resolve, 3000))
607
+ * return checkStatus()
608
+ * }
609
+ *
610
+ * const apikey = await checkStatus()
611
+ * ```
612
+ *
613
+ * @returns Authorization status and API key (if activated)
614
+ */
615
+ async check(check, pin) {
616
+ return super.get("/pin/check", { check, pin });
617
+ }
618
+ /**
619
+ * Helper method to wait for PIN authorization with automatic polling
620
+ *
621
+ * This method handles the polling logic for you, making it easier to
622
+ * implement the PIN authentication flow.
623
+ *
624
+ * @param check - Check ID from /pin/get
625
+ * @param pin - PIN code from /pin/get
626
+ * @param options - Polling options
627
+ * @param options.timeout - Maximum time to wait in milliseconds (default: 600000 = 10 minutes)
628
+ * @param options.interval - Polling interval in milliseconds (default: 3000 = 3 seconds)
629
+ *
630
+ * @example
631
+ * ```ts
632
+ * const pinData = await client.pin.generate()
633
+ * console.log('Visit:', pinData.user_url)
634
+ *
635
+ * try {
636
+ * const apikey = await client.pin.waitForAuth(pinData.check, pinData.pin, {
637
+ * timeout: 600000, // 10 minutes
638
+ * interval: 3000, // Check every 3 seconds
639
+ * })
640
+ * console.log('Authorized! API Key:', apikey)
641
+ * } catch (error) {
642
+ * console.error('Authorization timed out or failed')
643
+ * }
644
+ * ```
645
+ *
646
+ * @returns The API key once authorized
647
+ * @throws Error if timeout is reached or authorization fails
648
+ */
649
+ async waitForAuth(check, pin, options = {}) {
650
+ const timeout = options.timeout ?? 6e5;
651
+ const interval = options.interval ?? 3e3;
652
+ const startTime = Date.now();
653
+ while (Date.now() - startTime < timeout) {
654
+ const result = await this.check(check, pin);
655
+ if (result?.activated && result?.apikey) {
656
+ return result.apikey;
657
+ }
658
+ await new Promise((resolve) => setTimeout(resolve, interval));
659
+ }
660
+ throw new Error("PIN authorization timeout - user did not authorize within the time limit");
661
+ }
662
+ };
663
+
399
664
  // src/resources/user.ts
400
665
  var UserResource = class extends BaseResource {
401
666
  /**
@@ -403,13 +668,12 @@ var UserResource = class extends BaseResource {
403
668
  *
404
669
  * @example
405
670
  * ```ts
406
- * const user = await client.user.getInfo()
407
- * console.log(user.username, user.isPremium)
671
+ * const data = await client.user.getInfo()
672
+ * console.log(data.user.username, data.user.isPremium)
408
673
  * ```
409
674
  */
410
675
  async getInfo() {
411
- const data = await this.get("/user");
412
- return data?.user;
676
+ return this.get("/user");
413
677
  }
414
678
  /**
415
679
  * Get available hosts for the current user based on their subscription
@@ -437,7 +701,7 @@ var UserResource = class extends BaseResource {
437
701
  * ```
438
702
  */
439
703
  async clearNotification(code) {
440
- await this.get("/user/notification/clear", { code });
704
+ await this.post("/user/notification/clear", { code });
441
705
  }
442
706
  // ============================================
443
707
  // Saved Links Management
@@ -455,30 +719,60 @@ var UserResource = class extends BaseResource {
455
719
  return this.get("/user/links");
456
720
  }
457
721
  /**
458
- * Save a link for later use
722
+ * Save one or multiple links for later use
459
723
  *
460
- * @param link - The link to save
724
+ * Supports batch operations - you can save multiple links in a single request.
725
+ *
726
+ * @param links - Single link or array of links to save
461
727
  *
462
728
  * @example
463
729
  * ```ts
730
+ * // Save single link
464
731
  * await client.user.saveLink('https://example.com/file.zip')
732
+ *
733
+ * // Save multiple links at once
734
+ * await client.user.saveLink([
735
+ * 'https://example.com/file1.zip',
736
+ * 'https://example.com/file2.zip',
737
+ * 'https://example.com/file3.zip'
738
+ * ])
465
739
  * ```
466
740
  */
467
- async saveLink(link) {
468
- return this.get("/user/links/save", { link });
741
+ async saveLink(links) {
742
+ const linksArray = Array.isArray(links) ? links : [links];
743
+ const formData = new FormData();
744
+ for (const link of linksArray) {
745
+ formData.append("links[]", link);
746
+ }
747
+ return this.postFormData("/user/links/save", formData);
469
748
  }
470
749
  /**
471
- * Delete a saved link
750
+ * Delete one or multiple saved links
472
751
  *
473
- * @param link - The link to delete (can be the saved link ID or URL)
752
+ * Supports batch operations - you can delete multiple links in a single request.
753
+ *
754
+ * @param links - Single link or array of links to delete (can be saved link IDs or URLs)
474
755
  *
475
756
  * @example
476
757
  * ```ts
758
+ * // Delete single link
477
759
  * await client.user.deleteLink('saved-link-id')
760
+ *
761
+ * // Delete multiple links at once
762
+ * await client.user.deleteLink([
763
+ * 'saved-link-id-1',
764
+ * 'saved-link-id-2',
765
+ * 'saved-link-id-3'
766
+ * ])
478
767
  * ```
479
768
  */
480
- async deleteLink(link) {
481
- return this.get("/user/links/delete", { link });
769
+ async deleteLink(links) {
770
+ const linksArray = Array.isArray(links) ? links : [links];
771
+ const formData = new FormData();
772
+ for (const link of linksArray) {
773
+ formData.append("links[]", link);
774
+ }
775
+ return this.postFormData("/user/links/delete", formData);
482
776
  }
483
777
  // ============================================
484
778
  // History Management
@@ -504,12 +798,114 @@ var UserResource = class extends BaseResource {
504
798
  * ```
505
799
  */
506
800
  async clearHistory() {
507
- return this.get("/user/history/delete");
801
+ return this.post("/user/history/delete");
802
+ }
803
+ // ============================================
804
+ // Email Verification
805
+ // ============================================
806
+ /**
807
+ * Check email verification status
808
+ *
809
+ * @param token - Verification token
810
+ *
811
+ * @example
812
+ * ```ts
813
+ * const status = await client.user.getVerificationStatus('verification-token')
814
+ * console.log(status.verif) // 'waiting', 'allowed', or 'denied'
815
+ * ```
816
+ */
817
+ async getVerificationStatus(token) {
818
+ return this.post("/user/verif", void 0, { token });
819
+ }
820
+ /**
821
+ * Resend verification email
822
+ *
823
+ * @param token - Verification token
824
+ *
825
+ * @example
826
+ * ```ts
827
+ * await client.user.resendVerification('verification-token')
828
+ * ```
829
+ */
830
+ async resendVerification(token) {
831
+ return this.post("/user/verif/resend", void 0, { token });
832
+ }
833
+ };
834
+
835
+ // src/resources/voucher.ts
836
+ var VoucherResource = class extends BaseResource {
837
+ /**
838
+ * Get voucher balance for reseller accounts
839
+ *
840
+ * This endpoint allows resellers to check their remaining voucher balance.
841
+ * Only available for accounts with reseller privileges.
842
+ *
843
+ * @example
844
+ * ```ts
845
+ * const balance = await client.voucher.getBalance()
846
+ * console.log('Remaining balance:', balance.balance, '€')
847
+ * ```
848
+ *
849
+ * @returns Voucher balance information
850
+ */
851
+ async getBalance() {
852
+ return this.get("/voucher/balance");
853
+ }
854
+ /**
855
+ * Retrieve existing vouchers from reseller inventory
856
+ *
857
+ * This endpoint retrieves vouchers that were previously generated
858
+ * and are available in your inventory.
859
+ *
860
+ * @param quantity - Optional number of vouchers to retrieve
861
+ *
862
+ * @example
863
+ * ```ts
864
+ * // Get all available vouchers
865
+ * const allVouchers = await client.voucher.getVouchers()
866
+ * console.log('Vouchers:', allVouchers?.codes)
867
+ *
868
+ * // Get specific quantity
869
+ * const fiveVouchers = await client.voucher.getVouchers(5)
870
+ * ```
871
+ *
872
+ * @returns List of voucher codes
873
+ */
874
+ async getVouchers(quantity) {
875
+ const params = quantity ? { quantity } : void 0;
876
+ return this.get("/voucher/get", params);
877
+ }
878
+ /**
879
+ * Generate new vouchers (deducts from reseller balance)
880
+ *
881
+ * This endpoint creates new vouchers and deducts the cost from your
882
+ * reseller account balance.
883
+ *
884
+ * @param quantity - Number of vouchers to generate
885
+ * @param duration - Voucher duration in days
886
+ *
887
+ * @example
888
+ * ```ts
889
+ * // Generate 10 vouchers valid for 30 days
890
+ * const vouchers = await client.voucher.generateVouchers(10, 30)
891
+ * console.log('Generated vouchers:', vouchers?.codes)
892
+ *
893
+ * // Generate 5 vouchers valid for 7 days
894
+ * const weekVouchers = await client.voucher.generateVouchers(5, 7)
895
+ * ```
896
+ *
897
+ * @returns List of newly generated voucher codes
898
+ */
899
+ async generateVouchers(quantity, duration) {
900
+ return this.post("/voucher/generate", {
901
+ quantity,
902
+ duration
903
+ });
508
904
  }
509
905
  };
510
906
 
511
907
  // src/client.ts
512
- var DEFAULT_BASE_URL = "https://api.alldebrid.com/v4";
908
+ var DEFAULT_BASE_URL = "https://api.alldebrid.com/v4.1";
513
909
  var DEFAULT_AGENT = "@alldebrid/sdk";
514
910
  var DEFAULT_TIMEOUT = 3e4;
515
911
  var DEFAULT_MAX_RETRIES = 3;
@@ -531,6 +927,14 @@ var AllDebridClient = class {
531
927
  * Host resource for getting information about supported hosts
532
928
  */
533
929
  host;
930
+ /**
931
+ * Pin resource for PIN-based authentication
932
+ */
933
+ pin;
934
+ /**
935
+ * Voucher resource for reseller voucher management
936
+ */
937
+ voucher;
534
938
  constructor(config) {
535
939
  this.config = {
536
940
  apiKey: config.apiKey,
@@ -544,6 +948,8 @@ var AllDebridClient = class {
544
948
  this.link = new LinkResource(this);
545
949
  this.magnet = new MagnetResource(this);
546
950
  this.host = new HostResource(this);
951
+ this.pin = new PinResource(this);
952
+ this.voucher = new VoucherResource(this);
547
953
  }
548
954
  /**
549
955
  * Build query string from params
@@ -568,8 +974,11 @@ var AllDebridClient = class {
568
974
  }
569
975
  /**
570
976
  * Make a GET request
571
- * @param T - The generated response type (e.g., GetLinkUnlockResponse)
977
+ * @template T - The generated response type (e.g., GetLinkUnlockResponse)
978
+ * @param path - API endpoint path
979
+ * @param params - Optional query parameters
572
980
  * @returns The extracted data from the response (without the { status, data } wrapper)
981
+ * @internal
573
982
  */
574
983
  async get(path, params) {
575
984
  try {
@@ -596,14 +1005,30 @@ var AllDebridClient = class {
596
1005
  }
597
1006
  }
598
1007
  /**
599
- * Make a POST request
600
- * @param T - The generated response type
1008
+ * Make a POST request with application/x-www-form-urlencoded
1009
+ * @template T - The generated response type
1010
+ * @param path - API endpoint path
1011
+ * @param body - Request body (will be converted to URLSearchParams)
1012
+ * @param params - Optional query parameters
601
1013
  * @returns The extracted data from the response (without the { status, data } wrapper)
1014
+ * @internal
602
1015
  */
603
1016
  async post(path, body, params) {
604
1017
  try {
605
1018
  const url = this.buildUrl(path, params);
606
- const json = await wretch(this.config.baseUrl).auth(`Bearer ${this.config.apiKey}`).url(url).post(body).json();
1019
+ const formData = new URLSearchParams();
1020
+ if (body && typeof body === "object") {
1021
+ for (const [key, value] of Object.entries(body)) {
1022
+ if (value !== void 0 && value !== null) {
1023
+ if (Array.isArray(value)) {
1024
+ value.forEach((v) => formData.append(key, String(v)));
1025
+ } else {
1026
+ formData.append(key, String(value));
1027
+ }
1028
+ }
1029
+ }
1030
+ }
1031
+ const json = await wretch(this.config.baseUrl).auth(`Bearer ${this.config.apiKey}`).url(url).body(formData).post().json();
607
1032
  if (json.status === "error" && json.error) {
608
1033
  throw createTypedError(json.error.code, json.error.message);
609
1034
  }
@@ -626,8 +1051,12 @@ var AllDebridClient = class {
626
1051
  }
627
1052
  /**
628
1053
  * Make a POST request with FormData (multipart/form-data)
629
- * @param T - The generated response type
1054
+ * @template T - The generated response type
1055
+ * @param path - API endpoint path
1056
+ * @param formData - Form data to send
1057
+ * @param params - Optional query parameters
630
1058
  * @returns The extracted data from the response (without the { status, data } wrapper)
1059
+ * @internal
631
1060
  */
632
1061
  async postFormData(path, formData, params) {
633
1062
  try {
@@ -655,17 +1084,23 @@ var AllDebridClient = class {
655
1084
  }
656
1085
  /**
657
1086
  * Test the API connection
1087
+ *
1088
+ * This endpoint doesn't require authentication and can be used to verify
1089
+ * that the AllDebrid API is reachable.
1090
+ *
1091
+ * @example
1092
+ * ```ts
1093
+ * const result = await client.ping()
1094
+ * console.log(result.ping) // 'pong'
1095
+ * ```
1096
+ *
1097
+ * @returns Ping response with 'pong' message
658
1098
  */
659
1099
  async ping() {
660
- try {
661
- await this.get("/user");
662
- return true;
663
- } catch {
664
- return false;
665
- }
1100
+ return this.get("/ping");
666
1101
  }
667
1102
  };
668
1103
 
669
- export { AllDebridClient, AllDebridError, AuthenticationError, HostResource, LinkError, LinkResource, MagnetError, MagnetResource, NetworkError, UserResource, createTypedError };
1104
+ export { AllDebridClient, AllDebridError, AuthenticationError, HostResource, LinkError, LinkResource, MagnetError, MagnetResource, NetworkError, PinResource, UserResource, VoucherResource, createTypedError };
670
1105
  //# sourceMappingURL=index.js.map
671
1106
  //# sourceMappingURL=index.js.map