-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.html
More file actions
277 lines (261 loc) · 12 KB
/
index.html
File metadata and controls
277 lines (261 loc) · 12 KB
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
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
<!DOCTYPE html>
<html lang="en"> <!-- data-theme attribute will be added here by JS -->
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Playlist Transfer</title>
<link rel="stylesheet" href="style.css">
<!-- Modern Google Identity Services (GIS) library -->
<script src="https://accounts.google.com/gsi/client" async defer></script>
<!-- GAPI library for YouTube API calls -->
<script src="https://apis.google.com/js/api.js" async defer></script>
</head>
<body>
<div class="container">
<header>
<div class="theme-switch-wrapper">
<!-- From Uiverse.io by RiccardoRapelli -->
<label class="theme-switch">
<input id="theme-checkbox" type="checkbox" />
<div class="slider round">
<div class="sun-moon">
<svg id="moon-dot-1" class="moon-dot" viewBox="0 0 100 100">
<circle cx="50" cy="50" r="50"></circle>
</svg>
<svg id="moon-dot-2" class="moon-dot" viewBox="0 0 100 100">
<circle cx="50" cy="50" r="50"></circle>
</svg>
<svg id="moon-dot-3" class="moon-dot" viewBox="0 0 100 100">
<circle cx="50" cy="50" r="50"></circle>
</svg>
<svg id="light-ray-1" class="light-ray" viewBox="0 0 100 100">
<circle cx="50" cy="50" r="50"></circle>
</svg>
<svg id="light-ray-2" class="light-ray" viewBox="0 0 100 100">
<circle cx="50" cy="50" r="50"></circle>
</svg>
<svg id="light-ray-3" class="light-ray" viewBox="0 0 100 100">
<circle cx="50" cy="50" r="50"></circle>
</svg>
<svg id="cloud-1" class="cloud-dark" viewBox="0 0 100 100">
<circle cx="50" cy="50" r="50"></circle>
</svg>
<svg id="cloud-2" class="cloud-dark" viewBox="0 0 100 100">
<circle cx="50" cy="50" r="50"></circle>
</svg>
<svg id="cloud-3" class="cloud-dark" viewBox="0 0 100 100">
<circle cx="50" cy="50" r="50"></circle>
</svg>
<svg id="cloud-4" class="cloud-light" viewBox="0 0 100 100">
<circle cx="50" cy="50" r="50"></circle>
</svg>
<svg id="cloud-5" class="cloud-light" viewBox="0 0 100 100">
<circle cx="50" cy="50" r="50"></circle>
</svg>
<svg id="cloud-6" class="cloud-light" viewBox="0 0 100 100">
<circle cx="50" cy="50" r="50"></circle>
</svg>
</div>
<div class="stars">
<svg id="star-1" class="star" viewBox="0 0 20 20">
<path
d="M 0 10 C 10 10,10 10 ,0 10 C 10 10 , 10 10 , 10 20 C 10 10 , 10 10 , 20 10 C 10 10 , 10 10 , 10 0 C 10 10,10 10 ,0 10 Z">
</path>
</svg>
<svg id="star-2" class="star" viewBox="0 0 20 20">
<path
d="M 0 10 C 10 10,10 10 ,0 10 C 10 10 , 10 10 , 10 20 C 10 10 , 10 10 , 20 10 C 10 10 , 10 10 , 10 0 C 10 10,10 10 ,0 10 Z">
</path>
</svg>
<svg id="star-3" class="star" viewBox="0 0 20 20">
<path
d="M 0 10 C 10 10,10 10 ,0 10 C 10 10 , 10 10 , 10 20 C 10 10 , 10 10 , 20 10 C 10 10 , 10 10 , 10 0 C 10 10,10 10 ,0 10 Z">
</path>
</svg>
<svg id="star-4" class="star" viewBox="0 0 20 20">
<path
d="M 0 10 C 10 10,10 10 ,0 10 C 10 10 , 10 10 , 10 20 C 10 10 , 10 10 , 20 10 C 10 10 , 10 10 , 10 0 C 10 10,10 10 ,0 10 Z">
</path>
</svg>
</div>
</div>
</label>
</div>
<h1 id="home-link" class="clickable">Playlist Transfer</h1>
<p id="header-subtitle">Transfer your playlists between services.<br>Check out the <a
href="https://github.com/R-Besson/playlist-transfer" target="_blank">GitHub Repository</a><br>Made
by <a href="https://github.com/R-Besson" target="_blank">r-besson</a></p>
</header>
<main id="view-source-select" class="view visible">
<h2>Step 1: Select Source</h2>
<div class="form-group">
<label for="source-service">Source Service:</label>
<select id="source-service">
<option value="spotify">Spotify</option>
<option value="youtube">YouTube</option>
<option value="applemusic">Apple Music</option>
<option value="text">Text</option>
</select>
</div>
<div class="form-group" id="source-url-container">
<label for="source-url">Playlist URL:</label>
<input type="text" id="source-url" placeholder="Paste your playlist URL here">
<p class="field-note">Note: The source playlist must be set to <strong>public</strong>.</p>
</div>
<div class="form-group" id="source-text-container" style="display: none;">
<label for="source-text">Playlist Text:</label>
<textarea id="source-text" rows="10"
placeholder="Paste your song list here, one per line (e.g., Artist - Title)"></textarea>
<p class="field-note">Provide a list of songs, one per line. The format "Artist - Title" is recommended
for best results.</p>
</div>
<button id="btn-source-next">Next →</button>
</main>
<main id="view-am-fetch" class="view">
<h2>Step 1.5: Fetch Apple Music Data</h2>
<div class="instructions">
<p>Action Required:</p>
<ol>
<li>Open a new tab and go to <a href="https://music.apple.com" target="_blank">music.apple.com</a>
and log in.</li>
<li>Open the Developer Console (F12 or Ctrl+Shift+I).</li>
<li>Copy the code below and paste it into the console, then press Enter.</li>
<li>A pop-up will appear on the page. Click the "Copy Data" button.</li>
<li>Return to this tab, paste the copied data into the text box below, and click Continue.</li>
</ol>
</div>
<div id="am-fetch-code-container" class="code-block" style="margin-bottom: 1.5rem;">
<button class="copy-button">Copy Code</button>
<pre id="am-fetch-code"></pre>
</div>
<div class="form-group">
<label for="am-paste-data">Paste Playlist Data Here:</label>
<textarea id="am-paste-data" rows="5"
placeholder="Paste the data you copied from Apple Music here..."></textarea>
</div>
<button id="btn-am-fetch-continue">Continue →</button>
<button id="btn-am-fetch-back">← Back</button>
</main>
<main id="view-destination-select" class="view">
<h2>Step 2: Select Destination Service</h2>
<div class="service-cards">
<div class="card" data-service="spotify">Spotify</div>
<div class="card" data-service="youtube">YouTube</div>
<div class="card" data-service="applemusic">Apple Music</div>
<div class="card" data-service="text">Text</div>
</div>
<button id="btn-dest-back">← Back</button>
</main>
<main id="view-destination-setup" class="view">
<h2 id="setup-title">Step 3: Setup Credentials & Login</h2>
<div id="source-creds-section" style="display:none;">
<h4 id="source-creds-title">Source Credentials</h4>
<div id="source-instructions-container"></div>
<div id="source-creds-form"></div>
<hr>
</div>
<div id="destination-creds-section">
<h4 id="destination-creds-title">Destination Credentials & Login</h4>
<div id="youtube-destination-warning" class="field-note warning" style="display: none;">
<strong>Important:</strong> Due to YouTube's very strict API quota, transfers are processed slowly
to maximize success. Large playlists (~100+ songs) may not fully complete in one day.
</div>
<div class="form-group">
<label for="new-playlist-name">New Playlist Name</label>
<input type="text" id="new-playlist-name" placeholder="Enter a name for your new playlist">
</div>
<div id="fast-transfer-container" class="setting-switch-wrapper" style="display: none;">
<span class="switch-label">Enable Fast Transfer (High Quota Use)</span>
<label class="theme-switch">
<input type="checkbox" id="fast-transfer-checkbox">
<div class="slider round"></div>
</label>
</div>
<div id="destination-instructions-container"></div>
<div id="destination-creds-form"></div>
</div>
<button id="btn-login-and-transfer">Authenticate and Prepare</button>
<button id="btn-creds-back">← Back</button>
</main>
<main id="view-progress" class="view">
<h2 id="progress-title">Transfer in Progress...</h2>
<div class="loader"></div>
<p id="progress-text">Initializing...</p>
<ul id="track-log"></ul>
<button id="btn-start-over" style="display: none;">Start Over</button>
<button id="btn-continue-to-youtube-auth" style="display: none;">Sign in with Google to Continue</button>
</main>
<div id="templates" style="display: none;">
<div id="instructions-spotify-source-template">
<div class="instructions">
<p>How to get your Spotify Client ID:</p>
<ol>
<li>Go to the <a href="https://developer.spotify.com/dashboard/" target="_blank">Spotify
Developer Dashboard</a> and log in.</li>
<li>Click <strong>Create App</strong>. Give it any name and description.</li>
<li>Click <strong>Edit Settings</strong>. In <strong>Redirect URIs</strong>, you MUST add the
app's address (e.g., <code>https://r-besson.github.io/playlist-transfer/</code>).</li>
</ol>
</div>
</div>
<div id="instructions-youtube-source-template">
<div class="instructions">
<p>How to get your YouTube API Key:</p>
<ol>
<li>Go to the <a href="https://console.cloud.google.com/" target="_blank">Google Cloud
Console</a> and create a project.</li>
<li>Go to <strong>APIs & Services</strong> > <strong>Library</strong>. Search for and
<strong>Enable</strong> the <strong>YouTube Data API v3</strong>.
</li>
<li>Go to <strong>APIs & Services</strong> > <strong>Credentials</strong>. Click <strong>+
CREATE CREDENTIALS</strong> and choose <strong>API Key</strong>.</li>
</ol>
</div>
</div>
<div id="instructions-youtube-destination-template">
<div class="instructions">
<p>How to get your YouTube API Key & Client ID:</p>
<ol>
<li>Go to the <a href="https://console.cloud.google.com/" target="_blank">Google Cloud
Console</a> and create a project.</li>
<li>Go to <strong>APIs & Services</strong> > <strong>Library</strong>. Search for and
<strong>Enable</strong> the <strong>YouTube Data API v3</strong>.
</li>
<li>Go to <strong>APIs & Services</strong> > <strong>OAuth consent screen</strong> >
<strong>Audience</strong> and add your email as a <strong>Test User</strong>.
</li>
<li>Go to <strong>APIs & Services</strong> > <strong>Credentials</strong> and click <strong>+
CREATE CREDENTIALS</strong>.</li>
<li>Create an <strong>API Key</strong>.</li>
<li>Create an <strong>OAuth client ID</strong> for a <strong>Web application</strong> and add
this page's address (e.g., <code>https://r-besson.github.io/playlist-transfer/</code>) to
both <strong>Authorized JavaScript origins</strong> and <strong>Authorized redirect
URIs</strong>.</li>
</ol>
</div>
</div>
<div id="instructions-applemusic-destination-template">
<div class="instructions">
<p>Apple Music Transfer (Advanced Console Method):</p>
<ol>
<li>In your web browser, open a new tab and go to <a href="https://music.apple.com"
target="_blank">music.apple.com</a> and log in. <strong>This is required.</strong></li>
<li>Open the Developer Tools (press F12 or Ctrl+Shift+i).</li>
<li>Go to the <strong>Network</strong> tab. In the filter box, type <code>amp-api</code>.</li>
<li>Refresh the page. Click on any request in the list and go to <strong>Headers</strong> >
<strong>Request Headers</strong>.
</li>
<li>Keep clicking other requests until you see the following headers:</li>
<li>Copy the entire value for the <strong>authorization</strong> header (it starts with "Bearer
...").</li>
<li>Copy the entire value for the <strong>media-user-token</strong> header.</li>
</ol>
</div>
</div>
</div>
</div>
<!-- App Scripts -->
<script src="api.js"></script>
<script src="app.js"></script>
</body>
</html>