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.
- package/LICENSE +21 -0
- package/README.md +138 -0
- package/dist/commands/config.d.ts +3 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +124 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/list.d.ts +3 -0
- package/dist/commands/list.d.ts.map +1 -0
- package/dist/commands/list.js +138 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/migrate.d.ts +3 -0
- package/dist/commands/migrate.d.ts.map +1 -0
- package/dist/commands/migrate.js +179 -0
- package/dist/commands/migrate.js.map +1 -0
- package/dist/commands/status.d.ts +2 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +105 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +101 -0
- package/dist/index.js.map +1 -0
- package/dist/services/bunny.d.ts +42 -0
- package/dist/services/bunny.d.ts.map +1 -0
- package/dist/services/bunny.js +291 -0
- package/dist/services/bunny.js.map +1 -0
- package/dist/services/migration.d.ts +49 -0
- package/dist/services/migration.d.ts.map +1 -0
- package/dist/services/migration.js +425 -0
- package/dist/services/migration.js.map +1 -0
- package/dist/services/vimeo.d.ts +28 -0
- package/dist/services/vimeo.d.ts.map +1 -0
- package/dist/services/vimeo.js +203 -0
- package/dist/services/vimeo.js.map +1 -0
- package/dist/types/index.d.ts +199 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +15 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/config.d.ts +24 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/config.js +351 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/constants.d.ts +4 -0
- package/dist/utils/constants.d.ts.map +1 -0
- package/dist/utils/constants.js +11 -0
- package/dist/utils/constants.js.map +1 -0
- package/dist/utils/keychain.d.ts +28 -0
- package/dist/utils/keychain.d.ts.map +1 -0
- package/dist/utils/keychain.js +214 -0
- package/dist/utils/keychain.js.map +1 -0
- package/dist/utils/logger.d.ts +23 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +136 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/progress.d.ts +24 -0
- package/dist/utils/progress.d.ts.map +1 -0
- package/dist/utils/progress.js +108 -0
- package/dist/utils/progress.js.map +1 -0
- package/dist/utils/schemas.d.ts +117 -0
- package/dist/utils/schemas.d.ts.map +1 -0
- package/dist/utils/schemas.js +76 -0
- package/dist/utils/schemas.js.map +1 -0
- package/dist/utils/security.d.ts +47 -0
- package/dist/utils/security.d.ts.map +1 -0
- package/dist/utils/security.js +138 -0
- package/dist/utils/security.js.map +1 -0
- 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 @@
|
|
|
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 @@
|
|
|
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 @@
|
|
|
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
|