目的のファイルがあるかどうかを判別する方法
URLは確実に分かっているのですから、URLの存在確認さえできればOKです。つまり、HTTPアクセスしてResponseのステータスコードを見てやれば良いだけなのです。
http://creazy.net/2008/11/howto_find_youtube_hd_file_api_and_bookmarklet.html
これだけなら、XmlHttpRequest(FirefoxのGreasemonkeyではGM_xmlhttpRequest関数)だけで取得可能に思えるのですが、ここで一つ落とし穴が。存在確認の対象ファイルが数MB〜数十MBもあるメディアファイルのため、ロードし終わるのに非常に時間がかかってしまいます。
http://creazy.net/2008/11/howto_find_youtube_hd_file_api_and_bookmarklet.html
GM_xmlhttpRequestでは GET | POST でのリクエストしかできないようで、onload時にしかステータスコードを取得できませんでした。だけど、この場合必要なのは HEAD メソッドなのです!
http://creazy.net/2008/11/howto_find_youtube_hd_file_api_and_bookmarklet.html
■
method
http://diveintogreasemonkey.org/api/gm_xmlhttprequest.html
a string, the HTTP method to use on this request. Required. Generally GET, but can be any HTTP verb, including POST, PUT, and DELETE.
■
GM_xmlhttpRequest って、 302 で飛ばされてもその先まで追っかけるらしいので、res.status == 200 ってやっとけばいいのね。(いや、ぜんぜんわかっとらんけど)
■
http://usericons.relucks.org/twitter/swdyh
302 Found
レスポンスヘッダー
Date: Sat, 07 Mar 2009 13:33:54 GMT Server: Apache/2.2.4 (Ubuntu) Phusion_Passenger/2.0.6 X-Powered-By: Phusion Passenger (mod_rails/mod_rack) 2.0.6 Cache-Control: private, max-age=10800 Location: http://s3.amazonaws.com/twitter_production/profile_images/51990802/IMAG8174_bigger.JPG Content-Length: 0 Keep-Alive: timeout=15, max=99 Connection: Keep-Alive Content-Type: text/html
responseDetails is an object with five fields.
http://diveintogreasemonkey.org/api/gm_xmlhttprequest.htmlonload callback
The callback function for onload takes a single parameter, responseDetails.
function onloadCallback(responseDetails);
responseDetails is an object with five fields.
- status
- an integer, the HTTP status code of the response. 200 means the request completed normally.
- statusText
- a string, the HTTP status text. Status text is server-dependent.
- responseHeaders
- a string, the HTTP headers included in the response.
- responseText
- a string, the body of the response.
- readyState
- unused
■
res は responseDetails だったんだ。
■
そういえば http://www.onomatope.2-d.jp/program/firefox/tinyurltooltip.user.js を見たけど。
http://otsune.nowa.jp/entry/978ce4d80e
preview=1のCookieで302 RedirectしてresponseTextからa id="redirecturl"をmatchするよりも、preview=0で(ようするに通常どおりに)tinyurlにGM_xmlhttpRequestして、responseDetails.responseHeadersのLocation:を切り取る方がわずかに早くて負荷が少ないかも。(でもpreview.phpから切り出した方が安全か)
responseDetails.responseHeadersのLocation:を切り取る
■
でも GM_xmlhttpRequest は301 Moved Permanently「だけ」でアクセス止めて処理って出来るのか?
http://otsune.nowa.jp/entry/978ce4d80e
だから遅いのかな。GM_xmlhttpRequest
■
レスポンスヘッダに
Location: http://b.hatena.ne.jp/atom/edit/XXXXという部分があって、 「XXXX」という部分にブックマークIDがあるので、これを正規表現でも使って抜き出します。
http://blog.37to.net/2007/05/livedoor_clipdelicio_1/
var eid = res.responseHeaders.match( /location:[\s]*.+\/edit\/([0-9]+)/i )[1];
parseXmlHttpResponse
http://d.hatena.ne.jp/hetappi/20080518/1211125734
function GMUtil() { return arguments.callee._self || (arguments.callee._self = { instanceof: function(obj, type) { // todo: return obj.wrappedJSObject instanceof type.wrappedJSObject; }, get: function(url, callback) { var self = this; var opts = { method: 'get', url: url, onerror: function(resp) { callback(null); }, onload: function(resp) { callback(self.parseXmlHttpResponse(resp)); } }; GM_xmlhttpRequest(opts); }, parseXmlHttpResponse: function(resp) { if (!resp) return null; var obj = { readyState: resp.readyState, status: resp.status, statusText: resp.statusText, response: { body: { text: resp.responseText } } }; obj.response.headers = (function(headers) { var obj = {}; headers.split('\n').forEach(function(header) { var pair = header.split(': '); if (pair.length != 2) return; console.log(pair[0]); console.log(pair[1]); obj[pair[0]] = pair[1]; }); return obj; })(resp.responseHeaders); if (!obj.response.headers['Content-Type']) return obj; var contentType = obj.response.headers['Content-Type'].split('; '); if (contentType[0] == 'application/json') { obj.response.body.json = eval(resp.responseText); obj.response.body.type = 'json'; } else if (contentType[0] == 'text/html') { // todo: var r = document.createRange(); r.selectNode(document.documentElement); var d = document.implementation.createDocument(null, 'html', null); d.documentElement.appendChild(r.createContextualFragment(resp.responseText)); obj.response.body.document = d; obj.response.body.type = 'html'; } else if (contentType[0] == 'text/xml' || contentType[0] == 'application/xml') { obj.response.body.document = new DOMParser().parseFromString(resp.responseText, contentType[0]); obj.response.body.type = 'xml'; } else { obj.response.body.type = 'unknown'; } return obj; } }); }
method: 'HEAD',
HEAD, GM_xmlhttpRequestでいけますよー. GM的にはdetail.methodをそのままopenしてるだけみたいですし.
GMの該当部分のソース.
http://greasemonkey.devjavu.com/browser/trunk/src/chrome/chromeFiles/content/xmlhttprequester.js
実際にHEADを使っているscript例
http://coderepos.org/share/browser/lang/javascript/userscripts/ldrprefav.user.js?使用例
// ==UserScript== // @name test head method // @namespace test for head method on GM_xmlhttpRequest // @include * // ==/UserScript== (function(){ var opt = { url: 'http://tinyurl.com/bah7d8', onload: function(res){ if(res.status == 200) alert(res.finalUrl); }, method: 'HEAD', }; setTimeout(GM_xmlhttpRequest, 0, opt); })();http://d.hatena.ne.jp/taizooo/20090308#c1236441932
HEAD? GET? (GM_xmlhttpRequest)
http://taizooo.tumblr.com/post/84498593/head-get-gm-xmlhttprequest
cxx | 普通にGET/POSTだけだと勘違いしてた http://tinyurl.com/dcvlky | permalink |
cxx | "POST" or "GET"て書いてたのこっちだ https://developer.mozilla.org/en/XMLHttpRequest | permalink |
Constellation | head method いけるけどなあ | permalink |
Constellation | あり? HEADしてもbody返ってるんじゃなかろうか. しかしHEADDDDとかするとserverからnot implementedってきてるので確かにHEADを送ってると思うんだけど. どうなんだろ. | permalink |
cxx | GM_xmlhttpRequest、onloadでstatus見たら404なことがあったけどそういうものなのかな… | permalink |
Constellation | やっぱ特になにもチェックなしにmethodにセットしてるっぽいんだけどなあ. HTTP1.1のtraceだけはじいてるのだけはわかったけど. | permalink |
Constellation | あとtrackと | permalink |
cxx | 別にHEAD使えるの疑ったわけじゃないですけど… | permalink |
Constellation | @cxx でもなんかresponseTextとか見ると中身はいってますね. GETとあまり変わらない挙動っぽいですー. うーん... | permalink |
cxx | @Constellation えええ、そうなんですか… | permalink |
Constellation | @cxx これとかやると, 実際に読み込みます. メモリ容量を食いつぶしまくるので実行しないほうがいい感じですが. http://gist.github.com/75395 | permalink |
Constellation | これはFxのもんだいじゃないなー. たぶんHTTP1.1じゃないからserverがbodyを返してきてる. | permalink |
Constellation | これで, GM_xmlhttpRequestのmethodがHEADのときにHEADで受け取ってるのを確認した. http://gist.github.com/75402 | permalink |
Constellation | おー. GETメソッドを一回もつかってなくてキャッシュがない状態だと, HEADメソッドいけるなあ. responseText空っぽ. | permalink |
Constellation | そうか, cacheがあるかないかか. 一回もGETメソッドを使ってなかったらHEADしてもresponseTextないけど, 利便性のためなのか, cacheがあるとresponseTextにcacheが文字列として展開されて入るのなー. | permalink |
Constellation | それで, 一回もvideoページ開いてなかったらyoutubeの動画にむかってheadしても存在確認だけだけど, cacheがあるとそれを文字列に展開してメモリを逼迫するのか. cache利用だってわかっただけでも, 存在確認にはHEADを使ったほうがいいな. | permalink |
Constellation | でも文字列に展開されると怖いから, やっぱHEAD先は動画みたいなのにはしないほうがいいなあ. | permalink |
Constellation | あと, server側がHTTP1.1に対応してるかどうかかー. HEADなのにbody返してくるのもあるし. | permalink |
Constellation | 地震かー. | permalink |
Constellation | やばっ. 机に突っ伏したまま寝てた. | permalink |
■
http://coderepos.org/share/browser/lang/javascript/userscripts/reblogcommand.user.js?rev=28769#L107
108 var id = getIDByPermalink(aURL); 109 var d; 110 with (D()) { 111 d = Deferred(); 112 if (!id) { 113 wait(0).next(function() { d.call(); }); 114 return d; 115 } 116 }
このへん。Deffered ってなんぞや?