Redirect YouTube Shorts

Seamlessly redirect YouTube Shorts to the regular video player WITHOUT a page reload

  1. // ==UserScript==
  2. // @name Redirect YouTube Shorts
  3. // @version 2.0.0
  4. // @description Seamlessly redirect YouTube Shorts to the regular video player WITHOUT a page reload
  5. // @run-at document-start
  6. // @inject-into page
  7. // @match https://www.youtube.com/*
  8. // @exclude https://*.youtube.com/live_chat*
  9. // @exclude https://*.youtube.com/embed*
  10. // @exclude https://*.youtube.com/tv*
  11. // @exclude https:/tv.youtube.com/*
  12. // @icon https://www.google.com/s2/favicons?sz=64&domain=youtube.com
  13. // @author Fznhq
  14. // @namespace https://github.com/fznhq
  15. // @homepageURL https://github.com/fznhq/userscript-collection
  16. // @license GNU GPLv3
  17. // ==/UserScript==
  18.  
  19. (function () {
  20. const shortIdRegex = /(?:shorts\/)([^#\&\?]*)/;
  21.  
  22. /**
  23. * @param {string} url
  24. * @returns {string | null}
  25. */
  26. function parseId(url) {
  27. url = url.match(shortIdRegex);
  28. return url && url[1];
  29. }
  30.  
  31. const shortId = parseId(location.href);
  32. if (shortId) return location.replace("/watch?v=" + shortId);
  33.  
  34. /**
  35. * @param {object} obj
  36. * @param {string} target
  37. * @returns {any}
  38. */
  39. function dig(obj, target) {
  40. if (obj && typeof obj == "object") {
  41. if (target in obj) return obj[target];
  42. for (const k in obj) {
  43. const result = dig(obj[k], target);
  44. if (result !== undefined) return result;
  45. }
  46. }
  47. }
  48.  
  49. /**
  50. * @param {HTMLAnchorElement} element
  51. * @param {string} key
  52. * @returns {object}
  53. */
  54. function findData(element, key) {
  55. let data;
  56.  
  57. while (element && !(data = dig(element.data, key))) {
  58. element = element.parentElement;
  59. }
  60.  
  61. return data || {};
  62. }
  63. /**
  64. * @param {string} id
  65. */
  66. function redirectShort(id) {
  67. const element = document.querySelector(`#contents a[href*="${id}"]`);
  68. const onTap = findData(element, "onTap");
  69. const metadata = dig(onTap, "webCommandMetadata");
  70.  
  71. if (onTap.innertubeCommand && metadata) {
  72. metadata.url = `/watch?v=${id}`;
  73. metadata.webPageType = "WEB_PAGE_TYPE_WATCH";
  74. delete onTap.innertubeCommand.reelWatchEndpoint;
  75. onTap.innertubeCommand.watchEndpoint = { videoId: id };
  76. }
  77. }
  78.  
  79. function handleShortClick(/** @type {MouseEvent} */ ev) {
  80. /** @type {HTMLElement} */
  81. const target = ev.target;
  82.  
  83. if (target.closest) {
  84. const short = target.closest("a[href*=short]");
  85. if (short) redirectShort(parseId(short.href));
  86. }
  87. }
  88.  
  89. window.addEventListener("click", handleShortClick, true);
  90. })();