debrid-ts 0.0.0-stage → 0.1.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.
- package/LICENSE +21 -0
- package/README.md +181 -2
- package/dist/index.d.mts +1373 -0
- package/dist/index.mjs +1234 -0
- package/package.json +65 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Joshue Abance
|
|
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.
|
package/README.md
CHANGED
|
@@ -1,3 +1,182 @@
|
|
|
1
|
-
#
|
|
1
|
+
# debrid-ts
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Typed TypeScript clients for debrid services — **Real-Debrid**, **AllDebrid**
|
|
4
|
+
and **Premiumize** — with every documented endpoint as a method, plus one
|
|
5
|
+
provider-agnostic interface so your code can swap providers by changing a
|
|
6
|
+
constructor.
|
|
7
|
+
|
|
8
|
+
- Zero dependencies, ESM only, works anywhere `fetch` exists (Node ≥ 18.17,
|
|
9
|
+
Bun, Deno, browsers, edge runtimes)
|
|
10
|
+
- Full wire types for every endpoint (`Rd*`, `Ad*`, `Pm*`)
|
|
11
|
+
- Normalized `Torrent` / `UserInfo` / `DownloadLink` shapes across providers
|
|
12
|
+
- Consistent errors (`DebridError` with HTTP status and provider error code)
|
|
13
|
+
- Optional request timeout and HTTP 429 retries; injectable `fetch` for tests
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
npm install debrid-ts
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Quick start
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
import { RealDebrid } from 'debrid-ts'
|
|
23
|
+
|
|
24
|
+
const rd = new RealDebrid(process.env.RD_TOKEN!)
|
|
25
|
+
|
|
26
|
+
const user = await rd.getUser()
|
|
27
|
+
console.log(user.username, user.premium, user.expiresAt)
|
|
28
|
+
|
|
29
|
+
const torrent = await rd.addMagnet('magnet:?xt=urn:btih:...')
|
|
30
|
+
await rd.selectTorrentFiles(torrent.id, 'all') // Real-Debrid only starts after this
|
|
31
|
+
// ... poll rd.getTorrent(torrent.id) until status === 'downloaded' ...
|
|
32
|
+
const links = await rd.getDownloadLinks(torrent.id) // direct, unrestricted URLs
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## One interface, any provider
|
|
36
|
+
|
|
37
|
+
Every client implements `DebridProvider`:
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
import { AllDebrid, Premiumize, RealDebrid, type DebridProvider } from 'debrid-ts'
|
|
41
|
+
|
|
42
|
+
const provider: DebridProvider =
|
|
43
|
+
process.env.PROVIDER === 'ad' ? new AllDebrid(process.env.AD_KEY!)
|
|
44
|
+
: process.env.PROVIDER === 'pm' ? new Premiumize(process.env.PM_KEY!)
|
|
45
|
+
: new RealDebrid(process.env.RD_TOKEN!)
|
|
46
|
+
|
|
47
|
+
if (await provider.isCached(infoHash)) {
|
|
48
|
+
const torrent = await provider.addMagnet(`magnet:?xt=urn:btih:${infoHash}`)
|
|
49
|
+
const links = await provider.getDownloadLinks(torrent.id)
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
interface DebridProvider {
|
|
55
|
+
readonly name: string
|
|
56
|
+
getUser(): Promise<UserInfo>
|
|
57
|
+
isCached(hashOrMagnet: string): Promise<boolean>
|
|
58
|
+
addMagnet(magnet: string, options?: { host?: string }): Promise<Torrent>
|
|
59
|
+
listTorrents(offset?: number, limit?: number): Promise<Torrent[]>
|
|
60
|
+
getDownloadLinks(torrentId: string): Promise<DownloadLink[]>
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Provider differences worth knowing
|
|
65
|
+
|
|
66
|
+
| | Real-Debrid | AllDebrid | Premiumize |
|
|
67
|
+
| --- | --- | --- | --- |
|
|
68
|
+
| `isCached()` | **Probe:** adds the magnet, selects all files, checks for `downloaded`, deletes it. An uncached torrent may start for a few seconds. (RD disabled its cache endpoint.) | **Probe:** uploads the magnet, reads `ready`, deletes it. | Real lookup via `/cache/check`, no side effects. |
|
|
69
|
+
| After `addMagnet()` | Call `selectTorrentFiles()` to start. | Starts automatically. | Starts automatically. |
|
|
70
|
+
| `getDownloadLinks()` | Unrestricts each host link (one request per file). | Unlocks each `/f/` file link (one request per file, delayed links are awaited). | Reads the resulting file/folder; links are direct. |
|
|
71
|
+
| `listTorrents()` paging | Server-side. | Client-side slice (API returns all). | Client-side slice (API returns all). |
|
|
72
|
+
| `Torrent.hash` / `bytes` | ✅ | ✅ | Empty — the transfer list has neither. |
|
|
73
|
+
|
|
74
|
+
## Providers
|
|
75
|
+
|
|
76
|
+
### Real-Debrid
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
const rd = new RealDebrid(token) // https://real-debrid.com/apitoken, or an OAuth access token
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Covers every endpoint in the [API docs](https://api.real-debrid.com/):
|
|
83
|
+
`/disable_access_token`, `/time`, `/user`, `/unrestrict`, `/traffic`,
|
|
84
|
+
`/streaming`, `/downloads`, `/torrents`, `/hosts`, `/settings`. Raw methods
|
|
85
|
+
return wire types (`RdUser`, `RdTorrentInfo`, …).
|
|
86
|
+
|
|
87
|
+
OAuth helpers are static. For scripts and opensource apps (no client secret),
|
|
88
|
+
use the device flow with Real-Debrid's public opensource client id:
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
const device = await RealDebrid.getDeviceCode(undefined, { newCredentials: true })
|
|
92
|
+
console.log(`Open ${device.verification_url} and enter ${device.user_code}`)
|
|
93
|
+
|
|
94
|
+
const { credentials, token } = await RealDebrid.waitForDeviceToken({
|
|
95
|
+
deviceCode: device.device_code,
|
|
96
|
+
expiresIn: device.expires_in,
|
|
97
|
+
interval: device.interval,
|
|
98
|
+
})
|
|
99
|
+
const rd = new RealDebrid(token.access_token)
|
|
100
|
+
|
|
101
|
+
// later, when the access token expires:
|
|
102
|
+
const refreshed = await RealDebrid.refreshToken({
|
|
103
|
+
clientId: credentials.client_id,
|
|
104
|
+
clientSecret: credentials.client_secret,
|
|
105
|
+
refreshToken: token.refresh_token,
|
|
106
|
+
})
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Also available: `getAuthorizeUrl()` + `exchangeCode()` (website flow),
|
|
110
|
+
`getDeviceToken()` / `getDeviceCredentials()` (single polls) and
|
|
111
|
+
`getTokenWithPassword()`.
|
|
112
|
+
|
|
113
|
+
### AllDebrid
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
const ad = new AllDebrid(apiKey) // https://alldebrid.com/apikeys/
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Covers every non-deprecated endpoint in the [API docs](https://docs.alldebrid.com/):
|
|
120
|
+
hosts, user, links (unlock, streaming, delayed, redirector), magnets
|
|
121
|
+
(upload, `.torrent` upload, status incl. live mode, files, delete, restart),
|
|
122
|
+
saved/recent links and reseller vouchers. The `{ status, data }` envelope is
|
|
123
|
+
unwrapped for you; `{ status: "error" }` throws `DebridError`.
|
|
124
|
+
|
|
125
|
+
Get an apikey from a user with the PIN flow:
|
|
126
|
+
|
|
127
|
+
```ts
|
|
128
|
+
const pin = await AllDebrid.getPin()
|
|
129
|
+
console.log(`Open ${pin.user_url}`)
|
|
130
|
+
const apiKey = await AllDebrid.waitForPin(pin)
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Premiumize
|
|
134
|
+
|
|
135
|
+
```ts
|
|
136
|
+
const pm = new Premiumize(apiKey) // https://www.premiumize.me/account, or an OAuth access token
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Covers every endpoint in the [API docs](https://www.premiumize.me/api):
|
|
140
|
+
`/account`, `/folder` (incl. search and upload), `/item`, `/transfer`,
|
|
141
|
+
`/cache`, `/services`, `/zip`, plus OAuth 2.0 statics (PKCE / auth code via
|
|
142
|
+
`getAuthorizeUrl()` + `exchangeCode()`, device flow via `getDeviceCode()` +
|
|
143
|
+
`waitForDeviceToken()`, and the legacy password grant).
|
|
144
|
+
|
|
145
|
+
## Options
|
|
146
|
+
|
|
147
|
+
Every client (and every static OAuth helper) accepts:
|
|
148
|
+
|
|
149
|
+
```ts
|
|
150
|
+
new RealDebrid(token, {
|
|
151
|
+
timeout: 15_000, // per-request timeout in ms (default: none)
|
|
152
|
+
maxRetries: 2, // retry HTTP 429 responses, honoring Retry-After (default: 0)
|
|
153
|
+
fetch: myFetch, // custom fetch (proxies, tests, ...)
|
|
154
|
+
})
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Errors
|
|
158
|
+
|
|
159
|
+
Every failure — HTTP errors and errors reported inside a 200 envelope — is a
|
|
160
|
+
`DebridError`:
|
|
161
|
+
|
|
162
|
+
```ts
|
|
163
|
+
import { DebridError } from 'debrid-ts'
|
|
164
|
+
|
|
165
|
+
try {
|
|
166
|
+
await ad.unlockLink(url)
|
|
167
|
+
} catch (error) {
|
|
168
|
+
if (error instanceof DebridError) {
|
|
169
|
+
error.status // HTTP status (200 for envelope errors)
|
|
170
|
+
error.code // provider code: 'bad_token', 'LINK_DOWN', 'not_found', ...
|
|
171
|
+
error.details // raw error payload from the provider
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## Contributing
|
|
177
|
+
|
|
178
|
+
See [CONTRIBUTING.md](./CONTRIBUTING.md).
|
|
179
|
+
|
|
180
|
+
## License
|
|
181
|
+
|
|
182
|
+
[MIT](./LICENSE)
|