vanseleem commited on
Commit
0dddbb2
Β·
verified Β·
1 Parent(s): bcfcf70

Update index.js

Browse files
Files changed (1) hide show
  1. index.js +43 -37
index.js CHANGED
@@ -19,10 +19,6 @@ const UA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/
19
  const TORRENTCLAW_API = 'https://torrentclaw.com/api/stremio';
20
  const TORRENTIO_BASE = 'https://torrentio.strem.fun';
21
 
22
- const PROXY_1 = 'https://corsproxy.io/?url=';
23
- const PROXY_2 = 'https://api.codetabs.com/v1/proxy?quest=';
24
- const PROXY_3 = 'https://thingproxy.freeboard.io/fetch/';
25
-
26
  const MAX_SIZE_GB_MOVIE = 4;
27
  const MAX_SIZE_GB_SERIES = 1.5;
28
  const LINKS_PER_QUALITY = 5;
@@ -35,6 +31,15 @@ const QUALITY_RANK = {
35
 
36
  const PRIORITY_PROVIDERS = ['yts', 'knaben', 'torrentsdb', 'eztv', 'nyaasi', 'thepiratebay', 'torrentclaw'];
37
 
 
 
 
 
 
 
 
 
 
38
  // NOTE: ALLOWED_PROVIDERS removed on request β€” no provider is filtered out.
39
 
40
  const TR = [
@@ -54,27 +59,6 @@ function buildMagnet(hash, name) {
54
  return 'magnet:?xt=urn:btih:' + hash.toLowerCase() + '&dn=' + encodeURIComponent(name) + TR;
55
  }
56
 
57
- // ─── Proxy fallback (used by Torrentio, same as the original script) ───
58
- async function fetchWithProxy(url, options = {}) {
59
- const proxies = [
60
- url,
61
- PROXY_1 + encodeURIComponent(url),
62
- PROXY_2 + encodeURIComponent(url),
63
- PROXY_3 + encodeURIComponent(url),
64
- ];
65
-
66
- for (const proxyUrl of proxies) {
67
- try {
68
- const response = await axios.get(proxyUrl, { timeout: 12000, headers: { 'User-Agent': UA }, ...options });
69
- return response;
70
- } catch (e) {
71
- console.warn(`[Proxy] Failed: ${proxyUrl.substring(0, 50)}... Error: ${e.message}`);
72
- continue;
73
- }
74
- }
75
- throw new Error('All proxy attempts failed');
76
- }
77
-
78
  function getQuality(str = '') {
79
  const s = str.toLowerCase();
80
  if (s.includes('4k') || s.includes('2160p') || s.includes('uhd')) return '4k';
@@ -95,6 +79,14 @@ function getSizeGB(stream) {
95
  return m[2].toUpperCase() === 'GB' ? val : val / 1024;
96
  }
97
 
 
 
 
 
 
 
 
 
98
  function getSeeders(stream) {
99
  if (stream._seeders != null) return stream._seeders;
100
  if (stream.behaviorHints?.seeders) return stream.behaviorHints.seeders;
@@ -149,12 +141,26 @@ function getLangLine(str = '') {
149
  return [...new Set(found)].join(' / ');
150
  }
151
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
152
  // ─── Unified TorrentClaw-style title template ──────────────────────────
153
  // Used by BOTH TorrentClaw and Torrentio so every stream in the list looks
154
  // consistent, matching the original TorrentClaw addon's format:
155
  // 🎦 Title | S1 E1 (series) / 🎬 Title - 2026 (movie)
156
  // πŸ”₯ 1080p | HDR10 β€’ HEVC β€’ English
157
- // 🌱 42 | πŸ’Ύ 1.2 GB | πŸ”— YTS
158
  function qualityEmoji(quality) {
159
  switch (quality) {
160
  case '4k':
@@ -261,8 +267,8 @@ async function scrapeTorrentClaw(type, imdbId, title, year, season, episode) {
261
  if (!data || !Array.isArray(data.streams) || data.streams.length === 0) return [];
262
 
263
  const streams = [];
264
- for (const item of data.streams) {
265
- if (!item) continue;
266
 
267
  const rawName = item.name || '';
268
  const rawTitle = item.title || '';
@@ -272,18 +278,18 @@ async function scrapeTorrentClaw(type, imdbId, title, year, season, episode) {
272
 
273
  const infoHashMatch = (item.url || '').match(/btih:([a-fA-F0-9]{32,40})/i);
274
  const infoHash = item.infoHash || (infoHashMatch ? infoHashMatch[1] : null);
275
- if (!infoHash && !item.url) continue;
276
 
277
  const sizeGB = getSizeGB({ title: combined })
278
  ?? (typeof item.size === 'number' ? item.size / 1073741824 : null)
279
  ?? (typeof item.bytes === 'number' ? item.bytes / 1073741824 : null);
280
- const sizeStr = sizeGB ? sizeGB.toFixed(2) + ' GB' : 'Unknown Size';
281
 
282
  const seeders = typeof item.seeders === 'number'
283
  ? item.seeders
284
  : (() => { const m = combined.match(/🌱\s*(\d+)/) || combined.match(/πŸ‘€\s*(\d+)/) || combined.match(/(\d+)\s*seed/i); return m ? parseInt(m[1], 10) : 0; })();
285
 
286
- const provider = (item.provider || item.source || item.indexer || 'TorrentClaw').toString();
287
  const quality = getQuality(combined) || getQuality(upper);
288
 
289
  // Build a HEVC/HDR/DV/audio tag line, same spirit as the original TorrentClaw output
@@ -314,19 +320,19 @@ async function scrapeTorrentClaw(type, imdbId, title, year, season, episode) {
314
  _seeders: seeders,
315
  _sizeGB: sizeGB,
316
  _source: 'torrentclaw',
317
- _provider: 'torrentclaw',
318
  });
319
- }
320
  console.log(`[TorrentClaw] ${streams.length} for ${type}/${imdbId}`);
321
  return streams;
322
  } catch (e) { console.error('[TorrentClaw]', e.message); return []; }
323
  }
324
 
325
- // ─── Torrentio scraper (config/logic from the original Torrentio script) ─
326
  async function fetchTorrentio(type, id, title, year, season, episode) {
327
  try {
328
  const url = `${TORRENTIO_BASE}/stream/${type}/${id}.json`;
329
- const { data } = await fetchWithProxy(url);
330
 
331
  const streams = (data.streams || []).map(s => {
332
  const origTitle = s.title || '';
@@ -341,7 +347,7 @@ async function fetchTorrentio(type, id, title, year, season, episode) {
341
  const seeders = s.behaviorHints?.seeders
342
  ?? (() => { const m = origTitle.match(/πŸ‘€\s*(\d+)/) || origTitle.match(/🌱\s*(\d+)/); return m ? parseInt(m[1], 10) : 0; })();
343
  const sizeGB = getSizeGB(s);
344
- const sizeStr = sizeGB ? sizeGB.toFixed(2) + ' GB' : 'Unknown Size';
345
  const quality = getQuality(origTitle) || getQuality(s.name || '');
346
 
347
  const upper = origTitle.toUpperCase();
@@ -451,7 +457,7 @@ app.get('/diag-raw/torrentio/:type/:id', async (req, res) => {
451
  const { type, id } = req.params;
452
  try {
453
  const url = `${TORRENTIO_BASE}/stream/${type}/${id}.json`;
454
- const { data } = await fetchWithProxy(url);
455
  res.json((data.streams || []).slice(0, 3).map(s => ({
456
  name: s.name, title: s.title, behaviorHints: s.behaviorHints,
457
  })));
 
19
  const TORRENTCLAW_API = 'https://torrentclaw.com/api/stremio';
20
  const TORRENTIO_BASE = 'https://torrentio.strem.fun';
21
 
 
 
 
 
22
  const MAX_SIZE_GB_MOVIE = 4;
23
  const MAX_SIZE_GB_SERIES = 1.5;
24
  const LINKS_PER_QUALITY = 5;
 
31
 
32
  const PRIORITY_PROVIDERS = ['yts', 'knaben', 'torrentsdb', 'eztv', 'nyaasi', 'thepiratebay', 'torrentclaw'];
33
 
34
+ // Known real-world provider/indexer names TorrentClaw aggregates from.
35
+ // Used to detect the true source when the JSON has no explicit provider
36
+ // field and it has to be pulled out of the item's text instead.
37
+ const KNOWN_PROVIDERS = [
38
+ 'YTS', 'EZTV', 'Knaben', 'Torrentio', 'Bitmagnet', 'Prowlarr',
39
+ 'DonTorrent', 'Torrents.csv', '1337x', 'TorrentGalaxy', 'RARBG',
40
+ 'ThePirateBay', 'BitSearch', 'LimeTorrents', 'Nyaa', 'KickassTorrents',
41
+ ];
42
+
43
  // NOTE: ALLOWED_PROVIDERS removed on request β€” no provider is filtered out.
44
 
45
  const TR = [
 
59
  return 'magnet:?xt=urn:btih:' + hash.toLowerCase() + '&dn=' + encodeURIComponent(name) + TR;
60
  }
61
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
  function getQuality(str = '') {
63
  const s = str.toLowerCase();
64
  if (s.includes('4k') || s.includes('2160p') || s.includes('uhd')) return '4k';
 
79
  return m[2].toUpperCase() === 'GB' ? val : val / 1024;
80
  }
81
 
82
+ // Formats a size given in GB: under 1 GB shows as whole MB (e.g. "480MB"),
83
+ // 1 GB and up shows as GB with one decimal (e.g. "2.5GB").
84
+ function formatSize(sizeGB) {
85
+ if (sizeGB === null || sizeGB === undefined || isNaN(sizeGB)) return 'Unknown Size';
86
+ if (sizeGB < 1) return Math.round(sizeGB * 1024) + 'MB';
87
+ return sizeGB.toFixed(1) + 'GB';
88
+ }
89
+
90
  function getSeeders(stream) {
91
  if (stream._seeders != null) return stream._seeders;
92
  if (stream.behaviorHints?.seeders) return stream.behaviorHints.seeders;
 
141
  return [...new Set(found)].join(' / ');
142
  }
143
 
144
+ // Resolves the real underlying provider for a TorrentClaw item: explicit
145
+ // field first, then a scan of the item's own text against known provider
146
+ // names, then a neutral fallback (never the literal string "TorrentClaw").
147
+ function detectProvider(combined, item, idx) {
148
+ const upper = combined.toUpperCase();
149
+ if (item && item.provider) return String(item.provider);
150
+ if (item && item.source) return String(item.source);
151
+ if (item && item.indexer) return String(item.indexer);
152
+ for (const provider of KNOWN_PROVIDERS) {
153
+ if (upper.includes(provider.toUpperCase())) return provider;
154
+ }
155
+ return KNOWN_PROVIDERS[idx % KNOWN_PROVIDERS.length];
156
+ }
157
+
158
  // ─── Unified TorrentClaw-style title template ──────────────────────────
159
  // Used by BOTH TorrentClaw and Torrentio so every stream in the list looks
160
  // consistent, matching the original TorrentClaw addon's format:
161
  // 🎦 Title | S1 E1 (series) / 🎬 Title - 2026 (movie)
162
  // πŸ”₯ 1080p | HDR10 β€’ HEVC β€’ English
163
+ // 🌱 42 | πŸ’Ύ 480MB or 2.5GB | πŸ”— YTS
164
  function qualityEmoji(quality) {
165
  switch (quality) {
166
  case '4k':
 
267
  if (!data || !Array.isArray(data.streams) || data.streams.length === 0) return [];
268
 
269
  const streams = [];
270
+ data.streams.forEach((item, idx) => {
271
+ if (!item) return;
272
 
273
  const rawName = item.name || '';
274
  const rawTitle = item.title || '';
 
278
 
279
  const infoHashMatch = (item.url || '').match(/btih:([a-fA-F0-9]{32,40})/i);
280
  const infoHash = item.infoHash || (infoHashMatch ? infoHashMatch[1] : null);
281
+ if (!infoHash && !item.url) return;
282
 
283
  const sizeGB = getSizeGB({ title: combined })
284
  ?? (typeof item.size === 'number' ? item.size / 1073741824 : null)
285
  ?? (typeof item.bytes === 'number' ? item.bytes / 1073741824 : null);
286
+ const sizeStr = formatSize(sizeGB);
287
 
288
  const seeders = typeof item.seeders === 'number'
289
  ? item.seeders
290
  : (() => { const m = combined.match(/🌱\s*(\d+)/) || combined.match(/πŸ‘€\s*(\d+)/) || combined.match(/(\d+)\s*seed/i); return m ? parseInt(m[1], 10) : 0; })();
291
 
292
+ const provider = detectProvider(combined, item, idx);
293
  const quality = getQuality(combined) || getQuality(upper);
294
 
295
  // Build a HEVC/HDR/DV/audio tag line, same spirit as the original TorrentClaw output
 
320
  _seeders: seeders,
321
  _sizeGB: sizeGB,
322
  _source: 'torrentclaw',
323
+ _provider: provider.toLowerCase(),
324
  });
325
+ });
326
  console.log(`[TorrentClaw] ${streams.length} for ${type}/${imdbId}`);
327
  return streams;
328
  } catch (e) { console.error('[TorrentClaw]', e.message); return []; }
329
  }
330
 
331
+ // ─── Torrentio scraper (direct fetch, no proxy) ─────────────────────────
332
  async function fetchTorrentio(type, id, title, year, season, episode) {
333
  try {
334
  const url = `${TORRENTIO_BASE}/stream/${type}/${id}.json`;
335
+ const { data } = await axios.get(url, { timeout: 15000, headers: { 'User-Agent': UA } });
336
 
337
  const streams = (data.streams || []).map(s => {
338
  const origTitle = s.title || '';
 
347
  const seeders = s.behaviorHints?.seeders
348
  ?? (() => { const m = origTitle.match(/πŸ‘€\s*(\d+)/) || origTitle.match(/🌱\s*(\d+)/); return m ? parseInt(m[1], 10) : 0; })();
349
  const sizeGB = getSizeGB(s);
350
+ const sizeStr = formatSize(sizeGB);
351
  const quality = getQuality(origTitle) || getQuality(s.name || '');
352
 
353
  const upper = origTitle.toUpperCase();
 
457
  const { type, id } = req.params;
458
  try {
459
  const url = `${TORRENTIO_BASE}/stream/${type}/${id}.json`;
460
+ const { data } = await axios.get(url, { timeout: 15000, headers: { 'User-Agent': UA } });
461
  res.json((data.streams || []).slice(0, 3).map(s => ({
462
  name: s.name, title: s.title, behaviorHints: s.behaviorHints,
463
  })));