edvakf / NNDDMOD

オレオレ派生版NNDD (他人が使うようには作ってません)
1 stars 0 forks source link

NNDDDownloader を並列化したい #6

Open edvakf opened 13 years ago

edvakf commented 13 years ago

今は色んなリクエストを直列で処理しているらしい。

org/mineap/nndd/NNDDDownloader.as のコメントはこうなってる。

ニコニコ動画にアクセスし、ダウンロードを行います。処理は以下の順に進行します。
1.ログイン
2.動画ページへアクセス
3.コメントのDL
4.投稿者コメントのDL
5.ユーザーニコ割のDL(存在する場合)
6.サムネイル情報をDL
7.サムネイル画像をDL
8.市場情報をDL
9.動画をDL
各ステップの完了ごとにイベントが発行されます。
また、動画のDL時はプログレスイベントが発行されます。

このうち、並列してリクエストできそうなところを並列化して、動画再生と関係ないものを後回しにして、動画再生までにかかる時間を短縮したい。

ざっと見た感じ、動画再生までにやらなければいけないのは「ログイン」と「コメントのDL」と「投稿者コメントのDL」と「ユーザーニコ割のDL」で、「ログイン」以外は並列化できそう。また、同時に「動画をDL」も始めておいて、バッファに溜めておきたい。

その他の「動画ページへアクセス」と「サムネイル情報をDL」と「サムネイル画像をDL」と「市場情報をDL」は動画再生が始まってからでも良さそう。「動画ページへアクセス」は何やってるんだろ。動画の上に書いてあるコメントとタグ一覧?

今から調べる。

edvakf commented 13 years ago

ログイン以降の手順を詳しく追ってみる。

「動画ページへアクセス」から。

ここではまず、リダイレクトされるかを確認。リダイレクトされていたら videoId と thumbInfoId を置き換える(どういうケースが該当するんだろ?)。

さらに、ビデオのタイトルを取得。getVideoName(event.target.data) というやつ。title タグを読んでるだけ。タイトルが無ければ次に進まない。タイトルぐらい getflv から取得できてもいいのにな。

NNDDDownloader の requestForWatchOnly メソッドで呼ばれた場合はここで終了。次に進まない。

そうでなければ getflv API にリクエスト。これの完了を待たないとコメント取得できない。

過去ログモードのときは getwaybackkey にアクセスして waybackkey を取得。getflv で取得した threadId が必要。過去ログモードはストリーミング再生には関係ないっぽい。動画以外をダウンロードか、コメントのみダウンロードで when に null を渡したときだけ起こる。

次に「コメントをDL」へ進む。getflv と getwaybackkey の結果が必要。

通常コメントを取得したら、ここで何故か getflv の結果を見て「現在エコノミーモードです。ダウンロードしますか?」というアラートが出る。しかしこれはコメント取得の前にやったほうが良さそう。エコノミーモードでなければ(または前述の質問に Yes と答えた場合は)次へ進む。

次は「投稿者コメントをDL」。

ownerCommentGetStart の中で通常コメントをファイルに保存してるから分かりにくくなってるな。この処理は commentGetSuccess に移したい。

投稿者コメント取得したら、@cm という命令があるか見て、あればニコ割を取得。

ニコ割取得はなんか面倒なことになってる。getbgm を取得して、その結果を見て、さっきの @cm 命令と比べて、複数あったらそれを全部取得。最終的に getThumbInfo へ進む。

ニコ割取得も並列化できそう。

長くなるので一旦ここで投稿。

edvakf commented 13 years ago

「サムネイル情報をDL」について。

サムネイル情報というのは http://www.nicovideo.jp/api/getthumbinfo/sm9 のような API のこと。参考→ http://www.cathodemusic.net/api/lib/getthumbinfo.php

サムネイルだけでなく、動画のタイトル、動画の説明文、投稿日、動画の長さ、再生数、コメント数、マイリスト登録数、最後のコメント、動画のURL、動画のタイプ、現在登録されているタグ、などが取得できるらしい。動画が削除済みかどうかも分かる。

getflv は URL クエリ形式なのに対して getthumbinfo は XML なので、タグのような複雑な戻り値がある。getflv と一緒に最初にアクセスしても良さそう。

動画が削除済みならサムネイル画像を取得せずに市場へ進む。動画は手元にダウンロードしてあって動画情報をアップデートするときに使われるのかな。

「サムネイル画像をDL」はそのまま。そういえばコメントも getthumbinfo もサムネイルも、ストリーミングであっても一旦ローカルに保存してるっぽいな。

「市場情報をDL」は http://ichiba.nicovideo.jp/embed/?... を取得してファイルに保存。

「動画をDL」は ichibaInfoGetSuccess というメソッドから。_isVideoNotDownload というのは「ストリーミング再生するかどうか」らしい。名前が分かりにくい。VideoLoader では isStreamingPlay で受けてる。

ストリーミングの場合はここで DOWNLOAD_PROCESS_COMPLETE を出して終了。

ストリーミング再生ではない場合(ダウンロードの場合)は1MBごとにプログレスイベントを出してるが、ファイルへの保存は全ダウンロードが完了してから行うらしい。最後に VIDEO_GET_SUCCESS と DOWNLOAD_PROCESS_COMPLETE を出して終了。

edvakf commented 13 years ago

getthumbinfo はコミュニティ動画には使えないっぽい。

<?xml version="1.0" encoding="UTF-8"?>
<nicovideo_thumb_response status="fail">
<error>
<code>COMMUNITY</code>
<description>community</description>
</error>
</nicovideo_thumb_response>

getthumbinfo にアクセス→COMMUNITY ならページにアクセスしてタイトル取得という順番でも良さそう。

edvakf commented 13 years ago

普通の getthumbinfo はこんな感じ。

<nicovideo_thumb_response status="ok">
<thumb>
<video_id>
sm9
</video_id>
<title>
新・豪血寺一族 -煩悩解放 - レッツゴー!陰陽師
</title>
<description>
レッツゴー!陰陽師(フルコーラスバージョン)
</description>
<thumbnail_url>
http://tn-skr2.smilevideo.jp/smile?i=9
</thumbnail_url>
<first_retrieve>
2007-03-06T00:33:00+09:00
</first_retrieve>
<length>
5:20
</length>
<movie_type>
flv
</movie_type>
<size_high>
21138631
</size_high>
<size_low>
0
</size_low>
<view_counter>
7536998
</view_counter>
<comment_num>
3832143
</comment_num>
<mylist_counter>
87116
</mylist_counter>
<last_res_body>
昔みたいにまたこの動 英語wwwwwwww いえ−... 
</last_res_body>
<watch_url>
http://www.nicovideo.jp/watch/sm9
</watch_url>
<thumb_type>
video
</thumb_type>
<embeddable>
1
</embeddable>
<no_live_play>
0
</no_live_play>
<tags domain="jp">
<tag lock="1">
陰陽師
</tag>
<tag lock="1">
レッツゴー!陰陽師
</tag>
<tag lock="1">
公式
</tag>
<tag lock="1">
音楽
</tag>
<tag category="1" lock="1">
ゲーム
</tag>
<tag>
β時代の英雄
</tag>
<tag>
sm9
</tag>
<tag>
sm13→
</tag>
<tag>
謎の中毒性
</tag>
<tag>
JOYSOUND配信中
</tag>
</tags>
<tags domain="tw">
<tag>
存活期間最久動畫
</tag>
<tag>
豪血寺一族
</tag>
<tag>
編號個位數注目
</tag>
<tag>
惡靈退散
</tag>
</tags>
<user_id>
4
</user_id>
</thumb>
<style>
iframe, embed {display: none;}
</style>
</nicovideo_thumb_response>

公式動画(アニメ)だとこんな感じ。

<nicovideo_thumb_response status="ok">
<thumb>
<video_id>
so14372866
</video_id>
<title>
日常 #6:日常の第六話
</title>
<description>
公開から1週間無料配信!!■脚本:西岡麻衣子 ■絵コンテ/演出:三好一郎 ■作画監督:植野千世子動画一覧は 日常 チャンネル で ch60005第5話 watch/1304048885
</description>
<thumbnail_url>
http://tn-skr3.smilevideo.jp/smile?i=14372866
</thumbnail_url>
<first_retrieve>
2011-05-08T12:00:00+09:00
</first_retrieve>
<length>
23:39
</length>
<movie_type>
mp4
</movie_type>
<size_high>
346059047
</size_high>
<size_low>
51913872
</size_low>
<view_counter>
473142
</view_counter>
<comment_num>
131291
</comment_num>
<mylist_counter>
4071
</mylist_counter>
<last_res_body>
ワンカップwwwww !? wwwwwwwwww 手品用wwwww 受けww... 
</last_res_body>
<watch_url>
http://www.nicovideo.jp/watch/1304670936
</watch_url>
<thumb_type>
video
</thumb_type>
<embeddable>
1
</embeddable>
<no_live_play>
0
</no_live_play>
<tags domain="jp">
<tag category="1" lock="1">
アニメ
</tag>
<tag lock="1">
日常
</tag>
<tag lock="1">
古谷静佳
</tag>
<tag lock="1">
今野宏美
</tag>
</tags>
</thumb>
<style>
iframe, embed {display: none;}
</style>
</nicovideo_thumb_response>

コミュニティ動画は動画ページにアクセスすると JS でこういうふうになってる。

<script type="text/javascript"><!--
var Video = {
    v:                '1305212568',
    id:               'sm14434089',
    tags:             ['\u6b4c\u3063\u3066\u307f\u305f' ,'Mr.Alice' ,'\u30dc\u30ab\u30ed\u30aa\u30ea\u30b8\u30ca\u30eb\u3092\u6b4c\u3063\u3066\u307f\u305f' ,'\u821e(\u6b4c\u3044\u624b)' ],
    lockedTags:       ['\u6b4c\u3063\u3066\u307f\u305f' ],
    title:            '\u300cMr.Alice\u300d\u3092\u6b4c\u3063\u3066\u307f\u305f\u3002',
    description:      '\u3053\u3093\u3070\u3093\u306f\u3001\u821e\u3067\u3059\u3002\u3053\u3093\u306a\u72c2\u306a\u7ae5\u8a71\u306f\u30b9\u30c6\u30ad\uff01\u2282(\u00b0\u2200\u00b0)\u3064\u3000\u30b9\u30c6\u30ad\u306a\u672c\u5bb6\u69d8\uff01(\u00b0\u2200\u00b0)\u3064(<a href=\"http:\/\/www.nicovideo.jp\/watch\/sm12689129\" class=\"watch\">sm12689129<\/a>)',
    thumbnail:        'http:\/\/tn-skr2.smilevideo.jp\/smile?i=14434089',
    postedAt:         '2011/05/12 21:25:46',
    length:           315,
    viewCount:        47,
    mylistCount:      5,
    mainCommunityId:  null,
    communityId:      10415,
    channelId:        null,
    isDeleted:        false,
    isMymemory:       false,
    isMonetized:      false,
    isR18:            false};
--></script>

↑id: 'sm14434089' というのがあるな。これで http://ext.nicovideo.jp/api/getthumbinfo/sm14434089 にアクセスしてみると、ちゃんと情報が出てきた。sm14434089 というのは getflv の結果をデコードしてみると分かるみたい。

url=http://smile-pow42.nicovideo.jp/smile?m=14434089.49653
link=http://www.smilevideo.jp/view/14434089/14101621

こんな感じで。ふむふむ。

edvakf commented 13 years ago

http://d.hatena.ne.jp/gifnksm/20080707/1215443300 ↑に書いてあった。14434089 という数字が分かっても sm と nm の場合がある。しかし、/smile?m=14434089.49653 の "m=" の部分を見れば大体想像がつく。

  1. m= が mp4
  2. v= が flv
  3. s= が swf

ただし swf を投稿できるのはニコニコムービーメーカーからのみらしいので(少なくとも昔は)、nm と考えて良いらしい。

edvakf commented 13 years ago

NNDDDownloader は org/mineap/nndd/player/PlayerController.as の playMovie メソッドでキックオフされてる。(他のファイルもある)

playMovie は第一引数にファイルパスか URL が入る。URL の場合は www.nicovideo.jp/watch/... と smile\ の2つあって、まず /watch/ のほうの URL で呼ぶと、上に長々と書いたようなプロセスの末、VIDEO_GET_SUCCESS のタイミングで playMovie(smile**,...) を呼ぶ。

smile\ は動画の URL であり、このときは NNDDDownloader ではなくストリーミング再生のほうにまわす。

ここで

videoPlayer.label_downloadStatus.text = "";

してる。

ストリーミングの開始は this.init みたい。init の中で initStart して、この中で(swf でなければ)videoDisplay というのを初期化して、これがストリーミングをコントロールしてるみたい。swf だと変換プロセスが挟まる。

なかなか面倒だなあ。ほんとに手が付けられるのかな…やり始めたら結構な全面改装になってしまいそうな気がしてきた。

edvakf commented 13 years ago

NNDDDownloader がいろんな type のイベントを出すのは面倒。結局使われてる時は全部のイベントに同じリスナーを付けてるみたいだし。StatusEvent (または DataEvent) と ErrorEvent だけで text を変えれば良さそう。もしくは Event にして status プロパティを見るようにするか。

close メソッドの isCancel と isError は冗長な気がする。NNDDDownloader で使われてる時は true, true か false, false のどちらかしかない。他のファイル RenewDownloadManager.as と DownloadManager.as から使われてるときは true, false の形。 正常終了→false, false 内部でのエラー→true, true 外部からのキャンセル→true, false になっているらしい。 正常終了とエラーはそれぞれイベント出すようにして、キャンセルはイベント出さないことにしたほうが良さそう。 これらのイベントを出すのは close メソッドとは分ける。 close メソッドは内部のイベントリスナーを全部解除してリクエストも close するだけにする。

edvakf commented 13 years ago

外部からのキャンセル→true, false 外部だけじゃなかった。"現在エコノミーモードです。ダウンロードしますか?" で No にした場合もだ。

edvakf commented 13 years ago

fileIO.addFileStreamEventListener を使っている箇所があるけど、FileIO では openAsync は使ってないのでイベントではなくエラーが throw されるはず。

http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/flash/filesystem/FileStream.html#writeBytes%28%29

ioError:IOErrorEvent — You cannot write to the file (for example, because the file is missing). This event is dispatched only for files that have been opened for asynchronous operations (by using the openAsync() method).

edvakf commented 13 years ago

_thumbInfoId と _videoId は同じで良さそう。

edvakf commented 13 years ago

ストリーミング再生かどうかを _saveVideoName == "nndd" で判断してる箇所があるので、_isStreaming を作る。