From 974aa4706a100bb8234b03222c70220d1306aebf Mon Sep 17 00:00:00 2001 From: MeowcaTheoRange Date: Mon, 19 Feb 2024 16:36:23 -0600 Subject: [PATCH] first commit --- .env.example | 20 +++++++++ .gitignore | 3 ++ index.js | 124 +++++++++++++++++++++++++++++++++++++++++++++++++++ package.json | 19 ++++++++ 4 files changed, 166 insertions(+) create mode 100644 .env.example create mode 100644 .gitignore create mode 100644 index.js create mode 100644 package.json diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..b5f62b4 --- /dev/null +++ b/.env.example @@ -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/ \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9a30b15 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +node_modules/ +package-lock.json +.env \ No newline at end of file diff --git a/index.js b/index.js new file mode 100644 index 0000000..3dbbf5b --- /dev/null +++ b/index.js @@ -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(); \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..53c1cbd --- /dev/null +++ b/package.json @@ -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" + } +}