SteveCelticus commited on
Commit
446e7c2
·
verified ·
1 Parent(s): e922492

Upload 9 files

Browse files
Files changed (6) hide show
  1. Dockerfile +27 -27
  2. episodes.js +57 -27
  3. main.js +26 -12
  4. manifest.json +19 -19
  5. package-lock.json +34 -19
  6. rama_series.js +120 -112
Dockerfile CHANGED
@@ -1,27 +1,27 @@
1
- # Usa una immagine di Node.js come base
2
- FROM node:20-alpine
3
-
4
- # Imposta la directory di lavoro all'interno del container
5
- WORKDIR /app
6
-
7
- RUN npm install -g npm@11.2.0
8
-
9
- # Copia i file package.json e package-lock.json (se presente)
10
- COPY package.json ./
11
-
12
- RUN npm cache clean --force
13
-
14
- # Installa le dipendenze del progetto
15
- RUN npm install
16
-
17
- # Copia il resto dei file del progetto nella directory di lavoro
18
- COPY . .
19
-
20
- # Definisci la variabile d'ambiente (opzionale)
21
- # ENV PROXY_URL=""
22
-
23
- # Esponi la porta su cui l'addon sarà in ascolto
24
- EXPOSE 7000
25
-
26
- # Comando per avviare l'applicazione
27
- CMD [ "node", "main.js" ]
 
1
+ # Usa una immagine di Node.js come base
2
+ FROM node:20-alpine
3
+
4
+ # Imposta la directory di lavoro all'interno del container
5
+ WORKDIR /app
6
+
7
+ RUN npm install -g npm@11.2.0
8
+
9
+ # Copia i file package.json e package-lock.json (se presente)
10
+ COPY package.json ./
11
+
12
+ RUN npm cache clean --force
13
+
14
+ # Installa le dipendenze del progetto
15
+ RUN npm install
16
+
17
+ # Copia il resto dei file del progetto nella directory di lavoro
18
+ COPY . .
19
+
20
+ # Definisci la variabile d'ambiente (opzionale)
21
+ # ENV PROXY_URL=""
22
+
23
+ # Esponi la porta su cui l'addon sarà in ascolto
24
+ EXPOSE 7000
25
+
26
+ # Comando per avviare l'applicazione
27
+ CMD [ "node", "main.js" ]
episodes.js CHANGED
@@ -3,16 +3,6 @@ import cloudscraper from 'cloudscraper';
3
  import * as cheerio from 'cheerio';
4
  import { getStream } from './streams.js';
5
 
6
- const DEBUG_MODE = process.env.DEBUG === 'true';
7
- const CLOUDFLARE_RETRIES = process.env.CLOUDFLARE_RETRIES || 2;
8
- const META_CACHE_TTL = 30000; // 30 sec
9
-
10
- const userAgents = [
11
- 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.122 Safari/537.36',
12
- 'Mozilla/5.0 (Windows NT 10.0; rv:125.0) Gecko/20100101 Firefox/125.0',
13
- 'Mozilla/5.0 (Macintosh; Intel Mac OS X 14_5) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4 Safari/605.1.15',
14
- 'Mozilla/5.0 (Linux; Android 14; SM-S928U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.140 Mobile Safari/537.36'
15
- ];
16
 
17
  const metaCache = new Map();
18
 
@@ -45,46 +35,51 @@ async function fetchWithCloudscraper(url, retries = 2) {
45
  headers: getRandomHeaders(),
46
  followAllRedirects: true,
47
  maxRedirects: 2,
48
- timeout: 30000,
49
  resolveWithFullResponse: true // Aggiungi questa opzione
50
  });
51
 
 
52
  if (response.statusCode === 404) {
53
- console.warn(`[${i + 1}/${retries}] Errore 404 per ${url}. Interrompo i tentativi.`);
54
- return null; // Interrompi immediatamente se l'episodio non esiste
55
  }
56
 
57
- if (response && response.statusCode >= 200 && response.statusCode < 300) {
58
- console.log(`[${i + 1}/${retries}] Scraping riuscito: ${url}`);
59
- return response.body; // Restituisci il corpo della risposta
60
- } else {
61
- console.warn(`[${i + 1}/${retries}] Errore ${response.statusCode} per ${url}. Riprovo...`);
62
- await new Promise(resolve => setTimeout(resolve, 2000));
63
  }
 
 
 
 
64
  } catch (error) {
65
- console.error(`[${i + 1}/${retries}] Errore Cloudscraper per ${url}: ${error.message}`);
 
 
 
 
 
66
  if (error.message.includes('Cloudflare')) {
67
- console.warn(`[${i + 1}/${retries}] Rilevato Cloudflare. Attendo 10 secondi...`);
68
  await new Promise(resolve => setTimeout(resolve, 10000));
69
  } else {
70
- console.warn(`[${i + 1}/${retries}] Errore generico. Attendo 2 secondi...`);
71
  await new Promise(resolve => setTimeout(resolve, 2000));
72
  }
73
  }
74
  }
75
- console.error(`[FETCH_FAILED] Impossibile recuperare ${url} dopo ${retries} tentativi.`);
76
  return null;
77
  }
78
 
79
-
80
  async function getMeta(id) {
81
- const meta = { id, type: 'series', name: '', poster: '', episodes: [] };
82
  const cleanId = id.replace(/,/g, '-').toLowerCase();
83
  const baseId = cleanId.replace(/-\d{4}$/, '');
84
  const seriesLink = `https://ramaorientalfansub.tv/drama/${baseId}/`;
85
 
86
  if (metaCache.has(id)) {
87
- return { meta: metaCache.get(id) };
 
88
  }
89
 
90
  try {
@@ -97,11 +92,25 @@ async function getMeta(id) {
97
  const $ = cheerio.load(data);
98
  meta.name = $('a.text-accent').text().trim();
99
  meta.poster = $('img.wp-post-image').attr('src');
 
 
 
 
 
 
 
 
 
 
100
  let description = $('div.font-light > div:nth-child(1)').text().trim();
101
  if (meta.extra && meta.extra.tag) {
102
  description += ` [${meta.extra.tag.toUpperCase()}]`;
103
  }
104
  meta.description = description;
 
 
 
 
105
 
106
  // Recupera gli episodi
107
  meta.episodes = await getEpisodes(seriesLink, $, baseId); // Passa baseId a getEpisodes
@@ -151,10 +160,31 @@ async function getEpisodes(seriesLink, $, baseId) { // baseId come parametro
151
  break; // Interrompi il ciclo while
152
  }
153
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
154
  episodes.push({
155
  id: `episodio-${episodeNumber}`,
156
  title: `Episodio ${episodeNumber}`,
157
- thumbnail: 'div.swiper-slide:nth-child(1) > a:nth-child(1) > div:nth-child(1)',
158
  streams: [{
159
  title: `Episodio ${episodeNumber}`,
160
  url: stream,
 
3
  import * as cheerio from 'cheerio';
4
  import { getStream } from './streams.js';
5
 
 
 
 
 
 
 
 
 
 
 
6
 
7
  const metaCache = new Map();
8
 
 
35
  headers: getRandomHeaders(),
36
  followAllRedirects: true,
37
  maxRedirects: 2,
38
+ timeout: 10000,
39
  resolveWithFullResponse: true // Aggiungi questa opzione
40
  });
41
 
42
+ // Gestione 404 semplificata
43
  if (response.statusCode === 404) {
44
+ console.warn(`⚠️ [404] Pagina non trovata: ${url}`);
45
+ return null;
46
  }
47
 
48
+ if (response.statusCode >= 200 && response.statusCode < 300) {
49
+ console.log(`[${i + 1}/${retries}] Successo: ${url}`);
50
+ return response.body;
 
 
 
51
  }
52
+
53
+ console.warn(`⚠️ [${i + 1}/${retries}] Errore HTTP ${response.statusCode} per ${url}`);
54
+ await new Promise(resolve => setTimeout(resolve, 2000));
55
+
56
  } catch (error) {
57
+ // Gestione errori senza mostrare l'HTML
58
+ const errorMessage = error.response
59
+ ? `Errore ${error.response.statusCode}: ${error.message}`
60
+ : error.message;
61
+
62
+ console.warn(`⚠️ [${i + 1}/${retries}] ${errorMessage}`);
63
  if (error.message.includes('Cloudflare')) {
 
64
  await new Promise(resolve => setTimeout(resolve, 10000));
65
  } else {
 
66
  await new Promise(resolve => setTimeout(resolve, 2000));
67
  }
68
  }
69
  }
70
+ console.error(` Impossibile recuperare ${url}`);
71
  return null;
72
  }
73
 
 
74
  async function getMeta(id) {
75
+ const meta = { id, type: 'series', name: '', poster: '', episodes: null };
76
  const cleanId = id.replace(/,/g, '-').toLowerCase();
77
  const baseId = cleanId.replace(/-\d{4}$/, '');
78
  const seriesLink = `https://ramaorientalfansub.tv/drama/${baseId}/`;
79
 
80
  if (metaCache.has(id)) {
81
+ const cachedMeta = metaCache.get(id);
82
+ return { meta: { ...cachedMeta } }; // Restituisci una copia per evitare modifiche dirette
83
  }
84
 
85
  try {
 
92
  const $ = cheerio.load(data);
93
  meta.name = $('a.text-accent').text().trim();
94
  meta.poster = $('img.wp-post-image').attr('src');
95
+ // **NUOVA LOGICA PER RECUPERARE LA THUMBNAIL**
96
+ let thumbnail = $('div.thumbnail_url_episode_list > img').attr('data-src'); // Prova a prendere l'immagine con il nuovo selettore
97
+ if (!thumbnail) {
98
+ thumbnail = $('img.wp-post-image').attr('src'); // Se non la trova, usa il metodo precedente
99
+ console.log('Usando thumbnail wp-post-image'); // Log per debug
100
+ } else {
101
+ console.log('Usando thumbnail thumbnail_url_episode_list'); // Log per debug
102
+ }
103
+ meta.poster = thumbnail; // Assegna la thumbnail (trovata con uno dei due metodi) al poster
104
+
105
  let description = $('div.font-light > div:nth-child(1)').text().trim();
106
  if (meta.extra && meta.extra.tag) {
107
  description += ` [${meta.extra.tag.toUpperCase()}]`;
108
  }
109
  meta.description = description;
110
+ meta.seriesLink = seriesLink;
111
+ meta.baseId = baseId;
112
+
113
+ metaCache.set(id, meta);
114
 
115
  // Recupera gli episodi
116
  meta.episodes = await getEpisodes(seriesLink, $, baseId); // Passa baseId a getEpisodes
 
160
  break; // Interrompi il ciclo while
161
  }
162
 
163
+ const episodeData = await fetchWithCloudscraper(episodeLink);
164
+ if (!episodeData) {
165
+ console.warn(`Nessun dato ricevuto per ${episodeLink} durante il recupero della miniatura.`);
166
+ break;
167
+ }
168
+
169
+ const $$ = cheerio.load(episodeData); // Usa un'istanza separata di Cheerio
170
+
171
+ // **Selettore per la miniatura**
172
+ const thumbnailElement = $$('div.thumbnail_url_episode_list img.lazyloaded');
173
+ let thumbnailUrl = thumbnailElement.attr('data-src');
174
+
175
+ if (!thumbnailUrl) {
176
+ thumbnailUrl = thumbnailElement.attr('src'); //Fallback a src
177
+ }
178
+
179
+ if (!thumbnailUrl) {
180
+ console.warn(`Nessuna miniatura trovata per ${episodeLink}`);
181
+ thumbnailUrl = null; // Imposta a null se non trovata
182
+ }
183
+
184
  episodes.push({
185
  id: `episodio-${episodeNumber}`,
186
  title: `Episodio ${episodeNumber}`,
187
+ thumbnail: 'thumbnailUrl',
188
  streams: [{
189
  title: `Episodio ${episodeNumber}`,
190
  url: stream,
main.js CHANGED
@@ -3,26 +3,26 @@ import seriesCatalog from './rama_series.js';
3
  import { getMeta } from './episodes.js';
4
 
5
  const { addonBuilder, serveHTTP } = pkg;
6
- const META_CACHE_TTL = 90000; // 15 minuti
7
 
8
  const manifest = {
9
  "id": "community.ramaorientalfansub",
10
  "version": "1.0.6",
11
- "name": "Rama Oriental Fansub+",
12
  "description": "Addon migliorato con scraper avanzato",
13
  "catalogs": [
14
  {
15
  "type": "series",
16
  "id": "rama_series",
17
- "name": "Serie Coreane+",
18
  "extra": [{ "name": "skip" }]
19
  }
20
 
21
  ],
22
  "resources": ["catalog", "meta", "stream"],
23
  "types": ["series"],
24
- "logo": "https://ramaorientalfansub.tv/wp-content/uploads/2023/10/cropped-Logo-1.png",
25
- "background": "https://ramaorientalfansub.tv/wp-content/uploads/2023/10/2860055-e1696595653601.jpg"
26
  };
27
 
28
  const builder = new addonBuilder(manifest);
@@ -34,12 +34,20 @@ builder.defineStreamHandler(async ({ type, id }) => {
34
  if (type !== "series") return { streams: [] };
35
 
36
  let meta = metaCache.get(id);
37
- if (!meta || Date.now() - meta.timestamp > META_CACHE_TTL) {
38
- const result = await getMeta(id);
39
- meta = result.meta;
40
- metaCache.set(id, { ...meta, timestamp: Date.now() });
41
- }
 
 
 
 
 
 
 
42
 
 
43
  return {
44
  streams: meta.episodes?.flatMap(ep =>
45
  ep.streams.map(stream => ({
@@ -48,8 +56,13 @@ builder.defineStreamHandler(async ({ type, id }) => {
48
  type: "video/mp4",
49
  behaviorHints: { bingeGroup: id }
50
  }))
51
- ) || []
52
  };
 
 
 
 
 
53
  } catch (error) {
54
  console.error(`Handler Error: ${error.message}`);
55
  return { streams: [] };
@@ -60,7 +73,8 @@ builder.defineCatalogHandler(async (args) => {
60
  console.log("Catalog Handler chiamato con:", args); // Aggiungi questo log
61
  if (args.type === 'series' && args.id === 'rama_series') {
62
  return seriesCatalog(args);
63
- }
 
64
  return { metas: [] }; // Aggiungi un return di default
65
  });
66
 
 
3
  import { getMeta } from './episodes.js';
4
 
5
  const { addonBuilder, serveHTTP } = pkg;
6
+ const META_CACHE_TTL = 600000;
7
 
8
  const manifest = {
9
  "id": "community.ramaorientalfansub",
10
  "version": "1.0.6",
11
+ "name": "Rama Oriental Fansub +",
12
  "description": "Addon migliorato con scraper avanzato",
13
  "catalogs": [
14
  {
15
  "type": "series",
16
  "id": "rama_series",
17
+ "name": "Serie Coreane",
18
  "extra": [{ "name": "skip" }]
19
  }
20
 
21
  ],
22
  "resources": ["catalog", "meta", "stream"],
23
  "types": ["series"],
24
+ "logo": "https://i.imgur.com/i7VdVv7.png",
25
+ "background": "https://i.imgur.com/mtsxMk7.jpeg"
26
  };
27
 
28
  const builder = new addonBuilder(manifest);
 
34
  if (type !== "series") return { streams: [] };
35
 
36
  let meta = metaCache.get(id);
37
+ if (!meta) {
38
+ const metaResult = await getMeta(id);
39
+ meta = metaResult.meta;
40
+ metaCache.set(id, meta);
41
+ }
42
+
43
+ // Carica gli episodi solo se non sono già stati caricati
44
+ if (!meta.episodes) {
45
+ console.log(`Caricamento episodi per ${id}`);
46
+ meta.episodes = await getEpisodes(meta.seriesLink, meta.baseId);
47
+ metaCache.set(id, meta); // Aggiorna la cache con gli episodi
48
+ }
49
 
50
+ if (meta.episodes) {
51
  return {
52
  streams: meta.episodes?.flatMap(ep =>
53
  ep.streams.map(stream => ({
 
56
  type: "video/mp4",
57
  behaviorHints: { bingeGroup: id }
58
  }))
59
+ )
60
  };
61
+ } else {
62
+ console.warn(`Nessun episodio trovato per ${id}`);
63
+ return { streams: [] };
64
+ }
65
+
66
  } catch (error) {
67
  console.error(`Handler Error: ${error.message}`);
68
  return { streams: [] };
 
73
  console.log("Catalog Handler chiamato con:", args); // Aggiungi questo log
74
  if (args.type === 'series' && args.id === 'rama_series') {
75
  return seriesCatalog(args);
76
+
77
+ }
78
  return { metas: [] }; // Aggiungi un return di default
79
  });
80
 
manifest.json CHANGED
@@ -1,19 +1,19 @@
1
- {
2
- "id": "community.ramaorientalfansub",
3
- "version": "1.0.6",
4
- "name": "Rama Oriental Fansub",
5
- "description": "Addon per visualizzare serie e film coreani dal sito Rama Oriental Fansub",
6
- "catalogs": [
7
- {
8
- "type": "series",
9
- "id": "rama_series",
10
- "name": "Serie Coreane",
11
- "extra": [{ "name": "skip" }]
12
- }
13
-
14
- ],
15
- "resources": ["catalog", "meta", "stream"],
16
- "types": ["series"],
17
- "logo": "https://ramaorientalfansub.tv/wp-content/uploads/2023/10/cropped-Logo-1.png",
18
- "background": "https://ramaorientalfansub.tv/wp-content/uploads/2023/10/2860055-e1696595653601.jpg"
19
- }
 
1
+ {
2
+ "id": "community.ramaorientalfansub",
3
+ "version": "1.0.6",
4
+ "name": "Rama Oriental Fansub +",
5
+ "description": "Addon per visualizzare serie e film coreani dal sito Rama Oriental Fansub",
6
+ "catalogs": [
7
+ {
8
+ "type": "series",
9
+ "id": "rama_series",
10
+ "name": "Serie Coreane",
11
+ "extra": [{ "name": "skip" }]
12
+ }
13
+
14
+ ],
15
+ "resources": ["catalog", "meta", "stream"],
16
+ "types": ["series"],
17
+ "logo": "https://i.imgur.com/i7VdVv7.png",
18
+ "background": "https://i.imgur.com/mtsxMk7.jpeg"
19
+ }
package-lock.json CHANGED
@@ -16,6 +16,21 @@
16
  "uuid": "^9.0.1"
17
  }
18
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  "node_modules/tr46": {
20
  "version": "0.0.3",
21
  "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
@@ -1750,16 +1765,6 @@
1750
  "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
1751
  "license": "MIT"
1752
  },
1753
- "node_modules/request/node_modules/qs": {
1754
- "version": "6.5.3",
1755
- "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz",
1756
- "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==",
1757
- "license": "BSD-3-Clause",
1758
- "peer": true,
1759
- "engines": {
1760
- "node": ">=0.6"
1761
- }
1762
- },
1763
  "node_modules/express": {
1764
  "version": "4.21.2",
1765
  "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz",
@@ -2004,6 +2009,21 @@
2004
  "url": "https://github.com/sponsors/ljharb"
2005
  }
2006
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2007
  "node_modules/send/node_modules/encodeurl": {
2008
  "version": "1.0.2",
2009
  "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
@@ -2253,18 +2273,13 @@
2253
  }
2254
  },
2255
  "node_modules/qs": {
2256
- "version": "6.13.0",
2257
- "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
2258
- "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
2259
  "license": "BSD-3-Clause",
2260
- "dependencies": {
2261
- "side-channel": "^1.0.6"
2262
- },
2263
  "engines": {
2264
  "node": ">=0.6"
2265
- },
2266
- "funding": {
2267
- "url": "https://github.com/sponsors/ljharb"
2268
  }
2269
  },
2270
  "node_modules/restore-cursor": {
 
16
  "uuid": "^9.0.1"
17
  }
18
  },
19
+ "node_modules/body-parser/node_modules/qs": {
20
+ "version": "6.13.0",
21
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
22
+ "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
23
+ "license": "BSD-3-Clause",
24
+ "dependencies": {
25
+ "side-channel": "^1.0.6"
26
+ },
27
+ "engines": {
28
+ "node": ">=0.6"
29
+ },
30
+ "funding": {
31
+ "url": "https://github.com/sponsors/ljharb"
32
+ }
33
+ },
34
  "node_modules/tr46": {
35
  "version": "0.0.3",
36
  "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
 
1765
  "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
1766
  "license": "MIT"
1767
  },
 
 
 
 
 
 
 
 
 
 
1768
  "node_modules/express": {
1769
  "version": "4.21.2",
1770
  "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz",
 
2009
  "url": "https://github.com/sponsors/ljharb"
2010
  }
2011
  },
2012
+ "node_modules/express/node_modules/qs": {
2013
+ "version": "6.13.0",
2014
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
2015
+ "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
2016
+ "license": "BSD-3-Clause",
2017
+ "dependencies": {
2018
+ "side-channel": "^1.0.6"
2019
+ },
2020
+ "engines": {
2021
+ "node": ">=0.6"
2022
+ },
2023
+ "funding": {
2024
+ "url": "https://github.com/sponsors/ljharb"
2025
+ }
2026
+ },
2027
  "node_modules/send/node_modules/encodeurl": {
2028
  "version": "1.0.2",
2029
  "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
 
2273
  }
2274
  },
2275
  "node_modules/qs": {
2276
+ "version": "6.5.3",
2277
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz",
2278
+ "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==",
2279
  "license": "BSD-3-Clause",
2280
+ "peer": true,
 
 
2281
  "engines": {
2282
  "node": ">=0.6"
 
 
 
2283
  }
2284
  },
2285
  "node_modules/restore-cursor": {
rama_series.js CHANGED
@@ -1,113 +1,121 @@
1
- import cloudscraper from 'cloudscraper';
2
- import * as cheerio from 'cheerio';
3
-
4
-
5
- const userAgents = [
6
- 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36',
7
- 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:125.0) Gecko/20100101 Firefox/125.0',
8
- 'Mozilla/5.0 (Macintosh; Intel Mac OS X 14_5) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4 Safari/605.1.15'
9
- ];
10
-
11
- function getRandomHeaders() {
12
- return {
13
- 'User-Agent': userAgents[Math.floor(Math.random() * userAgents.length)],
14
- 'Referer': 'https://ramaorientalfansub.tv/paese/corea-del-sud/',
15
- 'Accept-Language': 'en-US,en;q=0.9'
16
- };
17
- }
18
-
19
- async function fetchWithCloudscraper(url) {
20
- try {
21
- const data = await cloudscraper.get({
22
- uri: url,
23
- headers: getRandomHeaders(),
24
- followAllRedirects: true,
25
- maxRedirects: 2,
26
- timeout: 10000,
27
- });
28
- return data;
29
- } catch (error) {
30
- console.error("Errore con Cloudscraper:", error);
31
- return null;
32
- }
33
- }
34
-
35
- const BASE_URL = 'https://ramaorientalfansub.tv/paese/corea-del-sud/';
36
- const ITEMS_PER_PAGE = 20;
37
- const MAX_PAGES = 35;
38
- const catalogCache = new Map();
39
-
40
- async function getCatalog(skip = 0) {
41
- const catalog = [];
42
- let pageNumber = Math.floor(skip / ITEMS_PER_PAGE) + 1;
43
- let itemsToLoad = ITEMS_PER_PAGE;
44
-
45
- while (catalog.length < itemsToLoad && pageNumber <= MAX_PAGES) {
46
- const pageUrl = `${BASE_URL}page/${pageNumber}/`;
47
- let data;
48
-
49
- if (catalogCache.has(pageUrl)) {
50
- data = catalogCache.get(pageUrl);
51
- } else {
52
- try {
53
- data = await fetchWithCloudscraper(pageUrl);
54
- if (!data) {
55
- pageNumber++;
56
- continue;
57
- }
58
- catalogCache.set(pageUrl, data);
59
- } catch (error) {
60
- console.error(`Errore nel caricamento della pagina ${pageNumber}:`, error);
61
- pageNumber++;
62
- continue;
63
- }
64
- }
65
-
66
- const $ = cheerio.load(data);
67
- let foundItemsOnPage = 0;
68
-
69
- $('div.w-full.bg-gradient-to-t.from-primary').each((index, element) => {
70
- if (catalog.length >= itemsToLoad) {
71
- return false;
72
- }
73
-
74
- const titleElement = $(element).find('a.text-sm.line-clamp-2.font-medium.leading-snug.lg\\:leading-normal');
75
- const title = titleElement.text().trim();
76
- const link = titleElement.attr('href');
77
- const poster = $(element).find('img.lazyload').attr('data-src');
78
- const tagElement = $(element).find('.inline-block.md\\:my-3.uppercase');
79
- const tagText = tagElement.text().trim().toLowerCase();
80
- let extraTag = null;
81
-
82
- if (tagText.includes('Tv')) { // Aggiungi questa condizione
83
-
84
- if (title && link) {
85
- const formattedTitle = title.replace(/\s+/g, '-').toLowerCase().replace(/[()]/g, '');
86
- const meta = {
87
- id: formattedTitle,
88
- type: 'series',
89
- name: title,
90
- poster: poster,
91
- description: title,
92
- imdbRating: "N/A",
93
- released: 2024,
94
- };
95
-
96
-
97
- catalog.push(meta);
98
- foundItemsOnPage++;
99
- }
100
- }
101
- });
102
-
103
- pageNumber++;
104
- }
105
-
106
- return catalog;
107
- }
108
-
109
- export default async function (args) {
110
- const skip = args.extra?.skip || 0;
111
- const metas = await getCatalog(skip);
112
- return { metas };
 
 
 
 
 
 
 
 
113
  };
 
1
+ import cloudscraper from 'cloudscraper';
2
+ import * as cheerio from 'cheerio';
3
+
4
+
5
+ const userAgents = [
6
+ 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36',
7
+ 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:125.0) Gecko/20100101 Firefox/125.0',
8
+ 'Mozilla/5.0 (Macintosh; Intel Mac OS X 14_5) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4 Safari/605.1.15'
9
+ ];
10
+
11
+ function getRandomHeaders() {
12
+ return {
13
+ 'User-Agent': userAgents[Math.floor(Math.random() * userAgents.length)],
14
+ 'Referer': 'https://ramaorientalfansub.tv/paese/corea-del-sud/',
15
+ 'Accept-Language': 'en-US,en;q=0.9'
16
+ };
17
+ }
18
+
19
+ async function fetchWithCloudscraper(url) {
20
+ try {
21
+ const data = await cloudscraper.get({
22
+ uri: url,
23
+ headers: getRandomHeaders(),
24
+ followAllRedirects: true,
25
+ maxRedirects: 2,
26
+ timeout: 10000,
27
+ });
28
+ return data;
29
+ } catch (error) {
30
+ console.error("Errore con Cloudscraper:", error);
31
+ return null;
32
+ }
33
+ }
34
+
35
+ const BASE_URL = 'https://ramaorientalfansub.tv/paese/corea-del-sud/';
36
+ const ITEMS_PER_PAGE = 25;
37
+ const MAX_PAGES = 35;
38
+ const catalogCache = new Map();
39
+
40
+ async function getCatalog(skip = 0) {
41
+ const catalog = [];
42
+ let pageNumber = Math.floor(skip / ITEMS_PER_PAGE) + 1;
43
+ let itemsToLoad = ITEMS_PER_PAGE;
44
+
45
+ while (catalog.length < itemsToLoad && pageNumber <= MAX_PAGES) {
46
+ const pageUrl = `${BASE_URL}page/${pageNumber}/`;
47
+ let data;
48
+
49
+ if (catalogCache.has(pageUrl)) {
50
+ data = catalogCache.get(pageUrl);
51
+ } else {
52
+ try {
53
+ data = await fetchWithCloudscraper(pageUrl);
54
+ if (!data) {
55
+ pageNumber++;
56
+ continue;
57
+ }
58
+ catalogCache.set(pageUrl, data);
59
+ } catch (error) {
60
+ console.error(`Errore nel caricamento della pagina ${pageNumber}:`, error);
61
+ pageNumber++;
62
+ continue;
63
+ }
64
+ }
65
+
66
+ const $ = cheerio.load(data);
67
+ let foundItemsOnPage = 0;
68
+
69
+ $('div.bg-gradient-to-t').each((index, element) => {
70
+ if (catalog.length >= itemsToLoad) return false;
71
+
72
+ // Recupera l'immagine del poster
73
+ const posterElement = $(element).find('img.object-cover');
74
+ let poster = posterElement.attr('data-src') || posterElement.attr('src');
75
+ if (!poster) {
76
+ console.warn(`Poster mancante per l'elemento ${index}`);
77
+ return true; // Continua il ciclo
78
+ }
79
+ // const poster = posterElement.attr('src');
80
+ const titleElement = $(element).find('a.text-sm.line-clamp-2.font-medium.leading-snug.lg\\:leading-normal');
81
+ const title = titleElement.text().trim();
82
+ const link = titleElement.attr('href');
83
+ // const poster = $(element).find('img.object-cover').attr('src');
84
+ // const poster = $(element).find('img.object-cover').attr('data-src');
85
+ const tagElement = $(element).find('div.text-xs.text-text-color.w-full.line-clamp-1.absolute.bottom-1.text-opacity-75 span.inline-block.md\\:mlb-3.uppercase');
86
+ const tagText = tagElement.text().trim().toLowerCase();
87
+
88
+
89
+
90
+ if (tagText.includes('tv')) { // Aggiungi questa condizione
91
+
92
+ if (title && link) {
93
+ const formattedTitle = title.replace(/\s+/g, '-').toLowerCase().replace(/[()]/g, '');
94
+ const meta = {
95
+ id: formattedTitle,
96
+ type: 'series',
97
+ name: title,
98
+ poster: poster || 'https://example.com/default-poster.jpg',
99
+ description: title,
100
+ imdbRating: "N/A",
101
+ released: 2024,
102
+ };
103
+
104
+
105
+ catalog.push(meta);
106
+ foundItemsOnPage++;
107
+ }
108
+ }
109
+ });
110
+
111
+ pageNumber++;
112
+ }
113
+
114
+ return catalog;
115
+ }
116
+
117
+ export default async function (args) {
118
+ const skip = args.extra?.skip || 0;
119
+ const metas = await getCatalog(skip);
120
+ return { metas };
121
  };