2020-07-01 22:22:11 +02:00
|
|
|
const Cheerio = require('cheerio');
|
2020-07-02 00:30:07 +02:00
|
|
|
const { promises: fs, createWriteStream } = require('fs');
|
2020-07-01 22:22:11 +02:00
|
|
|
const debug = require('debug')('vk-music-dl');
|
|
|
|
const vm = require('vm');
|
|
|
|
const Request = require('request-promise');
|
2020-07-01 22:23:59 +02:00
|
|
|
const Promise = require('bluebird');
|
|
|
|
const _ = require('lodash');
|
|
|
|
const leven = require('leven');
|
2020-07-02 00:30:07 +02:00
|
|
|
const fetch = require('node-fetch');
|
2020-07-01 22:22:11 +02:00
|
|
|
require('dotenv').config();
|
|
|
|
|
|
|
|
|
|
|
|
async function getSpotifyPlaylist(){
|
|
|
|
const spotifyPlaylistPageContent = await fs.readFile('./test.html');
|
|
|
|
const $ = Cheerio.load(spotifyPlaylistPageContent);
|
|
|
|
|
|
|
|
let playlist;
|
|
|
|
$('script').each(function(i, elem){
|
|
|
|
const content = $(this).html();
|
|
|
|
if(/Spotify.Entity/.test(content)){
|
|
|
|
playlist = vm.runInNewContext(content);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
return playlist;
|
|
|
|
}
|
|
|
|
|
2020-07-01 22:23:59 +02:00
|
|
|
async function searchOnVkMusic(query){
|
2020-07-01 22:22:11 +02:00
|
|
|
return await Request({
|
|
|
|
uri: 'https://api.vk.com/method/audio.search',
|
|
|
|
qs: {
|
|
|
|
v: '5.71',
|
|
|
|
access_token: process.env.ACCESS_TOKEN,
|
|
|
|
q: query,
|
2020-07-01 22:23:59 +02:00
|
|
|
//sort: '2',
|
|
|
|
count: 200,
|
2020-07-01 22:22:11 +02:00
|
|
|
},
|
|
|
|
headers: {
|
|
|
|
'User-Agent': 'KateMobileAndroid/48.2 lite-433 (Android 8.1.0; SDK 27; arm64-v8a; Google Pixel 2 XL; en)',
|
|
|
|
},
|
|
|
|
json: true
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-07-01 22:23:59 +02:00
|
|
|
function matchScore(spotifyMetas, vkmusicMetas){
|
|
|
|
const originalArtistNames = _.map(spotifyMetas.artists, 'name').join(', ').toLowerCase();
|
|
|
|
const originalTitle = spotifyMetas.name.toLowerCase();
|
|
|
|
const originalDuration = Math.round(spotifyMetas.duration_ms/1000);
|
|
|
|
|
|
|
|
const matchArtistScore = 1 - (leven(originalArtistNames, vkmusicMetas.artist.toLowerCase()) / Math.max(originalArtistNames.length, vkmusicMetas.artist.length));
|
|
|
|
const matchTitleScore = 1 - (leven(originalTitle, vkmusicMetas.title.toLowerCase()) / Math.max(originalTitle.length, vkmusicMetas.title.length));
|
|
|
|
const matchDurationScore = 1 - (Math.abs(originalDuration - vkmusicMetas.duration) / originalDuration);
|
|
|
|
debug('matchArtistScore=%f matchTitleScore=%f matchDurationScore=%f', matchArtistScore, matchTitleScore, matchDurationScore);
|
|
|
|
return matchArtistScore + matchTitleScore + matchDurationScore;
|
|
|
|
}
|
|
|
|
|
|
|
|
async function main(i = 0){
|
|
|
|
const playlist = await getSpotifyPlaylist();
|
|
|
|
await Promise.map([playlist.tracks.items[i]], async ({track}) => {
|
|
|
|
const artistNames = _.map(track.artists, 'name').join(', ');
|
|
|
|
debug('%s - %s', track.name, artistNames);
|
|
|
|
const { response } = await searchOnVkMusic(`${artistNames} ${track.name}`);
|
|
|
|
debug('response=%O', response);
|
|
|
|
const bestMatch = _.chain(response.items).map((item) => {
|
|
|
|
item.score = matchScore(track, item);
|
|
|
|
return item;
|
|
|
|
}).sortBy('score').last().value();
|
|
|
|
debug(bestMatch);
|
2020-07-02 00:30:07 +02:00
|
|
|
await fetch(bestMatch.url).then(res => {
|
|
|
|
res.body.pipe(createWriteStream(`./${bestMatch.title} - ${bestMatch.artist}.mp3`));
|
|
|
|
});
|
|
|
|
debug('Done');
|
2020-07-01 22:23:59 +02:00
|
|
|
});
|
|
|
|
// best match for Love Is a Bitch id:456240390
|
|
|
|
//const results = await searchOnVkMusic('Two Feet Love Is a Bitch');
|
|
|
|
//debug(await searchOnVkMusic('The Neighbourhood Daddy Issues'));
|
2020-07-01 22:22:11 +02:00
|
|
|
}
|
|
|
|
|
2020-07-01 22:23:59 +02:00
|
|
|
main(parseInt(process.argv[2]));
|