first commit
This commit is contained in:
commit
974aa4706a
4 changed files with 166 additions and 0 deletions
20
.env.example
Normal file
20
.env.example
Normal file
|
@ -0,0 +1,20 @@
|
|||
# Last.fm
|
||||
|
||||
LASTFM_INSTANCE= # ws.audioscrobbler.com
|
||||
|
||||
LASTFM_USERNAME= # meowcatheorange
|
||||
LASTFM_API_KEY= # cccccccccccccccccc...
|
||||
LASTFM_SHARED_SECRET= # cccccccccccccccccc...
|
||||
|
||||
# Cobalt
|
||||
|
||||
COBALT_INSTANCE= # co.wuk.sh
|
||||
|
||||
# Nextcloud
|
||||
|
||||
NEXTCLOUD_INSTANCE= # next.abtmtr.link
|
||||
|
||||
NEXTCLOUD_USERNAME= # meowcatheorange
|
||||
NEXTCLOUD_PASSWORD= # ccccc-ccccc-ccccc-ccccc-ccccc
|
||||
|
||||
NEXTCLOUD_FOLDER= # /Music/LastFMDownloader/
|
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
node_modules/
|
||||
package-lock.json
|
||||
.env
|
124
index.js
Normal file
124
index.js
Normal file
|
@ -0,0 +1,124 @@
|
|||
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();
|
19
package.json
Normal file
19
package.json
Normal file
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"name": "lastfm-downloader",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"dotenv": "^16.4.4",
|
||||
"fix-esm": "^1.0.1",
|
||||
"skewered": "^1.0.0",
|
||||
"webdav": "^5.3.2",
|
||||
"youtube-search-api": "^1.2.1"
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue