diff --git a/src/lib/formats.js b/src/lib/formats.js new file mode 100644 index 0000000..54ddd44 --- /dev/null +++ b/src/lib/formats.js @@ -0,0 +1,62 @@ +exports.formats = { + 5: ['flv', '240', 'mp3', 64, null], + 6: ['flv', '270', 'mp3', 64, null], + 13: ['3gp', 'N/A', 'aac', null, null], + 17: ['3gp', '144', 'aac', 24, null], + 18: ['mp4', '360', 'aac', 96, null], + 22: ['mp4', '720', 'aac', 192, null], + 34: ['flv', '360', 'aac', 128, null], + 35: ['flv', '280', 'aac', 128, null], + 36: ['3gp', '240', 'aac', 38, null], + 37: ['mp4', '1080', 'aac', 192, null], + 38: ['mp4', '3072', 'aac', 192, null], + 43: ['webm', '360', 'ogg', 128, null], + 44: ['webm', '480', 'ogg', 128, null], + 45: ['webm', '720', 'ogg', 192, null], + 46: ['webm', '1080', 'ogg', 192, null], + 83: ['mp4', '240', 'aac', 96, null], + 82: ['mp4', '360', 'aac', 96, null], + 59: ['mp4', '480', 'aac', 128, null], + 78: ['mp4', '480', 'aac', 128, null], + 85: ['mp4', '520', 'aac', 152, null], + 84: ['mp4', '720', 'aac', 192, null], + 100: ['webm', '360', 'ogg', 128, null], + 101: ['webm', '360', 'ogg', 192, null], + 102: ['webm', '720', 'ogg', 192, null], + 120: ['flv', '720', 'aac', 128, null], + 139: ['m4a', '48', 'aac', 38, 'a'], //Audio-only + 140: ['m4a', '128', 'aac', 128, 'a'], //Audio-only + 141: ['m4a', '256', 'aac', 256, 'a'], //Audio-only + 171: ['webm', '128', 'ogg', 128, 'a'], //Audio-only + 172: ['webm', '256', 'ogg', 192, 'a'], //Audio-only + 249: ['webm', "48", 'opus', 50, 'a'], + 250: ['webm', "48", 'opus', 70, 'a'], + 251: ['webm', "128", 'opus', 160, 'a'], + 160: ['mp4', '144', null, null, 'v'], //Video-only + 133: ['mp4', '240', null, null, 'v'], //Video-only + 134: ['mp4', '360', null, null, 'v'], //Video-only + 135: ['mp4', '480', null, null, 'v'], //Video-only + 298: ['mp4', '720', null, null, 'v'], //Video-only (60fps) + 136: ['mp4', '720', null, null, 'v'], //Video-only + 299: ['mp4', '1080', null, null, 'v'], //Video-only (60fps) + 137: ['mp4', '1080', null, null, 'v'], //Video-only + 138: ['mp4', '2160', null, null, 'v'], //Video-only + 266: ['mp4', '2160', null, null, 'v'], //Video-only + 264: ['mp4', '1440', null, null, 'v'], //Video-only + 278: ['webm', '144', null, null, 'v'], //Video-only + 242: ['webm', '240', null, null, 'v'], //Video-only + 243: ['webm', '360', null, null, 'v'], //Video-only + 244: ['webm', '480', null, null, 'v'], //Video-only + 245: ['webm', '480', null, null, 'v'], //Video-only + 246: ['webm', '480', null, null, 'v'], //Video-only + 302: ['webm', '720', null, null, 'v'], //Video-only (60fps) + 247: ['webm', '720', null, null, 'v'], //Video-only + 303: ['webm', '1080', null, null, 'v'], //Video-only (60fps) + 248: ['webm', '1080', null, null, 'v'], //Video-only + 313: ['webm', '2160', null, null, 'v'], //Video-only + 272: ['webm', '2160', null, null, 'v'], //Video-only + 271: ['webm', '1440', null, null, 'v'], //Video-only + 308: ['webm', '1440', null, null, 'v'], //Video-only (60fps) + 315: ['webm', '2160', null, null, 'v'], //Video-only (60fps) + }; + diff --git a/src/lib/sortformats.js b/src/lib/sortformats.js new file mode 100644 index 0000000..e60bfc4 --- /dev/null +++ b/src/lib/sortformats.js @@ -0,0 +1,148 @@ +var KNOWN = require('./formats').formats; + + +function to_object(previousValue,currentValue,currentIndex,array) { + previousValue[currentValue] = currentIndex; + return previousValue; +} + +function build_set(index) { + var members = {'null':true}; + for(var tag in KNOWN) { + members[KNOWN[tag][index]] = true; + } + + return members; +} + +function sanitize_order_strings(s,ss){ + var list = s.split(','); + list = list.map(function(a){ return a.replace(/\W/g,'')}).filter(function(i) { return ss[i] || (ss['acending'] && !isNaN(i));}); + return list.filter(function(item, pos) {item = (item === 'acending') ? 'decending' : item; return list.indexOf(item) == pos;}); +} + +const vaild_container = build_set(0); +const valid_video_resolution = {'acending':true, 'decending': true, 'null':true}; +const valid_audio_codec = build_set(2); +const valid_audio_bitrate = valid_video_resolution; +const valid_stream = build_set(4); +const valid_property = {'container':true, 'stream':true, 'video_resolution':true, 'audio_bitrate':true, 'audio_codec':true, 'null':true }; + +var user_container_order = 'mp4,webm,3gp,flv,m4a' + ',null'; +var user_stream_order = 'null,v,a' + ',null' ; +var user_audio_codec_order = 'aac,mp3,ogg,opus' + ',null'; +var user_video_resolution_order = 'decending' + ',null'; +var user_audio_bitrate_order = 'decending' + ',null'; +var user_property_order = 'stream, video_resolution, audio_bitrate'; + + +var audio_bitrate_order = sanitize_order_strings(user_audio_bitrate_order,valid_audio_bitrate).reduce(to_object,{'offset':3}); +var container_order = sanitize_order_strings(user_container_order,vaild_container).reduce(to_object,{'offset':0}); +var stream_order = sanitize_order_strings(user_stream_order,valid_stream).reduce(to_object,{'offset':4}); +var audio_codec_order = sanitize_order_strings(user_audio_codec_order,valid_audio_codec).reduce(to_object,{'offset':2}); +var video_resolution_order = sanitize_order_strings(user_video_resolution_order,valid_video_resolution).reduce(to_object,{'offset':1}); + + +const map_properties = {'container':container_order, 'stream':stream_order, 'video_resolution':video_resolution_order, 'audio_bitrate':audio_bitrate_order, 'audio_codec':audio_codec_order}; + +var property_order = sanitize_order_strings(user_property_order,valid_property).reverse().map(function(e){return map_properties[e];}); + +function cardinal_compare(s) { + return function(a,b) { + + var map_to_set = function(member) { + if (!(member in s)){ + if (typeof(member) === 'number') { + var as_string = JSON.stringify(member); + + if (!(as_string in s)) { + if ('acending' in s) { + return [member,s['acending']]; + }else if ('decending' in s) { + return [member,s['decending']]; + }else { + return ['null',0]; + } + + }else { + return [as_string,0]; + } + }else { + return ['null',0]; + } + }else { + return [member,0]; + } + }; + + + var t = map_to_set(a); + var na = t[0], a_in_range = t[1]; + t = map_to_set(b); + var nb = t[0], b_in_range = t[1]; + + + if(a_in_range && b_in_range) { + if('acending' in s) { + return na - nb; + }else { + return nb - na; + } + } + + if(a_in_range && !b_in_range) { + return a_in_range - s[nb]; + } + + if(!a_in_range && b_in_range) { + return s[na] - b_in_range; + } + + if(!a_in_range && !b_in_range) { + return s[na] - s[nb]; + } + + return 0; + + + } +} + +function stableSort(arr, cmpFunc) { + //wrap the arr elements in wrapper objects, so we can associate them with their origional starting index position + var arrOfWrapper = arr.map(function(elem, idx){ + return {elem: elem, idx: idx}; + }); + + //sort the wrappers, breaking sorting ties by using their elements orig index position + arrOfWrapper.sort(function(wrapperA, wrapperB){ + var cmpDiff = cmpFunc(wrapperA.elem, wrapperB.elem); + return cmpDiff === 0 + ? wrapperA.idx - wrapperB.idx + : cmpDiff; + }); + + //unwrap and return the elements + return arrOfWrapper.map(function(wrapper){ + return wrapper.elem; + }); +} + +function sort_formats(formats,order) { + + for(var i = 0; i < order.length; ++i) { + + var f = cardinal_compare(order[i]); + if(order[i] === video_resolution_order) { + formats = stableSort(formats,function(a,b) { a = KNOWN[a.itag][order[i].offset]; b = KNOWN[b.itag][order[i].offset]; a = parseInt(a); b = parseInt(b); return f(a,b);}); + }else{ + formats = stableSort(formats,function(a,b) {a = KNOWN[a.itag][order[i].offset]; b = KNOWN[b.itag][order[i].offset]; return f(a,b);}); + } + + } + + return formats; + +} + +exports.sortFormats = function(c) { return sort_formats(c,property_order);}; \ No newline at end of file diff --git a/src/lib/youtube.js b/src/lib/youtube.js index 1b2c6a0..ee3b279d 100644 --- a/src/lib/youtube.js +++ b/src/lib/youtube.js @@ -1,5 +1,7 @@ var {Cc, Ci, Cu} = require('chrome'), prefs = require('sdk/simple-prefs').prefs, + KNOWN = require('./formats').formats, + sortfunctions = require('./sortformats'), self = require('sdk/self'); Cu.import('resource://gre/modules/Promise.jsm'); @@ -97,68 +99,7 @@ var tagInfo = (function () { /* Converting video tag to video codec information */ var formatDictionary = (function () { - const KNOWN = { - 5: ['flv', '240', 'mp3', 64, null], - 6: ['flv', '270', 'mp3', 64, null], - 13: ['3gp', 'N/A', 'aac', null, null], - 17: ['3gp', '144', 'aac', 24, null], - 18: ['mp4', '360', 'aac', 96, null], - 22: ['mp4', '720', 'aac', 192, null], - 34: ['flv', '360', 'aac', 128, null], - 35: ['flv', '280', 'aac', 128, null], - 36: ['3gp', '240', 'aac', 38, null], - 37: ['mp4', '1080', 'aac', 192, null], - 38: ['mp4', '3072', 'aac', 192, null], - 43: ['webm', '360', 'ogg', 128, null], - 44: ['webm', '480', 'ogg', 128, null], - 45: ['webm', '720', 'ogg', 192, null], - 46: ['webm', '1080', 'ogg', 192, null], - 83: ['mp4', '240', 'aac', 96, null], - 82: ['mp4', '360', 'aac', 96, null], - 59: ['mp4', '480', 'aac', 128, null], - 78: ['mp4', '480', 'aac', 128, null], - 85: ['mp4', '520', 'aac', 152, null], - 84: ['mp4', '720', 'aac', 192, null], - 100: ['webm', '360', 'ogg', 128, null], - 101: ['webm', '360', 'ogg', 192, null], - 102: ['webm', '720', 'ogg', 192, null], - 120: ['flv', '720', 'aac', 128, null], - 139: ['m4a', '48', 'aac', 38, 'a'], //Audio-only - 140: ['m4a', '128', 'aac', 128, 'a'], //Audio-only - 141: ['m4a', '256', 'aac', 256, 'a'], //Audio-only - 171: ['webm', '128', 'ogg', 128, 'a'], //Audio-only - 172: ['webm', '256', 'ogg', 192, 'a'], //Audio-only - 249: ['webm', "48", 'opus', 50, 'a'], - 250: ['webm', "48", 'opus', 70, 'a'], - 251: ['webm', "128", 'opus', 160, 'a'], - 160: ['mp4', '144', null, null, 'v'], //Video-only - 133: ['mp4', '240', null, null, 'v'], //Video-only - 134: ['mp4', '360', null, null, 'v'], //Video-only - 135: ['mp4', '480', null, null, 'v'], //Video-only - 298: ['mp4', '720', null, null, 'v'], //Video-only (60fps) - 136: ['mp4', '720', null, null, 'v'], //Video-only - 299: ['mp4', '1080', null, null, 'v'], //Video-only (60fps) - 137: ['mp4', '1080', null, null, 'v'], //Video-only - 138: ['mp4', '2160', null, null, 'v'], //Video-only - 266: ['mp4', '2160', null, null, 'v'], //Video-only - 264: ['mp4', '1440', null, null, 'v'], //Video-only - 278: ['webm', '144', null, null, 'v'], //Video-only - 242: ['webm', '240', null, null, 'v'], //Video-only - 243: ['webm', '360', null, null, 'v'], //Video-only - 244: ['webm', '480', null, null, 'v'], //Video-only - 245: ['webm', '480', null, null, 'v'], //Video-only - 246: ['webm', '480', null, null, 'v'], //Video-only - 302: ['webm', '720', null, null, 'v'], //Video-only (60fps) - 247: ['webm', '720', null, null, 'v'], //Video-only - 303: ['webm', '1080', null, null, 'v'], //Video-only (60fps) - 248: ['webm', '1080', null, null, 'v'], //Video-only - 313: ['webm', '2160', null, null, 'v'], //Video-only - 272: ['webm', '2160', null, null, 'v'], //Video-only - 271: ['webm', '1440', null, null, 'v'], //Video-only - 308: ['webm', '1440', null, null, 'v'], //Video-only (60fps) - 315: ['webm', '2160', null, null, 'v'], //Video-only (60fps) - } - return function (obj) { + return function (obj) { var itag = obj.itag; if (!KNOWN[itag]) { return; @@ -670,35 +611,7 @@ function findOtherItags (info) { /* Sorting audio-only and video-only formats */ function sortFormats (info) { - info.formats = info.formats.sort(function (a, b) { - var aaIndex = a.dash == 'a', - baIndex = b.dash == 'a', - avIndex = a.dash == 'v', - bvIndex = b.dash == 'v'; - - if (aaIndex && baIndex) { - return b.audioBitrate - a.audioBitrate; - } - if (avIndex && bvIndex) { - var tmp = parseInt(b.resolution) - parseInt(a.resolution); - if (tmp === 0) { - tmp = parseInt(a.bitrate) - parseInt(b.bitrate); - } - return tmp; - } - if (aaIndex && bvIndex) { - return 1; - } - if (avIndex && baIndex) { - return -1; - } - if (!aaIndex && !avIndex && (baIndex || bvIndex)) { - return -1; - } - if (!baIndex && !bvIndex && (aaIndex || avIndex)) { - return 1; - } - }); + info.formats = sortfunctions.sortFormats(info.formats); return info; }