LinkTube

Replaces an embedded video with a link to the video page.

  1. // ==UserScript==
  2. // @name LinkTube
  3. // @version 2017.11.23
  4. // @description Replaces an embedded video with a link to the video page.
  5. // @author sebaro
  6. // @namespace http://isebaro.com/linktube
  7. // @license GPL version 3 or any later version; http://www.gnu.org/copyleft/gpl.html
  8. // @icon https://raw.githubusercontent.com/sebaro/linktube/master/linktube.png
  9. // @include *
  10. // @grant GM_xmlhttpRequest
  11. // @grant GM.xmlHttpRequest
  12. // ==/UserScript==
  13.  
  14.  
  15. /*
  16.  
  17. Copyright (C) 2011 - 2017 Sebastian Luncan
  18.  
  19. This program is free software: you can redistribute it and/or modify
  20. it under the terms of the GNU General Public License as published by
  21. the Free Software Foundation, either version 3 of the License, or
  22. (at your option) any later version.
  23.  
  24. This program is distributed in the hope that it will be useful,
  25. but WITHOUT ANY WARRANTY; without even the implied warranty of
  26. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  27. GNU General Public License for more details.
  28.  
  29. You should have received a copy of the GNU General Public License
  30. along with this program. If not, see <http://www.gnu.org/licenses/>.
  31.  
  32. Website: http://isebaro.com/linktube
  33. Contact: http://isebaro.com/contact
  34.  
  35. */
  36.  
  37.  
  38. (function() {
  39.  
  40.  
  41. // ==========Variables========== //
  42.  
  43. // Userscript
  44. var userscript = 'LinkTube';
  45.  
  46. // Contact
  47. var contact = 'http://isebaro.com/contact';
  48.  
  49. // Warning
  50. var warning = 'Couldn\'t get the video link. Please report it <a href="' + contact + '">here</a>.';
  51.  
  52. // Options
  53. var option = {'secure': true};
  54.  
  55.  
  56. // ==========Fixes========== //
  57.  
  58. // Don't run on frames or iframes
  59. if (window.top != window.self) return;
  60.  
  61.  
  62. // ==========Functions========== //
  63.  
  64. function createMyElement (type, content) {
  65. var obj = document.createElement(type);
  66. if (type == 'div') {
  67. if (content) obj.innerHTML = content;
  68. }
  69. return obj;
  70. }
  71.  
  72. function getMyElement (element, get, tag) {
  73. var obj;
  74. if (get == 'parent') obj = element.parentNode;
  75. else if (get == 'source') obj = element.src;
  76. else if (get == 'name') obj = element.name;
  77. else if (get == 'value') obj = element.value;
  78. else if (get == 'children') obj = element.getElementsByTagName(tag);
  79. return obj;
  80. }
  81.  
  82. function modifyMyElement (obj, type, content, clear) {
  83. if (type == 'div') {
  84. if (content) obj.innerHTML = content;
  85. }
  86. if (clear) {
  87. if (obj.hasChildNodes()) {
  88. while (obj.childNodes.length >= 1) {
  89. obj.removeChild(obj.firstChild);
  90. }
  91. }
  92. }
  93. }
  94.  
  95. function styleMyElement (obj, styles) {
  96. for (var property in styles) {
  97. if (styles.hasOwnProperty(property)) obj.style[property] = styles[property];
  98. }
  99. }
  100.  
  101. function appendMyElement (parent, child) {
  102. if (parent == 'body') document.body.appendChild(child);
  103. else parent.appendChild(child);
  104. }
  105.  
  106. function removeMyElement (parent, child) {
  107. if (parent == 'body') document.body.removeChild(child);
  108. else parent.removeChild(child);
  109. }
  110.  
  111. function replaceMyElement (parent, orphan, child) {
  112. parent.replaceChild(orphan, child);
  113. }
  114.  
  115. function YouTube (url, target) {
  116.  
  117. var ytVideoTitle;
  118. var ytVideoLength;
  119. function ytVideoInfo(ytPageSource) {
  120. /* Get Video Title */
  121. ytVideoTitle = ytPageSource.match(/"title":"(.*?)","lengthSeconds"/);
  122. ytVideoTitle = (ytVideoTitle) ? ytVideoTitle[1] : null;
  123. if (!ytVideoTitle) {
  124. ytVideoTitle = ytPageSource.match(/document.title\s*=\s*"(.*?)"/);
  125. ytVideoTitle = (ytVideoTitle) ? ytVideoTitle[1] : null;
  126. }
  127. if (!ytVideoTitle) {
  128. ytVideoTitle = ytPageSource.match(/meta\s+itemprop="name"\s+content="(.*?)"/);
  129. ytVideoTitle = (ytVideoTitle) ? ytVideoTitle[1] : null;
  130. }
  131. if (!ytVideoTitle) {
  132. ytVideoTitle = ytPageSource.match(/meta\s+property="og:title"\s+content="(.*?)"/);
  133. ytVideoTitle = (ytVideoTitle) ? ytVideoTitle[1] : null;
  134. }
  135. if (ytVideoTitle) {
  136. ytVideoTitle = ytVideoTitle.replace(/&quot;/g, '\'').replace(/&#34;/g, '\'').replace(/"/g, '\'');
  137. ytVideoTitle = ytVideoTitle.replace(/&#39;/g, '\'').replace(/'/g, '\'');
  138. ytVideoTitle = ytVideoTitle.replace(/&amp;/g, 'and').replace(/&/g, 'and');
  139. ytVideoTitle = ytVideoTitle.replace(/\?/g, '').replace(/[#:\*]/g, '-').replace(/\//g, '-');
  140. ytVideoTitle = ytVideoTitle.replace(/^\s+|\s+$/, '').replace(/\.+$/g, '');
  141. ytVideoTitle = ytVideoTitle.replace(/^YouTube\s-\s/, '');
  142. }
  143. /* Get Video Length */
  144. ytVideoLength = ytPageSource.match(/"lengthSeconds":"(.*?)"/);
  145. ytVideoLength = (ytVideoLength) ? ytVideoLength[1] : null;
  146. if (ytVideoLength) {
  147. ytVideoLength = new Date(ytVideoLength * 1000).toISOString().substr(11, 8).replace(/00:0?/, '');
  148. }
  149. else {
  150. ytVideoLength = ytPageSource.match(/meta\s+itemprop="duration"\s+content="(.*?)"/);
  151. ytVideoLength = (ytVideoLength) ? ytVideoLength[1].replace('PT', '').replace('M', ':').replace('S', '') : null;
  152. }
  153. }
  154. try {
  155. GM.xmlHttpRequest({
  156. method: 'GET',
  157. url: url,
  158. onload: function(response) {
  159. if (response.readyState === 4 && response.status === 200) {
  160. ytVideoInfo(response.responseText);
  161. ytVideoList = ytVideoTitle + ' (' + ytVideoLength + ')<br>' + '<a href="' + url + '">' + url + '</a>';
  162. modifyMyElement (target, 'div', ytVideoList, false);
  163. }
  164. else {
  165. ytVideoList = '<a href="' + url + '">' + url + '</a>';
  166. modifyMyElement (target, 'div', ytVideoList, false);
  167. }
  168. }
  169. });
  170. }
  171. catch(e) {
  172. try {
  173. GM_xmlhttpRequest({
  174. method: 'GET',
  175. url: url,
  176. onload: function(response) {
  177. if (response.readyState === 4 && response.status === 200) {
  178. ytVideoInfo(response.responseText);
  179. ytVideoList = ytVideoTitle + ' (' + ytVideoLength + ')<br>' + '<a href="' + url + '">' + url + '</a>';
  180. modifyMyElement (target, 'div', ytVideoList, false);
  181. }
  182. else {
  183. ytVideoList = '<a href="' + url + '">' + url + '</a>';
  184. modifyMyElement (target, 'div', ytVideoList, false);
  185. }
  186. }
  187. });
  188. }
  189. catch(e) {
  190. ytVideoList = '<a href="' + url + '">' + url + '</a>';
  191. modifyMyElement (target, 'div', ytVideoList, false);
  192. }
  193. }
  194. }
  195.  
  196. function embedMyLinks (element) {
  197. var elements;
  198. if (element == 'iframe') elements = iframeElements;
  199. else if (element == 'object') elements = objectElements;
  200. else if (element == 'embed') elements = embedElements;
  201. var child, parent, video, param, name;
  202. var foundSite;
  203. var linkID, videoID, videoLink, videoURL;
  204. var myScriptLogo = [];
  205. myScriptLogo[element] = [];
  206. var myScriptMess = [];
  207. myScriptMess[element] = [];
  208. var myLinkWindow = [];
  209. myLinkWindow[element] = [];
  210. for (var e = elements.length - 1; e >= 0; e--) {
  211. foundSite = false;
  212. child = elements[e];
  213. parent = getMyElement (child, 'parent', '');
  214. if (element == 'iframe' || element == 'embed') {
  215. video = getMyElement (child, 'source', '');
  216. }
  217. else if (element == 'object') {
  218. params = getMyElement (child, 'children', 'param');
  219. for (var p = 0; p < params.length; p++) {
  220. name = getMyElement (params[p], 'name', '');
  221. if (name == 'movie' || name == 'src') {
  222. video = getMyElement (params[p], 'value', '');
  223. if (!video) video = getMyElement (params[p], 'source', '');
  224. }
  225. }
  226. }
  227. if (video) {
  228. for (var l = 0; l < linkParsers.length; l++) {
  229. if (video.match(linkParsers[l]['source'])) {
  230. foundSite = true;
  231. linkID = l;
  232. break;
  233. }
  234. }
  235. }
  236. if (foundSite) {
  237. myScriptLogo[element][e] = createMyElement ('div', userscript);
  238. styleMyElement (myScriptLogo[element][e], {margin: '0px auto', padding: '10px', color: '#FFFFFF', fontSize: '20px', textAlign: 'center', textShadow: '#000000 -1px -1px 1px'});
  239. myScriptMess[element][e] = createMyElement ('div', '');
  240. myLinkWindow[element][e] = createMyElement ('div', '');
  241. appendMyElement (myLinkWindow[element][e], myScriptLogo[element][e]);
  242. appendMyElement (myLinkWindow[element][e], myScriptMess[element][e]);
  243. var childStyles = child.getAttribute('style');
  244. if (childStyles) {
  245. childStyles = childStyles.replace('absolute', 'relative');
  246. myLinkWindow[element][e].setAttribute('style', childStyles);
  247. styleMyElement (myLinkWindow[element][e], {backgroundColor: '#F4F4F4'});
  248. }
  249. else styleMyElement (myLinkWindow[element][e], {width: '100%', height: '100%', backgroundColor: '#F4F4F4'});
  250. styleMyElement (parent, {padding: '0px', height: '100%'});
  251. replaceMyElement(parent, myLinkWindow[element][e], child);
  252. videoID = video.match(linkParsers[linkID]['pattern']);
  253. videoID = (videoID) ? videoID[1] : null;
  254. if (videoID) {
  255. videoURL = linkParsers[linkID]['link'] + videoID;
  256. if (!option['secure']) videoURL = videoURL.replace(/^https/, 'http');
  257. styleMyElement (myScriptMess[element][e], {border: '3px solid #F4F4F4', margin: '0px auto', padding: '10px', backgroundColor: '#FFFFFF', color: '#00C000', textAlign: 'center', fontSize: '16px'});
  258. if (videoURL.indexOf('youtube.com') != -1) {
  259. YouTube(videoURL, myScriptMess[element][e]);
  260. }
  261. else {
  262. videoLink = '<a href="' + videoURL + '">' + videoURL + '</a>';
  263. modifyMyElement (myScriptMess[element][e], 'div', videoLink, false);
  264. }
  265. }
  266. else {
  267. styleMyElement (myScriptMess[element][e], {border: '3px solid #F4F4F4', margin: '0px auto', padding: '10px', backgroundColor: '#FFFFFF', color: '#AD0000', textAlign: 'center', fontSize: '16px'});
  268. modifyMyElement (myScriptMess[element][e], 'div', warning, false);
  269. }
  270. }
  271. }
  272.  
  273. }
  274.  
  275.  
  276. // ==========Websites========== //
  277.  
  278. /* Parsers */
  279. var linkParsers = [
  280. {'source': 'youtube(.com|-nocookie.com)/embed/(videoseries|\\?list)', 'pattern': 'list=(.*?)(&|$)', 'link': 'https://www.youtube.com/playlist?list='},
  281. {'source': 'youtube(.com|-nocookie.com)/embed/', 'pattern': '/embed/(.*?)(\\?|&|$)', 'link': 'https://www.youtube.com/watch?v='},
  282. {'source': 'youtube(.com|-nocookie.com)/v/', 'pattern': '/v/(.*?)(\\?|&|$)', 'link': 'https://www.youtube.com/watch?v='},
  283. {'source': 'dailymotion.com/embed/', 'pattern': '/video/(.*?)$', 'link': 'https://www.dailymotion.com/video/'},
  284. {'source': 'dailymotion.com/swf/(?!video)', 'pattern': '/swf/(.*?)$', 'link': 'https://www.dailymotion.com/video/'},
  285. {'source': 'dailymotion.com/swf/(?=video)', 'pattern': '/video/(.*?)$', 'link': 'https://www.dailymotion.com/video/'},
  286. {'source': 'vimeo.com/video/', 'pattern': '/video/(.*?)(\\?|&|$)', 'link': 'https://vimeo.com/'},
  287. {'source': 'vimeo.com/moogaloop', 'pattern': '/moogaloop.swf\\?clip_id=(.*?)(&|$)', 'link': 'https://vimeo.com/'},
  288. {'source': 'metacafe.com/embed/', 'pattern': '/embed/(.*?)/', 'link': 'https://www.metacafe.com/watch/'},
  289. {'source': 'metacafe.com/fplayer/', 'pattern': '/fplayer/(.*?)/', 'link': 'https://www.metacafe.com/watch/'},
  290. {'source': 'funnyordie.com/embed/', 'pattern': '/embed/(.*?)$', 'link': 'https://www.funnyordie.com/videos/'},
  291. {'source': 'vk.com/video', 'pattern': 'video_ext.php\\?(.*?)$', 'link': 'http://vk.com/video_ext.php?'},
  292. {'source': 'hostname=www.twitch.tv', 'pattern': 'channel=(.*?)(&|$)', 'link': 'http://www.twitch.tv/'}
  293. ];
  294.  
  295. /* IFrame */
  296. var iframeElements = getMyElement (document, 'children', 'iframe');
  297. if (iframeElements.length > 0 ) embedMyLinks ('iframe');
  298.  
  299. /* Object */
  300. var objectElements = getMyElement (document, 'children', 'object');
  301. if (objectElements.length > 0 ) embedMyLinks ('object');
  302.  
  303. /* Embed */
  304. var embedElements = getMyElement (document, 'children', 'embed');
  305. if (embedElements.length > 0 ) embedMyLinks ('embed');
  306.  
  307.  
  308. })();