にっき -3ページ目

にっき

Android開発してます。

2014年もあっという間に終わってしまった。

学生の時より1年がかなり早く感じる。
毎日違うことやってるからかなー
何も考えずに勉強だけしてるのとは違うね。

でも今の調子で何年も過ごしたくないなー
人生を無駄にしてるような気がする。

来年はなにか新しいことにチャレンジしていきたいね。
社会人バンドとか良いよなー、暇だから集まろうぜくらいのやつ。

(というか、今の部がもっと目的意識ハッキリしてくれれば良い話なんだがね。何したいのかサッパリわからん。)
以前より製作していたAndroidアプリの安定版をリリースしました。

NICOPOD - にこぽっど ニコニコ動画音楽プレイヤー

機能を可能な限り削ったため、かなりシンプルな仕上がりです・。・b
目立ったバグは無いはず(白目)
「ログイン→検索→再生orキャッシュ」しか出来ないからね!

ただ、デザインについてはもう少し考えたいな。
もっと厨二っぽくしてもいいかもしれん。
あと、マイリスとか履歴を実装するときはNavigationDrawer使ったほうが良いだろうな。
と、やりたいことはまだまだあるけど、取り敢えず安定版ってことでリリースです。

挙動がおかしい点や、不明点などがあればレビューやサポートメールへご連絡下さい。
Twitterが出来てから、ブログってのが廃れてるよなー
長文はこっちのほうが良いけど。
そもそも、そんなに書くことがないしな。

学生の時と比べると毎日変わらないしな・・・
時々ツマンネーな。って思う^p^



8月から製作しているアプリはβリリースしました。
会社の人に使ってもらって、色々修正しているけど
バグらしいバグはそんなに無かった。

これなら本リリースも、そろそろ出来そうかな。
画面遷移の構成を一部だけ作りなおそうと思ったけど、どうしようか。
ViewPagerを使って並べちゃうっていう手もあるけど…
休暇中にきてたメールを見て、ふと思いついたから書いてみる

内容は、もっと色々やるよーっていうのを他の部にも展開したもの。

悪くはない。むしろ積極性では褒められるべき点。他の人も真似しろってレベル。
ただ、現実的に考えると、現状で新しい案件が来ても人が圧倒的に足らない。

パワーが無い。そしてそれに危機感を持って対応しようとする人間も少ない。
儲けが上がらないとマズイ状況で、多くの案件をとってくるのはいい事だが
それに対応しようとしない奴が大半を占めている。

私はやりたくありません。というのが中堅層大半の考え。
ならお前は何なら出来るんだ?室内温度上がるから自宅に帰ってくれたほうが良いのだが?
といった状態。
そんな中で新しい仕事くれ、なんて愚行極まりない。
順番が間違ってる。雑魚の集まりと化してる内部を治すほうが先。
でも、治す気なんてそもそも無いのだから、いよいよこの部も終わりが近づいてきているなー
新人も頭は悪くないけど容量と要領どっちも悪いし。日々の会話をいちいちメモ取るとか、頭Celeronか?

いっそ俺を部長にしてくれないかね、権限欲しいわ。それか教育担当にして欲しいわ。独立権限で。
あー余計な仕事が増えていないことを祈るわ。
デバッグで色々調べたら混乱してきたので、備忘録も兼ねて整理する
今回調べたのは「getflvでurl取得して、そこから動画取得する」という箇所
色々な処理の途中で(PCから)ログイン切断してみた。

【前提】
視聴ページ
ログイン情報(user-session) が必須

getflv
ログイン情報(user-session) が必須

動画本体取得
視聴情報(nicohistory)
ログイン情報(user-session) が必須


【実験内容・結果】
1.視聴ページリクエスト時にログイン切断
→nicohistoryがCookieに返却されない

2.getflvリクエスト時にログイン切断
→urlがレスポンスに存在しない

3.動画本体取得時にログイン切断
→正常に動画を取得


1,2は予想通りだけど、3が予想外だった
結果から書くと、

正常なログイン情報(user-session)
動画url
nicohistoryがあれば

それぞれがどうであれ、また、外部でどうなってようが関係なく取得できるみたい。

--------------------------------

ここからが混乱した所だが、

1でnicohistoryを取得する際に利用したログイン情報と
2でurlを取得する際に利用するログイン情報は 同じである必要がなく(※1)
3ではそれぞれ必要なデータが取り敢えずあれば良いってこと

※1…ユーザーは同じ。異なるユーザー情報で行けたらヤバイと思う。

1 nicohistory取得
2で切断
2リトライ(1とuser-sessionが変わる)
3で切断(内部で1のnicohistory, 2の新user-sessionは保持、外部では切断状態)
ってやっても、取得できたし。


【結論】

というわけで、実装する際は
それぞれの情報を取得する際に、結果が不正なら
ログイン情報を再取得してリトライすれば良い。

【感想】
最初、全情報取得時に一貫したログイン情報が必要だと思ってたから混乱した。
意外と簡単なつくりなのか?同じユーザーなら何でも良いっぽいな。
難しく考えすぎたみたいだわ。アホらし。

結局、ログインして視聴ページ開いて、動画保管場所URL持ってれば勝ち確ってことか…



以下ソース
@Override
  protected Integer doInBackground(Void... unused) {
    SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mActivity);
    // ログイン情報取得
    BasicClientCookie loginCookie =
        new BasicClientCookie(Config.COOKIE_NAME_LOGIN, sp.getString(Config.COOKIE_NAME_LOGIN, null));
    loginCookie.setDomain(Config.COOKIE_DOMAIN);
    loginCookie.setPath(Config.COOKIE_PATH);


    // 視聴ページへアクセス
    NicoHelper nicoHelper = new NicoHelper(mActivity);
    Cookie watchCookie = nicoHelper.getWatchCookie(loginCookie);
    if (watchCookie == null) {
      // 視聴クッキーが不正ならログイン情報をリセットしてリトライ
      sp.edit().remove(Config.COOKIE_NAME_LOGIN).commit();
      if (nicoHelper.login() == Config.STATUS_OK) {
        loginCookie = new BasicClientCookie(Config.COOKIE_NAME_LOGIN, sp.getString(Config.COOKIE_NAME_LOGIN, null));
        loginCookie.setDomain(Config.COOKIE_DOMAIN);
        loginCookie.setPath(Config.COOKIE_PATH);
        watchCookie = nicoHelper.getWatchCookie(loginCookie); // 視聴クッキー取得リトライ
        if (watchCookie == null) {
          return null; // 2回目の視聴もNGなら終了
        }
      } else {
        return null;
      }
    }


    // 動画保管場所へアクセス
    HttpRequestHelper httpRequestHelperForFlv = new HttpRequestHelper();
    httpRequestHelperForFlv.setCookie(loginCookie);
    HttpResponse flvResponse = httpRequestHelperForFlv.getRequest(Config.FLASH_API_URL);
    if (flvResponse.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
      return null;
    }


    // レスポンスデータへの処理
    InputStream flvStream = null;
    InputStream movieStream = null;
    try {
      flvStream = flvResponse.getEntity().getContent();
      HashMap flvInfoMap =
          HttpRequestHelper.getResponseMap(HttpRequestHelper.getResponseString(flvStream));
      String movieUrl = flvInfoMap.get("url");
      if (movieUrl == null) {
        // 動画保管場所へのアクセスがNGならログイン情報をリセットしてリトライ
        sp.edit().remove(Config.COOKIE_NAME_LOGIN).commit();
        if (nicoHelper.login() == Config.STATUS_OK) {
          loginCookie = new BasicClientCookie(Config.COOKIE_NAME_LOGIN, sp.getString(Config.COOKIE_NAME_LOGIN, null));
          loginCookie.setDomain(Config.COOKIE_DOMAIN);
          loginCookie.setPath(Config.COOKIE_PATH);
          httpRequestHelperForFlv.setCookie(loginCookie);
          flvResponse = httpRequestHelperForFlv.getRequest(Config.FLASH_API_URL);
          if (flvResponse.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
            return null;
          }
          flvStream = flvResponse.getEntity().getContent();
          flvInfoMap = HttpRequestHelper.getResponseMap(HttpRequestHelper.getResponseString(flvStream));
          movieUrl = flvInfoMap.get("url");
          if (movieUrl == null) {
            return null; // 2回目もNGなら終了
          }
        } else {
          return null;
        }
      }
      // 動画取得(ここまで正常に来ていれば、外部ログアウトされても取得可能)
      HttpRequestHelper movieHttpRequestHelper = new HttpRequestHelper();
      movieHttpRequestHelper.setCookie(loginCookie); // ログインクッキー設定
      movieHttpRequestHelper.setCookie(watchCookie); // 視聴クッキー設定
      movieHttpRequestHelper.addHeader("Range", "bytes=0-1024000"); // ヘッダー設定
      HttpResponse movieResponse = movieHttpRequestHelper.getRequest(movieUrl); // ダウンロードリクエスト
      if (movieResponse.getStatusLine().getStatusCode() != HttpStatus.SC_OK
          && movieResponse.getStatusLine().getStatusCode() != HttpStatus.SC_PARTIAL_CONTENT) {
        return null;
      }
      movieStream = movieResponse.getEntity().getContent();
      downloadFile(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES), movieStream, "nico.mp4");


      // 通信終了
      httpRequestHelperForFlv.closeConnection();
      movieHttpRequestHelper.closeConnection();


      return Config.STATUS_OK;
    } catch (IllegalStateException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    } finally {
      if (flvStream != null) {
        try {
          flvStream.close();
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
      if (movieStream != null) {
        try {
          movieStream.close();
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
    }
    return null;
  }
ふと気になったんだけど、動画系のアプリって
大体キャッシュ機能付いてんじゃん?

同じ動画を見ようとしたときに、キャッシュしてあればオフラインで見れるし
途中までキャッシュしてたら、途中までは見れる&読み込みも途中から継続みたいな

これどうやって実装してんだろ
キャッシュ自体は実装簡単なんだけど、途中から再開とかデファクトスタンダードがわからん
outputするとき、どこまでDLしたかを保持しておけば良いのかな?
所詮は只のバイトデータだからいけんのかなあ。今度試してみよう。
Twitterにも書いちゃったけど、備忘録として書いとこう

前回記事の続き
1に関して実装が完了した。

【大まかな流れ】
非同期タスク内で以下の処理
※事前にログイン済みクッキーを保存しておき、以下のリクエスト時には必ず使う

getFlvを呼ぶ→URL取得(動画取得で使うメイン)
視聴ページ呼ぶ→「nicohistory」をそのままPreferenceに保存
処理完了後、動画閲覧用のActivityにURLを渡す(Callback)

VideoViewActivity(動画閲覧用アクティビティ)

「nicohistory(視聴Cookie)」と「user-session(ログインCookie)」を
「nicohistory=hoge;user-session=fuga;」に変換後
map.put("Cookie", cookieString); といった感じでマップに保持

setDataSourceに受け取ったURLと↑で作ったmapを渡す


再生できた━━━━(゚∀゚)━━━━ッ!!


【感想・補足】
参考にしたサイトの9割がwatchページを開いた後に~とかいうザックリな説明だったけど
結果的には視聴ページを開いた時に受け取る「nicohistory」というCookieを動画リクエスト時に渡せば良いだけだった。
これは簡単で、問題はMediaPlayerに渡す箇所。
setDataSource自体はMap<String, String>で受け取るからCookieを文字列にしないといけなかった。
ヘッダー情報として渡す際は「Cookie: name=value; name2=value2;(ry」という感じ。

んで、APIが14より前だとヘッダー情報が受け取れるsetDataSourceが
公開されてなくてどうしようもなかった。
APIレベル上げても良かったんだけど、

How do I include http headers with MediaPlayer setDataSource?
を参考に半ば強制的に呼び出した。at least since Froyo 2.2.x, API Level 8 とかあるし良いよねw

そんな感じで山場は超えたかな。
課題としては

・動画重いと頻繁に止まるのでスムーズな再生をしたい(もしくはスムーズに見せかけたい)
・有料動画とか、nm動画とか、エコノミーとか、ニコニコ固有の仕様に対する対応
・プレイヤーに機能追加(リピートとか)
・コメント閲覧・投稿機能(ニコニコの肝だよね。俺はコメントしないから要らないけど)

といった感じで山積みなんだけど、そもそもこのアプリの趣旨が
「シンプルで、通勤時にササッと見れるor聴ける」って感じだから
ガッツリとニコニコ使いたいユーザー向けではないんだよなあ

そもそも、多機能+無料なやつって一杯出回ってるし。
多機能だと朝ちょっと見るのには操作が多いんだよねー
どこまで詰め込むかの線引が難しいなあ。
動画を内部ディレクトリに保存しきってから
VideoViewで再生するのは出来た。

このままだと完全に動画保存されてからじゃないと再生できないとかいう
クソ仕様(保存した動画を外で見るって意味なら便利?)になっているため
次の課題として、動画を裏で保存しながら、再生というよくある処理にしないとな

ただ、今利用しているVideoViewそのままだとCookieを利用した接続ができないので
MediaPlayerを自分で実装しないといけないっぽいな
(このクラスにあるsetDataSourceがMap<String, String>でヘッダー情報を渡せるらしい)

取り敢えず、2段階実装にしよう

1.保存を経由せずに動画再生

ニコニコのgetFlvから動画URLを取得
見たい動画の視聴ページURLにリクエスト後、受け取ったクッキー「nicohistory」を保持
取得した動画URLとnicohistoryを持って動画情報を取得、再生

2.再生しながら裏で保存

1のタスクの裏で非同期で保存処理を実行(そもそもそんなことが出来るのか分からないけど)
※イメージとしては、レスポンスからInputStreamをAsyncTaskの中で保存処理する感じ
VideoDownLoadTaskクラスみたいなのを作ってやれば良いかな



ニコニコのAPIのgetFlvで
「対象の視聴ページを開いた状態」
じゃないと動画データを取得できないという仕様で躓いた

そもそもVideoView使う時にそんな細かい指定できんのか
クッキー渡そうと思って色々ググったけど、サーバーを間にかませるとか
そんなのばっかり(´・ω・`)

んで、InputStream保存か?って思ったけど
それ微妙にアウトなんじゃねえかと思った。良いのかなダウンロードするの。
さすがにこれだけの為にサーバー建てるのもアホらしいしなあ・。・

他の用途でサーバー買ったら試してみようかなあ

どうしよ
昨日やっとお星様になれました



長かったなー
赤色にするにはナイフと特殊武器やらないといけないんだよなあ
緑で1年くらい経ってんのかなー

それにしても、動向グラフの安定感やばいわ
もう少し上げたいんだけどな・。・