rill-node 0.0.0-stage → 2026.11.14

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 (82) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +9 -0
  3. package/README.md +64 -2
  4. package/binding.gyp +31 -0
  5. package/index.d.ts +45 -0
  6. package/index.js +112 -0
  7. package/native/darwin-arm64/librill.dylib +0 -0
  8. package/native/darwin-arm64/rill-media +0 -0
  9. package/native/darwin-arm64/rill_node.node +0 -0
  10. package/native/darwin-x64/librill.dylib +0 -0
  11. package/native/darwin-x64/rill-media +0 -0
  12. package/native/darwin-x64/rill_node.node +0 -0
  13. package/native/linux-arm64-gnu/librill.so +0 -0
  14. package/native/linux-arm64-gnu/rill-media +0 -0
  15. package/native/linux-arm64-gnu/rill_node.node +0 -0
  16. package/native/linux-x64-gnu/librill.so +0 -0
  17. package/native/linux-x64-gnu/rill-media +0 -0
  18. package/native/linux-x64-gnu/rill_node.node +0 -0
  19. package/native-src/include/rill.h +90 -0
  20. package/native-src/include/rill_media.h +22 -0
  21. package/native-src/shard.yml +9 -0
  22. package/native-src/src/c_api.cr +194 -0
  23. package/native-src/src/cli.cr +637 -0
  24. package/native-src/src/rill/c_api/job.cr +336 -0
  25. package/native-src/src/rill/c_api/memory.cr +32 -0
  26. package/native-src/src/rill/c_api/status.cr +25 -0
  27. package/native-src/src/rill/c_api/sync.cr +125 -0
  28. package/native-src/src/rill/c_api/worker_pool.cr +403 -0
  29. package/native-src/src/rill/cancellation.cr +29 -0
  30. package/native-src/src/rill/client.cr +52 -0
  31. package/native-src/src/rill/cookie_jar.cr +130 -0
  32. package/native-src/src/rill/dash/manifest.cr +279 -0
  33. package/native-src/src/rill/dash/parser.cr +227 -0
  34. package/native-src/src/rill/download_options.cr +96 -0
  35. package/native-src/src/rill/download_plan.cr +27 -0
  36. package/native-src/src/rill/download_progress.cr +16 -0
  37. package/native-src/src/rill/downloader.cr +662 -0
  38. package/native-src/src/rill/errors.cr +155 -0
  39. package/native-src/src/rill/extractor.cr +18 -0
  40. package/native-src/src/rill/extractor_registry.cr +98 -0
  41. package/native-src/src/rill/extractors/bandcamp.cr +265 -0
  42. package/native-src/src/rill/extractors/dash.cr +77 -0
  43. package/native-src/src/rill/extractors/direct_media.cr +73 -0
  44. package/native-src/src/rill/extractors/generic.cr +441 -0
  45. package/native-src/src/rill/extractors/hls.cr +202 -0
  46. package/native-src/src/rill/extractors/internet_archive.cr +180 -0
  47. package/native-src/src/rill/extractors/m3u.cr +81 -0
  48. package/native-src/src/rill/extractors/media_ccc_de.cr +219 -0
  49. package/native-src/src/rill/extractors/mixcloud.cr +495 -0
  50. package/native-src/src/rill/extractors/peer_tube.cr +226 -0
  51. package/native-src/src/rill/extractors/rumble.cr +325 -0
  52. package/native-src/src/rill/extractors/soundcloud.cr +553 -0
  53. package/native-src/src/rill/extractors/spotify.cr +184 -0
  54. package/native-src/src/rill/extractors/twitch_clip.cr +273 -0
  55. package/native-src/src/rill/extractors/youtube.cr +520 -0
  56. package/native-src/src/rill/format.cr +82 -0
  57. package/native-src/src/rill/format_selector.cr +143 -0
  58. package/native-src/src/rill/hls/parser.cr +257 -0
  59. package/native-src/src/rill/hls/playlist.cr +100 -0
  60. package/native-src/src/rill/http_client.cr +503 -0
  61. package/native-src/src/rill/http_url.cr +28 -0
  62. package/native-src/src/rill/json_value.cr +66 -0
  63. package/native-src/src/rill/media.cr +151 -0
  64. package/native-src/src/rill/media_muxer.cr +333 -0
  65. package/native-src/src/rill/mime_type.cr +58 -0
  66. package/native-src/src/rill/output_file_name.cr +91 -0
  67. package/native-src/src/rill/playlist.cr +109 -0
  68. package/native-src/src/rill/request_options.cr +179 -0
  69. package/native-src/src/rill/segment_assembly.cr +70 -0
  70. package/native-src/src/rill/timestamp.cr +34 -0
  71. package/native-src/src/rill/version.cr +3 -0
  72. package/native-src/src/rill.cr +47 -0
  73. package/package.json +48 -4
  74. package/scripts/build-native.js +46 -0
  75. package/scripts/install-gyp.js +10 -0
  76. package/scripts/install.js +19 -0
  77. package/scripts/lib.js +1 -0
  78. package/scripts/libdir.js +23 -0
  79. package/scripts/package-native.js +35 -0
  80. package/scripts/platform.js +41 -0
  81. package/scripts/prepack.js +47 -0
  82. package/src/addon.c +498 -0
@@ -0,0 +1,47 @@
1
+ const fs = require("node:fs");
2
+ const path = require("node:path");
3
+ const { spawnSync } = require("node:child_process");
4
+
5
+ const packageDir = path.resolve(__dirname, "..");
6
+ const root = path.resolve(packageDir, "..", "..");
7
+ const target = path.join(packageDir, "native-src");
8
+
9
+ fs.rmSync(target, { recursive: true, force: true });
10
+ fs.mkdirSync(target, { recursive: true });
11
+
12
+ for (const name of ["src", "include"]) {
13
+ fs.cpSync(path.join(root, name), path.join(target, name), { recursive: true });
14
+ }
15
+
16
+ fs.copyFileSync(path.join(root, "shard.yml"), path.join(target, "shard.yml"));
17
+
18
+ for (const name of ["LICENSE", "NOTICE"]) {
19
+ fs.copyFileSync(path.join(root, name), path.join(packageDir, name));
20
+ }
21
+
22
+ const nativeDir = path.join(packageDir, "native");
23
+ fs.mkdirSync(nativeDir, { recursive: true });
24
+ const hasPlatforms = fs.readdirSync(nativeDir, { withFileTypes: true }).some((entry) => entry.isDirectory());
25
+
26
+ if (hasPlatforms) {
27
+ fs.rmSync(path.join(nativeDir, process.platform === "win32" ? "rill-media.exe" : "rill-media"), {
28
+ force: true,
29
+ });
30
+ }
31
+
32
+ if (!hasPlatforms) {
33
+ const helperName = process.platform === "win32" ? "rill-media.exe" : "rill-media";
34
+ const helper = path.join(root, "build", helperName);
35
+
36
+ if (!fs.existsSync(helper)) {
37
+ const result = spawnSync("make", ["media-helper"], { cwd: root, stdio: "inherit" });
38
+ if (result.status !== 0 || !fs.existsSync(helper)) {
39
+ console.error("Failed to build the static rill-media helper.");
40
+ process.exit(1);
41
+ }
42
+ }
43
+
44
+ if (!fs.existsSync(path.join(nativeDir, helperName))) {
45
+ fs.copyFileSync(helper, path.join(nativeDir, helperName));
46
+ }
47
+ }
package/src/addon.c ADDED
@@ -0,0 +1,498 @@
1
+ #include <node_api.h>
2
+
3
+ #include <rill.h>
4
+
5
+ #include <stdint.h>
6
+ #include <stdlib.h>
7
+ #include <string.h>
8
+
9
+ typedef struct {
10
+ rill_download *handle;
11
+ } DownloadHandle;
12
+
13
+ typedef struct {
14
+ napi_async_work work;
15
+ napi_deferred deferred;
16
+ char *url;
17
+ char *options;
18
+ int status;
19
+ char *json;
20
+ char *error;
21
+ char *error_code;
22
+ } ExtractWork;
23
+
24
+ static char *get_string(napi_env env, napi_value value) {
25
+ size_t length = 0;
26
+ if (napi_get_value_string_utf8(env, value, NULL, 0, &length) != napi_ok) {
27
+ return NULL;
28
+ }
29
+
30
+ char *buffer = malloc(length + 1);
31
+ if (buffer == NULL) {
32
+ return NULL;
33
+ }
34
+
35
+ if (napi_get_value_string_utf8(env, value, buffer, length + 1, &length) != napi_ok) {
36
+ free(buffer);
37
+ return NULL;
38
+ }
39
+
40
+ return buffer;
41
+ }
42
+
43
+ static bool get_optional_string(napi_env env, napi_value value, char **out) {
44
+ napi_valuetype type = napi_undefined;
45
+ napi_typeof(env, value, &type);
46
+
47
+ if (type == napi_undefined || type == napi_null) {
48
+ *out = NULL;
49
+ return true;
50
+ }
51
+
52
+ *out = get_string(env, value);
53
+ return *out != NULL;
54
+ }
55
+
56
+ static void free_rill_string(char *value) {
57
+ if (value != NULL) {
58
+ rill_free_string(value);
59
+ }
60
+ }
61
+
62
+ static void throw_status(napi_env env, int status, const char *message, const char *kind) {
63
+ napi_value text;
64
+ napi_value error;
65
+ napi_value status_value;
66
+
67
+ napi_create_string_utf8(env, message != NULL ? message : "rill error", NAPI_AUTO_LENGTH, &text);
68
+ napi_create_error(env, NULL, text, &error);
69
+
70
+ napi_create_int32(env, status, &status_value);
71
+ napi_set_named_property(env, error, "status", status_value);
72
+
73
+ if (kind != NULL) {
74
+ napi_value kind_value;
75
+ napi_create_string_utf8(env, kind, NAPI_AUTO_LENGTH, &kind_value);
76
+ napi_set_named_property(env, error, "code", kind_value);
77
+ }
78
+
79
+ napi_throw(env, error);
80
+ }
81
+
82
+ static bool get_args(napi_env env, napi_callback_info info, size_t expected,
83
+ napi_value *argv, size_t *argc) {
84
+ size_t count = expected;
85
+ if (napi_get_cb_info(env, info, &count, argv, NULL, NULL) != napi_ok) {
86
+ return false;
87
+ }
88
+
89
+ *argc = count;
90
+ return true;
91
+ }
92
+
93
+ static napi_value AbiVersion(napi_env env, napi_callback_info info) {
94
+ (void)info;
95
+ napi_value result;
96
+ napi_create_int32(env, rill_abi_version(), &result);
97
+ return result;
98
+ }
99
+
100
+ static napi_value Init(napi_env env, napi_callback_info info) {
101
+ napi_value argv[1];
102
+ size_t argc = 0;
103
+ if (!get_args(env, info, 1, argv, &argc)) {
104
+ return NULL;
105
+ }
106
+
107
+ uint32_t workers = 0;
108
+ if (argc > 0) {
109
+ napi_get_value_uint32(env, argv[0], &workers);
110
+ }
111
+
112
+ int status = rill_init(workers);
113
+ if (status != RILL_OK) {
114
+ throw_status(env, status, "rill_init failed", "internal");
115
+ return NULL;
116
+ }
117
+
118
+ return NULL;
119
+ }
120
+
121
+ static napi_value Shutdown(napi_env env, napi_callback_info info) {
122
+ (void)info;
123
+ int status = rill_shutdown();
124
+ if (status != RILL_OK) {
125
+ throw_status(env, status, "rill_shutdown failed", "internal");
126
+ return NULL;
127
+ }
128
+
129
+ return NULL;
130
+ }
131
+
132
+ static napi_value ExtractJsonSync(napi_env env, napi_callback_info info) {
133
+ napi_value argv[2];
134
+ size_t argc = 0;
135
+ if (!get_args(env, info, 2, argv, &argc)) {
136
+ return NULL;
137
+ }
138
+
139
+ if (argc < 1) {
140
+ throw_status(env, RILL_ERROR_INVALID_ARGUMENT, "URL is required", "invalid_argument");
141
+ return NULL;
142
+ }
143
+
144
+ char *url = get_string(env, argv[0]);
145
+ char *options = NULL;
146
+ if (url == NULL) {
147
+ throw_status(env, RILL_ERROR_INVALID_ARGUMENT, "URL is invalid", "invalid_argument");
148
+ return NULL;
149
+ }
150
+ if (argc > 1 && !get_optional_string(env, argv[1], &options)) {
151
+ free(url);
152
+ throw_status(env, RILL_ERROR_INVALID_ARGUMENT, "options are invalid", "invalid_argument");
153
+ return NULL;
154
+ }
155
+
156
+ char *json = NULL;
157
+ char *error = NULL;
158
+ char *kind = NULL;
159
+ int status = rill_extract_json(url, options, &json, &error, &kind);
160
+
161
+ free(url);
162
+ free(options);
163
+
164
+ if (status != RILL_OK) {
165
+ throw_status(env, status, error, kind);
166
+ free_rill_string(json);
167
+ free_rill_string(error);
168
+ free_rill_string(kind);
169
+ return NULL;
170
+ }
171
+
172
+ napi_value result;
173
+ napi_create_string_utf8(env, json != NULL ? json : "", NAPI_AUTO_LENGTH, &result);
174
+ free_rill_string(json);
175
+ free_rill_string(error);
176
+ free_rill_string(kind);
177
+ return result;
178
+ }
179
+
180
+ static void ExtractExecute(napi_env env, void *data) {
181
+ (void)env;
182
+ ExtractWork *work = data;
183
+ work->status = rill_extract_json(work->url, work->options, &work->json,
184
+ &work->error, &work->error_code);
185
+ }
186
+
187
+ static void ExtractComplete(napi_env env, napi_status status, void *data) {
188
+ (void)status;
189
+ ExtractWork *work = data;
190
+
191
+ if (work->status == RILL_OK) {
192
+ napi_value result;
193
+ napi_create_string_utf8(env, work->json != NULL ? work->json : "",
194
+ NAPI_AUTO_LENGTH, &result);
195
+ napi_resolve_deferred(env, work->deferred, result);
196
+ } else {
197
+ napi_value text;
198
+ napi_value error;
199
+ napi_value status_value;
200
+ napi_create_string_utf8(env, work->error != NULL ? work->error : "extraction failed",
201
+ NAPI_AUTO_LENGTH, &text);
202
+ napi_create_error(env, NULL, text, &error);
203
+ napi_create_int32(env, work->status, &status_value);
204
+ napi_set_named_property(env, error, "status", status_value);
205
+ if (work->error_code != NULL) {
206
+ napi_value kind;
207
+ napi_create_string_utf8(env, work->error_code, NAPI_AUTO_LENGTH, &kind);
208
+ napi_set_named_property(env, error, "code", kind);
209
+ }
210
+ napi_reject_deferred(env, work->deferred, error);
211
+ }
212
+
213
+ napi_delete_async_work(env, work->work);
214
+ free(work->url);
215
+ free(work->options);
216
+ free_rill_string(work->json);
217
+ free_rill_string(work->error);
218
+ free_rill_string(work->error_code);
219
+ free(work);
220
+ }
221
+
222
+ static napi_value ExtractJson(napi_env env, napi_callback_info info) {
223
+ napi_value argv[2];
224
+ size_t argc = 0;
225
+ if (!get_args(env, info, 2, argv, &argc)) {
226
+ return NULL;
227
+ }
228
+
229
+ if (argc < 1) {
230
+ throw_status(env, RILL_ERROR_INVALID_ARGUMENT, "URL is required", "invalid_argument");
231
+ return NULL;
232
+ }
233
+
234
+ ExtractWork *work = calloc(1, sizeof(ExtractWork));
235
+ if (work == NULL) {
236
+ throw_status(env, RILL_ERROR_INTERNAL, "out of memory", "internal");
237
+ return NULL;
238
+ }
239
+
240
+ work->url = get_string(env, argv[0]);
241
+ if (work->url == NULL) {
242
+ free(work);
243
+ throw_status(env, RILL_ERROR_INVALID_ARGUMENT, "URL is invalid", "invalid_argument");
244
+ return NULL;
245
+ }
246
+
247
+ if (argc > 1 && !get_optional_string(env, argv[1], &work->options)) {
248
+ free(work->url);
249
+ free(work);
250
+ throw_status(env, RILL_ERROR_INVALID_ARGUMENT, "options are invalid", "invalid_argument");
251
+ return NULL;
252
+ }
253
+
254
+ napi_value promise;
255
+ napi_create_promise(env, &work->deferred, &promise);
256
+ napi_value resource_name;
257
+ napi_create_string_utf8(env, "rill_extract", NAPI_AUTO_LENGTH, &resource_name);
258
+
259
+ if (napi_create_async_work(env, NULL, resource_name, ExtractExecute,
260
+ ExtractComplete, work, &work->work) != napi_ok) {
261
+ free(work->url);
262
+ free(work->options);
263
+ free(work);
264
+ throw_status(env, RILL_ERROR_INTERNAL, "cannot create async work", "internal");
265
+ return NULL;
266
+ }
267
+
268
+ napi_queue_async_work(env, work->work);
269
+ return promise;
270
+ }
271
+
272
+ static void DownloadFinalize(napi_env env, void *data, void *hint) {
273
+ (void)env;
274
+ (void)hint;
275
+ DownloadHandle *download = data;
276
+ if (download->handle != NULL) {
277
+ rill_download_free(download->handle);
278
+ }
279
+ free(download);
280
+ }
281
+
282
+ static napi_value DownloadStart(napi_env env, napi_callback_info info) {
283
+ napi_value argv[3];
284
+ size_t argc = 0;
285
+ if (!get_args(env, info, 3, argv, &argc)) {
286
+ return NULL;
287
+ }
288
+
289
+ if (argc < 2) {
290
+ throw_status(env, RILL_ERROR_INVALID_ARGUMENT, "URL and output are required",
291
+ "invalid_argument");
292
+ return NULL;
293
+ }
294
+
295
+ char *url = get_string(env, argv[0]);
296
+ char *output = get_string(env, argv[1]);
297
+ char *options = NULL;
298
+
299
+ if (url == NULL || output == NULL ||
300
+ (argc > 2 && !get_optional_string(env, argv[2], &options))) {
301
+ free(url);
302
+ free(output);
303
+ free(options);
304
+ throw_status(env, RILL_ERROR_INVALID_ARGUMENT, "download arguments are invalid",
305
+ "invalid_argument");
306
+ return NULL;
307
+ }
308
+
309
+ char *error = NULL;
310
+ rill_download *handle = rill_download_start(url, output, options, &error);
311
+
312
+ free(url);
313
+ free(output);
314
+ free(options);
315
+
316
+ if (handle == NULL) {
317
+ throw_status(env, RILL_ERROR_INTERNAL, error, "internal");
318
+ free_rill_string(error);
319
+ return NULL;
320
+ }
321
+ free_rill_string(error);
322
+
323
+ DownloadHandle *download = malloc(sizeof(DownloadHandle));
324
+ if (download == NULL) {
325
+ rill_download_free(handle);
326
+ throw_status(env, RILL_ERROR_INTERNAL, "out of memory", "internal");
327
+ return NULL;
328
+ }
329
+ download->handle = handle;
330
+
331
+ napi_value external;
332
+ napi_create_external(env, download, DownloadFinalize, NULL, &external);
333
+ return external;
334
+ }
335
+
336
+ static DownloadHandle *get_download(napi_env env, napi_value value) {
337
+ void *data = NULL;
338
+ if (napi_get_value_external(env, value, &data) != napi_ok) {
339
+ return NULL;
340
+ }
341
+
342
+ return data;
343
+ }
344
+
345
+ static napi_value DownloadPoll(napi_env env, napi_callback_info info) {
346
+ napi_value argv[1];
347
+ size_t argc = 0;
348
+ if (!get_args(env, info, 1, argv, &argc) || argc < 1) {
349
+ throw_status(env, RILL_ERROR_INVALID_ARGUMENT, "download handle is required",
350
+ "invalid_argument");
351
+ return NULL;
352
+ }
353
+
354
+ DownloadHandle *download = get_download(env, argv[0]);
355
+ if (download == NULL || download->handle == NULL) {
356
+ throw_status(env, RILL_ERROR_INVALID_ARGUMENT, "download handle is closed",
357
+ "invalid_argument");
358
+ return NULL;
359
+ }
360
+
361
+ int state = 0;
362
+ int64_t downloaded = 0;
363
+ int64_t total = 0;
364
+ int status = rill_download_poll(download->handle, &state, &downloaded, &total);
365
+ if (status != RILL_OK) {
366
+ throw_status(env, status, "rill_download_poll failed", "internal");
367
+ return NULL;
368
+ }
369
+
370
+ napi_value result;
371
+ napi_value state_value;
372
+ napi_value downloaded_value;
373
+ napi_value total_value;
374
+
375
+ napi_create_object(env, &result);
376
+ napi_create_int32(env, state, &state_value);
377
+ napi_create_int64(env, downloaded, &downloaded_value);
378
+ napi_create_int64(env, total, &total_value);
379
+ napi_set_named_property(env, result, "state", state_value);
380
+ napi_set_named_property(env, result, "downloaded", downloaded_value);
381
+ napi_set_named_property(env, result, "total", total_value);
382
+ return result;
383
+ }
384
+
385
+ static napi_value DownloadCancel(napi_env env, napi_callback_info info) {
386
+ napi_value argv[1];
387
+ size_t argc = 0;
388
+ if (!get_args(env, info, 1, argv, &argc) || argc < 1) {
389
+ throw_status(env, RILL_ERROR_INVALID_ARGUMENT, "download handle is required",
390
+ "invalid_argument");
391
+ return NULL;
392
+ }
393
+
394
+ DownloadHandle *download = get_download(env, argv[0]);
395
+ if (download == NULL || download->handle == NULL) {
396
+ throw_status(env, RILL_ERROR_INVALID_ARGUMENT, "download handle is closed",
397
+ "invalid_argument");
398
+ return NULL;
399
+ }
400
+
401
+ int status = rill_download_cancel(download->handle);
402
+ if (status != RILL_OK) {
403
+ throw_status(env, status, "rill_download_cancel failed", "internal");
404
+ return NULL;
405
+ }
406
+
407
+ return NULL;
408
+ }
409
+
410
+ static napi_value DownloadError(napi_env env, napi_callback_info info) {
411
+ napi_value argv[1];
412
+ size_t argc = 0;
413
+ if (!get_args(env, info, 1, argv, &argc) || argc < 1) {
414
+ throw_status(env, RILL_ERROR_INVALID_ARGUMENT, "download handle is required",
415
+ "invalid_argument");
416
+ return NULL;
417
+ }
418
+
419
+ DownloadHandle *download = get_download(env, argv[0]);
420
+ if (download == NULL || download->handle == NULL) {
421
+ throw_status(env, RILL_ERROR_INVALID_ARGUMENT, "download handle is closed",
422
+ "invalid_argument");
423
+ return NULL;
424
+ }
425
+
426
+ char *message = rill_download_error(download->handle);
427
+ napi_value result;
428
+ napi_create_string_utf8(env, message != NULL ? message : "", NAPI_AUTO_LENGTH, &result);
429
+ free_rill_string(message);
430
+ return result;
431
+ }
432
+
433
+ static napi_value DownloadErrorCode(napi_env env, napi_callback_info info) {
434
+ napi_value argv[1];
435
+ size_t argc = 0;
436
+ if (!get_args(env, info, 1, argv, &argc) || argc < 1) {
437
+ throw_status(env, RILL_ERROR_INVALID_ARGUMENT, "download handle is required",
438
+ "invalid_argument");
439
+ return NULL;
440
+ }
441
+
442
+ DownloadHandle *download = get_download(env, argv[0]);
443
+ if (download == NULL || download->handle == NULL) {
444
+ throw_status(env, RILL_ERROR_INVALID_ARGUMENT, "download handle is closed",
445
+ "invalid_argument");
446
+ return NULL;
447
+ }
448
+
449
+ char *kind = rill_download_error_code(download->handle);
450
+ napi_value result;
451
+ napi_create_string_utf8(env, kind != NULL ? kind : "", NAPI_AUTO_LENGTH, &result);
452
+ free_rill_string(kind);
453
+ return result;
454
+ }
455
+
456
+ static napi_value DownloadClose(napi_env env, napi_callback_info info) {
457
+ napi_value argv[1];
458
+ size_t argc = 0;
459
+ if (!get_args(env, info, 1, argv, &argc) || argc < 1) {
460
+ throw_status(env, RILL_ERROR_INVALID_ARGUMENT, "download handle is required",
461
+ "invalid_argument");
462
+ return NULL;
463
+ }
464
+
465
+ DownloadHandle *download = get_download(env, argv[0]);
466
+ if (download == NULL) {
467
+ throw_status(env, RILL_ERROR_INVALID_ARGUMENT, "download handle is invalid",
468
+ "invalid_argument");
469
+ return NULL;
470
+ }
471
+
472
+ if (download->handle != NULL) {
473
+ rill_download_free(download->handle);
474
+ download->handle = NULL;
475
+ }
476
+
477
+ return NULL;
478
+ }
479
+
480
+ NAPI_MODULE_INIT() {
481
+ napi_property_descriptor properties[] = {
482
+ {"abiVersion", NULL, AbiVersion, NULL, NULL, NULL, napi_default, NULL},
483
+ {"init", NULL, Init, NULL, NULL, NULL, napi_default, NULL},
484
+ {"shutdown", NULL, Shutdown, NULL, NULL, NULL, napi_default, NULL},
485
+ {"extractJsonSync", NULL, ExtractJsonSync, NULL, NULL, NULL, napi_default, NULL},
486
+ {"extractJson", NULL, ExtractJson, NULL, NULL, NULL, napi_default, NULL},
487
+ {"downloadStart", NULL, DownloadStart, NULL, NULL, NULL, napi_default, NULL},
488
+ {"downloadPoll", NULL, DownloadPoll, NULL, NULL, NULL, napi_default, NULL},
489
+ {"downloadCancel", NULL, DownloadCancel, NULL, NULL, NULL, napi_default, NULL},
490
+ {"downloadError", NULL, DownloadError, NULL, NULL, NULL, napi_default, NULL},
491
+ {"downloadErrorCode", NULL, DownloadErrorCode, NULL, NULL, NULL, napi_default, NULL},
492
+ {"downloadClose", NULL, DownloadClose, NULL, NULL, NULL, napi_default, NULL},
493
+ };
494
+
495
+ napi_define_properties(env, exports,
496
+ sizeof(properties) / sizeof(properties[0]), properties);
497
+ return exports;
498
+ }