Musescore Free PDF Downloader

An automatic and free score downloader for musescore

  1. // ==UserScript==
  2. // @name Musescore Free PDF Downloader
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.0
  5. // @author malatia
  6. // @match https://musescore.com/*/*
  7. // @icon https://www.google.com/s2/favicons?sz=64&domain=musescore.com
  8. // @grant GM_addStyle
  9. // @require https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js
  10. // @require https://cdnjs.cloudflare.com/ajax/libs/pdf-lib/1.16.0/pdf-lib.min.js
  11. // @description An automatic and free score downloader for musescore
  12. // @license MIT
  13. // ==/UserScript==
  14.  
  15. // Idea from MuseScore Download By flancast90
  16.  
  17. let DEBUG = true
  18.  
  19. GM_addStyle(`.glowbutton {
  20. --glow-color: rgb(176, 252, 255);
  21. --glow-spread-color: rgba(123, 251, 255, 0.781);
  22. --enhanced-glow-color: rgb(206, 255, 255);
  23. --btn-color: rgb(61, 127, 136);
  24. border: 0.25em solid var(--glow-color);
  25. padding: 1em 1em;
  26. color: var(--glow-color);
  27. font-size: 15px;
  28. font-weight: bold;
  29. background-color: var(--btn-color);
  30. border-radius: 1em;
  31. outline: none;
  32. box-shadow: 0 0 1em 0.25em var(--glow-color),
  33. 0 0 4em 1em var(--glow-spread-color),
  34. inset 0 0 0.75em 0.25em var(--glow-color);
  35. text-shadow: 0 0 0.5em var(--glow-color);
  36. position: relative;
  37. transition: all 0.3s;
  38. }
  39.  
  40. .glowbutton::after {
  41. pointer-events: none;
  42. content: "";
  43. position: absolute;
  44. top: 120%;
  45. left: 0;
  46. height: 100%;
  47. width: 100%;
  48. background-color: var(--glow-spread-color);
  49. filter: blur(2em);
  50. opacity: 0.7;
  51. transform: perspective(1.5em) rotateX(35deg) scale(1, 0.6);
  52. }
  53.  
  54. .glowbutton:hover {
  55. color: var(--btn-color);
  56. background-color: var(--glow-color);
  57. box-shadow: 0 0 1em 0.25em var(--glow-color),
  58. 0 0 4em 2em var(--glow-spread-color),
  59. inset 0 0 0.75em 0.25em var(--glow-color);
  60. }
  61.  
  62. .glowbutton:active {
  63. box-shadow: 0 0 0.6em 0.25em var(--glow-color),
  64. 0 0 2.5em 2em var(--glow-spread-color),
  65. inset 0 0 0.5em 0.25em var(--glow-color);
  66. }
  67. `)
  68.  
  69. async function img_to_canvas_to_bytes(img) {
  70. return new Promise((resolve, reject) => {
  71. // Création d'un canvas pour dessiner l'image
  72. const canvas = document.createElement('canvas');
  73. const ctx = canvas.getContext('2d');
  74.  
  75. // Chargement de l'image dans le canvas
  76. img.crossOrigin = 'Anonymous'; // Permet d'accéder à l'image cross-origin
  77. img.onload = () => {
  78. canvas.width = img.width;
  79. canvas.height = img.height;
  80. ctx.drawImage(img, 0, 0);
  81.  
  82. // Conversion du contenu du canvas en données PNG
  83. const pngBytes = canvas.toDataURL('image/png').split(',')[1];
  84. resolve(pngBytes);
  85. };
  86. img.onerror = () => {
  87. reject(new Error('Échec du chargement de l\'image.'));
  88. };
  89. img.src = img.src; // Déclenche le chargement de l'image
  90. });
  91. }
  92.  
  93.  
  94.  
  95. async function fetchAndAssemblePDF(urls) {
  96. // Création d'un nouveau document PDF
  97. const pdfDoc = await PDFLib.PDFDocument.create();
  98.  
  99. // Tableau pour stocker les documents PNG générés
  100. const pngDocs = [];
  101.  
  102. // Parcours de chaque URL de fichier img
  103. console.log("Avant boucle urls")
  104.  
  105. for (const url of urls) {
  106.  
  107. console.log("url = " + url)
  108. // Récupération du fichier img à partir de l'URL
  109. const response = await fetch(url);
  110. console.log(response)
  111. const imgText = await response.text();
  112.  
  113. if (url.includes(".svg")) {
  114. const img = new Image();
  115. const imgLoaded = new Promise((resolve, reject) => {
  116. img.onload = resolve;
  117. img.onerror = reject;
  118. });
  119. // Création d'un blob à partir du texte SVG
  120. const svgBlob = new Blob([imgText], { type: 'image/svg+xml' });
  121.  
  122. // Création d'une image à partir du blob SVG
  123. img.src = URL.createObjectURL(svgBlob);
  124.  
  125. // Attente du chargement complet de l'image
  126. try {
  127. // Attendre le chargement complet de l'image
  128. await imgLoaded;
  129. console.log("Image SVG chargée avec succès");
  130. } catch (error) {
  131. console.error("Erreur lors du chargement de l'image SVG:", error);
  132. continue; // Passe à l'itération suivante dans la boucle
  133. }
  134.  
  135. // Création d'un canvas pour dessiner l'image SVG
  136. const canvas = document.createElement('canvas');
  137. canvas.width = img.width;
  138. canvas.height = img.height;
  139. const ctx = canvas.getContext('2d');
  140. ctx.drawImage(img, 0, 0);
  141.  
  142. // Conversion du canvas en format PNG
  143. const pngBytes = canvas.toDataURL('image/png').split(',')[1];
  144.  
  145. // Incorporation du PNG dans le document PDF
  146. const pngDoc = await pdfDoc.embedPng(pngBytes);
  147. pngDocs.push(pngDoc); // Ajout du document PNG au tableau
  148. }
  149.  
  150. else if (url.includes(".png")) {
  151. console.log("Dans la partie PNG");
  152. const img = new Image();
  153. const imgLoaded = new Promise((resolve, reject) => {
  154. img.onload = resolve;
  155. img.onerror = reject;
  156. });
  157.  
  158. // Chargement de l'image à partir de l'URL
  159. img.src = url;
  160. // Promesse basée sur le chargement complet de l'image
  161. try {
  162. // Attendre le chargement complet de l'image
  163. await imgLoaded;
  164. console.log("Image PNG chargée avec succès");
  165. } catch (error) {
  166. console.error("Erreur lors du chargement de l'image PNG:", error);
  167. continue; // Passe à l'itération suivante dans la boucle
  168. }
  169.  
  170. const pngBytes = await img_to_canvas_to_bytes(img)
  171.  
  172. // Incorporation de l'image PNG dans le document PDF
  173. const pngDoc = await pdfDoc.embedPng(pngBytes);
  174. pngDocs.push(pngDoc);
  175. }
  176. }
  177.  
  178. // Parcours des documents PNG pour les ajouter au document PDF
  179. for (const pngDoc of pngDocs) {
  180. const page = pdfDoc.addPage([pngDoc.width, pngDoc.height]);
  181. page.drawImage(pngDoc, {
  182. x: 0,
  183. y: 0,
  184. width: pngDoc.width,
  185. height: pngDoc.height,
  186. });
  187. }
  188.  
  189. // Enregistrement du document PDF
  190. const pdfBytes = await pdfDoc.save();
  191.  
  192. // Téléchargement du PDF
  193. const blob = new Blob([pdfBytes], { type: 'application/pdf' });
  194. const link = document.createElement('a');
  195. link.href = window.URL.createObjectURL(blob);
  196. let title = document.title.replace(" ", "_") + ".pdf"
  197. link.download = title;
  198. link.click();
  199. }
  200.  
  201. let scrolled = 0;
  202. let urls = [];
  203. let height;
  204. let scrollHeight;
  205. let scrollContainer
  206.  
  207.  
  208.  
  209. $(document).ready(async function () {
  210. setTimeout(() => {
  211. // ICI ON PARLE DU CONTENANT AVEC SCROLL
  212. scrollContainer = parseInt(document.getElementById('jmuse-scroller-component').scrollHeight);
  213. // ICI ON PARLE DES IMAGES
  214. height = parseInt(document.getElementsByClassName('KfFlO')[0].height);
  215. scrollHeight = (scrollContainer - (scrollContainer % height));
  216. // On cherche le bouton downlad de base pour le remplacer par le nôtre
  217. const btns = [...document.getElementsByTagName("button")]
  218. btns.filter(el => {
  219. const val = el.attributes.getNamedItem("name")?.value
  220. return val == "download"
  221. }).forEach(el => {
  222. const type = el.attributes.getNamedItem("name").value
  223. const fakeEl = el.cloneNode(true)
  224. // fakeEl.style.border = "2px #0dbc79 solid"
  225. fakeEl.value = "Download Free PDF"
  226. fakeEl.classList.add("glowbutton")
  227. // fakeEl.style.webkitBoxShadow = "0px 0px 127px 65px rgba(45,255,196,0.8);"
  228. // fakeEl.style.mozBoxShadow = "0px 0px 127px 65px rgba(45,255,196,0.8);;"
  229. // fakeEl.style.BoxShadow = "0px 0px 127px 65px rgba(45,255,196,0.8);"
  230. fakeEl.class =
  231. fakeEl.onclick = download_pdf
  232. el.parentNode.replaceChild(fakeEl, el)
  233. })
  234. }, 2000)
  235.  
  236. // function get_lazy_imgs() {
  237. // // Fonction récursive pour récupérer les images
  238. // while (scrolled < scrollHeight) {
  239. // // Attendre un peu avant de récupérer les images
  240. // setTimeout(() => {
  241. // // Faire défiler la fenêtre
  242. // console.log("Scrolled : " + scrolled)
  243. // console.log("scrollHeight : " + scrollHeight)
  244. // scrollContainer.scrollTop = scrolled;
  245. // scrolled += height;
  246. // if (DEBUG) console.log("Get_lazy")
  247. // // Récupérer les images
  248. // let images = document.getElementsByClassName('KfFlO');
  249. // if (images.length > 0) {
  250. // urls.push(images[images.length - 1].src);
  251. // }
  252. // }, 1000);
  253.  
  254.  
  255. // }
  256.  
  257. // }
  258.  
  259. function get_lazy_imgs() {
  260. console.log("Début get_lazy_images");
  261. return new Promise((resolve, reject) => {
  262. // Fonction récursive pour récupérer les images
  263. function fetchImages() {
  264.  
  265. scrollContainer = document.getElementById('jmuse-scroller-component');
  266. scrollHeight = scrollContainer.scrollHeight;
  267.  
  268. if (scrolled < scrollHeight) {
  269. // Faire défiler la fenêtre
  270. scrollContainer.scrollTop = scrolled;
  271. scrolled += height;
  272.  
  273. // Attendre un peu avant de récupérer les images
  274. setTimeout(() => {
  275. // Récupérer les images
  276. const images = document.getElementsByClassName('KfFlO');
  277. if (images.length > 0) {
  278. // Ajouter les URLs des images au tableau
  279. urls.push(images[images.length - 1].src);
  280. }
  281.  
  282. // Rappel récursif jusqu'à ce que le scroll atteigne la fin
  283. fetchImages();
  284. }, 1000);
  285. } else {
  286. // Résoudre la promesse avec les URLs des images
  287. resolve(urls);
  288. console.log("Fin get_lazy_images");
  289. }
  290. }
  291.  
  292. // Début de la récupération des images
  293. fetchImages();
  294. });
  295. }
  296.  
  297. async function download_pdf() {
  298. console.log("Avant get_lazy_images");
  299. await get_lazy_imgs(); // Attendre la récupération de toutes les URLs d'images
  300. console.log("Après get_lazy_images");
  301. console.log("Avant fetchAndAssemble");
  302. await fetchAndAssemblePDF(urls); // Utiliser les URLs pour générer le PDF
  303. console.log("Après fetchAndAssemble");
  304. scrolled = 0; // Réinitialiser le défilement
  305. }
  306.  
  307.  
  308.  
  309. });