Create exporter.js
This commit is contained in:
parent
5c4fff5abc
commit
1174281ed9
1 changed files with 133 additions and 0 deletions
133
exporter.js
Normal file
133
exporter.js
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
#!/bin/node
|
||||||
|
|
||||||
|
// Add your details here
|
||||||
|
const SERVER_ADDRESS = "https://jellyfin.example.com"; // Don't leave a trailing slash!!!
|
||||||
|
const API_KEY = ""; // Get this from Administration > API Keys
|
||||||
|
const USER_NAME = "user";
|
||||||
|
const LIBRARY_NAME = "Music";
|
||||||
|
|
||||||
|
if (SERVER_ADDRESS.includes("example") || API_KEY.trim() === "" || USER_NAME.trim() === "" || LIBRARY_NAME.trim() === "") {
|
||||||
|
console.log("Details are invalid, check the details at the top of this script file! (Lines 3 - 7)");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fs = require("fs");
|
||||||
|
let outText = "";
|
||||||
|
let albumCount = 0;
|
||||||
|
let songCount = 0;
|
||||||
|
|
||||||
|
class StrippedFunkyArray {
|
||||||
|
constructor() {
|
||||||
|
this.items = {};
|
||||||
|
this.itemKeys = [];
|
||||||
|
this.undefIndex = 9999;
|
||||||
|
}
|
||||||
|
|
||||||
|
set(key, item, regenerate = true) {
|
||||||
|
this.items[key] = item;
|
||||||
|
if (regenerate) {
|
||||||
|
this.itemKeys = Object.keys(this.items);
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
get(key) {
|
||||||
|
return this.items[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function apiRequest(url) {
|
||||||
|
const res = await fetch(`${SERVER_ADDRESS}/${url}`, {
|
||||||
|
headers: {
|
||||||
|
Authorization: `Mediabrowser Token="${API_KEY}"`
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return await res.json();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLargestFromNumericKey(keys) {
|
||||||
|
let largest = 0;
|
||||||
|
let next = 0;
|
||||||
|
for (const key of keys) {
|
||||||
|
next = parseInt(key);
|
||||||
|
if (next > largest) {
|
||||||
|
largest = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return largest + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("=============================================")
|
||||||
|
console.log("@ Holly's Jellyfin Library Expoter for VotV @");
|
||||||
|
console.log("@ It shouldn't have been this hard :( @");
|
||||||
|
console.log("=============================================\n");
|
||||||
|
(async () => {
|
||||||
|
console.log("Fetching users...");
|
||||||
|
const users = await apiRequest("Users");
|
||||||
|
let userId = "";
|
||||||
|
for (const user of users) {
|
||||||
|
if (user.Name === USER_NAME) {
|
||||||
|
userId = user.Id;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (userId === "") {
|
||||||
|
console.log(`Could not find user "${USER_NAME}" on the server.`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.log(`Found user "${USER_NAME}", ID = ${userId}.`);
|
||||||
|
|
||||||
|
console.log(`Fetching Libraries for ${userId}...`);
|
||||||
|
const libraries = await apiRequest(`Items?userId=${userId}`);
|
||||||
|
let libraryId = "";
|
||||||
|
for (const library of libraries.Items) {
|
||||||
|
if (library.Type !== "CollectionFolder") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (library.Name === LIBRARY_NAME) {
|
||||||
|
libraryId = library.Id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (libraryId === "") {
|
||||||
|
console.log(`Could not find library "${LIBRARY_NAME}" for user ${userId} on the server.`)
|
||||||
|
}
|
||||||
|
console.log(`Found library "${LIBRARY_NAME}", ID = ${libraryId}`);
|
||||||
|
|
||||||
|
console.log("Fetching library media...");
|
||||||
|
const media = await apiRequest(`Items?userId=${userId}&recursive=true&parentId=${libraryId}`);
|
||||||
|
|
||||||
|
console.log("Exporting...");
|
||||||
|
const groupedSongs = new StrippedFunkyArray();
|
||||||
|
for (const item of media.Items) {
|
||||||
|
if (item.Type !== "Audio") {
|
||||||
|
if (item.Type === "MusicAlbum") {
|
||||||
|
albumCount++;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let albumArray = groupedSongs.get(item.AlbumId);
|
||||||
|
if (albumArray == null) {
|
||||||
|
albumArray = groupedSongs.set(item.AlbumId, new StrippedFunkyArray());
|
||||||
|
}
|
||||||
|
albumArray.set(item.IndexNumber ?? albumArray.undefIndex++, item);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const albumArrayKey of groupedSongs.itemKeys) {
|
||||||
|
const albumArray = groupedSongs.get(albumArrayKey);
|
||||||
|
const largestIndexNumber = getLargestFromNumericKey(albumArray.itemKeys);
|
||||||
|
for (let i = 1; i < largestIndexNumber; i++) {
|
||||||
|
if (albumArray.items[i] != null) {
|
||||||
|
const item = albumArray.items[i];
|
||||||
|
outText += `${outText.length === 0 ? "" : "\n"}${item.Name} - ${item.Album ?? item.AlbumArtist}\n${SERVER_ADDRESS}/Items/${item.Id}/Download?api_key=${API_KEY}`;
|
||||||
|
songCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.writeFileSync("online.txt", outText);
|
||||||
|
console.log(`Exported ${songCount} songs from ${albumCount} albums to online.txt`);
|
||||||
|
})();
|
Loading…
Reference in a new issue