Crunchy ASS

drag n' drop external ASS subtitles into Crunchyroll WEB player

  1. // ==UserScript==
  2. // @name Crunchy ASS
  3. // @namespace https://github.com/ownadi/crunchy-ass
  4. // @version 2024-08-13
  5. // @description drag n' drop external ASS subtitles into Crunchyroll WEB player
  6. // @author ownadi
  7. // @match https://static.crunchyroll.com/vilos-v2/web/vilos/player.html
  8. // @icon https://www.google.com/s2/favicons?sz=64&domain=crunchyroll.com
  9. // @grant none
  10. // @license WTFPL; http://www.wtfpl.net
  11. // ==/UserScript==
  12.  
  13. (function () {
  14. "use strict";
  15.  
  16. let currentAss = null;
  17. const videoEl = document.querySelector("video");
  18.  
  19. ["dragenter", "dragover", "dragleave", "drop"].forEach((eventName) => {
  20. document.addEventListener(
  21. eventName,
  22. (e) => {
  23. e.preventDefault();
  24. e.stopPropagation();
  25.  
  26. if (eventName === "drop" || eventName === "dragleave") {
  27. videoEl.parentElement.style.border = "none";
  28. } else {
  29. videoEl.parentElement.style.border = "4px #ff640a dashed";
  30. }
  31. },
  32. true
  33. );
  34. });
  35.  
  36. const videoObserver = new MutationObserver(() => {
  37. currentAss?.destroy();
  38. });
  39. videoObserver.observe(videoEl, {
  40. attributes: true,
  41. attributeFilter: ["src"],
  42. });
  43.  
  44. document.addEventListener(
  45. "drop",
  46. async (e) => {
  47. e.stopPropagation();
  48.  
  49. const assScript = await e.dataTransfer.files[0].text();
  50.  
  51. import("https://cdn.jsdelivr.net/npm/assjs@latest/dist/ass.min.js").then(
  52. ({ default: ASS }) => {
  53. currentAss?.destroy();
  54.  
  55. currentAss = new ASS(assScript, videoEl);
  56.  
  57. videoEl.pause();
  58. videoEl.play();
  59. }
  60. );
  61. },
  62. true
  63. );
  64. })();