Spaces:
Paused
Paused
const axios = require('axios').default; | |
const { handleChannelRequest, getEpgInfoBatch } = require('./tvGuide'); | |
axios.defaults.headers.get["content-type"] = "application/json"; | |
axios.defaults.timeout = 100000 | |
axios.defaults.method = "GET" | |
function getUserData(userConf) { | |
let retrievedData, url, obj = {} | |
try { | |
retrievedData = JSON.parse(Buffer.from(userConf, 'base64').toString()) | |
} catch (error) { | |
return "error while parsing url" | |
} | |
let domainName, baseURL, idPrefix | |
if(typeof retrievedData === "object"){ | |
domainName = retrievedData.BaseURL.split("/")[2].split(":")[0] || "unknown" | |
baseURL = retrievedData.BaseURL | |
idPrefix = domainName.charAt(0) + domainName.substr(Math.ceil(domainName.length / 2 - 1), domainName.length % 2 === 0 ? 2 : 1) + domainName.charAt(domainName.length - 1) + ":"; | |
obj = { | |
baseURL, | |
domainName, | |
idPrefix, | |
username: retrievedData.username, | |
password: retrievedData.password, | |
timezone: retrievedData.timezone || 'UTC' | |
} | |
} else if(retrievedData.includes("http")){ | |
url = retrievedData | |
const queryString = url.split('?')[1] || "unknown" | |
baseURL = url.split('/')[0] + "//" + url.split('?')[0].split('/')[2] || "unknown" | |
domainName = url.split("?")[0].split("/")[2].split(":")[0] || "unknown" | |
idPrefix = domainName.charAt(0) + domainName.substr(Math.ceil(domainName.length / 2 - 1), domainName.length % 2 === 0 ? 2 : 1) + domainName.charAt(domainName.length - 1) + ":"; | |
if(queryString === undefined){return {result:"URL does not have any queries!"}} | |
if(baseURL === undefined){return {result:"URL does not seem like an url!"}} | |
obj.baseURL = baseURL | |
obj.domainName = domainName | |
obj.idPrefix = idPrefix | |
const urlParams = new URLSearchParams(queryString); | |
const entries = urlParams.entries(); | |
for(const entry of entries) { | |
obj[entry[0]] = entry[1] | |
} | |
obj.timezone = obj.timezone || 'UTC'; | |
} | |
if(obj.username && obj.password && obj.baseURL){ | |
return obj | |
}else{ | |
return {} | |
} | |
} | |
async function getManifest(url) { | |
const obj = getUserData(url) | |
let vod | |
try { | |
vod = await axios({url:`${obj.baseURL}/player_api.php?username=${obj.username}&password=${obj.password}&action=get_vod_categories`}) | |
} catch (error) { | |
return {error} | |
} | |
const vodJSON = vod.data | |
let movieCatalog = [] | |
if (vod.status === 200){ | |
vodJSON.forEach(i => { | |
let name = i.category_name | |
movieCatalog.push(name) | |
}); | |
} | |
let series | |
try { | |
series = await axios({url:`${obj.baseURL}/player_api.php?username=${obj.username}&password=${obj.password}&action=get_series_categories`}) | |
} catch (error) { | |
return {error} | |
} | |
const seriesJSON = series.data | |
let seriesCatalog = [] | |
if(series.status === 200){ | |
seriesJSON.forEach(i => { | |
let name = i.category_name | |
seriesCatalog.push(name) | |
}); | |
} | |
let live | |
try { | |
live = await axios({url:`${obj.baseURL}/player_api.php?username=${obj.username}&password=${obj.password}&action=get_live_categories`}) | |
} catch (error) { | |
return {error} | |
} | |
const liveJSON = live.data | |
let liveCatalog = [] | |
if(series.status === 200){ | |
liveJSON.forEach(i => { | |
let name = i.category_name | |
liveCatalog.push(name) | |
}); | |
} | |
const manifest = { | |
id:`org.community.${obj.domainName}` || "org.community.youriptv", | |
version:"2.0.0", | |
name:obj.domainName + " IPTV" || "Your IPTV", | |
description:`You will access to your ${obj.domainName} IPTV with this addon!`, | |
idPrefixes:[obj.idPrefix, "tt"], | |
catalogs:[ | |
{ | |
id:`${obj.idPrefix}movie`, | |
name: obj.domainName || "Your IPTV", | |
type:"movie", | |
extra:[{name:"genre",options:movieCatalog,isRequired:true}], | |
isRequired: true | |
}, | |
{ | |
id:`${obj.idPrefix}series`, | |
name:obj.domainName || "Your IPTV", | |
type:"series", | |
extra:[{name:"genre",options:seriesCatalog,isRequired:true}], | |
isRequired: true | |
}, | |
{ | |
id:`${obj.idPrefix}tv`, | |
name: obj.domainName || "Your IPTV", | |
type:"tv", | |
extra:[{name:"genre",options:liveCatalog,isRequired:true}], | |
} | |
], | |
resources:["catalog","meta","stream"], | |
types:["movie","series","tv"], | |
behaviorHints:{ | |
configurable: true, | |
configurationRequired: false, | |
epgSupported: true | |
}, | |
} | |
return manifest | |
} | |
function getValidUrl(url) { | |
try { | |
const urlObj = new URL(url); | |
return urlObj.protocol.startsWith('http') ? url : ''; | |
} catch { | |
return ''; | |
} | |
} | |
async function getCatalog(url,type,genre) { | |
const obj = getUserData(url) | |
let getCategoryID | |
try { | |
if(type === "movie"){ | |
getCategoryID = await axios({url:`${obj.baseURL}/player_api.php?username=${obj.username}&password=${obj.password}&action=get_vod_categories`}) | |
} | |
else if(type ==="series"){ | |
getCategoryID = await axios({url:`${obj.baseURL}/player_api.php?username=${obj.username}&password=${obj.password}&action=get_series_categories`}) | |
} | |
else if(type ==="tv"){ | |
getCategoryID = await axios({url:`${obj.baseURL}/player_api.php?username=${obj.username}&password=${obj.password}&action=get_live_categories`}) | |
} | |
}catch (error) { | |
return [] | |
} | |
let catID | |
getCategoryID.data.forEach(i => { | |
if(i.category_name === genre){ | |
catID = i.category_id | |
} | |
}); | |
let action = type === "movie" ? "get_vod_streams" : type === "series" ? "get_series": type ==="tv" ? "get_live_streams" : "error" | |
let paramsCat = { | |
username: obj.username, | |
password: obj.password, | |
action, | |
category_id: catID | |
} | |
let getCatalogs | |
try { | |
getCatalogs = await axios({url:`${obj.baseURL}/player_api.php`,method:"GET",params:paramsCat}) | |
} catch (error) { | |
return [] | |
} | |
let metas = [] | |
if (type === "tv") { | |
const channelIds = getCatalogs.data | |
.filter(i => i.epg_channel_id) | |
.map(i => ({ | |
stream_id: i.stream_id, | |
epg_channel_id: i.epg_channel_id | |
})); | |
let epgInfo; | |
try { | |
epgInfo = await getEpgInfoBatch(channelIds, obj.baseURL, obj.username, obj.password, obj.timezone); | |
} catch (error) { | |
console.error('Error fetching EPG info:', error); | |
} | |
getCatalogs.data.forEach(i => { | |
let id = obj.idPrefix + i.stream_id || ""; | |
let name = i.name; | |
let poster = getValidUrl(i.stream_icon); | |
let posterShape = "square"; | |
let meta = { id, type, name, poster, posterShape }; | |
if (epgInfo && epgInfo[i.stream_id]) { | |
const currentProgram = epgInfo[i.stream_id].currentProgram; | |
const nextProgram = epgInfo[i.stream_id].nextProgram; | |
let description = ''; | |
if (currentProgram) { | |
description += `Now: ${currentProgram.title}\n${currentProgram.start} - ${currentProgram.description}\n`; | |
} | |
if (nextProgram) { | |
description += `\nNext: ${nextProgram.title}\n${nextProgram.start} - ${nextProgram.description}`; | |
} | |
meta.description = description; | |
} else { | |
meta.description = "No program information available"; | |
} | |
metas.push(meta); | |
}); | |
} else { | |
getCatalogs.data.forEach(i => { | |
let id, name = i.name, poster, posterShape, imdbRating | |
if(type === "series"){ | |
id = obj.idPrefix + i.series_id || "" | |
poster = getValidUrl(i.cover) | |
imdbRating = i.rating || "" | |
posterShape = "poster" | |
}else if(type === "movie"){ | |
id = obj.idPrefix + i.stream_id || "" | |
poster = getValidUrl(i.stream_icon) | |
imdbRating = i.rating || "" | |
posterShape = "poster" | |
} | |
metas.push({id,type,name,poster,posterShape,imdbRating}) | |
}); | |
} | |
return metas | |
} | |
async function getMeta(url,type,id) { | |
const streamID = id.split(":")[1] | |
const obj = getUserData(url) | |
let action = type === "movie" ? "get_vod_info" : type === "series" ? "get_series_info": type ==="tv" ? "get_live_streams" : "error" | |
let requestID = type === "movie" ? "vod_id" : type === "series" ? "series_id": type ==="tv" ? "stream_id" : "error" | |
let params = { | |
username: obj.username, | |
password: obj.password, | |
action, | |
[requestID]:streamID | |
} | |
if(type === "tv"){ | |
delete params[requestID] | |
} | |
let getMeta | |
try { | |
getMeta = await axios({url:`${obj.baseURL}/player_api.php`,params}) | |
} catch (error) { | |
console.error('Error fetching metadata:', error); | |
return {} | |
} | |
let meta = {} | |
if(type === "movie"|| type === "series"){ | |
if (!getMeta.data || !getMeta.data.info) { | |
console.error('Unexpected response structure:', getMeta.data); | |
return {}; | |
} | |
meta = { | |
id: obj.idPrefix + streamID || "", | |
type, | |
name: getMeta.data.info.name || "", | |
poster: getMeta.data.info.cover_big || "", | |
background: (getMeta.data.info.backdrop_path && getMeta.data.info.backdrop_path[0]) || "https://www.stremio.com/website/wallpapers/stremio-wallpaper-5.jpg", | |
description: getMeta.data.info.description || "", | |
releaseInfo: getMeta.data.info.releaseDate || getMeta.data.info.releasedate || "" | |
} | |
if (meta.releaseInfo) { | |
meta.releaseInfo = String(meta.releaseInfo.split("-")[0]); | |
} | |
} | |
if(type === "series"){ | |
let videos = [] | |
const seasons = Object.keys(getMeta.data.episodes || {}) | |
seasons.forEach(season => { | |
(getMeta.data.episodes[season] || []).forEach(episode => { | |
let id = obj.idPrefix + episode.id || "" | |
let title = episode.title || "" | |
let season = episode.season || null | |
let episodeNo = episode.episode_num || null | |
let overview = episode.plot || "" | |
let thumbnail = episode.info?.movie_image || null | |
let released = episode.info?.releasedate ? new Date(episode.info.releasedate) : null | |
let container_extension = episode.container_extension || "mp4" | |
let streams = [{ | |
name:obj.domainName, | |
description: title, | |
url: `${obj.baseURL}/series/${obj.username}/${obj.password}/${episode.id}.${container_extension}`, | |
}] | |
videos.push({id,title,season,episode:episodeNo,overview,thumbnail,released,streams}) | |
}); | |
}); | |
meta.name = getMeta.data.info?.name || "", | |
meta.videos = videos | |
}else if(type === "movie"){ | |
let imdbRating = getMeta.data.info?.rating || "" | |
meta.imdbRating = imdbRating | |
}else if(type === "tv"){ | |
let metaTV = []; | |
const channelInfo = getMeta.data.find(i => Number(i.stream_id) === Number(streamID)); | |
if (channelInfo) { | |
let id = obj.idPrefix + channelInfo.stream_id; | |
let name = channelInfo.name || ""; | |
let background = "https://www.stremio.com/website/wallpapers/stremio-wallpaper-5.jpg"; | |
let logo = channelInfo.stream_icon || null; | |
try { | |
const epgInfo = await handleChannelRequest(channelInfo.epg_channel_id, obj.baseURL, obj.username, obj.password, obj.timezone); | |
if (epgInfo && epgInfo.currentProgram) { | |
let description = `Now: ${epgInfo.currentProgram.title}\n${epgInfo.currentProgram.start} - ${epgInfo.currentProgram.stop}\n${epgInfo.currentProgram.description || ''}\n\n`; | |
if (epgInfo.nextProgram) { | |
description += `Next: ${epgInfo.nextProgram.title}\n${epgInfo.nextProgram.start} - ${epgInfo.nextProgram.stop}\n${epgInfo.nextProgram.description || ''}`; | |
} | |
metaTV.push({ | |
id, | |
name, | |
type, | |
background, | |
logo, | |
poster: logo, | |
posterShape: "square", | |
description: description, | |
genres: ["Live TV"] | |
}); | |
} else { | |
metaTV.push({ | |
id, | |
name, | |
type, | |
background, | |
logo, | |
poster: logo, | |
posterShape: "square", | |
description: "No program information available", | |
genres: ["Live TV"] | |
}); | |
} | |
} catch (error) { | |
console.error('Error fetching EPG info:', error); | |
metaTV.push({ | |
id, | |
name, | |
type, | |
background, | |
logo, | |
poster: logo, | |
posterShape: "square", | |
description: "Error fetching program information", | |
genres: ["Live TV"] | |
}); | |
} | |
} | |
return metaTV[0] || {}; | |
} | |
return meta | |
} | |
module.exports = {getUserData, getManifest, getCatalog, getMeta} | |