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.util.HashMap;
19 import javax.net.ssl.HttpsURLConnection;
20 import saccubus.ConvertStopFlag;
21 import saccubus.util.FileUtil;
23 import static saccubus.net.VideoInfo.OfficialOption;
31 * 説明: ニコニコ動画の動画をコメントつきで保存
35 * 著作権: Copyright (c) 2007 PSI
45 public class NicoClientImpl implements NicoClient {
47 private String Cookie = null;
48 private final String User;
49 private final String Pass;
50 private boolean Logged_in = false;
51 private final ConvertStopFlag StopFlag;
52 private final Proxy ConProxy;
54 public NicoClientImpl(final String user, final String pass,
55 final ConvertStopFlag flag, final String proxy, final int proxy_port) {
58 if (proxy != null && proxy.length() > 0 && proxy_port >= 0
59 && proxy_port <= 65535) {
60 ConProxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxy,
63 ConProxy = Proxy.NO_PROXY;
71 public File getComment(VideoInfo vi, CommentInfo ci, final File file, final TextProgressListener status, String back_comment) {
72 return downloadComment(back_comment, file, vi, ci, status, false);
78 * @param file ダウンロード先ファイル.
79 * @param status 進捗通知リスナ.
80 * @return ダウンロードされたファイル. ダウンロードできなければnull.
83 public File getTcomment(VideoInfo vi, final File file, final TextProgressListener status) {
84 return downloadComment("500", file, vi, status, true);
87 private File downloadComment(String back_comment, final File file, VideoInfo vi, final TextProgressListener status,
88 boolean isTcomm) throws NumberFormatException {
89 return downloadComment(back_comment, file, vi, CommentInfo.DEFAULT, status, isTcomm);
92 private File downloadComment(String back_comment, final File file, VideoInfo vi, CommentInfo ci, final TextProgressListener status,
93 boolean isTcomm) throws NumberFormatException {
94 System.out.print("Downloading comment size:" + back_comment + "...");
96 if (file.canRead()) { // ファイルがすでに存在するなら削除する。
99 OutputStream fos = new FileOutputStream(file);
100 HttpURLConnection con = (HttpURLConnection) (new URL(vi.getMsgUrl())).openConnection(ConProxy);
101 con.setDoOutput(true);
102 con.setDoInput(true);
103 con.setRequestMethod("POST");
104 con.addRequestProperty("Cookie", Cookie);
105 con.addRequestProperty("Connection", "close");
107 OutputStream os = con.getOutputStream();
108 String tcommStr = (isTcomm) ? "fork=\"1\" " : "";
109 String official = "";
110 OfficialOption oo = vi.getOfficialOption();
112 official = "force_184=\"" + oo.getForce184() + "\" threadkey=\"" + oo.getThreadKey() + "\" ";
114 String req = "<thread user_id=\"" + vi.getUserId() + "\" when=\"" + ci.getWayBackTime() + "\" waybackkey=\""
115 + ci.getWayBackKey() + "\" res_from=\"-" + back_comment + "\" version=\"20061206\" thread=\"" + vi.
116 getThreadId() + "\" " + tcommStr + official + "/>";
117 os.write(req.getBytes());
120 if (con.getResponseCode() != HttpURLConnection.HTTP_OK) {
121 System.out.println("ng.\nCan't download comment:" + vi.getMsgUrl());
124 InputStream is = con.getInputStream();
127 String content_length_str = con.getHeaderField("Content-length");
128 if (content_length_str != null && !content_length_str.equals("")) {
129 max_size = Integer.parseInt(content_length_str);
132 final byte[] buf = new byte[1024 * 1024];
133 while ((read = is.read(buf, 0, buf.length)) > 0) {
134 fos.write(buf, 0, read);
137 String per = Double.toString((((double) size) * 100) / max_size);
138 per = per.substring(0, Math.min(per.indexOf(".") + 3, per.length()));
139 status.setText("コメントダウンロード:" + per + "パーセント完了");
141 status.setText("コメントダウンロード中:" + Integer.toString(size >> 10) + "kbytesダウンロード");
143 if (StopFlag.needStop()) {
144 System.out.println("Stopped.");
153 System.out.println("ok.");
159 } catch (IOException ex) {
160 ex.printStackTrace();
166 public File getVideo(VideoInfo vi, final File file, final TextProgressListener status) {
167 if (vi.getVideoUrl() == null) {
168 System.out.println("Video url is not detected.");
172 // if (file.canRead()) { // ファイルがすでに存在するなら削除する。
175 HttpURLConnection con = (HttpURLConnection) (new URL(vi.getVideoUrl())).openConnection(ConProxy);
177 con.setDoInput(true);
178 con.setRequestMethod("GET");
179 con.addRequestProperty("Cookie", Cookie);
181 if (con.getResponseCode() != HttpURLConnection.HTTP_OK) {
182 System.out.println("Can't get video:" + vi.getVideoUrl());
185 final String extension = Util.getExtention(con.getContentType());
186 File outFile = appendExtension(file, extension);
187 InputStream is = con.getInputStream();
188 OutputStream os = new FileOutputStream(outFile);
189 String content_length_str = con.getHeaderField("Content-length");
191 if (content_length_str != null && !content_length_str.equals("")) {
192 max_size = Integer.parseInt(content_length_str);
195 System.out.print("Downloading video...");
197 final byte[] buf = new byte[1024 * 1024];
198 while ((read = is.read(buf, 0, buf.length)) > 0) {
200 os.write(buf, 0, read);
202 String per = Double.toString((((double) size) * 100)
204 per = per.substring(0, Math.min(per.indexOf(".") + 3, per.length()));
205 status.setText("動画ダウンロード:" + per + "パーセント完了");
207 status.setText("動画ダウンロード中:" + Integer.toString(size >> 10)
210 if (StopFlag.needStop()) {
211 System.out.println("Stopped.");
220 System.out.println("ok.");
226 } catch (FileNotFoundException ex) {
227 ex.printStackTrace();
228 } catch (IOException ex) {
229 ex.printStackTrace();
235 public String getVideoHistoryAndTitle(String tag) throws IOException {
236 String url = "http://www.nicovideo.jp/watch/" + tag;
237 System.out.print("Getting video history...");
238 String new_cookie = null;
239 String videoTitle = null;
241 HttpURLConnection con = (HttpURLConnection) (new URL(url)).openConnection(ConProxy);
243 con.setRequestMethod("GET");
244 con.addRequestProperty("Cookie", Cookie);
245 con.addRequestProperty("Connection", "close");
247 if (con.getResponseCode() != HttpURLConnection.HTTP_OK) {
248 throw new IOException("Can't getVideoHistory:" + url);
253 while ((key = con.getHeaderFieldKey(i)) != null) {
254 if (key.equalsIgnoreCase("Set-Cookie")) {
255 value = con.getHeaderField(i);
257 new_cookie = value.substring(0, value.indexOf(";"));
262 BufferedReader br = new BufferedReader(new InputStreamReader(con.getInputStream(), "UTF-8"));
265 while ((ret = br.readLine()) != null && index < 0) {
266 final String TITLE_PARSE_STR_START = "<title>";
267 if ((index = ret.indexOf(TITLE_PARSE_STR_START)) >= 0) {
268 videoTitle = ret.substring(index + TITLE_PARSE_STR_START.length(), ret.indexOf("‐", index));
269 videoTitle = FileUtil.safeFileName(videoTitle);
274 if (new_cookie == null) {
275 System.out.println("Can't getVideoHistory: cannot get cookie.");
278 System.out.println("ok.");
280 Cookie += new_cookie;
286 public VideoInfo getVideoInfo(String tag) throws IOException {
287 final String videoTitle = getVideoHistoryAndTitle(tag);
289 String url = "http://flapi.nicovideo.jp/api/getflv/" + tag;
290 if (tag.startsWith("nm")) {
293 System.out.print("Getting video informations...");
294 Map<String, String> res = new NicoApiRequest(url).get();
295 String threadId = res.get("thread_id");
296 String videoUrl = res.get("url");
297 String msgUrl = res.get("ms");
298 String userId = res.get("user_id");
299 int videoLength = -1;
300 String videoLengthStr = res.get("l");
302 videoLength = Integer.parseInt(videoLengthStr);
303 } catch (NumberFormatException ex) {
306 OfficialOption oo = null;
307 if ("1".equals(res.get("needs_key"))) {
308 oo = getOfficialOption(threadId);
311 VideoInfo vi = new VideoInfo(videoTitle, threadId, videoUrl, msgUrl, userId, videoLength, oo);
312 System.out.println("ok.");
316 private OfficialOption getOfficialOption(String threadId) throws IOException {
317 String url = "http://flapi.nicovideo.jp/api/getthreadkey?thread=" + threadId;
318 Map<String, String> map = new NicoApiRequest(url).get();
319 return new OfficialOption(map.get("threadkey"), map.get("force_184"));
323 public String getWayBackKey(VideoInfo vi) throws IOException {
325 System.out.print("Getting wayback key...");
326 String url = "http://flapi.nicovideo.jp/api/getwaybackkey?thread="
330 HttpURLConnection con = (HttpURLConnection) (new URL(url)).openConnection(ConProxy);
332 con.setRequestMethod("GET");
333 con.addRequestProperty("Cookie", Cookie);
334 con.addRequestProperty("Connection", "close");
335 con.setDoInput(true);
337 if (con.getResponseCode() != HttpURLConnection.HTTP_OK) {
338 System.out.println("Can't get WayBackKey:" + url);
339 throw new IOException("Can't get WayBackKey:" + url);
342 BufferedReader br = new BufferedReader(new InputStreamReader(con.getInputStream()));
346 } catch (IOException ex1) {
347 System.out.println("ng.");
348 ex1.printStackTrace();
353 final String WAYBACKKEY_STR = "waybackkey=";
355 if ((idx = ret.indexOf(WAYBACKKEY_STR)) < 0) {
356 System.out.println("ng.");
357 System.out.println("Cannot find wayback key from response.");
358 throw new IOException("Cannot find wayback key from response.");
360 int end_idx = Math.max(ret.length(), ret.indexOf("&"));
361 String waybackkey = ret.substring(idx + WAYBACKKEY_STR.length(),
363 if (waybackkey == null || waybackkey.equals("")) {
364 System.out.println("ng.");
365 System.out.println("Cannot get wayback key.");
366 throw new IOException("Cannot get wayback key.");
368 System.out.println("ok. key:" + waybackkey);
373 public boolean isLoggedIn() {
377 private boolean login() {
379 HttpURLConnection con = (HttpsURLConnection) (new URL(
380 "https://secure.nicovideo.jp/secure/login?site=niconico")).openConnection(ConProxy);
382 con.setDoOutput(true);
383 HttpURLConnection.setFollowRedirects(false);
384 con.setInstanceFollowRedirects(false);
385 con.setRequestMethod("POST");
386 con.addRequestProperty("Connection", "close");
388 StringBuffer sb = new StringBuffer(4096);
389 sb.append("next_url=&");
391 sb.append(URLEncoder.encode(User, "Shift_JIS"));
392 sb.append("&password=");
393 sb.append(URLEncoder.encode(Pass, "Shift_JIS"));
394 sb.append("&submit.x=103&submit.y=16");
395 OutputStream os = con.getOutputStream();
396 os.write(sb.substring(0).getBytes());
399 int code = con.getResponseCode();
400 if (code < 200 || code >= 400) {
401 System.out.println("Can't login:" + con.getResponseMessage());
407 while ((key = con.getHeaderFieldKey(i)) != null) {
408 if (key.equalsIgnoreCase("Set-Cookie")) {
409 value = con.getHeaderField(i);
411 Cookie = value.substring(0, value.indexOf(";"));
417 if (Cookie == null) {
418 System.out.println("Can't login: cannot set cookie.");
421 System.out.println("Logged in.");
422 } catch (IOException ex) {
423 ex.printStackTrace();
429 private File appendExtension(File file, String extension) {
430 final String e = "." + extension;
431 final String defExt = ".flv";
432 String path = file.getPath();
433 if (path.endsWith(e)) {
435 } else if (path.endsWith(defExt)) {
436 path = path.substring(0, path.length() - defExt.length());
438 return new File(path + e);
441 private class NicoApiRequest {
443 private final String url;
445 private NicoApiRequest(String url) {
449 private Map<String, String> get() throws IOException {
450 Map<String, String> map = new HashMap<String, String>();
451 System.out.print("Getting video informations...");
452 HttpURLConnection con = (HttpURLConnection) (new URL(url)).openConnection(ConProxy);
454 con.setRequestMethod("GET");
455 con.addRequestProperty("Cookie", Cookie);
456 con.addRequestProperty("Connection", "close");
458 if (con.getResponseCode() != HttpURLConnection.HTTP_OK) {
459 throw new IOException("Can't getVideoInfo:" + url);
462 BufferedReader br = new BufferedReader(new InputStreamReader(con.getInputStream()));
463 String ret = br.readLine();
466 ret = URLDecoder.decode(ret, "Shift_JIS");
467 String[] array = ret.split("&");
469 for (int i = 0; i < array.length; i++) {
470 int idx = array[i].indexOf("=");
474 String key = array[i].substring(0, idx);
475 String value = array[i].substring(idx + 1);