Opera: Youtube视频下载(Part 2)
Tuesday, 18. July 2006, 13:58:00
// ==UserScript==
// @name Show Youtube flv URLs
// @author Returner
// @include http://*youtube.com/*
// ==/UserScript==
var autoLoad = false; //用这个变量控制页面加载后是否调用loadAll()
vidCol = new Array();
urlCol = new Array();
function getURL(vid,sURL) //读取并分析视频播放页面,获取flv URL
{
var req = new XMLHttpRequest();
if (req) {
req.onreadystatechange = function() {
if (req.readyState == 4) {
if (req.status == 200) {
if (document.getElementById(vid).innerHTML.indexOf('Retrieving')!=0)
return; //如果Retrieving的时候Content Box的内容改变了,则不写回获得到的URL
var res = req.responseText;
var basei = res.indexOf('/watch_fullscreen?');
if (basei>0){
document.getElementById(vid).innerHTML = '<strong>Download flv</strong>';
document.getElementById(vid).href='http://www.youtube.com/get_video?' +
res.substring(basei+18,res.indexOf('&fs=1&title=', basei));
}
else
alert('No video found');
}else{
document.getElementById(vid).innerHTML = 'Failed to get flv URL';
}
}
};
document.getElementById(vid).innerHTML = 'Retrieving flv URL...';
req.open('GET', sURL);
req.send(null);
}
}
function loadAll()//获取页面中所有视频的flv标签(会造成同时请求多个页面)
{
for (var i in vidCol) getURL(vidCol[i],urlCol[i]);
}
function addl()
{
var links=document.links;
var j = 0;
var indexHelp = -1;
if (!links.length) return;
for (var i = 0, thisLink; thisLink = links[i]; i++){
//忽略href=""的链接(一般都是有onclick属性的链接?)
//忽略有onclick属性的链接
//处理的URL必须是播放页面URL(http://.../watch?v=...)
//忽略Play all videos链接
//忽略图片链接
if(String(thisLink) != location.href &&
thisLink.getAttribute('onclick')==null &&
String(thisLink).indexOf("&feature=PlayList") < 0 &&
thisLink.innerHTML.indexOf("<IMG") < 0 &&
String(thisLink).match(/^http\S+\/watch\?v/i)!= null
){
var nextLink = links[i+1];
if(nextLink && !String(nextLink.getAttribute('id')).search(/^vid[0-9]+$/)){
nextLink.outerHTML = nextLink.outerHTML.replace(/vid[0-9]+/g,'vid'+i);
}else{
var addHTML = '<br><a ID=vid'+i+' href="javascript:getURL(\''+'vid'+i+
'\',\''+thisLink+'\')"><strong>Get flv URL</strong></a>';
thisLink.insertAdjacentHTML("afterEnd",addHTML);//插入链接
}
urlCol[j] = thisLink; //保存以供loadAll()使用
vidCol[j++] = 'vid'+i; //保存以供loadAll()使用
} else if (indexHelp < 0 && String(thisLink).indexOf("/t/help_center") >= 0) {
indexHelp = i; //寻找页面顶部的Help链接
}
}
if(j==0) return; //if no watch_url is found, exit
//no need to use 'else' - that would be an extra command
//and the earlier 'return' removes the need for 'else' here
if(indexHelp > 0){ //if the 'Help' link is found
var addedLink = null;
if (addedLink = document.getElementById('getAllflv')){
addedLink.innerHTML = '<b>Retrieve all (' + j + ') flv URL'+(j>1?'s':'')+'</b>';
//如果已有Retrieve all链接,则更新之
}else{
var helpLink = links[indexHelp];
var strClass = helpLink.getAttribute('class');
if (strClass) strClass = 'class="'+strClass+'"';
else strClass='';
helpLink.insertAdjacentHTML("afterEnd",
'<span id="flvSpan" class="utilDelim">|</span><a ' + strClass +
' href="javascript:loadAll()" id="getAllflv"><b>Retrieve all (' + j +
') flv URL'+(j>1?'s':'')+'</b></a>'
);//在Help链接后面插入Retrieve all链接
}
}
if(autoLoad) loadAll();
}
//content box process
var htmlBak = new Array();
function preShiftImage(bar_id){
var IBrowser = imageBrowsers[bar_id];
for (var i=0; i<IBrowser.display_array.length; i++)
htmlBak[i] = document.getElementById("title1_" + IBrowser.root_div_id + "_" + i).innerHTML;
//备份content box切换图片前的对应标题文字链接
}
function postShiftImage(bar_id){
var IBrowser = imageBrowsers[bar_id];
for (var i=0; i<IBrowser.display_array.length; i++){
var vid = htmlBak[i].match(/ID=\"?vid(\d+)/i)[1];
if(!vid) continue;
var newTitle = IBrowser.display_array[i].title1;
var addHTML = '<br><a ID=vid'+vid+' href="javascript:getURL(\''+'vid'+vid+
'\',\''+newTitle.match(/\/watch\?v[^\"\']+/i)+
'\')"><strong>Get flv URL</strong></a>'; //更新vid
document.getElementById("title1_" +
IBrowser.root_div_id + "_" + i).innerHTML = newTitle + addHTML;
//在标题后重新添加Get flv URL链接
}
}
function contentBoxProc()
{
var imgsCB = document.images;
var strClick;
if (!imgsCB.length) return;
for (var i = 0, thisIMG; thisIMG = imgsCB[i]; i++){
if ((strClick = thisIMG.getAttribute('onclick')) &&
strClick.indexOf('preShiftImage') == -1 ){
var idx = strClick.replace(/(;shift\S+(\(\S+\)))/i,
";preShiftImage$2$1;postShiftImage$2");
thisIMG.setAttribute('onclick',idx); //在shiftImage()前后插入上面两个函数
}
}
}
//如果移植到Opera 8甚至其它浏览器,应该删掉监听DOMContentLoaded这段代码
window.opera.addEventListener('AfterEvent.DOMContentLoaded',
function (e) {
addl();
//contentBoxProc();
},
false);
window.addEventListener('load',
function (e) {
addl();
contentBoxProc();
},
false);
脚本监听Opera 9新增的DOMContentLoaded事件,在页面载入后运行。同时监听Load事件,在页面完全加载(包括图像、Content Box)后再次执行。开头的注释中指定了应用脚本和不应用脚本的页面URL(之前在视频播放页面是不应用脚本的,现在去掉这个限制了)。DOMContentLoaded事件触发后,执行的是addl()函数。该函数扫描页面中的所有link,对符合条件的link(包含/watch?v=,不包含feature=PlayList,不是图像链接),构造一个对应的"Get flv URL"链接,链接的href为
javascript:getURL('vid#',LinkURL)
这个脚本函数调用。其中,每个URL都有一个独立的vid#,#为链接的"序号",LinkURL为找到的链接的原始URL。利用
links[i].insertAdjacentHTML将链接插入到找到的文本链接(正常情况下,这个链接就是视频的标题)后面。注意,如果用
innerHTML+="</a><br><a href=...>Get flv URL</a>"
这样是不正确的,前面的</a>会被自动去掉。虽然最终显示出来的结果没有问题,但是实质上是<a>标签的嵌套,还是不太爽...
枚举链接的同时,也会分析链接是不是页面顶部的那个Help链接,如果是,则把它的index记下来(indexHelp)。在处理完所有链接,并至少发现了一个正确的文本链接(j>0),则在Help链接后面添加一个Retrieve all flv URLs链接,href也是一个脚本,执行loadAll()函数。
loadAll()的功能是自动执行所有视频链接的getURL()函数。如果变量var autoLoad = true,在执行完addl后会自动执行loadAll()。如果没有设置autoLoad为true,可通过两种方式手动执行loadAll():一是在地址栏输入javascript:loadAll(),二是点击Help旁边新添加的那个链接。
当页面完全加载后,触发Load事件。这次主要是针对DOMContentLoaded触发是还没有完全生成(显示Loading)的Content Box,再次执行addl()进行全页面扫描(嗯,这种做法其实并不高效),然后执行contentBoxProc(),在Contetn Box的左右箭头的onclick脚本里面插入一些东西。
这样,对addl()来说,就要解决一个问题:如何避免重复插入“Get flv URL”链接。程序采用的解决方法很简单,看看当前处理的链接的下一个链接是否存在vid#这样的ID值。如果不存在,即可放心地去添加链接,如果存在,就更新一下它的vid#(前面说了,#是一个数字,为链接的“序号”,如果这次addl()在前面插入了新的链接,则这个已存在的链接的vid也要做相应修改,以免跟前面插入的新链接的vid重复)。
由于可能找到更多的flv播放链接,所以也要更新一下Help旁边的Retrieve all链接。程序通过
document.getElementById('getAllflv')
来判断是否已经存在Retrieve all链接。如果存在,则只要重写一下它的innerHTML,如果不存在,则添加(第二次一般都不会执行到“不存在”这种情况,除非Help链接本身不存在,从而第一次就没有把Retrieve all链接插进去)。
至于preShiftImage()和postShiftImage(),由于点击Content Box的左右箭头,会执行一个shiftImage函数,重写Content Box的内容,因此编写了这两个函数,实现shiftImage后重新添加“Get flv URL”链接。这两个函数用到了youtube本身关于Content Box的函数的一些东西(可参考shiftImage()的实现)。
页面出现Get flv URL链接后,就可以通过点击这些链接来得到对应视频的下载URL了。当点击页面上的Get flv URL时,执行getURL函数。getURL函数通过vid参数可知道点击的是哪个链接(即链接的ID),sURL参数则是对应的视频播放页面的URL。它获得flv URL的方式与前面的那个菜单命令相同,不过考虑到可能会同时执行多个getURL(同时获取多个视频的URL ),采用了异步执行的方式。函数一开始把链接的文本改写成"Retrieving flv URL..."提示用户等待,等服务器返回200时,则再改成"Download flv",并把链接的href由调用getURL()的脚本改成flv的URL。接下来用户就可以用这个链接来保存flv文件了。如果服务器返回其它值,则文本该为"Failed to get flv URL",不改变href,这样用户可以再次通过点击链接来重新执行getURL()。
在这里下载最新版本的脚本:
youtubeshowurl.js









How to use Quote function: