Musescore Downloader

download pdf or print any sheets!

  1. // ==UserScript==
  2. // @name Musescore Downloader
  3. // @name:zh Musescore 增强脚本
  4. // @version 1.3.0
  5. // @description download pdf or print any sheets!
  6. // @description:zh 去广告+任意下载乐谱
  7. // @author Charlie
  8. // @match https://musescore.com/*
  9. // @namespace https://gf.qytechs.cn/users/890174
  10. // @grant GM_addStyle
  11. // @grant unsafeWindow
  12. // ==/UserScript==
  13.  
  14. GM_addStyle(`
  15. .SUG3q > .hei_M:nth-child(3) > .AFyen:nth-child(2),
  16. .YaB2I > section :nth-last-child(n+2),
  17. aside > div:nth-last-child(2),
  18. aside > div:nth-child(2),
  19. aside > div:nth-child(3),
  20. header > *:not(nav),
  21. .hFMfR > .AFyen,
  22. footer,
  23. ._9aj2,
  24. .p6izg,
  25. .TnQwe,
  26. .U8wvj,
  27. .jxGLy,
  28. .YRlfn,
  29. .YMprF,
  30. .MXIPY {
  31. display: none !important;
  32. }
  33. .dXt6a {
  34. background-color: #e1effe !important;
  35. padding: .3em .8em !important;
  36. border-left: 5px solid #1a4f9f !important;
  37. font-size: 14px !important;
  38. }
  39. .ZvxB2, .AJXCt {
  40. background-color: #e1effe7c !important;
  41. padding: .6em .8em !important;
  42. border-radius: 4px !important;
  43. outline: 1.5px solid #1a4f9f !important;
  44. flex-direction: column !important;
  45. }
  46. aside a {
  47. padding: 0 .3em !important;
  48. transition: background-color ease-out .15s !important;
  49. }
  50. aside a:hover {
  51. background-color: #45f !important;
  52. color: #f3f3ff !important;
  53. }
  54. aside > div:nth-child(5) {
  55. border: none !important;
  56. }
  57. aside > div:nth-child(4),
  58. aside > div:nth-child(5) {
  59. padding: 12px 20px !important;
  60. }
  61. aside > div:nth-child(4) > div {
  62. display: flex !important;
  63. place-items: center !important;
  64. flex-direction: column !important;
  65. }
  66. aside > div:nth-child(4) > div > .Pl3iC {
  67. margin-right: 16px !important;
  68. }
  69. aside > div:nth-child(4) > div.PI5Hd.g1QZl.CgHMj > section:nth-child(2) {
  70. width: 100% !important;
  71. }
  72. .oQdm2 {
  73. max-width: unset !important;
  74. }
  75. aside table {
  76. background-color: #e1effe7c !important;
  77. padding: .3em .6em !important;
  78. border-radius: 3px !important;
  79. outline: 1.5px solid #1a4f9f !important;
  80. }
  81. aside table tr {
  82. padding-bottom: 5px !important;
  83. }
  84. .a0naR,
  85. .Au_pg,
  86. .Al3lQ {
  87. height: 32px !important;
  88. width: 32px !important;
  89. color: transparent !important;
  90. border-radius: 5px !important;
  91. }
  92. .hyzNL {
  93. max-height: unset !important;
  94. }
  95. .dXt6a section {
  96. margin: 0 !important;
  97. }`)
  98.  
  99. setTimeout(() => {
  100. "use strict"
  101. const ZH = navigator.language == "zh-CN"
  102.  
  103. async function download() {
  104. const Window = window.open()
  105. const id = location.pathname.match(/\/[^\/]*$/)[0].slice(1)
  106. const length = unsafeWindow.UGAPP.store.page.data.score.pages_count
  107. Window.document.write(`
  108. <!DOCTYPE html>
  109. <head>
  110. <title>${document.getElementsByTagName('h1')[0].innerText}</title>
  111. <style>
  112. body {
  113. margin: 0;
  114. display: flex;
  115. min-height: 100vh;
  116. flex-direction: column;
  117. place-items: center;
  118. justify-content: center;
  119. color: rgb(49, 63, 78);
  120. background-color: rgb(237, 242, 247);
  121. transform-origin: top;
  122. transition: background-color ease .3s;
  123. will-change: background-color;
  124. overflow-x: scroll;
  125. }
  126.  
  127. svg {
  128. width: 100vw;
  129. height: auto;
  130. display: block;
  131. margin: auto;
  132. }
  133.  
  134. .card {
  135. display: flex;
  136. text-align: center;
  137. place-items: center;
  138. font-size: 1.2em;
  139. width: ${ ZH ? 360 : 480 }px;
  140. box-shadow: 10px 10px 8px rgba(0, 0, 0, 0.07);
  141. padding: 1.5em 2em;
  142. background-color: rgb(255, 255, 255);
  143. border-radius: 8px;
  144. transition: all ease-out .3s;
  145. overflow: hidden;
  146. white-space: nowrap;
  147. }
  148.  
  149. body > * {
  150. animation: 1.5s intro;
  151. }
  152.  
  153. @keyframes intro {
  154. 0%, 20% { opacity: 0; }
  155. 100% { opacity: 1; }
  156. }
  157.  
  158. .card-icon {
  159. display: flex;
  160. flex-direction: column;
  161. place-items: center;
  162. margin-right: 3em;
  163. font-weight: bold;
  164. }
  165.  
  166. .card-text {
  167. flex: 1;
  168. }
  169.  
  170. b {
  171. color: rgb(49, 140, 252);
  172. }
  173.  
  174. .spinner, .spinner * { box-sizing: border-box; }
  175. .spinner {
  176. height: 40px;
  177. width: 40px;
  178. top: calc( -10px * 2 / 3);
  179. margin-left: calc(10px / 3);
  180. margin-bottom: calc(10px / 3);
  181. }
  182. .spinner .sq {
  183. height: 10px;
  184. width: 10px;
  185. top: calc( -10px * 2 / 3);
  186. margin-right: calc(10px / 3);
  187. margin-top: calc(10px / 3);
  188. background: rgb(49, 140, 252);
  189. float: left;
  190. position: relative;
  191. opacity: 0;
  192. animation: spinner 6s infinite;
  193. }
  194. ${Array(9).fill().map((_, i) => `.spinner .sq:nth-child(${i+1}) { animation-delay: calc(300ms * ${8-i}); }`).join("\n")}
  195. .spinner .clear { clear: both; }
  196. @keyframes spinner {
  197. 0% { opacity: 0; }
  198. 5% { opacity: 1; top: 0; }
  199. 50.9% { opacity: 1; top: 0; }
  200. 55.9% { opacity: 0; top: inherit; }
  201. }
  202.  
  203. @media print {
  204. @page {
  205. margin: 0;
  206. }
  207. button {
  208. display: none;
  209. }
  210. svg {
  211. width: 21cm;
  212. height: 29.7cm;
  213. }
  214. }
  215.  
  216. .btn-group {
  217. position: fixed;
  218. left: 32px;
  219. top: 24px;
  220. }
  221. button {
  222. font-size: 1.4em;
  223. font-weight: bold;
  224. box-shadow: 1px 2px 8px rgba(0, 0, 0, 0.1);
  225. padding: .4em ${ ZH ? 1.5 : 0.8 }em;
  226. margin-right: 1.2em;
  227. background-color: rgb(255, 255, 255);
  228. color: rgb(49, 63, 78);
  229. border-radius: 4px;
  230. cursor: pointer;
  231. outline: 1.5px solid rgb(49, 63, 78);
  232. border: none;
  233. transition: all ease-out .2s;
  234. }
  235. button:hover {
  236. background-color: rgb(235, 235, 235);
  237. }
  238. button:active {
  239. outline-color: black;
  240. transform: scale(0.97);
  241. }
  242.  
  243. .hint {
  244. margin: 1em 0;
  245. color: #a7b2be;
  246. }
  247. </style>
  248. </head>
  249. <body>
  250. <div class="card">
  251. <div class="card-icon">
  252. <div class="spinner">
  253. <div class="sq"></div>
  254. <div class="sq"></div>
  255. <div class="sq"></div>
  256. <div class="sq clear"></div>
  257. <div class="sq"></div>
  258. <div class="sq"></div>
  259. <div class="sq clear"></div>
  260. <div class="sq"></div>
  261. <div class="sq"></div>
  262. </div>
  263. <div class="card-icon-text">${ ZH ? "下载中" : "Downloading" }</div>
  264. </div>
  265. <div class="card-text">
  266. ${ ZH ? `已下载 <b id="download-status">0</b> 页,共 <b>${length}</b> 页`
  267. : `<b id="download-status">0</b> page(s) loaded, <b>${length}</b> total` }
  268. </div>
  269. </div>
  270. <span class="hint">${ ZH ? "点击左上方的 <b>打印</b> 按钮,选择 <b>另存为PDF</b> 即可下载乐谱。" : "To download the sheet, click <b>PRINT</b> button on the top left then select <b>Export PDF</b>." }</span>
  271. </body>`)
  272. Window.onbeforeunload = e => {
  273. e.preventDefault()
  274. e.returnValue = "QwQ"
  275. return "awa"
  276. }
  277.  
  278. let data = Array(length).fill(""), cnt = 0
  279.  
  280. async function getData() {
  281. return Promise.all(
  282. data.filter(e => e.length == 0).map((_, i) => new Promise(async (res, rej) => {
  283. let url = await fetch(`https://musescore.com/api/jmuse?id=${id}&type=img&v2=1&index=${i}`, {
  284. headers: { authorization: "8c022bdef45341074ce876ae57a48f64b86cdcf5" }
  285. }).then(e => e.json()).then(e => e.info.url).catch(rej)
  286. data[i] = await fetch(url).then(e => e.text()).catch(rej)
  287. Window.document.getElementById("download-status").innerText = ++cnt
  288. res()
  289. }))
  290. ).catch((err) => console.error(err))
  291. }
  292.  
  293. setTimeout(() => {
  294. Window.document.getElementsByClassName("hint")[0].innerText = ZH ? "如果长时间没反应就不要傻等着了,现在或者等会再试试吧。" : "If the process stoped, you can try again now or later."
  295. }, 8 * 1000)
  296.  
  297. await getData()
  298.  
  299. setTimeout(async () => {
  300. Window.document.getElementsByClassName("card-icon-text")[0].innerText = ZH ? "解析中" : "Decoding"
  301. if(cnt != length) {
  302. Window.document.getElementsByClassName("card-text")[0].innerText = ZH ? "下载失败,请稍后再试。" : "Download failed, try again later."
  303. return
  304. }
  305. setTimeout(() => {
  306. Window.document.body.style.background = "white"
  307. Window.document.body.innerHTML = data.join("") + `<div class="btn-group"><button onclick="print()">${ ZH ? "打印" : "PRINT" }</button>`
  308. const svgs = [...Window.document.getElementsByTagName("svg")]
  309. svgs.forEach((e) => e.setAttribute("viewBox", `0 0 ${e.width.baseVal.value} ${e.height.baseVal.value}`))
  310. Window.scrollTo(0, 0)
  311. }, 400)
  312. }, 400)
  313. }
  314.  
  315. const btns = [...document.getElementsByTagName("button")]
  316. btns.filter(el => {
  317. const val = el.attributes.getNamedItem("name")?.value
  318. return val == "download" || val == "print"
  319. }).forEach(el => {
  320. const type = el.attributes.getNamedItem("name").value
  321. const fakeEl = el.cloneNode(true)
  322. fakeEl.style.border = "2px #0dbc79 solid"
  323. if(type == "download") fakeEl.style.background = "#0dbc79"
  324. fakeEl.onclick = download
  325. el.parentNode.replaceChild(fakeEl, el)
  326. })
  327. }, 500)

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址