目的のファイルがあるかどうかを判別する方法

URLは確実に分かっているのですから、URLの存在確認さえできればOKです。つまり、HTTPアクセスしてResponseのステータスコードを見てやれば良いだけなのです。

http://creazy.net/2008/11/howto_find_youtube_hd_file_api_and_bookmarklet.html

これだけなら、XmlHttpRequestFirefoxGreasemonkeyでは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

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

http://s3.amazonaws.com/twitter_production/profile_images/51990802/IMAG8174_bigger.JPG

responseDetails is an object with five fields.

GM_xmlhttpRequest

onload 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
http://diveintogreasemonkey.org/api/gm_xmlhttprequest.html

そういえば http://www.onomatope.2-d.jp/program/firefox/tinyurltooltip.user.js を見たけど。
preview=1のCookieで302 RedirectしてresponseTextからa id="redirecturl"をmatchするよりも、preview=0で(ようするに通常どおりに)tinyurlGM_xmlhttpRequestして、responseDetails.responseHeadersのLocation:を切り取る方がわずかに早くて負荷が少ないかも。(でもpreview.phpから切り出した方が安全か)

http://otsune.nowa.jp/entry/978ce4d80e

responseDetails.responseHeadersのLocation:を切り取る

レスポンスヘッダに

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