// ==UserScript== // @name Github Copy Raw File URL and Download File // @name:zh-CN Github 复制原始文件 URL 与下载文件 // @name:zh-TW Github 複製原始檔案 URL 與下載檔案 // @name:vi Github Sao chép URL tệp gốc và tải xuống tệp // @name:ko Github 원본 파일 URL 복사 및 파일 다운로드 // @name:ja Github 原始ファイル URL をコピーし、ファイルをダウンロードする // @name:en Github Copy Raw File URL and Download File // @name:de Github Rohdatei-URL kopieren und Datei herunterladen // @description Add buttons at the end of each file line to copy the raw file URL and download the file // @description:zh-CN 在每个文件行的末尾添加按钮,以复制原始文件 URL 和下载文件 // @description:zh-TW 在每個檔案行的末尾添加按鈕,以複製原始檔案 URL 和下載檔案 // @description:vi Thêm nút vào cuối mỗi dòng tệp để sao chép URL tệp gốc và tải xuống tệp // @description:ko 각 파일 행 끝에 원본 파일 URL 복사 및 파일 다운로드 버튼 추가 // @description:ja 各ファイル行の末尾に、原始ファイルURLをコピーし、ファイルをダウンロードするボタンを追加 // @description:en Add buttons at the end of each file line to copy the raw file URL and download the file // @description:de Fügen Sie Schaltflächen am Ende jeder Dateizeile hinzu, um die Rohdatei-URL zu kopieren und die Datei herunterzuladen // @namespace https://github.com/ChinaGodMan/UserScripts // @version 2.2.0.8 // @author Kamikaze (https://github.com/Kamiikaze) ,人民的勤务员 // @match https://github.com/* // @icon https://www.google.com/s2/favicons?sz=64&domain=github.com // @run-at document-ready // @license MIT // @supportURL https://github.com/ChinaGodMan/UserScripts/issues // @homepageURL https://github.com/ChinaGodMan/UserScripts // @downloadURL none // ==/UserScript== // Need an Interval to detect path changes on github tree one-pager // Define the number of seconds const scanInterval = 2 const waitForFilelist = setInterval(() => { let fileListContainer = document.querySelector("div.Box > div.js-details-container.Details div") || document.querySelector("table") let fileList = [] let isTable = false if (fileListContainer.tBodies) { fileList = fileListContainer.tBodies[0].children isTable = true } else { fileList = fileListContainer.children } if (fileList < 1) return appendButtons(fileList, isTable) }, scanInterval * 1000) function appendButtons(fileList, isTable = false) { let fileUrl = "" let rawFileUrl = "" for (let i = 0; i < fileList.length; i++) { let file = fileList[i] if (file.classList.contains("cp-btn-rdy")) continue file.classList.add("cp-btn-rdy") if (!isTable) { if ( file.classList.contains("sr-only") || file.childElementCount !== 4 ) continue fileUrl = file.querySelector('div:nth-child(2) .js-navigation-open') .href } else { if (i === 0) continue if ( file.classList.contains("sr-only") ) continue fileUrl = file.querySelector("a") .href file = file.querySelector("td:nth-child(4) > div") } //alert(fileUrl) // Dont add button if its a folder if (!fileUrl.includes("/blob/")) continue rawFileUrl = fileUrl.replace('/blob/', '/raw/') file.style = "display: flex; justify-content: flex-end;" file.append(creatyCopyButton(rawFileUrl)) file.append(creatyDownButton(rawFileUrl)) } }; function creatyCopyButton(copyText) { const copy2clipboard = ` ` const copyButton = document.createElement('div') copyButton.setAttribute('role', 'gridcell') copyButton.style = "margin-left: 10px; display: inline;" copyButton.innerHTML = copy2clipboard copyButton.children[0].value = copyText copyButton.children[0].style = "cursor: pointer;" return copyButton } function creatyDownButton(copyText) { const copy2clipboard = ` ` const copyButton = document.createElement('div') copyButton.setAttribute('role', 'gridcell') copyButton.style = "margin-left: 10px; display: inline;" copyButton.innerHTML = copy2clipboard copyButton.children[0].value = copyText copyButton.children[0].style = "cursor: pointer;" copyButton.addEventListener('click', () => { // window.location.href = copyText; downloadFile(copyText, getFilenameFromUrl(copyText)) }) return copyButton } function downloadFile(url, filename) { var xhr = new XMLHttpRequest() xhr.open('GET', url, true) xhr.responseType = 'blob' xhr.onload = function () { if (xhr.status === 200) { var blob = xhr.response var objectUrl = window.URL.createObjectURL(blob) var a = document.createElement('a') a.href = objectUrl a.download = filename // 设置下载文件名 document.body.appendChild(a) a.click() window.URL.revokeObjectURL(objectUrl) // 清理 object URL document.body.removeChild(a) // 清理 DOM } } xhr.send() } function getFilenameFromUrl(url) { if (typeof url !== 'string' || url.trim() === '') { logMessage('getFilenameFromUrl', 'URL无效,默认文件名download', false) return 'download' // 返回一个默认的文件名 } var lastSlashIndex = url.lastIndexOf('/') if (lastSlashIndex === -1 || lastSlashIndex === url.length - 1) { logMessage('getFilenameFromUrl', 'URL格式无效缺少文件名,默认文件名download', false) return 'download' // 返回一个默认的文件名 } var filenameWithExtension = url.substring(lastSlashIndex + 1) var decodedFilename = decodeURIComponent(filenameWithExtension) decodedFilename = decodedFilename.replace(/%20/g, '_') // 替换所有的 %20 为下划线 return decodedFilename }