tor-dl 1.0.19 → 1.0.21
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/.watchlist-cache.json +37 -0
- package/README.md +112 -0
- package/dist/cli/parser.d.ts.map +1 -1
- package/dist/cli/parser.js +87 -1
- package/dist/cli/parser.js.map +1 -1
- package/dist/commands/user.d.ts +14 -0
- package/dist/commands/user.d.ts.map +1 -0
- package/dist/commands/user.js +313 -0
- package/dist/commands/user.js.map +1 -0
- package/dist/types.d.ts +1 -0
- package/dist/types.d.ts.map +1 -1
- package/filters.json +10 -9
- package/package.json +1 -1
- package/src/cli/parser.ts +93 -1
- package/src/commands/user.ts +318 -0
- package/src/types.ts +1 -0
- package/users.json +5 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"num": 1,
|
|
4
|
+
"name": "Children of Men",
|
|
5
|
+
"year": "2006",
|
|
6
|
+
"rating": "",
|
|
7
|
+
"url": "https://letterboxd.com/film/children-of-men/"
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
"num": 2,
|
|
11
|
+
"name": "The Drama",
|
|
12
|
+
"year": "2026",
|
|
13
|
+
"rating": "",
|
|
14
|
+
"url": "https://letterboxd.com/film/the-drama/"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"num": 3,
|
|
18
|
+
"name": "Forbidden Fruits",
|
|
19
|
+
"year": "2026",
|
|
20
|
+
"rating": "",
|
|
21
|
+
"url": "https://letterboxd.com/film/forbidden-fruits-2026/"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"num": 4,
|
|
25
|
+
"name": "They Will Kill You",
|
|
26
|
+
"year": "2026",
|
|
27
|
+
"rating": "",
|
|
28
|
+
"url": "https://letterboxd.com/film/they-will-kill-you/"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"num": 5,
|
|
32
|
+
"name": "Ready or Not 2: Here I Come",
|
|
33
|
+
"year": "2026",
|
|
34
|
+
"rating": "",
|
|
35
|
+
"url": "https://letterboxd.com/film/ready-or-not-2-here-i-come/"
|
|
36
|
+
}
|
|
37
|
+
]
|
package/README.md
CHANGED
|
@@ -106,6 +106,118 @@ Same as `o` command - opens .torrent in browser or copies magnet link.
|
|
|
106
106
|
tor-dl open <number>
|
|
107
107
|
```
|
|
108
108
|
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## Letterboxd Integration
|
|
112
|
+
|
|
113
|
+
tor-dl can fetch movies from your Letterboxd watchlist and search for torrents directly.
|
|
114
|
+
|
|
115
|
+
### Setup Your Username
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
tor-dl setuser Sedat85
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Replace `Sedat85` with your actual Letterboxd username.
|
|
122
|
+
|
|
123
|
+
### List Your Watchlist
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
tor-dl list
|
|
127
|
+
tor-dl list -l 10 # Limit to 10 movies
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
This fetches movies from your Letterboxd watchlist and displays them with numbers.
|
|
131
|
+
|
|
132
|
+
**Example output:**
|
|
133
|
+
|
|
134
|
+
```
|
|
135
|
+
--- Watchlist ---
|
|
136
|
+
1. Children of Men (2006)
|
|
137
|
+
2. The Drama (2026)
|
|
138
|
+
3. Forbidden Fruits (2026)
|
|
139
|
+
----------------
|
|
140
|
+
Use: tor-dl find <number> to search and download
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Find and Download
|
|
144
|
+
|
|
145
|
+
After listing your watchlist, use the movie number to search and download:
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
tor-dl find 1 # Search with year (default)
|
|
149
|
+
tor-dl find 1 --noyear # Search without year
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
- Default: searches "Movie Title 2026" (includes year for accuracy)
|
|
153
|
+
- `--noyear`: searches "Movie Title" only (more results)
|
|
154
|
+
|
|
155
|
+
### Configure Search Filters
|
|
156
|
+
|
|
157
|
+
Set default filters for all searches:
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
tor-dl setfilter -c movie # Category: movie
|
|
161
|
+
tor-dl setfilter -s 50 # Minimum 50 seeds
|
|
162
|
+
tor-dl setfilter --min-size 1GB # Minimum 1GB
|
|
163
|
+
tor-dl setfilter --max-size 5GB # Maximum 5GB
|
|
164
|
+
tor-dl setfilter -o seeds # Sort by seeds
|
|
165
|
+
tor-dl setfilter --order desc # Order: descending
|
|
166
|
+
tor-dl setfilter -l 30 # Limit 30 results
|
|
167
|
+
tor-dl setfilter -S yts,torrentscsv # Sources: YTS & torrentscsv
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### View Current Settings
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
tor-dl filters # Show current search filters
|
|
174
|
+
tor-dl user # Show current username
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
**Filters output:**
|
|
178
|
+
|
|
179
|
+
```
|
|
180
|
+
--- Current Filters ---
|
|
181
|
+
Category: movie
|
|
182
|
+
Min Seeds: 0
|
|
183
|
+
Max Seeds: unlimited
|
|
184
|
+
Min Size: 0
|
|
185
|
+
Max Size: 50GB
|
|
186
|
+
Sort By: seeds
|
|
187
|
+
Order: desc
|
|
188
|
+
Limit: 60
|
|
189
|
+
Sources: yts,thepiratebay,nyaa,torrentscsv
|
|
190
|
+
----------------------
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
**User output:**
|
|
194
|
+
|
|
195
|
+
```
|
|
196
|
+
--- User ---
|
|
197
|
+
Letterboxd: Sedat85
|
|
198
|
+
-----------
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### Complete Workflow
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
# 1. Set your Letterboxd username
|
|
205
|
+
tor-dl setuser Sedat85
|
|
206
|
+
|
|
207
|
+
# 2. Set preferred filters (optional)
|
|
208
|
+
tor-dl setfilter -c movie -s 50 --min-size 1GB
|
|
209
|
+
|
|
210
|
+
# 3. List your watchlist
|
|
211
|
+
tor-dl list
|
|
212
|
+
|
|
213
|
+
# 4. Find and download movie #1
|
|
214
|
+
tor-dl find 1
|
|
215
|
+
# OR without year for more results
|
|
216
|
+
tor-dl find 1 --noyear
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
109
221
|
### Options
|
|
110
222
|
|
|
111
223
|
| Flag | Description |
|
package/dist/cli/parser.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/cli/parser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGpC,OAAO,EAAE,YAAY,EAAiB,MAAM,UAAU,CAAC;AAWvD,wBAAgB,WAAW,IAAI,YAAY,
|
|
1
|
+
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/cli/parser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGpC,OAAO,EAAE,YAAY,EAAiB,MAAM,UAAU,CAAC;AAWvD,wBAAgB,WAAW,IAAI,YAAY,CAe1C;AAED,wBAAgB,YAAY,IAAI,OAAO,CAkLtC"}
|
package/dist/cli/parser.js
CHANGED
|
@@ -55,6 +55,7 @@ function loadFilters() {
|
|
|
55
55
|
return {
|
|
56
56
|
category: 'all',
|
|
57
57
|
minSeeds: 0,
|
|
58
|
+
maxSeeds: 0,
|
|
58
59
|
minSize: '0',
|
|
59
60
|
maxSize: '50GB',
|
|
60
61
|
sortBy: 'seeds',
|
|
@@ -68,7 +69,24 @@ function createParser() {
|
|
|
68
69
|
const mainHelp = `
|
|
69
70
|
Categories: all, movie, tv, anime, music, games, apps
|
|
70
71
|
Sources:
|
|
71
|
-
yts -
|
|
72
|
+
yts - movies | torrentscsv - general | thepiratebay - general | nyaa - anime
|
|
73
|
+
|
|
74
|
+
Letterboxd Integration:
|
|
75
|
+
setuser <username> Set your Letterboxd username
|
|
76
|
+
list List movies from your watchlist
|
|
77
|
+
find <number> Search and download a movie from list
|
|
78
|
+
filters Show current search filters
|
|
79
|
+
user Show current username
|
|
80
|
+
|
|
81
|
+
Examples:
|
|
82
|
+
tor-dl setuser Sedat85 # Set Letterboxd username
|
|
83
|
+
tor-dl list # List watchlist movies
|
|
84
|
+
tor-dl list -l 10 # Limit to 10 movies
|
|
85
|
+
tor-dl find 1 # Find first movie (with year)
|
|
86
|
+
tor-dl find 1 --noyear # Find without year
|
|
87
|
+
tor-dl setfilter -c movie -s 50 # Set filters
|
|
88
|
+
tor-dl filters # Show filters
|
|
89
|
+
tor-dl user # Show username`;
|
|
72
90
|
program
|
|
73
91
|
.name('tor-dl')
|
|
74
92
|
.description('CLI torrent search tool - search, open in browser, copy magnet links' + mainHelp)
|
|
@@ -135,6 +153,74 @@ Sources:
|
|
|
135
153
|
const { openInBrowser } = await Promise.resolve().then(() => __importStar(require('../commands/download')));
|
|
136
154
|
await openInBrowser(parseInt(number));
|
|
137
155
|
});
|
|
156
|
+
program
|
|
157
|
+
.command('setuser <username>')
|
|
158
|
+
.description('Set Letterboxd username (e.g., tor-dl setuser Sedat85)')
|
|
159
|
+
.action(async (username) => {
|
|
160
|
+
const { setUserCommand } = await Promise.resolve().then(() => __importStar(require('../commands/user')));
|
|
161
|
+
await setUserCommand(username);
|
|
162
|
+
});
|
|
163
|
+
program
|
|
164
|
+
.command('setfilter [filters...]')
|
|
165
|
+
.description('Set search filters (e.g., tor-dl setfilter -c movie -s 100 --min-size 1GB)')
|
|
166
|
+
.option('-c, --cat <category>', 'Category (all|movie|tv|anime|music|games|apps)')
|
|
167
|
+
.option('-s, --min-seeds <number>', 'Minimum seeders', parseInt)
|
|
168
|
+
.option('--max-seeds <number>', 'Maximum seeders', parseInt)
|
|
169
|
+
.option('--min-size <size>', 'Min size (e.g. 500MB, 1GB)')
|
|
170
|
+
.option('--max-size <size>', 'Max size (e.g. 5GB)')
|
|
171
|
+
.option('-o, --sort <sortBy>', 'Sort by (seeds|size|date)')
|
|
172
|
+
.option('--order <order>', 'Order (asc|desc)')
|
|
173
|
+
.option('-l, --limit <number>', 'Max results', parseInt)
|
|
174
|
+
.option('-S, --sources <sources>', 'Sources (yts,torrentscsv,thepiratebay,nyaa)')
|
|
175
|
+
.action(async (filters, options) => {
|
|
176
|
+
const { setFilterCommand } = await Promise.resolve().then(() => __importStar(require('../commands/user')));
|
|
177
|
+
await setFilterCommand(options);
|
|
178
|
+
});
|
|
179
|
+
program
|
|
180
|
+
.command('list')
|
|
181
|
+
.description('List movies from Letterboxd watchlist')
|
|
182
|
+
.option('-l, --limit <number>', 'Max movies to show', parseInt)
|
|
183
|
+
.action(async (options) => {
|
|
184
|
+
const { listCommand } = await Promise.resolve().then(() => __importStar(require('../commands/user')));
|
|
185
|
+
await listCommand(options);
|
|
186
|
+
});
|
|
187
|
+
program
|
|
188
|
+
.command('filters')
|
|
189
|
+
.description('Show current search filters')
|
|
190
|
+
.action(async () => {
|
|
191
|
+
const filters = loadFilters();
|
|
192
|
+
console.log('\n--- Current Filters ---');
|
|
193
|
+
console.log('Category:', filters.category);
|
|
194
|
+
console.log('Min Seeds:', filters.minSeeds);
|
|
195
|
+
console.log('Max Seeds:', filters.maxSeeds || 'unlimited');
|
|
196
|
+
console.log('Min Size:', filters.minSize);
|
|
197
|
+
console.log('Max Size:', filters.maxSize);
|
|
198
|
+
console.log('Sort By:', filters.sortBy);
|
|
199
|
+
console.log('Order:', filters.order);
|
|
200
|
+
console.log('Limit:', filters.limit);
|
|
201
|
+
console.log('Sources:', filters.sources || 'all');
|
|
202
|
+
console.log('----------------------\n');
|
|
203
|
+
});
|
|
204
|
+
program
|
|
205
|
+
.command('user')
|
|
206
|
+
.description('Show current username')
|
|
207
|
+
.action(async () => {
|
|
208
|
+
const { loadUserConfig } = await Promise.resolve().then(() => __importStar(require('../commands/user')));
|
|
209
|
+
const config = loadUserConfig();
|
|
210
|
+
const username = config.letterboxd.username;
|
|
211
|
+
console.log('\n--- User ---');
|
|
212
|
+
console.log('Letterboxd:', username || '(not set)');
|
|
213
|
+
console.log('-----------\n');
|
|
214
|
+
});
|
|
215
|
+
program
|
|
216
|
+
.command('find <number>')
|
|
217
|
+
.description('Download movie by number from list')
|
|
218
|
+
.option('--noyear', 'Search without year')
|
|
219
|
+
.allowUnknownOption()
|
|
220
|
+
.action(async (number, options) => {
|
|
221
|
+
const { findCommand } = await Promise.resolve().then(() => __importStar(require('../commands/user')));
|
|
222
|
+
await findCommand(parseInt(number), options);
|
|
223
|
+
});
|
|
138
224
|
program.on('command:*', () => {
|
|
139
225
|
console.error('Invalid command: %s\nSee --help for a list of available commands.', program.args[0]);
|
|
140
226
|
process.exit(1);
|
package/dist/cli/parser.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parser.js","sourceRoot":"","sources":["../../src/cli/parser.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAcA,
|
|
1
|
+
{"version":3,"file":"parser.js","sourceRoot":"","sources":["../../src/cli/parser.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
interface UserConfig {
|
|
2
|
+
letterboxd: {
|
|
3
|
+
username: string;
|
|
4
|
+
};
|
|
5
|
+
}
|
|
6
|
+
declare function loadUsers(): UserConfig;
|
|
7
|
+
export { loadUsers as loadUserConfig };
|
|
8
|
+
export declare function setUserCommand(username: string): Promise<void>;
|
|
9
|
+
export declare function setFilterCommand(options: any): Promise<void>;
|
|
10
|
+
export declare function listCommand(options: {
|
|
11
|
+
limit?: number;
|
|
12
|
+
}): Promise<void>;
|
|
13
|
+
export declare function findCommand(number: number, options?: any): Promise<void>;
|
|
14
|
+
//# sourceMappingURL=user.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"user.d.ts","sourceRoot":"","sources":["../../src/commands/user.ts"],"names":[],"mappings":"AASA,UAAU,UAAU;IAClB,UAAU,EAAE;QACV,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC;CACH;AAMD,iBAAS,SAAS,IAAI,UAAU,CAM/B;AAED,OAAO,EAAE,SAAS,IAAI,cAAc,EAAE,CAAC;AAMvC,wBAAsB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAKpE;AAED,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAoBlE;AAgCD,wBAAsB,WAAW,CAAC,OAAO,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA2L5E;AAED,wBAAsB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,GAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAoClF"}
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.loadUserConfig = loadUsers;
|
|
40
|
+
exports.setUserCommand = setUserCommand;
|
|
41
|
+
exports.setFilterCommand = setFilterCommand;
|
|
42
|
+
exports.listCommand = listCommand;
|
|
43
|
+
exports.findCommand = findCommand;
|
|
44
|
+
const fs_1 = require("fs");
|
|
45
|
+
const path_1 = require("path");
|
|
46
|
+
const axios_1 = __importDefault(require("axios"));
|
|
47
|
+
const cheerio = __importStar(require("cheerio"));
|
|
48
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
49
|
+
const ora_1 = __importDefault(require("ora"));
|
|
50
|
+
const parser_1 = require("../cli/parser");
|
|
51
|
+
function getUsersPath() {
|
|
52
|
+
return (0, path_1.join)(__dirname, '../../users.json');
|
|
53
|
+
}
|
|
54
|
+
function loadUsers() {
|
|
55
|
+
const usersPath = getUsersPath();
|
|
56
|
+
if ((0, fs_1.existsSync)(usersPath)) {
|
|
57
|
+
return JSON.parse((0, fs_1.readFileSync)(usersPath, 'utf-8'));
|
|
58
|
+
}
|
|
59
|
+
return { letterboxd: { username: '' } };
|
|
60
|
+
}
|
|
61
|
+
function saveUsers(config) {
|
|
62
|
+
(0, fs_1.writeFileSync)(getUsersPath(), JSON.stringify(config, null, 2));
|
|
63
|
+
}
|
|
64
|
+
async function setUserCommand(username) {
|
|
65
|
+
const config = loadUsers();
|
|
66
|
+
config.letterboxd.username = username;
|
|
67
|
+
saveUsers(config);
|
|
68
|
+
console.log(chalk_1.default.green(`Username set to: ${username}`));
|
|
69
|
+
}
|
|
70
|
+
async function setFilterCommand(options) {
|
|
71
|
+
const filters = (0, parser_1.loadFilters)();
|
|
72
|
+
if (options.cat)
|
|
73
|
+
filters.category = options.cat;
|
|
74
|
+
if (options.minSeeds !== undefined)
|
|
75
|
+
filters.minSeeds = options.minSeeds;
|
|
76
|
+
if (options.maxSeeds)
|
|
77
|
+
filters.maxSeeds = options.maxSeeds;
|
|
78
|
+
if (options.minSize)
|
|
79
|
+
filters.minSize = options.minSize;
|
|
80
|
+
if (options.maxSize)
|
|
81
|
+
filters.maxSize = options.maxSize;
|
|
82
|
+
if (options.sort)
|
|
83
|
+
filters.sortBy = options.sort;
|
|
84
|
+
if (options.order)
|
|
85
|
+
filters.order = options.order;
|
|
86
|
+
if (options.limit)
|
|
87
|
+
filters.limit = options.limit;
|
|
88
|
+
if (options.sources) {
|
|
89
|
+
const srcArr = options.sources.split ? options.sources.split(',') : options.sources;
|
|
90
|
+
filters.sources = Array.isArray(srcArr) ? srcArr.join(',') : srcArr;
|
|
91
|
+
}
|
|
92
|
+
const filtersPath = (0, path_1.join)(__dirname, '../../filters.json');
|
|
93
|
+
(0, fs_1.writeFileSync)(filtersPath, JSON.stringify(filters, null, 2));
|
|
94
|
+
console.log(chalk_1.default.green('Filters updated:'));
|
|
95
|
+
console.log(JSON.stringify(filters, null, 2));
|
|
96
|
+
}
|
|
97
|
+
function getWatchlistCachePath() {
|
|
98
|
+
return (0, path_1.join)(__dirname, '../../.watchlist-cache.json');
|
|
99
|
+
}
|
|
100
|
+
function loadWatchlistCache() {
|
|
101
|
+
const path = getWatchlistCachePath();
|
|
102
|
+
if ((0, fs_1.existsSync)(path)) {
|
|
103
|
+
try {
|
|
104
|
+
return JSON.parse((0, fs_1.readFileSync)(path, 'utf-8'));
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
return [];
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return [];
|
|
111
|
+
}
|
|
112
|
+
function saveWatchlistCache(movies) {
|
|
113
|
+
(0, fs_1.writeFileSync)(getWatchlistCachePath(), JSON.stringify(movies, null, 2));
|
|
114
|
+
}
|
|
115
|
+
let watchlistCache = [];
|
|
116
|
+
async function listCommand(options) {
|
|
117
|
+
const config = loadUsers();
|
|
118
|
+
const username = config.letterboxd.username;
|
|
119
|
+
if (!username) {
|
|
120
|
+
console.log(chalk_1.default.red('No username set. Use: tor-dl setuser <username>'));
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
const spinner = (0, ora_1.default)(`Fetching watchlist for ${username}...`).start();
|
|
124
|
+
try {
|
|
125
|
+
let movies = [];
|
|
126
|
+
const profileUrl = `https://letterboxd.com/${username}/`;
|
|
127
|
+
const headers = {
|
|
128
|
+
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
|
|
129
|
+
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
|
|
130
|
+
'Accept-Language': 'en-GB,en-US;q=0.9,en;q=0.8',
|
|
131
|
+
'Sec-Fetch-Dest': 'document',
|
|
132
|
+
'Sec-Fetch-Mode': 'navigate',
|
|
133
|
+
'Sec-Fetch-Site': 'none',
|
|
134
|
+
'Sec-Fetch-User': '?1',
|
|
135
|
+
'Upgrade-Insecure-Requests': '1'
|
|
136
|
+
};
|
|
137
|
+
const response = await axios_1.default.get(profileUrl, { headers, timeout: 30000, validateStatus: () => true });
|
|
138
|
+
if (response.status === 403) {
|
|
139
|
+
spinner.warn('Letterboxd blocking requests. Try using: tor-dl search "movie name" directly');
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
if (!response.data) {
|
|
143
|
+
spinner.warn('Empty response from Letterboxd');
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
const $ = cheerio.load(response.data);
|
|
147
|
+
$('[data-item-name]').each((i, elem) => {
|
|
148
|
+
if (options.limit && i >= options.limit)
|
|
149
|
+
return;
|
|
150
|
+
const el = $(elem);
|
|
151
|
+
const fullName = el.attr('data-item-name') || '';
|
|
152
|
+
const href = el.attr('data-item-link') || '';
|
|
153
|
+
if (fullName && href && href.includes('/film/')) {
|
|
154
|
+
const exists = movies.some(m => m.name === fullName);
|
|
155
|
+
if (!exists) {
|
|
156
|
+
const yearMatch = fullName.match(/\((\d{4})\)$/);
|
|
157
|
+
const movieName = yearMatch ? fullName.replace(/\s*\(\d{4}\)\s*$/, '').trim() : fullName;
|
|
158
|
+
movies.push({
|
|
159
|
+
num: movies.length + 1,
|
|
160
|
+
name: movieName,
|
|
161
|
+
year: yearMatch ? yearMatch[1] : '',
|
|
162
|
+
rating: '',
|
|
163
|
+
url: 'https://letterboxd.com' + href
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
if (movies.length === 0) {
|
|
169
|
+
$('a[href*="/film/"][title]').each((i, elem) => {
|
|
170
|
+
if (options.limit && i >= options.limit)
|
|
171
|
+
return;
|
|
172
|
+
const el = $(elem);
|
|
173
|
+
const name = el.attr('title') || '';
|
|
174
|
+
const href = el.attr('href') || '';
|
|
175
|
+
if (name && name.length > 1 && name.length < 200 && href.includes('/film/')) {
|
|
176
|
+
const exists = movies.some(m => m.name === name);
|
|
177
|
+
if (!exists) {
|
|
178
|
+
const yearMatch = name.match(/\((\d{4})\)$/);
|
|
179
|
+
movies.push({
|
|
180
|
+
num: movies.length + 1,
|
|
181
|
+
name: name,
|
|
182
|
+
year: yearMatch ? yearMatch[1] : '',
|
|
183
|
+
rating: '',
|
|
184
|
+
url: 'https://letterboxd.com' + href
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
if (movies.length === 0) {
|
|
191
|
+
$('a[href*="/film/"]').each((i, elem) => {
|
|
192
|
+
if (options.limit && i >= options.limit)
|
|
193
|
+
return;
|
|
194
|
+
const el = $(elem);
|
|
195
|
+
const href = el.attr('href') || '';
|
|
196
|
+
let name = el.text().trim();
|
|
197
|
+
name = name.replace(/\s+/g, ' ').replace(/^\d+\.\s*/, '').trim();
|
|
198
|
+
if (name && name.length > 1 && name.length < 200 && href.includes('/film/')) {
|
|
199
|
+
const exists = movies.some(m => m.name === name);
|
|
200
|
+
if (!exists) {
|
|
201
|
+
const yearMatch = name.match(/\((\d{4})\)$/);
|
|
202
|
+
let movieYear = yearMatch ? yearMatch[1] : '';
|
|
203
|
+
if (!movieYear) {
|
|
204
|
+
const urlYearMatch = href.match(/-(\d{4})\/?$/);
|
|
205
|
+
if (urlYearMatch)
|
|
206
|
+
movieYear = urlYearMatch[1];
|
|
207
|
+
}
|
|
208
|
+
movies.push({
|
|
209
|
+
num: movies.length + 1,
|
|
210
|
+
name: yearMatch ? name.replace(/\s*\(\d{4}\)\s*$/, '').trim() : name,
|
|
211
|
+
year: movieYear,
|
|
212
|
+
rating: '',
|
|
213
|
+
url: 'https://letterboxd.com' + href
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
if (movies.length > 0) {
|
|
220
|
+
watchlistCache = movies;
|
|
221
|
+
saveWatchlistCache(movies);
|
|
222
|
+
spinner.succeed(`Found ${movies.length} movies`);
|
|
223
|
+
console.log(chalk_1.default.gray('\n--- Watchlist ---'));
|
|
224
|
+
movies.forEach(m => {
|
|
225
|
+
const ratingStr = m.rating ? ` ${m.rating}` : '';
|
|
226
|
+
const yearStr = m.year ? `(${m.year})` : '';
|
|
227
|
+
console.log(chalk_1.default.cyan(`${m.num}. `) + chalk_1.default.white(m.name) + chalk_1.default.gray(` ${yearStr}${ratingStr}`));
|
|
228
|
+
});
|
|
229
|
+
console.log(chalk_1.default.gray('----------------\n'));
|
|
230
|
+
console.log(chalk_1.default.gray('Use: tor-dl find <number> to search and download'));
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
const watchlistUrl = `https://letterboxd.com/${username}/watchlist/`;
|
|
234
|
+
const watchlistResponse = await axios_1.default.get(watchlistUrl, { headers, timeout: 30000, validateStatus: () => true });
|
|
235
|
+
if (watchlistResponse.status === 403) {
|
|
236
|
+
spinner.warn('Letterboxd blocking requests. Try using: tor-dl setuser <username> then manually run search command');
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
const $wl = cheerio.load(watchlistResponse.data);
|
|
240
|
+
const cleanHtml = watchlistResponse.data.replace(/<script\b[^<]*(?:<[^<]*)<\/?script>/gi, '');
|
|
241
|
+
const $2 = cheerio.load(cleanHtml);
|
|
242
|
+
$2('a[href*="/film/"]').each((i, elem) => {
|
|
243
|
+
if (options.limit && i >= options.limit)
|
|
244
|
+
return;
|
|
245
|
+
const el = $2(elem);
|
|
246
|
+
const href = el.attr('href') || '';
|
|
247
|
+
let name = el.text().trim().replace(/\s*\(\d{4}\)\s*$/, '').replace(/\s+/g, ' ').trim();
|
|
248
|
+
if (href.includes('/film/') && name && name.length > 1 && name.length < 200) {
|
|
249
|
+
const exists = movies.some(m => m.name === name);
|
|
250
|
+
if (!exists) {
|
|
251
|
+
const yearMatch = name.match(/\((\d{4})\)$/);
|
|
252
|
+
movies.push({
|
|
253
|
+
num: movies.length + 1,
|
|
254
|
+
name: yearMatch ? name.replace(/\s*\(\d{4}\)\s*$/, '').trim() : name,
|
|
255
|
+
year: yearMatch ? yearMatch[1] : '',
|
|
256
|
+
rating: '',
|
|
257
|
+
url: 'https://letterboxd.com' + href
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
if (movies.length === 0) {
|
|
263
|
+
spinner.warn('No movies found in watchlist');
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
watchlistCache = movies;
|
|
267
|
+
saveWatchlistCache(movies);
|
|
268
|
+
spinner.succeed(`Found ${movies.length} movies`);
|
|
269
|
+
console.log(chalk_1.default.gray('\n--- Watchlist ---'));
|
|
270
|
+
movies.forEach(m => {
|
|
271
|
+
const ratingStr = m.rating ? ` ${m.rating}` : '';
|
|
272
|
+
const yearStr = m.year ? `(${m.year})` : '';
|
|
273
|
+
console.log(chalk_1.default.cyan(`${m.num}. `) + chalk_1.default.white(m.name) + chalk_1.default.gray(` ${yearStr}${ratingStr}`));
|
|
274
|
+
});
|
|
275
|
+
console.log(chalk_1.default.gray('----------------\n'));
|
|
276
|
+
console.log(chalk_1.default.gray('Use: tor-dl find <number> to search and download'));
|
|
277
|
+
}
|
|
278
|
+
catch (error) {
|
|
279
|
+
spinner.fail(`Failed to fetch watchlist: ${error.message}`);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
async function findCommand(number, options = {}) {
|
|
283
|
+
let movie = watchlistCache.find(m => m.num === number);
|
|
284
|
+
if (!movie) {
|
|
285
|
+
watchlistCache = loadWatchlistCache();
|
|
286
|
+
movie = watchlistCache.find(m => m.num === number);
|
|
287
|
+
}
|
|
288
|
+
if (!movie) {
|
|
289
|
+
console.log(chalk_1.default.red(`Movie #${number} not found. Run 'tor-dl list' first.`));
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
let searchQuery = movie.name;
|
|
293
|
+
if (!options.noyear && movie.year) {
|
|
294
|
+
searchQuery = `${movie.name} ${movie.year}`;
|
|
295
|
+
}
|
|
296
|
+
console.log(chalk_1.default.green(`Searching for: ${searchQuery}`));
|
|
297
|
+
const filters = (0, parser_1.loadFilters)();
|
|
298
|
+
const searchOptions = {
|
|
299
|
+
query: searchQuery,
|
|
300
|
+
category: filters.category,
|
|
301
|
+
minSeeds: filters.minSeeds,
|
|
302
|
+
maxSeeds: filters.maxSeeds,
|
|
303
|
+
minSize: filters.minSize,
|
|
304
|
+
maxSize: filters.maxSize,
|
|
305
|
+
sortBy: filters.sortBy,
|
|
306
|
+
order: filters.order,
|
|
307
|
+
limit: filters.limit,
|
|
308
|
+
sources: filters.sources ? filters.sources.split(',') : undefined
|
|
309
|
+
};
|
|
310
|
+
const { searchCommand } = await Promise.resolve().then(() => __importStar(require('./search')));
|
|
311
|
+
await searchCommand(searchOptions);
|
|
312
|
+
}
|
|
313
|
+
//# sourceMappingURL=user.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"user.js","sourceRoot":"","sources":["../../src/commands/user.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
CHANGED
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;CACvC;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;IAClC,KAAK,EAAE,KAAK,GAAG,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;IACnC,KAAK,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,eAAe;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC;IACnE,aAAa,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACtD,SAAS,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CACnD"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;CACvC;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;IAClC,KAAK,EAAE,KAAK,GAAG,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;IACnC,KAAK,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,eAAe;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC;IACnE,aAAa,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACtD,SAAS,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CACnD"}
|
package/filters.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
{
|
|
2
|
-
"category": "all",
|
|
3
|
-
"sources": "yts,thepiratebay,nyaa,torrentscsv",
|
|
4
|
-
"minSeeds": 0,
|
|
5
|
-
"
|
|
6
|
-
"
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
"
|
|
1
|
+
{
|
|
2
|
+
"category": "all",
|
|
3
|
+
"sources": "yts,thepiratebay,nyaa,torrentscsv",
|
|
4
|
+
"minSeeds": 0,
|
|
5
|
+
"maxSeeds": 0,
|
|
6
|
+
"minSize": "0",
|
|
7
|
+
"maxSize": "50GB",
|
|
8
|
+
"sortBy": "seeds",
|
|
9
|
+
"order": "desc",
|
|
10
|
+
"limit": 70
|
|
10
11
|
}
|
package/package.json
CHANGED
package/src/cli/parser.ts
CHANGED
|
@@ -20,6 +20,7 @@ export function loadFilters(): FilterConfig {
|
|
|
20
20
|
return {
|
|
21
21
|
category: 'all',
|
|
22
22
|
minSeeds: 0,
|
|
23
|
+
maxSeeds: 0,
|
|
23
24
|
minSize: '0',
|
|
24
25
|
maxSize: '50GB',
|
|
25
26
|
sortBy: 'seeds',
|
|
@@ -35,7 +36,24 @@ export function createParser(): Command {
|
|
|
35
36
|
const mainHelp = `
|
|
36
37
|
Categories: all, movie, tv, anime, music, games, apps
|
|
37
38
|
Sources:
|
|
38
|
-
yts - movies | torrentscsv - general | thepiratebay - general | nyaa - anime
|
|
39
|
+
yts - movies | torrentscsv - general | thepiratebay - general | nyaa - anime
|
|
40
|
+
|
|
41
|
+
Letterboxd Integration:
|
|
42
|
+
setuser <username> Set your Letterboxd username
|
|
43
|
+
list List movies from your watchlist
|
|
44
|
+
find <number> Search and download a movie from list
|
|
45
|
+
filters Show current search filters
|
|
46
|
+
user Show current username
|
|
47
|
+
|
|
48
|
+
Examples:
|
|
49
|
+
tor-dl setuser Sedat85 # Set Letterboxd username
|
|
50
|
+
tor-dl list # List watchlist movies
|
|
51
|
+
tor-dl list -l 10 # Limit to 10 movies
|
|
52
|
+
tor-dl find 1 # Find first movie (with year)
|
|
53
|
+
tor-dl find 1 --noyear # Find without year
|
|
54
|
+
tor-dl setfilter -c movie -s 50 # Set filters
|
|
55
|
+
tor-dl filters # Show filters
|
|
56
|
+
tor-dl user # Show username`;
|
|
39
57
|
|
|
40
58
|
program
|
|
41
59
|
.name('tor-dl')
|
|
@@ -109,6 +127,80 @@ Sources:
|
|
|
109
127
|
await openInBrowser(parseInt(number));
|
|
110
128
|
});
|
|
111
129
|
|
|
130
|
+
program
|
|
131
|
+
.command('setuser <username>')
|
|
132
|
+
.description('Set Letterboxd username (e.g., tor-dl setuser Sedat85)')
|
|
133
|
+
.action(async (username: string) => {
|
|
134
|
+
const { setUserCommand } = await import('../commands/user');
|
|
135
|
+
await setUserCommand(username);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
program
|
|
139
|
+
.command('setfilter [filters...]')
|
|
140
|
+
.description('Set search filters (e.g., tor-dl setfilter -c movie -s 100 --min-size 1GB)')
|
|
141
|
+
.option('-c, --cat <category>', 'Category (all|movie|tv|anime|music|games|apps)')
|
|
142
|
+
.option('-s, --min-seeds <number>', 'Minimum seeders', parseInt)
|
|
143
|
+
.option('--max-seeds <number>', 'Maximum seeders', parseInt)
|
|
144
|
+
.option('--min-size <size>', 'Min size (e.g. 500MB, 1GB)')
|
|
145
|
+
.option('--max-size <size>', 'Max size (e.g. 5GB)')
|
|
146
|
+
.option('-o, --sort <sortBy>', 'Sort by (seeds|size|date)')
|
|
147
|
+
.option('--order <order>', 'Order (asc|desc)')
|
|
148
|
+
.option('-l, --limit <number>', 'Max results', parseInt)
|
|
149
|
+
.option('-S, --sources <sources>', 'Sources (yts,torrentscsv,thepiratebay,nyaa)')
|
|
150
|
+
.action(async (filters: string[], options) => {
|
|
151
|
+
const { setFilterCommand } = await import('../commands/user');
|
|
152
|
+
await setFilterCommand(options);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
program
|
|
156
|
+
.command('list')
|
|
157
|
+
.description('List movies from Letterboxd watchlist')
|
|
158
|
+
.option('-l, --limit <number>', 'Max movies to show', parseInt)
|
|
159
|
+
.action(async (options) => {
|
|
160
|
+
const { listCommand } = await import('../commands/user');
|
|
161
|
+
await listCommand(options);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
program
|
|
165
|
+
.command('filters')
|
|
166
|
+
.description('Show current search filters')
|
|
167
|
+
.action(async () => {
|
|
168
|
+
const filters = loadFilters();
|
|
169
|
+
console.log('\n--- Current Filters ---');
|
|
170
|
+
console.log('Category:', filters.category);
|
|
171
|
+
console.log('Min Seeds:', filters.minSeeds);
|
|
172
|
+
console.log('Max Seeds:', filters.maxSeeds || 'unlimited');
|
|
173
|
+
console.log('Min Size:', filters.minSize);
|
|
174
|
+
console.log('Max Size:', filters.maxSize);
|
|
175
|
+
console.log('Sort By:', filters.sortBy);
|
|
176
|
+
console.log('Order:', filters.order);
|
|
177
|
+
console.log('Limit:', filters.limit);
|
|
178
|
+
console.log('Sources:', (filters as any).sources || 'all');
|
|
179
|
+
console.log('----------------------\n');
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
program
|
|
183
|
+
.command('user')
|
|
184
|
+
.description('Show current username')
|
|
185
|
+
.action(async () => {
|
|
186
|
+
const { loadUserConfig } = await import('../commands/user');
|
|
187
|
+
const config = loadUserConfig();
|
|
188
|
+
const username = config.letterboxd.username;
|
|
189
|
+
console.log('\n--- User ---');
|
|
190
|
+
console.log('Letterboxd:', username || '(not set)');
|
|
191
|
+
console.log('-----------\n');
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
program
|
|
195
|
+
.command('find <number>')
|
|
196
|
+
.description('Download movie by number from list')
|
|
197
|
+
.option('--noyear', 'Search without year')
|
|
198
|
+
.allowUnknownOption()
|
|
199
|
+
.action(async (number: string, options: any) => {
|
|
200
|
+
const { findCommand } = await import('../commands/user');
|
|
201
|
+
await findCommand(parseInt(number), options);
|
|
202
|
+
});
|
|
203
|
+
|
|
112
204
|
program.on('command:*', () => {
|
|
113
205
|
console.error('Invalid command: %s\nSee --help for a list of available commands.', program.args[0]);
|
|
114
206
|
process.exit(1);
|
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync, existsSync } from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import axios from 'axios';
|
|
4
|
+
import * as cheerio from 'cheerio';
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
import ora from 'ora';
|
|
7
|
+
import { FilterConfig, SearchOptions } from '../types';
|
|
8
|
+
import { loadFilters } from '../cli/parser';
|
|
9
|
+
|
|
10
|
+
interface UserConfig {
|
|
11
|
+
letterboxd: {
|
|
12
|
+
username: string;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function getUsersPath(): string {
|
|
17
|
+
return join(__dirname, '../../users.json');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function loadUsers(): UserConfig {
|
|
21
|
+
const usersPath = getUsersPath();
|
|
22
|
+
if (existsSync(usersPath)) {
|
|
23
|
+
return JSON.parse(readFileSync(usersPath, 'utf-8'));
|
|
24
|
+
}
|
|
25
|
+
return { letterboxd: { username: '' } };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export { loadUsers as loadUserConfig };
|
|
29
|
+
|
|
30
|
+
function saveUsers(config: UserConfig): void {
|
|
31
|
+
writeFileSync(getUsersPath(), JSON.stringify(config, null, 2));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export async function setUserCommand(username: string): Promise<void> {
|
|
35
|
+
const config = loadUsers();
|
|
36
|
+
config.letterboxd.username = username;
|
|
37
|
+
saveUsers(config);
|
|
38
|
+
console.log(chalk.green(`Username set to: ${username}`));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export async function setFilterCommand(options: any): Promise<void> {
|
|
42
|
+
const filters = loadFilters();
|
|
43
|
+
|
|
44
|
+
if (options.cat) filters.category = options.cat;
|
|
45
|
+
if (options.minSeeds !== undefined) filters.minSeeds = options.minSeeds;
|
|
46
|
+
if (options.maxSeeds) filters.maxSeeds = options.maxSeeds;
|
|
47
|
+
if (options.minSize) filters.minSize = options.minSize;
|
|
48
|
+
if (options.maxSize) filters.maxSize = options.maxSize;
|
|
49
|
+
if (options.sort) filters.sortBy = options.sort;
|
|
50
|
+
if (options.order) filters.order = options.order;
|
|
51
|
+
if (options.limit) filters.limit = options.limit;
|
|
52
|
+
if (options.sources) {
|
|
53
|
+
const srcArr = options.sources.split ? options.sources.split(',') : options.sources;
|
|
54
|
+
filters.sources = Array.isArray(srcArr) ? srcArr.join(',') : srcArr;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const filtersPath = join(__dirname, '../../filters.json');
|
|
58
|
+
writeFileSync(filtersPath, JSON.stringify(filters, null, 2));
|
|
59
|
+
console.log(chalk.green('Filters updated:'));
|
|
60
|
+
console.log(JSON.stringify(filters, null, 2));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
interface WatchlistMovie {
|
|
64
|
+
num: number;
|
|
65
|
+
name: string;
|
|
66
|
+
year: string;
|
|
67
|
+
rating: string;
|
|
68
|
+
url: string;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function getWatchlistCachePath(): string {
|
|
72
|
+
return join(__dirname, '../../.watchlist-cache.json');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function loadWatchlistCache(): WatchlistMovie[] {
|
|
76
|
+
const path = getWatchlistCachePath();
|
|
77
|
+
if (existsSync(path)) {
|
|
78
|
+
try {
|
|
79
|
+
return JSON.parse(readFileSync(path, 'utf-8'));
|
|
80
|
+
} catch {
|
|
81
|
+
return [];
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return [];
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function saveWatchlistCache(movies: WatchlistMovie[]): void {
|
|
88
|
+
writeFileSync(getWatchlistCachePath(), JSON.stringify(movies, null, 2));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
let watchlistCache: WatchlistMovie[] = [];
|
|
92
|
+
|
|
93
|
+
export async function listCommand(options: { limit?: number }): Promise<void> {
|
|
94
|
+
const config = loadUsers();
|
|
95
|
+
const username = config.letterboxd.username;
|
|
96
|
+
|
|
97
|
+
if (!username) {
|
|
98
|
+
console.log(chalk.red('No username set. Use: tor-dl setuser <username>'));
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const spinner = ora(`Fetching watchlist for ${username}...`).start();
|
|
103
|
+
|
|
104
|
+
try {
|
|
105
|
+
let movies: WatchlistMovie[] = [];
|
|
106
|
+
|
|
107
|
+
const profileUrl = `https://letterboxd.com/${username}/`;
|
|
108
|
+
const headers = {
|
|
109
|
+
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
|
|
110
|
+
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
|
|
111
|
+
'Accept-Language': 'en-GB,en-US;q=0.9,en;q=0.8',
|
|
112
|
+
'Sec-Fetch-Dest': 'document',
|
|
113
|
+
'Sec-Fetch-Mode': 'navigate',
|
|
114
|
+
'Sec-Fetch-Site': 'none',
|
|
115
|
+
'Sec-Fetch-User': '?1',
|
|
116
|
+
'Upgrade-Insecure-Requests': '1'
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
const response = await axios.get(profileUrl, { headers, timeout: 30000, validateStatus: () => true });
|
|
120
|
+
|
|
121
|
+
if (response.status === 403) {
|
|
122
|
+
spinner.warn('Letterboxd blocking requests. Try using: tor-dl search "movie name" directly');
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (!response.data) {
|
|
127
|
+
spinner.warn('Empty response from Letterboxd');
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const $ = cheerio.load(response.data);
|
|
132
|
+
|
|
133
|
+
$('[data-item-name]').each((i, elem) => {
|
|
134
|
+
if (options.limit && i >= options.limit) return;
|
|
135
|
+
|
|
136
|
+
const el = $(elem);
|
|
137
|
+
const fullName = el.attr('data-item-name') || '';
|
|
138
|
+
const href = el.attr('data-item-link') || '';
|
|
139
|
+
|
|
140
|
+
if (fullName && href && href.includes('/film/')) {
|
|
141
|
+
const exists = movies.some(m => m.name === fullName);
|
|
142
|
+
if (!exists) {
|
|
143
|
+
const yearMatch = fullName.match(/\((\d{4})\)$/);
|
|
144
|
+
const movieName = yearMatch ? fullName.replace(/\s*\(\d{4}\)\s*$/, '').trim() : fullName;
|
|
145
|
+
movies.push({
|
|
146
|
+
num: movies.length + 1,
|
|
147
|
+
name: movieName,
|
|
148
|
+
year: yearMatch ? yearMatch[1] : '',
|
|
149
|
+
rating: '',
|
|
150
|
+
url: 'https://letterboxd.com' + href
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
if (movies.length === 0) {
|
|
157
|
+
$('a[href*="/film/"][title]').each((i, elem) => {
|
|
158
|
+
if (options.limit && i >= options.limit) return;
|
|
159
|
+
|
|
160
|
+
const el = $(elem);
|
|
161
|
+
const name = el.attr('title') || '';
|
|
162
|
+
const href = el.attr('href') || '';
|
|
163
|
+
|
|
164
|
+
if (name && name.length > 1 && name.length < 200 && href.includes('/film/')) {
|
|
165
|
+
const exists = movies.some(m => m.name === name);
|
|
166
|
+
if (!exists) {
|
|
167
|
+
const yearMatch = name.match(/\((\d{4})\)$/);
|
|
168
|
+
movies.push({
|
|
169
|
+
num: movies.length + 1,
|
|
170
|
+
name: name,
|
|
171
|
+
year: yearMatch ? yearMatch[1] : '',
|
|
172
|
+
rating: '',
|
|
173
|
+
url: 'https://letterboxd.com' + href
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (movies.length === 0) {
|
|
181
|
+
$('a[href*="/film/"]').each((i, elem) => {
|
|
182
|
+
if (options.limit && i >= options.limit) return;
|
|
183
|
+
|
|
184
|
+
const el = $(elem);
|
|
185
|
+
const href = el.attr('href') || '';
|
|
186
|
+
let name = el.text().trim();
|
|
187
|
+
name = name.replace(/\s+/g, ' ').replace(/^\d+\.\s*/, '').trim();
|
|
188
|
+
|
|
189
|
+
if (name && name.length > 1 && name.length < 200 && href.includes('/film/')) {
|
|
190
|
+
const exists = movies.some(m => m.name === name);
|
|
191
|
+
if (!exists) {
|
|
192
|
+
const yearMatch = name.match(/\((\d{4})\)$/);
|
|
193
|
+
let movieYear = yearMatch ? yearMatch[1] : '';
|
|
194
|
+
if (!movieYear) {
|
|
195
|
+
const urlYearMatch = href.match(/-(\d{4})\/?$/);
|
|
196
|
+
if (urlYearMatch) movieYear = urlYearMatch[1];
|
|
197
|
+
}
|
|
198
|
+
movies.push({
|
|
199
|
+
num: movies.length + 1,
|
|
200
|
+
name: yearMatch ? name.replace(/\s*\(\d{4}\)\s*$/, '').trim() : name,
|
|
201
|
+
year: movieYear,
|
|
202
|
+
rating: '',
|
|
203
|
+
url: 'https://letterboxd.com' + href
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (movies.length > 0) {
|
|
211
|
+
watchlistCache = movies;
|
|
212
|
+
saveWatchlistCache(movies);
|
|
213
|
+
spinner.succeed(`Found ${movies.length} movies`);
|
|
214
|
+
|
|
215
|
+
console.log(chalk.gray('\n--- Watchlist ---'));
|
|
216
|
+
movies.forEach(m => {
|
|
217
|
+
const ratingStr = m.rating ? ` ${m.rating}` : '';
|
|
218
|
+
const yearStr = m.year ? `(${m.year})` : '';
|
|
219
|
+
console.log(chalk.cyan(`${m.num}. `) + chalk.white(m.name) + chalk.gray(` ${yearStr}${ratingStr}`));
|
|
220
|
+
});
|
|
221
|
+
console.log(chalk.gray('----------------\n'));
|
|
222
|
+
console.log(chalk.gray('Use: tor-dl find <number> to search and download'));
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const watchlistUrl = `https://letterboxd.com/${username}/watchlist/`;
|
|
227
|
+
const watchlistResponse = await axios.get(watchlistUrl, { headers, timeout: 30000, validateStatus: () => true });
|
|
228
|
+
|
|
229
|
+
if (watchlistResponse.status === 403) {
|
|
230
|
+
spinner.warn('Letterboxd blocking requests. Try using: tor-dl setuser <username> then manually run search command');
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
const $wl = cheerio.load(watchlistResponse.data);
|
|
234
|
+
|
|
235
|
+
const cleanHtml = watchlistResponse.data.replace(/<script\b[^<]*(?:<[^<]*)<\/?script>/gi, '');
|
|
236
|
+
const $2 = cheerio.load(cleanHtml);
|
|
237
|
+
|
|
238
|
+
$2('a[href*="/film/"]').each((i, elem) => {
|
|
239
|
+
if (options.limit && i >= options.limit) return;
|
|
240
|
+
const el = $2(elem);
|
|
241
|
+
const href = el.attr('href') || '';
|
|
242
|
+
let name = el.text().trim().replace(/\s*\(\d{4}\)\s*$/, '').replace(/\s+/g, ' ').trim();
|
|
243
|
+
|
|
244
|
+
if (href.includes('/film/') && name && name.length > 1 && name.length < 200) {
|
|
245
|
+
const exists = movies.some(m => m.name === name);
|
|
246
|
+
if (!exists) {
|
|
247
|
+
const yearMatch = name.match(/\((\d{4})\)$/);
|
|
248
|
+
movies.push({
|
|
249
|
+
num: movies.length + 1,
|
|
250
|
+
name: yearMatch ? name.replace(/\s*\(\d{4}\)\s*$/, '').trim() : name,
|
|
251
|
+
year: yearMatch ? yearMatch[1] : '',
|
|
252
|
+
rating: '',
|
|
253
|
+
url: 'https://letterboxd.com' + href
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
if (movies.length === 0) {
|
|
260
|
+
spinner.warn('No movies found in watchlist');
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
watchlistCache = movies;
|
|
265
|
+
saveWatchlistCache(movies);
|
|
266
|
+
spinner.succeed(`Found ${movies.length} movies`);
|
|
267
|
+
|
|
268
|
+
console.log(chalk.gray('\n--- Watchlist ---'));
|
|
269
|
+
movies.forEach(m => {
|
|
270
|
+
const ratingStr = m.rating ? ` ${m.rating}` : '';
|
|
271
|
+
const yearStr = m.year ? `(${m.year})` : '';
|
|
272
|
+
console.log(chalk.cyan(`${m.num}. `) + chalk.white(m.name) + chalk.gray(` ${yearStr}${ratingStr}`));
|
|
273
|
+
});
|
|
274
|
+
console.log(chalk.gray('----------------\n'));
|
|
275
|
+
console.log(chalk.gray('Use: tor-dl find <number> to search and download'));
|
|
276
|
+
|
|
277
|
+
} catch (error: any) {
|
|
278
|
+
spinner.fail(`Failed to fetch watchlist: ${error.message}`);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
export async function findCommand(number: number, options: any = {}): Promise<void> {
|
|
283
|
+
let movie = watchlistCache.find(m => m.num === number);
|
|
284
|
+
|
|
285
|
+
if (!movie) {
|
|
286
|
+
watchlistCache = loadWatchlistCache();
|
|
287
|
+
movie = watchlistCache.find(m => m.num === number);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
if (!movie) {
|
|
291
|
+
console.log(chalk.red(`Movie #${number} not found. Run 'tor-dl list' first.`));
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
let searchQuery = movie.name;
|
|
296
|
+
if (!options.noyear && movie.year) {
|
|
297
|
+
searchQuery = `${movie.name} ${movie.year}`;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
console.log(chalk.green(`Searching for: ${searchQuery}`));
|
|
301
|
+
|
|
302
|
+
const filters = loadFilters();
|
|
303
|
+
const searchOptions: SearchOptions = {
|
|
304
|
+
query: searchQuery,
|
|
305
|
+
category: filters.category,
|
|
306
|
+
minSeeds: filters.minSeeds,
|
|
307
|
+
maxSeeds: (filters as any).maxSeeds,
|
|
308
|
+
minSize: filters.minSize,
|
|
309
|
+
maxSize: filters.maxSize,
|
|
310
|
+
sortBy: filters.sortBy,
|
|
311
|
+
order: filters.order,
|
|
312
|
+
limit: filters.limit,
|
|
313
|
+
sources: (filters as any).sources ? (filters as any).sources.split(',') : undefined
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
const { searchCommand } = await import('./search');
|
|
317
|
+
await searchCommand(searchOptions);
|
|
318
|
+
}
|
package/src/types.ts
CHANGED