OSDN Git Service

printStackTraceの場所をLoggerに置き換え
[coroid/inqubus.git] / frontend / src / saccubus / net / NicoClientImpl.java
1 package saccubus.net;
2
3 import java.io.BufferedReader;
4 import java.io.File;
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;
11 import java.net.URL;
12 import java.net.HttpURLConnection;
13 import java.net.InetSocketAddress;
14 import java.net.Proxy;
15 import java.net.URLDecoder;
16 import java.net.URLEncoder;
17 import java.util.HashMap;
18 import java.util.Map;
19 import java.util.logging.Level;
20 import java.util.logging.Logger;
21 import javax.net.ssl.HttpsURLConnection;
22 import nicobrowser.util.Util;
23 import saccubus.ConvertStopFlag;
24 import saccubus.net.VideoInfo.OfficialOption;
25 import saccubus.util.FileUtil;
26
27 /**
28  * <p>
29  * タイトル: さきゅばす
30  * </p>
31  *
32  * <p>
33  * 説明: ニコニコ動画の動画をコメントつきで保存
34  * </p>
35  *
36  * <p>
37  * 著作権: Copyright (c) 2007 PSI
38  * </p>
39  *
40  * <p>
41  * 会社名:
42  * </p>
43  * @deprecated 2011/2 以降のコメントダウンロード仕様には対応していません.
44  * @author 未入力
45  * @version 1.0
46  */
47 @Deprecated
48 public class NicoClientImpl implements NicoClient {
49
50     private static final Logger logger = Logger.getLogger(NicoClientImpl.class.getName());
51     private String Cookie = null;
52     private final String User;
53     private final String Pass;
54     private boolean Logged_in = false;
55     private final ConvertStopFlag StopFlag;
56     private final Proxy ConProxy;
57
58     public NicoClientImpl(final String user, final String pass,
59             final ConvertStopFlag flag, final String proxy, final int proxy_port) {
60         User = user;
61         Pass = pass;
62         if (proxy != null && proxy.length() > 0 && proxy_port >= 0
63                 && proxy_port <= 65535) {
64             ConProxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxy,
65                     proxy_port));
66         } else {
67             ConProxy = Proxy.NO_PROXY;
68         }
69         // ログイン
70         Logged_in = login();
71         StopFlag = flag;
72     }
73
74     /**
75      * @deprecated reduceComment は無視されます(常にtrueの動作をします).
76      */
77     @Override
78     public File getComment(VideoInfo vi, CommentInfo ci, final File file, final TextProgressListener status,
79             int back_comment, boolean reduceComment) {
80         return downloadComment(back_comment, file, vi, ci, status, false);
81     }
82
83     /**
84      * 投稿者コメントをダウンロードする.
85      * @param vi ビデオ情報.
86      * @param file ダウンロード先ファイル.
87      * @param status 進捗通知リスナ.
88      * @return ダウンロードされたファイル. ダウンロードできなければnull.
89      */
90     @Override
91     public File getTcomment(VideoInfo vi, final File file, final TextProgressListener status) {
92         return downloadComment(1000, file, vi, status, true);
93     }
94
95     private File downloadComment(int back_comment, final File file, VideoInfo vi, final TextProgressListener status,
96             boolean isTcomm) throws NumberFormatException {
97         return downloadComment(back_comment, file, vi, null, status, isTcomm);
98     }
99
100     private File downloadComment(int back_comment, final File file, VideoInfo vi, CommentInfo ci,
101             final TextProgressListener status,
102             boolean isTcomm) throws NumberFormatException {
103         System.out.print("Downloading comment size:" + back_comment + "...");
104         try {
105             if (file.canRead()) { // ファイルがすでに存在するなら削除する。
106                 file.delete();
107             }
108             OutputStream fos = new FileOutputStream(file);
109             HttpURLConnection con = (HttpURLConnection) (new URL(vi.getMsgUrl())).openConnection(ConProxy);
110             con.setDoOutput(true);
111             con.setDoInput(true);
112             con.setRequestMethod("POST");
113             con.addRequestProperty("Cookie", Cookie);
114             con.addRequestProperty("Connection", "close");
115             con.connect();
116             OutputStream os = con.getOutputStream();
117             String tcommStr = (isTcomm) ? "fork=\"1\" " : "";
118             String official = "";
119             OfficialOption oo = vi.getOfficialOption();
120             if (oo != null) {
121                 official = "force_184=\"" + oo.getForce184() + "\" threadkey=\"" + oo.getThreadKey() + "\" ";
122             }
123             String req = "<thread user_id=\"" + vi.getUserId() + "\" when=\"" + ci.getWayBackTime() + "\" waybackkey=\""
124                     + ci.getWayBackKey() + "\" res_from=\"-" + back_comment + "\" version=\"20061206\" thread=\"" + vi.
125                     getThreadId() + "\" " + tcommStr + official + "/>";
126             os.write(req.getBytes());
127             os.flush();
128             os.close();
129             if (con.getResponseCode() != HttpURLConnection.HTTP_OK) {
130                 System.out.println("ng.\nCan't download comment:" + vi.getMsgUrl());
131                 return null;
132             }
133             InputStream is = con.getInputStream();
134             int read = 0;
135             int max_size = 0;
136             String content_length_str = con.getHeaderField("Content-length");
137             if (content_length_str != null && !content_length_str.equals("")) {
138                 max_size = Integer.parseInt(content_length_str);
139             }
140             int size = 0;
141             final byte[] buf = new byte[1024 * 1024];
142             while ((read = is.read(buf, 0, buf.length)) > 0) {
143                 fos.write(buf, 0, read);
144                 size += read;
145                 if (max_size != 0) {
146                     String per = Double.toString((((double) size) * 100) / max_size);
147                     per = per.substring(0, Math.min(per.indexOf(".") + 3, per.length()));
148                     status.setText("コメントダウンロード:" + per + "パーセント完了");
149                 } else {
150                     status.setText("コメントダウンロード中:" + Integer.toString(size >> 10) + "kbytesダウンロード");
151                 }
152                 if (StopFlag.needStop()) {
153                     System.out.println("Stopped.");
154                     is.close();
155                     os.flush();
156                     os.close();
157                     con.disconnect();
158                     file.delete();
159                     return null;
160                 }
161             }
162             System.out.println("ok.");
163             is.close();
164             fos.flush();
165             fos.close();
166             con.disconnect();
167             return file;
168         } catch (IOException ex) {
169             logger.log(Level.SEVERE, null, ex);
170         }
171         return null;
172     }
173
174     @Override
175     public File getVideo(VideoInfo vi, final File file, final TextProgressListener status) {
176         if (vi.getVideoUrl() == null) {
177             System.out.println("Video url is not detected.");
178             return null;
179         }
180         try {
181 //                      if (file.canRead()) { // ファイルがすでに存在するなら削除する。
182 //                              file.delete();
183 //                      }
184             HttpURLConnection con = (HttpURLConnection) (new URL(vi.getVideoUrl())).openConnection(ConProxy);
185             /* 出力のみ */
186             con.setDoInput(true);
187             con.setRequestMethod("GET");
188             con.addRequestProperty("Cookie", Cookie);
189             con.connect();
190             if (con.getResponseCode() != HttpURLConnection.HTTP_OK) {
191                 System.out.println("Can't get video:" + vi.getVideoUrl());
192                 return null;
193             }
194             final String extension = Util.getExtention(con.getContentType());
195             File outFile = appendExtension(file, extension);
196             InputStream is = con.getInputStream();
197             OutputStream os = new FileOutputStream(outFile);
198             String content_length_str = con.getHeaderField("Content-length");
199             int max_size = 0;
200             if (content_length_str != null && !content_length_str.equals("")) {
201                 max_size = Integer.parseInt(content_length_str);
202             }
203             int size = 0;
204             System.out.print("Downloading video...");
205             int read = 0;
206             final byte[] buf = new byte[1024 * 1024];
207             while ((read = is.read(buf, 0, buf.length)) > 0) {
208                 size += read;
209                 os.write(buf, 0, read);
210                 if (max_size != 0) {
211                     String per = Double.toString((((double) size) * 100)
212                             / max_size);
213                     per = per.substring(0, Math.min(per.indexOf(".") + 3, per.length()));
214                     status.setText("動画ダウンロード:" + per + "パーセント完了");
215                 } else {
216                     status.setText("動画ダウンロード中:" + Integer.toString(size >> 10)
217                             + "kbytesダウンロード");
218                 }
219                 if (StopFlag.needStop()) {
220                     System.out.println("Stopped.");
221                     is.close();
222                     os.flush();
223                     os.close();
224                     con.disconnect();
225                     outFile.delete();
226                     return null;
227                 }
228             }
229             System.out.println("ok.");
230             is.close();
231             os.flush();
232             os.close();
233             con.disconnect();
234             return outFile;
235         } catch (FileNotFoundException ex) {
236             logger.log(Level.SEVERE, null, ex);
237         } catch (IOException ex) {
238             logger.log(Level.SEVERE, null, ex);
239         }
240         return null;
241     }
242
243     /** @return ビデオ名 */
244     public String getVideoHistoryAndTitle(String tag) throws IOException {
245         String url = "http://www.nicovideo.jp/watch/" + tag;
246         System.out.print("Getting video history...");
247         String new_cookie = null;
248         String videoTitle = null;
249
250         HttpURLConnection con = (HttpURLConnection) (new URL(url)).openConnection(ConProxy);
251         /* リクエストの設定 */
252         con.setRequestMethod("GET");
253         con.addRequestProperty("Cookie", Cookie);
254         con.addRequestProperty("Connection", "close");
255         con.connect();
256         if (con.getResponseCode() != HttpURLConnection.HTTP_OK) {
257             throw new IOException("Can't getVideoHistory:" + url);
258         }
259         int i = 1;
260         String key;
261         String value;
262         while ((key = con.getHeaderFieldKey(i)) != null) {
263             if (key.equalsIgnoreCase("Set-Cookie")) {
264                 value = con.getHeaderField(i);
265                 if (value != null) {
266                     new_cookie = value.substring(0, value.indexOf(";"));
267                 }
268             }
269             i++;
270         }
271         BufferedReader br = new BufferedReader(new InputStreamReader(con.getInputStream(), "UTF-8"));
272         String ret;
273         int index = -1;
274         while ((ret = br.readLine()) != null && index < 0) {
275             final String TITLE_PARSE_STR_START = "<title>";
276             if ((index = ret.indexOf(TITLE_PARSE_STR_START)) >= 0) {
277                 videoTitle = ret.substring(index + TITLE_PARSE_STR_START.length(), ret.indexOf("‐", index));
278                 videoTitle = FileUtil.safeFileName(videoTitle);
279             }
280         }
281         br.close();
282         con.disconnect();
283         if (new_cookie == null) {
284             System.out.println("Can't getVideoHistory: cannot get cookie.");
285             return null;
286         }
287         System.out.println("ok.");
288         Cookie += "; ";
289         Cookie += new_cookie;
290
291         return videoTitle;
292     }
293
294     @Override
295     public VideoInfo getVideoInfo(String tag) throws IOException {
296         final String videoTitle = getVideoHistoryAndTitle(tag);
297
298         String url = "http://flapi.nicovideo.jp/api/getflv/" + tag;
299         if (tag.startsWith("nm")) {
300             url += "?as3=1";
301         }
302         System.out.print("Getting video informations...");
303         Map<String, String> res = new NicoApiRequest(url).get();
304         String threadId = res.get("thread_id");
305         String videoUrl = res.get("url");
306         String msgUrl = res.get("ms");
307         String userId = res.get("user_id");
308         int videoLength = -1;
309         String videoLengthStr = res.get("l");
310         try {
311             videoLength = Integer.parseInt(videoLengthStr);
312         } catch (NumberFormatException ex) {
313         }
314
315         OfficialOption oo = null;
316         if ("1".equals(res.get("needs_key"))) {
317             oo = getOfficialOption(threadId);
318         }
319
320         VideoInfo vi = new VideoInfo(videoTitle, threadId, videoUrl, msgUrl, userId, videoLength, oo);
321         System.out.println("ok.");
322         return vi;
323     }
324
325     private OfficialOption getOfficialOption(String threadId) throws IOException {
326         String url = "http://flapi.nicovideo.jp/api/getthreadkey?thread=" + threadId;
327         Map<String, String> map = new NicoApiRequest(url).get();
328         return new OfficialOption(map.get("threadkey"), map.get("force_184"));
329     }
330
331     @Override
332     public String getWayBackKey(VideoInfo vi) throws IOException {
333
334         System.out.print("Getting wayback key...");
335         String url = "http://flapi.nicovideo.jp/api/getwaybackkey?thread="
336                 + vi.getThreadId();
337         String ret = "";
338         try {
339             HttpURLConnection con = (HttpURLConnection) (new URL(url)).openConnection(ConProxy);
340             /* リクエストの設定 */
341             con.setRequestMethod("GET");
342             con.addRequestProperty("Cookie", Cookie);
343             con.addRequestProperty("Connection", "close");
344             con.setDoInput(true);
345             con.connect();
346             if (con.getResponseCode() != HttpURLConnection.HTTP_OK) {
347                 System.out.println("Can't get WayBackKey:" + url);
348                 throw new IOException("Can't get WayBackKey:" + url);
349             }
350             /* 戻り値の取得 */
351             BufferedReader br = new BufferedReader(new InputStreamReader(con.getInputStream()));
352             ret = br.readLine();
353             br.close();
354             con.disconnect();
355         } catch (IOException ex1) {
356             System.out.println("ng.");
357             logger.log(Level.SEVERE, null, ex1);
358             throw ex1;
359         }
360
361         int idx = 0;
362         final String WAYBACKKEY_STR = "waybackkey=";
363
364         if ((idx = ret.indexOf(WAYBACKKEY_STR)) < 0) {
365             System.out.println("ng.");
366             System.out.println("Cannot find wayback key from response.");
367             throw new IOException("Cannot find wayback key from response.");
368         }
369         int end_idx = Math.max(ret.length(), ret.indexOf("&"));
370         String waybackkey = ret.substring(idx + WAYBACKKEY_STR.length(),
371                 end_idx);
372         if (waybackkey == null || waybackkey.equals("")) {
373             System.out.println("ng.");
374             System.out.println("Cannot get wayback key.");
375             throw new IOException("Cannot get wayback key.");
376         }
377         System.out.println("ok. key:" + waybackkey);
378         return waybackkey;
379     }
380
381     @Override
382     public boolean isLoggedIn() {
383         return Logged_in;
384     }
385
386     private boolean login() {
387         try {
388             HttpURLConnection con = (HttpsURLConnection) (new URL(
389                     "https://secure.nicovideo.jp/secure/login?site=niconico")).openConnection(ConProxy);
390             /* 出力のみ */
391             con.setDoOutput(true);
392             HttpURLConnection.setFollowRedirects(false);
393             con.setInstanceFollowRedirects(false);
394             con.setRequestMethod("POST");
395             con.addRequestProperty("Connection", "close");
396             con.connect();
397             StringBuffer sb = new StringBuffer(4096);
398             sb.append("next_url=&");
399             sb.append("mail=");
400             sb.append(URLEncoder.encode(User, "Shift_JIS"));
401             sb.append("&password=");
402             sb.append(URLEncoder.encode(Pass, "Shift_JIS"));
403             sb.append("&submit.x=103&submit.y=16");
404             OutputStream os = con.getOutputStream();
405             os.write(sb.substring(0).getBytes());
406             os.flush();
407             os.close();
408             int code = con.getResponseCode();
409             if (code < 200 || code >= 400) {
410                 System.out.println("Can't login:" + con.getResponseMessage());
411                 return false;
412             }
413             int i = 1;
414             String key;
415             String value;
416             while ((key = con.getHeaderFieldKey(i)) != null) {
417                 if (key.equalsIgnoreCase("Set-Cookie")) {
418                     value = con.getHeaderField(i);
419                     if (value != null) {
420                         Cookie = value.substring(0, value.indexOf(";"));
421                     }
422                 }
423                 i++;
424             }
425             con.disconnect();
426             if (Cookie == null) {
427                 System.out.println("Can't login: cannot set cookie.");
428                 return false;
429             }
430             System.out.println("Logged in.");
431         } catch (IOException ex) {
432             logger.log(Level.SEVERE, null, ex);
433             return false;
434         }
435         return true;
436     }
437
438     private File appendExtension(File file, String extension) {
439         final String e = "." + extension;
440         final String defExt = ".flv";
441         String path = file.getPath();
442         if (path.endsWith(e)) {
443             return file;
444         } else if (path.endsWith(defExt)) {
445             path = path.substring(0, path.length() - defExt.length());
446         }
447         return new File(path + e);
448     }
449
450     private class NicoApiRequest {
451
452         private final String url;
453
454         private NicoApiRequest(String url) {
455             this.url = url;
456         }
457
458         private Map<String, String> get() throws IOException {
459             Map<String, String> map = new HashMap<String, String>();
460             System.out.print("Getting video informations...");
461             HttpURLConnection con = (HttpURLConnection) (new URL(url)).openConnection(ConProxy);
462             /* リクエストの設定 */
463             con.setRequestMethod("GET");
464             con.addRequestProperty("Cookie", Cookie);
465             con.addRequestProperty("Connection", "close");
466             con.connect();
467             if (con.getResponseCode() != HttpURLConnection.HTTP_OK) {
468                 throw new IOException("Can't getVideoInfo:" + url);
469             }
470             /* 戻り値の取得 */
471             BufferedReader br = new BufferedReader(new InputStreamReader(con.getInputStream()));
472             String ret = br.readLine();
473             br.close();
474             con.disconnect();
475             ret = URLDecoder.decode(ret, "Shift_JIS");
476             String[] array = ret.split("&");
477             int cnt = 0;
478             for (int i = 0; i < array.length; i++) {
479                 int idx = array[i].indexOf("=");
480                 if (idx < 0) {
481                     continue;
482                 }
483                 String key = array[i].substring(0, idx);
484                 String value = array[i].substring(idx + 1);
485                 map.put(key, value);
486             }
487             return map;
488         }
489     }
490 }