· 5 years ago · Jun 19, 2020, 06:32 PM
1// ==UserScript==
2// @name Plug.dj YT API Key workaround
3// @namespace https://plug.dj?refuid=4613422
4// @version 1.5.0
5// @author WiBla (contact.wibla@gmail.com)
6// @description This script will add a button next to "import/create playlist" that allows you to add videos without searching for them
7// @downloadURL https://gist.github.com/WiBla/ad1aa9a98949c624cd2886c1a25b5feb/raw/8d83fc7bb1ac77f8ff9494023991e21e5599ec56/yt-workaround.user.js
8// @updateURL https://gist.github.com/WiBla/ad1aa9a98949c624cd2886c1a25b5feb/raw/8d83fc7bb1ac77f8ff9494023991e21e5599ec56/yt-workaround.user.js
9// @include *://plug.dj/*
10// @include *://*.plug.dj/*
11// @exclude *://*.plug.dj/_/*
12// @exclude *://*.plug.dj/@/*
13// @exclude *://*.plug.dj/!/*
14// @exclude *://*.plug.dj/about
15// @exclude *://*.plug.dj/ba
16// @exclude *://*.plug.dj/forgot-password
17// @exclude *://*.plug.dj/founders
18// @exclude *://*.plug.dj/giftsub/*
19// @exclude *://*.plug.dj/jobs
20// @exclude *://*.plug.dj/legal
21// @exclude *://*.plug.dj/merch
22// @exclude *://*.plug.dj/partners
23// @exclude *://*.plug.dj/plot
24// @exclude *://*.plug.dj/privacy
25// @exclude *://*.plug.dj/purchase
26// @exclude *://*.plug.dj/subscribe
27// @exclude *://*.plug.dj/team
28// @exclude *://*.plug.dj/terms
29// @exclude *://*.plug.dj/press
30// @grant none
31// @run-at document-end
32// ==/UserScript==
33
34/* global $, gapi, API, _, require */
35(function() {
36 'use strict';
37
38 // Because plug.dj hides the interface while loading, this is necessary
39 // We can't just use document.readyState ($.ready)
40 function plugReady() {
41 return typeof API !== 'undefined' && API.enabled && typeof jQuery !== 'undefined' && typeof require !== 'undefined';
42 }
43 function autoReload() {
44 if (!plugReady()) {
45 setTimeout(autoReload, 200);
46 } else {
47 init();
48 }
49 }
50 function init() {
51 var pl = {}, media = {}, APIKey = localStorage.getItem('yt-api-key');
52
53 if (APIKey == null) {
54 APIKey = 'AIzaSyAXA_oKOYXbTgbLPFZ0WdBi81URHaMw60c';
55 API.chatLog('⚠ YT-workaround is using the default API key, this is not recommended as everyone uses the same and it is very limited. If you know how, you should definitely get your own and configure it with /key [YOUR KEY HERE]');
56 } else {
57 API.chatLog('YT-workaround is using a custom key, you rock ?');
58 }
59 window.gapi.client.setApiKey(APIKey);
60
61 function convert_time(duration) {
62 var a = duration.match(/\d+/g),
63 indexOfH = duration.indexOf('H'),
64 indexOfM = duration.indexOf('M'),
65 indexOfS = duration.indexOf('S');
66
67 switch(true) {
68 case indexOfM >= 0 && indexOfH == -1 && indexOfS == -1:
69 a = [0, a[0], 0];
70 break;
71
72 case indexOfH >= 0 && indexOfM == -1:
73 a = [a[0], 0, a[1]];
74 break;
75
76 case indexOfH >= 0 && indexOfM == -1 && indexOfS == -1:
77 a = [a[0], 0, 0];
78 break;
79 }
80
81 duration = 0;
82
83 switch(a.length) {
84 case 1:
85 duration = duration + parseInt(a[0]);
86 break;
87
88 case 2:
89 duration = duration + parseInt(a[0]) * 60;
90 duration = duration + parseInt(a[1]);
91 break;
92
93 case 3:
94 duration = duration + parseInt(a[0]) * 3600;
95 duration = duration + parseInt(a[1]) * 60;
96 duration = duration + parseInt(a[2]);
97 break;
98 }
99
100 return duration;
101 }
102 function completeMedia() {
103 $.ajax({
104 url: `https://www.googleapis.com/youtube/v3/videos?id=${media.cid}&key=${gapi.config.get().client.apiKey}&part=snippet,contentDetails`,
105 type: 'GET',
106 success: (data) => {
107 media.author = data.items[0].snippet.title.split('-');
108
109 // Video's title contains a "-" so parse it
110 if (media.author.length > 1) {
111 media.title = media.author[1].trim();
112 media.author = media.author[0].trim();
113 } else { // Otherwise, use the channel's name as author..
114 media.title = media.author[0].trim();
115 media.author = data.items[0].snippet.channelTitle;
116 }
117
118 media.image = data.items[0].snippet.thumbnails.default.url;
119 media.duration = convert_time(data.items[0].contentDetails.duration);
120 media.format = 1;
121 media.id = -1;
122
123 addMedia();
124 },
125 error: (err) => {
126 if (err.responseJSON.error.message.indexOf('Daily Limit') > -1) {
127 alert('The current API Key has exceeded its quota.\nTry setting your own by typing "/key [YOUR KEY HERE]" in the chat');
128 } else {
129 console.error('[YT-WORKAROUND]', err);
130 alert("Something went wrong with YouTube. You can check the console for more info.");
131 }
132 }
133 });
134 }
135 function addMedia() {
136 $.ajax({
137 url: `/_/playlists/${pl.id}/media/insert`,
138 type: 'POST',
139 data: JSON.stringify({
140 media: [media],
141 "append": false
142 }),
143 contentType: 'application/json',
144 success: () => alert('Media added!\nIf you don\'t see it, try switching playlists or refreshing.'),
145 error: (err) => {
146 if (err.responseJSON.status === 'maxItems') {
147 alert('This playlist is full!');
148 } else alert('Sorry, the media couldn\'t be added.');
149 }
150 });
151 }
152 function extractCID(cid) {
153 return cid.replace(/(?:https?:)?(?:\/\/)?(?:[0-9A-Z-]+\.)?(?:youtu\.be\/|youtube(?:-nocookie)?\.com\/\S*?[^\w\s-])((?!videoseries)[\w-]{11})(?=[^\w-]|$)(?![?=&+%\w.-]*(?:['"][^<>]*>|<\/a>))[?=&+%\w.-]*/gi, '$1');
154 }
155 function askCID() {
156 media.cid = prompt(`Importing in ${pl.name}:\nVideo's link:`);
157 if (!!media.cid === false) return;
158
159 media.cid = extractCID(media.cid);
160 completeMedia(media);
161 }
162
163 // let found = false;
164 // function findPlaylist() {
165 // if (found) return askCID();
166 // pl.name = prompt('In which playlist do you wish to add the song?\nName must be exactly the same');
167 // if (!!pl.name === false) return;
168
169 // $.ajax({
170 // url: '/_/playlists',
171 // success: (data) => {
172 // data.data.forEach((playlist, i, a) => {
173 // if (playlist.name === pl.name) {
174 // if (playlist.count === 200) {
175 // alert(`${pl.name} is full!`);
176 // } else {
177 // pl.id = playlist.id;
178 // found = true;
179 // askCID();
180 // }
181 // }
182 // if (i + 1 >= a.length && !found) {
183 // if (confirm(`"${pl.name}" couldn't be found. Do you whish to add the media in "${playlist.name}"?`)) {
184 // pl = playlist;
185 // askCID();
186 // }
187 // }
188 // });
189 // }
190 // });
191 // }
192
193 var $grabBtn = $('<div id="playlist-add-button" class="button" title="YouTube Grab+">'+
194 '<i class="fa fa-plus-square"></i>'+
195 '</div>');
196
197 // $grabBtn.click(function(event) {
198 // found = event.ctrlKey ? false : found;
199 // findPlaylist();
200 // });
201
202 var playlists = _.find(require.s.contexts._.defined, (m)=>m&&m.activeMedia);
203 $grabBtn.click(function() {
204 playlists.models.forEach((model) => {
205 model = model.toJSON();
206 if (model.visible) {
207 if (model.count === 200) {
208 alert(`${model.name} is full!`);
209 } else {
210 pl = model;
211 askCID();
212 }
213 }
214 });
215 });
216
217 $('.playlist-buttons-container .playlist-edit-group').prepend($grabBtn);
218
219 function chatCmd(cmd) {
220 cmd = cmd.split(' ');
221
222 if (cmd[0] === '/key' && cmd.length == 2) {
223 if (/AIza[0-9A-z\-_]{35}/.test(cmd[1]) === false) {
224 API.chatLog('This is not a valid YT API Key!');
225 return;
226 }
227
228 window.gapi.client.setApiKey(cmd[1]);
229 localStorage.setItem('yt-api-key', cmd[1]);
230 API.chatLog('Custom API key saved! You\'re a pro ?');
231 } else {
232 API.chatLog('Please use this format: /key [KEY] (do not include the square brackets obviously)');
233 }
234 }
235 API.on(API.CHAT_COMMAND, chatCmd);
236
237 API.chatLog('YT-workaround fully loaded! Refresh to de-activate it.');
238 }
239
240 autoReload();
241})();