// ==UserScript==
// @name Udemy下载助手(需配合Aria2使用)
// @name:en Udemy Downloader
// @namespace http://mofiter.com/
// @version 0.3
// @description 在 Udemy 上课程的课程内容页面添加下载按钮(可批量下载和单个视频下载),方便将视频下载到本地学习
// @description:en add button on udemy's course content to download videos
// @author mofiter
// @require https://cdn.bootcss.com/jquery/1.12.4/jquery.min.js
// @match *://www.udemy.com/*/learn/v4/content
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_xmlhttpRequest
// @grant GM_openInTab
// ==/UserScript==
(function() {
'use strict';
var $ = $ || window.$;
var log_count = 1;
var hasOpenAriac2Tab = false;
var video_quality = "720"; //视频清晰度
var aria2_url = "http://127.0.0.1:6800/jsonrpc"; //Aria2 地址
var course_save_path = '/Users/mofiter/Downloads/study163'; //课程保存路径
var mycourses_url = "https://www.udemy.com/api-2.0/users/me/subscribed-courses"; //已购所有课程的地址
var curricuclum_url = "https://www.udemy.com/api-2.0/courses/{0}/cached-subscriber-curriculum-items/?page_size=9999"; //当前课程的地址
var lesson_url = "https://www.udemy.com/api-2.0/assets/{0}?fields[asset]=@min,status,delayed_asset_message,time_estimation,stream_urls,captions,body&fields[caption]=@default,is_translation"; //一节课的地址
var cookies = document.cookie;
var match_cookie = cookies.match(/access_token=(\w+?);/);
var authorization = "Bearer " + match_cookie[1];
var mycourses_info;
var current_course_url = location.pathname;
var current_course_info = {'course_id':{},'course_name':{},'chapter_info':[]};
var isExpand = false; //下载助手是否展开
//自定义 log 函数
function mylog(param1,param2){
param1 = param1 ? param1 : "";
param2 = param2 ? param2 : "";
console.log("#" + log_count++ + "-UdemyDownloader-log:",param1,param2);
}
//javascript 字符串使用占位符轻松拼接
String.prototype.format = function() {
if(arguments.length == 0) return this;
var param = arguments[0];
var s = this;
if(typeof(param) == 'object') {
for(var key in param)
{s = s.replace(new RegExp("\\{" + key + "\\}", "g"), param[key]);}
return s;
} else {
for(var i = 0; i < arguments.length; i++)
{s = s.replace(new RegExp("\\{" + i + "\\}", "g"), arguments[i]);}
return s;
}
}
setTimeout(function(){
getCourseInfo();
loadSetting();
addDownloadAssistant();
addDownloadButton();
mylog("Udemy 下载助手加载完成 ~");
},5000); //页面加载完成后延时5秒执行
//获取视频信息
function getCourseInfo(){
$.ajax({
url:mycourses_url,
async:false,
method:'GET',
beforeSend:function(xhr){
xhr.setRequestHeader('authorization',authorization);
},
success:function(response){
mycourses_info = response.results;
}
});
$.each(mycourses_info,function(index,element){
if (current_course_url.indexOf(element.url) > -1) {
current_course_info.course_id = element.id;
current_course_info.course_name = element.title.replace(/:|\?|\*|"|<|>|\|/g," ");
return false;
}
});
curricuclum_url = curricuclum_url.format(current_course_info.course_id);
$.ajax({
url:curricuclum_url,
async:false,
method:'GET',
beforeSend:function(xhr){
xhr.setRequestHeader('authorization',authorization);
},
success:function(response){
var list = response.results;
list.push({'_class':'chapter','id':'useless','title':''});
var chapter = {'chapter_id':'','chapter_name':{},'lecture_info':[]};
var lecture_sn = 0;
list.forEach(function(item,index){
if(item._class == 'chapter'){
if(chapter.chapter_id != ''){
var data = JSON.parse(JSON.stringify(chapter));
current_course_info.chapter_info.push(data);
}
if(item.id != 'useless'){
chapter.chapter_id = item.id;
chapter.chapter_name = item.title.replace(/:|\?|\*|"|<|>|\|/g," ");
chapter.lecture_info = [];
}
}else if(item._class == 'lecture'){
var lecture = {'lecture_sn':(++lecture_sn),'lecture_id':item.id,'lecture_name':item.title.replace(/:|\?|\*|"|<|>|\|/g," "),'lecture_asset':{'asset_id':item.asset.id,'asset_name':item.asset.title.replace(/:|\?|\*|"|<|>|\|/g," "),'asset_type':item.asset.asset_type}};
chapter.lecture_info.push(lecture);
}
});
mylog(current_course_info);
}
});
}
//添加下载助手按钮
function addDownloadAssistant(){
var download_assistant = $('<li ui-sref-active="active"></li>');
var download_assistant_a = $('<a class="dropdown-toggle"><span class="mr5"><span>下载助手</span></span><i class="udi udi-caret-down"></i></a>');
var assistant_content = $('<div id="assistant_content" style="display:none;z-index:999999;position:absolute;width:90px;background-color:#fff;border:1px solid #ddd;top:55px;padding:5px 10px;text-align:center;line-height:30px;"></div>');
var batch_download = $('<a>批量下载</a>');
var assistant_setting = $('<a>设置</a>');
assistant_content.append(batch_download).append(assistant_setting);
download_assistant.append(download_assistant_a).append(assistant_content);
$('.nav-tabs').append(download_assistant);
download_assistant.click(function(event){
if(isExpand){
isExpand = false;
}else{
isExpand = true;
}
assistant_content.toggle();
event.stopPropagation();
});
$(document).click(function(){
if(isExpand){
assistant_content.hide();
isExpand = false;
}
});
batch_download.click(function(){
loadSetting();
if(course_save_path==""){
alert("请到下载助手的设置里面填写文件保存位置");
}else if(aria2_url==""){
alert("请到下载助手的设置里面填写 Aria2 地址");
}else{
batchDownload();
}
});
assistant_setting.click(function(){
showSetting();
});
}
//加载个人设置
function loadSetting(){
video_quality = GM_getValue('video_quality', '720');
aria2_url = GM_getValue('aria2_url','http://127.0.0.1:6800/jsonrpc');
course_save_path = GM_getValue('course_save_path','');
}
//打开设置
function showSetting(){
if(document.querySelector('#dl-setting') == null){
var container = document.createElement("div");
container.id = "dl-setting";
container.style = "position:fixed;z-index:999999;top:10%;right:2%;width:270px;background-color:#eee;padding:5px 10px;font-size:14px;border:1px solid;";
container.innerHTML =
"<div style='line-height:25px;'>" +
"<div style='text-align:center;font-size:16px;'>下载助手设置</div>" +
"<ul>" +
"<li>Aria2 地址:</li>" +
"<li><input type='text' id='aria2_url' name='aria2_url' value='" + aria2_url + "' style='width:100%'></input></li>" +
"<li>文件保存位置:</li>" +
"<li><input type='text' id='save_path' name='save_path' value='" + course_save_path + "' style='width:100%'></input></li>" +
"<li>清晰度:</li>" +
"<li><label title='720P'><input id='video-quality-1' name='video-quality' value='720' type='radio' style='margin:0 5px;'" + (video_quality=="720" ? "checked":"") + "></input>720P</label>" +
"<label title='480P' style='padding:0 5px;'><input id='video-quality-2' name='video-quality' value='480' type='radio' style='margin:0 5px;'" + (video_quality=="480" ? "checked":"") + "></input>480P</label>" +
"<label title='360P' style='padding:0 5px;'><input id='video-quality-3' name='video-quality' value='360' type='radio' style='margin:0 5px;'" + (video_quality=="360" ? "checked":"") + "></input>360P</label>" +
"<label title='144P' style='padding:0 5px;'><input id='video-quality-4' name='video-quality' value='144' type='radio' style='margin:0 5px;'" + (video_quality=="144" ? "checked":"") + "></input>144P</label></li>" +
"</ul>\n" +
"<input type='button' value='取消' id='cancel_button' style='position:relative;float:left;border:1px solid;padding:0 4px;'></input>\n" +
"<input type='button' value='保存' id='save_button' style='position:relative;float:right;border:1px solid;padding:0 4px;'></input>\n" +
"</div>";
document.body.appendChild(container);
}else{
loadSetting();
if(video_quality=="720"){
$('#video-quality-1').prop('checked',true);
}else if(video_quality=="480"){
$('#video-quality-2').prop('checked',true);
}else if(video_quality=="360"){
$('#video-quality-3').prop('checked',true);
}else if(video_quality=="144"){
$('#video-quality-4').prop('checked',true);
}
$('#aria2_url').value = aria2_url;
$('#save_path').value = course_save_path;
$('#dl-setting').show();
}
$('#save_button').click(function(){
GM_setValue('video_quality',$('input[name="video-quality"]:checked').val());
GM_setValue('aria2_url',$('input[name="aria2_url"]').val());
GM_setValue('course_save_path',$('input[name="save_path"]').val());
$('#dl-setting').hide();
});
$('#cancel_button').click(function(){
$('#dl-setting').hide();
});
}
//批量下载
function batchDownload(){
current_course_info.chapter_info.forEach(function(chapter,index){
chapter.lecture_info.forEach(function(lecture){
if(lecture.lecture_asset.asset_type == 'Video'){
var file_name = lecture.lecture_sn + '.' + lecture.lecture_name;
var save_path = course_save_path.replace(/\\/g,'\/') + '/' + current_course_info.course_name + '/' + (index + 1) + '.' + current_course_info.chapter_info[index].chapter_name;
var real_lesson_url = lesson_url.format(lecture.lecture_asset.asset_id);
getVideoUrl(real_lesson_url,file_name + '.mp4',save_path);
}
});
});
}
//添加下载按钮
function addDownloadButton(){
var li_items = $('div.section--section--CIk7q li.curriculum-item--curriculum-item--iJwX5');
$.each(li_items,function(index,element){
if (element.firstChild.childNodes[2].innerHTML == '') {
element.firstChild.style.paddingRight = "70px";
return true;
}
var download_button = document.createElement('span');
download_button.className = 'download-button';
download_button.innerHTML = '下载';
download_button.style.order = 5;
download_button.style.marginLeft = '20px';
(element.firstChild).append(download_button);
});
$('.download-button').each(function(){
$(this).click(function(event){
loadSetting();
if(course_save_path==""){
alert("请到下载助手的设置里面填写文件保存位置");
}else if(aria2_url==""){
alert("请到下载助手的设置里面填写 Aria2 地址");
}else{
var chapter_index = $(this).parents('.section--section--CIk7q').index();
var lecture_index = $(this).parents('.curriculum-item--curriculum-item--iJwX5').index();
var lecture = current_course_info.chapter_info[chapter_index].lecture_info[lecture_index];
mylog("选择的课为【lecture_name:" + lecture.lecture_name + ",lecture_id:" + lecture.lecture_id + '】');
var file_name = lecture.lecture_sn + '.' + lecture.lecture_name;
var save_path = course_save_path.replace(/\\/g,'\/') + '/' + current_course_info.course_name + '/' + (chapter_index + 1) + '.' + current_course_info.chapter_info[chapter_index].chapter_name;
var real_lesson_url = lesson_url.format(lecture.lecture_asset.asset_id);
var url = 'https://www.udemy.com/vuejs-app/learn/v4/t/lecture/{0}?start=0'.format(lecture.lecture_id);
getVideoUrl(real_lesson_url,file_name + '.mp4',save_path);
}
event.stopPropagation();
});
});
}
//获取视频下载地址
function getVideoUrl(url,file_name,save_path){
$.ajax({
url:url,
async:true,
method:'GET',
beforeSend:function(xhr){
xhr.setRequestHeader('authorization',authorization);
},
success:function(response){
var video_urls = response.stream_urls.Video;
video_urls.forEach(function(value){
if (value.label == video_quality){
//mylog(value.file);
sendDownloadTaskToAria2(value.file,file_name,save_path);
}
});
}
});
}
// 将下载链接发送到 Aria2 下载
function sendDownloadTaskToAria2(download_url,file_name,save_path){
var json_rpc = {
id:'',
jsonrpc:'2.0',
method:'aria2.addUri',
params:[
[download_url],
{
dir:save_path,
out:file_name
}
]
};
GM_xmlhttpRequest({
url:aria2_url,
method:'POST',
data:JSON.stringify(json_rpc),
onerror:function(response){
mylog(response);
},
onload:function(response){
mylog(response);
if (!hasOpenAriac2Tab){
GM_openInTab('http://aria2c.com/',{active:true});
hasOpenAriac2Tab = true;
}
}
});
}
})();