Advertisement
Angry_Ed

Untitled

May 23rd, 2018
457
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 8.13 KB | None | 0 0
  1. // ==UserScript==
  2. // @name Google Drive Video Player for Synchtube
  3. // @namespace gdpsptv
  4. // @description Play Google Drive videos on Synchtube
  5. // @include http://synchtu.be/r/*
  6. // @include https://synchtu.be/r/*
  7. // @include http://www.synchtu.be/r/*
  8. // @include https://www.synchtu.be/r/*
  9. // @grant unsafeWindow
  10. // @grant GM_xmlhttpRequest
  11. // @grant GM.xmlHttpRequest
  12. // @connect docs.google.com
  13. // @run-at document-end
  14. // @version 1.7.0
  15. // ==/UserScript==
  16.  
  17. try {
  18. function debug(message) {
  19. try {
  20. unsafeWindow.console.log('[Drive]', message);
  21. } catch (error) {
  22. unsafeWindow.console.error(error);
  23. }
  24. }
  25.  
  26. function httpRequest(opts) {
  27. if (typeof GM_xmlhttpRequest === 'undefined') {
  28. // Assume GM4.0
  29. debug('Using GM4.0 GM.xmlHttpRequest');
  30. GM.xmlHttpRequest(opts);
  31. } else {
  32. debug('Using old-style GM_xmlhttpRequest');
  33. GM_xmlhttpRequest(opts);
  34. }
  35. }
  36.  
  37. var ITAG_QMAP = {
  38. 37: 1080,
  39. 46: 1080,
  40. 22: 720,
  41. 45: 720,
  42. 59: 480,
  43. 44: 480,
  44. 35: 480,
  45. 18: 360,
  46. 43: 360,
  47. 34: 360
  48. };
  49.  
  50. var ITAG_CMAP = {
  51. 43: 'video/webm',
  52. 44: 'video/webm',
  53. 45: 'video/webm',
  54. 46: 'video/webm',
  55. 18: 'video/mp4',
  56. 22: 'video/mp4',
  57. 37: 'video/mp4',
  58. 59: 'video/mp4',
  59. 35: 'video/flv',
  60. 34: 'video/flv'
  61. };
  62.  
  63. function getVideoInfo(id, cb) {
  64. var url = 'https://docs.google.com/get_video_info?authuser='
  65. + '&docid=' + id
  66. + '&sle=true'
  67. + '&hl=en';
  68. debug('Fetching ' + url);
  69.  
  70. httpRequest({
  71. method: 'GET',
  72. url: url,
  73. onload: function (res) {
  74. try {
  75. debug('Got response ' + res.responseText);
  76.  
  77. if (res.status !== 200) {
  78. debug('Response status not 200: ' + res.status);
  79. return cb(
  80. 'Google Drive request failed: HTTP ' + res.status
  81. );
  82. }
  83.  
  84. var data = {};
  85. var error;
  86. // Google Santa sometimes eats login cookies and gets mad if there aren't any.
  87. if(/accounts\.google\.com\/ServiceLogin/.test(res.responseText)){
  88. error = 'Google Docs request failed: ' +
  89. 'This video requires you be logged into a Google account. ' +
  90. 'Open your Gmail in another tab and then refresh video.';
  91. return cb(error);
  92. }
  93.  
  94. res.responseText.split('&').forEach(function (kv) {
  95. var pair = kv.split('=');
  96. data[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]);
  97. });
  98.  
  99. if (data.status === 'fail') {
  100. error = 'Google Drive request failed: ' +
  101. unescape(data.reason).replace(/\+/g, ' ');
  102. return cb(error);
  103. }
  104.  
  105. if (!data.fmt_stream_map) {
  106. error = (
  107. 'Google has removed the video streams associated' +
  108. ' with this item. It can no longer be played.'
  109. );
  110.  
  111. return cb(error);
  112. }
  113.  
  114. data.links = {};
  115. data.fmt_stream_map.split(',').forEach(function (item) {
  116. var pair = item.split('|');
  117. data.links[pair[0]] = pair[1];
  118. });
  119. data.videoMap = mapLinks(data.links);
  120.  
  121. cb(null, data);
  122. } catch (error) {
  123. unsafeWindow.console.error(error);
  124. }
  125. },
  126.  
  127. onerror: function () {
  128. var error = 'Google Drive request failed: ' +
  129. 'metadata lookup HTTP request failed';
  130. error.reason = 'HTTP_ONERROR';
  131. return cb(error);
  132. }
  133. });
  134. }
  135.  
  136. function mapLinks(links) {
  137. var videos = {
  138. 1080: [],
  139. 720: [],
  140. 480: [],
  141. 360: []
  142. };
  143.  
  144. Object.keys(links).forEach(function (itag) {
  145. itag = parseInt(itag, 10);
  146. if (!ITAG_QMAP.hasOwnProperty(itag)) {
  147. return;
  148. }
  149.  
  150. videos[ITAG_QMAP[itag]].push({
  151. itag: itag,
  152. contentType: ITAG_CMAP[itag],
  153. link: links[itag]
  154. });
  155. });
  156.  
  157. return videos;
  158. }
  159.  
  160. /*
  161. * Greasemonkey 2.0 has this wonderful sandbox that attempts
  162. * to prevent script developers from shooting themselves in
  163. * the foot by removing the trigger from the gun, i.e. it's
  164. * impossible to cross the boundary between the browser JS VM
  165. * and the privileged sandbox that can run GM_xmlhttpRequest().
  166. *
  167. * So in this case, we have to resort to polling a special
  168. * variable to see if getGoogleDriveMetadata needs to be called
  169. * and deliver the result into another special variable that is
  170. * being polled on the browser side.
  171. */
  172.  
  173. /*
  174. * Browser side function -- sets gdUserscript.pollID to the
  175. * ID of the Drive video to be queried and polls
  176. * gdUserscript.pollResult for the result.
  177. */
  178. function getGoogleDriveMetadata_GM(id, callback) {
  179. debug('Setting GD poll ID to ' + id);
  180. unsafeWindow.gdUserscript.pollID = id;
  181. var tries = 0;
  182. var i = setInterval(function () {
  183. if (unsafeWindow.gdUserscript.pollResult) {
  184. debug('Got result');
  185. clearInterval(i);
  186. var result = unsafeWindow.gdUserscript.pollResult;
  187. unsafeWindow.gdUserscript.pollResult = null;
  188. callback(result.error, result.result);
  189. } else if (++tries > 100) {
  190. // Took longer than 10 seconds, give up
  191. clearInterval(i);
  192. }
  193. }, 100);
  194. }
  195.  
  196. /*
  197. * Sandbox side function -- polls gdUserscript.pollID for
  198. * the ID of a Drive video to be queried, looks up the
  199. * metadata, and stores it in gdUserscript.pollResult
  200. */
  201. function setupGDPoll() {
  202. unsafeWindow.gdUserscript = cloneInto({}, unsafeWindow);
  203. var pollInterval = setInterval(function () {
  204. if (unsafeWindow.gdUserscript.pollID) {
  205. var id = unsafeWindow.gdUserscript.pollID;
  206. unsafeWindow.gdUserscript.pollID = null;
  207. debug('Polled and got ' + id);
  208. getVideoInfo(id, function (error, data) {
  209. unsafeWindow.gdUserscript.pollResult = cloneInto({
  210. error: error,
  211. result: data
  212. }, unsafeWindow);
  213. });
  214. }
  215. }, 1000);
  216. }
  217.  
  218. var TM_COMPATIBLES = [
  219. 'Tampermonkey',
  220. 'Violentmonkey' // https://github.com/calzoneman/sync/issues/713
  221. ];
  222.  
  223. function isTampermonkeyCompatible() {
  224. try {
  225. return TM_COMPATIBLES.indexOf(GM_info.scriptHandler) >= 0;
  226. } catch (error) {
  227. return false;
  228. }
  229. }
  230.  
  231. if (isTampermonkeyCompatible()) {
  232. unsafeWindow.getGoogleDriveMetadata = getVideoInfo;
  233. } else {
  234. debug('Using non-TM polling workaround');
  235. unsafeWindow.getGoogleDriveMetadata = exportFunction(
  236. getGoogleDriveMetadata_GM, unsafeWindow);
  237. setupGDPoll();
  238. }
  239.  
  240. unsafeWindow.console.log('Initialized userscript Google Drive player');
  241. unsafeWindow.hasDriveUserscript = true;
  242. // Checked against GS_VERSION from data.js
  243. unsafeWindow.driveUserscriptVersion = '1.7';
  244. } catch (error) {
  245. unsafeWindow.console.error(error);
  246. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement