-
Notifications
You must be signed in to change notification settings - Fork 2
/
transfer_playlist.js
262 lines (222 loc) · 10.8 KB
/
transfer_playlist.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
// Created by Kevin Thai
// The script will take in a URL of a YouTube playlist and it will save it into
// a Spotify playlist for you. It requires a YouTube playlist (public/unlisted)
// and login information for Spotify (note: that information won't be saved).
// This will take in videos that are in a format that is mostly used for
// YouTube Music. The videos would be from the artist themselves with the
// name of the song as the title of the video.
// Ex. Title: "Treasure", Channel: "Bruno Mars" or
// Title: "Treasure", Channel: "Bruno Mars - Topic"
// let playlistName;
// let link;
// let spotifyEmail;
// let spotifyPass;
// let failedTransfers = [];
const reader = require('readline-sync');
/**
* Gets information from the user and stores it into a dictionary.
*/
function getInformationFromUser() {
let playlistName = reader.question("Enter a name for the playlist: ");
let link = reader.question('Enter your YouTube playlist link: ');
console.log("Please enter your Spotify login. Don't worry, it won't be saved elsewhere.");
let spotifyEmail = reader.question('Enter your Spotify email/username: ');
let spotifyPass = reader.question('Enter your Spotify password: ', { hideEchoBack: true});
let spotifyPassTest;
spotifyPassTest = reader.question('Re-enter your Spotify password: ', { hideEchoBack: true});
while(spotifyPass != spotifyPassTest)
{
console.log("The passwords are different! Try re-entering again...");
spotifyPass = reader.question('Enter your Spotify password: ', { hideEchoBack: true});
spotifyPassTest = reader.question('Re-enter your Spotify password: ', { hideEchoBack: true});
}
let spotifyPassEncrypt = "";
for(index=0; index<spotifyPass.length; index++) {
spotifyPassEncrypt = spotifyPassEncrypt + "*";
}
console.log("Your information: ");
console.log("[Playlist name]: " + playlistName);
console.log("[Playlist link]: " + link);
console.log("[Spotify username/email]: " + spotifyEmail);
console.log("[Spotify password]: " + spotifyPassEncrypt);
if(reader.keyInYN("Is the information correct?") == false) {
exit()
}
return {
name: playlistName,
link: link,
email: spotifyEmail,
password: spotifyPass
}
}
const puppeteer = require('puppeteer');
const { userInfo } = require('os');
var listSong = [];
var listChannel = [];
let totalNum;
/**
* This function takes in a URL of a YouTube playlist and will save each
* individual song and its artist into an array.
* @param {dictionary} info - information given from the user
*/
async function transferPlaylist(info) {
ready = false;
console.log("\n");
console.log("Opening browser...\n");
const browser = await puppeteer.launch();
const page = await browser.newPage();
// Configure the navigation timeout
await page.setDefaultNavigationTimeout(0);
// checks the link that user provided
try {
if(info.link.includes("https://www.youtube.com") == false) {
throw new Error("Not valid link.");
}
await page.goto(info.link);
}
catch (err) {
console.log("Invalid link. Either have the playlist be public/unlisted");
console.log("Remember to have the link be like this 'https://www.youtube.com...'");
return process.exit(1);
}
await page.setViewport({
width: 1200,
height: 800
});
await page.evaluate( ()=> {
window.scrollBy(0,10000);
});
await new Promise(r => setTimeout(r, 600));
console.log("Time to copy info!\n");
// saves the number of videos/songs in the playlist
const [el] = await page.$x('/html/body/ytd-app/div[1]/ytd-page-manager/ytd-browse[3]/ytd-playlist-header-renderer/div/div[2]/div[1]/div/div[1]/div[1]/ytd-playlist-byline-renderer/div/yt-formatted-string[1]');
const txtNumber= await el.getProperty('text');
let nameNumber = await txtNumber.jsonValue();
nameNumber = JSON.stringify(nameNumber);
nameNumber = nameNumber.replace('{"runs":[{"text":"','');
nameNumber = nameNumber.replace(' videos"}]}', '');
totalNum = parseInt(nameNumber);
let indexTotalNum = totalNum;
// the for-loop saves the title and channel from each song in the playlist
var index;
for (index =1; index < indexTotalNum + 1; index++) {
// saves the name of the song
try {
const[el2] = await page.$x('/html/body/ytd-app/div/ytd-page-manager/ytd-browse/ytd-two-column-browse-results-renderer/div[1]/ytd-section-list-renderer/div[2]/ytd-item-section-renderer/div[3]/ytd-playlist-video-list-renderer/div[3]/ytd-playlist-video-renderer[' + index + ']/div[2]/a/div/h3/span');
const txtSong = await el2.getProperty('textContent');
nameSong = await txtSong.jsonValue();
nameSong = JSON.stringify(nameSong);
nameSong = nameSong.replace(/\\n/g,'');
nameSong = nameSong.replace(/ +/g, '').replace(/"+/g,'');
nameSong = nameSong.replace('Music Video', 'MV').replace('MV', '');
nameSong = nameSong.replace(/[^ a-zA-Z0-9()]+/g, "").replace(/ +/g, '');
// saves the name of the channel that posted the song (relates to the artist).
const[el3] = await page.$x('/html/body/ytd-app/div/ytd-page-manager/ytd-browse/ytd-two-column-browse-results-renderer/div[1]/ytd-section-list-renderer/div[2]/ytd-item-section-renderer/div[3]/ytd-playlist-video-list-renderer/div[3]/ytd-playlist-video-renderer[' + index + ']/div[2]/a/div/ytd-video-meta-block/div[1]/div[1]/ytd-channel-name/div/div/yt-formatted-string/a');
const txtChannel = await el3.getProperty('text');
let nameChannel = await txtChannel.jsonValue();
nameChannel = JSON.stringify(nameChannel);
nameChannel = nameChannel.replace(/"+/g,'').replace(' - Topic', '').replace('OFFICIAL', '').replace('Official','');
nameChannel = nameChannel.replace("YouTube Channel", '');
nameChannel = nameChannel.replace("BANGTANTV", "BTS"); // This is a special case for BTS.
nameChannel = nameChannel.replace(/[^ a-zA-Z0-9()]+/g, "").replace(/ +/g, '');
if(nameChannel.length == 0 || nameSong.length == 0)
{
continue;
}
else
{
console.log(nameSong, nameChannel);
listSong.push(nameSong);
listChannel.push(nameChannel);
}
}
// A deleted video has been found.
catch (err)
{
console.log("<Found deleted video..>");
continue;
}
}
console.log(listSong);
console.log(listChannel);
await browser.close();
await makeSpotifyPlaylist(info);
}
/**
* This function will login with Spotify details and create a new playlist.
* After a new playlist is created, it will save the songs from the YouTube
* playlist into this new playlist on Spotify.
* @param {dictionary} userInfo - information given from the user
*/
async function makeSpotifyPlaylist(userInfo) {
let numSuccess=0;
console.log("Now transferring...");
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.setViewport({
width: 1200,
height: 800
});
// Configure the navigation timeout
await page.setDefaultNavigationTimeout(0);
await page.goto('https://accounts.spotify.com/en/login?continue=https:%2F%2Fopen.spotify.com%2F');
// It will input the login details. The program won't save the login details.
const inputUser = await page.$x('/html/body/div[1]/div[2]/div/form/div[1]/div/input');
await inputUser[0].type(userInfo.email);
const inputPass = await page.$x('/html/body/div[1]/div[2]/div/form/div[2]/div/input');
await inputPass[0].type(userInfo.password);
const buttonLogin = await page.$x('/html/body/div[1]/div[2]/div/form/div[4]/div[2]/button');
await buttonLogin[0].click();
await page.waitFor(7000);
// if can click on this successfully, it has logged in.
try {
// This will create a new, empty playlist.
await page.click('button.fcdf941c8ffa7d0878af0a4f04aa05bb-scss');
}
catch(err) {
browser.close
console.log("Failed to login Spotify. Invalid username or password.");
return process.exit(1);
}
const inputPlaylistName = await page.$x('/html/body/div[4]/div/div[3]/div/div[1]/div/div/input');
await inputPlaylistName[0].type(playlistName);
const buttonCreate = await page.$x('/html/body/div[4]/div/div[3]/div/div[2]/div[2]/button');
await buttonCreate[0].click();
const buttonSearch = await page.$x('/html/body/div[4]/div/div[2]/div[2]/nav/ul/li[2]/a');
await buttonSearch[0].click();
// This for-loop will search the song in Spotify. Then, it will add it to the playlist.
let index;
for (index = 0; index < totalNum; index++) {
const inputSong = await page.$x('/html/body/div[4]/div/div[2]/div[1]/header/div[3]/div/div/input');
await page.goto('https://open.spotify.com/search/'+ listSong[index]+ '%20' + listChannel[index]);
await page.waitFor(3000);
try {
const foundSong = await page.$x('/html/body/div[4]/div/div[2]/div[4]/div[1]/div/div[2]/div/div/div[2]/div/div/div/section[2]/div/div[2]/div[1]/div/div/div[3]');
await foundSong[0].click({button:'right'});
await page.waitFor(3000);
const buttonAdd = await page.$x('/html/body/div[4]/div/nav[1]/div[4]');
await buttonAdd[0].click();
await page.waitFor(3000);
const buttonPlaylist = await page.$x('/html/body/div[4]/div/div[3]/div/div[4]/div/div/div[2]/div[1]/div/div/div/div');
await buttonPlaylist[0].click();
await page.waitFor(3000);
console.log("Added " + listSong[index] + ' - ' + listChannel[index]);
numSuccess += 1;
}
catch(err) {
console.log("Failed to transfer " + listSong[index] + " - " + listChannel[index]);
failedTransfers.push(listSong[index] + " - " + listChannel[index]);
continue;
}
}
await browser.close();
// display the results
console.log("Sucessfully added " + numSuccess + "/" + totalNum + " songs!");
console.log("Failed transfers: ");
for(index=0; index<failedTransfers.length; index++){
console.log(failedTransfers[index]);
}
return process.exit(0);
}
informationFromUser = getInformationFromUser();
transferPlaylist(informationFromUser);