Rename MediaUtils and add CharAi Dumper
This commit is contained in:
parent
1860d1564e
commit
475c5193d7
3 changed files with 152 additions and 34 deletions
|
|
@ -3,6 +3,8 @@
|
|||
Various Userscripts
|
||||
|
||||
### Installation
|
||||
|
||||
| Site | Link |
|
||||
| -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Jellyfin QuickDelete | [](https://git.m3.fyi/Marsn3/userscripts/~raw/main/src/jellyfin-quick-delete.user.js) |
|
||||
| Character.ai History Dumper | [](https://git.m3.fyi/Marsn3/userscripts/~raw/main/src/charai-history-dumper.user.js) |
|
||||
|
|
|
|||
118
src/charai-history-dumper.user.js
Normal file
118
src/charai-history-dumper.user.js
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
// ==UserScript==
|
||||
// @name CharacterAI History Dumper
|
||||
// @namespace https://git.m3.fyi/Marsn3/userscripts
|
||||
// @match https://beta.character.ai/*
|
||||
// @grant none
|
||||
// @version 1.0
|
||||
// @author Marsn3
|
||||
// @description Allows downloading saved chat messages from CharacterAI.
|
||||
// @downloadURL https://git.m3.fyi/Marsn3/userscripts/~raw/main/src/charai-history-dumper.user
|
||||
// @updateURL https://git.m3.fyi/Marsn3/userscripts/~raw/main/src/charai-history-dumper.user
|
||||
// ==/UserScript==
|
||||
|
||||
const log = (firstArg, ...remainingArgs) =>
|
||||
console.log(
|
||||
`[CharacterAI History Dumper v1.0] ${firstArg}`,
|
||||
...remainingArgs
|
||||
);
|
||||
log.error = (firstArg, ...remainingArgs) =>
|
||||
console.error(
|
||||
`[CharacterAI History Dumper v1.0] ${firstArg}`,
|
||||
...remainingArgs
|
||||
);
|
||||
|
||||
// Endpoints to intercept.
|
||||
const CHARACTER_MESSAGES_URL =
|
||||
"https://beta.character.ai/chat/history/external/msgs/?history";
|
||||
/** Maps a character's identifier to their basic info + chat histories. */
|
||||
const characterToSavedDataMap = {};
|
||||
|
||||
/** Creates the "Download" link on the "View Saved Chats" page. */
|
||||
const addDownloadLinkInSavedChats = (dataString, filename) => {
|
||||
// Don't create duplicate links.
|
||||
if (document.getElementById("injected-chat-dl-link")) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We want to add a link next to the "your past conversations with XXX" text.
|
||||
const element = document.getElementsByClassName("postButtonCaption")[0];
|
||||
|
||||
const dataBlob = new Blob([dataString], { type: "text/plain" });
|
||||
const downloadLink = document.createElement("a");
|
||||
downloadLink.id = "injected-chat-dl-link";
|
||||
downloadLink.textContent = "Download";
|
||||
downloadLink.href = URL.createObjectURL(dataBlob);
|
||||
downloadLink.download = filename;
|
||||
downloadLink.style = "padding-left: 8px";
|
||||
element.appendChild(downloadLink);
|
||||
};
|
||||
|
||||
/** Escapes a string so it can be used inside a regex. */
|
||||
const escapeStringForRegExp = (stringToGoIntoTheRegex) => {
|
||||
return stringToGoIntoTheRegex.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&");
|
||||
};
|
||||
|
||||
/** Takes in chat histories and anonymizes them. */
|
||||
|
||||
/** Configures XHook to intercept the endpoints we care about. */
|
||||
const configureXHookIntercepts = () => {
|
||||
xhook.after((_req, res) => {
|
||||
try {
|
||||
const endpoint = res.finalUrl;
|
||||
if (endpoint !== undefined) {
|
||||
if (endpoint.split("=")[0] !== CHARACTER_MESSAGES_URL) {
|
||||
// We don't care about other endpoints.
|
||||
return;
|
||||
} else {
|
||||
console.log("Found URL");
|
||||
}
|
||||
|
||||
const data = JSON.parse(res.data);
|
||||
let characterIdentifier;
|
||||
|
||||
characterIdentifier = data.messages[0].src__name.trim();
|
||||
|
||||
// We have all the downloadable data for this character, and we're on the
|
||||
// correct page. Create the download link.
|
||||
log(
|
||||
`Got all the data for ${characterIdentifier}, creating download link.`
|
||||
);
|
||||
|
||||
//log("If it doesn't show up, here's the data:", JSON.stringify(data));
|
||||
|
||||
// For some reason, the link doesn't get added if we call this right now,
|
||||
// so we wait a little while instead. Probably React re-render fuckery.
|
||||
addDownloadLinkInSavedChats(
|
||||
JSON.stringify(data),
|
||||
`${characterIdentifier}.json`
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
log.error("ERROR:", err);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// This is where XHook (lib for intercepting XHR/AJAX calls) gets injected into
|
||||
// the document, and once it gets properly parsed it'll call out to the setup
|
||||
// function.
|
||||
//
|
||||
// Copy-pasted and slightly adapted from: https://stackoverflow.com/a/8578840
|
||||
log("Injecting XHook to intercept XHR/AJAX calls.");
|
||||
(function (document, elementTagName, elementTagId) {
|
||||
var js,
|
||||
fjs = document.getElementsByTagName(elementTagName)[0];
|
||||
if (document.getElementById(elementTagId)) {
|
||||
return;
|
||||
}
|
||||
js = document.createElement(elementTagName);
|
||||
js.id = elementTagId;
|
||||
js.onload = function () {
|
||||
log("Done! Configuring intercepts.");
|
||||
configureXHookIntercepts();
|
||||
};
|
||||
// Link to hosted version taken from the official repo:
|
||||
// https://github.com/jpillora/xhook
|
||||
js.src = "https://jpillora.com/xhook/dist/xhook.min.js";
|
||||
fjs.parentNode.insertBefore(js, fjs);
|
||||
})(document, "script", "xhook");
|
||||
|
|
@ -1,12 +1,14 @@
|
|||
// ==UserScript==
|
||||
// @name QuickDelete
|
||||
// @description Quickly delete or refresh items in Jellyfin
|
||||
// @name Jellyfin MediaUtils
|
||||
// @description Utilities for Jellyfin to quickly delete, update or compare media
|
||||
// @namespace https://git.m3.fyi/Marsn3/userscripts
|
||||
// @version 0.4
|
||||
// @version 0.5
|
||||
// @author Marsn3
|
||||
// @match https://media.m3.fyi/*
|
||||
// @icon https://www.google.com/s2/favicons?sz=64&domain=tampermonkey.net
|
||||
// @match YOUR-JELLYFIN-URL
|
||||
// @grant none
|
||||
// @downloadURL https://git.m3.fyi/Marsn3/userscripts/~raw/main/src/jellyfin-mediautils.user
|
||||
// @updateURL https://git.m3.fyi/Marsn3/userscripts/~raw/main/src/jellyfin-mediautils.user
|
||||
|
||||
// @run-at document-end
|
||||
// ==/UserScript==
|
||||
|
||||
|
|
@ -14,19 +16,19 @@
|
|||
"use strict";
|
||||
|
||||
// Define Constants
|
||||
const username = "mars";
|
||||
const password = "absw3712mcS";
|
||||
const userid = "e55a6ca076a14cb5a6ca9cfaa75498c1";
|
||||
const baseURL = window.origin;
|
||||
const re = /\[(.*)\]/;
|
||||
var apiKey = "38346c399d57454da3bdbf47ba716765";
|
||||
const PASSWORD = "PASSWORD";
|
||||
const USERID = "USERID";
|
||||
const BASEURL = window.origin;
|
||||
const RE = /\[(.*)\]/;
|
||||
const HEADERS = {
|
||||
"X-Emby-Authorization": `MediaBrowser Client="Jellyfin MediaUtils", Device="Browser", DeviceId="", Version="1.0.0", Token="${apiKey}"`,
|
||||
};
|
||||
var apiKey = "APIKEY";
|
||||
|
||||
// Authorize user
|
||||
fetch(`${baseURL}/Users/${userid}/Authenticate/?pw=${password}`, {
|
||||
fetch(`${BASEURL}/Users/${USERID}/Authenticate/?pw=${PASSWORD}`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"X-Emby-Authorization": `MediaBrowser Client="QuickDelete", Device="Browser", DeviceId="", Version="1.0.0", Token="${apiKey}"`,
|
||||
},
|
||||
headers: HEADERS,
|
||||
})
|
||||
.then((response) => response.json())
|
||||
.then((data) => (apiKey = data.AccessToken)); // Store AccessToken
|
||||
|
|
@ -53,9 +55,7 @@
|
|||
// Send refresh request
|
||||
fetch(url, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"X-Emby-Authorization": `MediaBrowser Client="QuickDelete", Device="Chrome", DeviceId="test", Version="10.8.9", Token="${apiKey}"`,
|
||||
},
|
||||
headers: HEADERS,
|
||||
});
|
||||
// Replace empty image with rotating spinner
|
||||
this.parentElement.firstChild.firstChild.classList.remove("audiotrack");
|
||||
|
|
@ -75,8 +75,9 @@
|
|||
}`;
|
||||
// Add style element to document head
|
||||
document.head.appendChild(style);
|
||||
this.parentElement.style.backgroundColor = "rgba(131, 165, 152, 0.3)"
|
||||
this.parentElement.firstChild.style.backgroundColor = "rgba(69, 133, 136, 0.5)"
|
||||
this.parentElement.style.backgroundColor = "rgba(131, 165, 152, 0.3)";
|
||||
this.parentElement.firstChild.style.backgroundColor =
|
||||
"rgba(69, 133, 136, 0.5)";
|
||||
};
|
||||
//Return complete element
|
||||
return el;
|
||||
|
|
@ -95,23 +96,22 @@
|
|||
|
||||
// Bind get function
|
||||
el.onclick = function () {
|
||||
let url = `${window.origin}/Users/${userid}/Items/${this.parentElement.dataset.id}`;
|
||||
let url = `${window.origin}/Users/${USERID}/Items/${this.parentElement.dataset.id}`;
|
||||
console.log(`Fetching ${url}`);
|
||||
|
||||
// Send deletion request
|
||||
fetch(url, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"X-Emby-Authorization": `MediaBrowser Client="QuickDelete", Device="Chrome", DeviceId="test", Version="10.8.9", Token="${apiKey}"`,
|
||||
},
|
||||
headers: HEADERS,
|
||||
})
|
||||
.then((response) => response.json())
|
||||
.then((data) =>
|
||||
// Display bitrate
|
||||
|
||||
alert(
|
||||
`${data.MediaStreams[0].BitRate.toString().substring(0, 4)}kbps | ${data.MediaStreams[0].BitRate.toString().length} | ${re.exec(data.Path)[1]
|
||||
}`
|
||||
`${data.MediaStreams[0].BitRate.toString().substring(0, 4)}kbps | ${
|
||||
data.MediaStreams[0].BitRate.toString().length
|
||||
} | ${RE.exec(data.Path)[1]}`
|
||||
)
|
||||
);
|
||||
};
|
||||
|
|
@ -138,9 +138,7 @@
|
|||
// Send deletion request
|
||||
fetch(url, {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"X-Emby-Authorization": `MediaBrowser Client="QuickDelete", Device="Chrome", DeviceId="test", Version="10.8.9", Token="${apiKey}"`,
|
||||
},
|
||||
headers: HEADERS,
|
||||
});
|
||||
|
||||
// Remove parent to provide feedback and prevent double deletion
|
||||
|
|
@ -158,8 +156,8 @@
|
|||
let curr = collection[i];
|
||||
if (curr.firstChild.firstChild !== null) {
|
||||
if (curr.firstChild.firstChild.classList.contains("audiotrack")) {
|
||||
curr.style.backgroundColor = "rgba(251, 73, 52, 0.7)"
|
||||
curr.firstChild.style.backgroundColor = "#cc2412"
|
||||
curr.style.backgroundColor = "rgba(251, 73, 52, 0.7)";
|
||||
curr.firstChild.style.backgroundColor = "#cc2412";
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Add table
Reference in a new issue