SteveCelticus commited on
Commit
7a9d6b2
·
verified ·
1 Parent(s): ab32af0

Upload 9 files

Browse files
Files changed (7) hide show
  1. episodes.js +90 -46
  2. main.js +53 -70
  3. manifest.json +1 -1
  4. package-lock.json +0 -0
  5. package.json +27 -22
  6. rama_series.js +31 -34
  7. streams.js +71 -58
episodes.js CHANGED
@@ -1,55 +1,95 @@
1
- import axios from 'axios';
2
  import cloudscraper from 'cloudscraper';
3
  import * as cheerio from 'cheerio';
4
- import { getStream, getEpisodes } from './streams.js'; // Importa sia getStream che getEpisodes
 
 
 
 
5
 
6
- // Lista di User-Agent
7
  const userAgents = [
8
- 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.3',
9
- 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:66.0) Gecko/20100101 Firefox/66.0',
10
- 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.1 Safari/605.1.15'
 
11
  ];
12
 
13
- // Funzione per ottenere headers casuali
14
- function getRandomHeaders() {
15
- const userAgent = userAgents[Math.floor(Math.random() * userAgents.length)];
16
- return {
17
- 'User-Agent': userAgent,
18
- 'Referer': 'https://ramaorientalfansub.tv/paese/corea-del-sud/',
19
- 'Accept-Language': 'en-US,en;q=0.9'
20
- };
21
  }
22
 
23
- // Istanza di Axios con headers casuali
24
- const axiosInstance = axios.create({
25
- headers: getRandomHeaders()
26
- });
27
- export { axiosInstance, axios, cloudscraper, cheerio };
28
- // Cache per i metadati delle serie
29
- const metaCache = new Map();
 
30
 
31
- async function fetchWithCloudscraper(url) {
32
- try {
33
- const data = await cloudscraper.get({
34
- uri: url,
35
- headers: getRandomHeaders() // Aggiungi headers casuali anche qui
36
- });
37
- return data;
38
- } catch (error) {
39
- console.error("Errore con Cloudscraper:", error);
40
- return null;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
  }
 
 
42
  }
43
 
 
44
  async function getMeta(id) {
45
  const meta = { id, type: 'series', name: '', poster: '', episodes: [] };
46
- const cleanId = id.replace(/,/g, '').replace(/\s+/g, '-').toLowerCase();
47
  const baseId = cleanId.replace(/-\d{4}$/, '');
48
  const seriesLink = `https://ramaorientalfansub.tv/drama/${baseId}/`;
49
- // Verifica se i metadati sono in cache
50
  if (metaCache.has(id)) {
51
- console.log(`Caricamento metadati dalla cache per ${id}`);
52
- return { meta: metaCache.get(id) }; // Restituisci direttamente i metadati dalla cache
53
  }
54
 
55
  try {
@@ -60,28 +100,32 @@ async function getMeta(id) {
60
  }
61
 
62
  const $ = cheerio.load(data);
63
- // Carica i metadati (name, poster, description)
64
  meta.name = $('a.text-accent').text().trim();
65
  meta.poster = $('img.wp-post-image').attr('src');
66
- let description = $('div.font-light > div:nth-child(1)').text().trim(); // Recupera la descrizione
67
- // Aggiungi il tag alla descrizione se presente
68
  if (meta.extra && meta.extra.tag) {
69
  description += ` [${meta.extra.tag.toUpperCase()}]`;
70
  }
 
 
 
 
 
 
 
 
 
 
 
 
71
 
72
- meta.description = description; // Assegna la descrizione modificata a meta.description
73
- // Carica gli episodi
74
- meta.episodes = await getEpisodes(seriesLink, $); // Passa l'oggetto cheerio
75
- console.log('Episodi trovati:', meta.episodes);
76
- // Salva i metadati nella cache (CON gli episodi)
77
  metaCache.set(id, meta);
78
- console.log(`Metadati salvati nella cache per ${id}`);
79
  } catch (error) {
80
  console.error('Errore nel caricamento dei dettagli della serie:', error);
81
  }
82
 
83
- console.log('Meta finale:', meta);
84
  return { meta };
85
  }
86
 
87
- export { getMeta };
 
 
 
1
  import cloudscraper from 'cloudscraper';
2
  import * as cheerio from 'cheerio';
3
+ import { getStream, getEpisodes } from './streams.js';
4
+
5
+ const DEBUG_MODE = process.env.DEBUG === 'true';
6
+ const CLOUDFLARE_RETRIES = process.env.CLOUDFLARE_RETRIES || 3;
7
+ const META_CACHE_TTL = 3600000; // 1 ora
8
 
 
9
  const userAgents = [
10
+ 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.122 Safari/537.36',
11
+ 'Mozilla/5.0 (Windows NT 10.0; rv:125.0) Gecko/20100101 Firefox/125.0',
12
+ 'Mozilla/5.0 (Macintosh; Intel Mac OS X 14_5) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4 Safari/605.1.15',
13
+ 'Mozilla/5.0 (Linux; Android 14; SM-S928U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.140 Mobile Safari/537.36'
14
  ];
15
 
16
+ const metaCache = new Map();
17
+
18
+ function debugLog(message) {
19
+ if (DEBUG_MODE) {
20
+ console.log(`[EPISODES][${new Date().toISOString()}] ${message}`);
21
+ }
 
 
22
  }
23
 
24
+ async function fetchWithCloudscraper(url, retries = 2) {
25
+ const userAgents = [
26
+ 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.122 Safari/537.36',
27
+ 'Mozilla/5.0 (Windows NT 10.0; rv:125.0) Gecko/20100101 Firefox/125.0',
28
+ 'Mozilla/5.0 (Macintosh; Intel Mac OS X 14_5) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4 Safari/605.1.15',
29
+ 'Mozilla/5.0 (Linux; Android 14; SM-S928U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.140 Mobile Safari/537.36',
30
+ 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/123.0.6312.122'
31
+ ];
32
 
33
+ function getRandomHeaders() {
34
+ return {
35
+ 'User-Agent': userAgents[Math.floor(Math.random() * userAgents.length)],
36
+ 'Referer': 'https://ramaorientalfansub.tv',
37
+ 'Accept-Language': 'en-US,en;q=0.9',
38
+ 'Upgrade-Insecure-Requests': '1',
39
+ 'Cache-Control': 'no-cache',
40
+ 'Pragma': 'no-cache',
41
+ 'Connection': 'keep-alive'
42
+ };
43
+ }
44
+
45
+ for (let i = 0; i < retries; i++) {
46
+ try {
47
+ console.log(`[${i + 1}/${retries}] Tentativo di scraping: ${url}`);
48
+ const response = await cloudscraper.get({
49
+ uri: url,
50
+ headers: getRandomHeaders(),
51
+ followAllRedirects: true,
52
+ maxRedirects: 5,
53
+ timeout: 30000,
54
+ resolveWithFullResponse: true // Aggiungi questa opzione
55
+ });
56
+
57
+ if (response.statusCode === 404) {
58
+ console.warn(`[${i + 1}/${retries}] Errore 404 per ${url}. Interrompo i tentativi.`);
59
+ return null; // Interrompi immediatamente se l'episodio non esiste
60
+ }
61
+
62
+ if (response && response.statusCode >= 200 && response.statusCode < 300) {
63
+ console.log(`[${i + 1}/${retries}] Scraping riuscito: ${url}`);
64
+ return response.body; // Restituisci il corpo della risposta
65
+ } else {
66
+ console.warn(`[${i + 1}/${retries}] Errore ${response.statusCode} per ${url}. Riprovo...`);
67
+ await new Promise(resolve => setTimeout(resolve, 5000));
68
+ }
69
+ } catch (error) {
70
+ console.error(`[${i + 1}/${retries}] Errore Cloudscraper per ${url}: ${error.message}`);
71
+ if (error.message.includes('Cloudflare')) {
72
+ console.warn(`[${i + 1}/${retries}] Rilevato Cloudflare. Attendo 15 secondi...`);
73
+ await new Promise(resolve => setTimeout(resolve, 15000));
74
+ } else {
75
+ console.warn(`[${i + 1}/${retries}] Errore generico. Attendo 5 secondi...`);
76
+ await new Promise(resolve => setTimeout(resolve, 5000));
77
+ }
78
+ }
79
  }
80
+ console.error(`[FETCH_FAILED] Impossibile recuperare ${url} dopo ${retries} tentativi.`);
81
+ return null;
82
  }
83
 
84
+
85
  async function getMeta(id) {
86
  const meta = { id, type: 'series', name: '', poster: '', episodes: [] };
87
+ const cleanId = id.replace(/,/g, '-').toLowerCase();
88
  const baseId = cleanId.replace(/-\d{4}$/, '');
89
  const seriesLink = `https://ramaorientalfansub.tv/drama/${baseId}/`;
90
+
91
  if (metaCache.has(id)) {
92
+ return { meta: metaCache.get(id) };
 
93
  }
94
 
95
  try {
 
100
  }
101
 
102
  const $ = cheerio.load(data);
 
103
  meta.name = $('a.text-accent').text().trim();
104
  meta.poster = $('img.wp-post-image').attr('src');
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
+
111
+ // Recupera gli episodi
112
+ meta.episodes = await getEpisodes(seriesLink, $);
113
+
114
+ // Aggiungi i link degli episodi alla descrizione
115
+ if (meta.episodes && meta.episodes.length > 0) {
116
+ description += "\n\nEpisodi:\n";
117
+ meta.episodes.forEach(episode => {
118
+ description += `- ${episode.title}: ${episode.streams[0].url}\n`;
119
+ });
120
+ }
121
 
 
 
 
 
 
122
  metaCache.set(id, meta);
 
123
  } catch (error) {
124
  console.error('Errore nel caricamento dei dettagli della serie:', error);
125
  }
126
 
 
127
  return { meta };
128
  }
129
 
130
+
131
+ export { getMeta };
main.js CHANGED
@@ -4,106 +4,89 @@ import filmsCatalog from './rama_films.js';
4
  import { getMeta } from './episodes.js';
5
 
6
  const { addonBuilder, serveHTTP } = pkg;
 
7
 
8
  const manifest = {
9
  "id": "community.ramaorientalfansub",
10
- "version": "1.0.5",
11
- "name": "Rama Oriental Fansub",
12
- "description": "Addon per visualizzare serie e film coreani dal sito Rama Oriental Fansub",
13
  "catalogs": [
14
  {
15
  "type": "series",
16
  "id": "rama_series",
17
- "name": "Serie Coreane",
18
  "extra": [{ "name": "skip" }]
19
  },
20
  {
21
  "type": "movie",
22
  "id": "rama_films",
23
- "name": "Film Coreani",
24
  "extra": [{ "name": "skip" }]
25
  }
26
  ],
27
- "resources": ["catalog", "meta", "stream"],
28
- "types": ["series", "movie"],
29
- "logo": "https://ramaorientalfansub.tv/wp-content/uploads/2023/10/cropped-Logo-1.png",
30
- "background": "https://ramaorientalfansub.tv/wp-content/uploads/2023/10/2860055-e1696595653601.jpg"
31
  };
32
 
33
  const builder = new addonBuilder(manifest);
34
  const metaCache = new Map();
35
 
36
- builder.defineStreamHandler(async ({ type, id }) => {
37
- if (type !== "series") {
38
- return Promise.resolve({ streams: [] });
39
- }
40
 
41
- console.log(`Richiesta stream per ID: ${id}`);
42
- let meta = null;
 
43
 
44
- if (metaCache.has(id)) {
45
- console.log(`Caricamento metadati dalla cache per ${id}`);
46
- meta = metaCache.get(id);
47
- } else {
48
- try {
49
- const metaResult = await getMeta(id);
50
- meta = metaResult.meta;
51
- metaCache.set(id, meta);
52
- } catch (error) {
53
- console.error(`Errore nel caricamento dei metadati per ${id}:`, error);
54
- return Promise.resolve({ streams: [] });
55
  }
56
- }
57
 
58
- if (!meta || !meta.episodes || meta.episodes.length === 0) {
59
- console.warn(`Nessun episodio trovato per ${id}`);
60
- return Promise.resolve({ streams: [] });
 
 
 
 
 
 
 
 
 
 
61
  }
62
-
63
- const streams = meta.episodes.flatMap(ep =>
64
- ep.streams.map(stream => ({
65
- title: `${ep.title} - ${stream.title}`,
66
- url: stream.url,
67
- type: "video/mp4",
68
- behaviorHints: {
69
- bingeGroup: id,
70
- notWebReady: false
71
- }
72
- }))
73
- );
74
-
75
- return Promise.resolve({ streams });
76
  });
77
 
78
  builder.defineCatalogHandler(async (args) => {
 
79
  if (args.type === 'series' && args.id === 'rama_series') {
80
- return seriesCatalog(args);
81
  } else if (args.type === 'movie' && args.id === 'rama_films') {
82
- return filmsCatalog(args);
83
  }
84
- });
85
-
86
- builder.defineMetaHandler(async (args) => {
87
- let meta = null;
88
-
89
- if (metaCache.has(args.id)) {
90
- console.log(`Caricamento metadati dalla cache per ${args.id}`);
91
- meta = { meta: metaCache.get(args.id) };
92
- } else {
93
- try {
94
- const metaResult = await getMeta(args.id);
95
- meta = metaResult.meta;
96
- metaCache.set(args.id, meta);
97
- } catch (error) {
98
- console.error(`Errore nel caricamento dei metadati per ${args.id}:`, error);
99
- return { meta: null };
100
- }
101
  }
102
-
103
  return { meta: { ...meta, extra: meta.extra } };
104
- });
105
-
106
- export default builder.getInterface();
107
-
108
- serveHTTP(builder.getInterface(), { port: 7000 });
109
- console.log(`Addon server is running at http://localhost:7000/manifest.json`);
 
4
  import { getMeta } from './episodes.js';
5
 
6
  const { addonBuilder, serveHTTP } = pkg;
7
+ const META_CACHE_TTL = 3600000; // 1 ora
8
 
9
  const manifest = {
10
  "id": "community.ramaorientalfansub",
11
+ "version": "1.0.6",
12
+ "name": "Rama Oriental Fansub+",
13
+ "description": "Addon migliorato con scraper avanzato",
14
  "catalogs": [
15
  {
16
  "type": "series",
17
  "id": "rama_series",
18
+ "name": "Serie Coreane+",
19
  "extra": [{ "name": "skip" }]
20
  },
21
  {
22
  "type": "movie",
23
  "id": "rama_films",
24
+ "name": "Film Coreani+",
25
  "extra": [{ "name": "skip" }]
26
  }
27
  ],
28
+ "resources": ["catalog", "meta", "stream"],
29
+ "types": ["series", "movie"],
30
+ "logo": "https://ramaorientalfansub.tv/wp-content/uploads/2023/10/cropped-Logo-1.png",
31
+ "background": "https://ramaorientalfansub.tv/wp-content/uploads/2023/10/2860055-e1696595653601.jpg"
32
  };
33
 
34
  const builder = new addonBuilder(manifest);
35
  const metaCache = new Map();
36
 
 
 
 
 
37
 
38
+ builder.defineStreamHandler(async ({ type, id }) => {
39
+ try {
40
+ if (type !== "series") return { streams: [] };
41
 
42
+ let meta = metaCache.get(id);
43
+ if (!meta || Date.now() - meta.timestamp > META_CACHE_TTL) {
44
+ const result = await getMeta(id);
45
+ meta = result.meta;
46
+ metaCache.set(id, { ...meta, timestamp: Date.now() });
 
 
 
 
 
 
47
  }
 
48
 
49
+ return {
50
+ streams: meta.episodes?.flatMap(ep =>
51
+ ep.streams.map(stream => ({
52
+ title: `${ep.title} - ${stream.title}`,
53
+ url: stream.url,
54
+ type: "video/mp4",
55
+ behaviorHints: { bingeGroup: id }
56
+ }))
57
+ ) || []
58
+ };
59
+ } catch (error) {
60
+ console.error(`Handler Error: ${error.message}`);
61
+ return { streams: [] };
62
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
63
  });
64
 
65
  builder.defineCatalogHandler(async (args) => {
66
+ console.log("Catalog Handler chiamato con:", args); // Aggiungi questo log
67
  if (args.type === 'series' && args.id === 'rama_series') {
68
+ return seriesCatalog(args);
69
  } else if (args.type === 'movie' && args.id === 'rama_films') {
70
+ return filmsCatalog(args);
71
  }
72
+ return { metas: [] }; // Aggiungi un return di default
73
+ });
74
+
75
+ builder.defineMetaHandler(async (args) => {
76
+ let meta = metaCache.get(args.id);
77
+ if (!meta) {
78
+ try {
79
+ const metaResult = await getMeta(args.id);
80
+ meta = metaResult.meta;
81
+ metaCache.set(args.id, meta);
82
+ } catch (error) {
83
+ console.error(`Errore nel caricamento dei metadati per ${args.id}:`, error);
84
+ return { meta: null };
85
+ }
 
 
 
86
  }
87
+
88
  return { meta: { ...meta, extra: meta.extra } };
89
+ });
90
+
91
+ serveHTTP(builder.getInterface(), { port: 7000 });
92
+ console.log(`Addon server is running at http://localhost:7000/manifest.json`);
 
 
manifest.json CHANGED
@@ -1,6 +1,6 @@
1
  {
2
  "id": "community.ramaorientalfansub",
3
- "version": "1.0.5",
4
  "name": "Rama Oriental Fansub",
5
  "description": "Addon per visualizzare serie e film coreani dal sito Rama Oriental Fansub",
6
  "catalogs": [
 
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": [
package-lock.json CHANGED
The diff for this file is too large to render. See raw diff
 
package.json CHANGED
@@ -1,23 +1,28 @@
1
  {
2
- "name": "stremio-rama-addon",
3
- "version": "1.0.0",
4
- "type": "module",
5
- "description": "Addon per Stremio per visualizzare serie e film coreani dal sito Rama Oriental Fansub",
6
- "main": "main.js",
7
- "scripts": {
8
- "start": "node main.js"
9
- },
10
- "author": "Il tuo nome",
11
- "license": "ISC",
12
- "dependencies": {
13
- "axios": "^1.5.0",
14
- "cheerio": "^1.0.0-rc.12",
15
- "cloudscraper": "4.6.0",
16
- "puppeteer": "24.3.1",
17
- "puppeteer-core": "^24.3.1",
18
- "puppeteer-extra": "^3.3.6",
19
- "puppeteer-extra-plugin-recaptcha": "^3.6.8",
20
- "puppeteer-extra-plugin-stealth": "^2.11.2",
21
- "stremio-addon-sdk": "^1.6.10"
22
- }
23
- }
 
 
 
 
 
 
1
  {
2
+ "name": "stremio-rama-addon",
3
+ "version": "1.0.6",
4
+ "main": "main.js",
5
+ "type": "module",
6
+ "scripts": {
7
+ "start": "node main.js",
8
+ "postinstall": "npx npm-force-resolutions"
9
+ },
10
+ "dependencies": {
11
+ "axios": "^1.6.2",
12
+ "cheerio": "^1.0.0",
13
+ "cloudscraper": "^4.1.4",
14
+ "stremio-addon-sdk": "^1.6.10",
15
+ "uuid": "^9.0.1"
16
+ },
17
+ "resolutions": {
18
+ "har-validator": "5.1.5",
19
+ "request": "2.88.2",
20
+ "tough-cookie": "^4.1.3"
21
+ },
22
+ "overrides": {
23
+ "cloudscraper": {
24
+ "request": "2.88.2",
25
+ "request-promise": "4.2.6"
26
+ }
27
+ }
28
+ }
rama_series.js CHANGED
@@ -1,16 +1,16 @@
1
  import cloudscraper from 'cloudscraper';
2
  import * as cheerio from 'cheerio';
3
 
 
4
  const userAgents = [
5
- 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.3',
6
- 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:66.0) Gecko/20100101 Firefox/66.0',
7
- 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.1 Safari/605.1.15'
8
  ];
9
 
10
  function getRandomHeaders() {
11
- const userAgent = userAgents[Math.floor(Math.random() * userAgents.length)];
12
  return {
13
- 'User-Agent': userAgent,
14
  'Referer': 'https://ramaorientalfansub.tv/paese/corea-del-sud/',
15
  'Accept-Language': 'en-US,en;q=0.9'
16
  };
@@ -20,7 +20,10 @@ async function fetchWithCloudscraper(url) {
20
  try {
21
  const data = await cloudscraper.get({
22
  uri: url,
23
- headers: getRandomHeaders()
 
 
 
24
  });
25
  return data;
26
  } catch (error) {
@@ -41,17 +44,14 @@ async function getCatalog(skip = 0) {
41
 
42
  while (catalog.length < itemsToLoad && pageNumber <= MAX_PAGES) {
43
  const pageUrl = `${BASE_URL}page/${pageNumber}/`;
44
- console.log(`Richiedendo pagina: ${pageNumber}`);
45
-
46
  let data;
 
47
  if (catalogCache.has(pageUrl)) {
48
- console.log(`Caricamento pagina ${pageNumber} dalla cache.`);
49
  data = catalogCache.get(pageUrl);
50
  } else {
51
  try {
52
  data = await fetchWithCloudscraper(pageUrl);
53
  if (!data) {
54
- console.warn(`Nessun dato ricevuto per ${pageUrl}`);
55
  pageNumber++;
56
  continue;
57
  }
@@ -68,7 +68,7 @@ async function getCatalog(skip = 0) {
68
 
69
  $('div.w-full.bg-gradient-to-t.from-primary').each((index, element) => {
70
  if (catalog.length >= itemsToLoad) {
71
- return false; // Interrompe il ciclo .each()
72
  }
73
 
74
  const titleElement = $(element).find('a.text-sm.line-clamp-2.font-medium.leading-snug.lg\\:leading-normal');
@@ -85,38 +85,35 @@ async function getCatalog(skip = 0) {
85
  extraTag = 'simulcast';
86
  }
87
 
88
- if (tagText.includes('tv')) {
89
- if (title && link) {
90
- const formattedTitle = title.replace(/\s+/g, '-').toLowerCase().replace(/[()]/g, '');
91
- const meta = {
92
- id: formattedTitle,
93
- type: 'series',
94
- name: title,
95
- poster,
96
- description: title,
97
- imdbRating: "N/A",
98
- released: 2024,
99
- };
100
-
101
- if (extraTag) {
102
- meta.extra = { tag: extraTag };
103
- }
104
-
105
- catalog.push(meta);
106
- foundItemsOnPage++;
107
  }
 
 
 
108
  }
109
  });
110
 
111
- console.log(`Pagina ${pageNumber}: trovati ${foundItemsOnPage} elementi.`);
112
  pageNumber++;
113
  }
114
 
115
- console.log(`Totale elementi raccolti: ${catalog.length}`);
116
  return catalog;
117
  }
118
 
119
  export default async function (args) {
120
  const skip = args.extra?.skip || 0;
121
- return { metas: await getCatalog(skip) };
122
- };
 
 
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
  };
 
20
  try {
21
  const data = await cloudscraper.get({
22
  uri: url,
23
+ headers: getRandomHeaders(),
24
+ followAllRedirects: true,
25
+ maxRedirects: 5,
26
+ timeout: 30000,
27
  });
28
  return data;
29
  } catch (error) {
 
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
  }
 
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');
 
85
  extraTag = 'simulcast';
86
  }
87
 
88
+ if (title && link) {
89
+ const formattedTitle = title.replace(/\s+/g, '-').toLowerCase().replace(/[()]/g, '');
90
+ const meta = {
91
+ id: formattedTitle,
92
+ type: 'series',
93
+ name: title,
94
+ poster,
95
+ description: title,
96
+ imdbRating: "N/A",
97
+ released: 2024,
98
+ };
99
+
100
+ if (extraTag) {
101
+ meta.extra = { tag: extraTag };
 
 
 
 
 
102
  }
103
+
104
+ catalog.push(meta);
105
+ foundItemsOnPage++;
106
  }
107
  });
108
 
 
109
  pageNumber++;
110
  }
111
 
 
112
  return catalog;
113
  }
114
 
115
  export default async function (args) {
116
  const skip = args.extra?.skip || 0;
117
+ const metas = await getCatalog(skip);
118
+ return { metas };
119
+ };
streams.js CHANGED
@@ -1,42 +1,74 @@
1
  import cloudscraper from 'cloudscraper';
2
  import * as cheerio from 'cheerio';
3
 
4
- // Lista di User-Agent
5
  const userAgents = [
6
- 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.3',
7
- 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:66.0) Gecko/20100101 Firefox/66.0',
8
- 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.1 Safari/605.1.15'
 
9
  ];
10
 
11
- // Funzione per ottenere headers casuali
12
- function getRandomHeaders() {
13
- const userAgent = userAgents[Math.floor(Math.random() * userAgents.length)];
14
- return {
15
- 'User-Agent': userAgent,
16
- 'Referer': 'https://ramaorientalfansub.tv', // Assicurati che il referer sia corretto
17
- 'Accept-Language': 'en-US,en;q=0.9'
18
- };
19
- }
20
 
21
- async function fetchWithCloudscraper(url) {
22
- try {
23
- const data = await cloudscraper.get({
24
- uri: url,
25
- headers: getRandomHeaders() // Aggiungi headers casuali
26
- });
27
- return data;
28
- } catch (error) {
29
- console.error("Errore con Cloudscraper:", error);
30
- // Gestisci l'errore e restituisci null o un valore appropriato
31
- return null;
32
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  }
34
 
35
- /**
36
- * Recupera il link dello stream per un episodio specifico.
37
- * @param {string} episodeLink - URL della pagina dell'episodio.
38
- * @returns {string|null} - URL dello stream o null se non trovato.
39
- */
40
  async function getStream(episodeLink) {
41
  let iframeSrc = null;
42
  try {
@@ -50,7 +82,6 @@ async function getStream(episodeLink) {
50
  iframeSrc = $('iframe').attr('src');
51
  if (iframeSrc) {
52
  iframeSrc = encodeURI(iframeSrc);
53
- console.log(`Stream trovato: ${decodeURI(iframeSrc)}`);
54
  if (iframeSrc.startsWith('https://server1.streamingrof.online/02-DRAMACOREANI')) {
55
  return iframeSrc;
56
  } else {
@@ -62,30 +93,20 @@ async function getStream(episodeLink) {
62
  return null;
63
  }
64
  } catch (err) {
65
- console.log(`Analizzando stream per: ${episodeLink}`);
66
- console.log('Iframe trovato:', iframeSrc);
67
  console.error(`Errore durante il recupero dello stream per ${episodeLink}:`, err);
68
  return null;
69
  }
70
  }
71
 
72
- /**
73
- * Recupera la lista degli episodi e i rispettivi stream per una serie.
74
- * @param {string} seriesLink - URL della pagina della serie.
75
- * @param {CheerioStatic} $ - Oggetto Cheerio caricato con la pagina della serie.
76
- * @returns {Array} - Lista di episodi con titolo, thumbnail e link allo stream.
77
- */
78
  async function getEpisodes(seriesLink, $) {
79
  try {
80
  const episodes = [];
81
  const baseEpisodeUrl = seriesLink.replace('/drama/', '/watch/');
82
- console.log('Chiamata a getEpisodes con URL:', seriesLink);
83
  let seriesId = seriesLink.split('/').filter(Boolean).pop();
84
- console.log('ID della serie estratto prima della pulizia:', seriesId);
85
- seriesId = seriesId.replace(/,/g, '').replace(/\s+/g, '-').toLowerCase();
86
- console.log('ID della serie dopo la pulizia:', seriesId);
87
  let seriesYear = null;
88
- // Estrai l'anno dalla pagina della serie (se presente)
89
  try {
90
  const titleText = $('title').text();
91
  const yearMatch = titleText.match(/\b(19|20)\d{2}\b/);
@@ -96,23 +117,19 @@ async function getEpisodes(seriesLink, $) {
96
  console.error('Errore durante il recupero dell\'anno della serie:', error);
97
  }
98
 
99
- console.log('Anno serie:', seriesYear);
100
- // Invece di cercare il numero massimo di episodi, proviamo semplicemente a caricarli in sequenza
101
  let episodeNumber = 1;
102
  while (true) {
103
  const episodeId = seriesYear ? `${seriesId}-${seriesYear}` : seriesId;
104
  const episodeLink = `https://ramaorientalfansub.tv/watch/${episodeId}-episodio-${episodeNumber}/`;
105
- console.log(`URL dell'episodio generato: ${episodeLink}`);
106
  try {
107
  const stream = await getStream(episodeLink);
108
  if (!stream) {
109
- console.log(`Stream non trovato per l'episodio ${episodeNumber}, interrompendo la ricerca.`);
110
- break; // Interrompi il ciclo se lo stream non viene trovato
111
  }
112
-
113
  episodes.push({
114
  id: `episodio-${episodeNumber}`,
115
- title: `${episodeNumber}`,
116
  thumbnail: 'div.swiper-slide:nth-child(1) > a:nth-child(1) > div:nth-child(1)',
117
  streams: [{
118
  title: `Episodio ${episodeNumber}`,
@@ -120,17 +137,13 @@ async function getEpisodes(seriesLink, $) {
120
  type: "video/mp4"
121
  }]
122
  });
123
- // Aggiungi un ritardo tra le richieste per ogni episodio
124
- await new Promise(resolve => setTimeout(resolve, 1000)); // Aspetta 1 secondo
125
  } catch (error) {
126
- console.error(`Errore durante il recupero dello stream per ${episodeLink}:`, err);
127
- break; // Interrompi il ciclo anche in caso di errore
128
  }
129
-
130
  episodeNumber++;
131
  }
132
-
133
- console.log('Episodi trovati:', JSON.stringify(episodes, null, 2));
134
  return episodes;
135
  } catch (err) {
136
  console.error('Errore durante il recupero degli episodi:', err);
@@ -138,4 +151,4 @@ async function getEpisodes(seriesLink, $) {
138
  }
139
  }
140
 
141
- export { getEpisodes, getStream }; // Esporta anche getStream
 
1
  import cloudscraper from 'cloudscraper';
2
  import * as cheerio from 'cheerio';
3
 
 
4
  const userAgents = [
5
+ 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.122 Safari/537.36',
6
+ 'Mozilla/5.0 (Windows NT 10.0; rv:125.0) Gecko/20100101 Firefox/125.0',
7
+ 'Mozilla/5.0 (Macintosh; Intel Mac OS X 14_5) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4 Safari/605.1.15',
8
+ 'Mozilla/5.0 (Linux; Android 14; SM-S928U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.140 Mobile Safari/537.36'
9
  ];
10
 
11
+ // Modifica fetchWithCloudscraper per accettare l'URL come parametro
12
+ async function fetchWithCloudscraper(url, retries = 2) {
13
+ const userAgents = [
14
+ 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.122 Safari/537.36',
15
+ 'Mozilla/5.0 (Windows NT 10.0; rv:125.0) Gecko/20100101 Firefox/125.0',
16
+ 'Mozilla/5.0 (Macintosh; Intel Mac OS X 14_5) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4 Safari/605.1.15',
17
+ 'Mozilla/5.0 (Linux; Android 14; SM-S928U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.140 Mobile Safari/537.36',
18
+ 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/123.0.6312.122'
19
+ ];
20
 
21
+ function getRandomHeaders() {
22
+ return {
23
+ 'User-Agent': userAgents[Math.floor(Math.random() * userAgents.length)],
24
+ 'Referer': 'https://ramaorientalfansub.tv',
25
+ 'Accept-Language': 'en-US,en;q=0.9',
26
+ 'Upgrade-Insecure-Requests': '1',
27
+ 'Cache-Control': 'no-cache',
28
+ 'Pragma': 'no-cache',
29
+ 'Connection': 'keep-alive'
30
+ };
 
31
  }
32
+
33
+ for (let i = 0; i < retries; i++) {
34
+ try {
35
+ console.log(`[${i + 1}/${retries}] Tentativo di scraping: ${url}`);
36
+ const response = await cloudscraper.get({
37
+ uri: url,
38
+ headers: getRandomHeaders(),
39
+ followAllRedirects: true,
40
+ maxRedirects: 5,
41
+ timeout: 30000,
42
+ resolveWithFullResponse: true // Aggiungi questa opzione
43
+ });
44
+
45
+ if (response.statusCode === 404) {
46
+ console.warn(`[${i + 1}/${retries}] Errore 404 per ${url}. Interrompo i tentativi.`);
47
+ return null; // Interrompi immediatamente se l'episodio non esiste
48
+ }
49
+
50
+ if (response && response.statusCode >= 200 && response.statusCode < 300) {
51
+ console.log(`[${i + 1}/${retries}] Scraping riuscito: ${url}`);
52
+ return response.body; // Restituisci il corpo della risposta
53
+ } else {
54
+ console.warn(`[${i + 1}/${retries}] Errore ${response.statusCode} per ${url}. Riprovo...`);
55
+ await new Promise(resolve => setTimeout(resolve, 5000));
56
+ }
57
+ } catch (error) {
58
+ console.error(`[${i + 1}/${retries}] Errore Cloudscraper per ${url}: ${error.message}`);
59
+ if (error.message.includes('Cloudflare')) {
60
+ console.warn(`[${i + 1}/${retries}] Rilevato Cloudflare. Attendo 15 secondi...`);
61
+ await new Promise(resolve => setTimeout(resolve, 15000));
62
+ } else {
63
+ console.warn(`[${i + 1}/${retries}] Errore generico. Attendo 5 secondi...`);
64
+ await new Promise(resolve => setTimeout(resolve, 5000));
65
+ }
66
+ }
67
+ }
68
+ console.error(`[FETCH_FAILED] Impossibile recuperare ${url} dopo ${retries} tentativi.`);
69
+ return null;
70
  }
71
 
 
 
 
 
 
72
  async function getStream(episodeLink) {
73
  let iframeSrc = null;
74
  try {
 
82
  iframeSrc = $('iframe').attr('src');
83
  if (iframeSrc) {
84
  iframeSrc = encodeURI(iframeSrc);
 
85
  if (iframeSrc.startsWith('https://server1.streamingrof.online/02-DRAMACOREANI')) {
86
  return iframeSrc;
87
  } else {
 
93
  return null;
94
  }
95
  } catch (err) {
 
 
96
  console.error(`Errore durante il recupero dello stream per ${episodeLink}:`, err);
97
  return null;
98
  }
99
  }
100
 
 
 
 
 
 
 
101
  async function getEpisodes(seriesLink, $) {
102
  try {
103
  const episodes = [];
104
  const baseEpisodeUrl = seriesLink.replace('/drama/', '/watch/');
 
105
  let seriesId = seriesLink.split('/').filter(Boolean).pop();
106
+ seriesId = seriesId.replace(/,/g, '-').toLowerCase();
107
+ seriesId = seriesId.replace(/--+/g, '-');
108
+
109
  let seriesYear = null;
 
110
  try {
111
  const titleText = $('title').text();
112
  const yearMatch = titleText.match(/\b(19|20)\d{2}\b/);
 
117
  console.error('Errore durante il recupero dell\'anno della serie:', error);
118
  }
119
 
 
 
120
  let episodeNumber = 1;
121
  while (true) {
122
  const episodeId = seriesYear ? `${seriesId}-${seriesYear}` : seriesId;
123
  const episodeLink = `https://ramaorientalfansub.tv/watch/${episodeId}-episodio-${episodeNumber}/`;
 
124
  try {
125
  const stream = await getStream(episodeLink);
126
  if (!stream) {
127
+ console.warn(`Nessuno stream trovato per ${episodeLink}. Interrompo.`);
128
+ break; // Interrompi il ciclo while
129
  }
 
130
  episodes.push({
131
  id: `episodio-${episodeNumber}`,
132
+ title: `Episodio ${episodeNumber}`,
133
  thumbnail: 'div.swiper-slide:nth-child(1) > a:nth-child(1) > div:nth-child(1)',
134
  streams: [{
135
  title: `Episodio ${episodeNumber}`,
 
137
  type: "video/mp4"
138
  }]
139
  });
140
+ // await new Promise(resolve => setTimeout(resolve, 1000)); // Rimuovi questo delay
 
141
  } catch (error) {
142
+ console.error(`Errore durante il recupero dello stream per ${episodeLink}:`, error);
143
+ break; // Interrompi il ciclo while anche in caso di errore
144
  }
 
145
  episodeNumber++;
146
  }
 
 
147
  return episodes;
148
  } catch (err) {
149
  console.error('Errore durante il recupero degli episodi:', err);
 
151
  }
152
  }
153
 
154
+ export { getEpisodes, getStream };