2013年お疲れ様でした

新年あけましておめでとうございます.

あっという間に2014年に突入してしまいましたね.

ということで2013年を振り返ります.(本当は2013年のうちにやりたかったんですが,PCが不調だったりでちょっとできなかった)

1月 就活に励む

2月 就活に勤しむ

3月 就活に暗雲が立ち込める

4月 進学にシフト.自前でサーバーを借り,ドメインを取り,遊ぶ.研究室に配属され,大学=研究室の日々をスタートする.

5月 Kinectで遊ぶ.研究室楽しい!!✌('ω'✌ )三✌('ω')✌三( ✌'ω')✌

6月 Twitterクライアント制作を始める.

7月 Twitterクライアントが形になってくる.

8月 院試が終わる.とりあえずTwitterクライアントを公開する.以後ちまちまとアップデートをする.

9月 某IT系企業でインターン開始

10月 インターン楽しい!!✌('ω'✌ )三✌('ω')✌三( ✌'ω')✌

11月 インターン楽しい!!✌('ω'✌ )三✌('ω')✌三( ✌'ω')✌

12月 25日に一人ですき家でカレー食ったあと一人で研究室行ってプリン食って研究室で寝た.

まあこんなところです.最後は笑うとこです.

2013年で触った技術的なことをざっと列挙すると

4月はサーバー周りをいじり倒し,研究室ではKinectのコードを書きなぐり,6月以降家とインターン先でAndroidのコードを書きまくっていました.それなりに充実しているように見えますが,実際そんなにアウトプット出せてないのであんまり達成感ないです.

研究室では理論が疎かになっている感MAXなので,今年はしっかりそっちの方もやっていきたいところ.せっかくハードもソフトもやる研究室なので,ハードも作っていきたいなーとか思ってたりしてます.

まあ,あと2年間モラトリアムが続くので,研究にせよ趣味にせよ,全力で楽しいことをやっていきたいですね.

今年もよろしくお願いします.

はてブで人気動画を狙うよりも自分でキーワード使って狙って行ったほうが精度が高い

こんばんは,みなさまいかがお過ごしでしょうか.もうすぐクリスマスですね.

今日はまた凝りもせず大人向け動画サイトから動画を収集するためのプログラムについての記事です.

はてブは万能じゃない

まず,はてブで人気動画を狙った場合なのですが,結構高い確率で元動画が死んでいます.

XVIDEOSなら7,8割,xHamsterなら4,5割といったところでしょうか.これは悲しいです.

また,カテゴリなどもバラバラで,全く好みでないジャンルの動画もまとめて引っこ抜いてしまいます.ホモビデオなんて大嫌いだ.

これはかなりキツイし面白く無いので改善します

キーワードを投げる

上記の問題をどう解決するか.簡単ですね.

たいていの大人向け動画サイトというのは,検索用のページが有ります.そこにキーワードを投げて返ってきた画面を元に動画を引っこ抜いていけばいいのです.

そういう感じに前書いたスクリプトを改修していったらそれなりに上手いこと出来ました.

コードはGitHubにおいておいたので,自由に使ってください.良いホワイトクリスマスを!

XDownloader

スクリプト改善した

はてブで人気のある動画を自動的にダウンロードするスクリプト書いたんだけど,さすがに動作を変更するときにソースをいじらなければならないのは不便なので改善した.

ついでに,保存用のディレクトリも自動的に作成するようにした.

コードはGitHubにおいてあるので煮るなり焼くなりご自由にどうぞ.
Rubyの実行環境はなんとか調べてやってください.

はてブで人気のある動画を自動的にダウンロードするスクリプト書いた

はてブで人気のエロ動画を自動でダウンロードするツール “xxxdl” 開発しましたを読んで、windows版を使ってみたら動かなかったので自分で作ってみた。

> 改善しました

Windows上で動かせるものを作るのはめんどくさそうだったので、ちょっと勉強していたRubyの練習がてらRubyで書いてUbuntuで動かすことにした。

処理の流れとしては、

  1. はてブから動画サイトのURLを引っ張ってくる
  2. 動画サイトのURLに飛んで、動画ファイルのURLを何とかして見つけるor生成する
  3. ファイルに保存
となる.そんなに難しくはなかったけど,強いて言うなら動画ファイルのURLをソースから探す部分が難しかった.

require "./downloader.rb"
require "./hatebu_loader.rb"

thread_max = 5
max_count = 100

xloader = HatebuLoader.new
#xloader.setHamster #xHamsterの動画をダウンロードするときはコメントを外す

xthreads = []
count = 0
locker = Mutex::new

thread_max.times do
	xthreads << Thread.start do
		while count < max_count
			link = nil
			cnt = nil
			locker.synchronize do
				cnt = count
				link = xloader.getUrl(count)
				count += 1
			end
			dl = VideoDownloader.new
			begin
				dl.setUrl(link)
			rescue OpenURI::HTTPError => e
				p e
				puts"error"
			else
				#dl.dlFromHamster #xHamsterの動画をダウンロードするときはこっちを使う
				dl.dlFromXVideos
			end
			puts "#{cnt} is finished."
		end
	end
end

xthreads.each do |x|
	x.join
end

puts "finish"
require "open-uri"
require "nokogiri"

class HatebuLoader
	def initialize
		@url = "http://b.hatena.ne.jp/entrylist"
		@query ={"url"=>"http://www.xvideos.jp", "sort"=>"count"}
	end
	def getUrl(count)
		@query["of"] = count.to_s
		url = @url.clone
		@query.each do |key, value|
			if !url.include?('?')
				url << '?' << key << '=' << value
			else
				url << '&' << key << '=' << value
			end
		end
		doc = Nokogiri::HTML(open(url))
		entries = doc.css('.entry-link')
		#puts url
		return entries[0].attributes['href'].value
	end

	def setHamster
		@query["url"]="http://xhamster.com"
	end
end
require "open-uri"
require "nokogiri"

class VideoDownloader
	def setUrl(url)
		@url = url
		@doc =  Nokogiri::HTML(open(url))
		@html = @doc.to_s
		@title = @doc.title
		@title.gsub!("/", "-") if @title.include? "/"
		#puts "loaded #{url}"
	end

	def dlFromXVideos
		if !@html.include? "flv_url"
			puts "cannot find video #{@url}"
			return
		end
		pos1 = @html.index("flv_url") + 8
		pos2 = @html.index("&amp;", pos1)
		len = pos2 - pos1
		@downloadUrl = URI.decode(@html[pos1, len])
		@title = @doc.css('div#main > h2')[0].child.text.strip
		downLoad
	end

	def dlFromHamster
		if @doc.css('#playerSwf').empty?
			puts "cannot find video #{@url}"
			return
		end
		pos1 = @html.index("\'srv\'") + 8
		pos2 = @html.index("\'file\'", pos1) - 8
		len = pos2 - pos1
		srv = @html[pos1, len]
		pos1 = @html.index("\'file\'") + 9
		pos2 = @html.index(".flv", pos1) + 4
		len = pos2 - pos1
		file = "/flv2/key=" + @html[pos1, len]
		@downloadUrl = srv + file
		@title = @doc.css("div#playerBox > h2.gr").text.strip
		downLoad
	end

	def downLoad
		fileName = "dl/" + @title + ".flv"
		if File.exist?(fileName)
			puts "#{@title}.flv is already downloaded"
			return
		end
		puts "downloading #{@title} #{@url}"
		File.open(fileName, 'wb') do |output|
			begin
				open(@downloadUrl) do |data|
					output.write(data.read)
				end
			rescue OpenURI::HTTPError
				File.open("error.log", 'a') do |f|
					f.puts "HTTPError, cannot download #{@title}, #{@url}\n#{@downloadUrl}"
				end
			end
		end
	end
end

GitHubに置いといたのでご自由にどうぞ。
マルチスレッドで動くんですが、排他制御が微妙で、設定したmax_countを多少ぶち抜いてしまったりします。直さなければ…

Twitterクライアント出来てました

Twitterクライアント,出来てました.実は先月末に公開してたんですけど記事に書く余力がなかったので.

Sleeping( ˘ω˘)Syar

Twitterクライアントを作ろうと思ったきっかけとなった某クライアントをリスペクトした結果がこのタイトルです.

リストをぬるぬる閲覧できるのが特徴です.

PagerSlidingTabStripという素晴らしいライブラリのおかげです.

他にも,ViewPagerIndicator,twitter4j,PullToRefreshListviewを使いました.

車輪の再発明を防ぐどころか,自分ではとても作れそうにない素敵な車輪を提供してくれる方々にほんとうに感謝です.

いずれは自分も利用する側ではなく提供する側に回りたいものです.

ソースを整理したら,GitHubできっと公開します.

Twitterクライアント製作記その5 複数アカウント対応+会話を表示

複数アカウント対応しました(仮)

アカウントごとに,認証して得られるOAuthTokenのTokenとTokenSecretをデータベースに保存するようにした.

ただ,色々動作が怪しすぎてデバッグしきれない(`;ω;´)

アカウントの削除はボタンだけ用意してあるけどまだ消せない.

リストはユーザーIDでデータベースから情報を引っ張ってこれる形にしたので,アカウントを切り替えた時にリストの状態も切り替わります.素敵.

会話を表示するのは,AsyncTaskでInRepyToStatusIDを追っていけば余裕だろうと思っていたけど,1つを使いまわすと恐ろしいことになった.

2つAsyncTaskを使うメソッドを用意して,順番に回すとうまく行った.これでいいのかはわからないけど.

そして,EGitが使えるようになった!というか元々入ってた!

追記

AsyncTaskの使い方が悪かったみたい.こういう感じに書けばリプライを順番に追えた.

AsyncTask<Void, Void, Status> loadTask;
private class LoadConversationTask extends AsyncTask<Void, Void, Status> {
	@Override
	protected void onPreExecute() {
		//プログレスバーを表示したりする
	}
	@Override
	protected twitter4j.Status doInBackground(Void... params) {
		return nextTweet(nextId);//nextIdのツイートを持ってくる
	}
	@Override
	protected void onPostExecute(twitter4j.Status result) {
		if (result != null) {
			mAdapter.add(result);
			mAdapter.notifyDataSetChanged();
			nextId = result.getInReplyToStatusId();
			Log.d(TAG, "nextId:" + nextId);
			if (nextId > 0) {
				loadTask = new LoadConversationTask();
				loadTask.execute();
			} else {
				Log.d(TAG, "正常終了");
			}
		} else {
			//取得できなかった時の処理をかく
		}
	}
		@Override
	protected void onCancelled() {
		super.onCancelled();
		Log.d(TAG, "onCancelled()");
		setFooterViewStandby();
	}
}
	void loadPrevious() {
	if (loadTask != null && loadTask.getStatus() == AsyncTask.Status.RUNNING) {
		Log.d(TAG, "loadPrevious() return : loadTask is running.");
		return;
	}
	loadTask = new LoadConversationTask();
	loadTask.execute();
}
private twitter4j.Status nextTweet(long id) {
	try {
		return mTwitter.showStatus(nextId);
	} catch (TwitterException e) {
		e.printStackTrace();
	}
	return null;
}

適当なタイミングでloadPrevious()を呼べば,ツイートが読み込み終わるたびにリプライ先を読みこんでくれる.

読み込み中に会話を表示するアクティビティが死んでも,loadTaskは走り続けるので,onPauseの時に,loadTask.cancel(true);を書いておく

trueだとdoInbackGroudの処理が直ちにとまってonCancel()に移り,falseだとが終わるまで待ってonCancel(Object)が動く.

Twitterクライアント製作記その4

なぜかリストを選んだり,会話を表示する前に複数アカウントを実装しようとしていた.

けどSharedPreferenceで複数アカウントの情報を保持してさらにリストの設定とかも保存するとなるとかなりやばいよなあ.

SQLite使えばUserIdをキーにして,色んな情報を保存しておけるっぽいし削除もわかりやすいっぽいからそうしよう.

それよりも早く会話を表示できるようにしよう.

出来たこと

  • アカウント管理画面を作った(追加はまだできない)
  • 再認証出来るようにした
  • ユーザーIDとスクリーンネームをSharedPreferenceに保存するようにした
問題点
  • ツイートをタップした時のDialogFragmentの表示が切れる
  • EGitの使い方がわからない
早くEGitを導入しないと死ぬ…!