Comet00 / comet /templates /index.html
Mythus's picture
Upload 26 files
01d9265 verified
<!DOCTYPE html>
<html class="sl-theme-dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta content="Comet" property="og:title" />
<meta content="Stremio's fastest torrent/debrid search add-on." property="og:description" />
<meta content="https://comet.fast" property="og:url" />
<meta content="https://i.imgur.com/jmVoVMu.jpeg" property="og:image" />
<meta content="#6b6ef8" data-react-helmet="true" name="theme-color" />
<title>Comet - Stremio's fastest torrent/debrid search add-on.</title>
<link id="favicon" rel="icon" type="image/x-icon" href="https://i.imgur.com/jmVoVMu.jpeg">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@2.15.1/cdn/themes/dark.css" />
<script type="module" src="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@2.15.1/cdn/shoelace-autoloader.js"></script>
<style>
:not(:defined) {
visibility: hidden;
}
body {
opacity: 0;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
min-height: 100vh;
margin: 0;
background: radial-gradient(ellipse at bottom, #25292c 0%, #0c0d13 100%);
font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", "Liberation Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
font-size: 1rem;
font-weight: 400;
}
body.ready {
opacity: 1;
transition: 0.25s opacity;
}
::-webkit-scrollbar {
overflow: hidden;
}
.header {
text-align: center;
width: 40%;
margin-bottom: 20px;
}
.comet-text {
font-size: calc(1.375rem + 1.5vw);
font-weight: 500;
margin-bottom: 0;
}
.emoji {
width: 1em;
height: 1em;
vertical-align: middle;
}
.form-container {
background-color: #1a1d20;
padding: 2rem;
border-radius: 0.375rem;
box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
width: 80vw;
max-width: 950px;
margin-bottom: 50px;
}
.form-item {
margin-bottom: 0.75rem;
}
sl-details {
margin-bottom: 0.75rem;
}
.centered-item {
display: flex;
justify-content: center;
}
.margin-left-install {
margin-right: 10px;
}
.stars {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 120%;
transform: rotate(-45deg);
z-index: -1;
}
.star {
--star-color: var(--primary-color);
--star-tail-length: 6em;
--star-tail-height: 2px;
--star-width: calc(var(--star-tail-length) / 6);
--fall-duration: 9s;
--tail-fade-duration: var(--fall-duration);
position: absolute;
top: var(--top-offset);
left: 0;
width: var(--star-tail-length);
height: var(--star-tail-height);
color: var(--star-color);
background: linear-gradient(45deg, currentColor, transparent);
border-radius: 50%;
filter: drop-shadow(0 0 6px currentColor);
transform: translate3d(104em, 0, 0);
animation: fall var(--fall-duration) var(--fall-delay) linear infinite, tail-fade var(--tail-fade-duration) var(--fall-delay) ease-out infinite;
}
@media screen and (max-width: 750px) {
.star {
animation: fall var(--fall-duration) var(--fall-delay) linear infinite;
}
}
.star:nth-child(1) {
--star-tail-length: 6.14em;
--top-offset: 3.64vh;
--fall-duration: 10.878s;
--fall-delay: 0.034s;
}
.star:nth-child(2) {
--star-tail-length: 5.08em;
--top-offset: 69.69vh;
--fall-duration: 11.372s;
--fall-delay: 1.679s;
}
.star:nth-child(3) {
--star-tail-length: 6.1em;
--top-offset: 64.3vh;
--fall-duration: 7.088s;
--fall-delay: 3.382s;
}
.star:nth-child(4) {
--star-tail-length: 6.66em;
--top-offset: 34.91vh;
--fall-duration: 6.184s;
--fall-delay: 9.61s;
}
.star:nth-child(5) {
--star-tail-length: 6.89em;
--top-offset: 24.92vh;
--fall-duration: 9.465s;
--fall-delay: 9.12s;
}
.star:nth-child(6) {
--star-tail-length: 6.5em;
--top-offset: 51.9vh;
--fall-duration: 10.52s;
--fall-delay: 0.214s;
}
.star:nth-child(7) {
--star-tail-length: 5.58em;
--top-offset: 24.15vh;
--fall-duration: 8.9s;
--fall-delay: 0.499s;
}
.star:nth-child(8) {
--star-tail-length: 6.26em;
--top-offset: 59.4vh;
--fall-duration: 7.671s;
--fall-delay: 1.694s;
}
.star:nth-child(9) {
--star-tail-length: 6.46em;
--top-offset: 54.52vh;
--fall-duration: 6.484s;
--fall-delay: 4.616s;
}
.star:nth-child(10) {
--star-tail-length: 5.34em;
--top-offset: 74.03vh;
--fall-duration: 8.565s;
--fall-delay: 1.159s;
}
.star:nth-child(11) {
--star-tail-length: 6.84em;
--top-offset: 90.94vh;
--fall-duration: 10.133s;
--fall-delay: 6.108s;
}
.star:nth-child(12) {
--star-tail-length: 5.48em;
--top-offset: 97.27vh;
--fall-duration: 10.248s;
--fall-delay: 4.186s;
}
.star:nth-child(13) {
--star-tail-length: 7.21em;
--top-offset: 17.75vh;
--fall-duration: 10.549s;
--fall-delay: 1.868s;
}
.star:nth-child(14) {
--star-tail-length: 6.35em;
--top-offset: 94.98vh;
--fall-duration: 9.682s;
--fall-delay: 9.327s;
}
.star:nth-child(15) {
--star-tail-length: 5.18em;
--top-offset: 52.87vh;
--fall-duration: 9.934s;
--fall-delay: 8.919s;
}
.star:nth-child(16) {
--star-tail-length: 5.07em;
--top-offset: 50.26vh;
--fall-duration: 11.826s;
--fall-delay: 8.478s;
}
.star:nth-child(17) {
--star-tail-length: 7.01em;
--top-offset: 85.73vh;
--fall-duration: 9.87s;
--fall-delay: 1.206s;
}
.star:nth-child(18) {
--star-tail-length: 6.39em;
--top-offset: 2.81vh;
--fall-duration: 10.292s;
--fall-delay: 4.394s;
}
.star:nth-child(19) {
--star-tail-length: 5.27em;
--top-offset: 56.83vh;
--fall-duration: 8.944s;
--fall-delay: 8.779s;
}
.star:nth-child(20) {
--star-tail-length: 6.07em;
--top-offset: 54.55vh;
--fall-duration: 9.324s;
--fall-delay: 7.375s;
}
.star:nth-child(21) {
--star-tail-length: 6.1em;
--top-offset: 87.67vh;
--fall-duration: 11.943s;
--fall-delay: 6.919s;
}
.star:nth-child(22) {
--star-tail-length: 5.98em;
--top-offset: 70.04vh;
--fall-duration: 9.995s;
--fall-delay: 4.472s;
}
.star:nth-child(23) {
--star-tail-length: 6.34em;
--top-offset: 77.19vh;
--fall-duration: 10.073s;
--fall-delay: 8.354s;
}
.star:nth-child(24) {
--star-tail-length: 6.95em;
--top-offset: 14.48vh;
--fall-duration: 9.028s;
--fall-delay: 7.638s;
}
.star:nth-child(25) {
--star-tail-length: 6.23em;
--top-offset: 8.48vh;
--fall-duration: 7.427s;
--fall-delay: 0.915s;
}
.star:nth-child(26) {
--star-tail-length: 5.09em;
--top-offset: 6.56vh;
--fall-duration: 7.706s;
--fall-delay: 2.841s;
}
.star:nth-child(27) {
--star-tail-length: 7.01em;
--top-offset: 92.85vh;
--fall-duration: 7.359s;
--fall-delay: 7.229s;
}
.star:nth-child(28) {
--star-tail-length: 5.49em;
--top-offset: 27.89vh;
--fall-duration: 10.344s;
--fall-delay: 2.346s;
}
.star:nth-child(29) {
--star-tail-length: 5.82em;
--top-offset: 56.08vh;
--fall-duration: 10.911s;
--fall-delay: 4.231s;
}
.star:nth-child(30) {
--star-tail-length: 7.24em;
--top-offset: 22.54vh;
--fall-duration: 9.344s;
--fall-delay: 2.112s;
}
.star:nth-child(31) {
--star-tail-length: 6.8em;
--top-offset: 59.49vh;
--fall-duration: 7.059s;
--fall-delay: 0.924s;
}
.star:nth-child(32) {
--star-tail-length: 5.22em;
--top-offset: 44.01vh;
--fall-duration: 10.121s;
--fall-delay: 0.591s;
}
.star:nth-child(33) {
--star-tail-length: 6.1em;
--top-offset: 78.61vh;
--fall-duration: 8.306s;
--fall-delay: 4.403s;
}
.star:nth-child(34) {
--star-tail-length: 7.26em;
--top-offset: 85.76vh;
--fall-duration: 7.058s;
--fall-delay: 6.772s;
}
.star:nth-child(35) {
--star-tail-length: 7.01em;
--top-offset: 77.17vh;
--fall-duration: 6.29s;
--fall-delay: 1.468s;
}
.star:nth-child(36) {
--star-tail-length: 5.17em;
--top-offset: 13.63vh;
--fall-duration: 6.739s;
--fall-delay: 0.019s;
}
.star:nth-child(37) {
--star-tail-length: 6.41em;
--top-offset: 70.18vh;
--fall-duration: 6.177s;
--fall-delay: 8.148s;
}
.star:nth-child(38) {
--star-tail-length: 5.32em;
--top-offset: 62.65vh;
--fall-duration: 10.476s;
--fall-delay: 0.98s;
}
.star:nth-child(39) {
--star-tail-length: 7.24em;
--top-offset: 66.12vh;
--fall-duration: 8.449s;
--fall-delay: 4.255s;
}
.star:nth-child(40) {
--star-tail-length: 6.73em;
--top-offset: 14.73vh;
--fall-duration: 9.857s;
--fall-delay: 6.867s;
}
.star:nth-child(41) {
--star-tail-length: 5.25em;
--top-offset: 45.23vh;
--fall-duration: 7.898s;
--fall-delay: 4.966s;
}
.star:nth-child(42) {
--star-tail-length: 6.73em;
--top-offset: 36.17vh;
--fall-duration: 7.32s;
--fall-delay: 3.93s;
}
.star:nth-child(43) {
--star-tail-length: 7.38em;
--top-offset: 83.09vh;
--fall-duration: 7.394s;
--fall-delay: 5.388s;
}
.star:nth-child(44) {
--star-tail-length: 5.18em;
--top-offset: 98.36vh;
--fall-duration: 6.905s;
--fall-delay: 2.771s;
}
.star:nth-child(45) {
--star-tail-length: 6.66em;
--top-offset: 27.99vh;
--fall-duration: 7.62s;
--fall-delay: 3.624s;
}
.star:nth-child(46) {
--star-tail-length: 5.19em;
--top-offset: 92vh;
--fall-duration: 9.158s;
--fall-delay: 1.984s;
}
.star:nth-child(47) {
--star-tail-length: 6.16em;
--top-offset: 2.87vh;
--fall-duration: 9.266s;
--fall-delay: 4.04s;
}
.star:nth-child(48) {
--star-tail-length: 6.34em;
--top-offset: 19.39vh;
--fall-duration: 7.503s;
--fall-delay: 0.045s;
}
.star:nth-child(49) {
--star-tail-length: 6.85em;
--top-offset: 79.92vh;
--fall-duration: 7.472s;
--fall-delay: 1.514s;
}
.star:nth-child(50) {
--star-tail-length: 7.35em;
--top-offset: 63.71vh;
--fall-duration: 8.117s;
--fall-delay: 4.46s;
}
.star::before, .star::after {
position: absolute;
content: "";
top: 0;
left: calc(var(--star-width) / -2);
width: var(--star-width);
height: 100%;
background: linear-gradient(45deg, transparent, currentColor, transparent);
border-radius: inherit;
animation: blink 2s linear infinite;
}
.star::before {
transform: rotate(45deg);
}
.star::after {
transform: rotate(-45deg);
}
@keyframes fall {
to {
transform: translate3d(-30em, 0, 0);
}
}
@keyframes tail-fade {
0%, 50% {
width: var(--star-tail-length);
opacity: 1;
}
70%, 80% {
width: 0;
opacity: 0.4;
}
100% {
width: 0;
opacity: 0;
}
}
@keyframes blink {
50% {
opacity: 0.6;
}
}
</style>
</head>
<body>
<div class="stars"></div>
<script>
let hasTouchScreen = false;
if ("maxTouchPoints" in navigator) {
hasTouchScreen = navigator.maxTouchPoints > 0;
} else if ("msMaxTouchPoints" in navigator) {
hasTouchScreen = navigator.msMaxTouchPoints > 0;
} else {
const mQ = matchMedia?.("(pointer:coarse)");
if (mQ?.media === "(pointer:coarse)") {
hasTouchScreen = !!mQ.matches;
} else if ("orientation" in window) {
hasTouchScreen = true;
} else {
const UA = navigator.userAgent;
hasTouchScreen =
/\b(BlackBerry|webOS|iPhone|IEMobile)\b/i.test(UA) ||
/\b(Android|Windows Phone|iPad|iPod)\b/i.test(UA);
}
}
if (!hasTouchScreen) {
document.addEventListener("DOMContentLoaded", function() {
const starsContainer = document.querySelector('.stars');
const starCount = 30
for (let i = 0; i < starCount; i++) {
const star = document.createElement("div");
star.classList.add("star");
starsContainer.appendChild(star);
}
});
}
</script>
<div class="header">
<p class="comet-text">
<img class="emoji" src="https://fonts.gstatic.com/s/e/notoemoji/latest/1f4ab/512.gif">
Comet - <a href="https://github.com/g0ldyy/comet">GitHub</a>
</p>
{{CUSTOM_HEADER_HTML|safe}}
</div>
<div class="form-container">
<div class="form-item">
<sl-select id="indexers" multiple clearable label="Indexers" placeholder="Select indexers" {% if not indexerManager %} disabled {% endif %}></sl-select>
</div>
<div class="form-item">
<sl-select id="languages" multiple clearable label="Languages" placeholder="Select languages"></sl-select>
</div>
<div class="form-item">
<sl-select id="resolutions" multiple clearable label="Resolutions" placeholder="Select resolutions"></sl-select>
</div>
<div class="form-item">
<sl-input id="maxResults" type="number" min=0 value=0 label="Max Results" placeholder="Enter max results"></sl-input>
</div>
<div class="form-item">
<sl-input id="maxSize" type="number" min=0 value=0 label="Max Size (GB)" placeholder="Enter max size in gigabytes"></sl-input>
</div>
<div class="form-item">
<sl-input id="debridStreamProxyPassword" label="Debrid Stream Proxy Password" placeholder="{% if not proxyDebridStream %}Configuration required, see docs{% else %}Enter password{% endif %}" help-text="Debrid Stream Proxying allows you to use your Debrid Service from multiple IPs at same time!" {% if not proxyDebridStream %} disabled {% endif %}></sl-input>
</div>
<div class="form-item">
<sl-select id="debridService" value="realdebrid" label="Debrid Service" placeholder="Select debrid service">
<sl-option value="realdebrid">Real-Debrid</sl-option>
<sl-option value="alldebrid">All-Debrid</sl-option>
<sl-option value="premiumize">Premiumize</sl-option>
<sl-option value="torbox">TorBox</sl-option>
<sl-option value="debridlink">Debrid-Link</sl-option>
</sl-select>
</div>
<div class="form-item">
<label for="debridService">
Debrid API Key -
<a id="apiKeyLink" href="https://real-debrid.com/apitoken" target="_blank">Get It Here</a>
</label>
<sl-input id="debridApiKey" placeholder="Enter API key"></sl-input>
</div>
<sl-details summary="Advanced Settings">
<div class="form-item">
<sl-select id="resultFormat" multiple label="Result Format" placeholder="Select what to show in result title" hoist max-options-visible=10>
</sl-select>
</div>
</sl-details>
<script>
document.getElementById("debridService").addEventListener("sl-change", function(event) {
const selectedService = event.target.value;
const apiKeyLink = document.getElementById("apiKeyLink");
if (selectedService === "realdebrid") {
apiKeyLink.href = "https://real-debrid.com/apitoken";
} else if (selectedService === "alldebrid") {
apiKeyLink.href = "https://alldebrid.com/apikeys";
} else if (selectedService === "premiumize") {
apiKeyLink.href = "https://premiumize.me/account";
} else if (selectedService === "torbox") {
apiKeyLink.href = "https://torbox.app/settings";
} else if (selectedService === "debridlink") {
apiKeyLink.href = "https://debrid-link.com/webapp/apikey";
}
});
</script>
<div class="centered-item">
<sl-button id="install" variant="neutral" class="margin-left-install">Install</sl-button>
<sl-alert id="installAlert" variant="neutral" duration="3000" closable>
<sl-icon slot="icon" name="clipboard2-check"></sl-icon>
<strong>Attempting to add the addon to Stremio...</strong>
</sl-alert>
<sl-button id="copy-link" variant="neutral">Copy Link</sl-button>
<sl-alert id="copyAlert" variant="neutral" duration="3000" closable>
<sl-icon slot="icon" name="clipboard2-check"></sl-icon>
<strong>The Stremio addon link has been automatically copied.</strong>
</sl-alert>
<script type="module">
const languagesEmojis = {
"Multi": "🌎",
"English": "🇬🇧",
"Japanese": "🇯🇵",
"Chinese": "🇨🇳",
"Russian": "🇷🇺",
"Arabic": "🇸🇦",
"Portuguese": "🇵🇹",
"Spanish": "🇪🇸",
"French": "🇫🇷",
"German": "🇩🇪",
"Italian": "🇮🇹",
"Korean": "🇰🇷",
"Hindi": "🇮🇳",
"Bengali": "🇧🇩",
"Punjabi": "🇵🇰",
"Marathi": "🇮🇳",
"Gujarati": "🇮🇳",
"Tamil": "🇮🇳",
"Telugu": "🇮🇳",
"Kannada": "🇮🇳",
"Malayalam": "🇮🇳",
"Thai": "🇹🇭",
"Vietnamese": "🇻🇳",
"Indonesian": "🇮🇩",
"Turkish": "🇹🇷",
"Hebrew": "🇮🇱",
"Persian": "🇮🇷",
"Ukrainian": "🇺🇦",
"Greek": "🇬🇷",
"Lithuanian": "🇱🇹",
"Latvian": "🇱🇻",
"Estonian": "🇪🇪",
"Polish": "🇵🇱",
"Czech": "🇨🇿",
"Slovak": "🇸🇰",
"Hungarian": "🇭🇺",
"Romanian": "🇷🇴",
"Bulgarian": "🇧🇬",
"Serbian": "🇷🇸",
"Croatian": "🇭🇷",
"Slovenian": "🇸🇮",
"Dutch": "🇳🇱",
"Danish": "🇩🇰",
"Finnish": "🇫🇮",
"Swedish": "🇸🇪",
"Norwegian": "🇳🇴",
"Malay": "🇲🇾",
"Latino": "💃🏻",
};
let defaultLanguages = [];
let defaultResolutions = [];
let defaultResultFormat = [];
document.addEventListener("DOMContentLoaded", async () => {
await Promise.allSettled([
customElements.whenDefined("sl-button"),
customElements.whenDefined("sl-alert"),
customElements.whenDefined("sl-select"),
customElements.whenDefined("sl-input"),
customElements.whenDefined("sl-option"),
customElements.whenDefined("sl-icon")
]);
const webConfig = {{webConfig|tojson}};
populateSelect("indexers", webConfig.indexers);
populateSelect("languages", webConfig.languages);
populateSelect("resolutions", webConfig.resolutions);
populateSelect("resultFormat", webConfig.resultFormat);
document.body.classList.add("ready");
defaultLanguages = webConfig.languages;
defaultResolutions = webConfig.resolutions;
defaultResultFormat = webConfig.resultFormat;
// try populate the form from previous settings
try {
const settings = getSettingsFromUrl();
if (settings != null) { populateFormFromSettings(settings); }
} catch (error) {
console.log("Failed to retrieve or parse settings from URL:", error);
}
});
function populateSelect(selectId, options) {
const selectElement = document.getElementById(selectId);
options.forEach(option => {
const optionElement = document.createElement("sl-option");
optionElement.value = option;
if (selectId === "languages") {
// For languages, prepend the flag emoji if it exists
const flag = languagesEmojis[option] || '';
optionElement.textContent = `${flag} ${option}`;
} else {
// For other selects, just use the option text as is
optionElement.textContent = option;
}
selectElement.appendChild(optionElement);
});
// Set the default value
selectElement.value = options; // Assuming first option as default
}
const installButton = document.querySelector("#install");
const installAlert = document.querySelector('#installAlert');
const copyLinkButton = document.querySelector("#copy-link");
const copyAlert = document.querySelector('#copyAlert');
function getSettings() {
const indexers = Array.from(document.getElementById("indexers").selectedOptions).map(option => option.value);
const languages = Array.from(document.getElementById("languages").selectedOptions).map(option => option.value);
const resolutions = Array.from(document.getElementById("resolutions").selectedOptions).map(option => option.value);
const maxResults = document.getElementById("maxResults").value;
const maxSize = document.getElementById("maxSize").value;
const resultFormat = Array.from(document.getElementById("resultFormat").selectedOptions).map(option => option.value);
const debridService = document.getElementById("debridService").value;
const debridApiKey = document.getElementById("debridApiKey").value;
const debridStreamProxyPassword = document.getElementById("debridStreamProxyPassword").value;
const selectedLanguages = languages.length === defaultLanguages.length && languages.every((val, index) => val === defaultLanguages[index]) ? ["All"] : languages;
const selectedResolutions = resolutions.length === defaultResolutions.length && resolutions.every((val, index) => val === defaultResolutions[index]) ? ["All"] : resolutions;
const selectedResultFormat = resultFormat.length === defaultResultFormat.length && resultFormat.every((val, index) => val === defaultResultFormat[index]) ? ["All"] : resultFormat;
return {
indexers: indexers,
maxResults: parseInt(maxResults),
maxSize: parseFloat(maxSize * 1073741824),
resultFormat: selectedResultFormat,
resolutions: selectedResolutions,
languages: selectedLanguages,
debridService: debridService,
debridApiKey: debridApiKey,
debridStreamProxyPassword: debridStreamProxyPassword,
};
}
copyLinkButton.addEventListener("click", () => {
const settings = getSettings();
const settingsString = btoa(JSON.stringify(settings))
const textArea = document.createElement("textarea");
textArea.value = `${window.location.origin}/${settingsString}/manifest.json`;
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
document.execCommand("copy");
document.body.removeChild(textArea);
console.log(settingsString);
copyAlert.toast();
});
installButton.addEventListener("click", () => {
const settings = getSettings();
const settingsString = btoa(JSON.stringify(settings))
window.location.href = `stremio://${window.location.host}/${settingsString}/manifest.json`;
console.log(settingsString);
installAlert.toast();
});
function getSettingsFromUrl() {
const url = window.location.href;
// Try parse settings from the URL
if (url.split("/").length < 5) {
console.log("no previous settings"); return null;
}
const settingsString = url.split("/")[3];
if (typeof settingsString === "undefined") {
console.log("can't fetch previous settings", settingsString)
return null;
}
const settingsJson = atob(settingsString);
return JSON.parse(settingsJson);
}
function populateFormFromSettings(settings) {
document.getElementById("maxResults").value = settings.maxResults;
document.getElementById("maxSize").value = settings.maxSize / 1073741824; // Convert back from bytes to GB
document.getElementById("debridService").value = settings.debridService;
document.getElementById("debridApiKey").value = settings.debridApiKey;
document.getElementById("debridStreamProxyPassword").value = settings.debridStreamProxyPassword;
document.getElementById("indexers").value = settings.indexers;
if (settings.languages != "All")
document.getElementById("languages").value = settings.languages;
if (settings.resolutions != "All")
document.getElementById("resolutions").value = settings.resolutions;
if (settings.resultFormat != "All")
document.getElementById("resultFormat").value = settings.resultFormat;
}
</script>
</div>
</div>
</body>
</html>