stnh70 commited on
Commit
325997b
·
verified ·
1 Parent(s): 1c0bc10

Update web.ts

Browse files
Files changed (1) hide show
  1. web.ts +117 -116
web.ts CHANGED
@@ -65,7 +65,7 @@ function createApiErrorObject(errorMessage: string, statusProviderName: string =
65
  };
66
  }
67
  function createProviderErrorObject(providerName: string, errorMessage: string): ProviderErrorResult {
68
- return createApiErrorObject(errorMessage, providerName);
69
  }
70
 
71
  function stringAtob(input: string): string {
@@ -105,17 +105,19 @@ function getSizeQuality(url: string): number {
105
  // --- Provider Specific Logic ---
106
 
107
  // == EmbedSu Provider ==
108
- const EMBED_SU_DOMAIN = "https://iqslgbok.deploy.cx/param/User-Agent=" + encodeURIComponent(USER_AGENT) + "/param/Origin="+ encodeURIComponent("https://embed.su") +"/param/Referer="+ encodeURIComponent("https://embed.su/") +"/https://embed.su";
 
 
109
  const EMBED_SU_HEADERS = {
110
  'User-Agent': USER_AGENT,
111
- 'Referer': "https://embed.su/",//EMBED_SU_DOMAIN
112
- 'Origin': "https://embed.su",//EMBED_SU_DOMAIN
113
  };
114
 
115
  async function getEmbedSu(tmdb_id: string, s?: string, e?: string): Promise<ProviderFunctionReturn> {
116
  const providerName = PROVIDERS.embedsu.displayName;
117
  try {
118
- const urlSearch = s && e ? `${EMBED_SU_DOMAIN}/embed/tv/${tmdb_id}/${s}/${e}` : `${EMBED_SU_DOMAIN}/embed/movie/${tmdb_id}`;
119
  const htmlSearchResponse = await fetch(urlSearch, { method: 'GET', headers: EMBED_SU_HEADERS });
120
  if (!htmlSearchResponse.ok) return createProviderErrorObject(providerName, `Failed to fetch initial page: HTTP ${htmlSearchResponse.status}`);
121
 
@@ -146,7 +148,7 @@ async function getEmbedSu(tmdb_id: string, s?: string, e?: string): Promise<Prov
146
  for (const item of secondDecode) {
147
  try {
148
  if (!item || !item.hash) continue;
149
- const urlDirect = `${EMBED_SU_DOMAIN}/api/e/${item.hash}`;
150
  const dataDirect = await requestGet(urlDirect, { "Referer": EMBED_SU_DOMAIN, "User-Agent": USER_AGENT, "Origin": EMBED_SU_DOMAIN });
151
  if (!dataDirect || !dataDirect.source) { console.warn(`${providerName}: No source found for hash ${item.hash}`); continue; }
152
 
@@ -154,7 +156,7 @@ async function getEmbedSu(tmdb_id: string, s?: string, e?: string): Promise<Prov
154
  url: sub.file, lang: sub.label ? sub.label.split('-')[0].trim().toLowerCase() : 'en'
155
  })).filter((track: Subtitle) => track.url);
156
 
157
- const requestDirectSize = await fetch(dataDirect.source.replace("https://embed.su",EMBED_SU_DOMAIN), { headers: EMBED_SU_HEADERS, method: "GET" });
158
  if (!requestDirectSize.ok) { console.warn(`${providerName}: Failed to fetch source ${dataDirect.source}: HTTP ${requestDirectSize.status}`); continue; }
159
 
160
  const parseRequest = await requestDirectSize.text();
@@ -163,7 +165,7 @@ async function getEmbedSu(tmdb_id: string, s?: string, e?: string): Promise<Prov
163
  const directQuality: MediaFile[] = patternSize.map(patternItem => {
164
  try {
165
  const sizeQuality = getSizeQuality(patternItem);
166
- let dURL = `${EMBED_SU_DOMAIN}${patternItem}`;
167
  dURL = dURL.replace(".png", ".m3u8");
168
  const fileObj: MediaFile = { file: dURL, type: 'hls', quality: `${sizeQuality}p`, lang: 'en' };
169
  if (s && e) { // Add season and episode if they exist (TV show)
@@ -176,7 +178,7 @@ async function getEmbedSu(tmdb_id: string, s?: string, e?: string): Promise<Prov
176
 
177
  if (!directQuality.length) { console.warn(`${providerName}: No valid qualities found for source ${dataDirect.source}`); continue; }
178
 
179
- return { source: { provider: providerName, files: directQuality, subtitles: tracks, headers: { "Referer": "https://embed.su", "User-Agent": USER_AGENT, "Origin": "https://embed.su" } } };
180
  } catch (error) { console.error(`${providerName}: Error processing item ${item.hash}:`, error); }
181
  }
182
  return createProviderErrorObject(providerName, "No valid sources found after processing all available items");
@@ -187,21 +189,20 @@ async function getEmbedSu(tmdb_id: string, s?: string, e?: string): Promise<Prov
187
  }
188
 
189
  // == VidSrc.SU Provider ==
190
- // const VIDSRC_SU_DOMAIN = "https://vidsrc.su/";
191
- const VIDSRC_SU_DOMAIN = "https://iqslgbok.deploy.cx/param/User-Agent=" + encodeURIComponent(USER_AGENT) + "/param/Origin="+ encodeURIComponent("https://vidsrc.su") +"/param/Referer="+ encodeURIComponent("https://vidsrc.su/") +"/https://vidsrc.su/";
192
- const VIDSRC_SU_HEADERS = { 'User-Agent': USER_AGENT, 'Referer': "https://vidsrc.su/", 'Origin': "https://vidsrc.su" };
193
 
194
  async function getVidSrcSu(tmdb_id: string, s?: string, e?: string): Promise<ProviderFunctionReturn> {
195
  const providerName = PROVIDERS.vidsrcsu.displayName;
196
  const embedUrl = s && e ? `${VIDSRC_SU_DOMAIN}embed/tv/${tmdb_id}/${s}/${e}` : `${VIDSRC_SU_DOMAIN}embed/movie/${tmdb_id}`;
197
- console.log(embedUrl)
198
  try {
199
  const response = await fetch(embedUrl, { headers: VIDSRC_SU_HEADERS });
200
  if (!response.ok) return createProviderErrorObject(providerName, `Failed to fetch embed page: HTTP ${response.status}`);
201
-
202
  const html = await response.text();
203
  let subtitles: Subtitle[] = [];
204
-
205
  const servers: MediaFile[] = [...html.matchAll(/label: 'Server (?:[^']*)', url: '(https?:\/\/[^']+\.m3u8[^']*)'/gi)].map(match => {
206
  const fileObj: MediaFile = { file: match[1], type: "hls", lang: "en" };
207
  if (s && e) { // Add season and episode if they exist
@@ -225,7 +226,7 @@ async function getVidSrcSu(tmdb_id: string, s?: string, e?: string): Promise<Pro
225
  }
226
 
227
  if (servers.length === 0) return createProviderErrorObject(providerName, "No valid video streams found in embed page");
228
- return { source: { provider: providerName, files: servers, subtitles: subtitles, headers: { "Referer": "https://vidsrc.su", "User-Agent": USER_AGENT, "Origin": "https://vidsrc.su" } } };
229
  } catch (error) {
230
  console.error(`${providerName}: Unexpected error:`, error);
231
  return createProviderErrorObject(providerName, `Unexpected error: ${error.message}`);
@@ -238,117 +239,117 @@ const AUTOEMBED_API_URL_BASE = "https://tom.autoembed.cc/api/getVideoSource";
238
 
239
  // Modified parseAutoEmbedM3U8 to accept s and e
240
  function parseAutoEmbedM3U8(m3u8Content: string, s?: string, e?: string, m3u8Url?: string): MediaFile[] {
241
- try {
242
- const lines = m3u8Content.split('\n');
243
- const sources: MediaFile[] = [];
244
- let currentQuality: string | undefined = undefined;
245
-
246
- let baseUrl = '';
247
- let domain = '';
248
- if (m3u8Url) {
249
- try {
250
- const urlObj = new URL(m3u8Url);
251
- domain = `${urlObj.protocol}//${urlObj.hostname}`;
252
-
253
- // Get base URL by removing the filename from the full URL
254
- const urlPath = m3u8Url.split('/');
255
- urlPath.pop(); // Remove the filename
256
- baseUrl = urlPath.join('/') + '/';
257
- } catch (error) {
258
- console.error('AutoEmbed: Error extracting URL information:', error);
259
- }
260
- }
261
 
262
- for (let i = 0; i < lines.length; i++) {
263
- const trimmedLine = lines[i].trim();
264
- if (trimmedLine.startsWith('#EXT-X-STREAM-INF:')) {
265
- const resolutionMatch = trimmedLine.match(/RESOLUTION=\d+x(\d+)/);
266
- currentQuality = resolutionMatch && resolutionMatch[1] ? `${resolutionMatch[1]}p` : undefined;
267
- for (let j = i + 1; j < lines.length; j++) {
268
- const nextLineTrimmed = lines[j].trim();
269
- if (nextLineTrimmed && !nextLineTrimmed.startsWith('#')) {
270
- // Handle different types of paths
271
- let filePath = nextLineTrimmed;
272
-
273
- // Skip URLs that already have protocol
274
- if (!nextLineTrimmed.match(/^https?:\/\//)) {
275
- if (nextLineTrimmed.startsWith('/')) {
276
- // Absolute path starting with '/'
277
- if (domain) {
278
- filePath = `${domain}${nextLineTrimmed}`;
279
- }
280
- } else {
281
- // Relative path
282
- if (baseUrl) {
283
- filePath = `${baseUrl}${nextLineTrimmed}`;
284
- }
285
- }
286
- }
287
- const fileObj: MediaFile = {
288
- file: filePath, type: "hls",
289
- quality: currentQuality || 'unknown', lang: "en"
290
- };
291
- if (s && e) { // Add season and episode if they exist
292
- fileObj.season = s;
293
- fileObj.episode = e;
294
- }
295
- sources.push(fileObj);
296
- i = j; break;
297
- }
298
- if (nextLineTrimmed.startsWith('#EXT')) break;
 
299
  }
300
- currentQuality = undefined;
 
 
 
 
 
 
 
 
301
  }
 
 
 
 
302
  }
303
- return sources;
304
- } catch (error) { console.error('AutoEmbed: Error parsing m3u8:', error); return []; }
 
 
 
305
  }
306
 
307
  function mapAutoEmbedSubtitles(apiSubtitles: any[]): Subtitle[] {
308
- if (!apiSubtitles || !Array.isArray(apiSubtitles)) return [];
309
- try {
310
- return apiSubtitles.map(subtitle => {
311
- const lang = (subtitle.label || 'unknown').split(' ')[0].toLowerCase();
312
- const fileUrl = subtitle.file || '';
313
- if (!fileUrl) return null;
314
- const fileExtension = fileUrl.split('.').pop()?.toLowerCase();
315
- const type = fileExtension === 'vtt' ? 'vtt' : (fileExtension === 'srt' ? 'srt' : undefined);
316
- return { url: fileUrl, lang: lang, type: type };
317
- }).filter((sub): sub is Subtitle => sub !== null && !!sub.url);
318
- } catch (error) { console.error('AutoEmbed: Error mapping subtitles:', error); return []; }
319
  }
320
 
321
  async function getAutoEmbed(tmdb_id: string, s?: string, e?: string): Promise<ProviderFunctionReturn> {
322
- const providerName = PROVIDERS.autoembed.displayName;
323
- const params = new URLSearchParams();
324
- if (s && e) { params.append("type", "tv"); params.append("id", `${tmdb_id}/${s}/${e}`); }
325
- else { params.append("type", "movie"); params.append("id", tmdb_id); }
326
- const apiUrl = `${AUTOEMBED_API_URL_BASE}?${params.toString()}`;
327
-
328
- try {
329
- const response = await fetch(apiUrl, { headers: { 'Referer': AUTOEMBED_DOMAIN, 'User-Agent': USER_AGENT } });
330
- if (!response.ok) {
331
- let errorBody = ""; try { errorBody = await response.text(); } catch (_) {}
332
- return createProviderErrorObject(providerName, `API request failed: HTTP ${response.status}. ${errorBody}`);
333
- }
334
- const data = await response.json();
335
- if (data.error || !data.videoSource) return createProviderErrorObject(providerName, data.error || "No videoSource found in API response");
336
-
337
- const m3u8Url = data.videoSource;
338
- const m3u8Response = await fetch(m3u8Url, { headers: { 'Referer': AUTOEMBED_DOMAIN, 'User-Agent': USER_AGENT } });
339
- if (!m3u8Response.ok) return createProviderErrorObject(providerName, `Failed to fetch m3u8 from ${m3u8Url}: HTTP ${m3u8Response.status}`);
340
-
341
- const m3u8Content = await m3u8Response.text();
342
- // Pass s and e to the M3U8 parser
343
- const files = parseAutoEmbedM3U8(m3u8Content, s, e, m3u8Url);
344
- if (files.length === 0) return createProviderErrorObject(providerName, "No valid streams found after parsing m3u8");
345
-
346
- const subtitles = data.subtitles ? mapAutoEmbedSubtitles(data.subtitles) : [];
347
- return { source: { provider: providerName, files: files, subtitles: subtitles, headers: { "Referer": AUTOEMBED_DOMAIN, "User-Agent": USER_AGENT, "Origin": AUTOEMBED_DOMAIN } } };
348
- } catch (error) {
349
- console.error(`${providerName}: Unexpected error - `, error);
350
- return createProviderErrorObject(providerName, `Network or processing error: ${error.message}`);
351
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
352
  }
353
 
354
  // --- Provider Configuration ---
 
65
  };
66
  }
67
  function createProviderErrorObject(providerName: string, errorMessage: string): ProviderErrorResult {
68
+ return createApiErrorObject(errorMessage, providerName);
69
  }
70
 
71
  function stringAtob(input: string): string {
 
105
  // --- Provider Specific Logic ---
106
 
107
  // == EmbedSu Provider ==
108
+ const EMBED_SU_DOMAIN = "https://embed.su";
109
+ const EMBED_SU_PROXY = "https://iqslgbok.deploy.cx/param/User-Agent=" + encodeURIComponent(USER_AGENT) + "/param/Origin=" + encodeURIComponent(EMBED_SU_DOMAIN) + "/param/Referer=" + encodeURIComponent(EMBED_SU_DOMAIN) + "/";
110
+
111
  const EMBED_SU_HEADERS = {
112
  'User-Agent': USER_AGENT,
113
+ 'Referer': EMBED_SU_DOMAIN,//EMBED_SU_DOMAIN
114
+ 'Origin': EMBED_SU_DOMAIN,//EMBED_SU_DOMAIN
115
  };
116
 
117
  async function getEmbedSu(tmdb_id: string, s?: string, e?: string): Promise<ProviderFunctionReturn> {
118
  const providerName = PROVIDERS.embedsu.displayName;
119
  try {
120
+ const urlSearch = s && e ? `${EMBED_SU_PROXY}${EMBED_SU_DOMAIN}/embed/tv/${tmdb_id}/${s}/${e}` : `${EMBED_SU_PROXY}${EMBED_SU_DOMAIN}/embed/movie/${tmdb_id}`;
121
  const htmlSearchResponse = await fetch(urlSearch, { method: 'GET', headers: EMBED_SU_HEADERS });
122
  if (!htmlSearchResponse.ok) return createProviderErrorObject(providerName, `Failed to fetch initial page: HTTP ${htmlSearchResponse.status}`);
123
 
 
148
  for (const item of secondDecode) {
149
  try {
150
  if (!item || !item.hash) continue;
151
+ const urlDirect = `${EMBED_SU_PROXY}${EMBED_SU_DOMAIN}/api/e/${item.hash}`;
152
  const dataDirect = await requestGet(urlDirect, { "Referer": EMBED_SU_DOMAIN, "User-Agent": USER_AGENT, "Origin": EMBED_SU_DOMAIN });
153
  if (!dataDirect || !dataDirect.source) { console.warn(`${providerName}: No source found for hash ${item.hash}`); continue; }
154
 
 
156
  url: sub.file, lang: sub.label ? sub.label.split('-')[0].trim().toLowerCase() : 'en'
157
  })).filter((track: Subtitle) => track.url);
158
 
159
+ const requestDirectSize = await fetch(EMBED_SU_PROXY + dataDirect.source, { headers: EMBED_SU_HEADERS, method: "GET" });
160
  if (!requestDirectSize.ok) { console.warn(`${providerName}: Failed to fetch source ${dataDirect.source}: HTTP ${requestDirectSize.status}`); continue; }
161
 
162
  const parseRequest = await requestDirectSize.text();
 
165
  const directQuality: MediaFile[] = patternSize.map(patternItem => {
166
  try {
167
  const sizeQuality = getSizeQuality(patternItem);
168
+ let dURL = `${EMBED_SU_PROXY}${EMBED_SU_DOMAIN}${patternItem}`;
169
  dURL = dURL.replace(".png", ".m3u8");
170
  const fileObj: MediaFile = { file: dURL, type: 'hls', quality: `${sizeQuality}p`, lang: 'en' };
171
  if (s && e) { // Add season and episode if they exist (TV show)
 
178
 
179
  if (!directQuality.length) { console.warn(`${providerName}: No valid qualities found for source ${dataDirect.source}`); continue; }
180
 
181
+ return { source: { provider: providerName, files: directQuality, subtitles: tracks, headers: { "Referer": EMBED_SU_DOMAIN, "User-Agent": USER_AGENT, "Origin": EMBED_SU_DOMAIN } } };
182
  } catch (error) { console.error(`${providerName}: Error processing item ${item.hash}:`, error); }
183
  }
184
  return createProviderErrorObject(providerName, "No valid sources found after processing all available items");
 
189
  }
190
 
191
  // == VidSrc.SU Provider ==
192
+ const VIDSRC_SU_DOMAIN = "https://vidsrc.su/";
193
+ const VIDSRC_SU_HEADERS = { 'User-Agent': USER_AGENT, 'Referer': VIDSRC_SU_DOMAIN, 'Origin': VIDSRC_SU_DOMAIN };
 
194
 
195
  async function getVidSrcSu(tmdb_id: string, s?: string, e?: string): Promise<ProviderFunctionReturn> {
196
  const providerName = PROVIDERS.vidsrcsu.displayName;
197
  const embedUrl = s && e ? `${VIDSRC_SU_DOMAIN}embed/tv/${tmdb_id}/${s}/${e}` : `${VIDSRC_SU_DOMAIN}embed/movie/${tmdb_id}`;
198
+
199
  try {
200
  const response = await fetch(embedUrl, { headers: VIDSRC_SU_HEADERS });
201
  if (!response.ok) return createProviderErrorObject(providerName, `Failed to fetch embed page: HTTP ${response.status}`);
202
+
203
  const html = await response.text();
204
  let subtitles: Subtitle[] = [];
205
+
206
  const servers: MediaFile[] = [...html.matchAll(/label: 'Server (?:[^']*)', url: '(https?:\/\/[^']+\.m3u8[^']*)'/gi)].map(match => {
207
  const fileObj: MediaFile = { file: match[1], type: "hls", lang: "en" };
208
  if (s && e) { // Add season and episode if they exist
 
226
  }
227
 
228
  if (servers.length === 0) return createProviderErrorObject(providerName, "No valid video streams found in embed page");
229
+ return { source: { provider: providerName, files: servers, subtitles: subtitles, headers: { "Referer": VIDSRC_SU_DOMAIN, "User-Agent": USER_AGENT, "Origin": VIDSRC_SU_DOMAIN } } };
230
  } catch (error) {
231
  console.error(`${providerName}: Unexpected error:`, error);
232
  return createProviderErrorObject(providerName, `Unexpected error: ${error.message}`);
 
239
 
240
  // Modified parseAutoEmbedM3U8 to accept s and e
241
  function parseAutoEmbedM3U8(m3u8Content: string, s?: string, e?: string, m3u8Url?: string): MediaFile[] {
242
+ try {
243
+ const lines = m3u8Content.split('\n');
244
+ const sources: MediaFile[] = [];
245
+ let currentQuality: string | undefined = undefined;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
246
 
247
+ let baseUrl = '';
248
+ let domain = '';
249
+ if (m3u8Url) {
250
+ try {
251
+ const urlObj = new URL(m3u8Url);
252
+ domain = `${urlObj.protocol}//${urlObj.hostname}`;
253
+
254
+ // Get base URL by removing the filename from the full URL
255
+ const urlPath = m3u8Url.split('/');
256
+ urlPath.pop(); // Remove the filename
257
+ baseUrl = urlPath.join('/') + '/';
258
+ } catch (error) {
259
+ console.error('AutoEmbed: Error extracting URL information:', error);
260
+ }
261
+ }
262
+
263
+ for (let i = 0; i < lines.length; i++) {
264
+ const trimmedLine = lines[i].trim();
265
+ if (trimmedLine.startsWith('#EXT-X-STREAM-INF:')) {
266
+ const resolutionMatch = trimmedLine.match(/RESOLUTION=\d+x(\d+)/);
267
+ currentQuality = resolutionMatch && resolutionMatch[1] ? `${resolutionMatch[1]}p` : undefined;
268
+ for (let j = i + 1; j < lines.length; j++) {
269
+ const nextLineTrimmed = lines[j].trim();
270
+ if (nextLineTrimmed && !nextLineTrimmed.startsWith('#')) {
271
+ // Handle different types of paths
272
+ let filePath = nextLineTrimmed;
273
+
274
+ // Skip URLs that already have protocol
275
+ if (!nextLineTrimmed.match(/^https?:\/\//)) {
276
+ if (nextLineTrimmed.startsWith('/')) {
277
+ // Absolute path starting with '/'
278
+ if (domain) {
279
+ filePath = `${domain}${nextLineTrimmed}`;
280
+ }
281
+ } else {
282
+ // Relative path
283
+ if (baseUrl) {
284
+ filePath = `${baseUrl}${nextLineTrimmed}`;
285
  }
286
+ }
287
+ }
288
+ const fileObj: MediaFile = {
289
+ file: filePath, type: "hls",
290
+ quality: currentQuality || 'unknown', lang: "en"
291
+ };
292
+ if (s && e) { // Add season and episode if they exist
293
+ fileObj.season = s;
294
+ fileObj.episode = e;
295
  }
296
+ sources.push(fileObj);
297
+ i = j; break;
298
+ }
299
+ if (nextLineTrimmed.startsWith('#EXT')) break;
300
  }
301
+ currentQuality = undefined;
302
+ }
303
+ }
304
+ return sources;
305
+ } catch (error) { console.error('AutoEmbed: Error parsing m3u8:', error); return []; }
306
  }
307
 
308
  function mapAutoEmbedSubtitles(apiSubtitles: any[]): Subtitle[] {
309
+ if (!apiSubtitles || !Array.isArray(apiSubtitles)) return [];
310
+ try {
311
+ return apiSubtitles.map(subtitle => {
312
+ const lang = (subtitle.label || 'unknown').split(' ')[0].toLowerCase();
313
+ const fileUrl = subtitle.file || '';
314
+ if (!fileUrl) return null;
315
+ const fileExtension = fileUrl.split('.').pop()?.toLowerCase();
316
+ const type = fileExtension === 'vtt' ? 'vtt' : (fileExtension === 'srt' ? 'srt' : undefined);
317
+ return { url: fileUrl, lang: lang, type: type };
318
+ }).filter((sub): sub is Subtitle => sub !== null && !!sub.url);
319
+ } catch (error) { console.error('AutoEmbed: Error mapping subtitles:', error); return []; }
320
  }
321
 
322
  async function getAutoEmbed(tmdb_id: string, s?: string, e?: string): Promise<ProviderFunctionReturn> {
323
+ const providerName = PROVIDERS.autoembed.displayName;
324
+ const params = new URLSearchParams();
325
+ if (s && e) { params.append("type", "tv"); params.append("id", `${tmdb_id}/${s}/${e}`); }
326
+ else { params.append("type", "movie"); params.append("id", tmdb_id); }
327
+ const apiUrl = `${AUTOEMBED_API_URL_BASE}?${params.toString()}`;
328
+
329
+ try {
330
+ const response = await fetch(apiUrl, { headers: { 'Referer': AUTOEMBED_DOMAIN, 'User-Agent': USER_AGENT } });
331
+ if (!response.ok) {
332
+ let errorBody = ""; try { errorBody = await response.text(); } catch (_) { }
333
+ return createProviderErrorObject(providerName, `API request failed: HTTP ${response.status}. ${errorBody}`);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
334
  }
335
+ const data = await response.json();
336
+ if (data.error || !data.videoSource) return createProviderErrorObject(providerName, data.error || "No videoSource found in API response");
337
+
338
+ const m3u8Url = data.videoSource;
339
+ const m3u8Response = await fetch(m3u8Url, { headers: { 'Referer': AUTOEMBED_DOMAIN, 'User-Agent': USER_AGENT } });
340
+ if (!m3u8Response.ok) return createProviderErrorObject(providerName, `Failed to fetch m3u8 from ${m3u8Url}: HTTP ${m3u8Response.status}`);
341
+
342
+ const m3u8Content = await m3u8Response.text();
343
+ // Pass s and e to the M3U8 parser
344
+ const files = parseAutoEmbedM3U8(m3u8Content, s, e, m3u8Url);
345
+ if (files.length === 0) return createProviderErrorObject(providerName, "No valid streams found after parsing m3u8");
346
+
347
+ const subtitles = data.subtitles ? mapAutoEmbedSubtitles(data.subtitles) : [];
348
+ return { source: { provider: providerName, files: files, subtitles: subtitles, headers: { "Referer": AUTOEMBED_DOMAIN, "User-Agent": USER_AGENT, "Origin": AUTOEMBED_DOMAIN } } };
349
+ } catch (error) {
350
+ console.error(`${providerName}: Unexpected error - `, error);
351
+ return createProviderErrorObject(providerName, `Network or processing error: ${error.message}`);
352
+ }
353
  }
354
 
355
  // --- Provider Configuration ---