tor-dl 1.0.19 → 1.0.20

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.
@@ -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 |
@@ -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,CAc1C;AAED,wBAAgB,YAAY,IAAI,OAAO,CAuFtC"}
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"}
@@ -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 - YTS (Movies) | torrentscsv - Torrents.csv | thepiratebay - The Pirate Bay | nyaa - Nyaa.si (Anime)`;
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);
@@ -1 +1 @@
1
- {"version":3,"file":"parser.js","sourceRoot":"","sources":["../../src/cli/parser.ts"],"names":[],"mappings":"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"}
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
@@ -31,6 +31,7 @@ export interface FilterConfig {
31
31
  category: string;
32
32
  sources?: string;
33
33
  minSeeds: number;
34
+ maxSeeds?: number;
34
35
  minSize: string;
35
36
  maxSize: string;
36
37
  sortBy: 'seeds' | 'size' | 'date';
@@ -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
- "minSize": "0",
6
- "maxSize": "50GB",
7
- "sortBy": "seeds",
8
- "order": "desc",
9
- "limit": 60
1
+ {
2
+ "category": "movie",
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tor-dl",
3
- "version": "1.0.19",
3
+ "version": "1.0.20",
4
4
  "description": "CLI torrent search tool - search, open in browser, copy magnet links",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
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
@@ -34,6 +34,7 @@ export interface FilterConfig {
34
34
  category: string;
35
35
  sources?: string;
36
36
  minSeeds: number;
37
+ maxSeeds?: number;
37
38
  minSize: string;
38
39
  maxSize: string;
39
40
  sortBy: 'seeds' | 'size' | 'date';
package/users.json ADDED
@@ -0,0 +1,5 @@
1
+ {
2
+ "letterboxd": {
3
+ "username": "Sedat85"
4
+ }
5
+ }