Implement song tagging

This commit is contained in:
MeowcaTheoRange 2024-02-21 10:25:06 -06:00
parent e70ce4a9ca
commit eb9263972e
2 changed files with 78 additions and 30 deletions

107
index.js
View file

@ -1,5 +1,5 @@
require('dotenv').config();
const { Readable } = require("stream");
const { Readable, PassThrough } = require("stream");
const { finished } = require("stream/promises");
const { searchMusics } = require('fix-esm').require("node-youtube-music");
const { GetListByKeyword } = require("youtube-search-api");
@ -9,9 +9,10 @@ const path = require("path");
const { readFileSync, writeFileSync } = require("fs");
const io = require('socket.io-client');
const { program } = require('commander');
const NodeID3 = require('node-id3');
function pathGenerator({ url, name, channelName }) {
return path.join(process.env.NEXTCLOUD_FOLDER, `${url}-${skewered(`${name} - ${channelName}`)}.mp3`)
return `${url}-${skewered(`${name} - ${channelName}`)}.mp3`;
}
function checkLastSong({ name, channelName }) {
@ -28,6 +29,7 @@ function writeLastSong({ name, channelName }) {
async function getVideo({ msName, msArtist }) {
let trackData;
let tags;
if (msName != null && msArtist != null) {
trackData = {
@ -40,6 +42,17 @@ async function getVideo({ msName, msArtist }) {
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]);
tags = {
title: trackData.name,
artist: trackData.artist.name,
album: trackData.album["#text"],
APIC: "./cover.png",
userDefinedUrl: [{
description: "Last.FM page",
url: trackData.url
}]
}
}
if (checkLastSong({
@ -68,8 +81,6 @@ async function getVideo({ msName, msArtist }) {
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"}
@ -91,7 +102,11 @@ async function getVideo({ msName, msArtist }) {
selectedVideo.channelName = selectedVideo.channelName.replace(/\s-\sTopic/ig, "");
return selectedVideo;
return {
tags,
...selectedVideo,
albumCover: trackData.image.at(-1)["#text"]
};
}
async function checkNextcloud({ url, name, channelName }, nextcloudClient) {
@ -100,13 +115,12 @@ async function checkNextcloud({ url, name, channelName }, nextcloudClient) {
return await nextcloudClient.exists(fileName);
}
async function downloadFromCobalt({ url, name, channelName }) {
async function downloadFromCobalt({ url }) {
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,
@ -142,23 +156,37 @@ async function downloadFromCobalt({ url, name, channelName }) {
break;
}
const { body } = await fetch(processorURL);
const body = Buffer.from(await fetch(processorURL).then(x => x.arrayBuffer()));
return {
fileStream: Readable.fromWeb(body),
url,
name,
channelName
};
return body;
}
async function uploadToNextcloud({ fileStream, url, name, channelName }, nextcloudClient) {
await nextcloudClient.createDirectory(process.env.NEXTCLOUD_FOLDER, {
async function downloadAlbumCover({ albumCover }) {
const body = Buffer.from(await fetch(albumCover).then(x => x.arrayBuffer()));
return body;
}
function applyTags({ fileStream, tags }) {
const body = NodeID3.update(tags, fileStream);
return body;
}
async function uploadToNextcloud({ fileStream, url, name, channelName, tags, albumCover }, nextcloudClient) {
await nextcloudClient.createDirectory(path.join(process.env.NEXTCLOUD_FOLDER, tags.album), {
recursive: true,
});
const fileBufferStream = new PassThrough().end(fileStream);
const albumBufferStream = new PassThrough().end(albumCover);
const fileName = pathGenerator({ url, name, channelName });
await finished(fileStream.pipe(nextcloudClient.createWriteStream(fileName)));
await finished(fileBufferStream.pipe(nextcloudClient.createWriteStream(path.join(process.env.NEXTCLOUD_FOLDER, fileName))));
await finished(albumBufferStream.pipe(nextcloudClient.createWriteStream(path.join(process.env.NEXTCLOUD_FOLDER, "cover.png"))));
return fileName;
}
@ -182,7 +210,6 @@ async function main() {
msName: options.song,
msArtist: options.artist
});
console.log(video);
if (video == null) return dismantle(socket);
@ -201,23 +228,43 @@ async function main() {
password: process.env.NOTIFICATION_SERVER_PASSWORD
})
const cobalt = await downloadFromCobalt(video);
console.log(cobalt);
const albumCover = await downloadAlbumCover(video);
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}`,
message: `Cobalt download starting - ${video.name} from ${video.channelName}`,
password: process.env.NOTIFICATION_SERVER_PASSWORD
})
const cobalt = await downloadFromCobalt(video);
socket.emit('nodeMessage', {
message: `Cobalt download finished - ${video.name} from ${video.channelName}`,
password: process.env.NOTIFICATION_SERVER_PASSWORD
});
const taggedMusic = applyTags({
fileStream: cobalt,
tags: video.tags
});
socket.emit('nodeMessage', {
message: `Nextcloud upload running - ${video.name} from ${video.channelName}`,
password: process.env.NOTIFICATION_SERVER_PASSWORD
});
const nextcloud = await uploadToNextcloud(
{
...video,
fileStream: taggedMusic,
albumCover: albumCover
},
nextcloudClient
);
socket.emit('nodeMessage', {
message: `Nextcloud upload finished - ${tags.name} from ${tags.channelName}`,
password: process.env.NOTIFICATION_SERVER_PASSWORD
})
return dismantle(socket);
}

View file

@ -14,6 +14,7 @@
"dotenv": "^16.4.4",
"express": "^4.18.2",
"fix-esm": "^1.0.1",
"node-id3": "^0.2.6",
"node-notifier": "^10.0.1",
"node-youtube-music": "^0.10.3",
"skewered": "^1.0.0",