3 import java.io.BufferedReader;
5 import java.io.FileNotFoundException;
6 import java.io.FileOutputStream;
7 import java.io.IOException;
8 import java.io.InputStream;
9 import java.io.InputStreamReader;
10 import java.io.OutputStream;
12 import java.net.HttpURLConnection;
13 import java.net.InetSocketAddress;
14 import java.net.Proxy;
15 import java.net.URLEncoder;
16 import java.net.URLDecoder;
17 import java.text.DateFormat;
18 import java.text.ParseException;
19 import java.text.SimpleDateFormat;
20 import java.util.Date;
21 import java.util.HashMap;
23 import javax.net.ssl.HttpsURLConnection;
24 import org.apache.commons.lang.StringUtils;
25 import saccubus.ConvertStopFlag;
26 import saccubus.util.FileUtil;
28 import static saccubus.net.VideoInfo.OfficialOption;
36 * 説明: ニコニコ動画の動画をコメントつきで保存
40 * 著作権: Copyright (c) 2007 PSI
50 public class NicoClientImpl implements NicoClient {
52 private String Cookie = null;
53 private final String User;
54 private final String Pass;
55 private boolean Logged_in = false;
56 private final ConvertStopFlag StopFlag;
57 private final Proxy ConProxy;
59 public NicoClientImpl(final String user, final String pass,
60 final ConvertStopFlag flag, final String proxy, final int proxy_port) {
63 if (proxy != null && proxy.length() > 0 && proxy_port >= 0
64 && proxy_port <= 65535) {
65 ConProxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxy,
68 ConProxy = Proxy.NO_PROXY;
76 public File getComment(VideoInfo vi, CommentInfo ci, final File file, final TextProgressListener status, String back_comment) {
77 return downloadComment(back_comment, file, vi, ci, status, false);
83 * @param file ダウンロード先ファイル.
84 * @param status 進捗通知リスナ.
85 * @return ダウンロードされたファイル. ダウンロードできなければnull.
88 public File getTcomment(VideoInfo vi, final File file, final TextProgressListener status) {
89 return downloadComment("500", file, vi, status, true);
92 private File downloadComment(String back_comment, final File file, VideoInfo vi, final TextProgressListener status,
93 boolean isTcomm) throws NumberFormatException {
94 return downloadComment(back_comment, file, vi, CommentInfo.DEFAULT, status, isTcomm);
97 private File downloadComment(String back_comment, final File file, VideoInfo vi, CommentInfo ci, final TextProgressListener status,
98 boolean isTcomm) throws NumberFormatException {
99 System.out.print("Downloading comment size:" + back_comment + "...");
101 if (file.canRead()) { // ファイルがすでに存在するなら削除する。
104 OutputStream fos = new FileOutputStream(file);
105 HttpURLConnection con = (HttpURLConnection) (new URL(vi.getMsgUrl())).openConnection(ConProxy);
106 con.setDoOutput(true);
107 con.setDoInput(true);
108 con.setRequestMethod("POST");
109 con.addRequestProperty("Cookie", Cookie);
110 con.addRequestProperty("Connection", "close");
112 OutputStream os = con.getOutputStream();
113 String tcommStr = (isTcomm) ? "fork=\"1\" " : "";
114 String official = "";
115 OfficialOption oo = vi.getOfficialOption();
117 official = "force_184=\"" + oo.getForce184() + "\" threadkey=\"" + oo.getThreadKey() + "\" ";
119 String req = "<thread user_id=\"" + vi.getUserId() + "\" when=\"" + ci.getWayBackTime() + "\" waybackkey=\""
120 + ci.getWayBackKey() + "\" res_from=\"-" + back_comment + "\" version=\"20061206\" thread=\"" + vi.
121 getThreadId() + "\" " + tcommStr + official + "/>";
122 os.write(req.getBytes());
125 if (con.getResponseCode() != HttpURLConnection.HTTP_OK) {
126 System.out.println("ng.\nCan't download comment:" + vi.getMsgUrl());
129 InputStream is = con.getInputStream();
132 String content_length_str = con.getHeaderField("Content-length");
133 if (content_length_str != null && !content_length_str.equals("")) {
134 max_size = Integer.parseInt(content_length_str);
137 final byte[] buf = new byte[1024 * 1024];
138 while ((read = is.read(buf, 0, buf.length)) > 0) {
139 fos.write(buf, 0, read);
142 String per = Double.toString((((double) size) * 100) / max_size);
143 per = per.substring(0, Math.min(per.indexOf(".") + 3, per.length()));
144 status.setText("コメントダウンロード:" + per + "パーセント完了");
146 status.setText("コメントダウンロード中:" + Integer.toString(size >> 10) + "kbytesダウンロード");
148 if (StopFlag.needStop()) {
149 System.out.println("Stopped.");
158 System.out.println("ok.");
164 } catch (IOException ex) {
165 ex.printStackTrace();
171 public File getVideo(VideoInfo vi, final File file, final TextProgressListener status) {
172 if (vi.getVideoUrl() == null) {
173 System.out.println("Video url is not detected.");
177 // if (file.canRead()) { // ファイルがすでに存在するなら削除する。
180 HttpURLConnection con = (HttpURLConnection) (new URL(vi.getVideoUrl())).openConnection(ConProxy);
182 con.setDoInput(true);
183 con.setRequestMethod("GET");
184 con.addRequestProperty("Cookie", Cookie);
186 if (con.getResponseCode() != HttpURLConnection.HTTP_OK) {
187 System.out.println("Can't get video:" + vi.getVideoUrl());
190 final String extension = Util.getExtention(con.getContentType());
191 File outFile = appendExtension(file, extension);
192 InputStream is = con.getInputStream();
193 OutputStream os = new FileOutputStream(outFile);
194 String content_length_str = con.getHeaderField("Content-length");
196 if (content_length_str != null && !content_length_str.equals("")) {
197 max_size = Integer.parseInt(content_length_str);
200 System.out.print("Downloading video...");
202 final byte[] buf = new byte[1024 * 1024];
203 while ((read = is.read(buf, 0, buf.length)) > 0) {
205 os.write(buf, 0, read);
207 String per = Double.toString((((double) size) * 100)
209 per = per.substring(0, Math.min(per.indexOf(".") + 3, per.length()));
210 status.setText("動画ダウンロード:" + per + "パーセント完了");
212 status.setText("動画ダウンロード中:" + Integer.toString(size >> 10)
215 if (StopFlag.needStop()) {
216 System.out.println("Stopped.");
225 System.out.println("ok.");
231 } catch (FileNotFoundException ex) {
232 ex.printStackTrace();
233 } catch (IOException ex) {
234 ex.printStackTrace();
240 public String getVideoHistoryAndTitle(String tag) throws IOException {
241 String url = "http://www.nicovideo.jp/watch/" + tag;
242 System.out.print("Getting video history...");
243 String new_cookie = null;
244 String videoTitle = null;
246 HttpURLConnection con = (HttpURLConnection) (new URL(url)).openConnection(ConProxy);
248 con.setRequestMethod("GET");
249 con.addRequestProperty("Cookie", Cookie);
250 con.addRequestProperty("Connection", "close");
252 if (con.getResponseCode() != HttpURLConnection.HTTP_OK) {
253 throw new IOException("Can't getVideoHistory:" + url);
258 while ((key = con.getHeaderFieldKey(i)) != null) {
259 if (key.equalsIgnoreCase("Set-Cookie")) {
260 value = con.getHeaderField(i);
262 new_cookie = value.substring(0, value.indexOf(";"));
267 BufferedReader br = new BufferedReader(new InputStreamReader(con.getInputStream(), "UTF-8"));
270 while ((ret = br.readLine()) != null && index < 0) {
271 final String TITLE_PARSE_STR_START = "<title>";
272 if ((index = ret.indexOf(TITLE_PARSE_STR_START)) >= 0) {
273 videoTitle = ret.substring(index + TITLE_PARSE_STR_START.length(), ret.indexOf("‐", index));
274 videoTitle = FileUtil.safeFileName(videoTitle);
279 if (new_cookie == null) {
280 System.out.println("Can't getVideoHistory: cannot get cookie.");
283 System.out.println("ok.");
285 Cookie += new_cookie;
291 public VideoInfo getVideoInfo(String tag) throws IOException {
292 final String videoTitle = getVideoHistoryAndTitle(tag);
294 String url = "http://flapi.nicovideo.jp/api/getflv/" + tag;
295 if (tag.startsWith("nm")) {
298 System.out.print("Getting video informations...");
299 Map<String, String> res = new NicoApiRequest(url).get();
300 String threadId = res.get("thread_id");
301 String videoUrl = res.get("url");
302 String msgUrl = res.get("ms");
303 String userId = res.get("user_id");
304 int videoLength = -1;
305 String videoLengthStr = res.get("l");
307 videoLength = Integer.parseInt(videoLengthStr);
308 } catch (NumberFormatException ex) {
311 OfficialOption oo = null;
312 if ("1".equals(res.get("needs_key"))) {
313 oo = getOfficialOption(threadId);
316 VideoInfo vi = new VideoInfo(videoTitle, threadId, videoUrl, msgUrl, userId, videoLength, oo);
317 System.out.println("ok.");
321 private OfficialOption getOfficialOption(String threadId) throws IOException {
322 String url = "http://flapi.nicovideo.jp/api/getthreadkey?thread=" + threadId;
323 Map<String, String> map = new NicoApiRequest(url).get();
324 return new OfficialOption(map.get("threadkey"), map.get("force_184"));
328 public CommentInfo getWayBackKey(VideoInfo vi, String time) throws IOException {
329 if (StringUtils.isBlank(time)) {
330 return CommentInfo.DEFAULT;
333 System.out.print("Setting wayback time...");
335 String waybacktime = "0";
337 final DateFormat fmt = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
338 date = fmt.parse(time);
339 } catch (ParseException ex2) {
344 final DateFormat fmt = new SimpleDateFormat("yyyy/MM/dd HH:mm");
345 date = fmt.parse(time);
346 } catch (ParseException ex3) {
351 waybacktime = Long.toString(date.getTime() / 1000);
352 System.out.println("ok.(" + date.toString() + "):" + waybacktime);
355 long tmp_time = Long.parseLong(time);
356 waybacktime = Long.toString(tmp_time);
357 date = new Date(tmp_time * 1000);
358 System.out.println("ok.(" + date.toString() + "):"
360 } catch (NumberFormatException ex4) {
361 System.out.println("ng.");
362 System.out.println("Cannot parse wayback time.");
363 throw new IOException("Cannot parse wayback time.", ex4);
366 System.out.print("Getting wayback key...");
367 String url = "http://flapi.nicovideo.jp/api/getwaybackkey?thread="
371 HttpURLConnection con = (HttpURLConnection) (new URL(url)).openConnection(ConProxy);
373 con.setRequestMethod("GET");
374 con.addRequestProperty("Cookie", Cookie);
375 con.addRequestProperty("Connection", "close");
376 con.setDoInput(true);
378 if (con.getResponseCode() != HttpURLConnection.HTTP_OK) {
379 System.out.println("Can't get WayBackKey:" + url);
380 throw new IOException("Can't get WayBackKey:" + url);
383 BufferedReader br = new BufferedReader(new InputStreamReader(con.getInputStream()));
387 } catch (IOException ex1) {
388 System.out.println("ng.");
389 ex1.printStackTrace();
394 final String WAYBACKKEY_STR = "waybackkey=";
396 if ((idx = ret.indexOf(WAYBACKKEY_STR)) < 0) {
397 System.out.println("ng.");
398 System.out.println("Cannot find wayback key from response.");
399 throw new IOException("Cannot find wayback key from response.");
401 int end_idx = Math.max(ret.length(), ret.indexOf("&"));
402 String waybackkey = ret.substring(idx + WAYBACKKEY_STR.length(),
404 if (waybackkey == null || waybackkey.equals("")) {
405 System.out.println("ng.");
406 System.out.println("Cannot get wayback key.");
407 throw new IOException("Cannot get wayback key.");
409 System.out.println("ok. key:" + waybackkey);
410 return new CommentInfo(waybackkey, waybacktime);
414 public boolean isLoggedIn() {
418 private boolean login() {
420 HttpURLConnection con = (HttpsURLConnection) (new URL(
421 "https://secure.nicovideo.jp/secure/login?site=niconico")).openConnection(ConProxy);
423 con.setDoOutput(true);
424 HttpURLConnection.setFollowRedirects(false);
425 con.setInstanceFollowRedirects(false);
426 con.setRequestMethod("POST");
427 con.addRequestProperty("Connection", "close");
429 StringBuffer sb = new StringBuffer(4096);
430 sb.append("next_url=&");
432 sb.append(URLEncoder.encode(User, "Shift_JIS"));
433 sb.append("&password=");
434 sb.append(URLEncoder.encode(Pass, "Shift_JIS"));
435 sb.append("&submit.x=103&submit.y=16");
436 OutputStream os = con.getOutputStream();
437 os.write(sb.substring(0).getBytes());
440 int code = con.getResponseCode();
441 if (code < 200 || code >= 400) {
442 System.out.println("Can't login:" + con.getResponseMessage());
448 while ((key = con.getHeaderFieldKey(i)) != null) {
449 if (key.equalsIgnoreCase("Set-Cookie")) {
450 value = con.getHeaderField(i);
452 Cookie = value.substring(0, value.indexOf(";"));
458 if (Cookie == null) {
459 System.out.println("Can't login: cannot set cookie.");
462 System.out.println("Logged in.");
463 } catch (IOException ex) {
464 ex.printStackTrace();
470 private File appendExtension(File file, String extension) {
471 final String e = "." + extension;
472 final String defExt = ".flv";
473 String path = file.getPath();
474 if (path.endsWith(e)) {
476 } else if (path.endsWith(defExt)) {
477 path = path.substring(0, path.length() - defExt.length());
479 return new File(path + e);
482 private class NicoApiRequest {
484 private final String url;
486 private NicoApiRequest(String url) {
490 private Map<String, String> get() throws IOException {
491 Map<String, String> map = new HashMap<String, String>();
492 System.out.print("Getting video informations...");
493 HttpURLConnection con = (HttpURLConnection) (new URL(url)).openConnection(ConProxy);
495 con.setRequestMethod("GET");
496 con.addRequestProperty("Cookie", Cookie);
497 con.addRequestProperty("Connection", "close");
499 if (con.getResponseCode() != HttpURLConnection.HTTP_OK) {
500 throw new IOException("Can't getVideoInfo:" + url);
503 BufferedReader br = new BufferedReader(new InputStreamReader(con.getInputStream()));
504 String ret = br.readLine();
507 ret = URLDecoder.decode(ret, "Shift_JIS");
508 String[] array = ret.split("&");
510 for (int i = 0; i < array.length; i++) {
511 int idx = array[i].indexOf("=");
515 String key = array[i].substring(0, idx);
516 String value = array[i].substring(idx + 1);