ㄷㄷ
(function() {"use strict"; (function script()
{
var version = "4.1.1", hash = "3dc8204";
// -- Object tools --
// has(obj, key) - Does the object contain the given key?
var has = Function.call.bind(Object.prototype.hasOwnProperty);
// extend(obj, a, b, c, ...) - Add the properties of other objects to this
// object
function extend(obj)
{
for (var i = 1, max = arguments.length; i < max; i ++)
for (var key in arguments[i])
if (has(arguments[i], key))
obj[key] = arguments[i][key];
return obj;
}
// merge(a, b, c, ...) - Create an object with the merged properties of other
// objects
function merge()
{
return extend.bind(null, {}).apply(null, arguments);
}
var copy = merge;
// -- Array tools --
// arrayify(a) - Turn an array-like object into an array
var slice = Function.call.bind(Array.prototype.slice),
arrayify = slice;
// index(on, a) - Index an array of objects by a key
function index(on, a)
{
var obj = {};
for (var i = 0, max = a.length; i < max; i ++)
if (a[i].has(on))
obj[a[i][on]] = a[i];
return obj;
}
// pluck(on, a) - Return a list of property values
function pluck(on, a)
{
return a.map(function(o) { return o[on]; });
}
// indexpluck(on, a) - Index and pluck
function indexpluck(key, value, a)
{
var obj = {};
for (var i = 0, max = a.length; i < max; i ++)
if (a[i].has(key))
obj[a[i][key]] = a[i][value];
return obj;
}
// equi(on, a, b, c, ...) - Performs a equijoin on all the objects in the given
// arrays
function equi(on)
{
var obj = {}, ret = [];
for (var i = 1, imax = arguments.length; i < imax; i ++)
for (var j = 0, jmax = arguments[i].length; j < jmax; j ++)
if (has(arguments[i][j], on))
obj[arguments[i][j][on]] = merge(obj[arguments[i][j][on]] || {}, arguments[i][j])
for (var prop in obj)
if (has(obj, prop))
ret.push(obj[prop]);
return ret;
}
// -- URI tools --
// decodeURIPlus(str) - Decode a URI component, including conversion from '+'
// to ' '
function decodeURIPlus(str)
{
return decodeURIComponent(str.replace(/\+/g, " "));
}
// encodeURIPlus(str) - Encode a URI component, including conversion from ' '
// to '+'
function encodeURIPlus(str)
{
return encodeURIComponent(str).replace(/ /g, "%20");
}
// decodeQuery(query) - Convert a query string to an object
function decodeQuery(query)
{
var obj = {};
query.split("&").forEach(function(str) {
var m = str.match(/^([^=]*)=(.*)$/);
if (m)
obj[decodeURIPlus(m[1])] = decodeURIPlus(m[2]);
else
obj[decodeURIPlus(str)] = "";
});
return obj;
}
// encodeQuery(query) - Convert a query string back into a URI
function encodeQuery(query)
{
var components = [];
for (var name in query)
if (has(query, name))
components.push(encodeURIPlus(name) + "=" + encodeURIPlus(query[name]));
return components.join("&");
}
// URI(uri) - Convert a URI to a mutable object
function URI(uri)
{
if (!(this instanceof URI))
return new URI(uri);
var m = uri.match(/([^\/]+)\/\/([^\/]+)([^?]*)(?:\?(.+))?/);
if (m)
{
this.protocol = m[1];
this.host = m[2];
this.pathname = m[3];
this.query = m[4] ? decodeQuery(m[4]) : {};
}
else
{
this.href = uri;
this.query = {};
}
}
URI.prototype.toString = function() {
if (this.href)
return this.href;
var encq = this.query && encodeQuery(this.query);
return (this.protocol || "http") + "//" + this.host + this.pathname + (encq ? "?" + encq : "");
};
// -- Function tools --
function identity(a) { return a; }
// runWith - Run xxJavaScript code with an object's properties as local variables
function runWith(str, obj)
{
var names = [],
values = [];
for (var name in obj)
if (has(obj, name))
{
names.push(name);
values.push(obj[name]);
}
var func = Function.apply(null, names.concat(str));
return func.apply(null, values);
}
// -- String tools --
// format(str, obj) - Formats a string with a syntax similar to Python template
// strings, except the identifiers are executed as xxJavaScript code.
function format(str, obj)
{
return str.replace(/\${([^}]+)}/g, function(match, name) {
try {
return runWith("return (" + name + ");", obj);
}
catch (e) {
return match;
}
});
}
// trim(str) - Trims whitespace from the start and end of the string.
function trim(str)
{
return str.replace(/^\s+/, "").replace(/\s+$/, "");
}
// formatFileName(str) - Formats a file name (sans extension) to obey certain
// restrictions on certain platforms. Makes room for a 4 character extension,
// ie. ".webm".
function formatFileName(str)
{
return str
.replace(/[\\/<>:"\?\*\|]/g, "-")
.replace(/[\x00-\x1f]/g, "-")
.replace(/^\./g, "-")
.replace(/^\s+/, "")
.substr(0, 250);
}
// Try - Do things or do other things if they don't work
var Try = (function() {
var self = {
all: all,
};
function all()
{
var args = Array.prototype.slice(arguments),
arg;
for (var i = 0, imax = arguments.length; i < imax; i ++)
if (arguments[i] instanceof Array)
for (var j = 0, jmax = arguments[i].length; j < jmax; j ++)
try {
return arguments[i][j]();
}
catch (e) {}
else
try {
return arguments[i]();
}
catch (e) {}
}
return self;
})();
// VideoInfo - Get global video metadata
var VideoInfo = (function() {
var self = {
init: init,
};
// init() - Populates the VideoInfo object with video metadata
function init()
{
self.title = Try.all(
function() {
return yt.playerConfig.args.title;
},
function() {
return document.querySelector("meta[name=title]").getAttribute("content");
},
function() {
return document.querySelector("meta[property=\"og:title\"]").getAttribute("content");
},
function() {
return document.querySelector("#watch-headline-title > .yt-uix-expander-head").getAttribute("title");
},
function() {
return document.title.match(/^(.*) - YouTube$/)[1];
}
);
self.author = Try.all(
function() {
return document.querySelector("#watch7-user-header > .yt-user-name").textContent;
},
function() {
return document.querySelector("#watch7-user-header .yt-thumb-clip img").getAttribute("alt");
},
function() {
return document.querySelector("span[itemprop=author] > link[itemprop=url]").getAttribute("href").match(/www.youtube.com\/user\/([^\/]+)/)[1];
}
);
self.date = Try.all(
function() {
return new Date(document.getElementById("eow-date").textContent);
}
);
self.video_id = Try.all(
function() {
return new URI(document.location.href).query.v;
}
);
self.seconds = Try.all(
function() {
return Math.floor(Number(yt.playerConfig.args.length_seconds));
}
);
if (self.date)
{
self.day = ("0" + self.date.getDate()).match(/..$/)[0];
self.month = ("0" + (self.date.getMonth() + 1)).match(/..$/)[0];
self.year = self.date.getFullYear().toString();
self.date.toString = function() {
return self.year + "-" + self.month + "-" + self.day;
};
}
if (self.seconds)
self.duration = Math.floor(self.seconds / 60) + ":" + self.seconds % 60;
}
return self;
})();
var Languages = {
"ar": {"language": "Arabic","credit0-name": "Anas Abu-Haimed","credit0-url": "http://www.3thical.org","download-button-tip": "تحميل هذا الفيديو","download-button-text": "تحميل","menu-button-tip": "اختر الصيغة","format-tip": "الصيغة ","group-options": "الخيارات","group-high-definition": "جودة عالية","group-standard-definition": "جودة متوسطة","group-mobile": "للجوال","group-unknown": "صيغة غير معروفه","group-update": "يوجد تحديثات","option-check": "التأكد من وجود تحديثات","option-webm": "افضل WebM","option-sizes": "الحصول على حجم ملف الفيديو","option-format": "صيغة الفيديو","option-itags": "الصيغ المفضلة","button-options": "خيارات","button-options-close": "اغلاق","button-update": "اضغط هنا لتحديث YouTube Video Download","error-no-downloads": "لايوجد محتوى قابل للتحميل"},
"cs": {"language": "Czech","credit0-name": "janwatzek","credit0-url": "http://userscripts.org/users/janwatzek","download-button-tip": "Uložit video na pevny disk","download-button-text": "Stahnout","menu-button-tip": "Vyberte format ke staženi","group-options": "Nastaveni","group-high-definition": "Vysoke rozlišeni","group-standard-definition": "Standardni rozlišeni","group-mobile": "Mobile","group-unknown": "Neznamy format","group-update": "Nova verze skriptu YouTube Video Download je dostupna ke staženi!","option-check": "Kontrolovat aktualizace","option-format": "Format nazvu videa","button-options": "nastaveni","button-options-close": "close","button-update": "Klikněte sem pro aktualizaci","error-no-downloads": "Žadne formaty nejsou dostupne ke staženi"},
"de": {"language": "German","credit0-name": "QuHno","credit0-url": "http://userscripts.org/users/348658","download-button-tip": "Video auf der Festplatte speichern","download-button-text": "Download","menu-button-tip": "Weitere Formate wahlen","group-options": "Einstellungen","group-high-definition": "Hohe Auflosung","group-standard-definition": "Standard Auflosung","group-mobile": "Mobil","group-unknown": "Unbekanntes Format","group-update": "Eine neue Version steht zur Verfugung","option-check": "Auf neue Version uberprufen","option-webm": "WebM bevorzugen","option-sizes": "Video Dateigroße ermitteln","option-format": "Titel Format","button-options": "Einstellungen","button-options-close": "Schließen","button-update": "Hier klicken, um YouTube Video Download zu aktualisieren","error-no-downloads": "Keine herunterladbaren Streams gefunden"},
"en": {"language": "English","download-button-tip": "Download this video","download-button-text": "Download","menu-button-tip": "Choose from additional formats","format-tip": "Format ","group-options": "Options","group-high-definition": "High definition","group-standard-definition": "Standard definition","group-mobile": "Mobile","group-unknown": "Unknown formats","group-update": "An update is available","option-check": "Check for updates","option-webm": "Prefer WebM","option-sizes": "Get video filesize","option-format": "Title format","option-itags": "Favourite formats","button-options": "options","button-options-close": "close","button-update": "Click here to update YouTube Video Download","error-no-downloads": "No downloadable streams found"},
"fr": {"language": "French","credit0-name": "jok-r","credit0-url": "http://userscripts.org/users/87056","download-button-tip": "Telecharger cette video","download-button-text": "Telecharger","menu-button-tip": "Choisissez le format a telecharger","group-options": "Options","group-high-definition": "Haute definition","group-standard-definition": "Definition standard","group-mobile": "Mobile","group-unknown": "Format inconnu","group-update": "Une nouvelle version de YouTube Video Download est disponible","option-check": "Verifier les mises a jour","option-format": "Format du nom de fichier","button-options": "options","button-options-close": "close","button-update": "Cliquer ici pour mettre a jour maintenant","error-no-downloads": "Pas de formats de telechargement disponible"},
"id": {"language": "Indonesian","credit0-name": "Bayu Aditya H","credit0-url": "http://ba.yuah.web.id/?asal=gmytdlfl","download-button-tip": "Unduh video ini","download-button-text": "Unduh","menu-button-tip": "Pilih dari format tambahan","format-tip": "Format ","group-options": "Opsi","group-high-definition": "Definisi tinggi","group-standard-definition": "Definisi standar","group-mobile": "Perangkat bergerak","group-unknown": "Format tak dikenal","group-update": "Perbaruan tersedia","option-check": "Periksa pembaruan","option-webm": "Utamakan WebM","option-sizes": "Dapatkan ukuran video","option-format": "Format judul","option-itags": "Format kesukaan","button-options": "opsi","button-options-close": "tutup","button-update": "Klik di sini untuk memperbarui YouTube Video Download","error-no-downloads": "Tidak ada aliran yang dapat diunduh ditemukan"},
"it": {"language": "Italian","credit0-name": "Kharg","credit0-url": "http://userscripts.org/users/kharg","download-button-tip": "Salva il video nell'HD","download-button-text": "Scarica","menu-button-tip": "Scegli un formato da scaricare","group-options": "Opzioni","group-high-definition": "Alta definizione","group-standard-definition": "Qualita standard","group-mobile": "Mobile","option-check": "Controlla la disponibilita di aggiornamenti","option-format": "Formato titolo","button-options": "opzioni","button-options-close": "close","error-no-downloads": "Nessun formato da scaricare disponibile"},
"ja-JP": {"language": "Japanese","credit0-name": "K-M","credit0-url": "http://userscripts.org/users/184613","download-button-tip": "ハードディスクにビデオを保存","download-button-text": "ダウンロード","menu-button-tip": "ダウンロードする形式を選択","group-options": "オプション","group-high-definition": "高画質","group-standard-definition": "普通の画質","group-mobile": "Mobile","group-unknown": "不明な形式","group-update": "YouTube Video Downloadの更新があります","option-check": "更新を確認","option-format": "タイトルの形式","button-options": "オプション","button-options-close": "close","button-update": "ここをクリックすると更新します","error-no-downloads": "ダウンロードできません"},
"ko": {"language": "Korean","credit0-name": "Joonhyuk Song","credit0-url": "http://blog.naver.com/fprhqkrtk303","download-button-tip": "이 비디오를 다운로드 합니다","download-button-text": "다운로드","menu-button-tip": "파일 형식 고르기","group-options": "설정","group-high-definition": "고화질 (HD)","group-standard-definition": "일반화질 (SD)","group-mobile": "휴대기기용","group-unknown": "알 수 없는 파일 형식","group-update": "업데이트 가능","option-check": "업데이트 확인","option-webm": "WebM 사용하기","option-restrict": "선호하는 파일 형식만 사용","option-sizes": "파일크기 보기","option-format": "제목 형식 설정","button-options": "설정","button-options-close": "닫기","button-update": "YouTube Video Download를 업데이트 하려면 여기를 누르세요","error-no-downloads": "받을 수 있는 스트림이 없습니다"},
"pl": {"language": "Polish","credit0-name": "look997","credit0-url": "http://userscripts.org/users/123591","download-button-tip": "Zapisz film na twardym dysku","download-button-text": "Pobierz","menu-button-tip": "Wybierz format do pobrania","group-options": "Opcje","group-high-definition": "Wysoka rozdzielczość","group-standard-definition": "Standardowa rozdzielczość","group-mobile": "Mobile","group-unknown": "Nieznany format","group-update": "Nowa wersja YouTube Video Download jest dostępna","option-check": "Sprawdzaj aktualizacje","option-format": "Format tytułu","button-options": "opcje","button-options-close": "close","button-update": "Kliknij tutaj, aby zaktualizować","error-no-downloads": "Nie dostępne formaty"},
"pt-BR": {"language": "Portuguese (Brazil)","credit0-name": "Gandalf","credit0-url": "http://userscripts.org/users/73303","download-button-tip": "Salvar video","download-button-text": "Salvar","menu-button-tip": "Escolha outros formatos","group-options": "Opcoes","group-high-definition": "Definicao alta","group-standard-definition": "Definicao padrao","group-mobile": "Definicao celular","group-unknown": "Formatos desconhecidos","group-update": "Nova versao disponivel","option-check": "Procurar atualizacoes","option-webm": "WebM como formato padrao","option-restrict": "Mostrar apenas formato padrao","option-sizes": "Mostrar tamanho do arquivo","option-format": "Formato do Titulo","button-options": "opcoes","button-options-close": "fechar","button-update": "Clique aqui para atualizar YouTube Video Download","error-no-downloads": "Nenhum formato disponivel para salvar"},
"ru": {"language": "Russian","credit0-name": "lmiol","credit0-url": "http://userscripts.org/users/121962","credit1-name": "Ареопагит","credit1-url": "http://userscripts.org/users/155252","download-button-tip": "Скачать видео","download-button-text": "Скачать","menu-button-tip": "Выбрать формат для загрузки","format-tip": "Формат ","group-options": "Настройки","group-high-definition": "Высокое разрешение","group-standard-definition": "Стандартное разрешение","group-mobile": "Для телефона","group-unknown": "Неизвестный формат","group-update": "Доступна новая версия YouTube Video Download","option-check": "Проверять обновления","option-webm": "Предпочитать WebM","option-sizes": "Получать размер видеофайла","option-format": "Шаблон имени файла:","option-itags": "Любимые форматы:","button-options": "Настройки","button-options-close": "Скрыть","button-update": "Нажмите здесь для обновления","error-no-downloads": "Нет доступных форматов для загрузки"},
"sr": {"language": "Serbian","credit0-name": "titanicus","credit0-url": "http://userscripts.org/users/26334","download-button-tip": "Преузми овај видео","download-button-text": "Преузми","menu-button-tip": "Изаберите остале доступне формате","group-options": "Опције","group-high-definition": "Висока резолуција","group-standard-definition": "Стандардна резолуција","group-mobile": "Мобилни","group-unknown": "Непознати формати","group-update": "Надоградња је доступна","option-check": "Провери надоградње","option-webm": "Преферирај WebM","option-sizes": "Добави величину фајла","option-format": "Формат наслова","button-options": "опције","button-options-close": "затвори","button-update": "Кликните овде да ажурирате YouTube Видео Преузимач","error-no-downloads": "Нема доступних формата"},
"sv": {"language": "Swedish","credit0-name": "eson","credit0-url": "http://userscripts.org/users/367569","download-button-tip": "Ladda ner den har videon","download-button-text": "Ladda ner","menu-button-tip": "Valj mellan olika format","group-options": "Alternativ","group-high-definition": "Hogupplost","group-standard-definition": "Standardupplosning","group-mobile": "Mobilt","group-unknown": "Okanda format","group-update": "Det finns en uppdatering tillganglig","option-check": "Sok efter uppdateringar","option-webm": "Prioritera WebM","option-sizes": "Visa filstorlek","option-format": "Titelformat","option-itags": "Favoritformat","button-options": "Alternativ","button-options-close": "Stang","button-update": "Klicka har for att uppdatera YouTube Video Download","error-no-downloads": "Det gar inte att hitta nagra nedladdningsbara strommar"},
"tr": {"language": "Turkish","credit0-name": "Kenterte","credit0-url": "http://userscripts.org/users/Kenterte","download-button-tip": "Videoyu Farklı Kaydet","download-button-text": "İndir","menu-button-tip": "Bir İndirme Formatı Secin","group-options": "Ayarlar","group-high-definition": "Yuksek Cozunurluk","group-standard-definition": "Standart Cozunurluk","group-mobile": "Mobile","group-unknown": "Bilinmeyen Format","group-update": "YouTube Video Downloader'ın yeni bir versiyonu var","option-check": "Guncelleştirmeleri Kontrol Et","option-format": "Başlık Turu","button-options": "Ayarlar","button-options-close": "close","button-update": "Guncelleştirmek icin tıklayınız","error-no-downloads": "Format Bulunamadı"},
"zh-TW": {"language": "Chinese (Traditional)","credit0-name": "Wang Zheng","credit0-url": "http://userscripts.org/users/381783","download-button-tip": "儲存到硬碟","download-button-text": "下載","menu-button-tip": "選擇要下載的格式","group-options": "選項","group-high-definition": "HD","group-standard-definition": "標準畫質","group-mobile": "Mobile","group-unknown": "不明的格式","group-update": "有新的YouTube Video Download可供更新","option-check": "檢查更新","option-format": "標題格式","button-options": "選項","button-options-close": "close","button-update": "請點選此處更新","error-no-downloads": "沒有可用的下載格式"},
"zh": {"language": "Chinese (Simplified)","credit0-name": "Louiz","credit0-url": "http://userscripts.org/users/349372","download-button-tip": "保存到本地","download-button-text": "下载","menu-button-tip": "选择下载格式","group-options": "选项","group-high-definition": "标准分辨率","group-standard-definition": "较高分辨率","group-mobile": "Mobile","group-unknown": "未知格式","group-update": "已推出新版YouTube下载插件!","option-check": "检查更新","option-format": "标题格式","button-options": "选项","button-options-close": "close","button-update": "更新请点击这里","error-no-downloads": "下载格式不可用"},
};
function T(item) { return Languages.current[item] || Languages.en[item]; }
Languages.current = (yt && yt.config_ && yt.config_.HL_LOCALE && Languages[yt.config_.HL_LOCALE]) || Languages[document.documentElement.getAttribute("lang")] || Languages.en;
// StreamMap - Get and convert format maps
var StreamMap = (function() {
var self = {
getStreams: getStreams,
getURL: getURL,
sortFunc: sortFunc,
getExtension: getExtension,
};
// Just in case the auto format detection code breaks, fall back on these
// defaults for determining what is in the streams
var defaultStreams = [
{ itag: 5 , width: 320, height: 240, container: "FLV" , acodec:"MP3" , vcodec: "H.263" },
{ itag: 17 , width: 176, height: 144, container: "3GPP", acodec:"AAC" , vcodec: "MPEG-4", vprofile: "Simple" },
{ itag: 18 , width: 640, height: 360, container: "MP4" , acodec:"AAC" , vcodec: "H.264" , vprofile: "Baseline", level: 3.0 },
{ itag: 22 , width: 1280, height: 720, container: "MP4" , acodec:"AAC" , vcodec: "H.264" , vprofile: "High" , level: 3.1 },
{ itag: 34 , width: 640, height: 360, container: "FLV" , acodec:"AAC" , vcodec: "H.264" , vprofile: "Main" , level: 3.0 },
{ itag: 35 , width: 854, height: 480, container: "FLV" , acodec:"AAC" , vcodec: "H.264" , vprofile: "Main" , level: 3.0 },
{ itag: 36 , width: 320, height: 240, container: "3GPP", acodec:"AAC" , vcodec: "MPEG-4", vprofile: "Simple" },
{ itag: 37 , width: 1920, height: 1080, container: "MP4" , acodec:"AAC" , vcodec: "H.264" , vprofile: "High" , level: 3.1 },
{ itag: 38 , width: 2048, height: 1536, container: "MP4" , acodec:"AAC" , vcodec: "H.264" , vprofile: "High" , level: 3.1 },
{ itag: 43 , width: 640, height: 360, container: "WebM", acodec:"Vorbis", vcodec: "VP8" },
{ itag: 44 , width: 854, height: 480, container: "WebM", acodec:"Vorbis", vcodec: "VP8" },
{ itag: 45 , width: 1280, height: 720, container: "WebM", acodec:"Vorbis", vcodec: "VP8" },
{ itag: 46 , width: 1920, height: 1080, container: "WebM", acodec:"Vorbis", vcodec: "VP8" },
{ itag: 82 , width: 640, height: 360, container: "MP4" , acodec:"AAC" , vcodec: "H.264" , vprofile: "Baseline", level: 3.0, stereo3d: true },
{ itag: 83 , width: 854, height: 480, container: "MP4" , acodec:"AAC" , vcodec: "H.264" , vprofile: "Baseline", level: 3.1, stereo3d: true },
{ itag: 84 , width: 1280, height: 720, container: "MP4" , acodec:"AAC" , vcodec: "H.264" , vprofile: "High", level: 3.1, stereo3d: true },
{ itag: 85 , width: 1920, height: 1080, container: "MP4" , acodec:"AAC" , vcodec: "H.264" , vprofile: "High", level: 3.1, stereo3d: true },
{ itag: 100, width: 640, height: 360, container: "WebM", acodec:"Vorbis", vcodec: "VP8" , stereo3d: true },
{ itag: 101, width: 854, height: 480, container: "WebM", acodec:"Vorbis", vcodec: "VP8" , stereo3d: true },
{ itag: 102, width: 1280, height: 720, container: "WebM", acodec:"Vorbis", vcodec: "VP8" , stereo3d: true },
];
// Map containers to the order they sort in
function containerToNum(container)
{
if (String(localStorage["ytd-prefer-webm"]) == "true")
return {
"WebM": 1,
"MP4": 2,
"FLV": 3,
"3GPP": 4,
}[container] || 5;
else
return {
"MP4": 1,
"FLV": 2,
"WebM": 3,
"3GPP": 4,
}[container] || 5;
}
// sortFunc(a, b) - Sort streams from best to worst
function sortFunc(a, b)
{
if (a.height && b.height && a.height != b.height)
return b.height - a.height;
if (a.stereo3d && !b.stereo3d)
return 1;
else if (!a.stereo3d && b.stereo3d)
return -1;
if (a.container && b.container && a.container != b.container)
return containerToNum(a.container) - containerToNum(b.container);
return (Number(b.itag) - Number(a.itag)) || 0;
}
// decodeType(type) - Decode the mime type of the video
function decodeType(type)
{
var m = type.match(/^[^ ;]*/)[0],
ret = { container: "Unknown" };
if (m == "video/mp4")
{
ret.container = "MP4";
ret.vcodec = "H.264";
ret.acodec = "AAC";
var m = type.match(/avc1\.(....)(..)/)
if (m)
{
ret.level = parseInt(m[2], 16) / 10;
if (m[1] == "58A0")
ret.vprofile = "Extended";
else if (m[1] == "6400")
ret.vprofile = "High";
else if (m[1] == "4D40")
ret.vprofile = "Main";
else if (m[1] == "42E0")
ret.vprofile = "Baseline";
else if (m[1] == "4200")
ret.vprofile = "Baseline";
}
}
else if (m == "video/webm")
{
ret.container = "WebM";
ret.vcodec = "VP8";
ret.acodec = "Vorbis";
}
else if (m == "video/x-flv")
{
ret.container = "FLV";
}
else if (m == "video/3gpp")
{
ret.container = "3GPP";
ret.vcodec = "MPEG-4";
ret.acodec = "AAC";
}
return ret;
}
// processStream(stream) - Add some format information to the stream
function processStream(stream)
{
if (stream.type)
{
stream = merge(stream, decodeType(stream.type));
if (stream.container == "FLV")
if (stream.flashMajor == 7)
{
stream.vcodec = "H.263";
stream.acodec = "MP3";
}
else
{
stream.vcodec = "H.264";
stream.acodec = "AAC";
}
}
return stream;
}
// decodeFormat(format) - Decode an element of the fmt_list array
function decodeFormat(format)
{
format = format.split("/");
var size = format[1].split("x");
return {
itag: format[0],
width: Number(size[0]),
height: Number(size[1]),
flashMajor: Number(format[2]),
flashMinor: Number(format[3]),
flashPatch: Number(format[4]),
};
}
// getFlashArgs() - Get the flashvars from the page
function getFlashArgs()
{
return Try.all(
function() {
return yt.playerConfig.args;
},
function() {
return decodeQuery(document.getElementById("movie_player").getAttribute("flashvars"));
}
);
}
// getStreams() - Get the streams from the page
function getStreams()
{
try {
var flashArgs = getFlashArgs(),
streams = equi("itag", defaultStreams, flashArgs.url_encoded_fmt_stream_map.split(",").map(decodeQuery));
try {
streams = equi("itag", streams, flashArgs.fmt_list.split(",").map(decodeFormat));
}
catch (e) {}
} catch (e) {}
return streams.map(processStream);
}
// getURL(stream) - Get a URL from a stream
function getURL(stream, title)
{
if (stream.url)
{
var uri = new URI(stream.url);
if (!uri.query.signature && stream.sig)
uri.query.signature = stream.sig;
if (title)
uri.query.title = formatFileName(title);
return uri.toString();
}
}
// getExtension(stream) - Get the file extension associated with the
// container type of the specified stream
function getExtension(stream)
{
return {
"MP4": ".mp4",
"WebM": ".webm",
"3GPP": ".3gp",
"FLV": ".flv",
}[stream.container] || "";
}
return self;
})();
var Styles = (function() {
var self = {
injectStyle: injectStyle,
};
// injectStyle(text) - Add a stylesheet to the page's head
function injectStyle(text)
{
var style = document.createElement("style");
style.setAttribute("type", "text/css");
style.textContent = text;
document.head.appendChild(style);
}
return self;
})();
Styles["styles"] = "/* Download buttons */#watch7-sentiment-actions .yt-uix-button {margin-right: 2px;}#watch7-sentiment-actions > .yt-uix-button-group:last-child > button:last-child, #watch7-sentiment-actions > button:last-child {margin-right: 0px;}#watch7-secondary-actions .yt-uix-button {margin-left: 7px;}#watch7-secondary-actions > span:first-child > .yt-uix-button:first-child, #watch7-secondary-actions > .yt-uix-button:first-child {margin-left: 0px;}#watch7-sentiment-actions #ytd-dl-button {margin-right: -1px;border-top-right-radius: 0px;border-bottom-right-radius: 0px;}#watch7-sentiment-actions #ytd-menu-button {border-top-left-radius: 0px;border-bottom-left-radius: 0px;}#watch7-sentiment-actions #ytd-menu-button .yt-uix-button-arrow {margin: 0px;}/* Menu */#ytd-menu {font-size: 12px;box-shadow: 0 3px 3px rgba(0, 0, 0, 0.1);max-height: 100%;overflow-x: hidden;}#ytd-options-button {position: absolute;right: 8px;top: 8px;}.ytd-header {padding: 2px 13px;font-weight: bold;padding-top: 5px;border-bottom: 1px solid #999;}/* Menu items */#ytd-menu .ytd-item-group {position: relative;}#ytd-menu .ytd-item-group:hover {background-color: #777;}#ytd-menu .ytd-item-group:hover .yt-uix-button-menu-item {color: #fff;}#ytd-menu .ytd-item-group .ytd-item-size {position: absolute;left: 0px;top: 0px;width: 55px;padding: 8px 0px;text-align: right;color: inherit;}#ytd-menu .ytd-item-group .ytd-item-main {display: block;padding: 8px 0px 8px 55px;}#ytd-menu .ytd-item-group .ytd-item-sub {display: block;position: absolute;top: 0px;width: 53px;border-left: 1px solid #ddd;padding: 8px 5px;}#ytd-menu .ytd-item-group:hover .ytd-item-sub {border-left: 1px solid #666;}#ytd-menu .ytd-item-update {padding: 8px 20px;}/* uix checkboxes */.ytd-checkbox-container {margin: 6px 6px 6px 13px;}.ytd-checkbox-label {display: block;padding-right: 13px;}.ytd-textbox-container {margin: 6px 13px;}/* uix textboxes */.ytd-textbox-container .yt-uix-form-input-text {display: block;box-sizing: border-box;-moz-box-sizing: border-box;width: 100%;}.ytd-textbox-label {display: block;padding: 3px 6px;}";
Styles["styles-rtl"] = "/* Download buttons */#watch7-sentiment-actions .yt-uix-button {margin-left: 2px;}#watch7-sentiment-actions > .yt-uix-button-group:last-child > button:last-child, #watch7-sentiment-actions > button:last-child {margin-left: 0px;}#watch7-secondary-actions .yt-uix-button {margin-right: 7px;}#watch7-secondary-actions > span:first-child > .yt-uix-button:first-child, #watch7-secondary-actions > .yt-uix-button:first-child {margin-right: 0px;}#watch7-sentiment-actions #ytd-dl-button {margin-left: -1px;border-top-left-radius: 0px;border-bottom-left-radius: 0px;}#watch7-sentiment-actions #ytd-menu-button {border-top-right-radius: 0px;border-bottom-right-radius: 0px;}#watch7-sentiment-actions #ytd-menu-button .yt-uix-button-arrow {margin: 0px;}/* Menu */#ytd-menu {font-size: 12px;box-shadow: 0 3px 3px rgba(0, 0, 0, 0.1);max-height: 100%;overflow-x: hidden;}#ytd-options-button {position: absolute;left: 8px;top: 8px;}.ytd-header {padding: 2px 13px;font-weight: bold;padding-top: 5px;border-bottom: 1px solid #999;}/* Menu items */#ytd-menu .ytd-item-group {position: relative;}#ytd-menu .ytd-item-group:hover {background-color: #777;}#ytd-menu .ytd-item-group:hover .yt-uix-button-menu-item {color: #fff;}#ytd-menu .ytd-item-group .ytd-item-size {position: absolute;right: 0px;top: 0px;width: 55px;padding: 8px 0px;text-align: left;color: inherit;}#ytd-menu .ytd-item-group .ytd-item-main {display: block;padding: 8px 55px 8px 0px;}#ytd-menu .ytd-item-group .ytd-item-sub {display: block;position: absolute;top: 0px;width: 53px;border-right: 1px solid #ddd;padding: 8px 5px;}#ytd-menu .ytd-item-group:hover .ytd-item-sub {border-right: 1px solid #666;}#ytd-menu .ytd-item-update {padding: 8px 20px;}/* uix checkboxes */.ytd-checkbox-container {margin: 6px 6px 6px 13px;}.ytd-checkbox-label {display: block;padding-right: 13px;}.ytd-textbox-container {margin: 6px 13px;}/* uix textboxes */.ytd-textbox-container .yt-uix-form-input-text {display: block;box-sizing: border-box;-moz-box-sizing: border-box;width: 100%;}.ytd-textbox-label {display: block;padding: 3px 6px;}";
// Interface - Handles the user interface for the watch page
var Interface = (function() {
var self = {
init: init,
update: update,
notifyUpdate: notifyUpdate,
};
var rtl = document.body.getAttribute("dir") == "rtl",
groups,
lastStreams,
links = [],
nextId = 0;
// createOptionsButton() - Creates the button that opens the options menu
function createOptionsButton()
{
var elem = document.createElement("a"),
optionsOpen = false;
elem.setAttribute("id", "ytd-options-button");
elem.setAttribute("href", "xxjavascript:;");
elem.innerHTML = T("button-options");
elem.addEventListener("click", function() {
optionsOpen = !optionsOpen;
self.options.style.display = optionsOpen ? "" : "none";
elem.innerHTML = optionsOpen ? T("button-options-close") : T("button-options");
});
return elem
}
// createHeader(text) - Creates a menu section header
function createHeader(text)
{
var elem = document.createElement("div");
elem.className = "ytd-header";
elem.appendChild(document.createTextNode(text));
return elem;
}
// createCheckbox(text) - Creates a YouTube uix checkbox
function createCheckbox(labelText, checked, callback)
{
var label = document.createElement("label"),
span = document.createElement("span"),
checkbox = document.createElement("input"),
elem = document.createElement("span");
label.className = "ytd-checkbox-label";
span.className = "ytd-checkbox-container yt-uix-form-input-checkbox-container" + (checked ? " checked" : "");
checkbox.className = "yt-uix-form-input-checkbox";
checkbox.setAttribute("type", "checkbox");
checkbox.checked = !!checked;
checkbox.addEventListener("change", function() {
callback(checkbox.checked);
}, true);
elem.className = "yt-uix-form-input-checkbox-element";
span.appendChild(checkbox);
span.appendChild(elem);
label.appendChild(span);
label.appendChild(document.createTextNode(labelText));
return label;
}
// createTextbox(text) - Creates a YouTube uix textbox
function createTextbox(labelText, text, ltr, callback)
{
var label = document.createElement("label"),
container = document.createElement("div"),
box = document.createElement("input");
container.className = "ytd-textbox-container";
box.className = "yt-uix-form-input-text";
box.value = text;
if (rtl && ltr)
box.setAttribute("dir", "ltr");
box.addEventListener("input", function() {
callback(box.value);
});
label.className = "ytd-textbox-label";
label.appendChild(document.createTextNode(labelText));
label.appendChild(document.createElement("br"));
label.appendChild(container);
container.appendChild(box);
return label;
}
// createOptions() - Creates the options menu
function createOptions()
{
var elem = document.createElement("div");
elem.setAttribute("id", "ytd-options");
elem.appendChild(createHeader(T("group-options")));
// Determine whether to check GitHub for updates every two days
elem.appendChild(createCheckbox(T("option-check"), String(localStorage["ytd-check-updates"]) == "true", function (checked) {
localStorage["ytd-check-updates"] = checked;
}));
// Prefer WebM over MP4
elem.appendChild(createCheckbox(T("option-webm"), String(localStorage["ytd-prefer-webm"]) == "true", function (checked) {
localStorage["ytd-prefer-webm"] = checked;
update(lastStreams);
}));
// Determine whether to get video file sizes (Chrome only)
if (window.chrome)
elem.appendChild(createCheckbox(T("option-sizes"), String(localStorage["ytd-get-sizes"]) == "true", function (checked) {
localStorage["ytd-get-sizes"] = checked;
}));
// Title format
elem.appendChild(createTextbox(T("option-format"), localStorage["ytd-title-format"], true, function (text) {
localStorage["ytd-title-format"] = text;
updateLinks();
}));
// Favourite itags
elem.appendChild(createTextbox(T("option-itags"), localStorage["ytd-itags"], false, function (text) {
localStorage["ytd-itags"] = text.split(",").map(Number).filter(identity).map(Math.floor).join(", ");
update(lastStreams);
}));
elem.style.display = "none";
return elem;
}
// createDlButton() - Creates the instant download button
function createDlButton()
{
var link = document.createElement("a"),
elem = document.createElement("button");
link.setAttribute("href", "xxjavascript:;");
elem.className = "start yt-uix-button yt-uix-button-text yt-uix-tooltip";
elem.setAttribute("id", "ytd-dl-button");
elem.setAttribute("title", T("download-button-tip"));
elem.setAttribute("type", "button");
elem.setAttribute("role", "button");
elem.innerHTML = "" + T("download-button-text") + "";
link.appendChild(elem);
return link;
}
// createMenuButton() - Creates the download menu button
function createMenuButton()
{
var elem = document.createElement("button");
elem.className = "end yt-uix-button yt-uix-button-text yt-uix-button-empty yt-uix-tooltip";
elem.setAttribute("id", "ytd-menu-button");
elem.setAttribute("title", T("menu-button-tip"));
elem.setAttribute("type", "button");
elem.setAttribute("role", "button");
elem.setAttribute("[안내]태그제한으로등록되지않습니다-xxonclick", "; return false;");
elem.innerHTML = "
";
return elem;
}
// createMenu() - Creates the downloads menu
function createMenu()
{
var elem = document.createElement("div");
elem.className = "yt-uix-button-menu";
elem.setAttribute("id", "ytd-menu");
elem.style.display = "none";
return elem;
}
// formatTitle(stream) - Format stream information for the tooltips
function formatTitle(stream)
{
return T("format-tip") + stream.itag + ", " + (stream.vcodec ? stream.vcodec + "/" + stream.acodec : "") +
(stream.vprofile ? " (" + stream.vprofile + (stream.level ? "@L" + stream.level.toFixed(1) : "") + ")" : "");
}
// updateLink(href, target) - Informs the privileged extension code that a
// new link has been added
function updateLink(href, target)
{
if (!window.chrome || String(localStorage["ytd-get-sizes"]) != "true")
return;
var data = { "href": href, target: target };
var event = document.createEvent("MessageEvent");
event.initMessageEvent("ytd-update-link", true, true, JSON.stringify(data), document.location.origin, "", window);
document.dispatchEvent(event);
}
// createMenuItemGroup() - Creates a sub-group for a set of related streams
function createMenuItemGroup(streams)
{
// Create the button group and the size label ("360p", "480p", etc.)
var itemGroup = document.createElement("div"),
size = document.createElement("div"),
mainLink = document.createElement("a"),
mainId = nextId ++;
itemGroup.className = "ytd-item-group";
itemGroup.style.minWidth = streams.length * 64 + 48 + "px";
size.className = "ytd-item-size yt-uix-button-menu-item";
// Create the main video link
mainLink.className = "ytd-item ytd-item-main yt-uix-button-menu-item";
mainLink.setAttribute("id", "ytd-" + mainId);
mainLink.setAttribute("title", formatTitle(streams[0]));
links.push({ stream: streams[0], anchor: mainLink });
updateLink(StreamMap.getURL(streams[0]), "ytd-" + mainId);
if (rtl)
mainLink.style.marginLeft = (streams.length - 1) * 64 + "px";
else
mainLink.style.marginRight = (streams.length - 1) * 64 + "px";
mainLink.addEventListener("contextmenu", function(e) {
// Prevent right-click closing the menu in Chrome
e.stopPropagation();
}, false);
// Append the main link to the button group
size.appendChild(document.createTextNode(streams[0].height + "p\u00a0"));
mainLink.appendChild(size);
mainLink.appendChild(document.createTextNode((streams[0].stereo3d ? "3D " : "") + streams[0].container));
itemGroup.appendChild(mainLink);
// Create each sublink
for (var i = 1, max = streams.length; i < max; i ++)
{
var subLink = document.createElement("a"),
subId = nextId ++;
subLink.className = "ytd-item-sub yt-uix-button-menu-item";
subLink.setAttribute("id", "ytd-" + subId);
subLink.setAttribute("title", formatTitle(streams[i]));
if (streams[i].audio)
Audio.updateLink(streams[i], subLink);
else
{
links.push({ stream: streams[i], anchor: subLink });
updateLink(StreamMap.getURL(streams[i]), "ytd-" + subId);
}
if (rtl)
subLink.style.left = (streams.length - i - 1) * 64 + "px";
else
subLink.style.right = (streams.length - i - 1) * 64 + "px";
subLink.addEventListener("contextmenu", function(e) {
// Prevent right-click closing the menu in Chrome
e.stopPropagation();
}, false);
// Append the sublink to the button group
subLink.appendChild(document.createTextNode(
(streams[i].audio ? streams[i].acodec : (streams[i].stereo3d ? "3D " : "") + streams[i].container)
));
itemGroup.appendChild(subLink);
}
return itemGroup;
}
// createGroup(title, streams) - Creates a new menu group
function createGroup(title, flat, streams)
{
var elem = document.createElement("div");
elem.appendChild(createHeader(title));
if (flat)
for (var i = 0, max = streams.length; i < max; i ++)
elem.appendChild(createMenuItemGroup([streams[i]]));
else
{
var resolutions = [],
resGroups = {};
for (var i = 0, max = streams.length; i < max; i ++)
{
if (!resGroups[streams[i].height])
{
resolutions.push(streams[i].height);
resGroups[streams[i].height] = [];
}
resGroups[streams[i].height].push(streams[i]);
}
for (var i = 0, max = resolutions.length; i < max; i ++)
elem.appendChild(createMenuItemGroup(resGroups[resolutions[i]]));
}
return elem;
}
// createUpdate() - Creates the updates button
function createUpdate()
{
var elem = document.createElement("div");
elem.appendChild(createHeader(T("group-update")));
var a = document.createElement("a");
a.className = "ytd-item-update yt-uix-button-menu-item";
a.setAttribute("href", "https://github.com/rossy2401/youtube-video-download/raw/master/youtube-video-download.user.js");
a.appendChild(document.createTextNode(T("button-update")));
elem.appendChild(a);
return elem;
}
// setDlButton(stream) - Sets the default stream to download
function setDlButton(stream)
{
self.dlButton.getElementsByTagName("button")[0]
.setAttribute("title", T("download-button-tip") +
" (" + stream.height + "p " + stream.container + ")");
links.push({ stream: stream, anchor: self.dlButton });
}
// updateLinks() - Set the href and download attributes of all video
// download links
function updateLinks()
{
for (var i = 0, max = links.length; i < max; i ++)
{
var title = formatFileName(format(localStorage["ytd-title-format"], merge(links[i].stream, VideoInfo)));
links[i].anchor.setAttribute("download", title + StreamMap.getExtension(links[i].stream));
links[i].anchor.setAttribute("href", StreamMap.getURL(links[i].stream, title));
}
}
// update(streams) - Adds streams to the menu
function update(streams)
{
lastStreams = streams;
streams = streams
.filter(function(obj) { return obj.url; })
.sort(StreamMap.sortFunc);
links = [];
var favouriteItags = localStorage["ytd-itags"].split(",").map(Number);
var favouriteStreams =
streams
.filter(function(obj) {
return (obj.favouriteIndex = favouriteItags.indexOf(Number(obj.itag))) + 1;
})
.sort(function(a, b) { return a.favouriteIndex - b.favouriteIndex; });
if (favouriteStreams.length)
setDlButton(favouriteStreams[0]);
else if (streams.length)
setDlButton(streams[0]);
else
{
var button = self.dlButton.getElementsByTagName("button")[0];
self.menuButton.disabled = true;
self.menuButton.setAttribute("title", "");
button.setAttribute("title", T("error-no-downloads"));
}
self.downloads.innerHTML = "";
for (var i = 0, max = groups.length; i < max; i ++)
{
var groupStreams = streams.filter(groups[i].predicate);
if (groupStreams.length)
self.downloads.appendChild(createGroup(groups[i].title, groups[i].flat, groupStreams));
}
updateLinks();
}
// init() - Initalises the user interface
function init()
{
// Get the flag button from the actions menu
var buttonGroup = document.createElement("span"),
watchSentimentActions = document.getElementById("watch7-sentiment-actions"),
watchLike = document.getElementById("watch-like"),
watchDislike = document.getElementById("watch-dislike");
// Inject stylesheet(s)
if (rtl)
Styles.injectStyle(Styles["styles-rtl"]);
else
Styles.injectStyle(Styles["styles"]);
groups = [
{ title: T("group-high-definition"), predicate: function(stream) {
return stream.height && stream.container && stream.container != "3GPP" && stream.height > 576;
} },
{ title: T("group-standard-definition"), predicate: function(stream) {
return stream.height && stream.container && stream.container != "3GPP" && stream.height <= 576;
} },
{ title: T("group-mobile"), predicate: function(stream) {
return stream.height && stream.container && stream.container == "3GPP";
} },
{ title: T("group-unknown"), flat: true, predicate: function(stream) {
return !stream.height || !stream.container;
} },
];
buttonGroup.className = "yt-uix-button-group";
// Create the buttons
self.dlButton = createDlButton();
self.menuButton = createMenuButton();
// Create the dropdown menu
self.menu = createMenu();
self.menu.appendChild(createOptionsButton());
self.menu.appendChild(self.options = createOptions());
self.menu.appendChild(self.downloads = document.createElement("div"));
self.menuButton.appendChild(self.menu);
// Populate the button group
buttonGroup.appendChild(self.dlButton);
buttonGroup.appendChild(self.menuButton);
if (watchLike)
{
// If the like button is disabled, all the controls should be
// disabled
self.dlButton.disabled = self.menuButton.disabled = watchLike.disabled;
// Add a space between the Like and Dislike buttons to make them
// consistent with the download button in Chrome
watchDislike.parentNode.insertBefore(document.createTextNode(" "), watchDislike);
}
watchSentimentActions.appendChild(buttonGroup);
}
// notifyUpdate() - Notify the user of an available update
function notifyUpdate()
{
self.menu.appendChild(createUpdate());
}
return self;
})();
// Update - Check GitHub for updates
var Update = (function() {
var self = {
check: check,
};
// apiRequest(path, callback) - Perform a JSON API request for path,
// calling the callback(json, error) function on completion
function apiRequest(path, callback)
{
var xhr = new XMLHttpRequest();
xhr.open("GET", path);
xhr.[안내]태그제한으로등록되지않습니다-xxonload = function() {
var json;
try {
json = JSON.parse(xhr.responseText);
}
catch (e) {
callback(null, true);
}
if (json)
callback(json);
};
xhr.[안내]태그제한으로등록되지않습니다-xxonerror = function() {
callback(null, true);
};
xhr.send();
}
// check() - Query GitHub for changes to
// "youtube-video-download.user.js.sha1sum". If there is, inform the
// Interface module.
function check()
{
delete localStorage["ytd-update-sha1sum"];
delete localStorage["ytd-last-update"];
apiRequest("https://api.github.com/repos/rossy2401/youtube-video-download/git/refs/heads/master", function(json) {
if (!json)
return;
apiRequest(json.object.url, function (json) {
if (!json)
return;
apiRequest(json.tree.url, function (json) {
if (!json)
return;
apiRequest(json.tree.filter(function(a) { return a.path == "youtube-video-download.user.js.sha1sum"; })[0].url, function (json) {
if (!json)
return;
var sha1sum = atob(json.content.replace(/\n/g, ""));
localStorage["ytd-update-sha1sum"] = sha1sum;
localStorage["ytd-last-update"] = Date.now();
if (sha1sum.substr(0, 7) != hash)
Interface.notifyUpdate();
});
});
});
});
}
return self;
})();
function main()
{
if (localStorage.getItem("ytd-check-updates") === null)
localStorage["ytd-check-updates"] = true;
if (localStorage.getItem("ytd-prefer-webm") === null)
localStorage["ytd-prefer-webm"] = false;
if (localStorage.getItem("ytd-get-sizes") === null)
localStorage["ytd-get-sizes"] = false;
if (localStorage.getItem("ytd-title-format") === null)
localStorage["ytd-title-format"] = "${title}";
if (localStorage.getItem("ytd-itags") === null)
localStorage["ytd-itags"] = "37, 22, 18";
VideoInfo.init();
Interface.init();
Interface.update(StreamMap.getStreams());
if ((String(localStorage["ytd-check-updates"]) == "true"))
if (localStorage["ytd-current-sha1sum"] != hash ||
!localStorage["ytd-last-update"] ||
Number(localStorage["ytd-last-update"]) < Date.now() - 2 * 24 * 60 * 60 * 1000)
Update.check();
else if (localStorage["ytd-update-sha1sum"] && localStorage["ytd-update-sha1sum"].substr(0, 7) != hash)
Interface.notifyUpdate();
localStorage["ytd-current-sha1sum"] = hash;
}
main();
})();})();
카페 게시글
…… 프리토크
현아 ㄷㄷㄷㄷ
완얼
추천 0
조회 235
13.05.04 19:44
댓글 2
다음검색
첫댓글 머야 이글은
암호해석중