NicoNicoPlaylist mod改造

まえおき

firefoxでニコニコの動画をプレイリスト(キュー)に放り込んで自動的に連続再生してくれるgreasemonkeyスクリプトがありまして。

http://userscripts.org/scripts/show/50639

ただ、自分はボカ路地を取り込んで再生するのが普段のスタイルなので、外部サイトから一括プレイリスト取り込みできないのでは使い物にならない。

そこで、ボカ路地ほか外部サイトでもページ内のニコニコへのリンクぜんぶ追加できるように改造しました。

改造の内容

この機能を担当しているpushAllVideos()メソッドでは、ニコニコ内で利用する場合、aタグを総ナメにして、class="video"のもののみを取得するようにしている。
でも外部サイトではclass="video"と設定されてるとは限らない。

たぶんこれは重複(タイトル文字列とサムネイル、両方にaタグで同じ動画へのリンクが張られてる)の排除が目的。

なので、classは見ずに、重複排除を別の形で行うようにしました。

適用の仕方

  1. グリモンのインストールについては省略。
  2. 「ユーザースクリプトの管理」で、プレイリスト取込の対象にしたい(小窓を表示させたい)サイトを追加する。
  3. 「ユーザースクリプトの編集」で、pushAllVideosメソッド内部の記述を以下のように入れ替える。

この改造はバージョン1.8.1をベースにしています。NicoNicoPlaylist modのバージョンがら更新されてたら、処理が変わってる可能性もあるので要確認。

before
                       this.clone();
                       var as=$x("//a[contains(concat(' ',normalize-space(@class),' '),'
video ')]");
                       if(chPage) as=as.concat($x("//a[contains(normalize-space(@class),'community_video')]"));
                       else if(dicPage) as=as.concat($x("//a[@rel='nofollow']"));
                       for(var i=0, len=as.length; i<len; i++){
                               var a=as[i],m,title,t;
                               if(a.className.indexOf("noadd")>-1) continue;
                               if(m=a.href.match(URL.WATCH_REGEXP)){
                                       if(dicPage) title=a.textContent;
                                       else title=(t=a.innerHTML.match(/<span[^>]*?>(.*?)<\/span>/))?
t[1]:a.innerHTML;
                                       this.playlist.push(new Video(m[1],title));
                               }
                       }

after
                       this.clone();
                       var as=$x("//a");
                       var list=[];
                       if(chPage) as=as.concat($x("//a[contains(normalize-space(@class),'community_video')]"));
                       else if(dicPage) as=as.concat($x("//a[@rel='nofollow']"));
                       for(var i=0, len=as.length; i<len; i++){
                               var a=as[i],m,title,t;
                               if(a.className.indexOf("noadd")>-1) continue;
                               if(m=a.href.match(URL.WATCH_REGEXP)){
                                       if(dicPage) title=a.textContent;
                                       else title=(t=a.innerHTML.match(/<span[^>]*?>(.*?)<\/span>/))?
t[1]:a.innerHTML;
                                       list[m[1]] = title;
                               }
                       }
                       for (var id in list) playlist.push(new Video(id,list[id]));

おまけ

本家NicoNicoPlaylistをベースにした改造もしてみたので必要にあわせてどうぞ。
function全体差し替え。

               pushAllVideos: function () {
                       var as = document.getElementsByTagName("a");
                       var videos = [];
                       var list = [];
                       for (var i = 0, len = as.length; i < len; i++) {
                               var a = as[i];
                               if (!a.href) continue;
                               var m;
                               if (m = a.href.match(NicoNico.WATCH_PAGE_REGEXP)) list[m[1]] = a.innerHTML;
                       }
                       for (var id in list) videos.push({ id: id, title: list[id] });
                       var videoId = this.getPageVideoId();
                       if (videoId) {
                               var self = this;
                               var thisVideo = new Video(videoId);
                               thisVideo.getRelatedVideos(function (videoId, title) {
                                       videos.push({ id: videoId, title: title });
                               }, function () {
                                       self.pushVideos(videos);
                               });
                       } else {
                               this.pushVideos(videos);
                       }
               },

PC整理

シゴタノやamachangに触発され、PCを色々整理しようと思った。
http://d.hatena.ne.jp/amachang/20081221/1229855379
http://cyblog.jp/modules/weblog/details.php?blog_id=822
http://de-lab.com/article/rememberthemilk-love/

windowsで環境を整える。
プログラムのフォルダをカテゴライズする。「[EUB][IMDT]_フォルダ」といった感じ。c:\appsに移す。c:\binも作る。
各種アプリへはfenrirを基本にアクセスするようにする。
dropbox入れる。RTM入れる。atoksync入れたが、winでは2006,Macでは2008なので辞書共有できないらしい。えー。買うのやだなあ。
https://addons.mozilla.org/ja/firefox/addon/2410
firefoxのsyncもする。

日記をもうちょっとさっくり書けるツールが欲しい。じゃないと長続きしなさそうだ。

WSHで作る便利スクリプト

アプリのインストールなど無駄に制限がある職場で、それでも便利にしたいときに使えるのがWSHWindows2000以降で組み込み機能なので便利。

フルパス取得

'フルパス取得スクリプト
'概要:選択したファイルのフルパスをクリップボードにコピーする
'20070309作成
'20070510改修:ショートカットの場合、ショートカット先を取得する機能を追加

Set WSHShell = WScript.CreateObject("WScript.Shell")
Set arg = WScript.Arguments
Set Fs = WScript.CreateObject("Scripting.FileSystemObject")

if arg.length=1 then
'単体の場合、
[TAB]files= getPath(arg(0))
else
'複数ファイルの場合、
[TAB]for i=0 to arg.length-1
[TAB][TAB]files = files & getPath(arg(i)) & vbCrLf
[TAB]next
end if

'クリップボードに取得した情報を格納する。
'WSH単体ではクリップボードへのアクセスはできないため、IEを仲介する。
Set objIE = CreateObject("InternetExplorer.Application")
objIE.Navigate("about:blank")
strURL = objIE.document.parentwindow.clipboardData.GetData("text")
ObjIE.Document.parentWindow.clipboardData.setData "text",files
objIE.Quit


function getPath(fl)
'引数として渡されたファイルから、フルパスを取得する。
[TAB]
[TAB]if Fs.GetExtensionName(fl)<>"lnk" Then
[TAB]'通常のファイルの場合
[TAB][TAB]getPath=Fs.GetAbsolutePathName(fl)
[TAB]else
[TAB]'ショートカットの場合
[TAB][TAB]If MsgBox(Fs.GetFileName(fl) & vbCrLf _
[TAB][TAB]   & "はショートカットです。" & vbCrLf _
[TAB][TAB]   & "ショートカット先のパスを取得しますか?",vbYesNo) = vbYes Then
[TAB][TAB][TAB]Set Sc = WSHShell.CreateShortcut(fl)
[TAB][TAB][TAB]getPath= Sc.TargetPath
[TAB][TAB][TAB]Set Sc = Nothing
[TAB][TAB]Else
[TAB][TAB][TAB]getPath=Fs.GetAbsolutePathName(fl)
[TAB][TAB]End if
[TAB]End if
end function

備忘くん

**forget_em_not.vbs
'VBS:備忘くん
'作成:07.04.26

'FileSystemオブジェクトのインスタンス化
Set FSO = WScript.CreateObject("Scripting.FileSystemObject")

'二重起動の抑止。アベンダーが存在するのは【備忘くん】起動最中のみ。
If FSO.FileExists("備忘くんアベンダー") then
[TAB]'二重起動の抑止2。アベンダーは起動中、1分毎に更新される。
[TAB]'つまりアベンダーの更新時刻が現時刻or1分前と等しくない場合、
[TAB]'アベンダーは存在するが備忘くんは動いていない。
[TAB]if  left(formatDateTime(now,vbGeneralDate),16) _
[TAB]  = left(formatDateTime(FSO.GetFile("備忘くんアベンダー").DateLastModified, vbGeneralDate),16) _
[TAB] or left(formatDateTime(now,vbGeneralDate),16) _
[TAB]  = left(formatDateTime(DateAdd("n", -1, now),vbGeneralDate),16) then

[TAB][TAB]i=msgbox ("すでに【備忘くん】が起動しています。",,"備忘くん")
[TAB][TAB]WScript.quit

[TAB]end if
end if

'起動。アベンダー作成。
Set test = FSO.OpenTextFile("備忘くんアベンダー",8,true)
test.WriteLine "当ファイルを削除することで、スクリプトをABENDできます。"
test.WriteLine "なお、起動した日時は" & date & time & "です。"
Set test = nothing

'起動メッセージ表示。ただし起動オプション "-h" がついているときは非表示
Set arg = WScript.Arguments

hidden=false

if arg.length=1 then
[TAB]if arg(0)="-h" then hidden=true
end if

if hidden=false then
[TAB][TAB]msgbox "【備忘くん】を起動します。" & vbCrLf _
[TAB][TAB]     & vbCrLf _
[TAB][TAB]     & "【備忘くん】は無限ループ仕様です。" & vbCrLf _
[TAB][TAB]     & "終了させたいときは、" & vbCrLf _
[TAB][TAB]     & "起動時に同フォルダに生成されるファイル「備忘くんアベンダー」を"  & vbCrLf _
[TAB][TAB]     & "エクスプローラから削除してください。ABENDできます。"  & vbCrLf _
[TAB][TAB]     & vbCrLf _
[TAB][TAB]     & "アラートさせるタスクは「forget_em_not.ini」に記入してください。"  & vbCrLf _
[TAB][TAB]     & "なお、iniファイル更新後、当スクリプトの再起動は必要ありません。"  & vbCrLf _
[TAB][TAB]     & vbCrLf _
[TAB][TAB]     & "起動時にこのメッセージを表示させたくない場合は、"  & vbCrLf _
[TAB][TAB]     & "起動オプションとして -h をつけてください。"
end if

Set WSHShell = WScript.CreateObject("WScript.Shell")

'メインを無限ループさせる。
do
[TAB]main
loop

'--------------------------------------------------------------------------------
Sub main()
    '分が変わるまで、一秒ずつ待ちつづける
    'アベンダーが削除されたときは、ただちに終了処理に移る
    nextMinute = minute(now) + 1
    if nextMinute = 60 then nextMinute = 0
    
    do until nextMinute = minute(now)
[TAB]If not FSO.FileExists("備忘くんアベンダー") Then
[TAB][TAB]i=msgbox ("【備忘くん】を終了します。",,"備忘くん")
[TAB][TAB]WScript.quit
[TAB]end if
    [TAB]WScript.Sleep 1000
    loop
    
    '分が変わったらタスクリストを読む
    WSHShell.Run "wscript subCallTask.vbs"
    
End Sub

**subCallTask.vbs
Set FSO = WScript.CreateObject("Scripting.FileSystemObject")
callTask

'--------------------------------------------------------------------------------
Sub callTask()

[TAB]Set test = FSO.OpenTextFile("備忘くんアベンダー",8,false)
[TAB]test.WriteLine time
[TAB]Set test = nothing

[TAB]Set INI = FSO.OpenTextFile("forget_em_not.ini",1,0)

[TAB]'タスクリストを空行まで読む
[TAB]do until INI.AtEndOfLine
[TAB][TAB]str = INI.readLine
[TAB][TAB]if instr(1,str,Chr(9))<>0 then
[TAB][TAB][TAB]tab1=instr(1,str,Chr(9))
[TAB][TAB][TAB]tab2=instr(tab1+1,str,Chr(9))
[TAB][TAB][TAB]
[TAB][TAB][TAB]tDate = left(str,tab1-1)
[TAB][TAB][TAB]tTime = mid(str, tab1+1, tab2-tab1-1)
[TAB][TAB][TAB]tMsg  = mid(str, tab2+1, len(str)-tab2+1)
[TAB][TAB][TAB]
[TAB][TAB][TAB]nowDate = month(now) & "/" & day(now)
[TAB][TAB][TAB]nowTime = hour(now) & ":" & right("0" & minute(now),2)
[TAB][TAB][TAB]
[TAB][TAB][TAB]if tDate = "d" then tDate = nowDate
[TAB][TAB][TAB]if tDate = "sun" and weekday(now) = 1 then tDate = nowDate
[TAB][TAB][TAB]if tDate = "mon" and weekday(now) = 2 then tDate = nowDate
[TAB][TAB][TAB]if tDate = "tue" and weekday(now) = 3 then tDate = nowDate
[TAB][TAB][TAB]if tDate = "wed" and weekday(now) = 4 then tDate = nowDate
[TAB][TAB][TAB]if tDate = "thu" and weekday(now) = 5 then tDate = nowDate
[TAB][TAB][TAB]if tDate = "fri" and weekday(now) = 6 then tDate = nowDate
[TAB][TAB][TAB]if tDate = "sat" and weekday(now) = 7 then tDate = nowDate
[TAB][TAB][TAB]
[TAB][TAB][TAB]if tDate = nowDate and tTime = nowTime then
[TAB][TAB][TAB][TAB]call msgbox (tMsg,,"備忘くん")
[TAB][TAB][TAB]end if
[TAB][TAB]end if
[TAB]loop

[TAB]INI.close
[TAB]set INI = nothing

End Sub

**%windir%\system32\wscript.exe forget_em_not.vbs -h

**forget_em_not.ini
【備忘くん】タスクリスト
*************************************************************************************
書式・仕様について。
  日付:スラッシュ区切り。十の位のゼロは書かない。
  時刻:コロン区切り。時は十の位のゼロを書かず、分は十の位のゼロを書く。
 例:
  ○ 1/1 1:01
  × 1-1 1:01
  × 1/1 1:1
  × 1/1 01:01
  × 01/01 1:01
 要は普通に書けってことです。
*
 日付には特殊書式を指定できます。
  日次でアラートしたいなら:d
  週次でアラートしたいなら:mon,tue,wed,thu,fri,sat,sun
*
 このファイルは forget_em_not.vbs と同じフォルダに置いてください。
 日付・時刻・メッセージはtab区切りで記述してください。
 タスクの更新はスクリプト本体の再起動をしなくても反映されます。
 なおスクリプトが読み込めるのは1分につき1メッセージのみです。
 複数タスクがあるときは、1メッセージにまとめて書くか、分をずらして書くこと。
*************************************************************************************
日付	時刻	メッセージ
-------------------------------------------------------------------------------------
d	9:10	メール便、忘れてない?
d	11:00	メールの帰り便、忘れてない?
d	16:00	残業するなら申請。
fri	17:30	D-Listが溜まってないか確かめよう!
*fri	17:31	コピー用紙は足りなくなってない?
9/16	15:30	DB乗せ替え準備。
*以下は乗せ替え日決定後に書く。
9/25	17:30	DB乗せ替え。
9/26	9:30	DB乗せ替え後処理。

d	14:00	SAMPLE★空行以降は読まない仕様です。よってこのメッセージはアラートされません!

右クリックメニューからの「送り先」を簡単に増やす真の方法

さらに言うと、この「SendTo」フォルダ自体のショートカットを「SendTo」フォルダに置くことで、右クリックメニューの「送る」から送り先を簡単に登録できるようになる──というわけだ。なお、Officeなどの一部アプリケーションは「スタートメニュー」−「プログラム」からは「SendTo」で登録できなかった。こうした場合、「Program Files」フォルダなどから直接プログラムを「SendTo」すれば登録できる。

http://www.itmedia.co.jp/bizid/articles/0709/14/news074.html

はいダウトー。これじゃまともに動きません。…よね?

SendToのショートカットに、ファイルを「送る」したら、コピーされる。「直接プログラムを「SendTo」」なんてしたら、プログラムファイルがそのままコピーされる。まともに動く保証無し。

id:ume-yがブクマコメントで言及していますが、そのとおり。
ショートカットを作成したいときには、WSHの力を借りるといいと思います。

Set Fs = WScript.CreateObject("Scripting.FileSystemObject")
Set WSHShell = WScript.CreateObject("WScript.Shell")

for i=0 to WScript.Arguments.length-1

    Set objSc = WSHShell.CreateShortcut(Fs.GetAbsolutePathName(WSHshell.SpecialFolders("sendTo")) & "\" & Fs.GetBaseName(WScript.Arguments(i)) & ".lnk")
    objSc.TargetPath=WScript.Arguments(i)
    objSc.save
next

WScript.quit

これをコピペして、「hoge.vbs」という名前で適当なフォルダに保存して、手始めに複製作ってhoge.vbs自体を複製へとドロップして、右クリック→「送る」に「hoge.vbs へのショートカット」が増えていたら成功。
無駄に複数ファイルに対応してます。複数のアプリをいっぺんに「送る」登録したい場合にも頼れる味方です。どんな場合だ。