// ==UserScript== // @version 3.9 // @name YouTube -> download MP3 or Video from YouTube. // @namespace https://greasyfork.org/ru/scripts/386967-youtube-download-mp3-or-video-from-youtube // @author A.Vasin // @description Simple YouTube MP3 & MP4 download buttons. Simple and fast. // @compatible chrome // @compatible firefox // @compatible opera // @compatible safari // @icon https://avasin.fly.dev/UserScripts/YouTube-Saver/logo.png // @include http*://www.youtube.com/* // @include http*://*.youtube.com/* // @include http*://youtube.com/* // @include http*://*.youtu.be/* // @include http*://youtu.be/* // @grant GM_addStyle // @grant GM_download // @run-at document-idle // @copyright 2020-02-21 // a.vasin // @license https://creativecommons.org/licenses/by-sa/4.0 // @downloadURL https://update.greasyfork.icu/scripts/386967/YouTube%20-%3E%20download%20MP3%20or%20Video%20from%20YouTube.user.js // @updateURL https://update.greasyfork.icu/scripts/386967/YouTube%20-%3E%20download%20MP3%20or%20Video%20from%20YouTube.meta.js // ==/UserScript== class YouTubeSaver { constructor() { this.btnHolderSel = '#meta-contents #subscribe-button'; this.downloadBtnClass = 'js-ytube-download'; this.downloadAudioClass = 'js-mp3-download'; this.language = (navigator.language || navigator.userLanguage).split('-')[0]; this.defaultLang = 'en'; this.baseServiceSupportedLangs = ['en', 'ru', 'sk', 'it', 'es', 'fr', 'de', 'nl', 'pt', 'tr', 'no', 'kr', 'jp', 'pl', 'cn', 'hu', 'in', 'ro', 'gr', 'cz', 'bg', 'rs', 'sa', 'id']; this.baseServiceLang = this.baseServiceSupportedLangs.includes(this.language) ? this.language : this.defaultLang; this.baseServiceUrl = `https://dirpy.com/studio?url=`; //https://y2mate.com/ru/youtube/sWgiVmcjt8c this.formatMap = { mp3: '1', mp4: '8', mp4HD: '7' }; this.audioServiceBaseUrl = 'https://www.y2mate.com/youtube-mp3/'; this.initInterval = 400; this.checkInterval = 1000; this.btnSize = '10px'; this.btnPadding = '10px 5px'; this.loaderHtml = '
'; this.langProps = { en: { 'download.mp3': 'Download MP3', 'download.video': 'Download VIDEO', 'download': 'Download' }, ru: { 'download.mp3': 'Скачать MP3', 'download.video': 'Скачать ВИДЕО', 'download': 'Скачать' }, sk: { 'download.mp3': 'Stiahnuť MP3', 'download.video': 'Stiahnuť VIDEO', 'download': 'Stiahnuť' }, ua: { 'download.mp3': 'Скачати MP3', 'download.video': 'Скачати VIDEO', 'download': 'Скачати' } } this.currentProps = this.langProps[this.language] || this.langProps[this.defaultLang]; this.init(); } getAudioBtnHtml(link) { const downloadUrl = this.getAudioDownloadUrl({url: link}); return ` ${this.loaderHtml} ${this.getLangProp('download.mp3')} `; } getVideoBtnHtml(link) { const downloadUrl = this.getMateDownloadUrl(link); return ` ${this.getLangProp('download.video')} `; } init() { this.addStyles(); this.initDownloadBtn(); this.addDownloadIframe(); } initDownloadBtn() { setInterval(() => { const appendToEl = document.querySelector(this.btnHolderSel); const downloadBtn = document.querySelector(`.${this.downloadBtnClass}`); // Append download buttons in case download mp3 button not available // && placeholder exist on page if(!downloadBtn && appendToEl) { this.appendBtns(appendToEl); } }, this.initInterval); } bindEvents(ctx) { const audioBtn = ctx.querySelector(`.${this.downloadAudioClass}`); audioBtn && audioBtn.addEventListener('click', this.onAudioDownload.bind(this, audioBtn)); } addDownloadIframe() { this.downloadFrame = document.createElement('iframe'); document.body.append(this.downloadFrame); } addStyles() { GM_addStyle(` .${this.downloadBtnClass} { position: relative; border: 2px solid #3f51b5; padding: ${this.btnPadding}; font-size: ${this.btnSize}; font-weight: 500; text-align: center; margin: 5px 4px 0; color: #3f51b5; text-decoration: none; flex-grow: 1; text-transform: uppercase; } .${this.downloadAudioClass} { border-color: #ff5722; color: #ff5722; } .loading { pointer-events: none; color: transparent; } .loading .loader { display: block; } .loader { display: none; position: absolute; top: 0; left: 0; width: 100%; height: 100%; text-align: center; font-size: 10px; } .loader>div { background-color: #ff5722; height: 100%; width: 6px; margin: 0 1px; display: inline-block; animation: sk-stretchdelay 1.2s infinite ease-in-out; } .loader .rect2 { animation-delay: -1.1s; } .loader .rect3 { animation-delay: -1.0s; } .loader .rect4 { animation-delay: -0.9s; } .loader .rect5 { animation-delay: -0.8s; } @keyframes sk-stretchdelay { 0%, 40%, 100% { transform: scaleY(0.4); } 20% { transform: scaleY(1.0); } } `); } getLangProp(id) { return this.currentProps[id]; } getAudioDownloadUrl({url} = {}) { const id = this.getVideoId(url); if(!id) { console.warn('Video ID not found/parsed.'); return id; } return `${this.audioServiceBaseUrl}${id}`; } getMateDownloadUrl(link) { const urlArray = link.split('.com'); return `${urlArray[0]}pp.com${urlArray[1]}`; } getBaseDownloadUrl({url, format = this.formatMap.mp3} = {}) { return `${this.baseServiceUrl}${encodeURIComponent(url)}&format=${format}`; } getNodeFromString(string) { const div = document.createElement('div'); div.innerHTML = string.trim(); return div.firstChild; } getVideoId(url) { const regExp = /^.*((youtu.be\/)|(v\/)|(\/u\/\w\/)|(embed\/)|(watch\?))\??v?=?([^#\&\?]*).*/; const match = url.match(regExp); return (match && match[7].length==11) ? match[7] : false; } getVideoTitle() { return (document.querySelector('.ytp-title-link') || document.title).innerText; } appendBtns(appendToEl) { const url = document.location.href; const audioBtnHtml = this.getAudioBtnHtml(url); const videoBtnHtml = this.getVideoBtnHtml(url); const downloadWrapper = this.getNodeFromString(`
${audioBtnHtml} ${videoBtnHtml}
`); //append buttons to the page appendToEl.append(downloadWrapper); // TODO: uncomment events for audio button // this.bindEvents(appendToEl); } downloadFile(url, btn) { this.downloadFrame.onerror = this.downloadFailed.bind(this, btn); /* TODO: Update handling error case this.downloadFrame.onload = () => { if(!this.downloadFrame.innerHTML) { this.downloadFailed(btn); this.downloadFrame.onload = null; this.downloadFrame.onerror = null; } }; */ this.downloadFrame.src = url; } onAudioDownload(btn, e) { e.preventDefault(); const _this = this; const url = btn.href; this.toggleLoader(btn); fetch(url) .then(resp => resp.json()) .then(({dlMusic, status}) => { switch (status) { case 'finished': _this.downloadFile(dlMusic, btn); setTimeout(_this.toggleLoader.bind(_this, btn, false), 500); break; case 'error': throw new Error(error); default: setTimeout(_this.onAudioDownload.bind(_this, btn, e), _this.checkInterval); break; } }) .catch(_this.downloadFailed.bind(_this, btn)); } downloadFailed(btn) { const alternativeUrl = this.getBaseDownloadUrl({url: window.location.href}); this.toggleLoader(btn, false); window.open(alternativeUrl, '_blank'); } toggleLoader(btn, isActivate = true, msg) { if(btn) { btn.classList.toggle('loading', isActivate); if(msg) { btn.innerText = msg; } } } } // Init downloader const saver = new YouTubeSaver();