// ==UserScript==
// @name Twitter Download Dog
// @namespace http://tampermonkey.net/
// @name:zh-TW Twitter 下載狗
// @name:zh-CN Twitter 下载狗
// @name:ja Twitter 犬をダウンロード
// @name:ko Twitter 개 다운로드
// @name:ru Twitter Загрузка собак
// @version 0.36
// @description DownLoad all the Twitter images and videos with one click without login twitter account.
// @description:zh-TW 一鍵下載所有Twitter圖片及視頻
// @description:zh-CN 一键下载所有Twitter图片及视频
// @description:ja ボタンを押してすべてのTwitter画像とビデオをダウンロードします。
// @description:ko 원 클릭 으로 다운로드 하 다 Twitter 이미지 및 영상
// @description:ru Массово скачивает все картинки и видео с Твиттер в один клик без необходимости логина
// @author macos2@github.com
// @match https://twitter.com/*
// @icon https://www.google.com/s2/favicons?domain=twitter.com
// @connect twsaver.com
// @connect twdown.net
// @connect savetweetvid.com
// @grant GM_download
// @grant GM_xmlhttpRequest
// ==/UserScript==
function add_area_to_header(){
var my_area=document.createElement("div");
my_area.id="my_area";
my_area.style="position: fixed; top: 25%; left:2%;width: 15%;";
let div_style="color: white;background-color: #1da1f2;border: 0px white;border-radius: 5px;margin: 2px;width: 205px";
add_button(my_area,
"DownLoad All",
function(){download_all();},
"button_download_all");
add_button(my_area,
"Scroll To End",
function(){ is_scroll_to_end=!is_scroll_to_end ; scroll_to_end_retry=0 ; scroll_to_end(null); },
"button_scroll_to_end");
var check_box_area=document.createElement("div");
check_box_area.style=div_style;
my_area.appendChild(check_box_area);
add_checkbox(check_box_area,"Auto Scroll To End",false,"auto_scroll");
add_checkbox(check_box_area,"Auto Expand Contents",true,"auto_expand");
//add_checkbox(check_box_area,"Other Twitter Contents",true,"other_twitter");
add_checkbox(check_box_area,"Video Include",true,"video_include");
add_checkbox(check_box_area,"Image Include",true,"image_include");
//add_checkbox(check_box_area,"Gif Include",true,"gif_include");
var seting_area=document.createElement("div");
seting_area.style=div_style;
//1:twsaver.com
//2:twdown.net
//3+:savetweetvid.com
seting_area.innerHTML="Video URL Parse Site</br><select id='video_url_parse_site'><option value=1>twsaver.com</option><option value=2>twdown.net</option><option value=3>savetweetvid.com</option></select></br>"+
"Image Quality<select id='image_qty'><option value='&name=large'>Large</option><option value='&name=orig'>Original</option><option value='&name=small'>Small</option></select></br>"+
"Video Quality<select id='video_qty'><option value='3'>Large</option><option value='2'>Middle</option><option value='1'>Small</option></select></br>"+
"FileName Format</br><input id='filename_fmt' name='filename_fmt' type='text' value='%u/%a-%t-%c-%f'>";
seting_area.id="video_url_parse_radio_area";
my_area.appendChild(seting_area);
var fn_fmt_help= add_button(seting_area,
"?",
function(){filename_fmt_help();},
"filename_fmt_help");
fn_fmt_help.style='color: #1da1f2;background-color: white;border: 0px white;border-radius: 5px;margin: 2px';
var status_area=document.createElement("div");
status_area.style="border: 0.5px solid gray;border-radius: 5px;margin: 2px;height: 120px;width: 205px";
my_area.appendChild(status_area);
var status_text=document.createElement("p");
status_text.id="status_text";
status_message(null);
status_area.appendChild(status_text);
document.body.appendChild(my_area);
}
function add_button(parent_element,text,fun,id){
var button=document.createElement("button");
button.innerHTML="<p style='margin: 2px; font-size: 13px;' >"+text+"</p>";
button.onclick=fun;
button.id=id;
button.style="color: white; background-color: #1da1f2; border: 0px white; border-radius: 5px;margin: 2px;height: 32px;width: 100px";
if(parent_element)parent_element.appendChild(button);
return button;
}
function add_checkbox(parent_element,text,value,id){
if(value){
parent_element.innerHTML+="<p style='margin: 2px;'><input type='checkbox' id="+id+" value="+value+" checked >"+text+"</p>";
}else{
parent_element.innerHTML+="<p style='margin: 2px;'><input type='checkbox' id="+id+" value="+value+">"+text+"</p>";
}
}
function filename_fmt_help(){
alert("=====Article Parameters=====\n"+
"\t%a \t: Article Author Name \n\t\t帖子作者名\n"+
"\t%c \t: Article Text Contents \n\t\t帖子文字内容\n"+
"\t%t \t: Article Created Time \n\t\t帖子发布时间\n"+
"=====Other Parameters=====\n"+
"\t%u \t: Current Site Twitter User Name \n\t\t当前浏览的Twitter用户名\n"+
"\t%q \t: Image or Video Quality Character,like \"L\",\"M\",\"S\"and \"O\" \n\t\t图像或视频的质量标注字符,例如:\"L\",\"M\",\"S\"and \"O\"\n"+
"\t%f \t: File Original Name \n\t\t文件的原名\n"+
"=====Special Character=====\n"+
"\t/ \t: Directory Separate,it will create all the parent directories which was not exist. it works on Windows and Linux."+
"\n\t\t目录分割符,会自动创建不存在父目录,Windows及Linux适用\n"+
"=====Note=====\n"+
"\tThe filename would be sliced if it's more than 128 utf8 characters,or it would be ingnored by the browser."+
"\n\t文件名会截取成小于127个utf-8字符长度,否则其会因无法保存而丢弃."
);
}
var is_scroll_to_end=false;
var scroll_to_end_retry=0;
function scroll_to_end(){
//console.log("is_scroll_to_end:"+is_scroll_to_end+"\tscroll_to_end_retry:"+scroll_to_end_retry);
if(is_scroll_to_end){
document.querySelector("#button_scroll_to_end").innerHTML="<p style='margin: 2px;' >Stop Scroll</p>";
if(window.scrollY < window.scrollMaxY){
scroll_to_end_retry=0;
setTimeout(function(){window.scrollByLines(window.scrollMaxY);scroll_to_end()},500);
status_message("Scroll To End...");
}else{
if(scroll_to_end_retry>5){
is_scroll_to_end=false;
scroll_to_end();
}else{
scroll_to_end_retry++;
setTimeout(function(){window.scrollByLines(window.scrollMaxY);scroll_to_end()},500);
status_message("Scroll To End,Retry:"+scroll_to_end_retry);
}
}
}else{
document.querySelector("#button_scroll_to_end").innerHTML="<p style='margin: 2px;' >Scroll To End</p>";
status_message(null);
if(download_all_scroll_to_end){
window.scrollTo(0,0);
status_message("Start DownLoad After 1 s");
setTimeout(download_all_init(),1000);
}
}
}
var is_download_all=false;
function download_all(){
is_download_all=!is_download_all;
var button=get_by_selector("#button_download_all");
if(is_download_all){
download_all_scroll_to_end=false;
download_all_current_article=0;
status_message("Start DownLoad After 1 s");
setTimeout(download_all_init(),1000);
button.innerHTML="<p style='margin: 2px;' >Stop</p>";
}else{
button.innerHTML="<p style='margin: 2px;' >DownLoad All</p>";
}
}
var download_count=0;
function status_message(text){
var status_text=get_by_selector("#status_text");
if(status_text){
status_text.innerHTML="";
if(download_count>0){
status_text.innerHTML+="<p style='margin: 2px;'>"+download_count+"Files Downloading</p>";
}
if(text){
status_text.innerHTML+="<p style='margin: 2px;'>"+text+"</p>";
}else{
status_text.innerHTML+="<p style='margin: 2px;'>Ready...</p>";
}
}
}
function get_by_xpath(express,node){
var i=document.evaluate(express,node,null,XPathResult.ANY_TYPE,null);
return i.iterateNext();
}
function get_by_selector(express){
var i=document.querySelector(express);
return i;
}
var download_all_scroll_to_end=false;
var download_all_current_article=0;
function download_all_init(){
if(get_by_selector("#auto_scroll").checked){
if(download_all_scroll_to_end==false){
download_all_scroll_to_end=true;
get_by_selector("#button_scroll_to_end").click();
return;
}
}
//init download param
download_image=get_by_selector("#image_include").checked;
download_video=get_by_selector("#video_include").checked;
download_expend=get_by_selector("#auto_expand").checked;
download_twitter_name="";
let download_twitter_name_area=get_by_xpath("//main/div/div/div/div[1]/div/div[2]/div/div/div[1]/div/div[2]/div/div",document);
let i=download_twitter_name_area.querySelectorAll("span");
i.forEach(function (x){ download_twitter_name+=x.innerText;});
download_retry=0;
video_url_parse_site=Number(document.querySelector("#video_url_parse_site").value);
image_quality=document.querySelector("#image_qty").value;
video_quality=Number(document.querySelector("#video_qty").value);
filename_fmt=document.querySelector("#filename_fmt").value;
if(filename_fmt==="")filename_fmt="%u/%a-%t-%c-%f";//in case of empty filename_fmt;
var first_article=get_by_xpath("//section/div/div/div/div/div/article",document);
var continue_article=get_by_selector("#current_target");
if(continue_article!=null){
if(confirm("Continute DownLoad Progress Before?\n继续之前的爬取进度吗?")){
first_article=continue_article;
}else{
window.scrollTo(0,0);
continue_article.id=null;
}
}
if(first_article){
first_article.id="current_target";
setTimeout(query_article(),500);
}
}
var download_image=false;
var download_video=false;
var download_expend=false;
var download_twitter_name="";
function query_article(){
download_all_current_article++;
console.log("Querying "+download_all_current_article+" Article");
status_message("Querying "+download_all_current_article+" Article");
var article=get_by_selector("#current_target");
if(article){
var brect=article.getBoundingClientRect();
window.scrollTo(brect.x,window.scrollY+brect.y);
if(download_expend){
//var expend=article.querySelector("article div:nth-child(2) div div div div div:nth-child(2) div div div div span span");
//var expend=get_by_xpath("div/div/div/div[2]/div[2]/div[2]/div[2]/div/div/div/div[2]/div/div/div/div/div/div/div[2]/div/div[2]/div/div/span",article);
var expend=get_by_xpath("div/div/div/div[2]/div[2]/div[2]/div[2]/div/div/div/div/div/div/div/div/div/div/div[2]/div/div[2]/div/div/span",article);
if(expend)expend.click();
}
setTimeout(function(){download_article_content(article);},200);
download_retry=0;
}else{
download_all_current_article--;
}
}
var filename_fmt="%f";
function download_filename_preprocess(article){
let temp=get_by_xpath("div/div/div/div[2]/div[2]/div[1]/div/div/div[1]/a/time/@datetime",article);
if (temp==null){
//this article had been deleted,skip it.
return temp;
}
let article_time=temp.value;
let article_twitter_name_area=get_by_xpath("div/div/div/div[2]/div[2]/div[1]/div/div/div[1]/div[1]/a/div",article);
let article_twitter_name="";
var i=article_twitter_name_area.querySelectorAll("span");
i.forEach(function (x){ article_twitter_name+=x.innerText;});
let article_describe="";
i=document.evaluate("div/div/div/div[2]/div[2]/div[2]/div[1]/div/span/text()",article,null,XPathResult.ANY_TYPE,null);
var j=i.iterateNext();
while(j!=null){
article_describe+=j.textContent;
j=i.iterateNext();
}
temp=filename_fmt;
article_twitter_name=article_twitter_name.replaceAll("\\","").replaceAll("/","").replaceAll("*","").replaceAll("?","").replaceAll("\"","").replaceAll(":","").replaceAll("<","").replaceAll(">","").replaceAll("|","");
article_describe=article_describe.replaceAll("\\","").replaceAll("/","").replaceAll("*","").replaceAll("?","").replaceAll("\"","").replaceAll(":","").replaceAll("<","").replaceAll(">","").replaceAll("|","");
download_twitter_name=download_twitter_name.replaceAll("\\","").replaceAll("/","").replaceAll("*","").replaceAll("?","").replaceAll("\"","").replaceAll(":","").replaceAll("<","").replaceAll(">","").replaceAll("|","");
temp=temp.replaceAll("%a",article_twitter_name);
temp=temp.replaceAll("%t",article_time);
temp=temp.replaceAll("%c",article_describe);
temp=temp.replaceAll("%u",download_twitter_name);
return temp;
}
function download_filename_postprocess(filename_preprocess,original_filename,qty_char=""){
let temp=filename_preprocess;
let suffix=original_filename.match("[\.][^\.]+$");
let name=original_filename.match("^.*(?=\\.)");
if(suffix==null)suffix=".txt"; //当作普通txt处理,让用户自行判断该文件是什么类型。
temp=temp.replaceAll("%f",name);
temp=temp.replaceAll("%q",qty_char);
let fn=temp.match("[^/]*$")[0];
let dir=temp.match("^.*/");
if(dir===null){
dir="";
}else{
dir=dir[0];
}
return dir+fn.slice(0, 120-suffix.length)+suffix;
}
function download_article_content(article){
let filename_preprocess=download_filename_preprocess(article);
if(filename_preprocess!=null){
//console.log("Download"+article_twitter_name+" @ "+article_time+" Article Content");
if(download_image) download_article_image(article,filename_preprocess);
if(download_video) download_article_video(article,filename_preprocess);
}
query_next_article();
}
function download_file(url,filename){
download_count++;
GM_download({
url:url,
name:filename,
saveAs:false,
onload:function(){download_count--;status_message(null);},
onerror:function(e){download_count--;status_message(null);console.log("Error:"+e.error+"\n");},
onprogress:function(e){status_message(e.finalUrl+" : "+Math.round(e.loaded/e.totalSize*1000)/10+" %");},
});
}
//"&name=large"
//"&name=orig"
//"&name=small"
var image_quality="&name=large";
function download_article_image(article,filename_preprocess){
var nodelist=article.querySelectorAll("img");
var src="";
var filename="";
var temp=null;
let qty_char="";
switch (image_quality){
case "&name=large":
qty_char="L";
break;
case "&name=orig":
qty_char="O";
break;
case "&name=small":
qty_char="S";
break;
default:break;
}
for (var i=1;i<nodelist.length;i++){
temp=nodelist[i].src.match(".*(?=[?])");
if(temp==null)continue;
src=temp[0];
filename=download_filename_postprocess(filename_preprocess,src.match("[^/]*$")[0]+".jpg",qty_char);
download_file(src+"?format=jpg"+image_quality,filename);
//GM_download({
// url:src+"?format=jpg"+image_quality,
// name:filename,
// saveAs:false,
//});
}
}
//1:twsaver.com
//2:twdown.net
//3+:savetweetvid.com
var video_url_parse_site=1;
//1:Low
//2:Middle
//3:Hight
var video_quality=3;
function download_article_video(article,filename_preprocess){
if(article.querySelector("video")==null)return;
var i=document.evaluate("div/div/div/div[2]/div[2]/div[1]/div/div/div[1]/a/@href",article,null,XPathResult.ANY_TYPE,null).iterateNext();
if(i==null)return;
var t="https://twitter.com/i/status/"+i.value.match("[^/]*$");
switch (video_url_parse_site){
case 1://twsaver.com
GM_xmlhttpRequest({
method:'POST',
url:'https://twsaver.com/system/action.php',
headers:{'Content-Type':'application/x-www-form-urlencoded,charset=UTF-8',Referer:'https://twsaver.com/'},
responseType:'json',
data:"url="+t,
context:{filename_preprocess},
onload:download_article_video_twsaver_cb,
});
break;
case 2://twdown.net
GM_xmlhttpRequest({
method:'POST',
url:'https://twdown.net/download.php',
headers:{'Content-Type':'application/x-www-form-urlencoded',Referer:'https://twdown.net'},
data:"URL="+t,
context:{filename_preprocess},
onload:download_article_video_twdown_cb,
});
break;
default://savetweetvid.com
GM_xmlhttpRequest({
method:'POST',
url:'https://www.savetweetvid.com/downloader',
headers:{'Content-Type':'application/x-www-form-urlencoded',Referer:'https://www.savetweetvid.com'},
data:"url="+t,
context:{filename_preprocess},
onload:download_article_video_savetweetvid_cb
});
break;
}
}
function download_article_video_twsaver_cb(result){
let url="";
var obj=result.response;
let qty_char="";
if(obj==null)return;
switch (video_quality){
case 1://small
url=obj.links[0].url;
qty_char="S";
break;
case 2://middle
if(obj.links.length>=2){
url=obj.links[obj.links.length-2].url;
}else{
url=obj.links[obj.links.length-1].url;
}
qty_char="M";
break;
default://large
qty_char="L";
url=obj.links[obj.links.length-1].url;
break;
}
let filename=download_filename_postprocess(result.context.filename_preprocess,url.match("[^/]*(?=\\?)")[0],qty_char);
download_file(url,filename);
//GM_download({
// url:url,
// name:filename,
// saveAs:false,
// });
}
function download_article_video_twdown_sort(a,b){
return a.res-b.res;
}
function download_article_video_twdown_cb(result){
var str=result.responseText;
if(str==null)return;
let parser=new DOMParser();
let doc=parser.parseFromString(str,"text/html");
let i=1;
let temp=0;
let order=new Array();
let xpath_res;
let qty_char="";
while(1){
xpath_res=doc.evaluate("/html/body/div[2]/div/center/div[2]/div/div[3]/table/tbody/tr["+i+"]/td[2]/text()",doc,null,XPathResult.ANY_TYPE,null).iterateNext();
if(xpath_res==null)break;
temp=Number(xpath_res.textContent.replace('x',''));
if(temp){
order.push({i:i,res:temp});
}else{
break;
}
i++;
}
order.sort(download_article_video_twdown_sort);
let a=null;
switch (video_quality){
case 1://small
a=doc.evaluate("/html/body/div[2]/div/center/div[2]/div/div[3]/table/tbody/tr["+order[0].i+"]/td[4]/a",doc,null,XPathResult.ANY_TYPE,null).iterateNext();
qty_char="S";
break;
case 2://middle
if(order.length>=2){
a=doc.evaluate("/html/body/div[2]/div/center/div[2]/div/div[3]/table/tbody/tr["+order[order.length-2].i+"]/td[4]/a",doc,null,XPathResult.ANY_TYPE,null).iterateNext();
}else{
a=doc.evaluate("/html/body/div[2]/div/center/div[2]/div/div[3]/table/tbody/tr["+order[order.length-1].i+"]/td[4]/a",doc,null,XPathResult.ANY_TYPE,null).iterateNext();
}
qty_char="M";
break;
default://large
a=doc.evaluate("/html/body/div[2]/div/center/div[2]/div/div[3]/table/tbody/tr["+order[order.length-1].i+"]/td[4]/a",doc,null,XPathResult.ANY_TYPE,null).iterateNext();
qty_char="L";
break;
}
let url=a.href;
let filename=download_filename_postprocess(result.context.filename_preprocess,url.match("[^/]*(?=\\?)")[0],qty_char);
download_file(url,filename);
//GM_download({
// url:url,
// name:filename,
// saveAs:false,
// });
}
function download_article_video_savetweetvid_cb(result){
var str=result.responseText;
if(str==null)return;
let parser=new DOMParser();
let doc=parser.parseFromString(str,"text/html");
let a=null;
let i=0;
let qty_char="";
switch (video_quality){
case 1://small
while(doc.evaluate("//table/tbody/tr["+i+"]/td[4]/a",doc,null,XPathResult.ANY_TYPE,null)){
i++;
}
a=doc.evaluate("//table/tbody/tr["+i-1+"]/td[4]/a",doc,null,XPathResult.ANY_TYPE,null).iterateNext();
qty_char="S";
break;
case 2://middle
a=doc.evaluate("//table/tbody/tr[2]/td[4]/a",doc,null,XPathResult.ANY_TYPE,null);
if(a==null){
a=doc.evaluate("//table/tbody/tr[1]/td[4]/a",doc,null,XPathResult.ANY_TYPE,null);
}
a=a.iterateNext();
qty_char="M";
break;
default://large
a=doc.evaluate("//table/tbody/tr[1]/td[4]/a",doc,null,XPathResult.ANY_TYPE,null).iterateNext();
qty_char="L";
break;
}
let url=a.href;
let filename=download_filename_postprocess(result.context.filename_preprocess,url.match("[^/]*(?=\\?)")[0],qty_char);
download_file(url,filename);
//GM_download({
// url:url,
// name:filename,
// saveAs:false,
// });
}
var download_retry=0;
function query_next_article(){
var pre_article=get_by_selector("#current_target");
var next_article=get_by_xpath("following::article",pre_article);
if(next_article==null){
download_retry++;
if(download_retry<10){
console.log("Querying "+download_all_current_article+" Article \tRetry: "+download_retry);
setTimeout(query_next_article(),500);
}else{
download_all();//switch to stop the download process.
status_message(null);
}
return;
}
download_retry=0;
if(pre_article)pre_article.id=null;
if(next_article)next_article.id="current_target";
if(is_download_all){
setTimeout(function(){query_article();},100);
}else{
status_message(null);
}
}
(function() {
'use strict';
add_area_to_header();
console.log("good dog");
// Your code here...
})();