vimeo2bunny 1.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.
Files changed (67) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +138 -0
  3. package/dist/commands/config.d.ts +3 -0
  4. package/dist/commands/config.d.ts.map +1 -0
  5. package/dist/commands/config.js +124 -0
  6. package/dist/commands/config.js.map +1 -0
  7. package/dist/commands/list.d.ts +3 -0
  8. package/dist/commands/list.d.ts.map +1 -0
  9. package/dist/commands/list.js +138 -0
  10. package/dist/commands/list.js.map +1 -0
  11. package/dist/commands/migrate.d.ts +3 -0
  12. package/dist/commands/migrate.d.ts.map +1 -0
  13. package/dist/commands/migrate.js +179 -0
  14. package/dist/commands/migrate.js.map +1 -0
  15. package/dist/commands/status.d.ts +2 -0
  16. package/dist/commands/status.d.ts.map +1 -0
  17. package/dist/commands/status.js +105 -0
  18. package/dist/commands/status.js.map +1 -0
  19. package/dist/index.d.ts +3 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +101 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/services/bunny.d.ts +42 -0
  24. package/dist/services/bunny.d.ts.map +1 -0
  25. package/dist/services/bunny.js +291 -0
  26. package/dist/services/bunny.js.map +1 -0
  27. package/dist/services/migration.d.ts +49 -0
  28. package/dist/services/migration.d.ts.map +1 -0
  29. package/dist/services/migration.js +425 -0
  30. package/dist/services/migration.js.map +1 -0
  31. package/dist/services/vimeo.d.ts +28 -0
  32. package/dist/services/vimeo.d.ts.map +1 -0
  33. package/dist/services/vimeo.js +203 -0
  34. package/dist/services/vimeo.js.map +1 -0
  35. package/dist/types/index.d.ts +199 -0
  36. package/dist/types/index.d.ts.map +1 -0
  37. package/dist/types/index.js +15 -0
  38. package/dist/types/index.js.map +1 -0
  39. package/dist/utils/config.d.ts +24 -0
  40. package/dist/utils/config.d.ts.map +1 -0
  41. package/dist/utils/config.js +351 -0
  42. package/dist/utils/config.js.map +1 -0
  43. package/dist/utils/constants.d.ts +4 -0
  44. package/dist/utils/constants.d.ts.map +1 -0
  45. package/dist/utils/constants.js +11 -0
  46. package/dist/utils/constants.js.map +1 -0
  47. package/dist/utils/keychain.d.ts +28 -0
  48. package/dist/utils/keychain.d.ts.map +1 -0
  49. package/dist/utils/keychain.js +214 -0
  50. package/dist/utils/keychain.js.map +1 -0
  51. package/dist/utils/logger.d.ts +23 -0
  52. package/dist/utils/logger.d.ts.map +1 -0
  53. package/dist/utils/logger.js +136 -0
  54. package/dist/utils/logger.js.map +1 -0
  55. package/dist/utils/progress.d.ts +24 -0
  56. package/dist/utils/progress.d.ts.map +1 -0
  57. package/dist/utils/progress.js +108 -0
  58. package/dist/utils/progress.js.map +1 -0
  59. package/dist/utils/schemas.d.ts +117 -0
  60. package/dist/utils/schemas.d.ts.map +1 -0
  61. package/dist/utils/schemas.js +76 -0
  62. package/dist/utils/schemas.js.map +1 -0
  63. package/dist/utils/security.d.ts +47 -0
  64. package/dist/utils/security.d.ts.map +1 -0
  65. package/dist/utils/security.js +138 -0
  66. package/dist/utils/security.js.map +1 -0
  67. package/package.json +73 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Bunny.net
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 ADDED
@@ -0,0 +1,138 @@
1
+ # vimeo2bunny
2
+
3
+ Migrate videos from Vimeo to [Bunny.net Stream](https://bunny.net/stream/). Videos transfer directly from Vimeo to Bunny via URL fetch — nothing is downloaded to your machine.
4
+
5
+ ## Quick Start
6
+
7
+ ```bash
8
+ # Run directly (no install needed)
9
+ npx vimeo2bunny config
10
+ npx vimeo2bunny migrate
11
+ ```
12
+
13
+ ## Installation
14
+
15
+ ### npx (recommended — zero install)
16
+
17
+ Just prefix any command with `npx`:
18
+
19
+ ```bash
20
+ npx vimeo2bunny <command>
21
+ ```
22
+
23
+ ### npm global install
24
+
25
+ ```bash
26
+ npm install -g vimeo2bunny
27
+ vimeo2bunny <command>
28
+ ```
29
+
30
+ ### From source
31
+
32
+ ```bash
33
+ git clone https://github.com/BunnyWay/vimeo2bunny.git
34
+ cd vimeo2bunny
35
+ npm install
36
+ npm run build
37
+ npm link # makes `vimeo2bunny` available globally
38
+ ```
39
+
40
+ ## Setup
41
+
42
+ You need API credentials from both Vimeo and Bunny.net.
43
+
44
+ ### 1. Get your Vimeo access token
45
+
46
+ 1. Go to the [Vimeo Developer Portal](https://developer.vimeo.com/apps)
47
+ 2. Create or select an app
48
+ 3. Generate an access token with scopes: `public`, `private`, `video_files`
49
+
50
+ > **Note:** A Vimeo Standard or Advanced plan is required for download links.
51
+
52
+ ### 2. Get your Bunny Stream credentials
53
+
54
+ 1. Log in to [Bunny.net](https://panel.bunny.net)
55
+ 2. Open your Stream library → **API** section
56
+ 3. Copy the **Library ID** and **Library API Key**
57
+
58
+ > This uses the library-specific API key, not the account-level key.
59
+
60
+ ### 3. Configure the CLI
61
+
62
+ ```bash
63
+ vimeo2bunny config
64
+ ```
65
+
66
+ You'll be prompted for your Vimeo token, Bunny Library ID, and Bunny API key.
67
+
68
+ Alternatively, set environment variables (or use a `.env` file):
69
+
70
+ ```bash
71
+ export VIMEO_ACCESS_TOKEN=your_token
72
+ export BUNNY_LIBRARY_ID=your_library_id
73
+ export BUNNY_LIBRARY_API_KEY=your_api_key
74
+ ```
75
+
76
+ ## Usage
77
+
78
+ ### List your Vimeo content
79
+
80
+ ```bash
81
+ vimeo2bunny list # everything
82
+ vimeo2bunny list --folders # folders only
83
+ vimeo2bunny list --videos # videos only
84
+ ```
85
+
86
+ ### Migrate videos
87
+
88
+ ```bash
89
+ # Preview what will happen (no changes made)
90
+ vimeo2bunny migrate --dry-run
91
+
92
+ # Run the migration
93
+ vimeo2bunny migrate
94
+
95
+ # Migrate a specific folder
96
+ vimeo2bunny migrate --folder <vimeo-folder-id>
97
+
98
+ # Adjust parallel transfers (default: 3, max: 20)
99
+ vimeo2bunny migrate --concurrency 5
100
+
101
+ # Resume an interrupted migration
102
+ vimeo2bunny migrate --resume
103
+ ```
104
+
105
+ ### Check migration status
106
+
107
+ ```bash
108
+ vimeo2bunny status
109
+ ```
110
+
111
+ ## How It Works
112
+
113
+ 1. **Discover** — Fetches all videos and folders from your Vimeo account
114
+ 2. **Map folders** — Creates Bunny collections matching your Vimeo folder structure
115
+ 3. **Transfer** — For each video, gets a download URL from Vimeo and sends it to Bunny's fetch endpoint. Bunny downloads directly from Vimeo.
116
+ 4. **Metadata** — Copies title, description, and tags to the Bunny video
117
+ 5. **Track** — Saves progress to `~/.vimeo2bunny/migration-state.json` so migrations can be resumed
118
+
119
+ ### What gets migrated
120
+
121
+ | Vimeo | Bunny | Notes |
122
+ |-------|-------|-------|
123
+ | `name` | `title` | Direct mapping |
124
+ | `description` | `metaTags[description]` | Stored as meta tag |
125
+ | `tags` | `metaTags[keywords]` | Comma-separated |
126
+ | Folder | Collection | Folder → Collection |
127
+
128
+ ## Troubleshooting
129
+
130
+ **"No download link available"** — Your Vimeo plan must be Standard or Advanced to access download links.
131
+
132
+ **"URL expired"** — Vimeo download URLs expire after 24 hours. The tool fetches fresh URLs, but very long pauses may require restarting.
133
+
134
+ **"Rate limited"** — Handled automatically with exponential backoff. No action needed.
135
+
136
+ ## License
137
+
138
+ MIT — see [LICENSE](LICENSE)
@@ -0,0 +1,3 @@
1
+ export declare function configCommand(): Promise<void>;
2
+ export declare function configResetCommand(): Promise<void>;
3
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":"AAiBA,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAkHnD;AAED,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC,CAYxD"}
@@ -0,0 +1,124 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.configCommand = configCommand;
7
+ exports.configResetCommand = configResetCommand;
8
+ const prompts_1 = require("@inquirer/prompts");
9
+ const chalk_1 = __importDefault(require("chalk"));
10
+ const vimeo_1 = require("../services/vimeo");
11
+ const bunny_1 = require("../services/bunny");
12
+ const config_1 = require("../utils/config");
13
+ const logger_1 = require("../utils/logger");
14
+ const progress_1 = require("../utils/progress");
15
+ async function configCommand() {
16
+ logger_1.logger.header('Vimeo to Bunny Migration - Configuration');
17
+ // Check if using secure keychain storage
18
+ const usingKeychain = await (0, config_1.isUsingKeychain)();
19
+ // Check for existing credentials (from keychain or config file)
20
+ const existingVimeoToken = await (0, config_1.getVimeoTokenAsync)();
21
+ const existingBunnyApiKey = await (0, config_1.getBunnyLibraryApiKeyAsync)();
22
+ const hasVimeoConfig = !!existingVimeoToken;
23
+ const hasBunnyConfig = !!existingBunnyApiKey;
24
+ if (hasVimeoConfig || hasBunnyConfig) {
25
+ logger_1.logger.info('Current configuration:');
26
+ logger_1.logger.info(` Vimeo: ${hasVimeoConfig ? chalk_1.default.green('Configured') : chalk_1.default.gray('Not configured')}`);
27
+ logger_1.logger.info(` Bunny: ${hasBunnyConfig ? chalk_1.default.green('Configured') : chalk_1.default.gray('Not configured')}`);
28
+ logger_1.logger.info(` Storage: ${usingKeychain ? chalk_1.default.green('System Keychain (secure)') : chalk_1.default.yellow('Config file')}`);
29
+ logger_1.logger.newline();
30
+ const reconfigure = await (0, prompts_1.confirm)({
31
+ message: 'Do you want to reconfigure?',
32
+ default: false,
33
+ });
34
+ if (!reconfigure) {
35
+ logger_1.logger.info('Configuration unchanged.');
36
+ return;
37
+ }
38
+ }
39
+ // Vimeo configuration
40
+ logger_1.logger.newline();
41
+ logger_1.logger.info(chalk_1.default.bold('Vimeo Configuration'));
42
+ logger_1.logger.info('Get your access token from: https://developer.vimeo.com/apps');
43
+ logger_1.logger.info('Required scopes: public, private, video_files');
44
+ logger_1.logger.newline();
45
+ const vimeoToken = await (0, prompts_1.password)({
46
+ message: 'Vimeo access token:',
47
+ mask: '*',
48
+ });
49
+ // Validate Vimeo credentials
50
+ const vimeoSpinner = (0, progress_1.createSpinner)('Validating Vimeo credentials...').start();
51
+ try {
52
+ const vimeoClient = (0, vimeo_1.createVimeoClient)(vimeoToken);
53
+ await vimeoClient.validateCredentials();
54
+ const accountInfo = await vimeoClient.getAccountInfo();
55
+ vimeoSpinner.succeed(`Vimeo connected: ${accountInfo.name} (${accountInfo.account})`);
56
+ if (!accountInfo.hasDownloadAccess) {
57
+ logger_1.logger.warn('Warning: Your Vimeo account may not have download access. ' +
58
+ 'Pro/Premium/Business plan is required for video download links.');
59
+ }
60
+ await (0, config_1.setVimeoConfigAsync)(vimeoToken);
61
+ }
62
+ catch (error) {
63
+ vimeoSpinner.fail('Vimeo validation failed');
64
+ logger_1.logger.error(error instanceof Error ? error.message : 'Unknown error');
65
+ return;
66
+ }
67
+ // Bunny configuration
68
+ logger_1.logger.newline();
69
+ logger_1.logger.info(chalk_1.default.bold('Bunny Stream Configuration'));
70
+ logger_1.logger.info('Get your Stream Library API key from your Bunny Stream library settings.');
71
+ logger_1.logger.info('Navigate to: Stream Library → API → Library API Key');
72
+ logger_1.logger.newline();
73
+ const bunnyLibraryId = await (0, prompts_1.input)({
74
+ message: 'Bunny Stream Library ID:',
75
+ });
76
+ const bunnyLibraryApiKey = await (0, prompts_1.password)({
77
+ message: 'Bunny Stream Library API key:',
78
+ mask: '*',
79
+ });
80
+ // Validate Bunny credentials
81
+ const bunnySpinner = (0, progress_1.createSpinner)('Validating Bunny credentials...').start();
82
+ try {
83
+ const bunnyClient = (0, bunny_1.createBunnyClient)(bunnyLibraryApiKey, bunnyLibraryId);
84
+ await bunnyClient.validateCredentials();
85
+ const libraryInfo = await bunnyClient.getLibraryInfo();
86
+ bunnySpinner.succeed(`Bunny connected: ${libraryInfo.name} (${libraryInfo.videoCount} existing videos)`);
87
+ await (0, config_1.setBunnyConfigAsync)(bunnyLibraryApiKey, bunnyLibraryId);
88
+ }
89
+ catch (error) {
90
+ bunnySpinner.fail('Bunny validation failed');
91
+ logger_1.logger.error(error instanceof Error ? error.message : 'Unknown error');
92
+ return;
93
+ }
94
+ logger_1.logger.newline();
95
+ logger_1.logger.success('Configuration saved successfully!');
96
+ // Show where credentials are stored
97
+ const finalUsingKeychain = await (0, config_1.isUsingKeychain)();
98
+ if (finalUsingKeychain) {
99
+ logger_1.logger.info('Credentials stored securely in system keychain');
100
+ }
101
+ else {
102
+ logger_1.logger.info(`Config file: ${(0, config_1.getConfigPath)()}`);
103
+ logger_1.logger.warn('Note: Keychain not available. Credentials stored in config file.');
104
+ }
105
+ logger_1.logger.newline();
106
+ logger_1.logger.info('Next steps:');
107
+ logger_1.logger.info(' 1. Run "vimeo2bunny list" to see your Vimeo content');
108
+ logger_1.logger.info(' 2. Run "vimeo2bunny migrate --dry-run" to preview the migration');
109
+ logger_1.logger.info(' 3. Run "vimeo2bunny migrate" to start the migration');
110
+ }
111
+ async function configResetCommand() {
112
+ const shouldReset = await (0, prompts_1.confirm)({
113
+ message: 'Are you sure you want to clear all configuration?',
114
+ default: false,
115
+ });
116
+ if (shouldReset) {
117
+ await (0, config_1.clearConfigAsync)();
118
+ logger_1.logger.success('Configuration cleared (including keychain credentials).');
119
+ }
120
+ else {
121
+ logger_1.logger.info('Configuration unchanged.');
122
+ }
123
+ }
124
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { ListOptions } from '../types';
2
+ export declare function listCommand(options: ListOptions): Promise<void>;
3
+ //# sourceMappingURL=list.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../src/commands/list.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAGvC,wBAAsB,WAAW,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CA+BrE"}
@@ -0,0 +1,138 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.listCommand = listCommand;
7
+ const chalk_1 = __importDefault(require("chalk"));
8
+ const vimeo_1 = require("../services/vimeo");
9
+ const config_1 = require("../utils/config");
10
+ const logger_1 = require("../utils/logger");
11
+ const progress_1 = require("../utils/progress");
12
+ const security_1 = require("../utils/security");
13
+ async function listCommand(options) {
14
+ const vimeoToken = await (0, config_1.getVimeoTokenAsync)();
15
+ if (!vimeoToken) {
16
+ logger_1.logger.error('Vimeo not configured. Run "vimeo2bunny config" first.');
17
+ process.exit(1);
18
+ }
19
+ const vimeoClient = (0, vimeo_1.createVimeoClient)(vimeoToken);
20
+ // Validate credentials
21
+ const spinner = (0, progress_1.createSpinner)('Connecting to Vimeo...').start();
22
+ try {
23
+ await vimeoClient.validateCredentials();
24
+ spinner.succeed('Connected to Vimeo');
25
+ }
26
+ catch (error) {
27
+ spinner.fail('Failed to connect to Vimeo');
28
+ logger_1.logger.error(error instanceof Error ? error.message : 'Unknown error');
29
+ process.exit(1);
30
+ }
31
+ const showFolders = options.folders || (!options.folders && !options.videos);
32
+ const showVideos = options.videos || (!options.folders && !options.videos);
33
+ if (showFolders) {
34
+ await listFolders(vimeoClient);
35
+ }
36
+ if (showVideos) {
37
+ await listVideos(vimeoClient);
38
+ }
39
+ }
40
+ async function listFolders(vimeoClient) {
41
+ const spinner = (0, progress_1.createSpinner)('Fetching folders...').start();
42
+ try {
43
+ const folders = await vimeoClient.listFolders();
44
+ spinner.succeed(`Found ${folders.length} folders`);
45
+ if (folders.length === 0) {
46
+ logger_1.logger.info('No folders found.');
47
+ return;
48
+ }
49
+ logger_1.logger.newline();
50
+ logger_1.logger.header('Folders');
51
+ const tableData = folders.map((folder) => ({
52
+ ID: vimeoClient.extractIdFromUri(folder.uri),
53
+ Name: (0, security_1.stripAnsi)(folder.name),
54
+ Videos: folder.metadata.connections.videos.total,
55
+ Modified: new Date(folder.modified_time).toLocaleDateString(),
56
+ }));
57
+ logger_1.logger.table(tableData);
58
+ }
59
+ catch (error) {
60
+ spinner.fail('Failed to fetch folders');
61
+ logger_1.logger.error(error instanceof Error ? error.message : 'Unknown error');
62
+ }
63
+ }
64
+ async function listVideos(vimeoClient) {
65
+ const spinner = (0, progress_1.createSpinner)('Fetching videos...').start();
66
+ try {
67
+ const { folders, videos, uncategorizedVideos } = await vimeoClient.getAllVideosWithFolders();
68
+ let totalVideos = uncategorizedVideos.length;
69
+ videos.forEach((v) => (totalVideos += v.length));
70
+ spinner.succeed(`Found ${totalVideos} videos in ${folders.length} folders`);
71
+ logger_1.logger.newline();
72
+ logger_1.logger.header('Videos by Folder');
73
+ // Display videos by folder
74
+ for (const folder of folders) {
75
+ const folderId = vimeoClient.extractIdFromUri(folder.uri);
76
+ const folderVideos = videos.get(folderId) || [];
77
+ if (folderVideos.length === 0)
78
+ continue;
79
+ // Sanitize folder name to prevent terminal injection
80
+ const safeFolderName = (0, security_1.stripAnsi)(folder.name);
81
+ logger_1.logger.info(chalk_1.default.bold(`📁 ${safeFolderName}`));
82
+ for (const video of folderVideos) {
83
+ const videoId = vimeoClient.extractVideoIdFromUri(video.uri);
84
+ const duration = (0, logger_1.formatDuration)(video.duration);
85
+ const resolution = `${video.width}x${video.height}`;
86
+ const hasDownload = video.download && video.download.length > 0;
87
+ const downloadIcon = hasDownload ? chalk_1.default.green('✓') : chalk_1.default.red('✗');
88
+ // Sanitize video name to prevent terminal injection
89
+ const safeVideoName = (0, security_1.stripAnsi)(video.name);
90
+ logger_1.logger.info(` └─ ${safeVideoName} (${duration}, ${resolution}) [Download: ${downloadIcon}]`);
91
+ }
92
+ logger_1.logger.newline();
93
+ }
94
+ // Display uncategorized videos
95
+ if (uncategorizedVideos.length > 0) {
96
+ logger_1.logger.info(chalk_1.default.bold('📁 (Uncategorized)'));
97
+ for (const video of uncategorizedVideos) {
98
+ const duration = (0, logger_1.formatDuration)(video.duration);
99
+ const resolution = `${video.width}x${video.height}`;
100
+ const hasDownload = video.download && video.download.length > 0;
101
+ const downloadIcon = hasDownload ? chalk_1.default.green('✓') : chalk_1.default.red('✗');
102
+ // Sanitize video name to prevent terminal injection
103
+ const safeVideoName = (0, security_1.stripAnsi)(video.name);
104
+ logger_1.logger.info(` └─ ${safeVideoName} (${duration}, ${resolution}) [Download: ${downloadIcon}]`);
105
+ }
106
+ logger_1.logger.newline();
107
+ }
108
+ // Summary
109
+ logger_1.logger.header('Summary');
110
+ let totalDuration = 0;
111
+ let totalSize = 0;
112
+ let videosWithDownload = 0;
113
+ const allVideos = [...uncategorizedVideos];
114
+ videos.forEach((v) => allVideos.push(...v));
115
+ for (const video of allVideos) {
116
+ totalDuration += video.duration || 0;
117
+ if (video.download && video.download.length > 0) {
118
+ totalSize += video.download[0].size || 0;
119
+ videosWithDownload++;
120
+ }
121
+ }
122
+ logger_1.logger.info(`Total folders: ${folders.length}`);
123
+ logger_1.logger.info(`Total videos: ${totalVideos}`);
124
+ logger_1.logger.info(`Total duration: ${(0, logger_1.formatDuration)(totalDuration)}`);
125
+ logger_1.logger.info(`Estimated size: ${(0, logger_1.formatBytes)(totalSize)}`);
126
+ logger_1.logger.info(`Videos with download links: ${videosWithDownload}/${totalVideos}`);
127
+ if (videosWithDownload < totalVideos) {
128
+ logger_1.logger.newline();
129
+ logger_1.logger.warn(`${totalVideos - videosWithDownload} videos don't have download links. ` +
130
+ 'These may require a Vimeo Pro/Premium/Business account to migrate.');
131
+ }
132
+ }
133
+ catch (error) {
134
+ spinner.fail('Failed to fetch videos');
135
+ logger_1.logger.error(error instanceof Error ? error.message : 'Unknown error');
136
+ }
137
+ }
138
+ //# sourceMappingURL=list.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list.js","sourceRoot":"","sources":["../../src/commands/list.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { MigrateOptions } from '../types';
2
+ export declare function migrateCommand(options: MigrateOptions): Promise<void>;
3
+ //# sourceMappingURL=migrate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migrate.d.ts","sourceRoot":"","sources":["../../src/commands/migrate.ts"],"names":[],"mappings":"AAYA,OAAO,EAAE,cAAc,EAAkB,MAAM,UAAU,CAAC;AAG1D,wBAAsB,cAAc,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CA8M3E"}
@@ -0,0 +1,179 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.migrateCommand = migrateCommand;
7
+ const chalk_1 = __importDefault(require("chalk"));
8
+ const prompts_1 = require("@inquirer/prompts");
9
+ const vimeo_1 = require("../services/vimeo");
10
+ const bunny_1 = require("../services/bunny");
11
+ const migration_1 = require("../services/migration");
12
+ const config_1 = require("../utils/config");
13
+ const logger_1 = require("../utils/logger");
14
+ const progress_1 = require("../utils/progress");
15
+ const constants_1 = require("../utils/constants");
16
+ async function migrateCommand(options) {
17
+ const vimeoToken = await (0, config_1.getVimeoTokenAsync)();
18
+ const bunnyLibraryApiKey = await (0, config_1.getBunnyLibraryApiKeyAsync)();
19
+ const libraryId = options.libraryId || await (0, config_1.getBunnyLibraryIdAsync)();
20
+ // Check Vimeo config
21
+ if (!vimeoToken) {
22
+ logger_1.logger.error('Vimeo not configured. Run "vimeo2bunny config" first.');
23
+ process.exit(1);
24
+ }
25
+ // Check Bunny config
26
+ if (!bunnyLibraryApiKey || !libraryId) {
27
+ logger_1.logger.error('Bunny Stream not configured. Run "vimeo2bunny config" first.');
28
+ process.exit(1);
29
+ }
30
+ logger_1.logger.header('Vimeo to Bunny Stream Migration');
31
+ // Convert timeout options to milliseconds
32
+ const requestTimeoutMs = options.requestTimeout
33
+ ? options.requestTimeout * 1000
34
+ : constants_1.DEFAULT_REQUEST_TIMEOUT;
35
+ const processingTimeoutMs = options.processingTimeout
36
+ ? options.processingTimeout * 60 * 1000
37
+ : constants_1.DEFAULT_PROCESSING_TIMEOUT;
38
+ // Create clients with timeout configuration
39
+ const vimeoClient = (0, vimeo_1.createVimeoClient)(vimeoToken, requestTimeoutMs);
40
+ const bunnyClient = (0, bunny_1.createBunnyClient)(bunnyLibraryApiKey, libraryId, requestTimeoutMs, processingTimeoutMs);
41
+ // Validate connections
42
+ const spinner = (0, progress_1.createSpinner)('Validating connections...').start();
43
+ let bunnyVideoCount = 0;
44
+ try {
45
+ await vimeoClient.validateCredentials();
46
+ const vimeoAccount = await vimeoClient.getAccountInfo();
47
+ spinner.text = 'Vimeo connected, checking Bunny...';
48
+ await bunnyClient.validateCredentials();
49
+ const bunnyLibrary = await bunnyClient.getLibraryInfo();
50
+ bunnyVideoCount = bunnyLibrary.videoCount;
51
+ spinner.succeed('Both services connected');
52
+ logger_1.logger.info(`Vimeo account: ${vimeoAccount.name} (${vimeoAccount.account})`);
53
+ logger_1.logger.info(`Bunny library: ${bunnyLibrary.name} (${bunnyLibrary.videoCount} existing videos)`);
54
+ if (!vimeoAccount.hasDownloadAccess) {
55
+ logger_1.logger.warn('Warning: Your Vimeo account may not have download access. ' +
56
+ 'Some videos may fail to migrate.');
57
+ }
58
+ }
59
+ catch (error) {
60
+ spinner.fail('Connection validation failed');
61
+ logger_1.logger.error(error instanceof Error ? error.message : 'Unknown error');
62
+ process.exit(1);
63
+ }
64
+ // Create migration service
65
+ const migrationService = (0, migration_1.createMigrationService)(vimeoClient, bunnyClient);
66
+ // Get summary
67
+ logger_1.logger.newline();
68
+ const summary = await migrationService.getSummary(options.folder);
69
+ logger_1.logger.info(chalk_1.default.bold('Migration Summary:'));
70
+ logger_1.logger.info(` Vimeo: ${summary.totalVideos} videos in ${summary.totalFolders} folders`);
71
+ logger_1.logger.info(` Bunny: ${bunnyVideoCount} existing videos (${summary.alreadyMigrated} synced from Vimeo)`);
72
+ logger_1.logger.newline();
73
+ logger_1.logger.info('Status:');
74
+ logger_1.logger.info(` - Already migrated: ${summary.alreadyMigrated}`);
75
+ logger_1.logger.info(` - New to migrate: ${summary.newVideos}`);
76
+ if (summary.totalDuration > 0) {
77
+ logger_1.logger.info(` - Total duration: ${(0, logger_1.formatDuration)(summary.totalDuration)}`);
78
+ }
79
+ if (summary.totalSize > 0) {
80
+ logger_1.logger.info(` - Estimated size: ${(0, logger_1.formatBytes)(summary.totalSize)}`);
81
+ }
82
+ logger_1.logger.newline();
83
+ // If all videos are already migrated, inform user and exit
84
+ if (summary.newVideos === 0 && summary.totalVideos > 0) {
85
+ logger_1.logger.success('All videos have already been migrated!');
86
+ logger_1.logger.info('Check your Bunny Stream dashboard to verify the videos.');
87
+ process.exit(0);
88
+ }
89
+ // Show videos to migrate
90
+ if (summary.newVideosList.length > 0) {
91
+ logger_1.logger.info(`Videos to migrate (${summary.newVideos}):`);
92
+ summary.newVideosList.forEach((v) => {
93
+ const folderInfo = v.folder ? ` (${v.folder})` : ' (uncategorized)';
94
+ logger_1.logger.info(` - ${v.name}${folderInfo}`);
95
+ });
96
+ logger_1.logger.newline();
97
+ }
98
+ // Show verbose migrated list if --verbose flag is set
99
+ if (options.verbose && summary.migratedVideosList.length > 0) {
100
+ logger_1.logger.info(`Already migrated (${summary.alreadyMigrated}):`);
101
+ summary.migratedVideosList.forEach((v) => {
102
+ const folderInfo = v.folder ? ` (${v.folder})` : ' (uncategorized)';
103
+ logger_1.logger.info(` - ${v.name}${folderInfo}`);
104
+ });
105
+ logger_1.logger.newline();
106
+ }
107
+ // Dry run mode
108
+ if (options.dryRun) {
109
+ logger_1.logger.info(chalk_1.default.yellow('DRY RUN MODE - No changes will be made'));
110
+ logger_1.logger.newline();
111
+ await migrationService.runMigration(libraryId, {
112
+ dryRun: true,
113
+ folderId: options.folder,
114
+ });
115
+ logger_1.logger.newline();
116
+ logger_1.logger.info('To run the actual migration, remove the --dry-run flag.');
117
+ return;
118
+ }
119
+ // Confirm migration
120
+ const shouldProceed = await (0, prompts_1.confirm)({
121
+ message: `Proceed with migrating ${summary.newVideos} videos?`,
122
+ default: true,
123
+ });
124
+ if (!shouldProceed) {
125
+ logger_1.logger.info('Migration cancelled.');
126
+ return;
127
+ }
128
+ // Run migration
129
+ const startTime = new Date();
130
+ const tracker = (0, progress_1.createProgressTracker)(summary.totalVideos);
131
+ logger_1.logger.newline();
132
+ logger_1.logger.info('Starting migration...');
133
+ logger_1.logger.info(`Concurrency: ${options.concurrency || 3} parallel transfers`);
134
+ logger_1.logger.newline();
135
+ try {
136
+ const state = await migrationService.runMigration(libraryId, {
137
+ dryRun: false,
138
+ folderId: options.folder,
139
+ concurrency: options.concurrency || 3,
140
+ resume: options.resume,
141
+ onProgress: (migrationState) => {
142
+ // Update tracker
143
+ tracker.completed = migrationState.videoMigrations.filter((m) => m.status === 'completed').length;
144
+ tracker.failed = migrationState.videoMigrations.filter((m) => m.status === 'failed').length;
145
+ tracker.processing = migrationState.videoMigrations.filter((m) => m.status === 'processing' || m.status === 'fetching').length;
146
+ tracker.pending = migrationState.videoMigrations.filter((m) => m.status === 'pending').length;
147
+ },
148
+ });
149
+ // Final summary
150
+ (0, progress_1.renderSummary)(tracker, startTime);
151
+ // Show failed videos if any
152
+ const failed = state.videoMigrations.filter((m) => m.status === 'failed');
153
+ if (failed.length > 0) {
154
+ logger_1.logger.newline();
155
+ logger_1.logger.error('Failed videos:');
156
+ failed.forEach((m) => {
157
+ logger_1.logger.error(` - ${m.vimeoVideoName}: ${m.error}`);
158
+ });
159
+ logger_1.logger.newline();
160
+ logger_1.logger.info('You can resume the migration to retry failed videos: vimeo2bunny migrate --resume');
161
+ }
162
+ // Success message
163
+ if (state.status === 'completed') {
164
+ logger_1.logger.newline();
165
+ logger_1.logger.success('Migration completed successfully!');
166
+ logger_1.logger.info('Check your Bunny Stream dashboard to verify the videos.');
167
+ }
168
+ // Exit cleanly (axios keep-alive connections prevent automatic exit)
169
+ process.exit(0);
170
+ }
171
+ catch (error) {
172
+ logger_1.logger.error('Migration failed:');
173
+ logger_1.logger.error(error instanceof Error ? error.message : 'Unknown error');
174
+ logger_1.logger.newline();
175
+ logger_1.logger.info('You can resume the migration: vimeo2bunny migrate --resume');
176
+ process.exit(1);
177
+ }
178
+ }
179
+ //# sourceMappingURL=migrate.js.map