Update 3.1.1

This commit is contained in:
mrcanelas
2025-01-30 18:05:46 -03:00
parent 22c93d7b58
commit a3477d6d69
9 changed files with 332 additions and 727 deletions
+2 -635
View File
@@ -1,645 +1,12 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>The Movie Database Addon - Stremio Addon</title>
<style>
* {
box-sizing: border-box;
}
body,
html {
margin: 0;
padding: 0;
width: 100%;
min-height: 100%;
}
body {
padding: 2vh;
font-size: 2.2vh;
}
html {
background-size: cover;
background-position: center;
background-repeat: no-repeat;
box-shadow: inset 0 0 0 2000px rgba(0, 0, 0, 0.6);
}
body {
display: flex;
flex-direction: column;
align-items: center;
font-family: "Open Sans", Arial, sans-serif;
color: white;
}
h1 {
font-size: 4.5vh;
font-weight: 700;
}
h2 {
font-size: 2.2vh;
font-weight: normal;
font-style: italic;
opacity: 0.8;
}
h3 {
font-size: 2.2vh;
}
p {
font-size: 1.75vh;
}
ul {
font-size: 1.75vh;
margin: 0;
margin-top: 1vh;
padding-left: 3vh;
}
a {
color: white;
}
a.install-link {
text-decoration: none;
}
button {
border: 0;
outline: 0;
color: white;
background: #8a5aab;
padding: 1.2vh 3.5vh;
margin: 2vh auto;
text-align: center;
font-size: 2.2vh;
font-weight: 600;
cursor: pointer;
display: block;
box-shadow: 0 0.5vh 1vh rgba(0, 0, 0, 0.2);
transition: box-shadow 0.1s ease-in-out;
}
button:hover {
box-shadow: none;
}
button:active {
box-shadow: 0 0 0 0.5vh white inset;
}
#addon {
width: 100%;
max-width: 50vh;
padding: 0 2vh;
margin: auto;
}
.logo {
height: 14vh;
width: 14vh;
margin: auto;
margin-bottom: 3vh;
}
.logo img {
width: 100%;
}
.contact {
position: relative;
bottom: 2vh;
width: 100%;
text-align: center;
}
.contact a {
font-size: 1.4vh;
font-style: italic;
}
.separator {
margin-bottom: 4vh;
}
.separator-small {
margin-bottom: 2vh;
}
.form-element {
margin-bottom: 2vh;
}
.label-to-top {
margin-bottom: 2vh;
}
.label-to-right {
margin-left: 1vh !important;
}
.full-width {
width: 100%;
height: 5vh;
padding: 0.5vh 0.5vh;
margin: auto;
border: 0.5vh solid #8a5aab;
font-size: 2.2vh;
cursor: pointer;
display: block;
box-shadow: 0 0.5vh 1vh rgba(0, 0, 0, 0.2);
transition: box-shadow 0.1s ease-in-out;
color: #8a5aab;
}
#language-select {
width: 100%;
height: 5vh;
padding: 0.5vh 0.5vh;
margin: auto;
border: 0.5vh solid #8a5aab;
font-size: 2.2vh;
cursor: pointer;
display: block;
box-shadow: 0 0.5vh 1vh rgba(0, 0, 0, 0.2);
transition: box-shadow 0.1s ease-in-out;
color: #8a5aab;
}
@media (max-width: 768px) {
h1 {
font-size: 4vh;
}
h2 {
font-size: 2vh;
}
button {
padding: 1vh 2.5vh;
font-size: 2vh;
}
#addon {
width: 90%;
}
}
@media (max-width: 480px) {
h1 {
font-size: 3.5vh;
}
h2 {
font-size: 1.8vh;
}
button {
padding: 1vh 2vh;
font-size: 1.8vh;
}
.logo {
height: 12vh;
width: 12vh;
}
#addon {
width: 100%;
padding: 0 1vh;
}
}
</style>
<link rel="shortcut icon" href="https://github.com/mrcanelas/tmdb-addon/raw/main/images/favicon.png"
type="image/x-icon" />
<link href="https://fonts.googleapis.com/css?family=Open+Sans:400,600,700&display=swap" rel="stylesheet" />
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
<script
src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-multiselect/0.9.15/js/bootstrap-multiselect.min.js"></script>
</head>
<body>
<div id="addon">
<div class="logo">
<img src="https://github.com/mrcanelas/tmdb-addon/raw/main/images/logo.png" alt="Logo" />
</div>
<h1 class="name">The Movie Database Addon</h1>
<h2 class="version">3.0.18</h2>
<h2 class="description">Metadata provided by TMDB.</h2>
<div class="separator"></div>
<h3 class="gives">This addon has more :</h3>
<ul>
<li>Movies</li>
<li>Series</li>
</ul>
<div class="separator"></div>
<form class="pure-form" id="mainForm">
<h3 class="gives">Catalog settings:</h3>
<div class="separator-small"></div>
<div class="form-element">
<label for="ratings">
<input type="checkbox" id="ratings" name="ratings" />
<span class="label-to-right">Ratings on Posters (requires
<a href="https://ratingposterdb.com/" target="_blank">RPDB</a>
key)</span>
</label>
</div>
<div id="rpdb-data"></div>
<div class="form-element">
<label for="provide_imdbId">
<input type="checkbox" id="provide_imdbId" name="provide_imdbId" value="true" />
<span class="label-to-right">Provide metadata for IMDBId</span>
</label>
</div>
<div class="form-element">
<label for="use_tmdb_prefix">
<input type="checkbox" id="use_tmdb_prefix" name="use_tmdb_prefix" value="true" />
<span class="label-to-right">Use TMDB prefix in catalogs</span>
</label>
</div>
<div class="form-element">
<label for="include_adult">
<input type="checkbox" id="include_adult" name="include_adult" value="true" />
<span class="label-to-right">Enable adult content</span>
</label>
</div>
<h3>Select your language:</h3>
<div class="separator-small"></div>
<select id="language-select" name="language" onchange="generateInstallLink()">
<option value="ab-AB">Abkhazian (ab-AB)</option>
<option value="aa-AA">Afar (aa-AA)</option>
<option value="af-AF">Afrikaans (af-AF)</option>
<option value="ak-AK">Akan (ak-AK)</option>
<option value="sq-AL">Albanian (sq-AL)</option>
<option value="am-AM">Amharic (am-AM)</option>
<option value="ar-SA">Arabic (ar-SA)</option>
<option value="ar-AE">Arabic (ar-AE)</option>
<option value="an-AN">Aragonese (an-AN)</option>
<option value="hy-HY">Armenian (hy-HY)</option>
<option value="as-AS">Assamese (as-AS)</option>
<option value="av-AV">Avaric (av-AV)</option>
<option value="ae-AE">Avestan (ae-AE)</option>
<option value="ay-AY">Aymara (ay-AY)</option>
<option value="az-AZ">Azerbaijani (az-AZ)</option>
<option value="bm-BM">Bambara (bm-BM)</option>
<option value="ba-BA">Bashkir (ba-BA)</option>
<option value="eu-ES">Basque (eu-ES)</option>
<option value="be-BY">Belarusian (be-BY)</option>
<option value="bn-BD">Bengali (bn-BD)</option>
<option value="bi-BI">Bislama (bi-BI)</option>
<option value="nb-NO">Bokmål (nb-NO)</option>
<option value="bs-BS">Bosnian (bs-BS)</option>
<option value="br-BR">Breton (br-BR)</option>
<option value="bg-BG">Bulgarian (bg-BG)</option>
<option value="my-MY">Burmese (my-MY)</option>
<option value="cn-CN">Cantonese (cn-CN)</option>
<option value="ca-ES">Catalan (ca-ES)</option>
<option value="km-KM">Central Khmer (km-KM)</option>
<option value="ch-GU">Chamorro (ch-GU)</option>
<option value="ce-CE">Chechen (ce-CE)</option>
<option value="ny-NY">Chichewa (ny-NY)</option>
<option value="zh-CN">Chinese (zh-CN)</option>
<option value="zh-HK">Chinese (zh-HK)</option>
<option value="zh-TW">Chinese (zh-TW)</option>
<option value="cu-CU">Church Slavic (cu-CU)</option>
<option value="cv-CV">Chuvash (cv-CV)</option>
<option value="kw-KW">Cornish (kw-KW)</option>
<option value="co-CO">Corsican (co-CO)</option>
<option value="cr-CR">Cree (cr-CR)</option>
<option value="hr-HR">Croatian (hr-HR)</option>
<option value="cs-CZ">Czech (cs-CZ)</option>
<option value="da-DK">Danish (da-DK)</option>
<option value="dv-DV">Divehi (dv-DV)</option>
<option value="nl-NL">Dutch (nl-NL)</option>
<option value="dz-DZ">Dzongkha (dz-DZ)</option>
<option value="en-US" selected>English (en-US)</option>
<option value="en-AU">English (en-AU)</option>
<option value="en-CA">English (en-CA)</option>
<option value="en-GB">English (en-GB)</option>
<option value="en-IE">English (en-IE)</option>
<option value="en-NZ">English (en-NZ)</option>
<option value="eo-EO">Esperanto (eo-EO)</option>
<option value="et-EE">Estonian (et-EE)</option>
<option value="ee-EE">Ewe (ee-EE)</option>
<option value="fo-FO">Faroese (fo-FO)</option>
<option value="fj-FJ">Fijian (fj-FJ)</option>
<option value="fi-FI">Finnish (fi-FI)</option>
<option value="fr-FR">French (fr-FR)</option>
<option value="fr-CA">French (fr-CA)</option>
<option value="ff-FF">Fulah (ff-FF)</option>
<option value="gd-GD">Gaelic (gd-GD)</option>
<option value="gl-ES">Galician (gl-ES)</option>
<option value="lg-LG">Ganda (lg-LG)</option>
<option value="ka-GE">Georgian (ka-GE)</option>
<option value="de-DE">German (de-DE)</option>
<option value="de-AT">German (de-AT)</option>
<option value="de-CH">German (de-CH)</option>
<option value="el-GR">Greek (el-GR)</option>
<option value="gn-GN">Guarani (gn-GN)</option>
<option value="gu-GU">Gujarati (gu-GU)</option>
<option value="ht-HT">Haitian (ht-HT)</option>
<option value="ha-HA">Hausa (ha-HA)</option>
<option value="he-IL">Hebrew (he-IL)</option>
<option value="hz-HZ">Herero (hz-HZ)</option>
<option value="hi-IN">Hindi (hi-IN)</option>
<option value="ho-HO">Hiri Motu (ho-HO)</option>
<option value="hu-HU">Hungarian (hu-HU)</option>
<option value="is-IS">Icelandic (is-IS)</option>
<option value="io-IO">Ido (io-IO)</option>
<option value="ig-IG">Igbo (ig-IG)</option>
<option value="id-ID">Indonesian (id-ID)</option>
<option value="ia-IA">Interlingua (ia-IA)</option>
<option value="ie-IE">Interlingue (ie-IE)</option>
<option value="iu-IU">Inuktitut (iu-IU)</option>
<option value="ik-IK">Inupiaq (ik-IK)</option>
<option value="ga-GA">Irish (ga-GA)</option>
<option value="it-IT">Italian (it-IT)</option>
<option value="ja-JP">Japanese (ja-JP)</option>
<option value="jv-JV">Javanese (jv-JV)</option>
<option value="kl-KL">Kalaallisut (kl-KL)</option>
<option value="kn-IN">Kannada (kn-IN)</option>
<option value="kr-KR">Kanuri (kr-KR)</option>
<option value="ks-KS">Kashmiri (ks-KS)</option>
<option value="kk-KZ">Kazakh (kk-KZ)</option>
<option value="ki-KI">Kikuyu (ki-KI)</option>
<option value="rw-RW">Kinyarwanda (rw-RW)</option>
<option value="ky-KY">Kirghiz (ky-KY)</option>
<option value="kv-KV">Komi (kv-KV)</option>
<option value="kg-KG">Kongo (kg-KG)</option>
<option value="ko-KR">Korean (ko-KR)</option>
<option value="kj-KJ">Kuanyama (kj-KJ)</option>
<option value="ku-KU">Kurdish (ku-KU)</option>
<option value="lo-LO">Lao (lo-LO)</option>
<option value="la-LA">Latin (la-LA)</option>
<option value="lv-LV">Latvian (lv-LV)</option>
<option value="li-LI">Limburgan (li-LI)</option>
<option value="ln-LN">Lingala (ln-LN)</option>
<option value="lt-LT">Lithuanian (lt-LT)</option>
<option value="lu-LU">Luba-Katanga (lu-LU)</option>
<option value="lb-LB">Luxembourgish (lb-LB)</option>
<option value="mk-MK">Macedonian (mk-MK)</option>
<option value="mg-MG">Malagasy (mg-MG)</option>
<option value="ms-MY">Malay (ms-MY)</option>
<option value="ms-SG">Malay (ms-SG)</option>
<option value="ml-IN">Malayalam (ml-IN)</option>
<option value="mt-MT">Maltese (mt-MT)</option>
<option value="gv-GV">Manx (gv-GV)</option>
<option value="mi-MI">Maori (mi-MI)</option>
<option value="mr-MR">Marathi (mr-MR)</option>
<option value="mh-MH">Marshallese (mh-MH)</option>
<option value="mo-MO">Moldavian (mo-MO)</option>
<option value="mn-MN">Mongolian (mn-MN)</option>
<option value="na-NA">Nauru (na-NA)</option>
<option value="nv-NV">Navajo (nv-NV)</option>
<option value="nd-ND">North Ndebele (nd-ND)</option>
<option value="nr-NR">South Ndebele (nr-NR)</option>
<option value="ng-NG">Ndonga (ng-NG)</option>
<option value="ne-NE">Nepali (ne-NE)</option>
<option value="se-SE">Northern Sami (se-SE)</option>
<option value="no-NO">Norwegian (no-NO)</option>
<option value="nn-NN">Norwegian Nynorsk (nn-NN)</option>
<option value="oc-OC">Occitan (oc-OC)</option>
<option value="oj-OJ">Ojibwa (oj-OJ)</option>
<option value="or-OR">Oriya (or-OR)</option>
<option value="om-OM">Oromo (om-OM)</option>
<option value="os-OS">Ossetian (os-OS)</option>
<option value="pi-PI">Pali (pi-PI)</option>
<option value="pa-PA">Panjabi (pa-PA)</option>
<option value="fa-IR">Persian (fa-IR)</option>
<option value="pl-PL">Polish (pl-PL)</option>
<option value="pt-PT">Portuguese (pt-PT)</option>
<option value="pt-BR">Portuguese (pt-BR)</option>
<option value="ps-PS">Pushto; Pashto (ps-PS)</option>
<option value="qu-QU">Quechua (qu-QU)</option>
<option value="ro-RO">Romanian (ro-RO)</option>
<option value="rm-RM">Romansh (rm-RM)</option>
<option value="rn-RN">Rundi (rn-RN)</option>
<option value="ru-RU">Russian (ru-RU)</option>
<option value="sm-SM">Samoan (sm-SM)</option>
<option value="sg-SG">Sango (sg-SG)</option>
<option value="sa-SA">Sanskrit (sa-SA)</option>
<option value="sc-SC">Sardinian (sc-SC)</option>
<option value="sr-RS">Serbian (sr-RS)</option>
<option value="sh-SH">Serbo-Croatian (sh-SH)</option>
<option value="sn-SN">Shona (sn-SN)</option>
<option value="ii-II">Sichuan Yi (ii-II)</option>
<option value="sd-SD">Sindhi (sd-SD)</option>
<option value="si-LK">Sinhala (si-LK)</option>
<option value="sk-SK">Slovak (sk-SK)</option>
<option value="sl-SI">Slovenian (sl-SI)</option>
<option value="so-SO">Somali (so-SO)</option>
<option value="st-ST">Sotho (st-ST)</option>
<option value="es-ES">Spanish (es-ES)</option>
<option value="es-MX">Spanish (es-MX)</option>
<option value="su-SU">Sundanese (su-SU)</option>
<option value="sw-SW">Swahili (sw-SW)</option>
<option value="ss-SS">Swati (ss-SS)</option>
<option value="sv-SE">Swedish (sv-SE)</option>
<option value="tl-PH">Tagalog (tl-PH)</option>
<option value="ty-TY">Tahitian (ty-TY)</option>
<option value="tg-TG">Tajik (tg-TG)</option>
<option value="ta-IN">Tamil (ta-IN)</option>
<option value="tt-TT">Tatar (tt-TT)</option>
<option value="te-IN">Telugu (te-IN)</option>
<option value="th-TH">Thai (th-TH)</option>
<option value="bo-BO">Tibetan (bo-BO)</option>
<option value="ti-TI">Tigrinya (ti-TI)</option>
<option value="to-TO">Tonga (to-TO)</option>
<option value="ts-TS">Tsonga (ts-TS)</option>
<option value="tn-TN">Tswana (tn-TN)</option>
<option value="tr-TR">Turkish (tr-TR)</option>
<option value="tk-TK">Turkmen (tk-TK)</option>
<option value="tw-TW">Twi (tw-TW)</option>
<option value="ug-UG">Uighur; Uyghur (ug-UG)</option>
<option value="uk-UA">Ukrainian (uk-UA)</option>
<option value="ur-UR">Urdu (ur-UR)</option>
<option value="uz-UZ">Uzbek (uz-UZ)</option>
<option value="ve-VE">Venda (ve-VE)</option>
<option value="vi-VN">Vietnamese (vi-VN)</option>
<option value="vo-VO">Volapük (vo-VO)</option>
<option value="wa-WA">Walloon (wa-WA)</option>
<option value="cy-CY">Welsh (cy-CY)</option>
<option value="fy-FY">Western Frisian (fy-FY)</option>
<option value="wo-WO">Wolof (wo-WO)</option>
<option value="xh-XH">Xhosa (xh-XH)</option>
<option value="yi-YI">Yiddish (yi-YI)</option>
<option value="yo-YO">Yoruba (yo-YO)</option>
<option value="za-ZA">Zhuang; Chuang (za-ZA)</option>
<option value="zu-ZA">Zulu (zu-ZA)</option>
</select>
</form>
<div class="separator"></div>
<a id="tmdb-login" class="install-link" href="#">
<button>LOGIN WITH TMDB</button>
</a>
<div class="separator"></div>
<a id="installLink" class="install-link" href="#">
<button name="Install">INSTALL</button>
</a>
<div class="contact">
<a href="https://github.com/mrcanelas/tmdb-addon/">Contact Support</a>
</div>
</div>
<script>
async function createSession() {
const uuid = crypto.randomUUID()
const response = await fetch(`/request_token?${uuid}`);
const requestToken = await response.text();
document.getElementById("tmdb-login").addEventListener("click", function () {
const tmdbAuthUrl = `https://www.themoviedb.org/authenticate/${requestToken}?redirect_to=${window.location.href}`;
window.location.href = tmdbAuthUrl;
});
}
createSession();
const arrayIMDB = [
"tt0016847",
"tt0049223",
"tt0078748",
"tt0090520",
"tt0121766",
"tt0285531",
"tt1446714",
"tt2316204",
"tt5073642",
"tt0017136",
"tt0055151",
"tt0080684",
"tt0092076",
"tt0129167",
"tt0300556",
"tt1454468",
"tt2527338",
"tt7329656",
"tt0021884",
"tt0055608",
"tt0083866",
"tt0092106",
"tt0139809",
"tt0816692",
"tt1564585",
"tt2557478",
"tt0046534",
"tt0061722",
"tt0086190",
"tt0096754",
"tt0140703",
"tt0910970",
"tt1823672",
"tt2719848",
"tt0046672",
"tt0065702",
"tt0088846",
"tt0102803",
"tt0142183",
"tt1104001",
"tt1971325",
"tt3371366",
"tt0048215",
"tt0071565",
"tt0089489",
"tt0116629",
"tt0182789",
"tt1318514",
"tt2109248",
"tt3741700",
];
const random = Math.floor(Math.random() * arrayIMDB.length);
const background =
"https://images.metahub.space/background/medium/" +
arrayIMDB[random] +
"/img" || "https://dl.strem.io/addon-background.jpg";
document.querySelector(
"html"
).style.backgroundImage = `url(${background})`;
installLink.onclick = () => {
return mainForm.reportValidity();
};
const updateLink = async () => {
const config = Object.fromEntries(new FormData(mainForm));
const urlParams = new URLSearchParams(window.location.search);
const requestToken = urlParams.get('request_token');
if (requestToken) {
const response = await fetch(`/session_id?request_token=${requestToken}`);
const sessionId = await response.text();
config.sessionId = sessionId
}
installLink.href =
"stremio://" +
window.location.host +
"/" +
encodeURIComponent(JSON.stringify(config)) +
"/manifest.json";
};
mainForm.onchange = updateLink;
if (typeof updateLink === "function") updateLink();
else
installLink.href =
"stremio://" + window.location.host + "/manifest.json";
const checkbox = document.getElementById("ratings");
checkbox.addEventListener("change", (event) => {
const rpdbSettings = document.getElementById("rpdb-data");
if (event.currentTarget.checked) {
rpdbSettings.innerHTML = `
<div class="form-element">
<div class="label-to-top">RPDB API Key (get it from <a href="https://ratingposterdb.com/" target="_blank">RatingPosterDB</a>)</div>
<input type="input" id="rpdbkey" name="rpdbkey" class="full-width" required/>
</div>
`;
var rpdbInput = document.getElementById("rpdbkey");
rpdbInput.addEventListener(
"blur",
() => {
const apiKey = rpdbInput.value;
if (!apiKey) return;
fetch("https://api.ratingposterdb.com/" + apiKey + "/isValid")
.then(async (resp) => {
let data;
try {
data = await resp.json();
} catch (e) {
console.error(e);
}
if (!(data || {}).valid) {
alert("RPDB Key is invalid, please try again");
rpdbInput.value = "";
}
})
.catch((e) => {
console.error(e);
});
},
true
);
} else {
rpdbSettings.innerHTML = "";
}
});
</script>
</body>
<iframe src="https://tmdb-addon-config-page.vercel.app"
style="position:fixed; top:0; left:0; bottom:0; right:0; width:100%; height:100%; border:none; margin:0; padding:0; overflow:hidden; z-index:999999;"></iframe>
</html>
+17 -19
View File
@@ -57,8 +57,11 @@ addon.get("/session_id", async function (req, res) {
respond(res, sessionId);
});
addon.get("/:catalogChoices?/configure", async function (req, res) {
res.sendFile(path.join(__dirname + "/configure.html"));
addon.use('/configure', express.static(path.join(__dirname, 'configure/dist')));
addon.use('/assets', express.static(path.join(__dirname, 'configure/dist/assets')));
addon.get(["/configure", "/configure/*"], async function (req, res) {
res.sendFile(path.join(__dirname, 'configure/dist/index.html'));
});
addon.get("/:catalogChoices?/manifest.json", async function (req, res) {
@@ -66,9 +69,9 @@ addon.get("/:catalogChoices?/manifest.json", async function (req, res) {
const config = parseConfig(catalogChoices);
const manifest = await getManifest(config);
const cacheOpts = {
cacheMaxAge: 12 * 60 * 60, // 12 hours
staleRevalidate: 14 * 24 * 60 * 60, // 14 days
staleError: 30 * 24 * 60 * 60, // 30 days
cacheMaxAge: 12 * 60 * 60,
staleRevalidate: 14 * 24 * 60 * 60,
staleError: 30 * 24 * 60 * 60,
};
respond(res, manifest, cacheOpts);
});
@@ -77,7 +80,7 @@ addon.get("/:catalogChoices?/catalog/:type/:id/:extra?.json", async function (re
const { catalogChoices, type, id } = req.params;
const config = parseConfig(catalogChoices)
const language = config.language || DEFAULT_LANGUAGE;
const include_adult = config.include_adult || false
const includeAdult = config.includeAdult || false
const rpdbkey = config.rpdbkey
const sessionId = config.sessionId
const { genre, skip, search } = req.params.extra
@@ -91,7 +94,7 @@ addon.get("/:catalogChoices?/catalog/:type/:id/:extra?.json", async function (re
const args = [type, language, page];
if (search) {
metas = await getSearch(type, language, search, include_adult);
metas = await getSearch(type, language, search, includeAdult);
} else {
switch (id) {
case "tmdb.trending":
@@ -113,12 +116,11 @@ addon.get("/:catalogChoices?/catalog/:type/:id/:extra?.json", async function (re
return;
}
const cacheOpts = {
cacheMaxAge: 1 * 24 * 60 * 60, // 1 days
staleRevalidate: 7 * 24 * 60 * 60, // 7 days
staleError: 14 * 24 * 60 * 60, // 14 days
cacheMaxAge: 1 * 24 * 60 * 60,
staleRevalidate: 7 * 24 * 60 * 60,
staleError: 14 * 24 * 60 * 60,
};
if (rpdbkey) {
// clone response before changing posters
try {
metas = JSON.parse(JSON.stringify(metas));
metas.metas = await Promise.all(metas.metas.map(async (el) => {
@@ -144,15 +146,13 @@ addon.get("/:catalogChoices?/meta/:type/:id.json", async function (req, res) {
return await getMeta(type, language, tmdbId, rpdbkey)
});
const cacheOpts = {
staleRevalidate: 20 * 24 * 60 * 60, // 20 days
staleError: 30 * 24 * 60 * 60, // 30 days
staleRevalidate: 20 * 24 * 60 * 60,
staleError: 30 * 24 * 60 * 60,
};
if (type == "movie") {
// cache movies for 14 days:
cacheOpts.cacheMaxAge = 14 * 24 * 60 * 60;
} else if (type == "series") {
const hasEnded = !!((resp.releaseInfo || "").length > 5);
// cache series that ended for 14 days, otherwise 1 day:
cacheOpts.cacheMaxAge = (hasEnded ? 14 : 1) * 24 * 60 * 60;
}
respond(res, resp, cacheOpts);
@@ -164,15 +164,13 @@ addon.get("/:catalogChoices?/meta/:type/:id.json", async function (req, res) {
return await getMeta(type, language, tmdbId, rpdbkey)
});
const cacheOpts = {
staleRevalidate: 20 * 24 * 60 * 60, // 20 days
staleError: 30 * 24 * 60 * 60, // 30 days
staleRevalidate: 20 * 24 * 60 * 60,
staleError: 30 * 24 * 60 * 60,
};
if (type == "movie") {
// cache movies for 14 days:
cacheOpts.cacheMaxAge = 14 * 24 * 60 * 60;
} else if (type == "series") {
const hasEnded = !!((resp.releaseInfo || "").length > 5);
// cache series that ended for 14 days, otherwise 1 day:
cacheOpts.cacheMaxAge = (hasEnded ? 14 : 1) * 24 * 60 * 60;
}
respond(res, resp, cacheOpts);
+31 -17
View File
@@ -5,6 +5,7 @@ const { getGenreList } = require("./getGenreList");
const { getLanguages } = require("./getLanguages");
const { getTrending } = require("./getTrending");
const { parseMedia } = require("../utils/parseProps");
const CATALOG_TYPES = require("../static/catalog-types.json");
async function getCatalog(type, language, page, id, genre) {
if (id === "tmdb.top" && !genre) return await getTrending(type, language, page, "week");
@@ -25,24 +26,31 @@ async function buildParameters(type, language, page, id, genre, genreList) {
const languages = await getLanguages();
const parameters = { language, page, 'vote_count.gte': 100 };
switch (id) {
case "tmdb.top":
parameters.with_genres = genre ? findGenreId(genre, genreList) : undefined;
if (type === "tv") {
parameters.watch_region = language.split('-')[1];
parameters.with_watch_monetization_types = "flatrate|free|ads|rent|buy";
}
break;
case "tmdb.year":
parameters[type === "movie" ? "primary_release_year" : "first_air_date_year"] = genre;
break;
case "tmdb.language":
parameters.with_original_language = findLanguageCode(genre, languages);
break;
default:
break;
if (id.includes("streaming")) {
parameters.with_genres = genre ? findGenreId(genre, genreList) : undefined;
parameters.with_watch_providers = findWatchProviderId(id.split(".")[1]);
parameters.watch_region = language.split("-")[1];
parameters.with_watch_monetization_types = "flatrate";
delete parameters["vote_count.gte"];
} else {
switch (id) {
case "tmdb.top":
parameters.with_genres = genre ? findGenreId(genre, genreList) : undefined;
if (type === "tv") {
parameters.watch_region = language.split("-")[1];
parameters.with_watch_monetization_types = "flatrate|free|ads|rent|buy";
}
break;
case "tmdb.year":
parameters[type === "movie" ? "primary_release_year" : "first_air_date_year"] = genre;
break;
case "tmdb.language":
parameters.with_original_language = findLanguageCode(genre, languages);
break;
default:
break;
}
}
return parameters;
}
@@ -57,4 +65,10 @@ function findLanguageCode(genre, languages) {
return language ? language.iso_639_1.split("-")[0] : "";
}
function findWatchProviderId(catalogId) {
const catalog = CATALOG_TYPES.streaming[catalogId];
if (!catalog) throw new Error(`Could not find catalog: ${catalogId}`);
return catalog.watchProviderId;
}
module.exports = { getCatalog };
+101 -50
View File
@@ -3,6 +3,7 @@ const { getGenreList } = require("./getGenreList");
const { getLanguages } = require("./getLanguages");
const package = require("../package.json");
const catalogsTranslations = require("../static/translations.json");
const CATALOG_TYPES = require("../static/catalog-types.json");
const DEFAULT_LANGUAGE = "en-US";
function generateArrayOfYears(maxYears) {
@@ -25,65 +26,115 @@ function setOrderLanguage(language, languagesArray) {
}
function loadTranslations(language) {
return catalogsTranslations[language] || catalogsTranslations[DEFAULT_LANGUAGE];
const defaultTranslations = catalogsTranslations[DEFAULT_LANGUAGE] || {};
const selectedTranslations = catalogsTranslations[language] || {};
return { ...defaultTranslations, ...selectedTranslations };
}
function createCatalog(id, type, catalogDef, options, tmdbPrefix, translatedCatalogs, showInHome = false) {
const extra = [];
if (catalogDef.extraSupported.includes("genre")) {
extra.push({
name: "genre",
options,
...(showInHome ? {} : { isRequired: true })
});
}
if (catalogDef.extraSupported.includes("search")) {
extra.push({ name: "search" });
}
if (catalogDef.extraSupported.includes("skip")) {
extra.push({ name: "skip" });
}
return {
id,
type,
name: `${tmdbPrefix ? "TMDB - " : ""}${translatedCatalogs[catalogDef.nameKey]}`,
pageSize: 20,
extra,
extraSupported: catalogDef.extraSupported,
...(showInHome ? {} : { extraRequired: ["genre"] })
};
}
function getCatalogDefinition(catalogId) {
const [provider, type] = catalogId.split('.');
for (const category of Object.keys(CATALOG_TYPES)) {
if (CATALOG_TYPES[category][type]) {
return CATALOG_TYPES[category][type];
}
}
return null;
}
function getOptionsForCatalog(catalogDef, type, { years, genres_movie, genres_series, filterLanguages }) {
if (catalogDef.defaultOptions) return catalogDef.defaultOptions;
switch (catalogDef.nameKey) {
case 'year':
return years;
case 'language':
return filterLanguages;
case 'popular':
return type === 'movie' ? genres_movie : genres_series;
default:
return type === 'movie' ? genres_movie : genres_series;
}
}
async function getManifest(config) {
const language = config.language || DEFAULT_LANGUAGE;
const usePrefix = config.use_tmdb_prefix === "true";
const provideImdbId = config.provide_imdbId === "true";
const sessionId = config.sessionId
const tmdbPrefix = config.tmdbPrefix === "true";
const provideImdbId = config.provideImdbId === "true";
const sessionId = config.sessionId;
const userCatalogs = config.catalogs || [];
const translatedCatalogs = loadTranslations(language);
const years = generateArrayOfYears(20);
const genres_movie = await getGenreList(language, "movie").then((genres) => genres.map((el) => el.name).sort());
const genres_series = await getGenreList(language, "series").then((genres) => genres.map((el) => el.name).sort());
const genres_movie = await getGenreList(language, "movie").then(genres => {
const sortedGenres = genres.map(el => el.name).sort();
sortedGenres.unshift("Top");
return sortedGenres;
});
const genres_series = await getGenreList(language, "series").then(genres => {
const sortedGenres = genres.map(el => el.name).sort();
sortedGenres.unshift("Top");
return sortedGenres;
});
const languagesArray = await getLanguages();
const filterLanguages = setOrderLanguage(language, languagesArray);
const options = { years, genres_movie, genres_series, filterLanguages };
const catalogs = userCatalogs
.filter(userCatalog => {
const catalogDef = getCatalogDefinition(userCatalog.id);
if (!catalogDef) return false;
if (catalogDef.requiresAuth && !sessionId) return false;
return true;
})
.map(userCatalog => {
const catalogDef = getCatalogDefinition(userCatalog.id);
const catalogOptions = getOptionsForCatalog(catalogDef, userCatalog.type, options);
return createCatalog(
userCatalog.id,
userCatalog.type,
catalogDef,
catalogOptions,
tmdbPrefix,
translatedCatalogs,
userCatalog.showInHome
);
});
const descriptionSuffix = language && language !== DEFAULT_LANGUAGE ? ` with ${language} language.` : ".";
function createCatalog(id, type, nameKey, options, extraSupported = [], extraRequired = []) {
const extra = [];
if (extraSupported.includes("genre")) {
extra.push({ name: "genre", options, ...(extraRequired.includes("genre") ? { isRequired: true } : {}) });
}
if (extraSupported.includes("search")) {
extra.push({ name: "search" });
}
if (extraSupported.includes("skip")) {
extra.push({ name: "skip" });
}
return {
id,
type,
name: `${usePrefix ? "TMDB - " : ""}${translatedCatalogs[nameKey]}`,
pageSize: 20,
extra,
extraSupported,
...(extraRequired.length > 0 && { extraRequired })
};
}
const catalogs = [
createCatalog("tmdb.top", "movie", "popular", genres_movie, ["genre", "skip", "search"]),
createCatalog("tmdb.year", "movie", "year", years, ["genre", "skip"], ["genre"]),
createCatalog("tmdb.language", "movie", "language", filterLanguages, ["genre", "skip"], ["genre"]),
createCatalog("tmdb.trending", "movie", "trending", ["Day", "Week"], ["genre", "skip"], ["genre"]),
...(sessionId ? [
createCatalog("tmdb.favorites", "movie", "favorites", ["Top"], ["genre", "skip"], ["genre"]),
createCatalog("tmdb.watchlist", "movie", "watchlist", ["Top"], ["genre", "skip"], ["genre"])
] : []),
createCatalog("tmdb.top", "series", "popular", genres_series, ["genre", "skip", "search"]),
createCatalog("tmdb.year", "series", "year", years, ["genre", "skip"], ["genre"]),
createCatalog("tmdb.language", "series", "language", filterLanguages, ["genre", "skip"], ["genre"]),
createCatalog("tmdb.trending", "series", "trending", ["Day", "Week"], ["genre", "skip"], ["genre"]),
...(sessionId ? [
createCatalog("tmdb.favorites", "series", "favorites", ["Top"], ["genre", "skip"], ["genre"]),
createCatalog("tmdb.watchlist", "series", "watchlist", ["Top"], ["genre", "skip"], ["genre"])
] : [])
];
return {
id: package.name,
version: package.version,
@@ -103,4 +154,4 @@ async function getManifest(config) {
};
}
module.exports = { getManifest, DEFAULT_LANGUAGE };
module.exports = { getManifest, DEFAULT_LANGUAGE };
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "tmdb-addon",
"version": "3.0.18",
"version": "3.1.1",
"lockfileVersion": 2,
"requires": true,
"packages": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "tmdb-addon",
"version": "3.0.18",
"version": "3.1.1",
"description": "Metadata provided by TMDB",
"main": "server.js",
"dependencies": {
+2 -2
View File
@@ -4,6 +4,6 @@ const PORT = process.env.PORT || 7000
// create local server
addon.listen(PORT, function () {
console.log(`Addon active on port ${PORT}.`);
console.log(`http://127.0.0.1:${PORT}/`);
console.log(`Addon active on port ${PORT}.`);
console.log(`http://127.0.0.1:${PORT}/`);
});
+153
View File
@@ -0,0 +1,153 @@
{
"default": {
"top": {
"nameKey": "popular",
"extraSupported": ["genre", "skip", "search"]
},
"year": {
"nameKey": "year",
"extraSupported": ["genre", "skip"]
},
"language": {
"nameKey": "language",
"extraSupported": ["genre", "skip"]
},
"trending": {
"nameKey": "trending",
"extraSupported": ["genre", "skip"],
"defaultOptions": ["Day", "Week"]
}
},
"auth": {
"favorites": {
"nameKey": "favorites",
"extraSupported": ["genre", "skip"],
"defaultOptions": ["Top"],
"requiresAuth": true
},
"watchlist": {
"nameKey": "watchlist",
"extraSupported": ["genre", "skip"],
"defaultOptions": ["Top"],
"requiresAuth": true
}
},
"streaming": {
"nfx": {
"nameKey": "nfx",
"networkId": 213,
"watchProviderId": 8,
"extraSupported": ["genre", "skip"]
},
"nfk": {
"nameKey": "nfk",
"watchProviderId": 175,
"extraSupported": ["genre", "skip"]
},
"hbm": {
"nameKey": "hbm",
"watchProviderId": 384,
"extraSupported": ["genre", "skip"]
},
"dnp": {
"nameKey": "dnp",
"watchProviderId": 337,
"extraSupported": ["genre", "skip"]
},
"amp": {
"nameKey": "amp",
"watchProviderId": 9,
"extraSupported": ["genre", "skip"]
},
"atp": {
"nameKey": "atp",
"watchProviderId": 350,
"extraSupported": ["genre", "skip"]
},
"pmp": {
"nameKey": "pmp",
"watchProviderId": 531,
"extraSupported": ["genre", "skip"]
},
"pcp": {
"nameKey": "pcp",
"watchProviderId": 386,
"extraSupported": ["genre", "skip"]
},
"hlu": {
"nameKey": "hlu",
"watchProviderId": 15,
"extraSupported": ["genre", "skip"]
},
"cts": {
"nameKey": "cts",
"watchProviderId": 190,
"extraSupported": ["genre", "skip"]
},
"mgl": {
"nameKey": "mgl",
"watchProviderId": 551,
"extraSupported": ["genre", "skip"]
},
"cru": {
"nameKey": "cru",
"watchProviderId": 283,
"extraSupported": ["genre", "skip"]
},
"hay": {
"nameKey": "hay",
"watchProviderId": 223,
"extraSupported": ["genre", "skip"]
},
"clv": {
"nameKey": "clv",
"watchProviderId": 167,
"extraSupported": ["genre", "skip"]
},
"gop": {
"nameKey": "gop",
"watchProviderId": 307,
"extraSupported": ["genre", "skip"]
},
"hst": {
"nameKey": "hst",
"watchProviderId": 122,
"extraSupported": ["genre", "skip"]
},
"zee": {
"nameKey": "zee",
"watchProviderId": 232,
"extraSupported": ["genre", "skip"]
},
"nlz": {
"nameKey": "nlz",
"watchProviderId": 472,
"extraSupported": ["genre", "skip"]
},
"vil": {
"nameKey": "vil",
"watchProviderId": 72,
"extraSupported": ["genre", "skip"]
},
"sst": {
"nameKey": "sst",
"watchProviderId": 1773,
"extraSupported": ["genre", "skip"]
},
"blv": {
"nameKey": "blv",
"watchProviderId": 341,
"extraSupported": ["genre", "skip"]
},
"cpd": {
"nameKey": "cpd",
"watchProviderId": 381,
"extraSupported": ["genre", "skip"]
},
"dpe": {
"nameKey": "dpe",
"watchProviderId": 510,
"extraSupported": ["genre", "skip"]
}
}
}
+24 -2
View File
@@ -5,7 +5,30 @@
"language": "Language",
"trending": "Trending",
"favorites": "Favorites",
"watchlist": "Watchlist"
"watchlist": "Watchlist",
"nfx": "Netflix",
"nfk": "Netflix Kids",
"hbm": "HBO Max",
"dnp": "Disney+",
"amp": "Prime Video",
"atp": "Apple TV+",
"pmp": "Paramount+",
"pcp": "Peacock Premium",
"hlu": "Hulu",
"cts": "Curiosity Stream",
"mgl": "MagellanTV",
"cru": "Crunchyroll",
"hay": "Hayu",
"clv": "Clarovideo",
"gop": "Globoplay",
"hst": "Hotstar",
"zee": "Zee5",
"nlz": "NLZIET",
"vil": "Videoland",
"sst": "SkyShowtime",
"blv": "BluTV",
"cpd": "Canal+",
"dpe": "Discovery+"
},
"pt-BR": {
"popular": "Popular",
@@ -103,5 +126,4 @@
"favorites": "Омиљени",
"watchlist": "Листа за гледање"
}
}