205 lines
No EOL
5.8 KiB
JavaScript
205 lines
No EOL
5.8 KiB
JavaScript
require('dotenv').config();
|
|
const { Readable } = require("stream");
|
|
const { finished } = require("stream/promises");
|
|
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");
|
|
const { readFileSync, writeFileSync } = require("fs");
|
|
const io = require('socket.io-client');
|
|
|
|
function pathGenerator({ url, name, channelName }) {
|
|
return path.join(process.env.NEXTCLOUD_FOLDER, `${url}-${skewered(`${name} - ${channelName}`)}.mp3`)
|
|
}
|
|
|
|
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}`, { recursive: true, encoding: 'utf-8' });
|
|
|
|
return null;
|
|
}
|
|
|
|
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]);
|
|
|
|
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
|
|
});
|
|
}
|
|
|
|
let selectedVideo = {
|
|
url: "",
|
|
name: "",
|
|
channelName: ""
|
|
};
|
|
|
|
const musicList = await searchMusics(`${trackData.artist.name} - ${trackData.name}`);
|
|
|
|
const youtubeMusicVideo = musicList.find((song) => {
|
|
return skewered(song.title).includes(skewered(trackData.name));
|
|
});
|
|
|
|
console.log(youtubeMusicVideo);
|
|
|
|
if (youtubeMusicVideo == null) {
|
|
const videoList = await GetListByKeyword(`${trackData.artist.name} - ${trackData.name}`, false, 1, [
|
|
{type: "video"}
|
|
]);
|
|
|
|
const musicVideo = videoList.items[0];
|
|
|
|
selectedVideo = {
|
|
url: musicVideo.id,
|
|
name: musicVideo.title,
|
|
channelName: musicVideo.channelTitle
|
|
};
|
|
}
|
|
else selectedVideo = {
|
|
url: youtubeMusicVideo.youtubeId,
|
|
name: youtubeMusicVideo.title,
|
|
channelName: youtubeMusicVideo.artists[0].name
|
|
}
|
|
|
|
return selectedVideo;
|
|
}
|
|
|
|
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 });
|
|
await finished(fileStream.pipe(nextcloudClient.createWriteStream(fileName)));
|
|
|
|
return fileName;
|
|
}
|
|
|
|
async function main() {
|
|
const socket = io.connect(`http://localhost:${process.env.NOTIFICATION_SERVER_PORT}`, {reconnect: true});
|
|
|
|
socket.on('connect', function (s) {
|
|
console.log('Successfully connected to notification server');
|
|
});
|
|
|
|
const video = await getVideo();
|
|
console.log(video);
|
|
|
|
if (video == null) return dismantle(socket);
|
|
|
|
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 dismantle(socket);
|
|
|
|
socket.emit('nodeMessage', {
|
|
message: `New song found - ${video.name} from ${video.channelName}`,
|
|
password: process.env.NOTIFICATION_SERVER_PASSWORD
|
|
})
|
|
|
|
const cobalt = await downloadFromCobalt(video);
|
|
console.log(cobalt);
|
|
|
|
socket.emit('nodeMessage', {
|
|
message: `Nextcloud upload running - ${cobalt.name} from ${cobalt.channelName}`,
|
|
password: process.env.NOTIFICATION_SERVER_PASSWORD
|
|
});
|
|
|
|
const nextcloud = await uploadToNextcloud(cobalt, nextcloudClient);
|
|
console.log(nextcloud);
|
|
|
|
socket.emit('nodeMessage', {
|
|
message: `Nextcloud upload finished - ${cobalt.name} from ${cobalt.channelName}`,
|
|
password: process.env.NOTIFICATION_SERVER_PASSWORD
|
|
})
|
|
|
|
|
|
|
|
return dismantle(socket);
|
|
}
|
|
|
|
main();
|
|
|
|
function dismantle(socket) {
|
|
socket.disconnect();
|
|
return null;
|
|
} |