From f03fcec5f3cc1cf79f36d0a294b0f89501d62177 Mon Sep 17 00:00:00 2001 From: Pan Cake Date: Thu, 1 Feb 2024 13:45:18 +1000 Subject: [PATCH] init commit --- README.md | 16 ++++ web/index.html | 198 +++++++++++++++++++++++++++++++++++++++++++++++++ web/script.js | 195 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 409 insertions(+) create mode 100644 README.md create mode 100644 web/index.html create mode 100644 web/script.js diff --git a/README.md b/README.md new file mode 100644 index 0000000..3077561 --- /dev/null +++ b/README.md @@ -0,0 +1,16 @@ +# Stremio Addon Manager +Manage your Stremio addons with ease. + +**WARNING: Use this at your own risk. This is not an official Stremio product and may break your Stremio installation. No support or warranty is given.** + +## Features +- Re-order your addons (including Cinemeta) +- Remove non-protected addons + +## Installation +1. Clone this repository +2. Open `web/index.html` in your browser +3. Follow instructions on the page + +## Thanks +Big thank you to `Sleeyax` and `` for the conversations and code snippets that made this really easy to implement. diff --git a/web/index.html b/web/index.html new file mode 100644 index 0000000..81f5b80 --- /dev/null +++ b/web/index.html @@ -0,0 +1,198 @@ + + + + + + + + Stremio Addon Manager + + + + + + + +
+
+ +
+
+

Why?

+

+ Stremio currently doesn't allow users to change the order that their installed addons appear on the home + screen. + As a work around, it is common for users to remove and re-install addons in the order they want + them to appear. This is a tedious and cumbersome process. This addon uses the internal Stremio API to + manipulate addon order without having to add/remove them. +

+

+ This is a workaround and not a solution. It is not recommended to + use this tool unless you are comfortable with the risks involved. + No support or warranty is given. +

+

How?

+
    +
  1. Login to https://web.stremio.com/ using your Stremio + credentials in your browser.
  2. +
  3. + Open the developer console and paste the follow code snippet + JSON.parse(localStorage.getItem("profile")).auth.key +
  4. +
  5. Take the output value and paste it into the form below.
  6. +
  7. Click 'Load Addons' to load your profiles addons.
  8. +
  9. Re-order your addons as you like.
  10. +
  11. Click the 'Sync To Stremio' button to sync the changes back to your profile.
  12. +
+
+
+

Configure

+
+
+ Step 0: Paste Stremio AuthKey +

+ + +

+
+
+ Step 1: Re-Order Addons +
    + +
+
+
+ Step 2: Sync Addons + +
+
+ +
+
+

Thank you

+

Big thank you to Sleeyax and <Code/> for the conversations and code + snippets that made this really easy to implement.

+ +
+ +
+

Frequently Asked Questions

+
+ + What risks are involved? + +

+ This was whipped together in a couple of hours. It is not well tested and may break your Stremio + profile. Stremio functionality relies on the existence of certain addons (ie, Cinemeta). It is + possible that the order of addons is also important to some functionality. +

+

+ There is currently no way to 'reset' your addons to previous configurations or default + configurations. +

+
+
+
+

Made with ❤️, 🤖 and 🥞's - enjoy!

+
+
+ + + diff --git a/web/script.js b/web/script.js new file mode 100644 index 0000000..3705f10 --- /dev/null +++ b/web/script.js @@ -0,0 +1,195 @@ +const addedAddonsListElement = document.querySelector(".sortable-list"); +const loadAddonButton = document.getElementById("load_addons_button"); +const syncAddonsButton = document.getElementById("sync_addons_button"); + +addonList = []; + +const initSortableList = (e) => { + e.preventDefault(); + const draggingItem = document.querySelector(".dragging"); + // Getting all items except currently dragging and making array of them + let siblings = [...addedAddonsListElement.querySelectorAll(".item:not(.dragging)")]; + + // Finding the sibling after which the dragging item should be placed + let nextSibling = siblings.find(sibling => { + return e.clientY <= sibling.getBoundingClientRect().top; + }); + + // Inserting the dragging item before the found sibling + addedAddonsListElement.insertBefore(draggingItem, nextSibling); + + // Update local addon list + syncLocalAddonList(); +} + +function removeAddon(url) { + // Remove addon from list + addonList = addonList.filter(addon => addon.transportUrl != url); + // Remove addon from list + removeAddonFromList(url); +} + +function getAuthKey() { + // Strip "'s and spaces from AuthKey in case user pasted them in + return document.getElementById("stremio_authkey_input_field").value.replaceAll('"', '').trim(); +} + +async function loadAddons() { + var authKey = getAuthKey(); + if (!authKey) { + alert("Please enter your Stremio auth key"); + throw new Error("No auth key"); + } + var json = JSON.stringify({ + type: 'AddonCollectionGet', + authKey, + update: true, + }); + var addons = await fetch('https://api.strem.io/api/addonCollectionGet', { + method: 'POST', + body: json, + }) + .then((res) => res.json()) + .then((data) => data.result.addons); + return addons; +} + +async function syncAddons() { + var authKey = getAuthKey(); + var json = JSON.stringify({ + type: 'AddonCollectionSet', + authKey, + addons: addonList, + }); + var addons = await fetch('https://api.strem.io/api/addonCollectionSet', { + method: 'POST', + body: json, + }) + .then((res) => res.json()) + .then((data) => { + if (!("result" in data) || data.result == null) { + alert("Failed to sync addons with unknown error"); + } else if (!data.result.success) { + alert("Failed to sync addons: " + data.result.error); + } else { + alert("Successfully synced addons!"); + } + }); + return addons; +} + +function addAddonToList(addon) { + const item = document.createElement("li"); + item.className = "item"; + item.draggable = true; + item.setAttribute("data-url", addon.transportUrl); + item.setAttribute("data-addon", JSON.stringify(addon)); + item.innerHTML = ` +
+
+
+
+ ${addon.manifest.name} +
+
+
+ `; + + // Populate logo or use a placeholder if manifest doesn't define one + if (addon.manifest.logo) { + item.querySelector(".logo_container").innerHTML = `${addon.manifest.name}`; + } else { + item.querySelector(".logo_container").innerHTML = `${addon.manifest.name}`; + } + + addonConfigurable = false; + if (addon.manifest.behaviorHints && addon.manifest.behaviorHints.configurable) { + addonConfigurable = true; + item.innerHTML += ` + + `; + } + item.innerHTML += ` + + `; + // Only show delete button if addon is not protected + addonDeletable = true; + if ("flags" in addon && addon.flags.protected) { + addonDeletable = false; + } else { + item.innerHTML += ` + + `; + } + item.innerHTML += ` + +
+ `; + addedAddonsListElement.appendChild(item); + + // Add elem event listeners + item.addEventListener("dragstart", () => { + // Adding dragging class to item after a delay + setTimeout(() => item.classList.add("dragging"), 0); + }); + // Removing dragging class from item on dragend event + item.addEventListener("dragend", () => item.classList.remove("dragging")); + // Add copy url button event listener + item.querySelector(".button.icon-only.copy-url").addEventListener("click", e => { + e.preventDefault(); + navigator.clipboard.writeText(addon.transportUrl); + }); + // Add delete button event listener + if (addonDeletable) { + item.querySelector(".button.icon-only.delete").addEventListener("click", e => { + e.preventDefault(); + removeAddon(addon.transportUrl); + }); + } + // Add visit addon configuration endpoint + if (addonConfigurable) { + // Add visit url button event listener - replacing protocol with https and path with /configure + item.querySelector(".button.icon-only.visit-url").addEventListener("click", e => { + e.preventDefault(); + window.open(addon.transportUrl.replace("stremio://", "https://").replace("/manifest.json", "/configure")); + }); + } +} + +function removeAddonFromList(url) { + const item = addedAddonsListElement.querySelector(`[data-url="${url}"]`); + item.remove(); +} + +function syncLocalAddonList() { + const items = addedAddonsListElement.querySelectorAll(".item"); + addonList = []; + items.forEach(item => { + addonList.push(JSON.parse(item.getAttribute("data-addon"))); + }); +} + +async function main() { + addedAddonsListElement.addEventListener("dragover", initSortableList); + addedAddonsListElement.addEventListener("dragenter", e => e.preventDefault()); + + syncAddonsButton.addEventListener("click", syncAddons); + loadAddonButton.addEventListener("click", async (e) => { + e.preventDefault(); + + await loadAddons().then((addons) => { + addons.forEach((addon) => { + addAddonToList(addon); + addonList.push(addon); + }); + }); + }); +} + +main().then(() => console.log('done')).catch(console.error);