Last active
November 10, 2016 13:59
-
-
Save esavelyeva/0e245d9e0979cc8f7fd0d4cc74dfcb3a to your computer and use it in GitHub Desktop.
Go to http://bookmarklets.org/maker/ to generate the bookmarklet
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//download.js v3.0, by dandavis; 2008-2014. [CCBY2] see http://danml.com/download.html for tests/usage | |
// v1 landed a FF+Chrome compat way of downloading strings to local un-named files, upgraded to use a hidden frame and optional mime | |
// v2 added named files via a[download], msSaveBlob, IE (10+) support, and window.URL support for larger+faster saves than dataURLs | |
// v3 added dataURL and Blob Input, bind-toggle arity, and legacy dataURL fallback was improved with force-download mime and base64 support | |
// data can be a string, Blob, File, or dataURL | |
function download(data, strFileName, strMimeType) { | |
var self = window, // this script is only for browsers anyway... | |
u = "application/octet-stream", // this default mime also triggers iframe downloads | |
m = strMimeType || u, | |
x = data, | |
D = document, | |
a = D.createElement("a"), | |
z = function (a) { | |
return String(a); | |
}, | |
B = self.Blob || self.MozBlob || self.WebKitBlob || z, | |
BB = self.MSBlobBuilder || self.WebKitBlobBuilder || self.BlobBuilder, | |
fn = strFileName || "download", | |
blob, | |
b, | |
ua, | |
fr; | |
//if(typeof B.bind === 'function' ){ B=B.bind(self); } | |
if (String(this) === "true") { //reverse arguments, allowing download.bind(true, "text/xml", "export.xml") to act as a callback | |
x = [x, m]; | |
m = x[0]; | |
x = x[1]; | |
} | |
//go ahead and download dataURLs right away | |
if (String(x).match(/^data\:[\w+\-]+\/[\w+\-]+[,;]/)) { | |
return navigator.msSaveBlob ? // IE10 can't do a[download], only Blobs: | |
navigator.msSaveBlob(d2b(x), fn) : | |
saver(x); // everyone else can save dataURLs un-processed | |
} //end if dataURL passed? | |
try { | |
blob = x instanceof B ? | |
x : | |
new B([x], { | |
type: m | |
}); | |
} catch (y) { | |
if (BB) { | |
b = new BB(); | |
b.append([x]); | |
blob = b.getBlob(m); // the blob | |
} | |
} | |
function d2b(u) { | |
var p = u.split(/[:;,]/), | |
t = p[1], | |
dec = p[2] == "base64" ? atob : decodeURIComponent, | |
bin = dec(p.pop()), | |
mx = bin.length, | |
i = 0, | |
uia = new Uint8Array(mx); | |
for (i; i < mx; ++i) uia[i] = bin.charCodeAt(i); | |
return new B([uia], { | |
type: t | |
}); | |
} | |
function saver(url, winMode) { | |
if ('download' in a) { //html5 A[download] | |
a.href = url; | |
a.setAttribute("download", fn); | |
a.innerHTML = "downloading..."; | |
D.body.appendChild(a); | |
setTimeout(function () { | |
a.click(); | |
D.body.removeChild(a); | |
if (winMode === true) { | |
setTimeout(function () { | |
self.URL.revokeObjectURL(a.href); | |
}, 250); | |
} | |
}, 66); | |
return true; | |
} | |
//do iframe dataURL download (old ch+FF): | |
var f = D.createElement("iframe"); | |
D.body.appendChild(f); | |
if (!winMode) { // force a mime that will download: | |
url = "data:" + url.replace(/^data:([\w\/\-\+]+)/, u); | |
} | |
f.src = url; | |
setTimeout(function () { | |
D.body.removeChild(f); | |
}, 333); | |
} //end saver | |
if (navigator.msSaveBlob) { // IE10+ : (has Blob, but not a[download] or URL) | |
return navigator.msSaveBlob(blob, fn); | |
} | |
if (self.URL) { // simple fast and modern way using Blob and URL: | |
saver(self.URL.createObjectURL(blob), true); | |
} else { | |
// handle non-Blob()+non-URL browsers: | |
if (typeof blob === "string" || blob.constructor === z) { | |
try { | |
return saver("data:" + m + ";base64," + self.btoa(blob)); | |
} catch (y) { | |
return saver("data:" + m + "," + encodeURIComponent(blob)); | |
} | |
} | |
// Blob but not URL: | |
fr = new FileReader(); | |
fr.onload = function (e) { | |
saver(this.result); | |
}; | |
fr.readAsDataURL(blob); | |
} | |
return true; | |
} /* end download() */ | |
/** | |
* Traverses the DOM nodes upwards to find a parent element with the specified class | |
* @param {DOM Node} child Child node | |
* @param {String} parentClass Parent class | |
* @return {Dom Node / null} Parent node with the specified class or null | |
*/ | |
function getParentFolder(child, parentClass) { | |
var parent = child.parentNode; | |
if (parent.className === parentClass) { | |
return parent; | |
} else if (parent.tagName.toLowerCase() === 'body') { | |
return null; | |
} else { | |
return getParentFolder(parent, parentClass); | |
} | |
} | |
/** | |
* Gets video URL addresses and titles | |
* @return {Object / null} Collection of videos or null | |
*/ | |
function getVideoLinks() { | |
var folderClass = 'folder-current', | |
folderEl = document.querySelector('.' + folderClass); | |
if (!folderEl) { | |
return null; | |
} | |
var seasonFolderClass = 'folder', | |
videoTitleClass = 'b-tab-item__title-inner', | |
videoTitle = document.querySelector('.' + videoTitleClass).querySelector('span').innerHTML.replace(/[^a-zAа-яА-Я\d]/g, ''), | |
videoSeasonEl = getParentFolder(folderEl, seasonFolderClass), | |
videoSeason = videoSeasonEl ? videoSeasonEl.querySelector('.header a b').innerHTML.replace(/ /g, '') : null, | |
videoClass = 'b-file-new', | |
videoEls = folderEl.querySelectorAll('.' + videoClass), | |
linkClass = 'b-file-new__link-material-download', | |
data = { | |
title: videoTitle + '_' + (videoSeason ? videoSeason : ''), | |
videos: [] | |
}; | |
for (var i = 0; i < videoEls.length; i++) { | |
var video = {}; | |
//Get only the videos currently displayed on page | |
if (videoEls[i].style.display !== 'none') { | |
//Get URL | |
video.href = videoEls[i].querySelectorAll('.' + linkClass)[0].href; | |
//Get meta | |
var splitHref = video.href.split('/'); | |
video.meta = decodeURIComponent(splitHref[splitHref.length - 1]).replace(/[+]/g, ' ').toLowerCase(); | |
//Add video to the collection | |
data.videos.push(video); | |
} | |
} | |
return data.videos.length > 0 ? data : null; | |
} | |
/** | |
* Creates plain text file containing M3U playlist | |
* @param {Array} videos Collection of video URL addresses and meta data | |
* @return {String} Playlist | |
*/ | |
function createPlaylist(videos) { | |
var playlist = '#EXTM3U \n'; | |
for (var i = 0; i < videos.length; i++) { | |
var video = '#EXTINF:' + (i + 1) + ', ' + videos[i].meta + '\n' + videos[i].href + '\n'; | |
playlist = playlist.concat(video); | |
} | |
return playlist; | |
} | |
/** | |
* Create markup for the popup containing the videos | |
* @param {Array} videos Collection of video URL addresses and meta data | |
* @return {document fragment} Popup markup | |
*/ | |
function createPopupMarkup(data) { | |
var bodyRect = document.querySelector('body').getBoundingClientRect(), | |
popupId = 'bml-popup-' + Date.now(), | |
downloadBtnId = 'bml-download-playlist', | |
dismissBtnId = 'bml-dismiss'; | |
/** | |
* Prompts downloading of plain text file | |
*/ | |
function onClick(ev) { | |
if (ev.target.id === downloadBtnId) { | |
var fileName = data.title + '.m3u'; | |
download(createPlaylist(data.videos), fileName, 'text/plain'); | |
} else if (ev.target.id === dismissBtnId) { | |
var html = document.querySelector('html'), | |
popup = document.querySelector('#' + popupId); | |
popup.parentNode.removeChild(popup); | |
html.style.overflow = 'auto'; | |
} | |
} | |
var frag = document.createDocumentFragment(), | |
popup = document.createElement('div'), | |
heading = document.createElement('p'), | |
list = document.createElement('ul'), | |
downloadBtn = document.createElement('button'), | |
dismissBtn = document.createElement('button'), | |
popupPadding = 24; | |
//Popup styles | |
popup.style.background = '#fff'; | |
popup.style.position = 'absolute'; | |
popup.style.top = (-bodyRect.top) + 'px'; | |
popup.style.bottom = (-bodyRect.top) + window.innerHeight + 'px'; | |
popup.style.right = 0; | |
popup.style.left = '50%'; | |
popup.style.minWidth = '300px'; | |
popup.style.minHeight = window.innerHeight - popupPadding * 2 + 'px'; | |
popup.style.padding = popupPadding + 'px'; | |
popup.style.overflow = 'scroll'; | |
popup.style.fontFamily = 'Helvetica, Arial, sans-serif'; | |
popup.style.fontSize = '16px'; | |
popup.style.lineHeight = '1.618'; | |
popup.style.transform = 'translate(-50%, 0)'; | |
popup.style.zIndex = 9999; | |
popup.style.boxShadow = '0px 0px 10px 5px hsla(0, 0%, 0%, .3333)'; | |
heading.textContent = data.title; | |
heading.style.fontSize = '20px'; | |
heading.style.marginBottom = '12px'; | |
popup.appendChild(heading); | |
for (var i = 0; i < data.videos.length; i++) { | |
var item = document.createElement('li'); | |
link = document.createElement('a'); | |
link.href = data.videos[i].href; | |
link.textContent = data.videos[i].meta; | |
item.appendChild(link); | |
list.appendChild(item); | |
} | |
list.style.marginBottom = '20px'; | |
popup.appendChild(list); | |
//Download button base | |
downloadBtn.type = 'button'; | |
downloadBtn.textContent = 'Download playlist'; | |
downloadBtn.id = downloadBtnId; | |
//Download button styles | |
downloadBtn.style.width = '100%'; | |
downloadBtn.style.padding = '12px 16px'; | |
downloadBtn.style.fontSize = '16px'; | |
downloadBtn.style.color = '#fff'; | |
downloadBtn.style.background = '#0074D9'; | |
downloadBtn.style.marginBottom = '12px'; | |
downloadBtn.style.cursor = 'pointer'; | |
//Add button to popup | |
popup.appendChild(downloadBtn); | |
//Dismiss button base | |
dismissBtn.type = 'button'; | |
dismissBtn.textContent = 'Dismiss'; | |
dismissBtn.id = dismissBtnId; | |
//Dismiss button styles | |
dismissBtn.style.width = '100%'; | |
dismissBtn.style.padding = '12px 16px'; | |
dismissBtn.style.fontSize = '16px'; | |
dismissBtn.style.color = '#fff'; | |
dismissBtn.style.background = '#AAAAAA'; | |
dismissBtn.style.marginBottom = '12px'; | |
dismissBtn.style.cursor = 'pointer'; | |
//Add button to popup | |
popup.appendChild(dismissBtn); | |
//Add event listener for download and dismiss buttons | |
document.addEventListener('click', onClick); | |
popup.id = popupId; | |
frag.appendChild(popup); | |
return frag; | |
} | |
/** | |
* Attach the document fragment to document body | |
* @param {document fragment} frag [description] | |
*/ | |
function attachFrag(frag) { | |
var html = document.querySelector('html'), | |
body = document.querySelector('body'); | |
html.style.overflow = 'hidden'; | |
body.appendChild(frag); | |
} | |
/** | |
* Init extraction of the playlist from fs.to | |
*/ | |
function init() { | |
var data = getVideoLinks(); | |
if (data) { | |
createPlaylist(data); | |
attachFrag(createPopupMarkup(data)); | |
} else { | |
alert('Pick videos folder first.'); | |
} | |
} | |
init(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment