NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript==
// @name DailyMotion Download
// @id DailyMotionDownload
// @version 1.4
// @description Adds download link for DailyMotion Videos
// @namespace https://openuserjs.org/users/volkan-k/scripts
// @match *://www.dailymotion.com/*
// @grant unsafeWindow
// @grant GM_download
// @grant GM_openInTab
// @grant GM_xmlhttpRequest
// @require https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js
// @require https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js
// @require https://cdnjs.cloudflare.com/ajax/libs/filesize/3.6.1/filesize.min.js
// @require-test https://paste.ee/r/XNUsq/0
// @connect cloudflare.com
// @connect dailymotion.com
// @license MIT
// ==/UserScript==
var usw = (typeof unsafeWindow !== 'undefined') ? unsafeWindow : window; // Firefox, Opera<15
var RANDOM = Math.floor(Math.random() * 1234567890);
var DIALOG_ID = 'dmotion_download' + RANDOM;
var dl_handle, download_div, progressbar, progressLabel;
var is_downloading = false;
var timer1, timer2, timer3;
if (typeof GM_openInTab === "undefined") {
GM_openInTab = window.open;
}
if (typeof GM_info.downloadMode === "undefined") {
GM_info.downloadMode = "browser";
}
function find(selector) {
return document.querySelector(selector);
}
function debugLog(message) {
console.log("USER-SCRIPT DAILYMOTION-DOWNLOAD : " + message);
}
function convert_relative_urls(css, base) {
return css.replace(/url\s*\(\s*['"]{1}([^'"]+)['"]{1}\s*\)/ig, function(match, capture) {
if (/^(https?|file|ftps?|mailto|javascript|data:image\/[^;]{2,9};):/i.test(capture)) {
return match; // url is already absolute
}
return "url('" + new URL(capture, base).href + "')";
});
}
function addStyle_external(css_link, once, xhr) {
id = btoa(css_link).replace(/[+\/=]+/ig, "");
if (typeof xhr === "boolean" && xhr === true) {
// xhr it
GM_xmlhttpRequest({
method: "GET",
url: css_link,
onerror: function(oEvent) {
alert("Error " + oEvent.target.status + " occurred while receiving the document.");
},
onload: function(response) {
if (response.readyState !== 4 || response.status !== 200) return;
addGlobalStyle(convert_relative_urls(response.responseText, css_link), once, id)
}
});
return;
}
var head, style;
head = document.getElementsByTagName('head')[0];
if (!head) {
return;
}
if (once && $("link[href='" + css_link + "']").length > 0) {
return;
}
style = document.createElement('link');
style.setAttribute("rel", "stylesheet");
style.setAttribute("type", "text/css");
style.setAttribute("id", id);
style.setAttribute("href", css_link);
head.appendChild(style);
}
function addGlobalStyle(css, once, id) {
var head, style;
head = document.getElementsByTagName('head')[0];
if (!head) {
return;
}
if (once && document.getElementById(id)) {
return;
}
style = document.createElement('style');
style.setAttribute("type", "text/css");
if (typeof id === "string") {
style.setAttribute("id", id);
}
style.innerHTML = css;
head.appendChild(style);
}
function downloadVideoNatively(e) {
if ($.inArray(GM_info.downloadMode, ["native", "browser"]) === -1) {
return;
}
var elem = e.currentTarget;
e.returnValue = false;
if (e.preventDefault) {
e.preventDefault();
}
var link = $(elem).find('a').attr('href');
var name = $(elem).find('a').attr('title') + '.mp4';
name = name.replace(/[\\<>:"\/|?*]*/g, "");
if (link && name) {
if (typeof GM_download !== 'undefined') {
if (GM_info.downloadMode === "native") {
open_download_dialog();
if (is_downloading === false) {
dl_handle = GM_download({
url: link,
name: name,
onerror: gm_dl_error,
onload: gm_dl_load,
onprogress: gm_dl_progress,
ontimeout: gm_dl_timeout
});
dl_handle.my_filename = name;
is_downloading = true;
}
} else {
GM_download(link, name); // browser handles
}
debugLog("Downloading should start now.. File Name = " + name + " ; File URL = " + link);
} else {
GM_openInTab(link);
debugLog("Download link should be opened in new tab now.. File Name = " + name + " ; File URL = " + link);
}
}
return false;
}
function open_download_dialog() {
if ($('#' + DIALOG_ID).length > 0 && $('#' + DIALOG_ID).dialog('isOpen')) {
$('#' + DIALOG_ID).dialog('close').remove();
}
var dialogButtons = [{
text: "Cancel Download",
click: cancel_download
}];
addStyle_external('https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.css', true, true);
addGlobalStyle("#progressbar" + RANDOM + "{margin-top: 20px;}.progress-label" + RANDOM + "{font-weight: bold; text-shadow: 1px 1px 0 #fff;}", true, 'gm_added_style_dmotiondl');
download_div = $("<div title='File Download' id='" + DIALOG_ID + "'></div>").dialog({
resizable: false,
closeText: "Continue in background",
buttons: dialogButtons,
close: function(event, ui) {
$(this).dialog('destroy').remove();
}
});
$("button.ui-dialog-titlebar-close").tooltip();
$('#' + DIALOG_ID).append('<div class="progress-label' + RANDOM + '">Starting download...</div>' +
'<div id="extrainfo' + RANDOM + '"></div>' +
'<div id="progressbar' + RANDOM + '"></div>');
/* $("#master" + RANDOM).slider({
value: volume,
create: function() {
$("#handle" + RANDOM).text(' = %' + $(this).slider("value"));
},
slide: function(event, ui) {
$("#handle" + RANDOM).text(' = %' + ui.value);
SetVolume(ui.value);
}
});*/
progressbar = $("#progressbar" + RANDOM);
progressLabel = $(".progress-label" + RANDOM);
progressbar.progressbar({
value: false,
change: function() {
progressLabel.text("Current Progress: " + progressbar.progressbar("value") + "%");
},
complete: function() {
progressLabel.text("Complete!");
change_button_to_close();
}
});
}
function gm_dl_progress(response) {
if ($('#' + DIALOG_ID).length === 0 || !$('#' + DIALOG_ID).dialog('isOpen')) {
return;
}
if (response.lengthComputable === false) {
return;
}
//console.log(response);
var fs_html = "<p>File size: " + filesize(response.total) + "</p>";
if (typeof dl_handle.my_filename === "string") {
fs_html += "<p>File name: " + dl_handle.my_filename + "</p>";
}
if ($("#extrainfo" + RANDOM).html() !== fs_html) {
$("#extrainfo" + RANDOM).html(fs_html);
}
var val = Math.floor(response.done / response.total * 100);
progressbar.progressbar("value", val);
}
function gm_dl_load(response) {
is_downloading = false;
if ($('#' + DIALOG_ID).length === 0 || !$('#' + DIALOG_ID).dialog('isOpen')) {
return;
}
progressbar.progressbar("value", 100);
change_button_to_close();
}
function gm_dl_timeout(response) {
is_downloading = false;
if ($('#' + DIALOG_ID).length === 0 || !$('#' + DIALOG_ID).dialog('isOpen')) {
return;
}
progressbar.progressbar("value", false);
progressLabel.text("Download time-out!");
change_button_to_close();
}
function gm_dl_error(response) {
is_downloading = false;
if ($('#' + DIALOG_ID).length === 0 || !$('#' + DIALOG_ID).dialog('isOpen')) {
return;
}
progressbar.progressbar("value", false);
progressLabel.text("Download error!");
change_button_to_close();
}
function change_button_to_close() {
download_div.dialog("option", "buttons", [{
text: "Close",
click: function(event, ui) {
$(this).dialog('close');
}
}]);
download_div.dialog("option", "closeText", "Close");
}
function cancel_download() {
is_downloading = false;
dl_handle.abort();
if ($('#' + DIALOG_ID).length === 0 || !$('#' + DIALOG_ID).dialog('isOpen')) {
return;
}
download_div.dialog("close");
}
function are_we_on_video_page(){
var url = window.location.href;
var video_id = getDailyMotionId(url);
if (video_id === null){
return false;
}
// else
return true;
}
function setup(new_data) {
if (((usw && '__PLAYER_CONFIG__' in usw) || (typeof new_data === "object")) && find("p[class^='QueueInfoContent__stats']")) {
try {
if (typeof new_data === "object" && new_data !== null ) {
debugLog("DEBUG: using XHR metadata");
var video_id=new_data.id;
var video_title=new_data.title;
var video_qualities=new_data.qualities;
} else if (typeof usw.__PLAYER_CONFIG__.metadata === "object" && usw.__PLAYER_CONFIG__.metadata !== null) {
debugLog("DEBUG: using __PLAYER_CONFIG__ metadata");
var video_id = usw.__PLAYER_CONFIG__.metadata.id;
var video_title = usw.__PLAYER_CONFIG__.metadata.title;
var video_qualities = usw.__PLAYER_CONFIG__.metadata.qualities;
} else if (are_we_on_video_page()===true) {
debugLog("DEBUG: can't find any metadata, but we are on video page, running XHR");
clearTimeout(timer3);
timer3=setTimeout(xhr_video_info,1000);
return;
} else {
debugLog("NOTICE: couldnt find any metadata nor video ID. Exiting..");
return;
}
var container = document.createElement("div");
container.setAttribute('class', 'dailymotion_downloader');
var url;
for (var resolution in video_qualities) {
video_qualities[resolution].some(function(element) {
if (element.type === "video/mp4") {
url = element.url;
//console.log(resolution); console.log(url); // for debug
var div = document.createElement("div");
div.style.display = "inline-block";
var span = document.createElement("span");
//span.innerHTML = resolution;
span.style.display = "inline-block";
span.style.width = "130px";
div.appendChild(span);
var a = document.createElement("a");
a.title = video_title;
a.href = url;
a.textContent = 'Download ' + resolution + 'p';
span.appendChild(a);
container.appendChild(div);
// listen to click event
div.addEventListener('click', downloadVideoNatively, false);
}
});
}
clearTimeout(timer1); // we created the button, stop setup() loop
clearInterval(timer2); // we will setInterval, clear previous one first.
timer2 = setInterval(function() {
var node = find("p[class^='QueueInfoContent__stats']");
if (node === null) {
return;
}
var oldButton = find('.dailymotion_downloader');
if (oldButton !== null && oldButton.innerHTML === container.innerHTML) {
return;
}
//oldButton && console.log(oldButton.outerHTML); // for debugging only.
//console.log(button.outerHTML); // for debugging only.
oldButton && oldButton.remove();
if (find('.dailymotion_downloader') === null) {
node.parentNode.insertBefore(container, node);
}
}, 500);
} catch (error) {
// log the error
console.error("[DailyMotion Download] Error retrieving video meta data:", error);
}
} else {
// try again later
timer1 = setTimeout(setup, 500,(typeof new_data === "object" ? new_data : false));
}
}
function getDailyMotionId(url) {
//return url.substring(url.lastIndexOf('/') + 1); // old buggy method.
var m = url.match(/^.+dailymotion.com\/(video|hub)\/([^_]+)[^#]*(#video=([^_&]+))?/);
if (m !== null) {
if (m[4] !== undefined) {
return m[4];
}
return m[2];
}
return null;
}
function xhr_video_info() {
var url = window.location.href;
var video_id = getDailyMotionId(url);
if (video_id === null){
debugLog("DEBUG: xhr_video_info() couldnt find video id");
return ;
}
GM_xmlhttpRequest({
method: "GET",
url: "https://www.dailymotion.com/player/metadata/video/" + video_id,
onerror: function(oEvent) {
alert("Error " + oEvent.target.status + " occurred while receiving the document.");
},
onload: function(response) {
if (response.readyState !== 4 || response.status !== 200) return;
setup(JSON.parse(response.responseText));
}
});
}
// start looking for video player
setup();
// f*cking dailymotion does not update window.__PLAYER_CONFIG__ object.
$(window).bind('popstate', function() {
debugLog("DEBUG: popstate event: calling xhr_video_info()");
xhr_video_info();
});
var window_history_pushState = window.history.pushState;
window.history.pushState = function() {
window_history_pushState.apply(window.history, arguments);
debugLog("DEBUG: pushstate event: calling xhr_video_info()");
xhr_video_info();
}
//console.log(container);