toggle-full-screen: use new helper function window_set_full_screen
[conkeror.git] / modules / page-modes / youtube.js
blobc52b7fc46bd59bf5cf18af847510a08a191ab7a2
1 /**
2  * (C) Copyright 2008 Jeremy Maitin-Shepard
3  * (C) Copyright 2009-2010 John J. Foerch
4  *
5  * Use, modification, and distribution are subject to the terms specified in the
6  * COPYING file.
7 **/
9 in_module(null);
11 require("content-buffer.js");
12 require("media.js");
14 let media_youtube_content_key_regexp = /&t=([^&]+)/;
15 let media_youtube_content_title_regexp = /<meta name="title" content="([^"]+)">/;
19  * Youtube Format Scrapers
20  *
21  *    Given a push function, the id of the video, the `t' key, and the
22  * text of the document (to scrape further data if necessary), the format
23  * scrapers' job is to call the push function with four args: url, file
24  * extension, mime type, and description.
25  *
26  *    Scrapers should return true on success and false on failure.
27  */
28 function youtube_scrape_standard_flv (push, id, t, text) {
29     push('http://youtube.com/get_video?video_id='+id+'&t='+t+'&asv=3',
30          'flv', 'video/x-flv', 'standard flv');
31     return true;
34 function youtube_scrape_hq_mp4 (push, id, t, text) {
35     if (/"fmt_map": ""/.test(text))
36         return false;
37     push('http://youtube.com/get_video?video_id='+id+'&t='+t+'&fmt=18'+'&asv=3',
38          'mp4', 'video/mp4', 'hq mp4');
39     return true;
42 function youtube_scrape_720p_mp4 (push, id, t, text) {
43     if (!(/'IS_HD_AVAILABLE': true/.test(text)))
44         return false;
45     push('http://youtube.com/get_video?video_id='+id+'&t='+t+'&fmt=22'+'&asv=3',
46          'mp4', 'video/mp4', '720p mp4');
47     return true;
50 function youtube_scrape_1080p_mp4 (push, id, t, text) {
51     if (!(/"fmt_map": "37/.test(text)))
52         return false;
53     push('http://youtube.com/get_video?video_id='+id+'&t='+t+'&fmt=37'+'&asv=3',
54          'mp4', 'video/mp4', '1080p mp4');
55     return true;
60  * Scraper Composition
61  */
63 /**
64  * make_call_each takes any number of functions as arguments and returns a
65  * closure which, when called, calls each of those functions in order, and
66  * finally returns true if any of the functions returned true.
67  */
68 function make_call_each () {
69     var fns = Array.prototype.slice.call(arguments, 0);
70     return function () {
71         var args = Array.prototype.slice.call(arguments, 0);
72         var found = false;
73         fns.map(function (fn) {
74             if (fn.apply(this, args))
75                 found = true;
76         });
77         return found;
78     };
81 /**
82  * make_call_each_until_success takes any number of functions as arguments
83  * and returns a closure which, when called, calls each of those functions
84  * in order until one returns true.
85  */
86 function make_call_each_until_success () {
87     var fns = Array.prototype.slice.call(arguments, 0);
88     return function () {
89         var args = Array.prototype.slice.call(arguments, 0);
90         for each (var fn in fns) {
91             if (fn.apply(this, args))
92                 return true;
93         }
94         return false;
95     };
98 define_variable('youtube_scrape_function',
99                 make_call_each(youtube_scrape_1080p_mp4,
100                                youtube_scrape_720p_mp4,
101                                youtube_scrape_hq_mp4,
102                                youtube_scrape_standard_flv),
103     "This function is called as the last step of scraping a youtube page, "+
104     "after the basic information needed to build the media url has been "+
105     "extracted. Youtube_scape_function is called with four arguments: a "+
106     "`push' function, the id, the t key, and the text of the page.  Its "+
107     "job is to call the push function for each media url desired with "+
108     "the arguments url, file extension, mime type, and description.  It "+
109     "should return true if it called the push function at least once, and "+
110     "otherwise false.");
113 function youtube_scrape_text (results, frame, id, text) {
114     var title_match = media_youtube_content_title_regexp.exec(text);
115     if (!title_match)
116         return null;
117     var title = decodeURIComponent(title_match[1]);
118     var res = media_youtube_content_key_regexp.exec(text);
119     if (!res)
120         return null;
121     function push (url, extension, mime_type, description) {
122         results.push(load_spec({
123             uri: url,
124             title: title,
125             filename_extension: extension,
126             source_frame: frame,
127             mime_type: mime_type,
128             description: description
129         }));
130     }
131     youtube_scrape_function(push, id, res[1], text);
134 function media_scrape_youtube (buffer, results) {
135     try {
136         var uri = buffer.current_uri.spec;
137         var result = media_youtube_uri_test_regexp.exec(uri);
138         if (!result)
139             return;
140         let text = buffer.document.documentElement.innerHTML;
141         let id = result[1];
142         youtube_scrape_text(results, buffer.top_frame, id, text);
143     } catch (e if !(e instanceof interactive_error)) {}
146 define_page_mode("youtube_mode",
147     $display_name = "YouTube",
148     $enable = function (buffer) {
149         media_setup_local_object_classes(buffer);
150     });
152 function media_scrape_embedded_youtube (buffer, results) {
153     const embedded_youtube_regexp = /^http:\/\/[a-zA-Z0-9\-.]+\.youtube\.com\/v\/(.*)$/;
154     for (let frame in frame_iterator(buffer.top_frame, buffer.focused_frame)) {
155         // Look for embedded YouTube videos
156         let obj_els = frame.document.getElementsByTagName("object");
157         for (let i = 0; i < obj_els.length; ++i) {
158             let obj_el = obj_els[i];
159             let param_els = obj_el.getElementsByTagName("param");
160             inner:
161             for (let j = 0; j < param_els.length; ++j) {
162                 let param_el = param_els[j];
163                 let match;
164                 if (param_el.getAttribute("name").toLowerCase() == "movie" &&
165                     (match = embedded_youtube_regexp.exec(param_el.getAttribute("value"))) != null) {
166                     try {
167                         let id = match[1];
168                         let lspec = load_spec({uri: "http://youtube.com/watch?v=" + id});
169                         var result =
170                             yield buffer.window.minibuffer.wait_for(
171                                 "Requesting " + lspec.uri + "...",
172                                 send_http_request(lspec));
173                         let text = result.responseText;
174                         if (text != null && text.length > 0)
175                             youtube_scrape_text(results, frame, id, text);
176                     } catch (e if (e instanceof abort)) {
177                         // Still allow other media scrapers to try even if the user aborted an http request,
178                         // but don't continue looking for embedded youtube videos.
179                         return;
180                     } catch (e) {
181                         // Some other error here means there was some problem with the request.
182                         // We'll just ignore it.
183                     }
184                     break inner;
185                 }
186             }
187         }
188     }
192 let media_youtube_uri_test_regexp = build_url_regex($domain = /(?:[a-z]+\.)?youtube/,
193                                                     $path = /watch\?v=([A-Za-z0-9\-_]+)/);
194 media_scrapers.unshift([/.*/, media_scrape_embedded_youtube]);
195 media_scrapers.unshift([media_youtube_uri_test_regexp, media_scrape_youtube]);
196 auto_mode_list.push([media_youtube_uri_test_regexp, youtube_mode]);
198 provide("youtube");