日記はScrapboxに移動しました。

  • Googleで℃-uteを正しく検索する

    昨年はレコード大賞最優秀新人賞を獲得し、また、紅白歌合戦への出場も果たしたことにより、℃-uteは既に国民的な存在になったといえますが、まだそれが何なのか良く知らない方も、もしかしたらいらっしゃるかもしれません。そのような方が、たとえばGoogleで「℃-ute」を検索してみた場合、おそらくは、下記のような残念な表示を見ることになると思います。

    ℃-ute に一致するページは見つかりませんでした。

    かといって、「C-ute」で検索したところで、まともな検索結果は期待できません。SEO的に最悪なことになってしまっています。

    界隈では随分前からFAQなのですが、正しくは「”℃-ute“」といった具合に、二重引用符で囲んだ状態で検索する必要があります。面倒ですよね。そこで、以下の通り、「℃-ute」と検索キーワードを入力した際に、自動的に二重引用符で囲むGreasemonkeyスクリプトを作成しました。

    これで℃-uteを検索しまくりですね。どうぞご利用ください。

  • Rimoの人気/新着ページに、RimoChにチャンネルを追加するリンクを表示するGreasemonkeyスクリプト

    ,

    RimoChへの、機能追加のお知らせです。
    Rimo人気/新着チャンネルページに、それらのチャンネルをRimoChに追加するリンクを表示するGreasemonkeyスクリプトを作りました。下記リンクにより、スクリプトをインストールできます。インストール後には、Rimoの人気/新着ページに「RimoChに追加する」というリンクが、各チャンネル名の下部に表示されるようになります。

    スクリプトのインストール後、初めてRimoの人気/新着ページに訪れた際にユーザIDの入力を求められますので、RimoChで利用しているはてなIDを、正しく入力してください。間違えて登録してしまうと、正しいリンクが表示されなくなりますので、その場合は、以下の画像の要領で、正しいユーザIDに修正してください。

  • 「Exif JSONサービス」から Exif 情報を引っ張ってきて表示したい

    , ,

    Exif JSONサービス – Ogawa::Memoranda」なんつって、画像の URL を指定すると、その画像の Exif 情報を返すなんてなステキ API が公開されたので、ぐりもんでいじってみたりしました。……が、以下のような感じで作ってみたのだけど、なんかマウスをガシガシ動かしまくって、Exif情報を出したり消したりしまくってると、Firefoxが固まる……。なんでー。
    まぁ以下、メモ掲載。

    // ==UserScript==
    // @name          Show Exif
    // @namespace     http://kentarok.org/
    // @include       *
    // ==/UserScript==
    //
    // ==Acknowledgments==
    //
    // This script appears thanks to:
    //
    // Exif JSON API - Ogawa::Memoranda
    // http://as-is.net/blog/archives/001206.html
    //
    // ==Author & Copyright==
    //
    // Copyright (C) 2007 by Kentaro Kuribayashi
    // This script is distributed under the MIT License
    //
    (function(){
    var popupId  = 'GM-Show-Exif';
    var callback = 'GM_showExif';
    var jsonp    = new JSONP;
    unsafeWindow.GM_showExif = showExif;
    document.body.appendChild($N('div', {
    id   : popupId,
    style: {
    color          : '#333333',
    border         : 'solid 1px #333333',
    backgroundColor: '#ffffb2',
    padding        : '1em',
    display        : 'none',
    position       : 'absolute',
    },
    }));
    $A(document.images).forEach(function (image) {
    var jsonpURI = ['http://as-is.net/exif/json?url=', image.src, '&callback=', callback].join('');
    image.addEventListener('mouseover', function (event) {
    jsonp.executeRequest(jsonpURI);
    $(popupId).style.display = 'block';
    $(popupId).style.top     = (event.pageY + 10) + 'px';
    $(popupId).style.left    = (event.pageX + 10) + 'px';
    }, false);
    image.addEventListener('mouseout', function () {
    jsonp.removeRequest(jsonpURI);
    $(popupId).style.display = 'none';
    }, false);
    });
    function showExif (exif) {
    var lines = [];
    for (var p in exif) {
    lines.push(['<strong>', p, ':</strong> ', exif[p], '<br>'].join(''));
    }
    $(popupId).innerHTML = lines.join("\n");
    };
    function JSONP () {
    this.requests = {};
    this.head = document.getElementsByTagName('head')[0];
    this.executeRequest = function (jsonpURI) {
    var script         = document.createElement('script');
    script.type    = 'text/javascript';
    script.charset = 'utf-8';
    script.src     = jsonpURI;
    this.head.appendChild(script);
    this.requests[jsonpURI] = script;
    };
    this.removeRequest = function (jsonpURI) {
    this.head.removeChild(this.requests[jsonpURI]);
    delete this.requests[jsonpURI];
    };
    }
    function $ (id) {
    return document.getElementById(id);
    }
    function $A (arg) {
    var array = [];
    for (var i = 0, l = arg.length; i < l; i++) {
    array.push(arg[i]);
    }
    return array;
    }
    function $N (tag, props, children) {
    var element = document.createElement(tag);
    for (var k in props) {
    var v = props[k];
    if (k == 'style') {
    for (var p in v) {
    element.style[p] = v[p];
    }
    }
    else if (k == 'event') {
    v.forEach(function (e) {
    element.addEventListener(e.name, e.callback, e.flag);
    });
    }
    else if (k == 'class') {
    element.className = v;
    }
    else {
    element.setAttribute(k, v);
    }
    }
    if (children instanceof Array) {
    children.forEach(function (child) {
    if ((typeof(child) == 'string' || child instanceof String)) {
    element.appendChild(document.createTextNode(child));
    }
    else {
    element.appendChild(child);
    }
    });
    }
    return element;
    }
    })();
    
  • livedoor Reader に ping を送信する Greasemonkey スクリプト

    livedoor Reader が ping の受け付けを開始したとのことだけど、「はてなダイアリーのヘルプ – リンク元・Ping送信設定を変える」に見られる通り、はてなの ping 送信先は決め打ちで、かつ、LDR に対応してないので、それ Gre(ry
    つかこれテキトー過ぎ。まぁ、テキトーじゃなかったことなど、これまで一度もなかったのだが!つか、これでうまくいってるのかどうか、よくわかりません><

    UPDATE: なんかうまくいってない気がする。まぁどうでもいいけど。

    MORE UPDATE: 編集画面の submit と同時に ping を送っちゃうと、エントリが保存されていないため、更新前のフィードが取得されちゃうようなので、画面遷移後に ping を送信するようにした。

    // ==UserScript==
    // @name          LDR UpdatePing
    // @namespace     http://antipop.gs/ns/greasemonkey/ldr_updateping
    // @include       http://d.hatena.ne.jp/*
    // ==/UserScript==
    //
    // This script was written by Kentaro Kuribayashi
    //
    // Homepage: http://antipop.gs/
    // Weblogs : http://d.hatena.ne.jp/antipop/ (in Japanese)
    //           http://antipop.typepad.com/    (in English )
    //
    // Copyright (C) 2006 by Kentaro Kuribayashi
    //
    // This script is distributed under the CCPL by-sa
    //  - See: http://creativecommons.org/licenses/by-sa/2.5/
    //
    (function(){
    var SCRIPT_NAME = 'LDR UpdatePing';
    var VERSION     = '0.02';
    var isAfterEdit = GM_getValue('isAfterEdit');
    var pingUri     = 'http://rpc.reader.livedoor.com/ping';
    if (!isAfterEdit && location.href.match(/http:\/\/d\.hatena\.ne\.jp\/[^\/]+\/edit.*/)) {
    $X('//input[@name = "edit"]')[0].addEventListener('click', function () {
    GM_setValue('isAfterEdit', true);
    }, false);
    }
    else if (isAfterEdit) {
    var title = document.title.split(' - ')[0];
    var link  = location.href.match(/(http:\/\/d\.hatena\.ne\.jp\/[^\/]+\/).*/)[1];
    var call  = <methodCall>
    <methodName>weblogUpdates.ping</methodName>
    <params>
    <param><value><string>{title}</string></value></param>
    <param><value><string>{link}</string></value></param>
    </params>
    </methodCall>
    ;
    GM_xmlhttpRequest({
    method  : 'POST',
    url     : pingUri,
    headers : {
    'User-Agent'   : [SCRIPT_NAME, '/', VERSION].join(''),
    'Content-Type' : 'text/xml',
    },
    data    : call.toString(),
    onload  : function (res) {
    alert(new XML(res.responseText.replace(/^<\?xml.+?\?>/, ''))..string[0]);
    GM_setValue('isAfterEdit', false);
    },
    onerror : function (res) {
    alert(['Error', res.statusText].join(' - '));
    },
    });
    }
    // $X was originally written by cho45 (http://lowreal.net/logs/2006/03/16/1), and
    // it is distributed under the CCPL by-sa same as this script
    function $X (exp, context) {
    if (!context) context = document;
    var resolver = function (prefix) {
    var o = document.createNSResolver(context)(prefix);
    return o ? o : (document.contentType == 'text/html') ? '' : 'http://www.w3.org/1999/xhtml';
    }
    var exp = document.createExpression(exp, resolver);
    var result = exp.evaluate(context, XPathResult.ANY_TYPE, null);
    switch (result.resultType) {
    case XPathResult.STRING_TYPE : return result.stringValue;
    case XPathResult.NUMBER_TYPE : return result.numberValue;
    case XPathResult.BOOLEAN_TYPE: return result.booleanValue;
    case XPathResult.UNORDERED_NODE_ITERATOR_TYPE: {
    result = exp.evaluate(context, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
    var ret = [];
    for (var i = 0, len = result.snapshotLength; i < len ; i++) {
    ret.push(result.snapshotItem(i));
    }
    return ret;
    }
    }
    return null;
    }
    })();
  • 未読フィード恐怖症について、あるいは、それ Greasemonkey でよくね?

    , ,

    ただ、RSSリーダーに慣れてくると、陥りがちな病気があるんです。
    その名も「未読RSS恐怖症」です。RSSリーダーに調子に乗って大量のブログやニュースサイトを登録したことで、毎日更新通知される大量の記事に処理が追いつかなくなり、徐々にRSSリーダーの未読記事が膨れ上がり、だんだんRSSリーダーを開くのが嫌になってくるという恐ろしい(?)病気です。

    それGre(ry)
    詳細については、livedoor Readerとは – はてな に掲載されている vi モードのあたりを。
    とはいえ、まぁ、LDR であれば「最速インターフェース研究会 :: how many feeds do you subscribe to?」を参考にして、レートを駆使すれば、かなりの程度までどうにかなるのではないでしょうか。
    僕の LDR は、毎日だいたい 1,500 から 2,000 くらい新着エントリがあって、フィードにレートをつけるのがめんどうくさくて放置してあるので、フラット表示にして、端から端まで全部、少なくともタイトルだけは眺めています。まぁ、レート管理しないでも済む程度の情報にしか触れていないってことですが。
    今後はぼちぼちレートつけるようにしていきたいなぁ。

    ストレスフリーの仕事術―仕事と人生をコントロールする52の法則
    デビッド アレン David Allen 田口 元
    二見書房 (2006/05)
  • Asamasi ID Filter その後

    ,

    以前晒した “Asamasi ID Filter” について、amazon の商品ページの URL がなんか変更されてるなーというのは知ってたけど、このスクリプトを自分で使ってる分には困る場面があんまりなかったので放置してたのですが、どうにかしろよ!といわれたので、激しくいまさらですが対応しました。

    // ==UserScript==
    // @name          Asamasi ID Filter
    // @namespace     http://antipop.gs/ns/greasemonkey/asamasiidfilter
    // @description   convert malicious things to something good
    // @include       *
    // @exclude       http://www.amazon.co.jp/exec/obidos/tg/browse/-/896244/*
    // @exclude       http://www.amazon.co.jp/exec/obidos/tg/browse/-/10667101/*
    // @exclude       http://www.amazon.co.jp/exec/obidos/tg/browse/-/927712/*
    // ==/UserScript==
    (function() {
    var ids = new Array(
    'foo-22',
    'bar-22',
    'baz-22',
    //      'antipop-22',
    );
    var links   = document.links;
    var pattern = '^http://(?:www.amazon.co.jp/(?:exec/obidos/ASIN/|o/ASIN/|gp/product/)|d.hatena.ne.jp/asin/)([^/]+)/?.*';
    var regexp  = new RegExp(pattern, 'i');
    for (var i = 0, length = links.length; i < length; i++) {
    if (links[i].href.match(regexp)) {
    links[i].href = new Array(
    'http://www.amazon.co.jp/exec/obidos/ASIN/',
    RegExp.$1,
    '/ref=nosim/',
    ids[Math.floor(Math.random() * ids.length)]).join('');
    }
    }
    })();
    
    JavaScriptビジュアル・リファレンス
    シーズ
    エムディエヌコーポレーション (2004/11)
    売り上げランキング: 29,745
  • livedoor Reader で読んだフィードの履歴を遡れるようにする greasemonkey スクリプト

    ,

    LDR でさくさくフィードを読み進めているときに、さっき読んでたフィードに戻りたいと思いきや、すでにマイフィードのリストは更新されており、そのフィードは消えていて簡単には戻れずに、残念な思いをすることがあります。
    そこで、以下の通りのスクリプトを書いて、読んだフィードの履歴を遡れるようにしてみました。Shift + p / Shift + n で履歴をいったりきたりできます。
    LDR のキャッシュの仕組みがよくわからないので、フィードを丸ごと配列に保存しておくという強引なやりくちを用いています……。
    インストール: ldr_feed_history.user.js

    // ==UserScript==
    // @name          LDR Feed History
    // @namespace     http://antipop.gs/ns/greasemonkey/ldr_feed_history
    // @include       http://reader.livedoor.com/reader/*
    // ==/UserScript==
    (function(){
    var historyMaxLength = 5;
    var w = unsafeWindow;
    var _onload = w.onload;
    var onload  = function(){with (w) {
    State.GM_now_reading  = null;
    State.GM_feed_index   = 0;
    State.GM_feed_history = [];
    register_hook('after_printfeed', function (feed) {
    var filtered = State.GM_feed_history.filter(function (iter) {
    return (iter.subscribe_id == feed.subscribe_id);
    });
    if (!filtered.length) {
    State.GM_feed_history.unshift(feed);
    while (State.GM_feed_history.length > (historyMaxLength + 1)) {
    State.GM_feed_history.pop();
    }
    State.GM_now_reading = State.now_reading;
    State.GM_feed_index  = 0;
    }
    });
    Keybind.add('P', function () {
    if (State.GM_feed_index < (State.GM_feed_history.length - 1)) {
    print_feed(State.GM_feed_history[++State.GM_feed_index]);
    State.now_reading = State.GM_now_reading;
    }
    });
    KeyConfig.history_up = 'P';
    KeyHelp.history_up   = '\u5C65\u6B74\u3092\u904E\u53BB\u306B\u5411\u304B\u3063\u3066\u79FB\u52D5\u3059\u308B';
    Keybind.add('N', function () {
    if (State.GM_feed_index > 0) {
    print_feed(State.GM_feed_history[--State.GM_feed_index]);
    State.now_reading = State.GM_now_reading;
    }
    });
    KeyConfig.history_down = 'N';
    KeyHelp.history_down   = '\u5C65\u6B74\u3092\u73FE\u5728\u306B\u5411\u304B\u3063\u3066\u79FB\u52D5\u3059\u308B';
    }};
    w.onload = function(){
    _onload();
    onload();
    };
    })();

    ライブドアの技術面に関する特集が掲載されています。

    JavaScriptビジュアル・リファレンス
    シーズ
    エムディエヌコーポレーション (2004/11)
    売り上げランキング: 29,745

    非常に評判の高い JavaScript リファレンス。

  • Update: LDR で最速動画ウォッチング + YouTube Anywhere

    , ,

    “どこでも YouTube” を実現する Greasemonkey スクリプト YouTube Anywhere」で公開したスクリプトを、せっかくなので、LDR にも対応してみました。「livedoor Reader + YouTube + はてなブックマークで最速動画ウォッチング」と「“どこでも YouTube” を実現する Greasemonkey スクリプト YouTube Anywhere」を足して 2 で割らない感じ。つってもまぁ、最低限 LDR 上で動くようにしただけなのがアレ。それなりに動くので、まぁいいや。あと、ついでにnirvashの日記に触発されて、Qooqle Video Clippers!HATENA-TUBE のフィードにも対応。
    使いかたを再度説明。
    LDR 上では、YouTube, Qooqle, HATENA-TUBE の動画エントリを見てる時に、ショートカットキー “y” を押すとプレイヤ表示、さらに “y” を押すとプレイヤを隠します。その他のサイト(デフォルトでは del.icio.usはてなブックマーク)では、YouTube の動画ページのリンクの脇にアイコンが表示されるので、クリックするとプレイヤが表示されます。
    プレイヤに表示されているアイコンをあれこれ押してみればわかりますが、動画ダウンロードや、各種クリッピングサービスへのリンク等が配置されています。
    インストールは以下に示すリンクから。このスクリプトをインストールしたら、以前「livedoor Reader + YouTube + はてなブックマークで最速動画ウォッチング」で公開した YouTube Player on LDR は必要ありませんので、そっはアンインストールしちゃってください。
    インストール: youtube_anywhere.user.js

    // ==UserScript==
    //
    // @name          YouTube Anywhere
    // @namespace     http://antipop.gs/ns/greasemonkey/youtube_anywhere
    // @include       http://reader.livedoor.com/reader*
    // @include       http://b.hatena.ne.jp/*
    // @include       http://del.icio.us/*
    //
    // ==/UserScript==
    //
    // ==Configuration==
    //
    // add other domains to @include for more use
    //
    // - include YouTube.
    // @include       *youtube.com/*
    //
    // - include whole the web
    // @include       *
    //
    //==/Configuration==
    //
    //==Acknowledgments==
    //
    // Bitcons >> SOME RANDOM DUDE
    //  - http://somerandomdude.net/srd-projects/bitcons/
    //
    //==/Acknowledgments==
    //
    // ==Copyright==
    //
    // Copyright (C) 2006 by Kentaro Kuribayashi
    //
    // This script is distributed under the CCPL by-sa
    //  - See: http://creativecommons.org/licenses/by-sa/2.5/
    //
    (function(){
    var playerContainerId = 'GM_youtube_player';
    var playerWidth  = 425;
    var playerHeight = 350;
    var overlayId    = 'GM_overlay';
    var regexp = [
    new RegExp('^http://(?:www\\.)?youtube\\.com/(?:watch)?\\?.*v=([^&]+).*$', 'i'),
    new RegExp('^http://clippers\\.qooqle\\.jp/video/([^/]+).*$', 'i'),
    new RegExp('^http://www\\.fladdict\\.net/app/hatenatube/#v=([^&]+).*$', 'i'),
    ];
    if (location.href.match('^http://reader\\.livedoor\\.com/reader.*$', 'i')) {
    var w = unsafeWindow;
    var _onload = w.onload;
    var onload  = function(){with (w) {
    Keybind.add('y', function () {
    if ($(playerContainerId)) {
    hidePlayer();
    }
    else {
    var item = get_active_item(true);
    if (item) {
    regexp.forEach(function (r) {
    if (item.link.match(r)) {
    showPlayer(RegExp.$1, item.title);
    }
    });
    }
    }
    });
    }};
    w.onload = function(){
    _onload();
    onload();
    };
    }
    else {
    $A(document.links).forEach(function (link) {
    if (link.href.match(regexp[0])) {
    var videoId    = RegExp.$1;
    var videoTitle = link.innerHTML.replace(/<.+?>/g, '');
    var icon = $C('img', {
    'src'   : 'http://www.youtube.com/favicon.ico',
    'alt'   : 'YouTube',
    'title' : 'watch this movie',
    'style' : {
    'marginRight' : '0.5em',
    'cursor'      : 'pointer',
    },
    'event' : [{
    'name'     : 'click',
    'callback' : function () { showPlayer(videoId, videoTitle); },
    'flag'     : false,
    }],
    });
    link.parentNode.insertBefore(icon, link);
    }
    });
    }
    function $ (id) { return document.getElementById(id); };
    function $A (arg) {
    var array = [];
    for (var i = 0; i < arg.length; i++) {
    array.push(arg[i]);
    }
    return array;
    }
    function $C (tag, prop) {
    var element = document.createElement(tag);
    for (var i in prop) {
    if (i == 'style') {
    for (var p in prop[i]) {
    element.style[p] = prop[i][p];
    }
    }
    else if (i == 'event') {
    prop[i].forEach(function (e) {
    element.addEventListener(e.name, e.callback, e.flag);
    });
    }
    else {
    element[i] = prop[i];
    }
    }
    return element;
    };
    function createPlayerContainer (videoId, videoTitle) {
    var playerContainer = $C('div', {
    'id'     : playerContainerId,
    'style'  : {
    'position'        : 'fixed',
    'zIndex'          : '100',
    'backgroundColor' : '#ffffff',
    'padding'         : '0.5em 1.5em',
    },
    });
    var playerHeader = createPlayerHeader(videoTitle);
    var player       = createPlayer(videoId);
    var playerFooter = createPlayerFooter(videoId, videoTitle, player);
    [playerHeader, player, playerFooter].forEach(function (e) {
    playerContainer.appendChild(e);
    });
    return playerContainer;
    }
    function createPlayerHeader (videoTitle) {
    var playerHeader = $C('p', {
    'style' : {
    'fontWeight' : 'bold',
    'margin'     : '0.5em 2em 0.5em 0',
    }
    });
    var closeButton = $C('img', {
    'id'    : 'GM_close_utton',
    'src'   : getImageUrl('close'),
    'title' : 'Hide this player',
    'style' : {
    'display'         : 'block',
    'position'        : 'absolute',
    'top'             : '5px',
    'right'           : '5px',
    'cursor'          : 'pointer',
    'backgroundColor' : '#00cc00',
    },
    'event' : [
    {
    'name'     : 'click',
    'callback' : function () { hidePlayer(); },
    'flag'     : false,
    },
    {
    'name'     : 'mouseover',
    'callback' : function () { $('GM_close_utton').style.backgroundColor = '#ffd700'; },
    'flag'     : false,
    },
    {
    'name'     : 'mouseout',
    'callback' : function () { $('GM_close_utton').style.backgroundColor = '#00cc00'; },
    'flag'     : false,
    },
    ],
    });
    [document.createTextNode(videoTitle), closeButton,].forEach(function (e) {
    playerHeader.appendChild(e);
    });
    return playerHeader;
    }
    function createPlayerFooter (videoId, videoTitle, player) {
    var playerFooter = $C('div', {
    'style' : {
    'padding' : '0.5em 0',
    }
    });
    var linkContainer = $C('p', {style : { 'textAlign' : 'center', 'margin' : '0', }});
    var youtubeUri    = ['http://www.youtube.com/watch?v=', videoId].join('');
    [
    {
    'id'       : 'GM_download',
    'title'    : 'download this video',
    'getUri'   : ['http://youtubech.com/test/read.cgi?dl=', videoId].join(''),
    'label'    : 'Download',
    'image'    : 'download',
    },
    {
    'id'       : 'GM_post_to_hatena',
    'title'    : 'post to Hatena::Bookmark',
    'getUri'   : ['http://b.hatena.ne.jp/add?mode=confirm&url=', encodeURIComponent(youtubeUri)].join(''),
    'label'    : 'Hatena',
    'image'    : 'arrow',
    },
    {
    'id'       : 'GM_post_to_delicious',
    'title'    : 'post to del.icio.us',
    'getUri'   : ['http://del.icio.us/post?v=4;url=', encodeURIComponent(youtubeUri), ';title=', encodeURIComponent(videoTitle)].join(''),
    'label'    : 'del.icio.us',
    'image'    : 'arrow',
    },
    {
    'id'       : 'GM_post_to_qooqle',
    'title'    : 'post to qooqle',
    'getUri'   : ['http://clippers.qooqle.jp/post?url=', encodeURIComponent(youtubeUri)].join(''),
    'label'    : 'Qooqle',
    'image'    : 'arrow',
    },
    ].forEach(function (i) {
    var link = $C('span', {
    'id'    : i.id,
    'title' : i.title,
    'style' : {
    'color'         : '#00cc00',
    'textIndent'    : '20px',
    'verticalAlign' : 'middle',
    'marginRight'   : '0.5em',
    'cursor'        : 'pointer',
    },
    'event' : [
    {
    'name'     : 'click',
    'callback' : function () { window.open(i.getUri); },
    'flag'     : false,
    },
    {
    'name'     : 'mouseover',
    'callback' : function () { $(i.id).style.color = '#ffc125'; },
    'flag'     : false,
    },
    {
    'name'     : 'mouseout',
    'callback' : function () { $(i.id).style.color = '#00cc00'; },
    'flag'     : false,
    },
    ],
    });
    var icon = $C('img', {
    'src'   : getImageUrl(i.image),
    'style' : {
    'marginRight'     : '0.5em',
    'backgroundColor' : '#00cc00',
    },
    });
    [icon, document.createTextNode(i.label)].forEach(function (e) {
    link.appendChild(e);
    });
    linkContainer.appendChild(link);
    });
    playerFooter.appendChild(linkContainer);
    [
    {
    'label' : 'URI:',
    'id'    : 'youtube_uri',
    'value' : ['http://www.youtube.com/watch?v=', videoId].join(''),
    },
    {
    'label' : 'Player:',
    'id'    : 'youtube_embed',
    'value' : player.innerHTML,
    },
    {
    'label' : 'Hatena:',
    'id'    : 'hatena_notation',
    'value' : ['[http://youtubech.com/test/read.cgi', encodeURIComponent(['?dl=', videoId, '&.flv'].join('')), ':movie]'].join(''),
    },
    ].forEach(function (i) {
    var inputContainer = $C('p', { 'style' : { 'margin': '0.2em 0 0 0'} });
    var label = $C('label', {
    'for'   : i.id,
    });
    label.innerHTML = i.label;
    var input = $C('input', {
    'id'    : i.id,
    'style' : {
    'border'   : 'solid 1px #333333',
    'width'    : '80%',
    'position' : 'absolute',
    'right'    : '1.5em',
    },
    'event'   : [
    {
    'name'     : 'focus',
    'callback' : function () { $(i.id).select(); },
    'flag'     : false,
    },
    {
    'name'     : 'click',
    'callback' : function () { $(i.id).select(); },
    'flag'     : false,
    },
    ]
    });
    input.value = i.value;
    [label, input].forEach(function (e) {
    inputContainer.appendChild(e);
    });
    playerFooter.appendChild(inputContainer);
    });
    return playerFooter;
    }
    function createPlayer (videoId) {
    var videoSrc = ['http://www.youtube.com/v/', videoId].join('');
    var div    = $C('div', {});
    var object = $C('object', {
    'width'  : playerWidth,
    'height' : playerHeight,
    });
    var param  = $C('param',  {
    'name'   : 'movie',
    'value'  : videoSrc,
    });
    var embed  = $C('embed',  {
    'width'  : playerWidth,
    'height' : playerHeight,
    'type'   : 'application/x-shockwave-flash',
    'src'    : videoSrc,
    });
    [param, embed].forEach(function (e) { object.appendChild(e); });
    div.appendChild(object);
    return div;
    }
    function showPlayer (videoId, videoTitle) {
    var playerContainer  = createPlayerContainer(videoId, videoTitle);
    var overlay = $C('div', {
    id     : overlayId,
    style  : {
    'position'        : 'fixed',
    'top'             : '0%',
    'left'            : '0%',
    'width'           : '100%',
    'height'          : '100%',
    'zIndex'          : '99',
    'backgroundImage' : ['url("', getImageUrl('overlay'), '")'].join(''),
    },
    event  : [{
    'name'     : 'click',
    'callback' : function () { hidePlayer(); },
    'flag'     : false,
    }]
    });
    [overlay, playerContainer].forEach(function (e) {
    document.body.appendChild(e);
    });
    centering($(playerContainerId), 0, 0);
    }
    function hidePlayer () {
    [$(playerContainerId), $(overlayId)].forEach(function (e) {
    e.parentNode.removeChild(e);
    });
    }
    function centering (element, x, y) {
    with (element.style) {
    left = ((window.innerWidth  - element.offsetWidth)  / 2 + (x)) + 'px';
    top  = ((window.innerHeight - element.offsetHeight) / 2 + (y)) + 'px';
    }
    }
    function getImageUrl (name) {
    var images = {
    'close'    : "data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%10%00%00%00%10%01%03%00%00%00%25%3Dm%22%00%00%00%03sBIT%08%08%08%DB%E1O%E0%00%00%00%06PLTE%FF%FF%FF%FF%FF%FFU%7C%F5l%00%00%00%02tRNS%FF%00%E5%B70J%00%00%00%09pHYs%00%00%0B%12%00%00%0B%12%01%D2%DD~%FC%00%00%00!tEXtSoftware%00Macromedia%20Fireworks%204.0%EA%26'u%00%00%00%16tEXtCreation%20Time%0006%2F02%2F06%3Eg%BF%E4%00%00%00%22IDATx%9Cc%F8%FF%9F%01%8E%3E%9Fg%F8%D8%CF%F0C%9E%E1%8F%3D%08%01%19%40.P%10I%0D%00%8C%AA%1B%19%A3%7D%C5%A0%00%00%00%00IEND%AEB%60%82",
    'download' : "data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%10%00%00%00%10%01%03%00%00%00%25%3Dm%22%00%00%00%03sBIT%08%08%08%DB%E1O%E0%00%00%00%06PLTE%FF%FF%FF%FF%FF%FFU%7C%F5l%00%00%00%02tRNS%FF%00%E5%B70J%00%00%00%09pHYs%00%00%0B%12%00%00%0B%12%01%D2%DD~%FC%00%00%00!tEXtSoftware%00Macromedia%20Fireworks%204.0%EA%26'u%00%00%00%16tEXtCreation%20Time%0006%2F02%2F06%3Eg%BF%E4%00%00%00'IDATx%9Cc%F8%FF%9F%01%82%FE%D5C%D1%07~%86%1F%F2%0C%7F%EC%19%BE%E53%FC%BE%CF%F0w%3FH%10%A6%0C%00p%88%1A5%81%FD%3F%D0%00%00%00%00IEND%AEB%60%82",
    'arrow'    : "data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%10%00%00%00%10%01%03%00%00%00%25%3Dm%22%00%00%00%03sBIT%08%08%08%DB%E1O%E0%00%00%00%06PLTE%FF%FF%FF%FF%FF%FFU%7C%F5l%00%00%00%02tRNS%FF%00%E5%B70J%00%00%00%09pHYs%00%00%0B%12%00%00%0B%12%01%D2%DD~%FC%00%00%00!tEXtSoftware%00Macromedia%20Fireworks%204.0%EA%26'u%00%00%00%16tEXtCreation%20Time%0006%2F02%2F06%3Eg%BF%E4%00%00%00%26IDATx%9Cc%F8%FF%9F%01%8E%3E%F0%83%D0%7F~%86%7F%FC%0C%7F%FC%19~%9Cg%F8x%9E%E1%F3y%06%245%00p%20%1A%9E%26%CAE%0A%00%00%00%00IEND%AEB%60%82",
    'overlay'  : "data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%01%00%00%00%01%01%03%00%00%00%25%DBV%CA%00%00%00%03sBIT%08%08%08%DB%E1O%E0%00%00%00%06PLTE%FF%FF%FF%00%00%00U%C2%D3~%00%00%00%02tRNS%00%BB*%20%A7%3C%00%00%00%09pHYs%00%00%1B%BC%00%00%1B%BC%01%BA%B7%A0%BB%00%00%00!tEXtSoftware%00Macromedia%20Fireworks%204.0%EA%26'u%00%00%00%0AIDATx%9Cch%00%00%00%82%00%81w%CDr%B6%00%00%00%00IEND%AEB%60%82",
    };
    return images[name];
    }
    })();
  • "どこでも YouTube" を実現する Greasemonkey スクリプト YouTube Anywhere

    ,

    YouTube の動画を鑑賞・収集する上で便利で素敵なサービスがいろいろと登場しています。

    ただまぁ僕の Web ブラウズのやり方は「livedoor Reader + YouTube + はてなブックマークで最速動画ウォッチング」で紹介したように、厳選したサービスを使い込む感じで、それ以外はまぁひと様のリンクとか、自分とこのブックマークに蓄積したもの経由でしか YouTube の動画を見ないだろうな、とか思うわけです。
    そんな折り、かせいさんとこ – del.icio.us上のYouTubeへのリンクを直接見たりダウンロードできるGreasemonkey にて公開されていたスクリプトに触発されて、僕も似たようなものを作ってみました。YouTube の動画へのリンクがあると、そのリンクの脇に YouTube アイコンを表示、アイコンをクリックすると HATENA-TUBE ライクなプレイヤが現われるってなもの。
    YouTube player on Hatena::Bookmark
    デフォルトでは del.icio.usはてなブックマーク上のみで動作するよう設定してありますが、「ツール -> Manage UserScripts」から include するドメインを増やすよう設定すれば、いろんなところで使えます。
    まぁ、サイトによってはスタイルとかがかなりと悲惨なことにもなりかねませんが、そのへんは見なかったことにして、あまり気にしないでください。

    追記。
    このスクリプトは「antipop – Update: LDR で最速動画ウォッチング + YouTube Anywhere」で更新されています。そちらのエントリも合せてご覧ください。

    インストール: youtube_anywhere.user.js

    // ==UserScript==
    //
    // @name          YouTube Anywhere
    // @namespace     http://antipop.gs/ns/greasemonkey/youtube_anywhere
    // @include       http://b.hatena.ne.jp/*
    // @include       http://del.icio.us/*
    //
    // ==/UserScript==
    //
    // ==Configuration==
    //
    // add other domains to @include for more use
    //
    // - include YouTube.
    // @include       *youtube.com/*
    //
    // - include whole the web
    // @include       *
    //
    //==/Configuration==
    //
    //==Acknowledgments==
    //
    // Bitcons >> SOME RANDOM DUDE
    //  - http://somerandomdude.net/srd-projects/bitcons/
    //
    //==/Acknowledgments==
    //
    // ==Copyright==
    //
    // Copyright (C) 2006 by Kentaro Kuribayashi
    //
    // This script is distributed under the CCPL by-sa
    //  - See: http://creativecommons.org/licenses/by-sa/2.5/
    //
    // ==/Copyright==
    //
    (function(){
    var playerContainerId = 'GM_youtube_player';
    var playerWidth  = 425;
    var playerHeight = 350;
    var overlayId    = 'GM_overlay';
    var regexp       = new RegExp('^http://(?:www\\.)?youtube\\.com/(?:watch)?\\?.*v=([^&]+).*$', 'i');
    $A(document.links).forEach(function (link) {
    if (link.href.match(regexp)) {
    var videoId    = RegExp.$1;
    var videoTitle = link.innerHTML.replace(/<.+?>/g, '');
    var icon = $C('img', {
    'src'   : 'http://www.youtube.com/favicon.ico',
    'alt'   : 'YouTube',
    'title' : 'watch this movie',
    'style' : {
    'marginRight' : '0.5em',
    'cursor'      : 'pointer',
    },
    'event' : [{
    'name'     : 'click',
    'callback' : function () { showPlayer(videoId, videoTitle); },
    'flag'     : false,
    }],
    });
    link.parentNode.insertBefore(icon, link);
    }
    });
    function $ (id) { return document.getElementById(id); };
    function $A (arg) {
    var array = [];
    for (var i = 0; i < arg.length; i++) {
    array.push(arg[i]);
    }
    return array;
    }
    function $C (tag, prop) {
    var element = document.createElement(tag);
    for (var i in prop) {
    if (i == 'style') {
    for (var p in prop[i]) {
    element.style[p] = prop[i][p];
    }
    }
    else if (i == 'event') {
    prop[i].forEach(function (e) {
    element.addEventListener(e.name, e.callback, e.flag);
    });
    }
    else {
    element[i] = prop[i];
    }
    }
    return element;
    };
    function createPlayerContainer (videoId, videoTitle) {
    var playerContainer = $C('div', {
    'id'     : playerContainerId,
    'style'  : {
    'position'        : 'fixed',
    'zIndex'          : '100',
    'backgroundColor' : '#ffffff',
    'padding'         : '0.5em 1.5em',
    },
    });
    var playerHeader = createPlayerHeader(videoTitle);
    var player       = createPlayer(videoId);
    var playerFooter = createPlayerFooter(videoId, videoTitle, player);
    [playerHeader, player, playerFooter].forEach(function (e) {
    playerContainer.appendChild(e);
    });
    return playerContainer;
    }
    function createPlayerHeader (videoTitle) {
    var playerHeader = $C('p', {
    'style' : {
    'fontWeight' : 'bold',
    'margin'     : '0.5em 2em 0.5em 0',
    }
    });
    var closeButton = $C('img', {
    'id'    : 'GM_close_utton',
    'src'   : getImageUrl('close'),
    'title' : 'Hide this player',
    'style' : {
    'display'         : 'block',
    'position'        : 'absolute',
    'top'             : '5px',
    'right'           : '5px',
    'cursor'          : 'pointer',
    'backgroundColor' : '#00cc00',
    },
    'event' : [
    {
    'name'     : 'click',
    'callback' : function () { hidePlayer(); },
    'flag'     : false,
    },
    {
    'name'     : 'mouseover',
    'callback' : function () { $('GM_close_utton').style.backgroundColor = '#ffd700'; },
    'flag'     : false,
    },
    {
    'name'     : 'mouseout',
    'callback' : function () { $('GM_close_utton').style.backgroundColor = '#00cc00'; },
    'flag'     : false,
    },
    ],
    });
    [document.createTextNode(videoTitle), closeButton,].forEach(function (e) {
    playerHeader.appendChild(e);
    });
    return playerHeader;
    }
    function createPlayerFooter (videoId, videoTitle, player) {
    var playerFooter = $C('div', {
    'style' : {
    'padding' : '0.5em 0',
    }
    });
    var linkContainer = $C('p', {style : { 'textAlign' : 'center', 'margin' : '0', }});
    var youtubeUri    = ['http://www.youtube.com/watch?v=', videoId].join('');
    [
    {
    'id'       : 'GM_download',
    'title'    : 'download this video',
    'getUri'   : ['http://youtubech.com/test/read.cgi?dl=', videoId].join(''),
    'label'    : 'Download',
    'image'    : 'download',
    },
    {
    'id'       : 'GM_post_to_hatena',
    'title'    : 'post to Hatena::Bookmark',
    'getUri'   : ['http://b.hatena.ne.jp/add?mode=confirm&url=', encodeURIComponent(youtubeUri)].join(''),
    'label'    : 'Hatena',
    'image'    : 'arrow',
    },
    {
    'id'       : 'GM_post_to_delicious',
    'title'    : 'post to del.icio.us',
    'getUri'   : ['http://del.icio.us/post?v=4;url=', encodeURIComponent(youtubeUri), ';title=', encodeURIComponent(videoTitle)].join(''),
    'label'    : 'del.icio.us',
    'image'    : 'arrow',
    },
    {
    'id'       : 'GM_post_to_qooqle',
    'title'    : 'post to qooqle',
    'getUri'   : ['http://clippers.qooqle.jp/post?url=', encodeURIComponent(youtubeUri)].join(''),
    'label'    : 'Qooqle',
    'image'    : 'arrow',
    },
    ].forEach(function (i) {
    var link = $C('span', {
    'id'    : i.id,
    'title' : i.title,
    'style' : {
    'color'         : '#00cc00',
    'textIndent'    : '20px',
    'verticalAlign' : 'middle',
    'marginRight'   : '0.5em',
    'cursor'        : 'pointer',
    },
    'event' : [
    {
    'name'     : 'click',
    'callback' : function () { window.open(i.getUri); },
    'flag'     : false,
    },
    {
    'name'     : 'mouseover',
    'callback' : function () { $(i.id).style.color = '#ffc125'; },
    'flag'     : false,
    },
    {
    'name'     : 'mouseout',
    'callback' : function () { $(i.id).style.color = '#00cc00'; },
    'flag'     : false,
    },
    ],
    });
    var icon = $C('img', {
    'src'   : getImageUrl(i.image),
    'style' : {
    'marginRight'     : '0.5em',
    'backgroundColor' : '#00cc00',
    },
    });
    [icon, document.createTextNode(i.label)].forEach(function (e) {
    link.appendChild(e);
    });
    linkContainer.appendChild(link);
    });
    playerFooter.appendChild(linkContainer);
    [
    {
    'label' : 'URI:',
    'id'    : 'youtube_uri',
    'value' : ['http://www.youtube.com/watch?v=', videoId].join(''),
    },
    {
    'label' : 'Player:',
    'id'    : 'youtube_embed',
    'value' : player.innerHTML,
    },
    {
    'label' : 'Hatena:',
    'id'    : 'hatena_notation',
    'value' : ['[http://youtubech.com/test/read.cgi', encodeURIComponent(['?dl=', videoId, '&.flv'].join('')), ':movie]'].join(''),
    },
    ].forEach(function (i) {
    var inputContainer = $C('p', { 'style' : { 'margin': '0.2em 0 0 0'} });
    var label = $C('label', {
    'for'   : i.id,
    });
    label.innerHTML = i.label;
    var input = $C('input', {
    'id'    : i.id,
    'style' : {
    'border'   : 'solid 1px #333333',
    'width'    : '80%',
    'position' : 'absolute',
    'right'    : '1.5em',
    },
    'event'   : [
    {
    'name'     : 'focus',
    'callback' : function () { $(i.id).select(); },
    'flag'     : false,
    },
    {
    'name'     : 'click',
    'callback' : function () { $(i.id).select(); },
    'flag'     : false,
    },
    ]
    });
    input.value = i.value;
    [label, input].forEach(function (e) {
    inputContainer.appendChild(e);
    });
    playerFooter.appendChild(inputContainer);
    });
    return playerFooter;
    }
    function createPlayer (videoId) {
    var videoSrc = ['http://www.youtube.com/v/', videoId].join('');
    var div    = $C('div', {});
    var object = $C('object', {
    'width'  : playerWidth,
    'height' : playerHeight,
    });
    var param  = $C('param',  {
    'name'   : 'movie',
    'value'  : videoSrc,
    });
    var embed  = $C('embed',  {
    'width'  : playerWidth,
    'height' : playerHeight,
    'type'   : 'application/x-shockwave-flash',
    'src'    : videoSrc,
    });
    [param, embed].forEach(function (e) { object.appendChild(e); });
    div.appendChild(object);
    return div;
    }
    function showPlayer (videoId, videoTitle) {
    var playerContainer  = createPlayerContainer(videoId, videoTitle);
    var overlay = $C('div', {
    id     : overlayId,
    style  : {
    'position'        : 'fixed',
    'top'             : '0%',
    'left'            : '0%',
    'width'           : '100%',
    'height'          : '100%',
    'zIndex'          : '99',
    'backgroundImage' : ['url("', getImageUrl('overlay'), '")'].join(''),
    },
    event  : [{
    'name'     : 'click',
    'callback' : function () { hidePlayer(); },
    'flag'     : false,
    }]
    });
    [overlay, playerContainer].forEach(function (e) {
    document.body.appendChild(e);
    });
    centering($(playerContainerId), 0, -10);
    }
    function hidePlayer () {
    [$(playerContainerId), $(overlayId)].forEach(function (e) {
    e.parentNode.removeChild(e);
    });
    }
    function centering (element, x, y) {
    with (element.style) {
    left = ((window.innerWidth  - element.offsetWidth)  / 2 + (x)) + 'px';
    top  = ((window.innerHeight - element.offsetHeight) / 2 + (y)) + 'px';
    }
    }
    function getImageUrl (name) {
    var images = {
    'close'    : "data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%10%00%00%00%10%01%03%00%00%00%25%3Dm%22%00%00%00%03sBIT%08%08%08%DB%E1O%E0%00%00%00%06PLTE%FF%FF%FF%FF%FF%FFU%7C%F5l%00%00%00%02tRNS%FF%00%E5%B70J%00%00%00%09pHYs%00%00%0B%12%00%00%0B%12%01%D2%DD~%FC%00%00%00!tEXtSoftware%00Macromedia%20Fireworks%204.0%EA%26'u%00%00%00%16tEXtCreation%20Time%0006%2F02%2F06%3Eg%BF%E4%00%00%00%22IDATx%9Cc%F8%FF%9F%01%8E%3E%9Fg%F8%D8%CF%F0C%9E%E1%8F%3D%08%01%19%40.P%10I%0D%00%8C%AA%1B%19%A3%7D%C5%A0%00%00%00%00IEND%AEB%60%82",
    'download' : "data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%10%00%00%00%10%01%03%00%00%00%25%3Dm%22%00%00%00%03sBIT%08%08%08%DB%E1O%E0%00%00%00%06PLTE%FF%FF%FF%FF%FF%FFU%7C%F5l%00%00%00%02tRNS%FF%00%E5%B70J%00%00%00%09pHYs%00%00%0B%12%00%00%0B%12%01%D2%DD~%FC%00%00%00!tEXtSoftware%00Macromedia%20Fireworks%204.0%EA%26'u%00%00%00%16tEXtCreation%20Time%0006%2F02%2F06%3Eg%BF%E4%00%00%00'IDATx%9Cc%F8%FF%9F%01%82%FE%D5C%D1%07~%86%1F%F2%0C%7F%EC%19%BE%E53%FC%BE%CF%F0w%3FH%10%A6%0C%00p%88%1A5%81%FD%3F%D0%00%00%00%00IEND%AEB%60%82",
    'arrow'    : "data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%10%00%00%00%10%01%03%00%00%00%25%3Dm%22%00%00%00%03sBIT%08%08%08%DB%E1O%E0%00%00%00%06PLTE%FF%FF%FF%FF%FF%FFU%7C%F5l%00%00%00%02tRNS%FF%00%E5%B70J%00%00%00%09pHYs%00%00%0B%12%00%00%0B%12%01%D2%DD~%FC%00%00%00!tEXtSoftware%00Macromedia%20Fireworks%204.0%EA%26'u%00%00%00%16tEXtCreation%20Time%0006%2F02%2F06%3Eg%BF%E4%00%00%00%26IDATx%9Cc%F8%FF%9F%01%8E%3E%F0%83%D0%7F~%86%7F%FC%0C%7F%FC%19~%9Cg%F8x%9E%E1%F3y%06%245%00p%20%1A%9E%26%CAE%0A%00%00%00%00IEND%AEB%60%82",
    'overlay'  : "data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%01%00%00%00%01%01%03%00%00%00%25%DBV%CA%00%00%00%03sBIT%08%08%08%DB%E1O%E0%00%00%00%06PLTE%FF%FF%FF%00%00%00U%C2%D3~%00%00%00%02tRNS%00%BB*%20%A7%3C%00%00%00%09pHYs%00%00%1B%BC%00%00%1B%BC%01%BA%B7%A0%BB%00%00%00!tEXtSoftware%00Macromedia%20Fireworks%204.0%EA%26'u%00%00%00%0AIDATx%9Cch%00%00%00%82%00%81w%CDr%B6%00%00%00%00IEND%AEB%60%82",
    };
    return images[name];
    }
    })();
  • livedoor Reader + YouTube + はてなブックマークで最速動画ウォッチング

    , , ,

    YouTube のタグによる検索結果の feed アイコンを表示する Greasemonkey スクリプト」で述べた通り、YouTube の検索結果による feed を利用できることがわかったので、早速 livedoor Reader に登録してみました。それはそれでまぁいいんだけど、「これはよさげ?」という動画をいちいちタブを開いて観るのめんどくさい!feed のチェックから視聴、はたまた気に入った動画のブックマークまでの全てを、LDR 内で完結させたい!!!という思いがむくむくと湧き起こってきました。
    というわけで、上記要求を満たすのに必要なもののうち、欠けていた LDR 内で YouTube の動画を観られるようにする greasemonkey スクリプトを書いた(詳細は後述)ので、なかなか快適な YouTube ヲチ生活を送れそうです。百聞は一見に如かずというわけで、とりあえずデモンストレーション動画をご覧ください。
    YouTube – Mashup livedoor Reader, YouTube and Hatena::Bookmark
    http://ktr.s101.xrea.com/pg6mxOCCxnM.flv
    つか、初めて YouTube に動画をうpってみたのですが、画質が激しく劣化するんですな。。。↑のプレイヤじゃぁ小さ過ぎて、なにをやってるのかよくわかんないかも。詳しくは、リンク先の YouTube にてご覧ください(それでもなんかばっちい画質になっちゃってるけど……)。
    見ればおおむねわかると思うけど、デモ動画の流れを順を追って説明します。

    1. YouTube で「中川翔子」を検索
    2. 中川翔子の検索結果が表示される
    3. 検索語入力欄の左に feed アイコンが表示されているのが確認できる
    4. feed をブラウザで表示、ブックマークレットにより、その feed を LDR に登録
    5. LDR に画面が遷移
    6. とりあえずデモなので、上で登録した feed だけ表示されるように、条件絞り込み欄に「中川翔子」と入力
    7. YouTube :: Tag // 中川翔子」が表示される
    8. ショートカット “y” キー押下により、ブーンをやっている映像がその場で YouTube のプレイヤーにより表示される
    9. ブーン閉じる
    10. 次のエントリへ移動
    11. 「Brilliant Dream PV (試聴バージョン)」を開く
    12. 気に入ったので、動画を観ながらショートカット “b” キーを押下し、はてなブックマークにぶくまする
    13. PV 閉じる
    14. 終了

    ここでは YouTube の feed を見ている時にその場で動画を観るデモを見せましたが、エントリの URL が YouTube のものでありさえすればいいので、たとえばはてなブックマーク等でブックマークした YouTube 動画等も観ることができます。
    なんか緊張してとちったりしてるところがあって、いまいち速い感じを演出できなかったのですが、まぁなかなか快適です。LDR 内で feed をチェックしながら YouTube のプレイヤを表示、動画を観られるようにする greasemonkey スクリプトは以下からインストールできます。使い方は、フォーカスされているエントリが YouTube の動画である時に、ショートカット “y” キーを押すだけです。どうぞご利用ください。

    追記。
    このスクリプトは「antipop – Update: LDR で最速動画ウォッチング + YouTube Anywhere」で更新されています。そちらのエントリも合せてご覧ください。

    インストール: ldr_youtube_player.user.js

    // ==UserScript==
    // @name          YouTube Player on LDR
    // @namespace     http://antipop.gs/ns/greasemonkey/ldr_youtube_player
    // @include       http://reader.livedoor.com/reader*
    // ==/UserScript==
    (function(){
    var w       = unsafeWindow;
    var _onload = w.onload;
    var onload  = function(){
    with (w) {
    var playerId = 'GM_youtube_player';
    Keybind.add('y', function () {
    if ($('overlay')) {
    hidePlayer();
    }
    else {
    showPlayer();
    }
    });
    function showPlayer () {
    var item = get_active_item(true);
    if (item) {
    var regexp = new RegExp('^http://(?:www\\.)?youtube\\.com/(?:watch)?\\?.*v=([^&]+).*$', 'i');
    if (item.link.match(regexp)) {
    var overlay = $N("div", {id : 'overlay'});
    var player  = createPlayer(RegExp.$1);
    [overlay, player].forEach(function(e){document.body.appendChild(e)});
    centering(player.id, 0, 50);
    }
    }
    }
    function hidePlayer () {
    [playerId, 'overlay'].forEach(function(id){DOM.remove(id);});
    }
    function createPlayer (videoId) {
    var videoSrc = ['http://www.youtube.com/v/', videoId].join('');
    var object = $N('object', {
    id     : playerId,
    width  : 425,
    height : 350,
    style  : {position : 'absolute'}
    });
    var param  = $N('param',  {
    name   : 'movie',
    value  : videoSrc
    });
    var embed  = $N('embed',  {
    width  : 425,
    height : 350,
    type   : 'application/x-shockwave-flash',
    src    : videoSrc
    });
    [param, embed].forEach(function(e){object.appendChild(e);});
    return object;
    }
    }
    };
    w.onload = function(){
    _onload();
    onload();
    };
    })();