Spaces:
Paused
Paused
const express = require("express"); | |
const path = require("path"); // Import path module for path handling | |
const app = express(); | |
const config = require('./config'); | |
const MANIFEST = require('./manifest'); | |
const { getManifest, getCatalog, getMeta, getUserData } = require("./addon"); | |
const { createXtreamModule } = require("./xtream-module"); | |
const NodeCache = require("node-cache"); | |
const myCache = new NodeCache({ stdTTL: 200 }); | |
// Middleware to allow CORS and set response headers | |
var respond = function (res, data) { | |
res.setHeader('Access-Control-Allow-Origin', '*'); | |
res.setHeader('Access-Control-Allow-Headers', '*'); | |
res.setHeader('Content-Type', 'application/json'); | |
res.send(data); | |
}; | |
// Serve static files from the 'assets' directory | |
app.use('/assets', express.static(path.join(__dirname, 'assets'))); | |
app.engine('html', require('ejs').renderFile); | |
app.set('views', path.join(__dirname, 'views')); | |
app.get("/", function (req, res) { | |
res.redirect("/configure"); | |
}); | |
app.get("/:userConf?/configure", function (req, res) { | |
const newManifest = { ...{ MANIFEST } }; | |
res.render('configure.html', newManifest); | |
}); | |
app.get('/manifest.json', function (req, res) { | |
const newManifest = { ...MANIFEST }; | |
newManifest.behaviorHints.configurationRequired = true; | |
respond(res, newManifest); | |
}); | |
app.get('/:userConf/manifest.json', async function (req, res) { | |
let newManifest = { ...MANIFEST }; | |
if (!((req || {}).params || {}).userConf) { | |
newManifest.behaviorHints.configurationRequired = true; | |
respond(res, newManifest); | |
} else { | |
try { | |
if (myCache.has(`manifest-${req.params.userConf}`)) { | |
respond(res, myCache.get(`manifest-${req.params.userConf}`)); | |
} else { | |
newManifest = await getManifest(req.params.userConf); | |
if (newManifest && newManifest.id) { | |
myCache.set(`manifest-${req.params.userConf}`, newManifest); | |
} | |
respond(res, newManifest); | |
} | |
} catch (error) { | |
console.log(error); | |
respond(res, { error: error }); | |
} | |
} | |
}); | |
app.get('/:userConf/catalog/:type/:id/:extra?.json', async function (req, res) { | |
let { userConf, type, id, extra } = req.params; | |
let extraObj; | |
if (extra) { | |
try { | |
extraObj = JSON.parse('{"' + decodeURI(extra.replace(/&/g, '","').replace(/=/g, '":"')) + '"}'); | |
} catch (error) { | |
console.log(error); | |
return respond(res, { metas: [] }); | |
} | |
} | |
if (extraObj && extraObj.genre && extraObj.genre.includes("+")) { | |
extraObj.genre = extraObj.genre.replace(/\+/g, ' '); | |
} | |
let metas = []; | |
try { | |
if (myCache.has(`catalog-${userConf}-${type}-${id}-${extra}`)) { | |
respond(res, myCache.get(`catalog-${userConf}-${type}-${id}-${extra}`)); | |
} else { | |
metas = await getCatalog(userConf, type, extraObj.genre); | |
if (metas.length > 0) { | |
myCache.set(`catalog-${userConf}-${type}-${id}-${extra}`, { metas: metas }); | |
} | |
respond(res, { metas: metas }); | |
} | |
} catch (error) { | |
console.log(error); | |
respond(res, { metas: [] }); | |
} | |
}); | |
app.get('/:userConf/meta/:type/:id.json', async function (req, res) { | |
let { userConf, type, id } = req.params; | |
try { | |
if (myCache.has(`meta-${userConf}-${type}-${id}`)) { | |
respond(res, myCache.get(`meta-${userConf}-${type}-${id}`)); | |
} else { | |
const meta = await getMeta(userConf, type, id); | |
if (meta && meta.id) { | |
myCache.set(`meta-${userConf}-${type}-${id}`, { meta: meta }); | |
} | |
respond(res, { meta: meta }); | |
} | |
} catch (error) { | |
console.log(error); | |
respond(res, { error }); | |
} | |
}); | |
app.get('/:userConf/stream/:type/:id.json', async function (req, res) { | |
let { userConf, type, id } = req.params; | |
const obj = getUserData(userConf); | |
if (id.startsWith('tt')) { | |
// Handle IMDb IDs using the Xtream module | |
const xtreamModule = createXtreamModule(obj.baseURL, obj.username, obj.password); | |
try { | |
const xtreamResponse = await xtreamModule(id); | |
if (xtreamResponse && xtreamResponse.contentUrl) { | |
console.log('Stream URL for IMDb ID:', xtreamResponse.contentUrl); | |
respond(res, { streams: [{ url: xtreamResponse.contentUrl, name: xtreamResponse.title }] }); | |
} else { | |
console.log('No stream found for IMDb ID:', id); | |
respond(res, { streams: [] }); | |
} | |
} catch (error) { | |
console.error('Xtream module error:', error); | |
respond(res, { streams: [] }); | |
} | |
} else { | |
// Existing logic for non-IMDb IDs | |
let extension = "mp4"; | |
const streamID = id.split(":")[1]; | |
let stream = []; | |
if (type === "tv") { | |
type = "live"; | |
extension = "ts"; | |
const url = `${obj.baseURL}/${type}/${obj.username}/${obj.password}/${streamID}.${extension}`; | |
console.log('Stream URL for TV:', url); | |
stream = [{ | |
url: url, | |
name: "Watch Now", | |
behaviorHints: { | |
notWebReady: true | |
} | |
}]; | |
} else { | |
const url = `${obj.baseURL}/${type}/${obj.username}/${obj.password}/${streamID}.${extension}`; | |
console.log('Stream URL for VOD:', url); | |
stream = [{ | |
url: url, | |
name: "Watch Now" | |
}]; | |
} | |
respond(res, { streams: stream }); | |
} | |
}); | |
// Listen for incoming requests | |
// Listen for incoming requests | |
if (module.parent) { | |
module.exports = app; | |
} else { | |
// Bind to 0.0.0.0 and use the configured port | |
app.listen(config.port, '0.0.0.0', function () { | |
console.log(`Server started: ${config.addon}`); | |
console.log(`Listening on: ${config.local} (${config.port})`); | |
}); | |
} | |