LastFMDownloader/index.js

124 lines
3.6 KiB
JavaScript
Raw Normal View History

2024-02-19 22:36:23 +00:00
require('dotenv').config();
const process = require('process');
const { Readable } = require('stream');
// const { searchMusics } = require("fix-esm").require('node-youtube-music');
const { GetListByKeyword } = require("youtube-search-api");
const skewered = require("skewered");
const { createClient } = require('fix-esm').require("webdav");
const path = require("path");
function pathGenerator({ url, name, channelName }) {
return path.join(process.env.NEXTCLOUD_FOLDER, `${url}-${skewered(`${name} - ${channelName}`)}.mp3`)
}
async function getVideo() {
const trackData = await fetch(
`https://${process.env.LASTFM_INSTANCE}/2.0/?method=user.getrecenttracks&user=${process.env.LASTFM_USERNAME}&api_key=${process.env.LASTFM_API_KEY}&format=json&limit=1&extended=1`
).then(x => x.json()).then(data => data.recenttracks.track[0]);
// const musics = await searchMusics(`${trackData.artist.name} - ${trackData.name}`);
const videoList = await GetListByKeyword(`${trackData.artist.name} - ${trackData.name}`, false, 1, [
{type: "video"}
]);
const musicVideo = videoList.items[0];
return {
url: musicVideo.id,
name: musicVideo.title,
channelName: musicVideo.channelTitle
};
}
async function checkNextcloud({ url, name, channelName }, nextcloudClient) {
const fileName = pathGenerator({ url, name, channelName });
return await nextcloudClient.exists(fileName);
}
async function downloadFromCobalt({ url, name, channelName }) {
const processor = await fetch(`https://${process.env.COBALT_INSTANCE}/api/json`, {
method: "POST",
body: JSON.stringify({
url: `https://youtu.be/${url}`,
vCodec: "h264",
vQuality: "144",
aFormat: "mp3",
filenamePattern: "basic",
isAudioOnly: true,
disableMetadata: false
}),
headers: [
["Accept", "application/json"],
["Content-Type", "application/json"],
]
}).then(x => x.json());
let processorURL;
switch (processor.status) {
case 'error':
case 'rate-limit':
throw new Error(`Error! (${processor.text})`);
case 'redirect':
processorURL = processor.audio || processor.url;
break;
case 'picker':
throw new Error("Can't handle picker!");
case 'stream':
const streamProcessor = await fetch(`${processor.url}&p=1`).then(x => x.json());
if (streamProcessor.status === "continue")
processorURL = processor.audio || processor.url;
else processorURL = processor.audio || processor.url;
break;
default:
processorURL = processor.audio || processor.url;
break;
}
const { body } = await fetch(processorURL);
return {
fileStream: Readable.fromWeb(body),
url,
name,
channelName
};
}
async function uploadToNextcloud({ fileStream, url, name, channelName }, nextcloudClient) {
await nextcloudClient.createDirectory(process.env.NEXTCLOUD_FOLDER, {
recursive: true,
});
const fileName = pathGenerator({ url, name, channelName });
fileStream.pipe(nextcloudClient.createWriteStream(fileName));
return fileName;
}
async function main() {
const video = await getVideo();
console.log(video);
const nextcloudClient = createClient(
`https://${process.env.NEXTCLOUD_INSTANCE}/remote.php/dav/files/${process.env.NEXTCLOUD_USERNAME}/`,
{
username: process.env.NEXTCLOUD_USERNAME,
password: process.env.NEXTCLOUD_PASSWORD,
}
);
if (await checkNextcloud(video, nextcloudClient)) return null;
const cobalt = await downloadFromCobalt(video);
console.log(cobalt);
const nextcloud = await uploadToNextcloud(cobalt, nextcloudClient);
console.log(nextcloud);
return nextcloud;
}
main();