xtremio-plus / addon.js
mikmc's picture
Update addon.js
8dd1a34 verified
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}