@epic-web/workshop-utils 6.66.0 → 6.68.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.
|
@@ -35,6 +35,16 @@ export const downloadProgressEmitter = new DownloadProgressEmitter();
|
|
|
35
35
|
function emitDownloadProgress(progress) {
|
|
36
36
|
downloadProgressEmitter.emit(DOWNLOAD_PROGRESS_EVENTS.PROGRESS, progress);
|
|
37
37
|
}
|
|
38
|
+
function formatDownloadError(error) {
|
|
39
|
+
if (error instanceof Error) {
|
|
40
|
+
return {
|
|
41
|
+
name: error.name,
|
|
42
|
+
message: error.message,
|
|
43
|
+
stack: error.stack,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
return { message: String(error) };
|
|
47
|
+
}
|
|
38
48
|
let downloadState = {
|
|
39
49
|
status: 'idle',
|
|
40
50
|
startedAt: null,
|
|
@@ -351,6 +361,7 @@ async function isOfflineVideoReady(index, playbackId, keyId, cryptoVersion, work
|
|
|
351
361
|
async function downloadMuxVideo({ playbackId, filePath, key, iv, resolution, }) {
|
|
352
362
|
const urls = getMuxMp4Urls(playbackId, resolution);
|
|
353
363
|
let lastError = null;
|
|
364
|
+
const attempts = [];
|
|
354
365
|
emitDownloadProgress({
|
|
355
366
|
playbackId,
|
|
356
367
|
bytesDownloaded: 0,
|
|
@@ -360,12 +371,14 @@ async function downloadMuxVideo({ playbackId, filePath, key, iv, resolution, })
|
|
|
360
371
|
for (const url of urls) {
|
|
361
372
|
log.info('Attempting mux download', { playbackId, url });
|
|
362
373
|
const response = await fetch(url).catch((error) => {
|
|
363
|
-
|
|
374
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
375
|
+
lastError = error instanceof Error ? error : new Error(message);
|
|
364
376
|
log.warn('Mux download request failed', {
|
|
365
377
|
playbackId,
|
|
366
378
|
url,
|
|
367
|
-
message
|
|
379
|
+
message,
|
|
368
380
|
});
|
|
381
|
+
attempts.push({ url, error: message });
|
|
369
382
|
return null;
|
|
370
383
|
});
|
|
371
384
|
if (!response)
|
|
@@ -376,6 +389,13 @@ async function downloadMuxVideo({ playbackId, filePath, key, iv, resolution, })
|
|
|
376
389
|
playbackId,
|
|
377
390
|
url,
|
|
378
391
|
status: response.status,
|
|
392
|
+
statusText: response.statusText,
|
|
393
|
+
hasBody: Boolean(response.body),
|
|
394
|
+
});
|
|
395
|
+
attempts.push({
|
|
396
|
+
url,
|
|
397
|
+
status: response.status,
|
|
398
|
+
statusText: response.statusText,
|
|
379
399
|
hasBody: Boolean(response.body),
|
|
380
400
|
});
|
|
381
401
|
continue;
|
|
@@ -428,6 +448,12 @@ async function downloadMuxVideo({ playbackId, filePath, key, iv, resolution, })
|
|
|
428
448
|
totalBytes: null,
|
|
429
449
|
status: 'error',
|
|
430
450
|
});
|
|
451
|
+
log.error('Mux download failed', {
|
|
452
|
+
playbackId,
|
|
453
|
+
resolution,
|
|
454
|
+
attempts,
|
|
455
|
+
error: lastError ? formatDownloadError(lastError) : null,
|
|
456
|
+
});
|
|
431
457
|
throw lastError ?? new Error(`Unable to download video ${playbackId}`);
|
|
432
458
|
}
|
|
433
459
|
async function runOfflineVideoDownloads({ videos, index, keyInfo, workshop, resolution, }) {
|
|
@@ -485,7 +511,15 @@ async function runOfflineVideoDownloads({ videos, index, keyInfo, workshop, reso
|
|
|
485
511
|
error: message,
|
|
486
512
|
updatedAt: new Date().toISOString(),
|
|
487
513
|
};
|
|
488
|
-
log.error(
|
|
514
|
+
log.error('Offline video download failed', {
|
|
515
|
+
playbackId: video.playbackId,
|
|
516
|
+
title: video.title,
|
|
517
|
+
url: video.url,
|
|
518
|
+
resolution,
|
|
519
|
+
fileName: entry.fileName,
|
|
520
|
+
filePath: path.join(getOfflineVideoDir(), entry.fileName),
|
|
521
|
+
error: formatDownloadError(error),
|
|
522
|
+
});
|
|
489
523
|
}
|
|
490
524
|
finally {
|
|
491
525
|
downloadState.completed += 1;
|
|
@@ -710,7 +744,15 @@ export async function downloadOfflineVideo({ playbackId, title, url, }) {
|
|
|
710
744
|
catch (error) {
|
|
711
745
|
const detailedMessage = error instanceof Error ? error.message : 'Download failed';
|
|
712
746
|
const message = `Failed to download "${title}". Please try again.`;
|
|
713
|
-
log.error(
|
|
747
|
+
log.error('Offline video download failed', {
|
|
748
|
+
playbackId,
|
|
749
|
+
title,
|
|
750
|
+
url,
|
|
751
|
+
resolution,
|
|
752
|
+
fileName: entry.fileName,
|
|
753
|
+
filePath: path.join(getOfflineVideoDir(), entry.fileName),
|
|
754
|
+
error: formatDownloadError(error),
|
|
755
|
+
});
|
|
714
756
|
index[playbackId] = {
|
|
715
757
|
...entry,
|
|
716
758
|
status: 'error',
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
export declare const PACKAGE_MANAGERS: readonly ["npm", "pnpm", "yarn", "bun"];
|
|
3
|
+
export type PackageManager = (typeof PACKAGE_MANAGERS)[number];
|
|
2
4
|
declare const ConfigSchema: z.ZodObject<{
|
|
3
5
|
reposDirectory: z.ZodOptional<z.ZodString>;
|
|
6
|
+
packageManager: z.ZodOptional<z.ZodEnum<{
|
|
7
|
+
npm: "npm";
|
|
8
|
+
pnpm: "pnpm";
|
|
9
|
+
yarn: "yarn";
|
|
10
|
+
bun: "bun";
|
|
11
|
+
}>>;
|
|
4
12
|
}, z.core.$strip>;
|
|
5
13
|
export type Workshop = {
|
|
6
14
|
name: string;
|
|
@@ -17,6 +25,10 @@ export declare function getReposDirectory(): Promise<string>;
|
|
|
17
25
|
export declare function isReposDirectoryConfigured(): Promise<boolean>;
|
|
18
26
|
export declare function getDefaultReposDir(): string;
|
|
19
27
|
export declare function setReposDirectory(directory: string): Promise<void>;
|
|
28
|
+
export declare function getPackageManager(): Promise<PackageManager>;
|
|
29
|
+
export declare function isPackageManagerConfigured(): Promise<boolean>;
|
|
30
|
+
export declare function setPackageManager(packageManager: PackageManager): Promise<void>;
|
|
31
|
+
export declare function clearPackageManager(): Promise<void>;
|
|
20
32
|
export type ReposDirectoryStatus = {
|
|
21
33
|
accessible: true;
|
|
22
34
|
} | {
|
package/dist/workshops.server.js
CHANGED
|
@@ -19,9 +19,12 @@ const EpicshopConfigSchema = z.object({
|
|
|
19
19
|
})
|
|
20
20
|
.optional(),
|
|
21
21
|
});
|
|
22
|
+
export const PACKAGE_MANAGERS = ['npm', 'pnpm', 'yarn', 'bun'];
|
|
23
|
+
const PackageManagerSchema = z.enum(PACKAGE_MANAGERS);
|
|
22
24
|
// Schema for workshop configuration (stored settings only)
|
|
23
25
|
const ConfigSchema = z.object({
|
|
24
26
|
reposDirectory: z.string().optional(),
|
|
27
|
+
packageManager: PackageManagerSchema.optional(),
|
|
25
28
|
});
|
|
26
29
|
function getDefaultReposDirectory() {
|
|
27
30
|
return path.join(os.homedir(), 'epic-workshops');
|
|
@@ -86,6 +89,24 @@ export async function setReposDirectory(directory) {
|
|
|
86
89
|
config.reposDirectory = path.resolve(directory);
|
|
87
90
|
await saveConfig(config);
|
|
88
91
|
}
|
|
92
|
+
export async function getPackageManager() {
|
|
93
|
+
const config = await loadConfig();
|
|
94
|
+
return config.packageManager ?? 'npm';
|
|
95
|
+
}
|
|
96
|
+
export async function isPackageManagerConfigured() {
|
|
97
|
+
const config = await loadConfig();
|
|
98
|
+
return Boolean(config.packageManager);
|
|
99
|
+
}
|
|
100
|
+
export async function setPackageManager(packageManager) {
|
|
101
|
+
const config = await loadConfig();
|
|
102
|
+
config.packageManager = packageManager;
|
|
103
|
+
await saveConfig(config);
|
|
104
|
+
}
|
|
105
|
+
export async function clearPackageManager() {
|
|
106
|
+
const config = await loadConfig();
|
|
107
|
+
delete config.packageManager;
|
|
108
|
+
await saveConfig(config);
|
|
109
|
+
}
|
|
89
110
|
/**
|
|
90
111
|
* Verify that the configured repos directory exists and is accessible.
|
|
91
112
|
* If the directory doesn't exist, attempts to create it.
|