vanseleem commited on
Commit
2cd7a3e
·
verified ·
1 Parent(s): cc86d82

Update index.js

Browse files
Files changed (1) hide show
  1. index.js +99 -149
index.js CHANGED
@@ -3,18 +3,10 @@
3
  const express = require('express');
4
  const axios = require('axios');
5
 
6
- const app = express();
7
  const PORT = process.env.PORT || 7860;
8
 
9
- const UA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36';
10
-
11
- const TR = [
12
- 'udp://tracker.opentrackr.org:1337/announce',
13
- 'udp://open.stealth.si:80/announce',
14
- 'udp://open.demonii.com:1337/announce',
15
- 'udp://tracker.torrent.eu.org:451/announce',
16
- 'udp://p4p.arenabg.com:1337/announce',
17
- ].map(t => '&tr=' + encodeURIComponent(t)).join('');
18
 
19
  app.use((req, res, next) => {
20
  res.header('Access-Control-Allow-Origin', '*');
@@ -24,189 +16,147 @@ app.use((req, res, next) => {
24
  next();
25
  });
26
 
27
- // ─── SubsPlease Scraper ────────────────────────────────────────
28
  async function scrapeSubsPlease(title) {
29
- if (!title) return [];
30
  try {
31
- const query = encodeURIComponent(title);
32
  const { data } = await axios.get(
33
- `https://subsplease.org/api/?f=search&tz=&s=${query}`,
34
- { timeout: 10000, headers: { 'User-Agent': UA } }
35
  );
36
 
37
- if (!data || Object.keys(data).length === 0) return [];
38
-
39
  const streams = [];
40
- for (const [showKey, showData] of Object.entries(data)) {
41
- if (!showData.downloads || !Array.isArray(showData.downloads)) continue;
42
-
43
- for (const dl of showData.downloads) {
44
- if (!dl.magnet || !dl.res) continue;
45
-
46
- const hashMatch = dl.magnet.match(/btih:([a-fA-F0-9]{32,40})/i);
47
- const hash = hashMatch ? hashMatch[1] : '';
48
- if (!hash) continue;
49
-
50
- const sizeMatch = dl.magnet.match(/size=(\d+)/i);
51
- const sizeGB = sizeMatch ? parseInt(sizeMatch[1], 10) / 1073741824 : null;
52
-
53
- streams.push({
54
- infoHash: hash.toLowerCase(),
55
- name: `${showData.show_name} ${dl.res}p`,
56
- title: `SubsPlease - ${showData.show_name} ${dl.res}p`,
57
- magnet: dl.magnet,
58
- quality: dl.res,
59
- size: sizeGB ? sizeGB.toFixed(2) + ' GB' : 'N/A',
60
- seeders: 'N/A',
61
- _source: 'subsplease',
62
- });
63
  }
64
  }
65
- console.log(`[SubsPlease] ✅ Found ${streams.length} results for "${title}"`);
66
- return streams;
67
- } catch (e) {
68
- console.error('[SubsPlease] ❌', e.message);
69
- return [];
70
  }
71
  }
72
 
73
- // ─── APIBay Scraper ────────────────────────────────────────
74
  async function scrapeAPIBay(title) {
75
- if (!title) return [];
76
  try {
77
  const { data } = await axios.get(
78
  `https://apibay.org/q.php?q=${encodeURIComponent(title)}&cat=208`,
79
- { timeout: 12000, headers: { 'User-Agent': UA } }
80
  );
81
 
82
- if (!Array.isArray(data) || data.length === 0) return [];
 
 
 
 
 
83
 
84
- const streams = [];
85
- for (const item of data) {
86
- if (!item.info_hash || !item.name) continue;
87
-
88
- const magnet = `magnet:?xt=urn:btih:${item.info_hash.toLowerCase()}&dn=${encodeURIComponent(item.name)}${TR}`;
89
- const sizeGB = item.size ? parseInt(item.size, 10) / 1073741824 : null;
90
- const seeders = item.seeders ? parseInt(item.seeders, 10) : 0;
91
-
92
- streams.push({
93
- infoHash: item.info_hash.toLowerCase(),
94
- name: item.name,
95
- title: `APIBay (TPB) - ${item.name.substring(0, 60)}...`,
96
- magnet: magnet,
97
- seeders: seeders,
98
- size: sizeGB ? sizeGB.toFixed(2) + ' GB' : 'N/A',
99
- _source: 'apibay',
100
- });
101
- }
102
- console.log(`[APIBay] ✅ Found ${streams.length} results for "${title}"`);
103
- return streams;
104
- } catch (e) {
105
- console.error('[APIBay] ❌', e.message);
106
- return [];
107
  }
108
  }
109
 
110
- // ─── Torrents.csv Scraper ────────────────────────────────────────
111
  async function scrapeTorrentsCSV(title) {
112
- if (!title) return [];
113
  try {
114
  const { data } = await axios.get(
115
  `https://torrents-csv.com/service/search?q=${encodeURIComponent(title)}`,
116
- { timeout: 12000, headers: { 'User-Agent': UA } }
117
  );
118
 
119
- if (!Array.isArray(data) || data.length === 0) return [];
 
 
 
 
 
120
 
121
- const streams = [];
122
- for (const item of data) {
123
- if (!item.infohash || !item.name) continue;
124
-
125
- const magnet = `magnet:?xt=urn:btih:${item.infohash.toLowerCase()}&dn=${encodeURIComponent(item.name)}${TR}`;
126
- const sizeGB = item.size ? item.size / 1073741824 : null;
127
- const seeders = item.seeders || 0;
128
-
129
- streams.push({
130
- infoHash: item.infohash.toLowerCase(),
131
- name: item.name,
132
- title: `Torrents.csv - ${item.name.substring(0, 60)}...`,
133
- magnet: magnet,
134
- seeders: seeders,
135
- size: sizeGB ? sizeGB.toFixed(2) + ' GB' : 'N/A',
136
- _source: 'torrentcsv',
137
- });
138
- }
139
- console.log(`[Torrents.csv] ✅ Found ${streams.length} results for "${title}"`);
140
- return streams;
141
- } catch (e) {
142
- console.error('[Torrents.csv] ❌', e.message);
143
- return [];
144
  }
145
  }
146
 
147
- // ─── Test Endpoint ────────────────────────────────────────
148
  app.get('/test/:query', async (req, res) => {
149
- const { query } = req.params;
150
- console.log(`\n🔍 Testing with query: "${query}"\n`);
151
 
152
- const [subspleaseR, apibayR, torrentscsvR] = await Promise.allSettled([
153
  scrapeSubsPlease(query),
154
  scrapeAPIBay(query),
155
- scrapeTorrentsCSV(query),
156
  ]);
157
 
158
- const subsplease = subspleaseR.status === 'fulfilled' ? subspleaseR.value : [];
159
- const apibay = apibayR.status === 'fulfilled' ? apibayR.value : [];
160
- const torrentscsv = torrentscsvR.status === 'fulfilled' ? torrentscsvR.value : [];
161
-
162
  res.json({
163
  query,
164
- results: {
165
- subsplease: {
166
- status: subspleaseR.status,
167
- count: subsplease.length,
168
- data: subsplease.slice(0, 3),
169
- },
170
- apibay: {
171
- status: apibayR.status,
172
- count: apibay.length,
173
- data: apibay.slice(0, 3),
174
- },
175
- torrentscsv: {
176
- status: torrentscsvR.status,
177
- count: torrentscsv.length,
178
- data: torrentscsv.slice(0, 3),
179
- },
180
- total: subsplease.length + apibay.length + torrentscsv.length,
181
- },
182
  });
183
  });
184
 
185
- // ─── Health Check ────────────────────────────────────────
186
  app.get('/', (req, res) => {
187
- res.json({
188
- status: '✅ Running',
189
- version: '1.0.0',
190
- endpoints: [
191
- 'GET /test/:query - Test all three scrapers',
192
- 'Examples:',
193
- ' /test/Breaking%20Bad',
194
- ' /test/Solo%20Leveling',
195
- ' /test/The%20Office',
196
- ],
197
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
198
  });
199
 
200
  app.listen(PORT, '0.0.0.0', () => {
201
- console.log(`\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`);
202
- console.log(`🧪 Test Script Running on Port ${PORT}`);
203
- console.log(`━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`);
204
- console.log(`\n📝 Test URLs:\n`);
205
- console.log(` SubsPlease (Anime):`);
206
- console.log(` http://localhost:${PORT}/test/Solo%20Leveling\n`);
207
- console.log(` APIBay (TV Series):`);
208
- console.log(` http://localhost:${PORT}/test/Breaking%20Bad\n`);
209
- console.log(` Torrents.csv (General):`);
210
- console.log(` http://localhost:${PORT}/test/The%20Office\n`);
211
- console.log(`━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n`);
212
  });
 
3
  const express = require('express');
4
  const axios = require('axios');
5
 
6
+ const app = express();
7
  const PORT = process.env.PORT || 7860;
8
 
9
+ const UA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36';
 
 
 
 
 
 
 
 
10
 
11
  app.use((req, res, next) => {
12
  res.header('Access-Control-Allow-Origin', '*');
 
16
  next();
17
  });
18
 
19
+ // SubsPlease
20
  async function scrapeSubsPlease(title) {
 
21
  try {
 
22
  const { data } = await axios.get(
23
+ `https://subsplease.org/api/?f=search&tz=&s=${encodeURIComponent(title)}`,
24
+ { timeout: 8000 }
25
  );
26
 
 
 
27
  const streams = [];
28
+ for (const [key, show] of Object.entries(data || {})) {
29
+ if (show.downloads) {
30
+ for (const dl of show.downloads) {
31
+ if (dl.magnet && dl.res) {
32
+ streams.push({
33
+ title: `${show.show_name} ${dl.res}p`,
34
+ magnet: dl.magnet,
35
+ res: dl.res,
36
+ source: 'subsplease'
37
+ });
38
+ }
39
+ }
 
 
 
 
 
 
 
 
 
 
 
40
  }
41
  }
42
+ return { ok: true, count: streams.length, data: streams.slice(0, 3) };
43
+ } catch (e) {
44
+ return { ok: false, error: e.message, count: 0, data: [] };
 
 
45
  }
46
  }
47
 
48
+ // APIBay
49
  async function scrapeAPIBay(title) {
 
50
  try {
51
  const { data } = await axios.get(
52
  `https://apibay.org/q.php?q=${encodeURIComponent(title)}&cat=208`,
53
+ { timeout: 10000 }
54
  );
55
 
56
+ const streams = (data || []).slice(0, 10).map(item => ({
57
+ title: item.name,
58
+ seeders: item.seeders || 0,
59
+ hash: item.info_hash,
60
+ source: 'apibay'
61
+ }));
62
 
63
+ return { ok: true, count: streams.length, data: streams };
64
+ } catch (e) {
65
+ return { ok: false, error: e.message, count: 0, data: [] };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66
  }
67
  }
68
 
69
+ // Torrents.csv
70
  async function scrapeTorrentsCSV(title) {
 
71
  try {
72
  const { data } = await axios.get(
73
  `https://torrents-csv.com/service/search?q=${encodeURIComponent(title)}`,
74
+ { timeout: 10000 }
75
  );
76
 
77
+ const streams = (data || []).slice(0, 10).map(item => ({
78
+ title: item.name,
79
+ seeders: item.seeders || 0,
80
+ hash: item.infohash,
81
+ source: 'torrentcsv'
82
+ }));
83
 
84
+ return { ok: true, count: streams.length, data: streams };
85
+ } catch (e) {
86
+ return { ok: false, error: e.message, count: 0, data: [] };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
87
  }
88
  }
89
 
90
+ // Test endpoint
91
  app.get('/test/:query', async (req, res) => {
92
+ const query = decodeURIComponent(req.params.query);
 
93
 
94
+ const [sp, ab, csv] = await Promise.allSettled([
95
  scrapeSubsPlease(query),
96
  scrapeAPIBay(query),
97
+ scrapeTorrentsCSV(query)
98
  ]);
99
 
 
 
 
 
100
  res.json({
101
  query,
102
+ subsplease: sp.status === 'fulfilled' ? sp.value : { ok: false, error: 'Failed', count: 0 },
103
+ apibay: ab.status === 'fulfilled' ? ab.value : { ok: false, error: 'Failed', count: 0 },
104
+ torrentcsv: csv.status === 'fulfilled' ? csv.value : { ok: false, error: 'Failed', count: 0 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
105
  });
106
  });
107
 
108
+ // Main page
109
  app.get('/', (req, res) => {
110
+ res.type('html').send(`
111
+ <!DOCTYPE html>
112
+ <html>
113
+ <head>
114
+ <title>Test APIs</title>
115
+ <style>
116
+ body { font-family: Arial; background: #0d1117; color: #c9d1d9; padding: 20px; }
117
+ .container { max-width: 800px; margin: 0 auto; }
118
+ h1 { color: #58a6ff; }
119
+ .test-btn { background: #1f6feb; color: white; padding: 10px 20px; border: none; border-radius: 6px; cursor: pointer; margin: 5px; }
120
+ .test-btn:hover { background: #388bfd; }
121
+ pre { background: #161b22; padding: 15px; border-radius: 6px; overflow-x: auto; }
122
+ .status { padding: 10px; margin: 10px 0; border-radius: 6px; }
123
+ .ok { background: #0d3922; color: #3fb950; }
124
+ .error { background: #3d1f1a; color: #ff7b72; }
125
+ </style>
126
+ </head>
127
+ <body>
128
+ <div class="container">
129
+ <h1>🧪 Torrent APIs Test</h1>
130
+ <button class="test-btn" onclick="test('Breaking Bad')">Breaking Bad</button>
131
+ <button class="test-btn" onclick="test('Solo Leveling')">Solo Leveling</button>
132
+ <button class="test-btn" onclick="test('The Office')">The Office</button>
133
+ <input type="text" id="query" placeholder="Custom query" style="padding: 8px; width: 200px; background: #0d1117; color: #c9d1d9; border: 1px solid #30363d; border-radius: 6px;">
134
+ <button class="test-btn" onclick="customTest()">Search</button>
135
+ <pre id="result">Click a button to test...</pre>
136
+ </div>
137
+
138
+ <script>
139
+ async function test(q) {
140
+ document.getElementById('result').textContent = 'Loading...';
141
+ try {
142
+ const res = await fetch('/test/' + encodeURIComponent(q));
143
+ const data = await res.json();
144
+ document.getElementById('result').textContent = JSON.stringify(data, null, 2);
145
+ } catch (e) {
146
+ document.getElementById('result').textContent = 'Error: ' + e.message;
147
+ }
148
+ }
149
+
150
+ function customTest() {
151
+ const q = document.getElementById('query').value;
152
+ if (q) test(q);
153
+ }
154
+ </script>
155
+ </body>
156
+ </html>
157
+ `);
158
  });
159
 
160
  app.listen(PORT, '0.0.0.0', () => {
161
+ console.log(`✅ Running on port ${PORT}`);
 
 
 
 
 
 
 
 
 
 
162
  });