2024-02-19 22:36:23 +00:00
|
|
|
require('dotenv').config();
|
2024-02-19 23:16:29 +00:00
|
|
|
const { Readable } = require("stream");
|
2024-02-19 22:36:23 +00:00
|
|
|
const { GetListByKeyword } = require("youtube-search-api");
|
|
|
|
const skewered = require("skewered");
|
2024-02-19 23:16:29 +00:00
|
|
|
const { createClient } = require("fix-esm").require("webdav");
|
2024-02-19 22:36:23 +00:00
|
|
|
const path = require("path");
|
2024-02-19 23:16:29 +00:00
|
|
|
const { readFileSync, writeFileSync } = require("fs");
|
2024-02-19 22:36:23 +00:00
|
|
|
|
|
|
|
function pathGenerator({ url, name, channelName }) {
|
|
|
|
return path.join(process.env.NEXTCLOUD_FOLDER, `${url}-${skewered(`${name} - ${channelName}`)}.mp3`)
|
|
|
|
}
|
|
|
|
|
2024-02-19 23:16:29 +00:00
|
|
|
function checkLastSong({ name, channelName }) {
|
|
|
|
const curSong = readFileSync(path.join(__dirname, "last_song.txt"), 'utf8');
|
|
|
|
|
|
|
|
return (curSong === `${name} - ${channelName}`);
|
|
|
|
}
|
|
|
|
|
|
|
|
function writeLastSong({ name, channelName }) {
|
|
|
|
writeFileSync(path.join(__dirname, "last_song.txt"), `${name} - ${channelName}`, 'utf8');
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2024-02-19 22:36:23 +00:00
|
|
|
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]);
|
|
|
|
|
2024-02-19 23:16:29 +00:00
|
|
|
if (checkLastSong({
|
|
|
|
name: trackData.name,
|
|
|
|
channelName: trackData.artist.name
|
|
|
|
})) {
|
|
|
|
console.log("Last song check failed");
|
|
|
|
return null;
|
|
|
|
} else {
|
|
|
|
console.log("Last song check succeeded, writing file");
|
|
|
|
writeLastSong({
|
|
|
|
name: trackData.name,
|
|
|
|
channelName: trackData.artist.name
|
|
|
|
});
|
|
|
|
}
|
2024-02-19 22:36:23 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
|
2024-02-19 23:16:29 +00:00
|
|
|
if (video == null) return null;
|
|
|
|
|
2024-02-19 22:36:23 +00:00
|
|
|
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();
|